diff --git a/README b/README new file mode 100644 index 000000000..5fd1557bd --- /dev/null +++ b/README @@ -0,0 +1 @@ +A News/RSS Reader that controls the amount, relevancy, and interestingness of news subscriptions. \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/__init__.py b/apps/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/analyzer/__init__.py b/apps/analyzer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/analyzer/models.py b/apps/analyzer/models.py new file mode 100644 index 000000000..f59993744 --- /dev/null +++ b/apps/analyzer/models.py @@ -0,0 +1,6 @@ +from django.db import models +from django.contrib.auth.models import User +import datetime +from apps.rss_feeds.models import Feed, Story +from apps.reader.models import UserSubscription, ReadStories +from utils import feedparser, object_manager diff --git a/apps/analyzer/views.py b/apps/analyzer/views.py new file mode 100644 index 000000000..60f00ef0e --- /dev/null +++ b/apps/analyzer/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/apps/opml_import/__init__.py b/apps/opml_import/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/opml_import/models.py b/apps/opml_import/models.py new file mode 100644 index 000000000..6b4c247ab --- /dev/null +++ b/apps/opml_import/models.py @@ -0,0 +1,5 @@ +from django.db import models +from django.contrib.auth.models import User +from apps.rss_feeds.models import Feed, Story +import datetime + diff --git a/apps/opml_import/urls.py b/apps/opml_import/urls.py new file mode 100644 index 000000000..bffd84191 --- /dev/null +++ b/apps/opml_import/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns('apps.opml_import.views', + (r'^$', 'opml_import'), + (r'^process', 'process'), +) diff --git a/apps/opml_import/views.py b/apps/opml_import/views.py new file mode 100644 index 000000000..ea9dc9595 --- /dev/null +++ b/apps/opml_import/views.py @@ -0,0 +1,44 @@ +from django.shortcuts import render_to_response, get_list_or_404, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.template import RequestContext +from apps.rss_feeds.models import Feed, Story +from apps.reader.models import UserSubscription, ReadStories, UserSubscriptionFolders +from utils.json import json_encode +import utils.opml as opml +from django.contrib.auth.models import User +from django.http import HttpResponse, HttpRequest +from django.core import serializers +from pprint import pprint +import datetime + + +def opml_import(request): + context = None + return render_to_response('opml_import/import.xhtml', context, + context_instance=RequestContext(request)) + +def process(request): + context = None + outline = opml.from_string(request.POST['opml']) + feeds = [] + for folder in outline: + for feed in folder: + feed_data = dict(feed_address=feed.xmlUrl, feed_link=feed.htmlUrl, feed_title=feed.title) + feeds.append(feed_data) + new_feed = Feed(**feed_data) + try: + new_feed.save() + except: + new_feed = Feed.objects.get(**feed_data) + us = UserSubscription(feed=new_feed, user=request.user) + try: + us.save() + except: + us = UserSubscription.objects.get(feed=new_feed, user=request.user) + user_sub_folder = UserSubscriptionFolders(user=request.user, feed=new_feed, user_sub=us, folder=folder.text) + try: + user_sub_folder.save() + except: + pass + data = json_encode(feeds) + return HttpResponse(data, mimetype='application/json') \ No newline at end of file diff --git a/apps/profile/__init__.py b/apps/profile/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/profile/models.py b/apps/profile/models.py new file mode 100644 index 000000000..4a86bfe72 --- /dev/null +++ b/apps/profile/models.py @@ -0,0 +1,5 @@ +from django.db import models +from django.contrib.auth.models import User + +class UserProfile(models.Model): + user = models.ForeignKey(User, unique=True) \ No newline at end of file diff --git a/apps/profile/views.py b/apps/profile/views.py new file mode 100644 index 000000000..60f00ef0e --- /dev/null +++ b/apps/profile/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/apps/reader/__init__.py b/apps/reader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/reader/admin.py b/apps/reader/admin.py new file mode 100644 index 000000000..e54ee46ab --- /dev/null +++ b/apps/reader/admin.py @@ -0,0 +1,6 @@ +from apps.reader.models import UserSubscription, ReadStories, UserSubscriptionFolders +from django.contrib import admin + +admin.site.register(UserSubscription) +admin.site.register(ReadStories) +admin.site.register(UserSubscriptionFolders) \ No newline at end of file diff --git a/apps/reader/models.py b/apps/reader/models.py new file mode 100644 index 000000000..2c2a7e5d1 --- /dev/null +++ b/apps/reader/models.py @@ -0,0 +1,113 @@ +from django.db import models +from django.contrib.auth.models import User +import datetime +from apps.rss_feeds.models import Feed, Story +from utils import feedparser, object_manager + +class UserSubscription(models.Model): + user = models.ForeignKey(User) + feed = models.ForeignKey(Feed) + last_read_date = models.DateTimeField(default=datetime.datetime(2000,1,1)) + mark_read_date = models.DateTimeField(default=datetime.datetime(2000,1,1)) + unread_count = models.IntegerField(default=0) + unread_count_updated = models.DateTimeField( + default=datetime.datetime(2000,1,1) + ) + + def __unicode__(self): + return self.user.username + ': [' + self.feed.feed_title + '] ' + + def save(self, force_insert=False, force_update=False): + self.unread_count_updated = datetime.datetime.now() + super(UserSubscription, self).save(force_insert, force_update) + + def get_user_feeds(self): + return Feed.objects.get(user=self.user, feed=feeds) + + def count_unread(self): + if self.unread_count_updated > self.feed.last_update: + return self.unread_count + + count = (self.stories_newer_lastread() + + self.stories_between_lastread_allread()) + if count == 0: + self.mark_read_date = datetime.datetime.now() + self.last_read_date = datetime.datetime.now() + self.unread_count_updated = datetime.datetime.now() + self.unread_count = 0 + self.save() + else: + self.unread_count = count + self.unread_count_updated = datetime.datetime.now() + self.save() + return count + + def mark_read(self): + self.last_read_date = datetime.datetime.now() + self.unread_count -= 1 + self.unread_count_updated = datetime.datetime.now() + self.save() + + def mark_feed_read(self): + self.last_read_date = datetime.datetime.now() + self.mark_read_date = datetime.datetime.now() + self.unread_count = 0 + self.unread_count_updated = datetime.datetime.now() + self.save() + readstories = ReadStories.objects.filter(user=self.user, feed=self.feed) + readstories.delete() + + def stories_newer_lastread(self): + return self.feed.new_stories_since_date(self.last_read_date) + + def stories_between_lastread_allread(self): + story_count = Story.objects.filter( + story_date__gte=self.mark_read_date, + story_date__lte=self.last_read_date, + story_feed=self.feed + ).count() + read_count = ReadStories.objects.filter( + feed=self.feed, + read_date__gte=self.mark_read_date, + read_date__lte=self.last_read_date + ).count() + return story_count - read_count + + def subscribe_to_feed(self, feed_id): + feed = Feed.objects.get(id=feed_id) + new_subscription = UserSubscription(user=self.user, feed=feed) + new_subscription.save() + + class Meta: + unique_together = ("user", "feed") + + +class ReadStories(models.Model): + user = models.ForeignKey(User) + feed = models.ForeignKey(Feed) + story = models.ForeignKey(Story) + read_date = models.DateTimeField(auto_now=True) + + def __unicode__(self): + return (self.user.username + ': [' + self.feed.feed_title + '] ' + + self.story.story_title) + + class Meta: + verbose_name_plural = "read stories" + verbose_name = "read story" + unique_together = ("user", "story") + +class UserSubscriptionFolders(models.Model): + user = models.ForeignKey(User) + user_sub = models.ForeignKey(UserSubscription) + feed = models.ForeignKey(Feed) + folder = models.CharField(max_length=255) + + def __unicode__(self): + return (self.user.username + ': [' + self.feed.feed_title + '] ' + + self.folder) + + class Meta: + verbose_name_plural = "folders" + verbose_name = "folder" + unique_together = ("user", "user_sub") \ No newline at end of file diff --git a/apps/reader/urls.py b/apps/reader/urls.py new file mode 100644 index 000000000..29fb95148 --- /dev/null +++ b/apps/reader/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns('apps.reader.views', + (r'^$', 'index'), + (r'^load_single_feed', 'load_single_feed'), + (r'^load_feeds', 'load_feeds'), + (r'^refresh_all_feeds', 'refresh_all_feeds'), + (r'^refresh_feed', 'refresh_feed'), + (r'^mark_story_as_read', 'mark_story_as_read'), + (r'^mark_feed_as_read', 'mark_feed_as_read'), + (r'^get_read_feed_items', 'get_read_feed_items'), +) diff --git a/apps/reader/views.py b/apps/reader/views.py new file mode 100644 index 000000000..e8bbe0dd7 --- /dev/null +++ b/apps/reader/views.py @@ -0,0 +1,200 @@ +from django.shortcuts import render_to_response, get_list_or_404, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.template import RequestContext +from apps.rss_feeds.models import Feed, Story +from apps.reader.models import UserSubscription, ReadStories, UserSubscriptionFolders +from utils.json import json_encode +from utils.story_functions import format_story_link_date__short, format_story_link_date__long +from utils.user_functions import get_user +from django.contrib.auth.models import User +from django.http import HttpResponse, HttpRequest +from django.core import serializers +from pprint import pprint +from django.utils.safestring import mark_safe +from utils.feedcache.threading_model import fetch_feeds +import datetime +import threading + +def index(request): + # feeds = Feed.objects.filter(usersubscription__user=request.user) + # for f in feeds: + # f.update() + + # context = feeds + context = {} + + user = request.user + user_info = _parse_user_info(user) + context.update(user_info) + return render_to_response('reader/feeds.xhtml', context, + context_instance=RequestContext(request)) + +def refresh_all_feeds(request): + force_update = False # request.GET.get('force', False) + feeds = Feed.objects.all() + + t = threading.Thread(target=refresh_feeds, + args=[feeds]) + t.setDaemon(True) + t.start() + + # feeds = fetch_feeds(force_update, feeds) + + context = {} + + user = request.user + user_info = _parse_user_info(user) + context.update(user_info) + + return render_to_response('reader/feeds.xhtml', context, + context_instance=RequestContext(request)) + +def refresh_feed(request): + feed_id = request.REQUEST['feed_id'] + force_update = request.GET.get('force', False) + feeds = Feed.objects.filter(id=feed_id) + + feeds = fetch_feeds(force_update, feeds) + + context = {} + + user = request.user + user_info = _parse_user_info(user) + context.update(user_info) + + return render_to_response('reader/feeds.xhtml', context, + context_instance=RequestContext(request)) + +def refresh_feeds(feeds): + for f in feeds: + f.update() + return + +def load_feeds(request): + user = get_user(request) + + us = UserSubscriptionFolders.objects.select_related().filter( + user=user + ) + + feeds = [] + folders = [] + for sub in us: + sub.feed.unread_count = sub.user_sub.count_unread() + if sub.folder not in folders: + folders.append(sub.folder) + feeds.append({'folder': sub.folder, 'feeds': []}) + for folder in feeds: + if folder['folder'] == sub.folder: + folder['feeds'].append(sub.feed) + + # Alphabetize folders, then feeds inside folders + feeds.sort(lambda x, y: cmp(x['folder'].lower(), y['folder'].lower())) + for feed in feeds: + feed['feeds'].sort(lambda x, y: cmp(x.feed_title.lower(), y.feed_title.lower())) + for f in feed['feeds']: + f.feed_address = mark_safe(f.feed_address) + + context = feeds + + data = json_encode(context) + return HttpResponse(data, mimetype='application/json') + +def load_single_feed(request): + user = get_user(request) + + offset = int(request.REQUEST.get('offset', 0)) + limit = int(request.REQUEST.get('limit', 25)) + feed_id = request.REQUEST['feed_id'] + stories=Story.objects.filter(story_feed=feed_id)[offset:offset+limit] + feed = Feed.objects.get(id=feed_id) + force_update = request.GET.get('force', False) + + if force_update: + fetch_feeds(force_update, [feed]) + + us = UserSubscription.objects.select_related("feed").filter(user=user) + for sub in us: + if sub.feed_id == feed.id: + + print "Feed: " + feed.feed_title + user_readstories = ReadStories.objects.filter( + user=user, + feed=feed + ) + for story in stories: + story.short_parsed_date = format_story_link_date__short(story.story_date) + story.long_parsed_date = format_story_link_date__long(story.story_date) + story.story_feed_title = feed.feed_title + story.story_feed_link = mark_safe(feed.feed_link) + story.story_permalink = mark_safe(story.story_permalink) + if story.story_date < sub.mark_read_date: + story.read_status = 1 + elif story.story_date > sub.last_read_date: + story.read_status = 0 + else: + if story.id in [u_rs.story_id for u_rs in user_readstories]: + print "READ: " + story.read_status = 1 + else: + print "unread: " + story.read_status = 0 + + context = stories + data = json_encode(context) + return HttpResponse(data, mimetype='text/plain') + + +@login_required +def mark_story_as_read(request): + story_id = request.REQUEST['story_id'] + story = Story.objects.select_related("story_feed").get(id=story_id) + + read_story = ReadStories.objects.filter(story=story_id, user=request.user, feed=story.story_feed).count() + + print read_story + if read_story: + data = json_encode(dict(code=1)) + else: + us = UserSubscription.objects.get( + feed=story.story_feed, + user=request.user + ) + us.mark_read() + print "Marked Read: " + str(story_id) + ' ' + str(story.id) + m = ReadStories(story=story, user=request.user, feed=story.story_feed) + data = json_encode(dict(code=0)) + try: + m.save() + except: + data = json_encode(dict(code=2)) + return HttpResponse(data) + +@login_required +def mark_feed_as_read(request): + feed_id = int(request.REQUEST['feed_id']) + feed = Feed.objects.get(id=feed_id) + + us = UserSubscription.objects.get(feed=feed, user=request.user) + us.mark_feed_read() + + ReadStories.objects.filter(user=request.user, feed=feed_id).delete() + data = json_encode(dict(code=0)) + try: + m.save() + except: + data = json_encode(dict(code=1)) + return HttpResponse(data) + +@login_required +def get_read_feed_items(request, username): + feeds = get_list_or_404(Feed) + +def _parse_user_info(user): + return { + 'user_info': { + 'is_anonymous': json_encode(user.is_anonymous()), + 'is_authenticated': json_encode(user.is_authenticated()), + 'username': user.username if user.is_authenticated() else 'Anonymous' + } + } \ No newline at end of file diff --git a/apps/registration/__init__.py b/apps/registration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/registration/admin.py b/apps/registration/admin.py new file mode 100644 index 000000000..eacda3e79 --- /dev/null +++ b/apps/registration/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +from apps.registration.models import RegistrationProfile + + +class RegistrationAdmin(admin.ModelAdmin): + list_display = ('__unicode__', 'activation_key_expired') + search_fields = ('user__username', 'user__first_name') + + +admin.site.register(RegistrationProfile, RegistrationAdmin) diff --git a/apps/registration/docs/forms.txt b/apps/registration/docs/forms.txt new file mode 100644 index 000000000..45898b6e2 --- /dev/null +++ b/apps/registration/docs/forms.txt @@ -0,0 +1,82 @@ +===== +Forms +===== + + +To ease and automate the process of validating user information during +registration, several form classes (built using Django's `newforms +library`_) are provided: a base ``RegistrationForm`` and subclasses +which provide specific customized functionality. All of the forms +described below are found in ``registration.forms``. + +.. _newforms library: http://www.djangoproject.com/documentation/newforms/ + + +``RegistrationForm`` +==================== + +Form for registering a new user account. + +Validates that the requested username is not already in use, and +requires the password to be entered twice to catch typos. + +Subclasses should feel free to add any additional validation they +need, but should either preserve the base ``save()`` or implement a +``save()`` which accepts the ``profile_callback`` keyword argument and +passes it through to +``RegistrationProfile.objects.create_inactive_user()``. + +Fields: + +``username`` + The new user's requested username. Will be validated according to + the same regular expression Django's authentication system uses to + validate usernames. + +``email`` + The new user's email address. Must be a well-formed email address. + +``password1`` + The new user's password. + +``password2`` + The password, again, to catch typos. + + +Non-validation methods: + +``save()`` + Creates the new ``User`` and ``RegistrationProfile``, and returns + the ``User``. + + This is essentially a light wrapper around + ``RegistrationProfile.objects.create_inactive_user()``, feeding it + the form data and a profile callback (see the documentation on + ``create_inactive_user()`` for details) if supplied. + + +Subclasses of ``RegistrationForm`` +================================== + +As explained above, subclasses may add any additional validation they +like, but must either preserve the ``save()`` method or implement a +``save()`` method with an identical signature. + +Three subclasses are included as examples, and as ready-made +implementations of useful customizations: + +``RegistrationFormTermsOfService`` + Subclass of ``RegistrationForm`` which adds a required checkbox + for agreeing to a site's Terms of Service. + +``RegistrationFormUniqueEmail`` + Subclass of ``RegistrationForm`` which enforces uniqueness of + email addresses. + +``RegistrationFormNoFreeEmail`` + Subclass of ``RegistrationForm`` which disallows registration with + email addresses from popular free webmail services; moderately + useful for preventing automated spam registrations. + + To change the list of banned domains, subclass this form and + override the attribute ``bad_domains``. diff --git a/apps/registration/docs/models.txt b/apps/registration/docs/models.txt new file mode 100644 index 000000000..7995dfbc8 --- /dev/null +++ b/apps/registration/docs/models.txt @@ -0,0 +1,174 @@ +=================== +Models and managers +=================== + + +Because the two-step process of registration and activation requires +some means of temporarily storing activation key and retrieving it for +verification, a simple model -- +``registration.models.RegistrationProfile`` -- is provided in this +application, and a custom manager -- +``registration.models.RegistrationManager`` -- is included and defines +several useful methods for interacting with ``RegistrationProfile``. + +Both the ``RegistrationProfile`` model and the ``RegistrationManager`` +are found in ``registration.models``. + + +The ``RegistrationProfile`` model +================================= + +A simple profile which stores an activation key for use during user +account registration. + +Generally, you will not want to interact directly with instances of +this model; the provided manager includes methods for creating and +activating new accounts, as well as for cleaning out accounts which +have never been activated. + +While it is possible to use this model as the value of the +``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do +so. This model's sole purpose is to store data temporarily during +account registration and activation, and a mechanism for automatically +creating an instance of a site-specific profile model is provided via +the ``create_inactive_user`` on ``RegistrationManager``. + +``RegistrationProfile`` objects have the following fields: + +``activation_key`` + A SHA1 hash used as an account's activation key. + +``user`` + The ``User`` object for which activation information is being + stored. + +``RegistrationProfile`` also has one custom method defined: + +``activation_key_expired()`` + Determines whether this ``RegistrationProfile``'s activation key + has expired. + + Returns ``True`` if the key has expired, ``False`` otherwise. + + Key expiration is determined by a two-step process: + + 1. If the user has already activated, the key will have been reset + to the string ``ALREADY_ACTIVATED``. Re-activating is not + permitted, and so this method returns ``True`` in this case. + + 2. Otherwise, the date the user signed up is incremented by the + number of days specified in the setting + ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of days + after signup during which a user is allowed to activate their + account); if the result is less than or equal to the current + date, the key has expired and this method returns ``True``. + + +The ``RegistrationManager`` +=========================== + +Custom manager for the ``RegistrationProfile`` model. + +The methods defined here provide shortcuts for account creation and +activation (including generation and emailing of activation keys), and +for cleaning out expired inactive accounts. + +Methods: + +``activate_user(activation_key)`` + Validates an activation key and activates the corresponding + ``User`` if valid. + + If the key is valid and has not expired, returns the ``User`` + after activating. + + If the key is not valid or has expired, returns ``False``. + + If the key is valid but the ``User`` is already active, returns + ``False``. + + To prevent reactivation of an account which has been deactivated + by site administrators, the activation key is reset to the string + ``ALREADY_ACTIVATED`` after successful activation. + +``create_inactive_user(username, password, email, send_email=True, profile_callback=None)`` + Creates a new, inactive ``User``, generates a + ``RegistrationProfile`` and emails its activation key to the + ``User``. Returns the new ``User``. + + To disable the email, call with ``send_email=False``. + + The activation email will make use of two templates: + + ``registration/activation_email_subject.txt`` + This template will be used for the subject line of the + email. It receives one context variable, ``site``, which is + the currently-active ``django.contrib.sites.models.Site`` + instance. Because it is used as the subject line of an email, + this template's output **must** be only a single line of text; + output longer than one line will be forcibly joined into only + a single line. + + ``registration/activation_email.txt`` + This template will be used for the body of the email. It will + receive three context variables: ``activation_key`` will be + the user's activation key (for use in constructing a URL to + activate the account), ``expiration_days`` will be the number + of days for which the key will be valid and ``site`` will be + the currently-active ``django.contrib.sites.models.Site`` + instance. + + To enable creation of a custom user profile along with the + ``User`` (e.g., the model specified in the ``AUTH_PROFILE_MODULE`` + setting), define a function which knows how to create and save an + instance of that model with appropriate default values, and pass + it as the keyword argument ``profile_callback``. This function + should accept one keyword argument: + + ``user`` + The ``User`` to relate the profile to. + +``create_profile(user)`` + Creates a ``RegistrationProfile`` for a given ``User``. Returns + the ``RegistrationProfile``. + + The activation key for the ``RegistrationProfile`` will be a SHA1 + hash, generated from a combination of the ``User``'s username and + a random salt. + +``deleted_expired_users()`` + Removes expired instances of ``RegistrationProfile`` and their + associated ``User`` objects. + + Accounts to be deleted are identified by searching for instances + of ``RegistrationProfile`` with expired activation keys, and then + checking to see if their associated ``User`` instances have the + field ``is_active`` set to ``False``; any ``User`` who is both + inactive and has an expired activation key will be deleted. + + It is recommended that this method be executed regularly as part + of your routine site maintenance; this application provides a + custom management command which will call this method, accessible + as ``manage.py cleanupregistration``. + + Regularly clearing out accounts which have never been activated + serves two useful purposes: + + 1. It alleviates the ocasional need to reset a + ``RegistrationProfile`` and/or re-send an activation email when + a user does not receive or does not act upon the initial + activation email; since the account will be deleted, the user + will be able to simply re-register and receive a new activation + key. + + 2. It prevents the possibility of a malicious user registering one + or more accounts and never activating them (thus denying the + use of those usernames to anyone else); since those accounts + will be deleted, the usernames will become available for use + again. + + If you have a troublesome ``User`` and wish to disable their + account while keeping it in the database, simply delete the + associated ``RegistrationProfile``; an inactive ``User`` which + does not have an associated ``RegistrationProfile`` will not be + deleted. diff --git a/apps/registration/docs/overview.txt b/apps/registration/docs/overview.txt new file mode 100644 index 000000000..87264d29d --- /dev/null +++ b/apps/registration/docs/overview.txt @@ -0,0 +1,337 @@ +=================== +Django registration +=================== + + +This is a fairly simple user-registration application for Django_, +designed to make allowing user signups as painless as possible. + +.. _Django: http://www.djangoproject.com/ + + +Overview +======== + +This application enables a common user-registration workflow: + +1. User fills out a registration form, selecting a username and + password and entering an email address. + +2. An inactive account is created, and an activation link is sent to + the user's email address. + +3. User clicks the activation link, the account becomes active and the + user is able to log in and begin contributing to your site. + +Various methods of extending and customizing the registration process +are also provided. + + +Installation +============ + +In order to use django-registration, you will need to have a +functioning installation of Django 1.0 or newer; due to changes needed +to stabilize Django's APIs prior to the 1.0 release, +django-registration will not work with older releases of Django. + +There are three basic ways to install django-registration: +automatically installing a package using Python's package-management +tools, manually installing a package, and installing from a Mercurial + + +Using a package-management tool +------------------------------- + +The easiest way by far to install django-registration and most other +interesting Python software is by using an automated +package-management tool, so if you're not already familiar with the +available tools for Python, now's as good a time as any to get +started. + +The most popular option currently is `easy_install`_; refer to its +documentation to see how to get it set up. Once you've got it, you'll +be able to simply type:: + + easy_install django-registration + +And it will handle the rest. + +Another option that's currently gaining steam (and which I personally +prefer for Python package management) is `pip`_. Once again, you'll +want to refer to its documentation to get up and running, but once you +have you'll be able to type:: + + pip install django-registration + +And you'll be done. + + +Manually installing the 0.7 package +----------------------------------- + +If you'd prefer to do things the old-fashioned way, you can manually +download the `django-registration 0.7 package`_ from the Python +Package Index. This will get you a file named +"django-registration-0.7.tar.gz" which you can unpack (double-click on +the file on most operating systems) to create a directory named +"django-registration-0.7". Inside will be a script named "setup.py"; +running:: + + python setup.py install + +will install django-registration (though keep in mind that this +defaults to a system-wide installation, and so may require +administrative privileges on your computer). + + +Installing from a Mercurial checkout +------------------------------------ + +If you have `Mercurial`_ installed on your computer, you can also +obtain a complete copy of django-registration by typing:: + + hg clone http://bitbucket.org/ubernostrum/django-registration/ + +Inside the resulting "django-registration" directory will be a +directory named "registration", which is the actual Python module for +this application; you can symlink it from somewhere on your Python +path. If you prefer, you can use the setup.py script in the +"django-registration" directory to perform a normal installation, but +using a symlink offers easy upgrades: simply running ``hg pull -u`` +inside the django-registration directory will fetch updates from the +main repository and apply them to your local copy. + + +.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall +.. _pip: http://pypi.python.org/pypi/pip/ +.. _django-registration 0.7 package: http://pypi.python.org/pypi/django-registration/0.7 +.. _Mercurial: http://www.selenic.com/mercurial/wiki/ + + +Basic use +========= + +To use the registration system with all its default settings, you'll +need to do the following: + +1. Add ``registration`` to the ``INSTALLED_APPS`` setting of your + Django project. + +2. Add the setting ``ACCOUNT_ACTIVATION_DAYS`` to your settings file; + this should be the number of days activation keys will remain valid + after an account is registered. + +3. Create the necessary templates (see the section on templates below + for details). + +4. Add this line to your site's root URLConf:: + + (r'^accounts/', include('registration.urls')), + +5. Link people to ``/accounts/register/`` so they can start signing + up. Using the default URLConf will also automatically set up the + authentication-oriented views in ``django.contrib.auth`` for you, + so if you use it you can point people to, e.g., + ``/accounts/login/`` to log in. + + +Templates used by django-registration +===================================== + +The views included in django-registration make use of five templates: + +* ``registration/registration_form.html`` displays the registration + form for users to sign up. + +* ``registration/registration_complete.html`` is displayed after the + activation email has been sent, to tell the new user to check + his/her email. + +* ``registration/activation_email_subject.txt`` is used for the + subject of the activation email. + +* ``registration/activation_email.txt`` is used for the body of the + activation email. + +* ``registration/activate.html`` is displayed when a user attempts to + activate his/her account. + +Examples of all of these templates are not provided; you will need to +create them yourself. For views defined in this application, see the +included `views documentation`_ for details on available context +variables, and for details on the templates used by the activation +email see the included `models documentation`_. + +Additionally, the URLConf provided with django-registration includes +URL patterns for useful views in Django's built-in authentication +application -- this means that a single ``include`` in your root +URLConf can wire up registration and the auth application's login, +logout, and password change/reset views. If you choose to use these +views you will need to provide your own templates for them; consult +`the Django authentication documentation`_ for details on the +templates and contexts used by these views. + +.. _views documentation: views.txt +.. _models documentation: models.txt +.. _the Django authentication documentation: http://www.djangoproject.com/documentation/authentication/ + + +How it works +============ + +Using the recommended default configuration, the URL +``/accounts/register/`` will map to the view +``registration.views.register``, which displays a registration form +(an instance of ``registration.forms.RegistrationForm``); this form +asks for a username, email address and password, and verifies that the +username is available and requires the password to be entered twice +(to catch typos). It then does three things: + +1. Creates an instance of ``django.contrib.models.auth.User``, using + the supplied username, email address and password; the + ``is_active`` field on the new ``User`` will be set to ``False``, + meaning that the account is inactive and the user will not be able + to log in yet. + +2. Creates an instance of ``registration.models.RegistrationProfile``, + stores an activation key (a SHA1 hash generated from the new user's + username plus a randomly-generated "salt"), and relates that + ``RegistrationProfile`` to the ``User`` it just created. + +3. Sends an email to the user (at the address they supplied) + containing a link which can be clicked to activate the account. + +For details on customizing this process, including use of alternate +registration form classes and automatic creation of a site-specific +profile, see the sections on customization below. + +After the activation email has been sent, +``registration.views.register`` issues a redirect to the URL +``/accounts/register/complete/``. By default, this is mapped to the +``direct_to_template`` generic view, and displays the template +``registration/registration_complete.html``; this is intended to show +a short message telling the user to check his/her email for the +activation link. + +The activation link will map to the view +``registration.views.activate``, which will attempt to activate the +account by setting the ``is_active`` field on the ``User`` to +``True``. If the activation key for the ``User`` has expired (this is +controlled by the setting ``ACCOUNT_ACTIVATION_DAYS``, as described +above), the account will not be activated (see the section on +maintenance below for instructions on cleaning out expired accounts +which have not been activated). + + +Maintenance +=========== + +Inevitably, a site which uses a two-step process for user signup -- +registration followed by activation -- will accumulate a certain +number of accounts which were registered but never activated. These +accounts clutter up the database and tie up usernames which might +otherwise be actively used, so it's desirable to clean them out +periodically. For this purpose, a script, +``registration/bin/deleted_expired_users.py``, is provided, which is +suitable for use as a regular cron job. See that file for notes on how +to add it to your crontab, and the included models documentation (see +below) for discussion of how it works and some caveats. + + +Where to go from here +===================== + +Full documentation for all included components is bundled in the +packaged release; see the following files for details: + +* `Forms documentation`_ for details on ``RegistrationForm``, + pre-packaged subclasses and available customizations. + +* `Models documentation`_ for details on ``RegistrationProfile`` and + its custom manager. + +* `Views documentation`_ for details on the ``register`` and + ``activate`` views, and methods for customizing them. + +.. _Forms documentation: forms.txt +.. _Models documentation: models.txt +.. _Views documentation: views.txt + + +Development +=========== + +The `latest released version`_ of this application is 0.7, and is +quite stable; it's already been deployed on a number of sites, +including djangoproject.com. You can also obtain the absolute freshest +code from `the development repository_`, but be warned that the +development code may not always be backwards-compatible, and may well +contain bugs that haven't yet been fixed. + +This document covers the 0.7 release of django-registration; new +features introduced in the development trunk will be added to the +documentation at the time of the next packaged release. + +.. _latest released version: http://pypi.python.org/pypi/django-registration/0.7 +.. _the development repository: http://www.bitbucket.org/ubernostrum/django-registration/src/ + + +Changes from previous versions +============================== + +Several new features were added between version 0.2 and version 0.3; +for details, see the CHANGELOG.txt file distributed with the packaged +0.3 release. + +One important change to note before upgrading an installation of +version 0.1 is a change to the ``RegistrationProfile`` model; the +field ``key_generated`` has been removed, since it was redundant with +the field ``date_joined`` on Django's bundled ``User`` model. Since +this field became a ``NOT NULL`` column in the database, you will need +to either drop the ``NOT NULL`` constraint or, preferably, simply drop +the column. Consult your database's documentation for the correct way +to handle this. + +Between version 0.3 and version 0.4, validation of the password fields +was moved from ``clean_password2()`` to ``clean_password()``; this +means that errors from mismatched passwords will now show up in +``non_field_errors()`` instead of ``errors["password2"]``. + +Between version 0.6 and version 0.7, the script +``registration/bin/delete_expired_users.py`` was removed, and replaced +with a custom management command; you can now simply run ``manage.py +cleanupregistration`` from any project which has django-registration +installed. + + +Dependencies +============ + +The only dependencies for this application are a functioning install +of Django 1.0 or newer and, of course, a Django project in which you'd +like to use it. + +Your Django project should have ``django.contrib.admin``, +``django.contrib.auth`` and ``django.contrib.sites`` in its +``INSTALLED_APPS`` setting. + + +What this application does not do +================================= + +This application does not integrate in any way with OpenID, nor should +it; one of the key selling points of OpenID is that users **don't** +have to walk through an explicit registration step for every site or +service they want to use :) + + +If you spot a bug +================= + +Head over to this application's `project page on Bitbucket`_ and +check `the issues list`_ to see if it's already been reported. If not, +open a new issue and I'll do my best to respond quickly. + +.. _project page on Bitbucket: http://www.bitbucket.org/ubernostrum/django-registration/overview/ +.. _the issues list: http://www.bitbucket.org/ubernostrum/django-registration/issues/ diff --git a/apps/registration/docs/views.txt b/apps/registration/docs/views.txt new file mode 100644 index 000000000..017feef14 --- /dev/null +++ b/apps/registration/docs/views.txt @@ -0,0 +1,120 @@ +===== +Views +===== + + +Two views are included which, between them, handle the process of +first registering and then activating new user accounts; both views +are found in ``registration.views``. + + +``activate`` +============ + +Activate a ``User``'s account from an activation key, if their key is +valid and hasn't expired. + +By default, use the template ``registration/activate.html``; to +change this, pass the name of a template as the keyword argument +``template_name``. + +**Required arguments** + +``activation_key`` + The activation key to validate and use for activating the + ``User``. + +**Optional arguments** + +``extra_context`` + A dictionary of variables to add to the template context. Any + callable object in this dictionary will be called to produce the + end result which appears in the context. + +``template_name`` + A custom template to use. + +**Context:** + +``account`` + The ``User`` object corresponding to the account, if the + activation was successful. ``False`` if the activation was not + successful. + +``expiration_days`` + The number of days for which activation keys stay valid after + registration. + +Any extra variables supplied in the ``extra_context`` argument (see +above). + +**Template:** + +registration/activate.html or ``template_name`` keyword argument. + + +``register`` +============ + +Allow a new user to register an account. + +Following successful registration, issue a redirect; by default, this +will be whatever URL corresponds to the named URL pattern +``registration_complete``, which will be +``/accounts/register/complete/`` if using the included URLConf. To +change this, point that named pattern at another URL, or pass your +preferred URL as the keyword argument ``success_url``. + +By default, ``registration.forms.RegistrationForm`` will be used as +the registration form; to change this, pass a different form class as +the ``form_class`` keyword argument. The form class you specify must +have a method ``save`` which will create and return the new ``User``, +and that method must accept the keyword argument ``profile_callback`` +(see below). + +To enable creation of a site-specific user profile object for the new +user, pass a function which will create the profile object as the +keyword argument ``profile_callback``. See +``RegistrationManager.create_inactive_user`` in the file ``models.py`` +for details on how to write this function. + +By default, use the template ``registration/registration_form.html``; +to change this, pass the name of a template as the keyword argument +``template_name``. + +**Required arguments** + +None. + +**Optional arguments** + +``form_class`` + The form class to use for registration. + +``extra_context`` + A dictionary of variables to add to the template context. Any + callable object in this dictionary will be called to produce the + end result which appears in the context. + +``profile_callback`` + A function which will be used to create a site-specific profile + instance for the new ``User``. + +``success_url`` + The URL to redirect to on successful registration. + +``template_name`` + A custom template to use. + +**Context:** + +``form`` + The registration form. + +Any extra variables supplied in the ``extra_context`` argument (see +above). + +**Template:** + +registration/registration_form.html or ``template_name`` keyword +argument. diff --git a/apps/registration/forms.py b/apps/registration/forms.py new file mode 100644 index 000000000..fdcc60d85 --- /dev/null +++ b/apps/registration/forms.py @@ -0,0 +1,142 @@ +""" +Forms and validation code for user registration. + +""" + + +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User + +from apps.registration.models import RegistrationProfile + + +# I put this on all required fields, because it's easier to pick up +# on them with CSS or JavaScript if they have a class of "required" +# in the HTML. Your mileage may vary. If/when Django ticket #3515 +# lands in trunk, this will no longer be necessary. +attrs_dict = { 'class': 'required' } + + +class RegistrationForm(forms.Form): + """ + Form for registering a new user account. + + Validates that the requested username is not already in use, and + requires the password to be entered twice to catch typos. + + Subclasses should feel free to add any additional validation they + need, but should either preserve the base ``save()`` or implement + a ``save()`` which accepts the ``profile_callback`` keyword + argument and passes it through to + ``RegistrationProfile.objects.create_inactive_user()``. + + """ + username = forms.RegexField(regex=r'^\w+$', + max_length=30, + widget=forms.TextInput(attrs=attrs_dict), + label=_(u'username')) + email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, + maxlength=75)), + label=_(u'email address')) + password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), + label=_(u'password')) + password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), + label=_(u'password (again)')) + + def clean_username(self): + """ + Validate that the username is alphanumeric and is not already + in use. + + """ + try: + user = User.objects.get(username__iexact=self.cleaned_data['username']) + except User.DoesNotExist: + return self.cleaned_data['username'] + raise forms.ValidationError(_(u'This username is already taken. Please choose another.')) + + def clean(self): + """ + Verifiy that the values entered into the two password fields + match. Note that an error here will end up in + ``non_field_errors()`` because it doesn't apply to a single + field. + + """ + if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data: + if self.cleaned_data['password1'] != self.cleaned_data['password2']: + raise forms.ValidationError(_(u'You must type the same password each time')) + return self.cleaned_data + + def save(self, profile_callback=None): + """ + Create the new ``User`` and ``RegistrationProfile``, and + returns the ``User``. + + This is essentially a light wrapper around + ``RegistrationProfile.objects.create_inactive_user()``, + feeding it the form data and a profile callback (see the + documentation on ``create_inactive_user()`` for details) if + supplied. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'], + password=self.cleaned_data['password1'], + email=self.cleaned_data['email'], + profile_callback=profile_callback) + return new_user + + +class RegistrationFormTermsOfService(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which adds a required checkbox + for agreeing to a site's Terms of Service. + + """ + tos = forms.BooleanField(widget=forms.CheckboxInput(attrs=attrs_dict), + label=_(u'I have read and agree to the Terms of Service'), + error_messages={ 'required': u"You must agree to the terms to register" }) + + +class RegistrationFormUniqueEmail(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which enforces uniqueness of + email addresses. + + """ + def clean_email(self): + """ + Validate that the supplied email address is unique for the + site. + + """ + if User.objects.filter(email__iexact=self.cleaned_data['email']): + raise forms.ValidationError(_(u'This email address is already in use. Please supply a different email address.')) + return self.cleaned_data['email'] + + +class RegistrationFormNoFreeEmail(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which disallows registration with + email addresses from popular free webmail services; moderately + useful for preventing automated spam registrations. + + To change the list of banned domains, subclass this form and + override the attribute ``bad_domains``. + + """ + bad_domains = ['aim.com', 'aol.com', 'email.com', 'gmail.com', + 'googlemail.com', 'hotmail.com', 'hushmail.com', + 'msn.com', 'mail.ru', 'mailinator.com', 'live.com'] + + def clean_email(self): + """ + Check the supplied email address against a list of known free + webmail domains. + + """ + email_domain = self.cleaned_data['email'].split('@')[1] + if email_domain in self.bad_domains: + raise forms.ValidationError(_(u'Registration using free email addresses is prohibited. Please supply a different email address.')) + return self.cleaned_data['email'] diff --git a/apps/registration/management/__init__.py b/apps/registration/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/registration/management/commands/__init__.py b/apps/registration/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/registration/management/commands/cleanupregistration.py b/apps/registration/management/commands/cleanupregistration.py new file mode 100644 index 000000000..33f0ad6a1 --- /dev/null +++ b/apps/registration/management/commands/cleanupregistration.py @@ -0,0 +1,19 @@ +""" +A management command which deletes expired accounts (e.g., +accounts which signed up but never activated) from the database. + +Calls ``RegistrationProfile.objects.delete_expired_users()``, which +contains the actual logic for determining which accounts are deleted. + +""" + +from django.core.management.base import NoArgsCommand + +from apps.registration.models import RegistrationProfile + + +class Command(NoArgsCommand): + help = "Delete expired user registrations from the database" + + def handle_noargs(self, **options): + RegistrationProfile.objects.delete_expired_users() diff --git a/apps/registration/models.py b/apps/registration/models.py new file mode 100644 index 000000000..d13a07aac --- /dev/null +++ b/apps/registration/models.py @@ -0,0 +1,250 @@ +import datetime +import random +import re +import sha + +from django.conf import settings +from django.db import models +from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User +from django.contrib.sites.models import Site + + +SHA1_RE = re.compile('^[a-f0-9]{40}$') + + +class RegistrationManager(models.Manager): + """ + Custom manager for the ``RegistrationProfile`` model. + + The methods defined here provide shortcuts for account creation + and activation (including generation and emailing of activation + keys), and for cleaning out expired inactive accounts. + + """ + def activate_user(self, activation_key): + """ + Validate an activation key and activate the corresponding + ``User`` if valid. + + If the key is valid and has not expired, return the ``User`` + after activating. + + If the key is not valid or has expired, return ``False``. + + If the key is valid but the ``User`` is already active, + return ``False``. + + To prevent reactivation of an account which has been + deactivated by site administrators, the activation key is + reset to the string ``ALREADY_ACTIVATED`` after successful + activation. + + """ + # Make sure the key we're trying conforms to the pattern of a + # SHA1 hash; if it doesn't, no point trying to look it up in + # the database. + if SHA1_RE.search(activation_key): + try: + profile = self.get(activation_key=activation_key) + except self.model.DoesNotExist: + return False + if not profile.activation_key_expired(): + user = profile.user + user.is_active = True + user.save() + profile.activation_key = self.model.ACTIVATED + profile.save() + return user + return False + + def create_inactive_user(self, username, password, email, + send_email=True, profile_callback=None): + """ + Create a new, inactive ``User``, generates a + ``RegistrationProfile`` and email its activation key to the + ``User``, returning the new ``User``. + + To disable the email, call with ``send_email=False``. + + The activation email will make use of two templates: + + ``registration/activation_email_subject.txt`` + This template will be used for the subject line of the + email. It receives one context variable, ``site``, which + is the currently-active + ``django.contrib.sites.models.Site`` instance. Because it + is used as the subject line of an email, this template's + output **must** be only a single line of text; output + longer than one line will be forcibly joined into only a + single line. + + ``registration/activation_email.txt`` + This template will be used for the body of the email. It + will receive three context variables: ``activation_key`` + will be the user's activation key (for use in constructing + a URL to activate the account), ``expiration_days`` will + be the number of days for which the key will be valid and + ``site`` will be the currently-active + ``django.contrib.sites.models.Site`` instance. + + To enable creation of a custom user profile along with the + ``User`` (e.g., the model specified in the + ``AUTH_PROFILE_MODULE`` setting), define a function which + knows how to create and save an instance of that model with + appropriate default values, and pass it as the keyword + argument ``profile_callback``. This function should accept one + keyword argument: + + ``user`` + The ``User`` to relate the profile to. + + """ + new_user = User.objects.create_user(username, email, password) + new_user.is_active = False + new_user.save() + + registration_profile = self.create_profile(new_user) + + if profile_callback is not None: + profile_callback(user=new_user) + + if send_email: + from django.core.mail import send_mail + current_site = Site.objects.get_current() + + subject = render_to_string('registration/activation_email_subject.txt', + { 'site': current_site }) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + + message = render_to_string('registration/activation_email.txt', + { 'activation_key': registration_profile.activation_key, + 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS, + 'site': current_site }) + + send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.email]) + return new_user + + def create_profile(self, user): + """ + Create a ``RegistrationProfile`` for a given + ``User``, and return the ``RegistrationProfile``. + + The activation key for the ``RegistrationProfile`` will be a + SHA1 hash, generated from a combination of the ``User``'s + username and a random salt. + + """ + salt = sha.new(str(random.random())).hexdigest()[:5] + activation_key = sha.new(salt+user.username).hexdigest() + return self.create(user=user, + activation_key=activation_key) + + def delete_expired_users(self): + """ + Remove expired instances of ``RegistrationProfile`` and their + associated ``User``s. + + Accounts to be deleted are identified by searching for + instances of ``RegistrationProfile`` with expired activation + keys, and then checking to see if their associated ``User`` + instances have the field ``is_active`` set to ``False``; any + ``User`` who is both inactive and has an expired activation + key will be deleted. + + It is recommended that this method be executed regularly as + part of your routine site maintenance; this application + provides a custom management command which will call this + method, accessible as ``manage.py cleanupregistration``. + + Regularly clearing out accounts which have never been + activated serves two useful purposes: + + 1. It alleviates the ocasional need to reset a + ``RegistrationProfile`` and/or re-send an activation email + when a user does not receive or does not act upon the + initial activation email; since the account will be + deleted, the user will be able to simply re-register and + receive a new activation key. + + 2. It prevents the possibility of a malicious user registering + one or more accounts and never activating them (thus + denying the use of those usernames to anyone else); since + those accounts will be deleted, the usernames will become + available for use again. + + If you have a troublesome ``User`` and wish to disable their + account while keeping it in the database, simply delete the + associated ``RegistrationProfile``; an inactive ``User`` which + does not have an associated ``RegistrationProfile`` will not + be deleted. + + """ + for profile in self.all(): + if profile.activation_key_expired(): + user = profile.user + if not user.is_active: + user.delete() + + +class RegistrationProfile(models.Model): + """ + A simple profile which stores an activation key for use during + user account registration. + + Generally, you will not want to interact directly with instances + of this model; the provided manager includes methods + for creating and activating new accounts, as well as for cleaning + out accounts which have never been activated. + + While it is possible to use this model as the value of the + ``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do + so. This model's sole purpose is to store data temporarily during + account registration and activation, and a mechanism for + automatically creating an instance of a site-specific profile + model is provided via the ``create_inactive_user`` on + ``RegistrationManager``. + + """ + ACTIVATED = u"ALREADY_ACTIVATED" + + user = models.ForeignKey(User, unique=True, verbose_name=_('user')) + activation_key = models.CharField(_('activation key'), max_length=40) + + objects = RegistrationManager() + + class Meta: + verbose_name = _('registration profile') + verbose_name_plural = _('registration profiles') + + def __unicode__(self): + return u"Registration information for %s" % self.user + + def activation_key_expired(self): + """ + Determine whether this ``RegistrationProfile``'s activation + key has expired, returning a boolean -- ``True`` if the key + has expired. + + Key expiration is determined by a two-step process: + + 1. If the user has already activated, the key will have been + reset to the string ``ALREADY_ACTIVATED``. Re-activating is + not permitted, and so this method returns ``True`` in this + case. + + 2. Otherwise, the date the user signed up is incremented by + the number of days specified in the setting + ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of + days after signup during which a user is allowed to + activate their account); if the result is less than or + equal to the current date, the key has expired and this + method returns ``True``. + + """ + expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) + return self.activation_key == self.ACTIVATED or \ + (self.user.date_joined + expiration_date <= datetime.datetime.now()) + activation_key_expired.boolean = True diff --git a/apps/registration/tests.py b/apps/registration/tests.py new file mode 100644 index 000000000..57a67dde3 --- /dev/null +++ b/apps/registration/tests.py @@ -0,0 +1,318 @@ +""" +Unit tests for django-registration. + +These tests assume that you've completed all the prerequisites for +getting django-registration running in the default setup, to wit: + +1. You have ``registration`` in your ``INSTALLED_APPS`` setting. + +2. You have created all of the templates mentioned in this + application's documentation. + +3. You have added the setting ``ACCOUNT_ACTIVATION_DAYS`` to your + settings file. + +4. You have URL patterns pointing to the registration and activation + views, with the names ``registration_register`` and + ``registration_activate``, respectively, and a URL pattern named + 'registration_complete'. + +""" + +import datetime +import sha + +from django.conf import settings +from django.contrib.auth.models import User +from django.core import mail +from django.core import management +from django.core.urlresolvers import reverse +from django.test import TestCase + +from apps.registration import forms +from apps.registration.models import RegistrationProfile + + +class RegistrationTestCase(TestCase): + """ + Base class for the test cases; this sets up two users -- one + expired, one not -- which are used to exercise various parts of + the application. + + """ + def setUp(self): + self.sample_user = RegistrationProfile.objects.create_inactive_user(username='alice', + password='secret', + email='alice@example.com') + self.expired_user = RegistrationProfile.objects.create_inactive_user(username='bob', + password='swordfish', + email='bob@example.com') + self.expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + self.expired_user.save() + + +class RegistrationModelTests(RegistrationTestCase): + """ + Tests for the model-oriented functionality of django-registration, + including ``RegistrationProfile`` and its custom manager. + + """ + def test_new_user_is_inactive(self): + """ + Test that a newly-created user is inactive. + + """ + self.failIf(self.sample_user.is_active) + + def test_registration_profile_created(self): + """ + Test that a ``RegistrationProfile`` is created for a new user. + + """ + self.assertEqual(RegistrationProfile.objects.count(), 2) + + def test_activation_email(self): + """ + Test that user signup sends an activation email. + + """ + self.assertEqual(len(mail.outbox), 2) + + def test_activation(self): + """ + Test that user activation actually activates the user and + properly resets the activation key, and fails for an + already-active or expired user, or an invalid key. + + """ + # Activating a valid user returns the user. + self.failUnlessEqual(RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.sample_user).activation_key).pk, + self.sample_user.pk) + + # The activated user must now be active. + self.failUnless(User.objects.get(pk=self.sample_user.pk).is_active) + + # The activation key must now be reset to the "already activated" constant. + self.failUnlessEqual(RegistrationProfile.objects.get(user=self.sample_user).activation_key, + RegistrationProfile.ACTIVATED) + + # Activating an expired user returns False. + self.failIf(RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.expired_user).activation_key)) + + # Activating from a key that isn't a SHA1 hash returns False. + self.failIf(RegistrationProfile.objects.activate_user('foo')) + + # Activating from a key that doesn't exist returns False. + self.failIf(RegistrationProfile.objects.activate_user(sha.new('foo').hexdigest())) + + def test_account_expiration_condition(self): + """ + Test that ``RegistrationProfile.activation_key_expired()`` + returns ``True`` for expired users and for active users, and + ``False`` otherwise. + + """ + # Unexpired user returns False. + self.failIf(RegistrationProfile.objects.get(user=self.sample_user).activation_key_expired()) + + # Expired user returns True. + self.failUnless(RegistrationProfile.objects.get(user=self.expired_user).activation_key_expired()) + + # Activated user returns True. + RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.sample_user).activation_key) + self.failUnless(RegistrationProfile.objects.get(user=self.sample_user).activation_key_expired()) + + def test_expired_user_deletion(self): + """ + Test that + ``RegistrationProfile.objects.delete_expired_users()`` deletes + only inactive users whose activation window has expired. + + """ + RegistrationProfile.objects.delete_expired_users() + self.assertEqual(RegistrationProfile.objects.count(), 1) + + def test_management_command(self): + """ + Test that ``manage.py cleanupregistration`` functions + correctly. + + """ + management.call_command('cleanupregistration') + self.assertEqual(RegistrationProfile.objects.count(), 1) + + +class RegistrationFormTests(RegistrationTestCase): + """ + Tests for the forms and custom validation logic included in + django-registration. + + """ + def test_registration_form(self): + """ + Test that ``RegistrationForm`` enforces username constraints + and matching passwords. + + """ + invalid_data_dicts = [ + # Non-alphanumeric username. + { + 'data': + { 'username': 'foo/bar', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo' }, + 'error': + ('username', [u"Enter a valid value."]) + }, + # Already-existing username. + { + 'data': + { 'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'secret', + 'password2': 'secret' }, + 'error': + ('username', [u"This username is already taken. Please choose another."]) + }, + # Mismatched passwords. + { + 'data': + { 'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'bar' }, + 'error': + ('__all__', [u"You must type the same password each time"]) + }, + ] + + for invalid_dict in invalid_data_dicts: + form = forms.RegistrationForm(data=invalid_dict['data']) + self.failIf(form.is_valid()) + self.assertEqual(form.errors[invalid_dict['error'][0]], invalid_dict['error'][1]) + + form = forms.RegistrationForm(data={ 'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo' }) + self.failUnless(form.is_valid()) + + def test_registration_form_tos(self): + """ + Test that ``RegistrationFormTermsOfService`` requires + agreement to the terms of service. + + """ + form = forms.RegistrationFormTermsOfService(data={ 'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo' }) + self.failIf(form.is_valid()) + self.assertEqual(form.errors['tos'], [u"You must agree to the terms to register"]) + + form = forms.RegistrationFormTermsOfService(data={ 'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo', + 'tos': 'on' }) + self.failUnless(form.is_valid()) + + def test_registration_form_unique_email(self): + """ + Test that ``RegistrationFormUniqueEmail`` validates uniqueness + of email addresses. + + """ + form = forms.RegistrationFormUniqueEmail(data={ 'username': 'foo', + 'email': 'alice@example.com', + 'password1': 'foo', + 'password2': 'foo' }) + self.failIf(form.is_valid()) + self.assertEqual(form.errors['email'], [u"This email address is already in use. Please supply a different email address."]) + + form = forms.RegistrationFormUniqueEmail(data={ 'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo' }) + self.failUnless(form.is_valid()) + + def test_registration_form_no_free_email(self): + """ + Test that ``RegistrationFormNoFreeEmail`` disallows + registration with free email addresses. + + """ + base_data = { 'username': 'foo', + 'password1': 'foo', + 'password2': 'foo' } + for domain in ('aim.com', 'aol.com', 'email.com', 'gmail.com', + 'googlemail.com', 'hotmail.com', 'hushmail.com', + 'msn.com', 'mail.ru', 'mailinator.com', 'live.com'): + invalid_data = base_data.copy() + invalid_data['email'] = u"foo@%s" % domain + form = forms.RegistrationFormNoFreeEmail(data=invalid_data) + self.failIf(form.is_valid()) + self.assertEqual(form.errors['email'], [u"Registration using free email addresses is prohibited. Please supply a different email address."]) + + base_data['email'] = 'foo@example.com' + form = forms.RegistrationFormNoFreeEmail(data=base_data) + self.failUnless(form.is_valid()) + + +class RegistrationViewTests(RegistrationTestCase): + """ + Tests for the views included in django-registration. + + """ + def test_registration_view(self): + """ + Test that the registration view rejects invalid submissions, + and creates a new user and redirects after a valid submission. + + """ + # Invalid data fails. + response = self.client.post(reverse('registration_register'), + data={ 'username': 'alice', # Will fail on username uniqueness. + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo' }) + self.assertEqual(response.status_code, 200) + self.failUnless(response.context['form']) + self.failUnless(response.context['form'].errors) + + response = self.client.post(reverse('registration_register'), + data={ 'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo' }) + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], 'http://testserver%s' % reverse('registration_complete')) + self.assertEqual(RegistrationProfile.objects.count(), 3) + + def test_activation_view(self): + """ + Test that the activation view activates the user from a valid + key and fails if the key is invalid or has expired. + + """ + # Valid user puts the user account into the context. + response = self.client.get(reverse('registration_activate', + kwargs={ 'activation_key': RegistrationProfile.objects.get(user=self.sample_user).activation_key })) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['account'].pk, self.sample_user.pk) + + # Expired user sets the account to False. + response = self.client.get(reverse('registration_activate', + kwargs={ 'activation_key': RegistrationProfile.objects.get(user=self.expired_user).activation_key })) + self.failIf(response.context['account']) + + # Invalid key gets to the view, but sets account to False. + response = self.client.get(reverse('registration_activate', + kwargs={ 'activation_key': 'foo' })) + self.failIf(response.context['account']) + + # Nonexistent key sets the account to False. + response = self.client.get(reverse('registration_activate', + kwargs={ 'activation_key': sha.new('foo').hexdigest() })) + self.failIf(response.context['account']) diff --git a/apps/registration/urls.py b/apps/registration/urls.py new file mode 100644 index 000000000..17cd97016 --- /dev/null +++ b/apps/registration/urls.py @@ -0,0 +1,72 @@ +""" +URLConf for Django user registration and authentication. + +If the default behavior of the registration views is acceptable to +you, simply use a line like this in your root URLConf to set up the +default URLs for registration:: + + (r'^accounts/', include('registration.urls')), + +This will also automatically set up the views in +``django.contrib.auth`` at sensible default locations. + +But if you'd like to customize the behavior (e.g., by passing extra +arguments to the various views) or split up the URLs, feel free to set +up your own URL patterns for these views instead. If you do, it's a +good idea to use the names ``registration_activate``, +``registration_complete`` and ``registration_register`` for the +various steps of the user-signup process. + +""" + + +from django.conf.urls.defaults import * +from django.views.generic.simple import direct_to_template +from django.contrib.auth import views as auth_views + +from apps.registration.views import activate +from apps.registration.views import register + + +urlpatterns = patterns('apps.registration.views', + # Activation keys get matched by \w+ instead of the more specific + # [a-fA-F0-9]{40} because a bad activation key should still get to the view; + # that way it can return a sensible "invalid key" message instead of a + # confusing 404. + url(r'^activate/(?P\w+)/$', + 'activate', + name='registration_activate'), + url(r'^login/$', + auth_views.login, + {'template_name': 'registration/login.html'}, + name='auth_login'), + url(r'^logout/$', + auth_views.logout, + {'template_name': 'registration/login.html'}, + name='auth_logout'), + url(r'^password/change/$', + auth_views.password_change, + name='auth_password_change'), + url(r'^password/change/done/$', + auth_views.password_change_done, + name='auth_password_change_done'), + url(r'^password/reset/$', + auth_views.password_reset, + name='auth_password_reset'), + url(r'^password/reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', + auth_views.password_reset_confirm, + name='auth_password_reset_confirm'), + url(r'^password/reset/complete/$', + auth_views.password_reset_complete, + name='auth_password_reset_complete'), + url(r'^password/reset/done/$', + auth_views.password_reset_done, + name='auth_password_reset_done'), + url(r'^register/$', + 'register', + name='registration_register'), + url(r'^register/complete/$', + direct_to_template, + {'template': 'registration/registration_complete.html'}, + name='registration_complete'), + ) diff --git a/apps/registration/views.py b/apps/registration/views.py new file mode 100644 index 000000000..945ee8fbb --- /dev/null +++ b/apps/registration/views.py @@ -0,0 +1,164 @@ +""" +Views which allow users to create and activate accounts. + +""" + + +from django.conf import settings +from django.core.urlresolvers import reverse +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import RequestContext + +from apps.registration.forms import RegistrationForm +from apps.registration.models import RegistrationProfile + + +def activate(request, activation_key, + template_name='registration/activate.html', + extra_context=None): + """ + Activate a ``User``'s account from an activation key, if their key + is valid and hasn't expired. + + By default, use the template ``registration/activate.html``; to + change this, pass the name of a template as the keyword argument + ``template_name``. + + **Required arguments** + + ``activation_key`` + The activation key to validate and use for activating the + ``User``. + + **Optional arguments** + + ``extra_context`` + A dictionary of variables to add to the template context. Any + callable object in this dictionary will be called to produce + the end result which appears in the context. + + ``template_name`` + A custom template to use. + + **Context:** + + ``account`` + The ``User`` object corresponding to the account, if the + activation was successful. ``False`` if the activation was not + successful. + + ``expiration_days`` + The number of days for which activation keys stay valid after + registration. + + Any extra variables supplied in the ``extra_context`` argument + (see above). + + **Template:** + + registration/activate.html or ``template_name`` keyword argument. + + """ + activation_key = activation_key.lower() # Normalize before trying anything with it. + account = RegistrationProfile.objects.activate_user(activation_key) + if extra_context is None: + extra_context = {} + context = RequestContext(request) + for key, value in extra_context.items(): + context[key] = callable(value) and value() or value + return render_to_response(template_name, + { 'account': account, + 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS }, + context_instance=context) + + +def register(request, success_url=None, + form_class=RegistrationForm, profile_callback=None, + template_name='registration/registration_form.html', + extra_context=None): + """ + Allow a new user to register an account. + + Following successful registration, issue a redirect; by default, + this will be whatever URL corresponds to the named URL pattern + ``registration_complete``, which will be + ``/accounts/register/complete/`` if using the included URLConf. To + change this, point that named pattern at another URL, or pass your + preferred URL as the keyword argument ``success_url``. + + By default, ``registration.forms.RegistrationForm`` will be used + as the registration form; to change this, pass a different form + class as the ``form_class`` keyword argument. The form class you + specify must have a method ``save`` which will create and return + the new ``User``, and that method must accept the keyword argument + ``profile_callback`` (see below). + + To enable creation of a site-specific user profile object for the + new user, pass a function which will create the profile object as + the keyword argument ``profile_callback``. See + ``RegistrationManager.create_inactive_user`` in the file + ``models.py`` for details on how to write this function. + + By default, use the template + ``registration/registration_form.html``; to change this, pass the + name of a template as the keyword argument ``template_name``. + + **Required arguments** + + None. + + **Optional arguments** + + ``form_class`` + The form class to use for registration. + + ``extra_context`` + A dictionary of variables to add to the template context. Any + callable object in this dictionary will be called to produce + the end result which appears in the context. + + ``profile_callback`` + A function which will be used to create a site-specific + profile instance for the new ``User``. + + ``success_url`` + The URL to redirect to on successful registration. + + ``template_name`` + A custom template to use. + + **Context:** + + ``form`` + The registration form. + + Any extra variables supplied in the ``extra_context`` argument + (see above). + + **Template:** + + registration/registration_form.html or ``template_name`` keyword + argument. + + """ + if request.method == 'POST': + form = form_class(data=request.POST, files=request.FILES) + if form.is_valid(): + new_user = form.save(profile_callback=profile_callback) + # success_url needs to be dynamically generated here; setting a + # a default value using reverse() will cause circular-import + # problems with the default URLConf for this application, which + # imports this file. + return HttpResponseRedirect(success_url or reverse('registration_complete')) + else: + form = form_class() + + if extra_context is None: + extra_context = {} + context = RequestContext(request) + for key, value in extra_context.items(): + context[key] = callable(value) and value() or value + return render_to_response(template_name, + { 'form': form }, + context_instance=context) diff --git a/apps/rss_feeds/__init__.py b/apps/rss_feeds/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/rss_feeds/admin.py b/apps/rss_feeds/admin.py new file mode 100644 index 000000000..3a1e3f96e --- /dev/null +++ b/apps/rss_feeds/admin.py @@ -0,0 +1,6 @@ +from apps.rss_feeds.models import Feed, Story, Tag +from django.contrib import admin + +admin.site.register(Feed) +admin.site.register(Story) +admin.site.register(Tag) \ No newline at end of file diff --git a/apps/rss_feeds/models.py b/apps/rss_feeds/models.py new file mode 100644 index 000000000..916e9c90c --- /dev/null +++ b/apps/rss_feeds/models.py @@ -0,0 +1,244 @@ +from django.db import models +from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType +from django.core.cache import cache +from utils import feedparser, object_manager +from utils.dateutil.parser import parse as dateutil_parse +from utils.feed_functions import encode, prints, mtime +import time, datetime, random +from pprint import pprint +from django.utils.http import urlquote +from django.db.models import Q +from utils.diff import HTMLDiff + +USER_AGENT = 'NewsBlur v1.0 - newsblur.com' + +class Feed(models.Model): + feed_address = models.URLField(max_length=255, verify_exists=True, unique=True) + feed_link = models.URLField(max_length=200, blank=True) + feed_title = models.CharField(max_length=255, blank=True) + active = models.BooleanField(default=True) + num_subscribers = models.IntegerField(default=0) + last_update = models.DateTimeField(auto_now=True, default=0) + min_to_decay = models.IntegerField(default=15) + days_to_trim = models.IntegerField(default=90) + creation = models.DateField(auto_now_add=True) + etag = models.CharField(max_length=50, blank=True) + last_modified = models.DateTimeField(null=True, blank=True) + + + def __unicode__(self): + return self.feed_title + + def last_updated(self): + return time.time() - time.mktime(self.last_update.timetuple()) + + def new_stories_since_date(self, date): + story_count = Story.objects.filter(story_date__gte=date, + story_feed=self).count() + return story_count + + def add_feed(self, feed_address, feed_link, feed_title): + print locals() + + def update(self, force=False, feed=None): + if (self.last_updated() / 60) < (self.min_to_decay + (random.random()*self.min_to_decay)) and not force: + print 'Feed unchanged: ' + self.feed_title + return + + feed_updated, feed = cache.get("feed:" + self.feed_address, (None, None,)) + if feed and not force: + print 'Feed Cached: ' + self.feed_title + if not feed or force: + last_modified = None + now = datetime.datetime.now() + if self.last_modified: + last_modified = datetime.datetime.timetuple(self.last_modified) + if not feed: + print '[%d] Retrieving Feed: %s %s' % (self.id, self.feed_title, last_modified) + feed = feedparser.parse(self.feed_address, + etag=self.etag, + modified=last_modified, + agent=USER_AGENT) + cache.set("feed:" + self.feed_address, (now, feed), + self.min_to_decay * 60 * 5) + + self.last_update = datetime.datetime.now() + + # check for movement or disappearance + if hasattr(feed, 'status'): + if feed.status == 301: + self.feed_url = feed.href + if feed.status == 410: + self.active = False + if feed.status >= 400: + return + + # Fill in optional fields + if not self.feed_title: + self.feed_title = feed.feed.get('title', + feed.feed.get('link', 'No Title')) + if not self.feed_link: + self.feed_link = feed.feed.get('link', 'null:') + + self.etag = feed.get('etag', '') + if not self.etag: + self.etag = '' + + self.last_modified = mtime(feed.get('modified', datetime.datetime.timetuple(datetime.datetime.now()))) + + self.save() + + for story in feed['entries']: + self.save_story(story) + + self.trim_feed(); + + return + + def trim_feed(self): + date_diff = datetime.datetime.now() - datetime.timedelta(self.days_to_trim) + stories = Story.objects.filter(story_feed=self, story_date__lte=date_diff) + for story in stories: + story.story_past_trim_date = True + story.save() + + def save_story(self, story): + story = self._pre_process_story(story) + + if story.get('title'): + story_contents = story.get('content') + if story_contents is not None: + story_content = story_contents[0]['value'] + else: + story_content = story.get('summary') + existing_story = self._exists_story(story) + if not existing_story: + pub_date = datetime.datetime.timetuple(story.get('published')) + print '- New story: %s %s' % (pub_date, story.get('title')) + + s = Story(story_feed = self, + story_date = story.get('published'), + story_title = story.get('title'), + story_content = story_content, + story_author = story.get('author'), + story_permalink = story.get('link') + ) + try: + s.save() + except: + pass + elif existing_story.story_title != story.get('title') \ + or existing_story.story_content != story_content: + # update story + print '- Updated story in feed (%s): %s / %s' % (self.feed_title, len(existing_story.story_content), len(story_content)) + + original_content = None + if existing_story.story_original_content: + original_content = existing_story.story_original_content + else: + original_content = existing_story.story_content + diff = HTMLDiff(original_content, story_content) + print "\t\tDiff: %s %s %s" % diff.getStats() + print '\tExisting title / New: : \n\t\t- %s\n\t\t- %s' % (existing_story.story_title, story.get('title')) + + s = Story(id = existing_story.id, + story_feed = self, + story_date = story.get('published'), + story_title = story.get('title'), + story_content = diff.getDiff(), + story_original_content = original_content, + story_author = story.get('author'), + story_permalink = story.get('link') + ) + try: + s.save() + except: + pass + # else: + # print "Unchanged story: %s " % story.get('title') + + return + + def _exists_story(self, entry): + pub_date = entry['published'] + start_date = pub_date - datetime.timedelta(hours=12) + end_date = pub_date + datetime.timedelta(hours=12) + # print "Dates: %s %s %s" % (pub_date, start_date, end_date) + existing_story = Story.objects.filter( + ( + Q(story_title__iexact=entry['title']) + & Q(story_date__range=(start_date, end_date)) + ) + | ( + Q(story_permalink__iexact=entry['link']) + & Q(story_date__range=(start_date, end_date)) + ), + story_feed = self + ) + if len(existing_story): + return existing_story[0] + else: + return None + + def _pre_process_story(self, entry): + date_published = entry.get('published', entry.get('updated')) + if not date_published: + date_published = str(datetime.datetime.now()) + date_published = dateutil_parse(date_published) + # Change the date to UTC and remove timezone info since + # MySQL doesn't support it. + timezone_diff = datetime.datetime.utcnow() - datetime.datetime.now() + date_published_offset = date_published.utcoffset() + if date_published_offset: + date_published = (date_published - date_published_offset + - timezone_diff).replace(tzinfo=None) + else: + date_published = date_published.replace(tzinfo=None) + + entry['published'] = date_published + + protocol_index = entry['link'].find("://") + if protocol_index != -1: + entry['link'] = (entry['link'][:protocol_index+3] + + urlquote(entry['link'][protocol_index+3:])) + else: + entry['link'] = urlquote(entry['link']) + return entry + + class Meta: + db_table="feeds" + ordering=["feed_title"] + +class Tag(models.Model): + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + + def save(self): + super(Tag, self).save() + +class Story(models.Model): + '''A feed item''' + story_feed = models.ForeignKey(Feed) + story_date = models.DateTimeField() + story_title = models.CharField(max_length=255) + story_content = models.TextField(null=True, blank=True) + story_original_content = models.TextField(null=True, blank=True) + story_content_type = models.CharField(max_length=255, null=True, + blank=True) + story_author = models.CharField(max_length=255, null=True, blank=True) + story_permalink = models.CharField(max_length=1000) + story_past_trim_date = models.BooleanField(default=False) + tags = models.ManyToManyField(Tag) + + def __unicode__(self): + return self.story_title + + class Meta: + verbose_name_plural = "stories" + verbose_name = "story" + db_table="stories" + ordering=["-story_date", "story_feed"] + \ No newline at end of file diff --git a/apps/rss_feeds/tests.py b/apps/rss_feeds/tests.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/rss_feeds/views.py b/apps/rss_feeds/views.py new file mode 100644 index 000000000..60f00ef0e --- /dev/null +++ b/apps/rss_feeds/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/manage.py b/manage.py new file mode 100755 index 000000000..5e78ea979 --- /dev/null +++ b/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +from django.core.management import execute_manager +try: + import settings # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) + sys.exit(1) + +if __name__ == "__main__": + execute_manager(settings) diff --git a/media/css/reader.css b/media/css/reader.css new file mode 100644 index 000000000..44a682b0c --- /dev/null +++ b/media/css/reader.css @@ -0,0 +1,509 @@ +/* ========== */ +/* = Global = */ +/* ========== */ + +body { + /*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; + font-family: 'Lucida Grande',Helvetica, Arial; + font-size:62%; +} + +a, a:active, a:hover, a:visited, button { + outline: none; +} + +a img { + border: none; +} + +/* =================== */ +/* = Resize Controls = */ +/* =================== */ + +.ui-layout-resizer-west { + background: #e0e0e0 url(../img/reader/resize_west_small.png) repeat-y 50% 50%; +} + +.ui-layout-resizer-north { + background: #e0e0e0 url(../img/reader/resize_north.png) repeat-x 50% 50%; +} + +/* ============= */ +/* = Feed List = */ +/* ============= */ + +#feed_list { + position: fixed; + left: 0px; + top: 0px; + width: 220px; + background-color: #D7DDE6; + border-right: 1px solid #808080; + z-index: 20; + font-size: 1.05em; + overflow-y: auto; +} + +#feed_list .folder { + margin: 6px 0px 4px; + background: transparent url(../img/icons/silk/folder.png) no-repeat 3px 1px; +} + +#feed_list .folder .folder_title { + padding: 3px 0px 4px 22px; + font-weight: bold; + display: block; + color: #404040; + text-transform: uppercase; +} + +#feed_list .feed { + position: relative; + cursor: pointer; + border-top: 1px solid #D7DDE6; + border-bottom: 1px solid #D7DDE6; +} + +#feed_list .feed_id { + display: none; +} +#feed_list img.feed_favicon { + position: absolute; + top: 2px; + left: 24px; +} +#feed_list .feed_title { + display: block; + font-weight: bold; + padding: 4px 42px 2px 45px; + text-decoration: none; + color: #272727; + line-height: 1.3em; +} + +#feed_list .feed.no_unread_items .feed_title { + font-weight: normal; +} + +#feed_list .feed.selected { + background: #f6a828 url(../theme/images/ui-bg_highlight-hard_35_f6a828_1x100.png) 0 50% repeat-x; + border-top: 1px solid #A8A8A8; + border-bottom: 1px solid #A8A8A8; +} +#feed_list .unread_count { + position: absolute; + top: 3px; + right: 7px; + font-weight: bold; + color: #FFF; + padding: 0 6px; + background-color: #8eb6e8; +} + + +/* ================ */ +/* = Story Titles = */ +/* ================ */ + +#story_titles { + z-index: 10; + position: fixed; + top: 0px; + left: 0px; + height: 200px; + width: 100%; + overflow-y: scroll; + font-size: 1.1em; +} + +#story_titles .wrapper { + margin-left: 220px; +} + +#story_titles .feed_bar { + font-weight: bold; + font-size: 1.3em; + padding: 2px 140px 2px 28px; + background: #dadada url(../theme/images/dadada_40x100_textures_03_highlight_soft_75.png) 0 50% repeat-x; + border-bottom: 2px solid #404040; + position: relative; +} + +#story_titles .feed_bar .feed_heading { + display: block; + text-align: center; +} + +#story_titles .feed_bar .feed_heading .feed_favicon { + margin-right: 8px; + vertical-align: middle; +} + +#story_titles .feed_bar .unread_count { + position: absolute; + right: 4px; + top: 4px; +} + +#story_titles .feed_bar .feed_id { + display: none; +} + +#story_titles .story { + position: relative; + cursor: pointer; + font-weight: bold; + padding: 0px 206px 0px 28px; + border-top: 1px solid #E7EDF6; + text-decoration: none; + color: #272727; + line-height: 1em; + background: transparent url(../img/icons/silk/bullet_orange.png) no-repeat 6px 50%; +} + +#story_titles .story.NB-story-hover { + background-color: #f0f0f0; +} + +#story_titles .story a.story_title { + text-decoration: none; + color: #272727; + display: block; + padding: 4px 0px; +} +#story_titles .story .story_id { + display: none; +} + +#story_titles .story.read { + font-weight: normal; + background: none; +} + +#story_titles .story .story_date { + position: absolute; + right: 4px; + width: 200px; + top: 4px; +} +#story_titles .story.selected { + color: #304080; + border-top: 1px solid #D7DDE6; + background: #dadada url(../theme/images/dadada_40x100_textures_03_highlight_soft_75.png) 0 50% repeat-x; +} +#story_titles .story.after_selected { + border-top: 1px solid #D7DDE6; +} + +/* =================== */ +/* = Story Navigator = */ +/* =================== */ + +#story_pane .story_navigator { + position: absolute; + right: 4px; + top: 6px; +} + +#story_pane a.button { + outline: none; + border: none; + text-decoration: none; + cursor: pointer; + white-space: nowrap; + vertical-align: middle; + display: -moz-inline-box; + display: inline-block; + overflow: visible; + color: #000; + background-color: #acc; + padding: 5px 7px; + margin: 0px 2px; +} + +#story_pane a.button:hover { + background-color: #cee; +} + +#story_pane a.button.next_unread { + float: right; + width: 24px; + height: 16px; + background: #acc url(../img/icons/silk/arrow_down.png) no-repeat 50% 50%; +} + +#story_pane a.button.next_unread:hover { + background: #cee url(../img/icons/silk/arrow_down.png) no-repeat 50% 50%; +} + +#story_pane a.button.previous_unread { + float: right; + width: 16px; + height: 16px; + background: #acc url(../img/icons/silk/arrow_up.png) no-repeat 50% 50%; +} + +#story_pane a.button.previous_unread:hover { + background: #cee url(../img/icons/silk/arrow_up.png) no-repeat 50% 50%; +} + +/* ================= */ +/* = Story Content = */ +/* ================= */ + +#story_pane { + z-index: 10; + position: fixed; + top: 200px; + left: 0px; + width: 100%; + overflow-y: scroll; + font-size: 1.2em; + line-height: 1.5em; + height: 315px; +} + +#story_pane .wrapper { + margin-left: 220px; + position: relative; +} + + +#story_pane .story_title { + font-weight: bold; + font-size: 1.3em; + padding: 12px 140px 12px 28px; + background: #dadada url(../theme/images/dadada_40x100_textures_03_highlight_soft_75.png) 0 50% repeat-x; + border-top: 4px solid #404040; +} + +#story_pane .story_title a { + text-decoration: none; + color: #101050; +} + +#story_pane .story_title a:hover { + color: #1010A0; +} + +#story_pane .story_meta { + color: #606060; + font-weight: bold; + font-size: .8em; + width: 6em; + line-height: 1.9em; + clear: both; + float: left; + text-transform: uppercase; + padding: 0px 4px 0px 0px; +} + +#story_pane .story_feed { + padding: 4px 140px 0px 28px; +} + +#story_pane .story_feed .feed_favicon { + position: absolute; + left: 0px; + top: -1px; +} + +#story_pane .story_feed .data { + padding-left: 20px; + position: relative; +} + +#story_pane .story_author { + padding: 0px 140px 0px 28px; +} + +#story_pane .story_date { + padding: 0px 140px 0px 28px; +} + +#story_pane .story_content { + border-top: 1px solid #909090; + margin: 12px 140px 24px 28px; + padding: 12px 0px; +} + +#story_pane .story_endbar { + height: 8px; + border-top: 1px solid #404040; + background: #dadada url(../theme/images/dadada_40x100_textures_03_highlight_soft_75.png) 0 50% repeat-x; +} +/* ======================= */ +/* = Story Modifications = */ +/* ======================= */ + +#story_pane { + color: #2b2b2b; +} + +#story_pane p { + clear: both; +} + +#story_pane blockquote { + background-color: #F0F0F0; + border-left: 1px solid #9B9B9B; + padding: .5em 2em; + margin: 0px; +} + +/* ============ */ +/* = Task Bar = */ +/* ============ */ + +#task_bar { + height: 29px; + background: #e0e0e0 url(../img/reader/taskbar_background.png) repeat-x top left; +} + +/* ==================== */ +/* = OPML Import Form = */ +/* ==================== */ + +form.opml_import_form { + +} + +form.opml_import_form textarea { + width: 100%; + height: 200px; +} + +form.opml_import_form .section { + clear: both; + margin: 2px 0px; +} + +form.opml_import_form label { + display: block; +} +form.opml_import_form input { + display: block; + clear: both; + float: left; + margin: 0px 4px; +} + +/* ============== */ +/* = Bottom Bar = */ +/* ============== */ + +/*************************/ +/* Recommended for menus */ +/*************************/ + +button.menu { + cursor: pointer; +} + +div.menu { + display: none; + z-index: 99; + position: absolute; +} + +div.menu.active { + z-index: 100; +} + +/***********************/ +/* Completely optional */ +/***********************/ + +.menu_button { + line-height: 1.8; + text-shadow: 1px 1px #ddd; + color: #222; + background: #a8acae; + margin: 3px 2em 0 0.5em; + padding: 0 0.8em; + border: 0; + border-bottom: 1px solid #686c6e; + -webkit-border-radius: 0.5em; + -moz-border-radius: 0.5em; + border-radius: 0.5em; + float: right; + position: relative; + cursor: pointer; +} + + +.menu_button img { + vertical-align: -3px; + margin-right: 0.2em; +} + +.menu_button .menu { + display: none; + right: 0px; + bottom: 20px; + cursor: normal; +} + +.menu_button.hover .menu { + display: block; +} +.menu_button.hover { + -webkit-border-top-left-radius: 0; + -webkit-border-top-right-radius: 0; + -moz-border-radius-topleft: 0; + -moz-border-radius-topright: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top-color: #cacece; +} + +button.menu:hover, +button.menu.active { + background-color: #cacece; + text-shadow: 1px 1px #fff; +} + +div.menu { + font-size: 88%; + color: #444; + background: #cacece; + padding: 1em; + border: 1px solid #cacece; + border-top-width: 3px; + -moz-border-radius: 0.5em; + -moz-border-radius-topleft: 0; + -webkit-border-radius: 0.5em; + -webkit-border-top-left-radius: 0; + border-radius: 0.5em; + border-top-left-radius: 0; +} + +div.menu.active { + background-color: #eaeeee; +} + +div.menu h3 { + font-size: 108%; + font-weight: bold; + color: #444; + margin: 0; +} + +div.menu h4 { + font-size: 100%; + font-weight: normal; + line-height: 1.5; + color: #999; + margin: 0 0 0.5em 0; + white-space: nowrap; +} + +div.menu hr { + border: 0; + border-top: 1px solid #ddd; + border-bottom: 1px solid #fff; +} + +div.menu ul { + padding-left: 1.5em; +} \ No newline at end of file diff --git a/media/img/icons/actions/add-user.gif b/media/img/icons/actions/add-user.gif new file mode 100755 index 000000000..fe1ddd7c9 Binary files /dev/null and b/media/img/icons/actions/add-user.gif differ diff --git a/media/img/icons/actions/add-user.png b/media/img/icons/actions/add-user.png new file mode 100755 index 000000000..fa686a7c1 Binary files /dev/null and b/media/img/icons/actions/add-user.png differ diff --git a/media/img/icons/actions/add.gif b/media/img/icons/actions/add.gif new file mode 100755 index 000000000..14bbf4901 Binary files /dev/null and b/media/img/icons/actions/add.gif differ diff --git a/media/img/icons/actions/add.png b/media/img/icons/actions/add.png new file mode 100755 index 000000000..c59374382 Binary files /dev/null and b/media/img/icons/actions/add.png differ diff --git a/media/img/icons/actions/arrow-down.gif b/media/img/icons/actions/arrow-down.gif new file mode 100755 index 000000000..5456a1142 Binary files /dev/null and b/media/img/icons/actions/arrow-down.gif differ diff --git a/media/img/icons/actions/arrow-down.png b/media/img/icons/actions/arrow-down.png new file mode 100755 index 000000000..b4dd4e0c3 Binary files /dev/null and b/media/img/icons/actions/arrow-down.png differ diff --git a/media/img/icons/actions/arrow-left.gif b/media/img/icons/actions/arrow-left.gif new file mode 100755 index 000000000..5e0c9abde Binary files /dev/null and b/media/img/icons/actions/arrow-left.gif differ diff --git a/media/img/icons/actions/arrow-left.png b/media/img/icons/actions/arrow-left.png new file mode 100755 index 000000000..c9fe1d9d9 Binary files /dev/null and b/media/img/icons/actions/arrow-left.png differ diff --git a/media/img/icons/actions/arrow-right.gif b/media/img/icons/actions/arrow-right.gif new file mode 100755 index 000000000..51389ac55 Binary files /dev/null and b/media/img/icons/actions/arrow-right.gif differ diff --git a/media/img/icons/actions/arrow-right.png b/media/img/icons/actions/arrow-right.png new file mode 100755 index 000000000..0627638d5 Binary files /dev/null and b/media/img/icons/actions/arrow-right.png differ diff --git a/media/img/icons/actions/arrow-up.gif b/media/img/icons/actions/arrow-up.gif new file mode 100755 index 000000000..d731b3971 Binary files /dev/null and b/media/img/icons/actions/arrow-up.gif differ diff --git a/media/img/icons/actions/arrow-up.png b/media/img/icons/actions/arrow-up.png new file mode 100755 index 000000000..45554b51c Binary files /dev/null and b/media/img/icons/actions/arrow-up.png differ diff --git a/media/img/icons/actions/button-bold.gif b/media/img/icons/actions/button-bold.gif new file mode 100755 index 000000000..194e82486 Binary files /dev/null and b/media/img/icons/actions/button-bold.gif differ diff --git a/media/img/icons/actions/button-bold.png b/media/img/icons/actions/button-bold.png new file mode 100755 index 000000000..a88b135e3 Binary files /dev/null and b/media/img/icons/actions/button-bold.png differ diff --git a/media/img/icons/actions/button-italic.gif b/media/img/icons/actions/button-italic.gif new file mode 100755 index 000000000..4c730e44b Binary files /dev/null and b/media/img/icons/actions/button-italic.gif differ diff --git a/media/img/icons/actions/button-italic.png b/media/img/icons/actions/button-italic.png new file mode 100755 index 000000000..1815c0eed Binary files /dev/null and b/media/img/icons/actions/button-italic.png differ diff --git a/media/img/icons/actions/button-underline.gif b/media/img/icons/actions/button-underline.gif new file mode 100755 index 000000000..7d7794be5 Binary files /dev/null and b/media/img/icons/actions/button-underline.gif differ diff --git a/media/img/icons/actions/button-underline.png b/media/img/icons/actions/button-underline.png new file mode 100755 index 000000000..acb800079 Binary files /dev/null and b/media/img/icons/actions/button-underline.png differ diff --git a/media/img/icons/actions/button.gif b/media/img/icons/actions/button.gif new file mode 100755 index 000000000..04126eda0 Binary files /dev/null and b/media/img/icons/actions/button.gif differ diff --git a/media/img/icons/actions/button.png b/media/img/icons/actions/button.png new file mode 100755 index 000000000..b444e5e24 Binary files /dev/null and b/media/img/icons/actions/button.png differ diff --git a/media/img/icons/actions/cancel.gif b/media/img/icons/actions/cancel.gif new file mode 100755 index 000000000..9c1a6244c Binary files /dev/null and b/media/img/icons/actions/cancel.gif differ diff --git a/media/img/icons/actions/cancel.png b/media/img/icons/actions/cancel.png new file mode 100755 index 000000000..9a8ffc89c Binary files /dev/null and b/media/img/icons/actions/cancel.png differ diff --git a/media/img/icons/actions/clipboard.gif b/media/img/icons/actions/clipboard.gif new file mode 100755 index 000000000..2b3bcac8e Binary files /dev/null and b/media/img/icons/actions/clipboard.gif differ diff --git a/media/img/icons/actions/clipboard.png b/media/img/icons/actions/clipboard.png new file mode 100755 index 000000000..124828328 Binary files /dev/null and b/media/img/icons/actions/clipboard.png differ diff --git a/media/img/icons/actions/copy.gif b/media/img/icons/actions/copy.gif new file mode 100755 index 000000000..466ed6428 Binary files /dev/null and b/media/img/icons/actions/copy.gif differ diff --git a/media/img/icons/actions/copy.png b/media/img/icons/actions/copy.png new file mode 100755 index 000000000..a01c1705e Binary files /dev/null and b/media/img/icons/actions/copy.png differ diff --git a/media/img/icons/actions/cut.gif b/media/img/icons/actions/cut.gif new file mode 100755 index 000000000..9f42d0c77 Binary files /dev/null and b/media/img/icons/actions/cut.gif differ diff --git a/media/img/icons/actions/cut.png b/media/img/icons/actions/cut.png new file mode 100755 index 000000000..bb15238de Binary files /dev/null and b/media/img/icons/actions/cut.png differ diff --git a/media/img/icons/actions/delete-user.gif b/media/img/icons/actions/delete-user.gif new file mode 100755 index 000000000..0a1d2d08c Binary files /dev/null and b/media/img/icons/actions/delete-user.gif differ diff --git a/media/img/icons/actions/delete-user.png b/media/img/icons/actions/delete-user.png new file mode 100755 index 000000000..43ac59d52 Binary files /dev/null and b/media/img/icons/actions/delete-user.png differ diff --git a/media/img/icons/actions/forbidden-alt.gif b/media/img/icons/actions/forbidden-alt.gif new file mode 100755 index 000000000..5460285e2 Binary files /dev/null and b/media/img/icons/actions/forbidden-alt.gif differ diff --git a/media/img/icons/actions/forbidden-alt.png b/media/img/icons/actions/forbidden-alt.png new file mode 100755 index 000000000..7bdeeb2b8 Binary files /dev/null and b/media/img/icons/actions/forbidden-alt.png differ diff --git a/media/img/icons/actions/forbidden.gif b/media/img/icons/actions/forbidden.gif new file mode 100755 index 000000000..f9baa0732 Binary files /dev/null and b/media/img/icons/actions/forbidden.gif differ diff --git a/media/img/icons/actions/forbidden.png b/media/img/icons/actions/forbidden.png new file mode 100755 index 000000000..3bd7d3120 Binary files /dev/null and b/media/img/icons/actions/forbidden.png differ diff --git a/media/img/icons/actions/info.gif b/media/img/icons/actions/info.gif new file mode 100755 index 000000000..c83d242f4 Binary files /dev/null and b/media/img/icons/actions/info.gif differ diff --git a/media/img/icons/actions/info.png b/media/img/icons/actions/info.png new file mode 100755 index 000000000..cb6c3930a Binary files /dev/null and b/media/img/icons/actions/info.png differ diff --git a/media/img/icons/actions/lock.gif b/media/img/icons/actions/lock.gif new file mode 100755 index 000000000..3b332469b Binary files /dev/null and b/media/img/icons/actions/lock.gif differ diff --git a/media/img/icons/actions/lock.png b/media/img/icons/actions/lock.png new file mode 100755 index 000000000..c5e1747e6 Binary files /dev/null and b/media/img/icons/actions/lock.png differ diff --git a/media/img/icons/actions/mail-new.gif b/media/img/icons/actions/mail-new.gif new file mode 100755 index 000000000..68d3174ed Binary files /dev/null and b/media/img/icons/actions/mail-new.gif differ diff --git a/media/img/icons/actions/mail-new.png b/media/img/icons/actions/mail-new.png new file mode 100755 index 000000000..c1ea0a25d Binary files /dev/null and b/media/img/icons/actions/mail-new.png differ diff --git a/media/img/icons/actions/ok.gif b/media/img/icons/actions/ok.gif new file mode 100755 index 000000000..23b45d574 Binary files /dev/null and b/media/img/icons/actions/ok.gif differ diff --git a/media/img/icons/actions/ok.png b/media/img/icons/actions/ok.png new file mode 100755 index 000000000..cc7df71d2 Binary files /dev/null and b/media/img/icons/actions/ok.png differ diff --git a/media/img/icons/actions/paste.gif b/media/img/icons/actions/paste.gif new file mode 100755 index 000000000..0fb25fc63 Binary files /dev/null and b/media/img/icons/actions/paste.gif differ diff --git a/media/img/icons/actions/paste.png b/media/img/icons/actions/paste.png new file mode 100755 index 000000000..ad90c3511 Binary files /dev/null and b/media/img/icons/actions/paste.png differ diff --git a/media/img/icons/actions/refresh.gif b/media/img/icons/actions/refresh.gif new file mode 100755 index 000000000..93e70198f Binary files /dev/null and b/media/img/icons/actions/refresh.gif differ diff --git a/media/img/icons/actions/refresh.png b/media/img/icons/actions/refresh.png new file mode 100755 index 000000000..be23d15c8 Binary files /dev/null and b/media/img/icons/actions/refresh.png differ diff --git a/media/img/icons/actions/search.gif b/media/img/icons/actions/search.gif new file mode 100755 index 000000000..d8b0e8fb9 Binary files /dev/null and b/media/img/icons/actions/search.gif differ diff --git a/media/img/icons/actions/search.png b/media/img/icons/actions/search.png new file mode 100755 index 000000000..e1408d9e8 Binary files /dev/null and b/media/img/icons/actions/search.png differ diff --git a/media/img/icons/actions/splitwindow.gif b/media/img/icons/actions/splitwindow.gif new file mode 100755 index 000000000..45710d42c Binary files /dev/null and b/media/img/icons/actions/splitwindow.gif differ diff --git a/media/img/icons/actions/splitwindow.png b/media/img/icons/actions/splitwindow.png new file mode 100755 index 000000000..d10f38115 Binary files /dev/null and b/media/img/icons/actions/splitwindow.png differ diff --git a/media/img/icons/actions/stop.gif b/media/img/icons/actions/stop.gif new file mode 100755 index 000000000..d73d812fa Binary files /dev/null and b/media/img/icons/actions/stop.gif differ diff --git a/media/img/icons/actions/stop.png b/media/img/icons/actions/stop.png new file mode 100755 index 000000000..4222e4549 Binary files /dev/null and b/media/img/icons/actions/stop.png differ diff --git a/media/img/icons/actions/subtract.gif b/media/img/icons/actions/subtract.gif new file mode 100755 index 000000000..516396ec3 Binary files /dev/null and b/media/img/icons/actions/subtract.gif differ diff --git a/media/img/icons/actions/subtract.png b/media/img/icons/actions/subtract.png new file mode 100755 index 000000000..3d867674c Binary files /dev/null and b/media/img/icons/actions/subtract.png differ diff --git a/media/img/icons/actions/tab-new.gif b/media/img/icons/actions/tab-new.gif new file mode 100755 index 000000000..c7608470e Binary files /dev/null and b/media/img/icons/actions/tab-new.gif differ diff --git a/media/img/icons/actions/tab-new.png b/media/img/icons/actions/tab-new.png new file mode 100755 index 000000000..d8487e447 Binary files /dev/null and b/media/img/icons/actions/tab-new.png differ diff --git a/media/img/icons/actions/tab.gif b/media/img/icons/actions/tab.gif new file mode 100755 index 000000000..24c2bcb54 Binary files /dev/null and b/media/img/icons/actions/tab.gif differ diff --git a/media/img/icons/actions/tab.png b/media/img/icons/actions/tab.png new file mode 100755 index 000000000..214742639 Binary files /dev/null and b/media/img/icons/actions/tab.png differ diff --git a/media/img/icons/actions/tabs-new.gif b/media/img/icons/actions/tabs-new.gif new file mode 100755 index 000000000..3985d5dd5 Binary files /dev/null and b/media/img/icons/actions/tabs-new.gif differ diff --git a/media/img/icons/actions/tabs-new.png b/media/img/icons/actions/tabs-new.png new file mode 100755 index 000000000..8f33a67f1 Binary files /dev/null and b/media/img/icons/actions/tabs-new.png differ diff --git a/media/img/icons/actions/tabs.gif b/media/img/icons/actions/tabs.gif new file mode 100755 index 000000000..f5a78477a Binary files /dev/null and b/media/img/icons/actions/tabs.gif differ diff --git a/media/img/icons/actions/tabs.png b/media/img/icons/actions/tabs.png new file mode 100755 index 000000000..ffabc2e99 Binary files /dev/null and b/media/img/icons/actions/tabs.png differ diff --git a/media/img/icons/actions/unlock.gif b/media/img/icons/actions/unlock.gif new file mode 100755 index 000000000..e69936c6e Binary files /dev/null and b/media/img/icons/actions/unlock.gif differ diff --git a/media/img/icons/actions/unlock.png b/media/img/icons/actions/unlock.png new file mode 100755 index 000000000..cdd9b31c9 Binary files /dev/null and b/media/img/icons/actions/unlock.png differ diff --git a/media/img/icons/actions/window-new.gif b/media/img/icons/actions/window-new.gif new file mode 100755 index 000000000..ba542e447 Binary files /dev/null and b/media/img/icons/actions/window-new.gif differ diff --git a/media/img/icons/actions/window-new.png b/media/img/icons/actions/window-new.png new file mode 100755 index 000000000..b5908dd50 Binary files /dev/null and b/media/img/icons/actions/window-new.png differ diff --git a/media/img/icons/actions/window-resize.gif b/media/img/icons/actions/window-resize.gif new file mode 100755 index 000000000..3e9aaea31 Binary files /dev/null and b/media/img/icons/actions/window-resize.gif differ diff --git a/media/img/icons/actions/window-resize.png b/media/img/icons/actions/window-resize.png new file mode 100755 index 000000000..475c31ea6 Binary files /dev/null and b/media/img/icons/actions/window-resize.png differ diff --git a/media/img/icons/actions/window.gif b/media/img/icons/actions/window.gif new file mode 100755 index 000000000..b97888de8 Binary files /dev/null and b/media/img/icons/actions/window.gif differ diff --git a/media/img/icons/actions/window.png b/media/img/icons/actions/window.png new file mode 100755 index 000000000..951bf34e1 Binary files /dev/null and b/media/img/icons/actions/window.png differ diff --git a/media/img/icons/actions/windows.gif b/media/img/icons/actions/windows.gif new file mode 100755 index 000000000..77841eb7a Binary files /dev/null and b/media/img/icons/actions/windows.gif differ diff --git a/media/img/icons/actions/windows.png b/media/img/icons/actions/windows.png new file mode 100755 index 000000000..9d21f9bc5 Binary files /dev/null and b/media/img/icons/actions/windows.png differ diff --git a/media/img/icons/actions/wrench.gif b/media/img/icons/actions/wrench.gif new file mode 100755 index 000000000..5fb9bd476 Binary files /dev/null and b/media/img/icons/actions/wrench.gif differ diff --git a/media/img/icons/actions/wrench.png b/media/img/icons/actions/wrench.png new file mode 100755 index 000000000..b9599fed4 Binary files /dev/null and b/media/img/icons/actions/wrench.png differ diff --git a/media/img/icons/actions/zoom-in.gif b/media/img/icons/actions/zoom-in.gif new file mode 100755 index 000000000..c26a53ccf Binary files /dev/null and b/media/img/icons/actions/zoom-in.gif differ diff --git a/media/img/icons/actions/zoom-in.png b/media/img/icons/actions/zoom-in.png new file mode 100755 index 000000000..fbdd7051e Binary files /dev/null and b/media/img/icons/actions/zoom-in.png differ diff --git a/media/img/icons/actions/zoom-out.gif b/media/img/icons/actions/zoom-out.gif new file mode 100755 index 000000000..0233f5531 Binary files /dev/null and b/media/img/icons/actions/zoom-out.gif differ diff --git a/media/img/icons/actions/zoom-out.png b/media/img/icons/actions/zoom-out.png new file mode 100755 index 000000000..40a41f2ca Binary files /dev/null and b/media/img/icons/actions/zoom-out.png differ diff --git a/media/img/icons/flags/flag-austria.gif b/media/img/icons/flags/flag-austria.gif new file mode 100755 index 000000000..9af38f514 Binary files /dev/null and b/media/img/icons/flags/flag-austria.gif differ diff --git a/media/img/icons/flags/flag-austria.png b/media/img/icons/flags/flag-austria.png new file mode 100755 index 000000000..97a7e6e25 Binary files /dev/null and b/media/img/icons/flags/flag-austria.png differ diff --git a/media/img/icons/flags/flag-belgium.gif b/media/img/icons/flags/flag-belgium.gif new file mode 100755 index 000000000..bfcf2c1b8 Binary files /dev/null and b/media/img/icons/flags/flag-belgium.gif differ diff --git a/media/img/icons/flags/flag-belgium.png b/media/img/icons/flags/flag-belgium.png new file mode 100755 index 000000000..b091f617c Binary files /dev/null and b/media/img/icons/flags/flag-belgium.png differ diff --git a/media/img/icons/flags/flag-bulgaria.gif b/media/img/icons/flags/flag-bulgaria.gif new file mode 100755 index 000000000..64d9d638b Binary files /dev/null and b/media/img/icons/flags/flag-bulgaria.gif differ diff --git a/media/img/icons/flags/flag-bulgaria.png b/media/img/icons/flags/flag-bulgaria.png new file mode 100755 index 000000000..8f8f45a58 Binary files /dev/null and b/media/img/icons/flags/flag-bulgaria.png differ diff --git a/media/img/icons/flags/flag-czech-republic.gif b/media/img/icons/flags/flag-czech-republic.gif new file mode 100755 index 000000000..7d1b88a6d Binary files /dev/null and b/media/img/icons/flags/flag-czech-republic.gif differ diff --git a/media/img/icons/flags/flag-czech-republic.png b/media/img/icons/flags/flag-czech-republic.png new file mode 100755 index 000000000..4d9cbc024 Binary files /dev/null and b/media/img/icons/flags/flag-czech-republic.png differ diff --git a/media/img/icons/flags/flag-denmark.gif b/media/img/icons/flags/flag-denmark.gif new file mode 100755 index 000000000..492c7e9ff Binary files /dev/null and b/media/img/icons/flags/flag-denmark.gif differ diff --git a/media/img/icons/flags/flag-denmark.png b/media/img/icons/flags/flag-denmark.png new file mode 100755 index 000000000..38dd116c5 Binary files /dev/null and b/media/img/icons/flags/flag-denmark.png differ diff --git a/media/img/icons/flags/flag-estonia.gif b/media/img/icons/flags/flag-estonia.gif new file mode 100755 index 000000000..0c322da2e Binary files /dev/null and b/media/img/icons/flags/flag-estonia.gif differ diff --git a/media/img/icons/flags/flag-estonia.png b/media/img/icons/flags/flag-estonia.png new file mode 100755 index 000000000..b961dd973 Binary files /dev/null and b/media/img/icons/flags/flag-estonia.png differ diff --git a/media/img/icons/flags/flag-france.gif b/media/img/icons/flags/flag-france.gif new file mode 100755 index 000000000..0c1bcf306 Binary files /dev/null and b/media/img/icons/flags/flag-france.gif differ diff --git a/media/img/icons/flags/flag-france.png b/media/img/icons/flags/flag-france.png new file mode 100755 index 000000000..dc7a3d6f7 Binary files /dev/null and b/media/img/icons/flags/flag-france.png differ diff --git a/media/img/icons/flags/flag-germany.gif b/media/img/icons/flags/flag-germany.gif new file mode 100755 index 000000000..3462de080 Binary files /dev/null and b/media/img/icons/flags/flag-germany.gif differ diff --git a/media/img/icons/flags/flag-germany.png b/media/img/icons/flags/flag-germany.png new file mode 100755 index 000000000..dd1cd9bdb Binary files /dev/null and b/media/img/icons/flags/flag-germany.png differ diff --git a/media/img/icons/flags/flag-greece.gif b/media/img/icons/flags/flag-greece.gif new file mode 100755 index 000000000..9f51ef474 Binary files /dev/null and b/media/img/icons/flags/flag-greece.gif differ diff --git a/media/img/icons/flags/flag-greece.png b/media/img/icons/flags/flag-greece.png new file mode 100755 index 000000000..69dad621d Binary files /dev/null and b/media/img/icons/flags/flag-greece.png differ diff --git a/media/img/icons/flags/flag-holland.gif b/media/img/icons/flags/flag-holland.gif new file mode 100755 index 000000000..16b94733c Binary files /dev/null and b/media/img/icons/flags/flag-holland.gif differ diff --git a/media/img/icons/flags/flag-holland.png b/media/img/icons/flags/flag-holland.png new file mode 100755 index 000000000..5a55fedaf Binary files /dev/null and b/media/img/icons/flags/flag-holland.png differ diff --git a/media/img/icons/flags/flag-hungary.gif b/media/img/icons/flags/flag-hungary.gif new file mode 100755 index 000000000..ee474e9d0 Binary files /dev/null and b/media/img/icons/flags/flag-hungary.gif differ diff --git a/media/img/icons/flags/flag-hungary.png b/media/img/icons/flags/flag-hungary.png new file mode 100755 index 000000000..095a76f86 Binary files /dev/null and b/media/img/icons/flags/flag-hungary.png differ diff --git a/media/img/icons/flags/flag-ireland.gif b/media/img/icons/flags/flag-ireland.gif new file mode 100755 index 000000000..9990a7df0 Binary files /dev/null and b/media/img/icons/flags/flag-ireland.gif differ diff --git a/media/img/icons/flags/flag-ireland.png b/media/img/icons/flags/flag-ireland.png new file mode 100755 index 000000000..429ab853e Binary files /dev/null and b/media/img/icons/flags/flag-ireland.png differ diff --git a/media/img/icons/flags/flag-italy.gif b/media/img/icons/flags/flag-italy.gif new file mode 100755 index 000000000..e01583338 Binary files /dev/null and b/media/img/icons/flags/flag-italy.gif differ diff --git a/media/img/icons/flags/flag-italy.png b/media/img/icons/flags/flag-italy.png new file mode 100755 index 000000000..eba4d5e83 Binary files /dev/null and b/media/img/icons/flags/flag-italy.png differ diff --git a/media/img/icons/flags/flag-luxemburg.gif b/media/img/icons/flags/flag-luxemburg.gif new file mode 100755 index 000000000..f6cb0e323 Binary files /dev/null and b/media/img/icons/flags/flag-luxemburg.gif differ diff --git a/media/img/icons/flags/flag-luxemburg.png b/media/img/icons/flags/flag-luxemburg.png new file mode 100755 index 000000000..6f8241fa7 Binary files /dev/null and b/media/img/icons/flags/flag-luxemburg.png differ diff --git a/media/img/icons/flags/flag-rumania.gif b/media/img/icons/flags/flag-rumania.gif new file mode 100755 index 000000000..bfb30be5b Binary files /dev/null and b/media/img/icons/flags/flag-rumania.gif differ diff --git a/media/img/icons/flags/flag-rumania.png b/media/img/icons/flags/flag-rumania.png new file mode 100755 index 000000000..11fd02407 Binary files /dev/null and b/media/img/icons/flags/flag-rumania.png differ diff --git a/media/img/icons/flags/flag-spain.gif b/media/img/icons/flags/flag-spain.gif new file mode 100755 index 000000000..d482bdf4a Binary files /dev/null and b/media/img/icons/flags/flag-spain.gif differ diff --git a/media/img/icons/flags/flag-spain.png b/media/img/icons/flags/flag-spain.png new file mode 100755 index 000000000..ec36b0786 Binary files /dev/null and b/media/img/icons/flags/flag-spain.png differ diff --git a/media/img/icons/flags/flag-turkey.gif b/media/img/icons/flags/flag-turkey.gif new file mode 100755 index 000000000..02c7480d2 Binary files /dev/null and b/media/img/icons/flags/flag-turkey.gif differ diff --git a/media/img/icons/flags/flag-turkey.png b/media/img/icons/flags/flag-turkey.png new file mode 100755 index 000000000..f0f04dec7 Binary files /dev/null and b/media/img/icons/flags/flag-turkey.png differ diff --git a/media/img/icons/media-devices/camera.gif b/media/img/icons/media-devices/camera.gif new file mode 100755 index 000000000..465ff66a3 Binary files /dev/null and b/media/img/icons/media-devices/camera.gif differ diff --git a/media/img/icons/media-devices/camera.png b/media/img/icons/media-devices/camera.png new file mode 100755 index 000000000..e54ddbd28 Binary files /dev/null and b/media/img/icons/media-devices/camera.png differ diff --git a/media/img/icons/media-devices/cellphone.gif b/media/img/icons/media-devices/cellphone.gif new file mode 100755 index 000000000..8caafb237 Binary files /dev/null and b/media/img/icons/media-devices/cellphone.gif differ diff --git a/media/img/icons/media-devices/cellphone.png b/media/img/icons/media-devices/cellphone.png new file mode 100755 index 000000000..69e6fa515 Binary files /dev/null and b/media/img/icons/media-devices/cellphone.png differ diff --git a/media/img/icons/media-devices/floppy.gif b/media/img/icons/media-devices/floppy.gif new file mode 100755 index 000000000..7bf0ff79f Binary files /dev/null and b/media/img/icons/media-devices/floppy.gif differ diff --git a/media/img/icons/media-devices/floppy.png b/media/img/icons/media-devices/floppy.png new file mode 100755 index 000000000..2435ba500 Binary files /dev/null and b/media/img/icons/media-devices/floppy.png differ diff --git a/media/img/icons/media-devices/headphones.gif b/media/img/icons/media-devices/headphones.gif new file mode 100755 index 000000000..9b20813d2 Binary files /dev/null and b/media/img/icons/media-devices/headphones.gif differ diff --git a/media/img/icons/media-devices/headphones.png b/media/img/icons/media-devices/headphones.png new file mode 100755 index 000000000..82598e93f Binary files /dev/null and b/media/img/icons/media-devices/headphones.png differ diff --git a/media/img/icons/media-devices/imac.gif b/media/img/icons/media-devices/imac.gif new file mode 100755 index 000000000..b5e668f05 Binary files /dev/null and b/media/img/icons/media-devices/imac.gif differ diff --git a/media/img/icons/media-devices/imac.png b/media/img/icons/media-devices/imac.png new file mode 100755 index 000000000..17bb2815a Binary files /dev/null and b/media/img/icons/media-devices/imac.png differ diff --git a/media/img/icons/media-devices/ipod.gif b/media/img/icons/media-devices/ipod.gif new file mode 100755 index 000000000..6ce8a4a14 Binary files /dev/null and b/media/img/icons/media-devices/ipod.gif differ diff --git a/media/img/icons/media-devices/ipod.png b/media/img/icons/media-devices/ipod.png new file mode 100755 index 000000000..b9241ab61 Binary files /dev/null and b/media/img/icons/media-devices/ipod.png differ diff --git a/media/img/icons/media-devices/joystick.gif b/media/img/icons/media-devices/joystick.gif new file mode 100755 index 000000000..5b35ae31a Binary files /dev/null and b/media/img/icons/media-devices/joystick.gif differ diff --git a/media/img/icons/media-devices/joystick.png b/media/img/icons/media-devices/joystick.png new file mode 100755 index 000000000..d3038a457 Binary files /dev/null and b/media/img/icons/media-devices/joystick.png differ diff --git a/media/img/icons/media-devices/microphone-vintage.gif b/media/img/icons/media-devices/microphone-vintage.gif new file mode 100755 index 000000000..65c617e70 Binary files /dev/null and b/media/img/icons/media-devices/microphone-vintage.gif differ diff --git a/media/img/icons/media-devices/microphone-vintage.png b/media/img/icons/media-devices/microphone-vintage.png new file mode 100755 index 000000000..b2999756f Binary files /dev/null and b/media/img/icons/media-devices/microphone-vintage.png differ diff --git a/media/img/icons/media-devices/microphone.gif b/media/img/icons/media-devices/microphone.gif new file mode 100755 index 000000000..b53ea810e Binary files /dev/null and b/media/img/icons/media-devices/microphone.gif differ diff --git a/media/img/icons/media-devices/microphone.png b/media/img/icons/media-devices/microphone.png new file mode 100755 index 000000000..d623300cc Binary files /dev/null and b/media/img/icons/media-devices/microphone.png differ diff --git a/media/img/icons/media-devices/mobile.gif b/media/img/icons/media-devices/mobile.gif new file mode 100755 index 000000000..37f371b00 Binary files /dev/null and b/media/img/icons/media-devices/mobile.gif differ diff --git a/media/img/icons/media-devices/mobile.png b/media/img/icons/media-devices/mobile.png new file mode 100755 index 000000000..ffb994796 Binary files /dev/null and b/media/img/icons/media-devices/mobile.png differ diff --git a/media/img/icons/media-devices/movie.gif b/media/img/icons/media-devices/movie.gif new file mode 100755 index 000000000..3360e826f Binary files /dev/null and b/media/img/icons/media-devices/movie.gif differ diff --git a/media/img/icons/media-devices/movie.png b/media/img/icons/media-devices/movie.png new file mode 100755 index 000000000..a11e6f8f4 Binary files /dev/null and b/media/img/icons/media-devices/movie.png differ diff --git a/media/img/icons/media-devices/pc.gif b/media/img/icons/media-devices/pc.gif new file mode 100755 index 000000000..49c3f26be Binary files /dev/null and b/media/img/icons/media-devices/pc.gif differ diff --git a/media/img/icons/media-devices/pc.png b/media/img/icons/media-devices/pc.png new file mode 100755 index 000000000..96d0989df Binary files /dev/null and b/media/img/icons/media-devices/pc.png differ diff --git a/media/img/icons/media-devices/printer.gif b/media/img/icons/media-devices/printer.gif new file mode 100755 index 000000000..3f54f34ad Binary files /dev/null and b/media/img/icons/media-devices/printer.gif differ diff --git a/media/img/icons/media-devices/printer.png b/media/img/icons/media-devices/printer.png new file mode 100755 index 000000000..267d1397e Binary files /dev/null and b/media/img/icons/media-devices/printer.png differ diff --git a/media/img/icons/media-devices/rom.gif b/media/img/icons/media-devices/rom.gif new file mode 100755 index 000000000..aa5540a54 Binary files /dev/null and b/media/img/icons/media-devices/rom.gif differ diff --git a/media/img/icons/media-devices/rom.png b/media/img/icons/media-devices/rom.png new file mode 100755 index 000000000..b5a34bb2f Binary files /dev/null and b/media/img/icons/media-devices/rom.png differ diff --git a/media/img/icons/media-devices/screen.gif b/media/img/icons/media-devices/screen.gif new file mode 100755 index 000000000..037d9f2e8 Binary files /dev/null and b/media/img/icons/media-devices/screen.gif differ diff --git a/media/img/icons/media-devices/screen.png b/media/img/icons/media-devices/screen.png new file mode 100755 index 000000000..6fd5d2e2d Binary files /dev/null and b/media/img/icons/media-devices/screen.png differ diff --git a/media/img/icons/media-devices/signal.gif b/media/img/icons/media-devices/signal.gif new file mode 100755 index 000000000..cf28a5cbf Binary files /dev/null and b/media/img/icons/media-devices/signal.gif differ diff --git a/media/img/icons/media-devices/signal.png b/media/img/icons/media-devices/signal.png new file mode 100755 index 000000000..6723593cf Binary files /dev/null and b/media/img/icons/media-devices/signal.png differ diff --git a/media/img/icons/media-devices/tv.gif b/media/img/icons/media-devices/tv.gif new file mode 100755 index 000000000..605659894 Binary files /dev/null and b/media/img/icons/media-devices/tv.gif differ diff --git a/media/img/icons/media-devices/tv.png b/media/img/icons/media-devices/tv.png new file mode 100755 index 000000000..f68eae128 Binary files /dev/null and b/media/img/icons/media-devices/tv.png differ diff --git a/media/img/icons/media-devices/usb-stick-blue.gif b/media/img/icons/media-devices/usb-stick-blue.gif new file mode 100755 index 000000000..0c9091215 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-blue.gif differ diff --git a/media/img/icons/media-devices/usb-stick-blue.png b/media/img/icons/media-devices/usb-stick-blue.png new file mode 100755 index 000000000..64b0bd325 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-blue.png differ diff --git a/media/img/icons/media-devices/usb-stick-green.gif b/media/img/icons/media-devices/usb-stick-green.gif new file mode 100755 index 000000000..85d4c4350 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-green.gif differ diff --git a/media/img/icons/media-devices/usb-stick-green.png b/media/img/icons/media-devices/usb-stick-green.png new file mode 100755 index 000000000..9b18d0467 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-green.png differ diff --git a/media/img/icons/media-devices/usb-stick-light.gif b/media/img/icons/media-devices/usb-stick-light.gif new file mode 100755 index 000000000..6ee850987 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-light.gif differ diff --git a/media/img/icons/media-devices/usb-stick-light.png b/media/img/icons/media-devices/usb-stick-light.png new file mode 100755 index 000000000..c748f4ec3 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-light.png differ diff --git a/media/img/icons/media-devices/usb-stick-orange.gif b/media/img/icons/media-devices/usb-stick-orange.gif new file mode 100755 index 000000000..7157d76f4 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-orange.gif differ diff --git a/media/img/icons/media-devices/usb-stick-orange.png b/media/img/icons/media-devices/usb-stick-orange.png new file mode 100755 index 000000000..062797ef2 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-orange.png differ diff --git a/media/img/icons/media-devices/usb-stick-red.gif b/media/img/icons/media-devices/usb-stick-red.gif new file mode 100755 index 000000000..73a8be6b2 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-red.gif differ diff --git a/media/img/icons/media-devices/usb-stick-red.png b/media/img/icons/media-devices/usb-stick-red.png new file mode 100755 index 000000000..f939b9275 Binary files /dev/null and b/media/img/icons/media-devices/usb-stick-red.png differ diff --git a/media/img/icons/media-devices/webcam.gif b/media/img/icons/media-devices/webcam.gif new file mode 100755 index 000000000..64e1bd9c7 Binary files /dev/null and b/media/img/icons/media-devices/webcam.gif differ diff --git a/media/img/icons/media-devices/webcam.png b/media/img/icons/media-devices/webcam.png new file mode 100755 index 000000000..d58f9c9b5 Binary files /dev/null and b/media/img/icons/media-devices/webcam.png differ diff --git a/media/img/icons/mini/action_back.gif b/media/img/icons/mini/action_back.gif new file mode 100755 index 000000000..46a8ffb19 Binary files /dev/null and b/media/img/icons/mini/action_back.gif differ diff --git a/media/img/icons/mini/action_forward.gif b/media/img/icons/mini/action_forward.gif new file mode 100755 index 000000000..21da43758 Binary files /dev/null and b/media/img/icons/mini/action_forward.gif differ diff --git a/media/img/icons/mini/action_go.gif b/media/img/icons/mini/action_go.gif new file mode 100755 index 000000000..82ae7ed82 Binary files /dev/null and b/media/img/icons/mini/action_go.gif differ diff --git a/media/img/icons/mini/action_paste.gif b/media/img/icons/mini/action_paste.gif new file mode 100755 index 000000000..bffd6b0b3 Binary files /dev/null and b/media/img/icons/mini/action_paste.gif differ diff --git a/media/img/icons/mini/action_print.gif b/media/img/icons/mini/action_print.gif new file mode 100755 index 000000000..92e7e0a1a Binary files /dev/null and b/media/img/icons/mini/action_print.gif differ diff --git a/media/img/icons/mini/action_refresh.gif b/media/img/icons/mini/action_refresh.gif new file mode 100755 index 000000000..8268958a1 Binary files /dev/null and b/media/img/icons/mini/action_refresh.gif differ diff --git a/media/img/icons/mini/action_refresh_blue.gif b/media/img/icons/mini/action_refresh_blue.gif new file mode 100755 index 000000000..7c8bcd00f Binary files /dev/null and b/media/img/icons/mini/action_refresh_blue.gif differ diff --git a/media/img/icons/mini/action_save.gif b/media/img/icons/mini/action_save.gif new file mode 100755 index 000000000..6e6f7decc Binary files /dev/null and b/media/img/icons/mini/action_save.gif differ diff --git a/media/img/icons/mini/action_stop.gif b/media/img/icons/mini/action_stop.gif new file mode 100755 index 000000000..c941c1902 Binary files /dev/null and b/media/img/icons/mini/action_stop.gif differ diff --git a/media/img/icons/mini/application_dreamweaver.gif b/media/img/icons/mini/application_dreamweaver.gif new file mode 100755 index 000000000..15fe1071e Binary files /dev/null and b/media/img/icons/mini/application_dreamweaver.gif differ diff --git a/media/img/icons/mini/application_firefox.gif b/media/img/icons/mini/application_firefox.gif new file mode 100755 index 000000000..1192e3e77 Binary files /dev/null and b/media/img/icons/mini/application_firefox.gif differ diff --git a/media/img/icons/mini/application_flash.gif b/media/img/icons/mini/application_flash.gif new file mode 100755 index 000000000..92abbcbe2 Binary files /dev/null and b/media/img/icons/mini/application_flash.gif differ diff --git a/media/img/icons/mini/arrow_down.gif b/media/img/icons/mini/arrow_down.gif new file mode 100755 index 000000000..f0bb6a4ea Binary files /dev/null and b/media/img/icons/mini/arrow_down.gif differ diff --git a/media/img/icons/mini/arrow_left.gif b/media/img/icons/mini/arrow_left.gif new file mode 100755 index 000000000..932ade160 Binary files /dev/null and b/media/img/icons/mini/arrow_left.gif differ diff --git a/media/img/icons/mini/arrow_right.gif b/media/img/icons/mini/arrow_right.gif new file mode 100755 index 000000000..780431c2b Binary files /dev/null and b/media/img/icons/mini/arrow_right.gif differ diff --git a/media/img/icons/mini/arrow_up.gif b/media/img/icons/mini/arrow_up.gif new file mode 100755 index 000000000..e8234178e Binary files /dev/null and b/media/img/icons/mini/arrow_up.gif differ diff --git a/media/img/icons/mini/box.gif b/media/img/icons/mini/box.gif new file mode 100755 index 000000000..cd4729a5d Binary files /dev/null and b/media/img/icons/mini/box.gif differ diff --git a/media/img/icons/mini/calendar.gif b/media/img/icons/mini/calendar.gif new file mode 100755 index 000000000..f78788cbe Binary files /dev/null and b/media/img/icons/mini/calendar.gif differ diff --git a/media/img/icons/mini/comment.gif b/media/img/icons/mini/comment.gif new file mode 100755 index 000000000..897cf2e3e Binary files /dev/null and b/media/img/icons/mini/comment.gif differ diff --git a/media/img/icons/mini/comment_blue.gif b/media/img/icons/mini/comment_blue.gif new file mode 100755 index 000000000..897cf2e3e Binary files /dev/null and b/media/img/icons/mini/comment_blue.gif differ diff --git a/media/img/icons/mini/comment_delete.gif b/media/img/icons/mini/comment_delete.gif new file mode 100755 index 000000000..691b546b3 Binary files /dev/null and b/media/img/icons/mini/comment_delete.gif differ diff --git a/media/img/icons/mini/comment_new.gif b/media/img/icons/mini/comment_new.gif new file mode 100755 index 000000000..aa7e9d3ff Binary files /dev/null and b/media/img/icons/mini/comment_new.gif differ diff --git a/media/img/icons/mini/comment_yellow.gif b/media/img/icons/mini/comment_yellow.gif new file mode 100755 index 000000000..df7158a47 Binary files /dev/null and b/media/img/icons/mini/comment_yellow.gif differ diff --git a/media/img/icons/mini/copy.gif b/media/img/icons/mini/copy.gif new file mode 100755 index 000000000..ff81b3bf2 Binary files /dev/null and b/media/img/icons/mini/copy.gif differ diff --git a/media/img/icons/mini/cut.gif b/media/img/icons/mini/cut.gif new file mode 100755 index 000000000..ffd7f4f7d Binary files /dev/null and b/media/img/icons/mini/cut.gif differ diff --git a/media/img/icons/mini/date.gif b/media/img/icons/mini/date.gif new file mode 100755 index 000000000..0cbdf001c Binary files /dev/null and b/media/img/icons/mini/date.gif differ diff --git a/media/img/icons/mini/date_delete.gif b/media/img/icons/mini/date_delete.gif new file mode 100755 index 000000000..65f716b75 Binary files /dev/null and b/media/img/icons/mini/date_delete.gif differ diff --git a/media/img/icons/mini/date_new.gif b/media/img/icons/mini/date_new.gif new file mode 100755 index 000000000..e224d7803 Binary files /dev/null and b/media/img/icons/mini/date_new.gif differ diff --git a/media/img/icons/mini/file_acrobat.gif b/media/img/icons/mini/file_acrobat.gif new file mode 100755 index 000000000..7bc7de95d Binary files /dev/null and b/media/img/icons/mini/file_acrobat.gif differ diff --git a/media/img/icons/mini/file_font.gif b/media/img/icons/mini/file_font.gif new file mode 100755 index 000000000..52b5ef3e4 Binary files /dev/null and b/media/img/icons/mini/file_font.gif differ diff --git a/media/img/icons/mini/file_font_truetype.gif b/media/img/icons/mini/file_font_truetype.gif new file mode 100755 index 000000000..0569425f1 Binary files /dev/null and b/media/img/icons/mini/file_font_truetype.gif differ diff --git a/media/img/icons/mini/flag_blue.gif b/media/img/icons/mini/flag_blue.gif new file mode 100755 index 000000000..a0924052f Binary files /dev/null and b/media/img/icons/mini/flag_blue.gif differ diff --git a/media/img/icons/mini/flag_green.gif b/media/img/icons/mini/flag_green.gif new file mode 100755 index 000000000..76b89250d Binary files /dev/null and b/media/img/icons/mini/flag_green.gif differ diff --git a/media/img/icons/mini/flag_orange.gif b/media/img/icons/mini/flag_orange.gif new file mode 100755 index 000000000..0596070f2 Binary files /dev/null and b/media/img/icons/mini/flag_orange.gif differ diff --git a/media/img/icons/mini/flag_red.gif b/media/img/icons/mini/flag_red.gif new file mode 100755 index 000000000..16698e32e Binary files /dev/null and b/media/img/icons/mini/flag_red.gif differ diff --git a/media/img/icons/mini/flag_white.gif b/media/img/icons/mini/flag_white.gif new file mode 100755 index 000000000..b566c018c Binary files /dev/null and b/media/img/icons/mini/flag_white.gif differ diff --git a/media/img/icons/mini/folder.gif b/media/img/icons/mini/folder.gif new file mode 100755 index 000000000..8dc04c495 Binary files /dev/null and b/media/img/icons/mini/folder.gif differ diff --git a/media/img/icons/mini/folder_delete.gif b/media/img/icons/mini/folder_delete.gif new file mode 100755 index 000000000..ce9f44835 Binary files /dev/null and b/media/img/icons/mini/folder_delete.gif differ diff --git a/media/img/icons/mini/folder_images.gif b/media/img/icons/mini/folder_images.gif new file mode 100755 index 000000000..81a7c3b41 Binary files /dev/null and b/media/img/icons/mini/folder_images.gif differ diff --git a/media/img/icons/mini/folder_lock.gif b/media/img/icons/mini/folder_lock.gif new file mode 100755 index 000000000..b1bfa0205 Binary files /dev/null and b/media/img/icons/mini/folder_lock.gif differ diff --git a/media/img/icons/mini/folder_new.gif b/media/img/icons/mini/folder_new.gif new file mode 100755 index 000000000..5f30cfa2e Binary files /dev/null and b/media/img/icons/mini/folder_new.gif differ diff --git a/media/img/icons/mini/folder_page.gif b/media/img/icons/mini/folder_page.gif new file mode 100755 index 000000000..a8c53e2f7 Binary files /dev/null and b/media/img/icons/mini/folder_page.gif differ diff --git a/media/img/icons/mini/icon_accept.gif b/media/img/icons/mini/icon_accept.gif new file mode 100755 index 000000000..35e949963 Binary files /dev/null and b/media/img/icons/mini/icon_accept.gif differ diff --git a/media/img/icons/mini/icon_airmail.gif b/media/img/icons/mini/icon_airmail.gif new file mode 100755 index 000000000..2be845745 Binary files /dev/null and b/media/img/icons/mini/icon_airmail.gif differ diff --git a/media/img/icons/mini/icon_alert.gif b/media/img/icons/mini/icon_alert.gif new file mode 100755 index 000000000..429a5d9be Binary files /dev/null and b/media/img/icons/mini/icon_alert.gif differ diff --git a/media/img/icons/mini/icon_attachment.gif b/media/img/icons/mini/icon_attachment.gif new file mode 100755 index 000000000..4400e61e9 Binary files /dev/null and b/media/img/icons/mini/icon_attachment.gif differ diff --git a/media/img/icons/mini/icon_clock.gif b/media/img/icons/mini/icon_clock.gif new file mode 100755 index 000000000..6ac6ebca2 Binary files /dev/null and b/media/img/icons/mini/icon_clock.gif differ diff --git a/media/img/icons/mini/icon_component.gif b/media/img/icons/mini/icon_component.gif new file mode 100755 index 000000000..bde079f97 Binary files /dev/null and b/media/img/icons/mini/icon_component.gif differ diff --git a/media/img/icons/mini/icon_download.gif b/media/img/icons/mini/icon_download.gif new file mode 100755 index 000000000..d052d0920 Binary files /dev/null and b/media/img/icons/mini/icon_download.gif differ diff --git a/media/img/icons/mini/icon_email.gif b/media/img/icons/mini/icon_email.gif new file mode 100755 index 000000000..62bc6ffd4 Binary files /dev/null and b/media/img/icons/mini/icon_email.gif differ diff --git a/media/img/icons/mini/icon_extension.gif b/media/img/icons/mini/icon_extension.gif new file mode 100755 index 000000000..50c2d4622 Binary files /dev/null and b/media/img/icons/mini/icon_extension.gif differ diff --git a/media/img/icons/mini/icon_favourites.gif b/media/img/icons/mini/icon_favourites.gif new file mode 100755 index 000000000..785b93875 Binary files /dev/null and b/media/img/icons/mini/icon_favourites.gif differ diff --git a/media/img/icons/mini/icon_get_world.gif b/media/img/icons/mini/icon_get_world.gif new file mode 100755 index 000000000..f99a5383b Binary files /dev/null and b/media/img/icons/mini/icon_get_world.gif differ diff --git a/media/img/icons/mini/icon_history.gif b/media/img/icons/mini/icon_history.gif new file mode 100755 index 000000000..4473b1bc7 Binary files /dev/null and b/media/img/icons/mini/icon_history.gif differ diff --git a/media/img/icons/mini/icon_home.gif b/media/img/icons/mini/icon_home.gif new file mode 100755 index 000000000..327e76f92 Binary files /dev/null and b/media/img/icons/mini/icon_home.gif differ diff --git a/media/img/icons/mini/icon_info.gif b/media/img/icons/mini/icon_info.gif new file mode 100755 index 000000000..9dfa0e196 Binary files /dev/null and b/media/img/icons/mini/icon_info.gif differ diff --git a/media/img/icons/mini/icon_key.gif b/media/img/icons/mini/icon_key.gif new file mode 100755 index 000000000..e3853e5af Binary files /dev/null and b/media/img/icons/mini/icon_key.gif differ diff --git a/media/img/icons/mini/icon_link.gif b/media/img/icons/mini/icon_link.gif new file mode 100755 index 000000000..c64c23c1f Binary files /dev/null and b/media/img/icons/mini/icon_link.gif differ diff --git a/media/img/icons/mini/icon_mail.gif b/media/img/icons/mini/icon_mail.gif new file mode 100755 index 000000000..5b5c7a75b Binary files /dev/null and b/media/img/icons/mini/icon_mail.gif differ diff --git a/media/img/icons/mini/icon_monitor_mac.gif b/media/img/icons/mini/icon_monitor_mac.gif new file mode 100755 index 000000000..b70028777 Binary files /dev/null and b/media/img/icons/mini/icon_monitor_mac.gif differ diff --git a/media/img/icons/mini/icon_monitor_pc.gif b/media/img/icons/mini/icon_monitor_pc.gif new file mode 100755 index 000000000..d9a947268 Binary files /dev/null and b/media/img/icons/mini/icon_monitor_pc.gif differ diff --git a/media/img/icons/mini/icon_network.gif b/media/img/icons/mini/icon_network.gif new file mode 100755 index 000000000..c607888a0 Binary files /dev/null and b/media/img/icons/mini/icon_network.gif differ diff --git a/media/img/icons/mini/icon_package.gif b/media/img/icons/mini/icon_package.gif new file mode 100755 index 000000000..e01b1ffbd Binary files /dev/null and b/media/img/icons/mini/icon_package.gif differ diff --git a/media/img/icons/mini/icon_package_get.gif b/media/img/icons/mini/icon_package_get.gif new file mode 100755 index 000000000..19712a9c2 Binary files /dev/null and b/media/img/icons/mini/icon_package_get.gif differ diff --git a/media/img/icons/mini/icon_package_open.gif b/media/img/icons/mini/icon_package_open.gif new file mode 100755 index 000000000..e630faf91 Binary files /dev/null and b/media/img/icons/mini/icon_package_open.gif differ diff --git a/media/img/icons/mini/icon_padlock.gif b/media/img/icons/mini/icon_padlock.gif new file mode 100755 index 000000000..f70cc953a Binary files /dev/null and b/media/img/icons/mini/icon_padlock.gif differ diff --git a/media/img/icons/mini/icon_security.gif b/media/img/icons/mini/icon_security.gif new file mode 100755 index 000000000..929284daa Binary files /dev/null and b/media/img/icons/mini/icon_security.gif differ diff --git a/media/img/icons/mini/icon_settings.gif b/media/img/icons/mini/icon_settings.gif new file mode 100755 index 000000000..14339f700 Binary files /dev/null and b/media/img/icons/mini/icon_settings.gif differ diff --git a/media/img/icons/mini/icon_user.gif b/media/img/icons/mini/icon_user.gif new file mode 100755 index 000000000..dcb5c2a89 Binary files /dev/null and b/media/img/icons/mini/icon_user.gif differ diff --git a/media/img/icons/mini/icon_wand.gif b/media/img/icons/mini/icon_wand.gif new file mode 100755 index 000000000..d4eb47e76 Binary files /dev/null and b/media/img/icons/mini/icon_wand.gif differ diff --git a/media/img/icons/mini/icon_world.gif b/media/img/icons/mini/icon_world.gif new file mode 100755 index 000000000..355c1a950 Binary files /dev/null and b/media/img/icons/mini/icon_world.gif differ diff --git a/media/img/icons/mini/icon_world_dynamic.gif b/media/img/icons/mini/icon_world_dynamic.gif new file mode 100755 index 000000000..e5ed08ce6 Binary files /dev/null and b/media/img/icons/mini/icon_world_dynamic.gif differ diff --git a/media/img/icons/mini/image.gif b/media/img/icons/mini/image.gif new file mode 100755 index 000000000..d5924f4bb Binary files /dev/null and b/media/img/icons/mini/image.gif differ diff --git a/media/img/icons/mini/image_new.gif b/media/img/icons/mini/image_new.gif new file mode 100755 index 000000000..4f4d12e5d Binary files /dev/null and b/media/img/icons/mini/image_new.gif differ diff --git a/media/img/icons/mini/interface_browser.gif b/media/img/icons/mini/interface_browser.gif new file mode 100755 index 000000000..602ef5042 Binary files /dev/null and b/media/img/icons/mini/interface_browser.gif differ diff --git a/media/img/icons/mini/interface_dialog.gif b/media/img/icons/mini/interface_dialog.gif new file mode 100755 index 000000000..31e1b071d Binary files /dev/null and b/media/img/icons/mini/interface_dialog.gif differ diff --git a/media/img/icons/mini/interface_installer.gif b/media/img/icons/mini/interface_installer.gif new file mode 100755 index 000000000..4b11af38e Binary files /dev/null and b/media/img/icons/mini/interface_installer.gif differ diff --git a/media/img/icons/mini/list_comments.gif b/media/img/icons/mini/list_comments.gif new file mode 100755 index 000000000..b313894a1 Binary files /dev/null and b/media/img/icons/mini/list_comments.gif differ diff --git a/media/img/icons/mini/list_components.gif b/media/img/icons/mini/list_components.gif new file mode 100755 index 000000000..d38de9d29 Binary files /dev/null and b/media/img/icons/mini/list_components.gif differ diff --git a/media/img/icons/mini/list_errors.gif b/media/img/icons/mini/list_errors.gif new file mode 100755 index 000000000..2e68d107c Binary files /dev/null and b/media/img/icons/mini/list_errors.gif differ diff --git a/media/img/icons/mini/list_extensions.gif b/media/img/icons/mini/list_extensions.gif new file mode 100755 index 000000000..a5e542a0c Binary files /dev/null and b/media/img/icons/mini/list_extensions.gif differ diff --git a/media/img/icons/mini/list_images.gif b/media/img/icons/mini/list_images.gif new file mode 100755 index 000000000..f744f19a3 Binary files /dev/null and b/media/img/icons/mini/list_images.gif differ diff --git a/media/img/icons/mini/list_keys.gif b/media/img/icons/mini/list_keys.gif new file mode 100755 index 000000000..6d3990a01 Binary files /dev/null and b/media/img/icons/mini/list_keys.gif differ diff --git a/media/img/icons/mini/list_links.gif b/media/img/icons/mini/list_links.gif new file mode 100755 index 000000000..4beb04496 Binary files /dev/null and b/media/img/icons/mini/list_links.gif differ diff --git a/media/img/icons/mini/list_packages.gif b/media/img/icons/mini/list_packages.gif new file mode 100755 index 000000000..c6cadf3bb Binary files /dev/null and b/media/img/icons/mini/list_packages.gif differ diff --git a/media/img/icons/mini/list_security.gif b/media/img/icons/mini/list_security.gif new file mode 100755 index 000000000..3e32002f1 Binary files /dev/null and b/media/img/icons/mini/list_security.gif differ diff --git a/media/img/icons/mini/list_settings.gif b/media/img/icons/mini/list_settings.gif new file mode 100755 index 000000000..084509565 Binary files /dev/null and b/media/img/icons/mini/list_settings.gif differ diff --git a/media/img/icons/mini/list_users.gif b/media/img/icons/mini/list_users.gif new file mode 100755 index 000000000..394aba63f Binary files /dev/null and b/media/img/icons/mini/list_users.gif differ diff --git a/media/img/icons/mini/list_world.gif b/media/img/icons/mini/list_world.gif new file mode 100755 index 000000000..a56265e03 Binary files /dev/null and b/media/img/icons/mini/list_world.gif differ diff --git a/media/img/icons/mini/note.gif b/media/img/icons/mini/note.gif new file mode 100755 index 000000000..17b9f4185 Binary files /dev/null and b/media/img/icons/mini/note.gif differ diff --git a/media/img/icons/mini/note_delete.gif b/media/img/icons/mini/note_delete.gif new file mode 100755 index 000000000..13402a582 Binary files /dev/null and b/media/img/icons/mini/note_delete.gif differ diff --git a/media/img/icons/mini/note_new.gif b/media/img/icons/mini/note_new.gif new file mode 100755 index 000000000..431ff64d1 Binary files /dev/null and b/media/img/icons/mini/note_new.gif differ diff --git a/media/img/icons/mini/page.gif b/media/img/icons/mini/page.gif new file mode 100755 index 000000000..c5743c066 Binary files /dev/null and b/media/img/icons/mini/page.gif differ diff --git a/media/img/icons/mini/page_alert.gif b/media/img/icons/mini/page_alert.gif new file mode 100755 index 000000000..4719a1f92 Binary files /dev/null and b/media/img/icons/mini/page_alert.gif differ diff --git a/media/img/icons/mini/page_attachment.gif b/media/img/icons/mini/page_attachment.gif new file mode 100755 index 000000000..a8e1b45ce Binary files /dev/null and b/media/img/icons/mini/page_attachment.gif differ diff --git a/media/img/icons/mini/page_bookmark.gif b/media/img/icons/mini/page_bookmark.gif new file mode 100755 index 000000000..1b2a923a5 Binary files /dev/null and b/media/img/icons/mini/page_bookmark.gif differ diff --git a/media/img/icons/mini/page_boy.gif b/media/img/icons/mini/page_boy.gif new file mode 100755 index 000000000..4ae3ff0a7 Binary files /dev/null and b/media/img/icons/mini/page_boy.gif differ diff --git a/media/img/icons/mini/page_code.gif b/media/img/icons/mini/page_code.gif new file mode 100755 index 000000000..912517b83 Binary files /dev/null and b/media/img/icons/mini/page_code.gif differ diff --git a/media/img/icons/mini/page_colors.gif b/media/img/icons/mini/page_colors.gif new file mode 100755 index 000000000..0141d3842 Binary files /dev/null and b/media/img/icons/mini/page_colors.gif differ diff --git a/media/img/icons/mini/page_component.gif b/media/img/icons/mini/page_component.gif new file mode 100755 index 000000000..f9aee3f3c Binary files /dev/null and b/media/img/icons/mini/page_component.gif differ diff --git a/media/img/icons/mini/page_cross.gif b/media/img/icons/mini/page_cross.gif new file mode 100755 index 000000000..bf43a0a0b Binary files /dev/null and b/media/img/icons/mini/page_cross.gif differ diff --git a/media/img/icons/mini/page_delete.gif b/media/img/icons/mini/page_delete.gif new file mode 100755 index 000000000..0c6be0348 Binary files /dev/null and b/media/img/icons/mini/page_delete.gif differ diff --git a/media/img/icons/mini/page_deny.gif b/media/img/icons/mini/page_deny.gif new file mode 100755 index 000000000..2bf5013ab Binary files /dev/null and b/media/img/icons/mini/page_deny.gif differ diff --git a/media/img/icons/mini/page_down.gif b/media/img/icons/mini/page_down.gif new file mode 100755 index 000000000..390ba6974 Binary files /dev/null and b/media/img/icons/mini/page_down.gif differ diff --git a/media/img/icons/mini/page_dynamic.gif b/media/img/icons/mini/page_dynamic.gif new file mode 100755 index 000000000..11c59e68e Binary files /dev/null and b/media/img/icons/mini/page_dynamic.gif differ diff --git a/media/img/icons/mini/page_edit.gif b/media/img/icons/mini/page_edit.gif new file mode 100755 index 000000000..7b02b6e72 Binary files /dev/null and b/media/img/icons/mini/page_edit.gif differ diff --git a/media/img/icons/mini/page_extension.gif b/media/img/icons/mini/page_extension.gif new file mode 100755 index 000000000..7bbe56a7b Binary files /dev/null and b/media/img/icons/mini/page_extension.gif differ diff --git a/media/img/icons/mini/page_favourites.gif b/media/img/icons/mini/page_favourites.gif new file mode 100755 index 000000000..eab0d9d1a Binary files /dev/null and b/media/img/icons/mini/page_favourites.gif differ diff --git a/media/img/icons/mini/page_find.gif b/media/img/icons/mini/page_find.gif new file mode 100755 index 000000000..9ae5e3489 Binary files /dev/null and b/media/img/icons/mini/page_find.gif differ diff --git a/media/img/icons/mini/page_flash.gif b/media/img/icons/mini/page_flash.gif new file mode 100755 index 000000000..6e17277b1 Binary files /dev/null and b/media/img/icons/mini/page_flash.gif differ diff --git a/media/img/icons/mini/page_girl.gif b/media/img/icons/mini/page_girl.gif new file mode 100755 index 000000000..9f48a2239 Binary files /dev/null and b/media/img/icons/mini/page_girl.gif differ diff --git a/media/img/icons/mini/page_html.gif b/media/img/icons/mini/page_html.gif new file mode 100755 index 000000000..34e79d162 Binary files /dev/null and b/media/img/icons/mini/page_html.gif differ diff --git a/media/img/icons/mini/page_java.gif b/media/img/icons/mini/page_java.gif new file mode 100755 index 000000000..6592a87a0 Binary files /dev/null and b/media/img/icons/mini/page_java.gif differ diff --git a/media/img/icons/mini/page_key.gif b/media/img/icons/mini/page_key.gif new file mode 100755 index 000000000..ebf83ca7d Binary files /dev/null and b/media/img/icons/mini/page_key.gif differ diff --git a/media/img/icons/mini/page_left.gif b/media/img/icons/mini/page_left.gif new file mode 100755 index 000000000..f222b4693 Binary files /dev/null and b/media/img/icons/mini/page_left.gif differ diff --git a/media/img/icons/mini/page_link.gif b/media/img/icons/mini/page_link.gif new file mode 100755 index 000000000..ff0b39d0f Binary files /dev/null and b/media/img/icons/mini/page_link.gif differ diff --git a/media/img/icons/mini/page_lock.gif b/media/img/icons/mini/page_lock.gif new file mode 100755 index 000000000..07a275685 Binary files /dev/null and b/media/img/icons/mini/page_lock.gif differ diff --git a/media/img/icons/mini/page_new.gif b/media/img/icons/mini/page_new.gif new file mode 100755 index 000000000..0888abf85 Binary files /dev/null and b/media/img/icons/mini/page_new.gif differ diff --git a/media/img/icons/mini/page_next.gif b/media/img/icons/mini/page_next.gif new file mode 100755 index 000000000..60769b66d Binary files /dev/null and b/media/img/icons/mini/page_next.gif differ diff --git a/media/img/icons/mini/page_package.gif b/media/img/icons/mini/page_package.gif new file mode 100755 index 000000000..d94a07586 Binary files /dev/null and b/media/img/icons/mini/page_package.gif differ diff --git a/media/img/icons/mini/page_php.gif b/media/img/icons/mini/page_php.gif new file mode 100755 index 000000000..d99790321 Binary files /dev/null and b/media/img/icons/mini/page_php.gif differ diff --git a/media/img/icons/mini/page_prev.gif b/media/img/icons/mini/page_prev.gif new file mode 100755 index 000000000..f0bb78da6 Binary files /dev/null and b/media/img/icons/mini/page_prev.gif differ diff --git a/media/img/icons/mini/page_refresh.gif b/media/img/icons/mini/page_refresh.gif new file mode 100755 index 000000000..dc238461e Binary files /dev/null and b/media/img/icons/mini/page_refresh.gif differ diff --git a/media/img/icons/mini/page_right.gif b/media/img/icons/mini/page_right.gif new file mode 100755 index 000000000..aedf4ba62 Binary files /dev/null and b/media/img/icons/mini/page_right.gif differ diff --git a/media/img/icons/mini/page_script.gif b/media/img/icons/mini/page_script.gif new file mode 100755 index 000000000..44717b306 Binary files /dev/null and b/media/img/icons/mini/page_script.gif differ diff --git a/media/img/icons/mini/page_security.gif b/media/img/icons/mini/page_security.gif new file mode 100755 index 000000000..5050dc4b5 Binary files /dev/null and b/media/img/icons/mini/page_security.gif differ diff --git a/media/img/icons/mini/page_settings.gif b/media/img/icons/mini/page_settings.gif new file mode 100755 index 000000000..af4099724 Binary files /dev/null and b/media/img/icons/mini/page_settings.gif differ diff --git a/media/img/icons/mini/page_sound.gif b/media/img/icons/mini/page_sound.gif new file mode 100755 index 000000000..1526ad11c Binary files /dev/null and b/media/img/icons/mini/page_sound.gif differ diff --git a/media/img/icons/mini/page_tag_blue.gif b/media/img/icons/mini/page_tag_blue.gif new file mode 100755 index 000000000..5ceb96ced Binary files /dev/null and b/media/img/icons/mini/page_tag_blue.gif differ diff --git a/media/img/icons/mini/page_tag_red.gif b/media/img/icons/mini/page_tag_red.gif new file mode 100755 index 000000000..4aa2416b6 Binary files /dev/null and b/media/img/icons/mini/page_tag_red.gif differ diff --git a/media/img/icons/mini/page_text.gif b/media/img/icons/mini/page_text.gif new file mode 100755 index 000000000..c1132e472 Binary files /dev/null and b/media/img/icons/mini/page_text.gif differ diff --git a/media/img/icons/mini/page_text_delete.gif b/media/img/icons/mini/page_text_delete.gif new file mode 100755 index 000000000..f3178ab40 Binary files /dev/null and b/media/img/icons/mini/page_text_delete.gif differ diff --git a/media/img/icons/mini/page_tick.gif b/media/img/icons/mini/page_tick.gif new file mode 100755 index 000000000..d7edd0c0d Binary files /dev/null and b/media/img/icons/mini/page_tick.gif differ diff --git a/media/img/icons/mini/page_tree.gif b/media/img/icons/mini/page_tree.gif new file mode 100755 index 000000000..847274e6d Binary files /dev/null and b/media/img/icons/mini/page_tree.gif differ diff --git a/media/img/icons/mini/page_up.gif b/media/img/icons/mini/page_up.gif new file mode 100755 index 000000000..08ef934c7 Binary files /dev/null and b/media/img/icons/mini/page_up.gif differ diff --git a/media/img/icons/mini/page_url.gif b/media/img/icons/mini/page_url.gif new file mode 100755 index 000000000..1b27952c2 Binary files /dev/null and b/media/img/icons/mini/page_url.gif differ diff --git a/media/img/icons/mini/page_user.gif b/media/img/icons/mini/page_user.gif new file mode 100755 index 000000000..ad39fb794 Binary files /dev/null and b/media/img/icons/mini/page_user.gif differ diff --git a/media/img/icons/mini/page_user_dark.gif b/media/img/icons/mini/page_user_dark.gif new file mode 100755 index 000000000..6ee2c1c81 Binary files /dev/null and b/media/img/icons/mini/page_user_dark.gif differ diff --git a/media/img/icons/mini/page_user_light.gif b/media/img/icons/mini/page_user_light.gif new file mode 100755 index 000000000..2b7a924b7 Binary files /dev/null and b/media/img/icons/mini/page_user_light.gif differ diff --git a/media/img/icons/mini/page_video.gif b/media/img/icons/mini/page_video.gif new file mode 100755 index 000000000..a1c50325d Binary files /dev/null and b/media/img/icons/mini/page_video.gif differ diff --git a/media/img/icons/mini/page_wizard.gif b/media/img/icons/mini/page_wizard.gif new file mode 100755 index 000000000..fd816ea7e Binary files /dev/null and b/media/img/icons/mini/page_wizard.gif differ diff --git a/media/img/icons/mini/readme.txt b/media/img/icons/mini/readme.txt new file mode 100755 index 000000000..0b0307974 --- /dev/null +++ b/media/img/icons/mini/readme.txt @@ -0,0 +1,2 @@ +mini icons - famfamfam.com +Contact: mjames@gmail.com \ No newline at end of file diff --git a/media/img/icons/mini/table.gif b/media/img/icons/mini/table.gif new file mode 100755 index 000000000..e5daec441 Binary files /dev/null and b/media/img/icons/mini/table.gif differ diff --git a/media/img/icons/mini/table_delete.gif b/media/img/icons/mini/table_delete.gif new file mode 100755 index 000000000..99190cd53 Binary files /dev/null and b/media/img/icons/mini/table_delete.gif differ diff --git a/media/img/icons/mini/tables.gif b/media/img/icons/mini/tables.gif new file mode 100755 index 000000000..87246be87 Binary files /dev/null and b/media/img/icons/mini/tables.gif differ diff --git a/media/img/icons/silk/accept.png b/media/img/icons/silk/accept.png new file mode 100755 index 000000000..89c8129a4 Binary files /dev/null and b/media/img/icons/silk/accept.png differ diff --git a/media/img/icons/silk/add.png b/media/img/icons/silk/add.png new file mode 100755 index 000000000..6332fefea Binary files /dev/null and b/media/img/icons/silk/add.png differ diff --git a/media/img/icons/silk/anchor.png b/media/img/icons/silk/anchor.png new file mode 100755 index 000000000..9b3422c61 Binary files /dev/null and b/media/img/icons/silk/anchor.png differ diff --git a/media/img/icons/silk/application.png b/media/img/icons/silk/application.png new file mode 100755 index 000000000..1dee9e366 Binary files /dev/null and b/media/img/icons/silk/application.png differ diff --git a/media/img/icons/silk/application_add.png b/media/img/icons/silk/application_add.png new file mode 100755 index 000000000..2e945076c Binary files /dev/null and b/media/img/icons/silk/application_add.png differ diff --git a/media/img/icons/silk/application_cascade.png b/media/img/icons/silk/application_cascade.png new file mode 100755 index 000000000..da5c622ea Binary files /dev/null and b/media/img/icons/silk/application_cascade.png differ diff --git a/media/img/icons/silk/application_delete.png b/media/img/icons/silk/application_delete.png new file mode 100755 index 000000000..0a335acf6 Binary files /dev/null and b/media/img/icons/silk/application_delete.png differ diff --git a/media/img/icons/silk/application_double.png b/media/img/icons/silk/application_double.png new file mode 100755 index 000000000..647592f2e Binary files /dev/null and b/media/img/icons/silk/application_double.png differ diff --git a/media/img/icons/silk/application_edit.png b/media/img/icons/silk/application_edit.png new file mode 100755 index 000000000..fb2efb877 Binary files /dev/null and b/media/img/icons/silk/application_edit.png differ diff --git a/media/img/icons/silk/application_error.png b/media/img/icons/silk/application_error.png new file mode 100755 index 000000000..b35fa5717 Binary files /dev/null and b/media/img/icons/silk/application_error.png differ diff --git a/media/img/icons/silk/application_form.png b/media/img/icons/silk/application_form.png new file mode 100755 index 000000000..807b862cf Binary files /dev/null and b/media/img/icons/silk/application_form.png differ diff --git a/media/img/icons/silk/application_form_add.png b/media/img/icons/silk/application_form_add.png new file mode 100755 index 000000000..28c2175e6 Binary files /dev/null and b/media/img/icons/silk/application_form_add.png differ diff --git a/media/img/icons/silk/application_form_delete.png b/media/img/icons/silk/application_form_delete.png new file mode 100755 index 000000000..cd305ec83 Binary files /dev/null and b/media/img/icons/silk/application_form_delete.png differ diff --git a/media/img/icons/silk/application_form_edit.png b/media/img/icons/silk/application_form_edit.png new file mode 100755 index 000000000..af486c940 Binary files /dev/null and b/media/img/icons/silk/application_form_edit.png differ diff --git a/media/img/icons/silk/application_form_magnify.png b/media/img/icons/silk/application_form_magnify.png new file mode 100755 index 000000000..7b7fbd17e Binary files /dev/null and b/media/img/icons/silk/application_form_magnify.png differ diff --git a/media/img/icons/silk/application_get.png b/media/img/icons/silk/application_get.png new file mode 100755 index 000000000..28e41ea2b Binary files /dev/null and b/media/img/icons/silk/application_get.png differ diff --git a/media/img/icons/silk/application_go.png b/media/img/icons/silk/application_go.png new file mode 100755 index 000000000..5cc2b0dd3 Binary files /dev/null and b/media/img/icons/silk/application_go.png differ diff --git a/media/img/icons/silk/application_home.png b/media/img/icons/silk/application_home.png new file mode 100755 index 000000000..b60d0c85a Binary files /dev/null and b/media/img/icons/silk/application_home.png differ diff --git a/media/img/icons/silk/application_key.png b/media/img/icons/silk/application_key.png new file mode 100755 index 000000000..998d65c69 Binary files /dev/null and b/media/img/icons/silk/application_key.png differ diff --git a/media/img/icons/silk/application_lightning.png b/media/img/icons/silk/application_lightning.png new file mode 100755 index 000000000..7e91545c7 Binary files /dev/null and b/media/img/icons/silk/application_lightning.png differ diff --git a/media/img/icons/silk/application_link.png b/media/img/icons/silk/application_link.png new file mode 100755 index 000000000..f8fbb3ed9 Binary files /dev/null and b/media/img/icons/silk/application_link.png differ diff --git a/media/img/icons/silk/application_osx.png b/media/img/icons/silk/application_osx.png new file mode 100755 index 000000000..9f022ece8 Binary files /dev/null and b/media/img/icons/silk/application_osx.png differ diff --git a/media/img/icons/silk/application_osx_terminal.png b/media/img/icons/silk/application_osx_terminal.png new file mode 100755 index 000000000..b3d8ce01e Binary files /dev/null and b/media/img/icons/silk/application_osx_terminal.png differ diff --git a/media/img/icons/silk/application_put.png b/media/img/icons/silk/application_put.png new file mode 100755 index 000000000..c30cf5989 Binary files /dev/null and b/media/img/icons/silk/application_put.png differ diff --git a/media/img/icons/silk/application_side_boxes.png b/media/img/icons/silk/application_side_boxes.png new file mode 100755 index 000000000..efbf3c4f8 Binary files /dev/null and b/media/img/icons/silk/application_side_boxes.png differ diff --git a/media/img/icons/silk/application_side_contract.png b/media/img/icons/silk/application_side_contract.png new file mode 100755 index 000000000..3585f94d6 Binary files /dev/null and b/media/img/icons/silk/application_side_contract.png differ diff --git a/media/img/icons/silk/application_side_expand.png b/media/img/icons/silk/application_side_expand.png new file mode 100755 index 000000000..030cf7c37 Binary files /dev/null and b/media/img/icons/silk/application_side_expand.png differ diff --git a/media/img/icons/silk/application_side_list.png b/media/img/icons/silk/application_side_list.png new file mode 100755 index 000000000..248eaf1ac Binary files /dev/null and b/media/img/icons/silk/application_side_list.png differ diff --git a/media/img/icons/silk/application_side_tree.png b/media/img/icons/silk/application_side_tree.png new file mode 100755 index 000000000..f04a52b3d Binary files /dev/null and b/media/img/icons/silk/application_side_tree.png differ diff --git a/media/img/icons/silk/application_split.png b/media/img/icons/silk/application_split.png new file mode 100755 index 000000000..a91c78a5c Binary files /dev/null and b/media/img/icons/silk/application_split.png differ diff --git a/media/img/icons/silk/application_tile_horizontal.png b/media/img/icons/silk/application_tile_horizontal.png new file mode 100755 index 000000000..8a1191c38 Binary files /dev/null and b/media/img/icons/silk/application_tile_horizontal.png differ diff --git a/media/img/icons/silk/application_tile_vertical.png b/media/img/icons/silk/application_tile_vertical.png new file mode 100755 index 000000000..1d40383d3 Binary files /dev/null and b/media/img/icons/silk/application_tile_vertical.png differ diff --git a/media/img/icons/silk/application_view_columns.png b/media/img/icons/silk/application_view_columns.png new file mode 100755 index 000000000..dc2e9d5f5 Binary files /dev/null and b/media/img/icons/silk/application_view_columns.png differ diff --git a/media/img/icons/silk/application_view_detail.png b/media/img/icons/silk/application_view_detail.png new file mode 100755 index 000000000..aba044bbc Binary files /dev/null and b/media/img/icons/silk/application_view_detail.png differ diff --git a/media/img/icons/silk/application_view_gallery.png b/media/img/icons/silk/application_view_gallery.png new file mode 100755 index 000000000..851950db7 Binary files /dev/null and b/media/img/icons/silk/application_view_gallery.png differ diff --git a/media/img/icons/silk/application_view_icons.png b/media/img/icons/silk/application_view_icons.png new file mode 100755 index 000000000..6a93cdaa7 Binary files /dev/null and b/media/img/icons/silk/application_view_icons.png differ diff --git a/media/img/icons/silk/application_view_list.png b/media/img/icons/silk/application_view_list.png new file mode 100755 index 000000000..acc30b853 Binary files /dev/null and b/media/img/icons/silk/application_view_list.png differ diff --git a/media/img/icons/silk/application_view_tile.png b/media/img/icons/silk/application_view_tile.png new file mode 100755 index 000000000..3bc0bd32f Binary files /dev/null and b/media/img/icons/silk/application_view_tile.png differ diff --git a/media/img/icons/silk/application_xp.png b/media/img/icons/silk/application_xp.png new file mode 100755 index 000000000..d22860a31 Binary files /dev/null and b/media/img/icons/silk/application_xp.png differ diff --git a/media/img/icons/silk/application_xp_terminal.png b/media/img/icons/silk/application_xp_terminal.png new file mode 100755 index 000000000..c28dd6381 Binary files /dev/null and b/media/img/icons/silk/application_xp_terminal.png differ diff --git a/media/img/icons/silk/arrow_branch.png b/media/img/icons/silk/arrow_branch.png new file mode 100755 index 000000000..7542db1d1 Binary files /dev/null and b/media/img/icons/silk/arrow_branch.png differ diff --git a/media/img/icons/silk/arrow_divide.png b/media/img/icons/silk/arrow_divide.png new file mode 100755 index 000000000..61a7b1d99 Binary files /dev/null and b/media/img/icons/silk/arrow_divide.png differ diff --git a/media/img/icons/silk/arrow_down.png b/media/img/icons/silk/arrow_down.png new file mode 100755 index 000000000..2c4e27937 Binary files /dev/null and b/media/img/icons/silk/arrow_down.png differ diff --git a/media/img/icons/silk/arrow_in.png b/media/img/icons/silk/arrow_in.png new file mode 100755 index 000000000..745c65134 Binary files /dev/null and b/media/img/icons/silk/arrow_in.png differ diff --git a/media/img/icons/silk/arrow_inout.png b/media/img/icons/silk/arrow_inout.png new file mode 100755 index 000000000..1b763672a Binary files /dev/null and b/media/img/icons/silk/arrow_inout.png differ diff --git a/media/img/icons/silk/arrow_join.png b/media/img/icons/silk/arrow_join.png new file mode 100755 index 000000000..a128413d8 Binary files /dev/null and b/media/img/icons/silk/arrow_join.png differ diff --git a/media/img/icons/silk/arrow_left.png b/media/img/icons/silk/arrow_left.png new file mode 100755 index 000000000..5dc696781 Binary files /dev/null and b/media/img/icons/silk/arrow_left.png differ diff --git a/media/img/icons/silk/arrow_merge.png b/media/img/icons/silk/arrow_merge.png new file mode 100755 index 000000000..7502dbb33 Binary files /dev/null and b/media/img/icons/silk/arrow_merge.png differ diff --git a/media/img/icons/silk/arrow_out.png b/media/img/icons/silk/arrow_out.png new file mode 100755 index 000000000..2e9bc42be Binary files /dev/null and b/media/img/icons/silk/arrow_out.png differ diff --git a/media/img/icons/silk/arrow_redo.png b/media/img/icons/silk/arrow_redo.png new file mode 100755 index 000000000..fdc394c7c Binary files /dev/null and b/media/img/icons/silk/arrow_redo.png differ diff --git a/media/img/icons/silk/arrow_refresh.png b/media/img/icons/silk/arrow_refresh.png new file mode 100755 index 000000000..0de26566d Binary files /dev/null and b/media/img/icons/silk/arrow_refresh.png differ diff --git a/media/img/icons/silk/arrow_refresh_small.png b/media/img/icons/silk/arrow_refresh_small.png new file mode 100755 index 000000000..d3087dfc9 Binary files /dev/null and b/media/img/icons/silk/arrow_refresh_small.png differ diff --git a/media/img/icons/silk/arrow_right.png b/media/img/icons/silk/arrow_right.png new file mode 100755 index 000000000..b1a181923 Binary files /dev/null and b/media/img/icons/silk/arrow_right.png differ diff --git a/media/img/icons/silk/arrow_rotate_anticlockwise.png b/media/img/icons/silk/arrow_rotate_anticlockwise.png new file mode 100755 index 000000000..46c75aa85 Binary files /dev/null and b/media/img/icons/silk/arrow_rotate_anticlockwise.png differ diff --git a/media/img/icons/silk/arrow_rotate_clockwise.png b/media/img/icons/silk/arrow_rotate_clockwise.png new file mode 100755 index 000000000..aa65210eb Binary files /dev/null and b/media/img/icons/silk/arrow_rotate_clockwise.png differ diff --git a/media/img/icons/silk/arrow_switch.png b/media/img/icons/silk/arrow_switch.png new file mode 100755 index 000000000..258c16c63 Binary files /dev/null and b/media/img/icons/silk/arrow_switch.png differ diff --git a/media/img/icons/silk/arrow_turn_left.png b/media/img/icons/silk/arrow_turn_left.png new file mode 100755 index 000000000..a3d6c9e39 Binary files /dev/null and b/media/img/icons/silk/arrow_turn_left.png differ diff --git a/media/img/icons/silk/arrow_turn_right.png b/media/img/icons/silk/arrow_turn_right.png new file mode 100755 index 000000000..629f20d62 Binary files /dev/null and b/media/img/icons/silk/arrow_turn_right.png differ diff --git a/media/img/icons/silk/arrow_undo.png b/media/img/icons/silk/arrow_undo.png new file mode 100755 index 000000000..6972c5e59 Binary files /dev/null and b/media/img/icons/silk/arrow_undo.png differ diff --git a/media/img/icons/silk/arrow_up.png b/media/img/icons/silk/arrow_up.png new file mode 100755 index 000000000..1ebb19324 Binary files /dev/null and b/media/img/icons/silk/arrow_up.png differ diff --git a/media/img/icons/silk/asterisk_orange.png b/media/img/icons/silk/asterisk_orange.png new file mode 100755 index 000000000..1ebebde54 Binary files /dev/null and b/media/img/icons/silk/asterisk_orange.png differ diff --git a/media/img/icons/silk/asterisk_yellow.png b/media/img/icons/silk/asterisk_yellow.png new file mode 100755 index 000000000..bab7cc9bc Binary files /dev/null and b/media/img/icons/silk/asterisk_yellow.png differ diff --git a/media/img/icons/silk/attach.png b/media/img/icons/silk/attach.png new file mode 100755 index 000000000..ea897cc9f Binary files /dev/null and b/media/img/icons/silk/attach.png differ diff --git a/media/img/icons/silk/award_star_add.png b/media/img/icons/silk/award_star_add.png new file mode 100755 index 000000000..9c4be9be2 Binary files /dev/null and b/media/img/icons/silk/award_star_add.png differ diff --git a/media/img/icons/silk/award_star_bronze_1.png b/media/img/icons/silk/award_star_bronze_1.png new file mode 100755 index 000000000..658c7117b Binary files /dev/null and b/media/img/icons/silk/award_star_bronze_1.png differ diff --git a/media/img/icons/silk/award_star_bronze_2.png b/media/img/icons/silk/award_star_bronze_2.png new file mode 100755 index 000000000..e47babd7b Binary files /dev/null and b/media/img/icons/silk/award_star_bronze_2.png differ diff --git a/media/img/icons/silk/award_star_bronze_3.png b/media/img/icons/silk/award_star_bronze_3.png new file mode 100755 index 000000000..396e4b3a2 Binary files /dev/null and b/media/img/icons/silk/award_star_bronze_3.png differ diff --git a/media/img/icons/silk/award_star_delete.png b/media/img/icons/silk/award_star_delete.png new file mode 100755 index 000000000..4721b152a Binary files /dev/null and b/media/img/icons/silk/award_star_delete.png differ diff --git a/media/img/icons/silk/award_star_gold_1.png b/media/img/icons/silk/award_star_gold_1.png new file mode 100755 index 000000000..97a22b72e Binary files /dev/null and b/media/img/icons/silk/award_star_gold_1.png differ diff --git a/media/img/icons/silk/award_star_gold_2.png b/media/img/icons/silk/award_star_gold_2.png new file mode 100755 index 000000000..0eaa57175 Binary files /dev/null and b/media/img/icons/silk/award_star_gold_2.png differ diff --git a/media/img/icons/silk/award_star_gold_3.png b/media/img/icons/silk/award_star_gold_3.png new file mode 100755 index 000000000..124c9914f Binary files /dev/null and b/media/img/icons/silk/award_star_gold_3.png differ diff --git a/media/img/icons/silk/award_star_silver_1.png b/media/img/icons/silk/award_star_silver_1.png new file mode 100755 index 000000000..028a54626 Binary files /dev/null and b/media/img/icons/silk/award_star_silver_1.png differ diff --git a/media/img/icons/silk/award_star_silver_2.png b/media/img/icons/silk/award_star_silver_2.png new file mode 100755 index 000000000..e487c3a19 Binary files /dev/null and b/media/img/icons/silk/award_star_silver_2.png differ diff --git a/media/img/icons/silk/award_star_silver_3.png b/media/img/icons/silk/award_star_silver_3.png new file mode 100755 index 000000000..1d72d4724 Binary files /dev/null and b/media/img/icons/silk/award_star_silver_3.png differ diff --git a/media/img/icons/silk/basket.png b/media/img/icons/silk/basket.png new file mode 100755 index 000000000..b0686d787 Binary files /dev/null and b/media/img/icons/silk/basket.png differ diff --git a/media/img/icons/silk/basket_add.png b/media/img/icons/silk/basket_add.png new file mode 100755 index 000000000..355436823 Binary files /dev/null and b/media/img/icons/silk/basket_add.png differ diff --git a/media/img/icons/silk/basket_delete.png b/media/img/icons/silk/basket_delete.png new file mode 100755 index 000000000..1349974f6 Binary files /dev/null and b/media/img/icons/silk/basket_delete.png differ diff --git a/media/img/icons/silk/basket_edit.png b/media/img/icons/silk/basket_edit.png new file mode 100755 index 000000000..8138bbdf0 Binary files /dev/null and b/media/img/icons/silk/basket_edit.png differ diff --git a/media/img/icons/silk/basket_error.png b/media/img/icons/silk/basket_error.png new file mode 100755 index 000000000..3978b2923 Binary files /dev/null and b/media/img/icons/silk/basket_error.png differ diff --git a/media/img/icons/silk/basket_go.png b/media/img/icons/silk/basket_go.png new file mode 100755 index 000000000..ed8b9a576 Binary files /dev/null and b/media/img/icons/silk/basket_go.png differ diff --git a/media/img/icons/silk/basket_put.png b/media/img/icons/silk/basket_put.png new file mode 100755 index 000000000..be62faaaa Binary files /dev/null and b/media/img/icons/silk/basket_put.png differ diff --git a/media/img/icons/silk/basket_remove.png b/media/img/icons/silk/basket_remove.png new file mode 100755 index 000000000..04dd7fd3b Binary files /dev/null and b/media/img/icons/silk/basket_remove.png differ diff --git a/media/img/icons/silk/bell.png b/media/img/icons/silk/bell.png new file mode 100755 index 000000000..6e0015df4 Binary files /dev/null and b/media/img/icons/silk/bell.png differ diff --git a/media/img/icons/silk/bell_add.png b/media/img/icons/silk/bell_add.png new file mode 100755 index 000000000..7db01d627 Binary files /dev/null and b/media/img/icons/silk/bell_add.png differ diff --git a/media/img/icons/silk/bell_delete.png b/media/img/icons/silk/bell_delete.png new file mode 100755 index 000000000..23907bbba Binary files /dev/null and b/media/img/icons/silk/bell_delete.png differ diff --git a/media/img/icons/silk/bell_error.png b/media/img/icons/silk/bell_error.png new file mode 100755 index 000000000..a0ddc0028 Binary files /dev/null and b/media/img/icons/silk/bell_error.png differ diff --git a/media/img/icons/silk/bell_go.png b/media/img/icons/silk/bell_go.png new file mode 100755 index 000000000..b89bb3434 Binary files /dev/null and b/media/img/icons/silk/bell_go.png differ diff --git a/media/img/icons/silk/bell_link.png b/media/img/icons/silk/bell_link.png new file mode 100755 index 000000000..b8c64af1e Binary files /dev/null and b/media/img/icons/silk/bell_link.png differ diff --git a/media/img/icons/silk/bin.png b/media/img/icons/silk/bin.png new file mode 100755 index 000000000..ebad933c8 Binary files /dev/null and b/media/img/icons/silk/bin.png differ diff --git a/media/img/icons/silk/bin_closed.png b/media/img/icons/silk/bin_closed.png new file mode 100755 index 000000000..afe22ba99 Binary files /dev/null and b/media/img/icons/silk/bin_closed.png differ diff --git a/media/img/icons/silk/bin_empty.png b/media/img/icons/silk/bin_empty.png new file mode 100755 index 000000000..375b8bf6a Binary files /dev/null and b/media/img/icons/silk/bin_empty.png differ diff --git a/media/img/icons/silk/bomb.png b/media/img/icons/silk/bomb.png new file mode 100755 index 000000000..1be37974a Binary files /dev/null and b/media/img/icons/silk/bomb.png differ diff --git a/media/img/icons/silk/book.png b/media/img/icons/silk/book.png new file mode 100755 index 000000000..b0f4dd792 Binary files /dev/null and b/media/img/icons/silk/book.png differ diff --git a/media/img/icons/silk/book_add.png b/media/img/icons/silk/book_add.png new file mode 100755 index 000000000..e2f084727 Binary files /dev/null and b/media/img/icons/silk/book_add.png differ diff --git a/media/img/icons/silk/book_addresses.png b/media/img/icons/silk/book_addresses.png new file mode 100755 index 000000000..b73419ba8 Binary files /dev/null and b/media/img/icons/silk/book_addresses.png differ diff --git a/media/img/icons/silk/book_delete.png b/media/img/icons/silk/book_delete.png new file mode 100755 index 000000000..d9a6340d7 Binary files /dev/null and b/media/img/icons/silk/book_delete.png differ diff --git a/media/img/icons/silk/book_edit.png b/media/img/icons/silk/book_edit.png new file mode 100755 index 000000000..6e756cc85 Binary files /dev/null and b/media/img/icons/silk/book_edit.png differ diff --git a/media/img/icons/silk/book_error.png b/media/img/icons/silk/book_error.png new file mode 100755 index 000000000..f3fbed0dc Binary files /dev/null and b/media/img/icons/silk/book_error.png differ diff --git a/media/img/icons/silk/book_go.png b/media/img/icons/silk/book_go.png new file mode 100755 index 000000000..cd4e1964c Binary files /dev/null and b/media/img/icons/silk/book_go.png differ diff --git a/media/img/icons/silk/book_key.png b/media/img/icons/silk/book_key.png new file mode 100755 index 000000000..d8e23ec93 Binary files /dev/null and b/media/img/icons/silk/book_key.png differ diff --git a/media/img/icons/silk/book_link.png b/media/img/icons/silk/book_link.png new file mode 100755 index 000000000..dd0820e86 Binary files /dev/null and b/media/img/icons/silk/book_link.png differ diff --git a/media/img/icons/silk/book_next.png b/media/img/icons/silk/book_next.png new file mode 100755 index 000000000..ff2ea1ab9 Binary files /dev/null and b/media/img/icons/silk/book_next.png differ diff --git a/media/img/icons/silk/book_open.png b/media/img/icons/silk/book_open.png new file mode 100755 index 000000000..7d863f949 Binary files /dev/null and b/media/img/icons/silk/book_open.png differ diff --git a/media/img/icons/silk/book_previous.png b/media/img/icons/silk/book_previous.png new file mode 100755 index 000000000..2e53c6980 Binary files /dev/null and b/media/img/icons/silk/book_previous.png differ diff --git a/media/img/icons/silk/box.png b/media/img/icons/silk/box.png new file mode 100755 index 000000000..8443c23eb Binary files /dev/null and b/media/img/icons/silk/box.png differ diff --git a/media/img/icons/silk/brick.png b/media/img/icons/silk/brick.png new file mode 100755 index 000000000..7851cf34c Binary files /dev/null and b/media/img/icons/silk/brick.png differ diff --git a/media/img/icons/silk/brick_add.png b/media/img/icons/silk/brick_add.png new file mode 100755 index 000000000..fac186bfb Binary files /dev/null and b/media/img/icons/silk/brick_add.png differ diff --git a/media/img/icons/silk/brick_delete.png b/media/img/icons/silk/brick_delete.png new file mode 100755 index 000000000..3a8c37348 Binary files /dev/null and b/media/img/icons/silk/brick_delete.png differ diff --git a/media/img/icons/silk/brick_edit.png b/media/img/icons/silk/brick_edit.png new file mode 100755 index 000000000..eb06df3bb Binary files /dev/null and b/media/img/icons/silk/brick_edit.png differ diff --git a/media/img/icons/silk/brick_error.png b/media/img/icons/silk/brick_error.png new file mode 100755 index 000000000..18ab01eb2 Binary files /dev/null and b/media/img/icons/silk/brick_error.png differ diff --git a/media/img/icons/silk/brick_go.png b/media/img/icons/silk/brick_go.png new file mode 100755 index 000000000..fe0d33567 Binary files /dev/null and b/media/img/icons/silk/brick_go.png differ diff --git a/media/img/icons/silk/brick_link.png b/media/img/icons/silk/brick_link.png new file mode 100755 index 000000000..9ebf013a2 Binary files /dev/null and b/media/img/icons/silk/brick_link.png differ diff --git a/media/img/icons/silk/bricks.png b/media/img/icons/silk/bricks.png new file mode 100755 index 000000000..0905f933b Binary files /dev/null and b/media/img/icons/silk/bricks.png differ diff --git a/media/img/icons/silk/briefcase.png b/media/img/icons/silk/briefcase.png new file mode 100755 index 000000000..05c564912 Binary files /dev/null and b/media/img/icons/silk/briefcase.png differ diff --git a/media/img/icons/silk/bug.png b/media/img/icons/silk/bug.png new file mode 100755 index 000000000..2d5fb90ec Binary files /dev/null and b/media/img/icons/silk/bug.png differ diff --git a/media/img/icons/silk/bug_add.png b/media/img/icons/silk/bug_add.png new file mode 100755 index 000000000..ced781747 Binary files /dev/null and b/media/img/icons/silk/bug_add.png differ diff --git a/media/img/icons/silk/bug_delete.png b/media/img/icons/silk/bug_delete.png new file mode 100755 index 000000000..e81d7571a Binary files /dev/null and b/media/img/icons/silk/bug_delete.png differ diff --git a/media/img/icons/silk/bug_edit.png b/media/img/icons/silk/bug_edit.png new file mode 100755 index 000000000..e5c7dc057 Binary files /dev/null and b/media/img/icons/silk/bug_edit.png differ diff --git a/media/img/icons/silk/bug_error.png b/media/img/icons/silk/bug_error.png new file mode 100755 index 000000000..c4e8c2809 Binary files /dev/null and b/media/img/icons/silk/bug_error.png differ diff --git a/media/img/icons/silk/bug_go.png b/media/img/icons/silk/bug_go.png new file mode 100755 index 000000000..4e4ae99db Binary files /dev/null and b/media/img/icons/silk/bug_go.png differ diff --git a/media/img/icons/silk/bug_link.png b/media/img/icons/silk/bug_link.png new file mode 100755 index 000000000..30e25abac Binary files /dev/null and b/media/img/icons/silk/bug_link.png differ diff --git a/media/img/icons/silk/building.png b/media/img/icons/silk/building.png new file mode 100755 index 000000000..11a017cfa Binary files /dev/null and b/media/img/icons/silk/building.png differ diff --git a/media/img/icons/silk/building_add.png b/media/img/icons/silk/building_add.png new file mode 100755 index 000000000..d88e2b9ab Binary files /dev/null and b/media/img/icons/silk/building_add.png differ diff --git a/media/img/icons/silk/building_delete.png b/media/img/icons/silk/building_delete.png new file mode 100755 index 000000000..db6455d2d Binary files /dev/null and b/media/img/icons/silk/building_delete.png differ diff --git a/media/img/icons/silk/building_edit.png b/media/img/icons/silk/building_edit.png new file mode 100755 index 000000000..646db36bf Binary files /dev/null and b/media/img/icons/silk/building_edit.png differ diff --git a/media/img/icons/silk/building_error.png b/media/img/icons/silk/building_error.png new file mode 100755 index 000000000..a342eefc6 Binary files /dev/null and b/media/img/icons/silk/building_error.png differ diff --git a/media/img/icons/silk/building_go.png b/media/img/icons/silk/building_go.png new file mode 100755 index 000000000..cdcbcb381 Binary files /dev/null and b/media/img/icons/silk/building_go.png differ diff --git a/media/img/icons/silk/building_key.png b/media/img/icons/silk/building_key.png new file mode 100755 index 000000000..8b79e30ed Binary files /dev/null and b/media/img/icons/silk/building_key.png differ diff --git a/media/img/icons/silk/building_link.png b/media/img/icons/silk/building_link.png new file mode 100755 index 000000000..a340629ac Binary files /dev/null and b/media/img/icons/silk/building_link.png differ diff --git a/media/img/icons/silk/bullet_add.png b/media/img/icons/silk/bullet_add.png new file mode 100755 index 000000000..41ff8335b Binary files /dev/null and b/media/img/icons/silk/bullet_add.png differ diff --git a/media/img/icons/silk/bullet_arrow_bottom.png b/media/img/icons/silk/bullet_arrow_bottom.png new file mode 100755 index 000000000..1a28d8250 Binary files /dev/null and b/media/img/icons/silk/bullet_arrow_bottom.png differ diff --git a/media/img/icons/silk/bullet_arrow_down.png b/media/img/icons/silk/bullet_arrow_down.png new file mode 100755 index 000000000..9b23c06d7 Binary files /dev/null and b/media/img/icons/silk/bullet_arrow_down.png differ diff --git a/media/img/icons/silk/bullet_arrow_top.png b/media/img/icons/silk/bullet_arrow_top.png new file mode 100755 index 000000000..0ce86d2b2 Binary files /dev/null and b/media/img/icons/silk/bullet_arrow_top.png differ diff --git a/media/img/icons/silk/bullet_arrow_up.png b/media/img/icons/silk/bullet_arrow_up.png new file mode 100755 index 000000000..24df0f421 Binary files /dev/null and b/media/img/icons/silk/bullet_arrow_up.png differ diff --git a/media/img/icons/silk/bullet_black.png b/media/img/icons/silk/bullet_black.png new file mode 100755 index 000000000..57619706d Binary files /dev/null and b/media/img/icons/silk/bullet_black.png differ diff --git a/media/img/icons/silk/bullet_blue.png b/media/img/icons/silk/bullet_blue.png new file mode 100755 index 000000000..a7651ec8a Binary files /dev/null and b/media/img/icons/silk/bullet_blue.png differ diff --git a/media/img/icons/silk/bullet_delete.png b/media/img/icons/silk/bullet_delete.png new file mode 100755 index 000000000..bd6271b24 Binary files /dev/null and b/media/img/icons/silk/bullet_delete.png differ diff --git a/media/img/icons/silk/bullet_disk.png b/media/img/icons/silk/bullet_disk.png new file mode 100755 index 000000000..209c6a786 Binary files /dev/null and b/media/img/icons/silk/bullet_disk.png differ diff --git a/media/img/icons/silk/bullet_error.png b/media/img/icons/silk/bullet_error.png new file mode 100755 index 000000000..bca2b491f Binary files /dev/null and b/media/img/icons/silk/bullet_error.png differ diff --git a/media/img/icons/silk/bullet_feed.png b/media/img/icons/silk/bullet_feed.png new file mode 100755 index 000000000..1a0e0f18f Binary files /dev/null and b/media/img/icons/silk/bullet_feed.png differ diff --git a/media/img/icons/silk/bullet_go.png b/media/img/icons/silk/bullet_go.png new file mode 100755 index 000000000..bc4faa709 Binary files /dev/null and b/media/img/icons/silk/bullet_go.png differ diff --git a/media/img/icons/silk/bullet_green.png b/media/img/icons/silk/bullet_green.png new file mode 100755 index 000000000..058ad261f Binary files /dev/null and b/media/img/icons/silk/bullet_green.png differ diff --git a/media/img/icons/silk/bullet_key.png b/media/img/icons/silk/bullet_key.png new file mode 100755 index 000000000..3d37f2ea4 Binary files /dev/null and b/media/img/icons/silk/bullet_key.png differ diff --git a/media/img/icons/silk/bullet_orange.png b/media/img/icons/silk/bullet_orange.png new file mode 100755 index 000000000..fa63024e5 Binary files /dev/null and b/media/img/icons/silk/bullet_orange.png differ diff --git a/media/img/icons/silk/bullet_picture.png b/media/img/icons/silk/bullet_picture.png new file mode 100755 index 000000000..386cb302f Binary files /dev/null and b/media/img/icons/silk/bullet_picture.png differ diff --git a/media/img/icons/silk/bullet_pink.png b/media/img/icons/silk/bullet_pink.png new file mode 100755 index 000000000..0c9f73e3f Binary files /dev/null and b/media/img/icons/silk/bullet_pink.png differ diff --git a/media/img/icons/silk/bullet_purple.png b/media/img/icons/silk/bullet_purple.png new file mode 100755 index 000000000..52ba5036b Binary files /dev/null and b/media/img/icons/silk/bullet_purple.png differ diff --git a/media/img/icons/silk/bullet_red.png b/media/img/icons/silk/bullet_red.png new file mode 100755 index 000000000..0cd803115 Binary files /dev/null and b/media/img/icons/silk/bullet_red.png differ diff --git a/media/img/icons/silk/bullet_star.png b/media/img/icons/silk/bullet_star.png new file mode 100755 index 000000000..fab774a32 Binary files /dev/null and b/media/img/icons/silk/bullet_star.png differ diff --git a/media/img/icons/silk/bullet_toggle_minus.png b/media/img/icons/silk/bullet_toggle_minus.png new file mode 100755 index 000000000..b47ce55f6 Binary files /dev/null and b/media/img/icons/silk/bullet_toggle_minus.png differ diff --git a/media/img/icons/silk/bullet_toggle_plus.png b/media/img/icons/silk/bullet_toggle_plus.png new file mode 100755 index 000000000..9ab4a8966 Binary files /dev/null and b/media/img/icons/silk/bullet_toggle_plus.png differ diff --git a/media/img/icons/silk/bullet_white.png b/media/img/icons/silk/bullet_white.png new file mode 100755 index 000000000..a9af8d44b Binary files /dev/null and b/media/img/icons/silk/bullet_white.png differ diff --git a/media/img/icons/silk/bullet_wrench.png b/media/img/icons/silk/bullet_wrench.png new file mode 100755 index 000000000..67817e6e5 Binary files /dev/null and b/media/img/icons/silk/bullet_wrench.png differ diff --git a/media/img/icons/silk/bullet_yellow.png b/media/img/icons/silk/bullet_yellow.png new file mode 100755 index 000000000..6469cea7e Binary files /dev/null and b/media/img/icons/silk/bullet_yellow.png differ diff --git a/media/img/icons/silk/cake.png b/media/img/icons/silk/cake.png new file mode 100755 index 000000000..4ef151aee Binary files /dev/null and b/media/img/icons/silk/cake.png differ diff --git a/media/img/icons/silk/calculator.png b/media/img/icons/silk/calculator.png new file mode 100755 index 000000000..701a60a5a Binary files /dev/null and b/media/img/icons/silk/calculator.png differ diff --git a/media/img/icons/silk/calculator_add.png b/media/img/icons/silk/calculator_add.png new file mode 100755 index 000000000..fd377bde3 Binary files /dev/null and b/media/img/icons/silk/calculator_add.png differ diff --git a/media/img/icons/silk/calculator_delete.png b/media/img/icons/silk/calculator_delete.png new file mode 100755 index 000000000..ac96170ec Binary files /dev/null and b/media/img/icons/silk/calculator_delete.png differ diff --git a/media/img/icons/silk/calculator_edit.png b/media/img/icons/silk/calculator_edit.png new file mode 100755 index 000000000..63b06b987 Binary files /dev/null and b/media/img/icons/silk/calculator_edit.png differ diff --git a/media/img/icons/silk/calculator_error.png b/media/img/icons/silk/calculator_error.png new file mode 100755 index 000000000..0bc4288a4 Binary files /dev/null and b/media/img/icons/silk/calculator_error.png differ diff --git a/media/img/icons/silk/calculator_link.png b/media/img/icons/silk/calculator_link.png new file mode 100755 index 000000000..a2a8fe69f Binary files /dev/null and b/media/img/icons/silk/calculator_link.png differ diff --git a/media/img/icons/silk/calendar.png b/media/img/icons/silk/calendar.png new file mode 100755 index 000000000..658913852 Binary files /dev/null and b/media/img/icons/silk/calendar.png differ diff --git a/media/img/icons/silk/calendar_add.png b/media/img/icons/silk/calendar_add.png new file mode 100755 index 000000000..17679db6b Binary files /dev/null and b/media/img/icons/silk/calendar_add.png differ diff --git a/media/img/icons/silk/calendar_delete.png b/media/img/icons/silk/calendar_delete.png new file mode 100755 index 000000000..69a3b10ad Binary files /dev/null and b/media/img/icons/silk/calendar_delete.png differ diff --git a/media/img/icons/silk/calendar_edit.png b/media/img/icons/silk/calendar_edit.png new file mode 100755 index 000000000..d1d2d6e68 Binary files /dev/null and b/media/img/icons/silk/calendar_edit.png differ diff --git a/media/img/icons/silk/calendar_link.png b/media/img/icons/silk/calendar_link.png new file mode 100755 index 000000000..6b106b942 Binary files /dev/null and b/media/img/icons/silk/calendar_link.png differ diff --git a/media/img/icons/silk/calendar_view_day.png b/media/img/icons/silk/calendar_view_day.png new file mode 100755 index 000000000..9740f76ee Binary files /dev/null and b/media/img/icons/silk/calendar_view_day.png differ diff --git a/media/img/icons/silk/calendar_view_month.png b/media/img/icons/silk/calendar_view_month.png new file mode 100755 index 000000000..6cff76c1d Binary files /dev/null and b/media/img/icons/silk/calendar_view_month.png differ diff --git a/media/img/icons/silk/calendar_view_week.png b/media/img/icons/silk/calendar_view_week.png new file mode 100755 index 000000000..8fe695f51 Binary files /dev/null and b/media/img/icons/silk/calendar_view_week.png differ diff --git a/media/img/icons/silk/camera.png b/media/img/icons/silk/camera.png new file mode 100755 index 000000000..8536d1a79 Binary files /dev/null and b/media/img/icons/silk/camera.png differ diff --git a/media/img/icons/silk/camera_add.png b/media/img/icons/silk/camera_add.png new file mode 100755 index 000000000..08b5da989 Binary files /dev/null and b/media/img/icons/silk/camera_add.png differ diff --git a/media/img/icons/silk/camera_delete.png b/media/img/icons/silk/camera_delete.png new file mode 100755 index 000000000..3846d74cd Binary files /dev/null and b/media/img/icons/silk/camera_delete.png differ diff --git a/media/img/icons/silk/camera_edit.png b/media/img/icons/silk/camera_edit.png new file mode 100755 index 000000000..b5015b103 Binary files /dev/null and b/media/img/icons/silk/camera_edit.png differ diff --git a/media/img/icons/silk/camera_error.png b/media/img/icons/silk/camera_error.png new file mode 100755 index 000000000..3c1bc95c9 Binary files /dev/null and b/media/img/icons/silk/camera_error.png differ diff --git a/media/img/icons/silk/camera_go.png b/media/img/icons/silk/camera_go.png new file mode 100755 index 000000000..94ce2b255 Binary files /dev/null and b/media/img/icons/silk/camera_go.png differ diff --git a/media/img/icons/silk/camera_link.png b/media/img/icons/silk/camera_link.png new file mode 100755 index 000000000..d2ac9f935 Binary files /dev/null and b/media/img/icons/silk/camera_link.png differ diff --git a/media/img/icons/silk/camera_small.png b/media/img/icons/silk/camera_small.png new file mode 100755 index 000000000..454b0b019 Binary files /dev/null and b/media/img/icons/silk/camera_small.png differ diff --git a/media/img/icons/silk/cancel.png b/media/img/icons/silk/cancel.png new file mode 100755 index 000000000..c149c2bc0 Binary files /dev/null and b/media/img/icons/silk/cancel.png differ diff --git a/media/img/icons/silk/car.png b/media/img/icons/silk/car.png new file mode 100755 index 000000000..4f3a770f0 Binary files /dev/null and b/media/img/icons/silk/car.png differ diff --git a/media/img/icons/silk/car_add.png b/media/img/icons/silk/car_add.png new file mode 100755 index 000000000..1215a51a4 Binary files /dev/null and b/media/img/icons/silk/car_add.png differ diff --git a/media/img/icons/silk/car_delete.png b/media/img/icons/silk/car_delete.png new file mode 100755 index 000000000..2803b5678 Binary files /dev/null and b/media/img/icons/silk/car_delete.png differ diff --git a/media/img/icons/silk/cart.png b/media/img/icons/silk/cart.png new file mode 100755 index 000000000..1baf7b9fd Binary files /dev/null and b/media/img/icons/silk/cart.png differ diff --git a/media/img/icons/silk/cart_add.png b/media/img/icons/silk/cart_add.png new file mode 100755 index 000000000..45c290008 Binary files /dev/null and b/media/img/icons/silk/cart_add.png differ diff --git a/media/img/icons/silk/cart_delete.png b/media/img/icons/silk/cart_delete.png new file mode 100755 index 000000000..ac5bce5c8 Binary files /dev/null and b/media/img/icons/silk/cart_delete.png differ diff --git a/media/img/icons/silk/cart_edit.png b/media/img/icons/silk/cart_edit.png new file mode 100755 index 000000000..b94ff88e6 Binary files /dev/null and b/media/img/icons/silk/cart_edit.png differ diff --git a/media/img/icons/silk/cart_error.png b/media/img/icons/silk/cart_error.png new file mode 100755 index 000000000..144c8353a Binary files /dev/null and b/media/img/icons/silk/cart_error.png differ diff --git a/media/img/icons/silk/cart_go.png b/media/img/icons/silk/cart_go.png new file mode 100755 index 000000000..20ee0584f Binary files /dev/null and b/media/img/icons/silk/cart_go.png differ diff --git a/media/img/icons/silk/cart_put.png b/media/img/icons/silk/cart_put.png new file mode 100755 index 000000000..3aec353e0 Binary files /dev/null and b/media/img/icons/silk/cart_put.png differ diff --git a/media/img/icons/silk/cart_remove.png b/media/img/icons/silk/cart_remove.png new file mode 100755 index 000000000..360217b52 Binary files /dev/null and b/media/img/icons/silk/cart_remove.png differ diff --git a/media/img/icons/silk/cd.png b/media/img/icons/silk/cd.png new file mode 100755 index 000000000..ef4322357 Binary files /dev/null and b/media/img/icons/silk/cd.png differ diff --git a/media/img/icons/silk/cd_add.png b/media/img/icons/silk/cd_add.png new file mode 100755 index 000000000..b0254effa Binary files /dev/null and b/media/img/icons/silk/cd_add.png differ diff --git a/media/img/icons/silk/cd_burn.png b/media/img/icons/silk/cd_burn.png new file mode 100755 index 000000000..157cb0ba9 Binary files /dev/null and b/media/img/icons/silk/cd_burn.png differ diff --git a/media/img/icons/silk/cd_delete.png b/media/img/icons/silk/cd_delete.png new file mode 100755 index 000000000..7d7b3d525 Binary files /dev/null and b/media/img/icons/silk/cd_delete.png differ diff --git a/media/img/icons/silk/cd_edit.png b/media/img/icons/silk/cd_edit.png new file mode 100755 index 000000000..b0dc194b0 Binary files /dev/null and b/media/img/icons/silk/cd_edit.png differ diff --git a/media/img/icons/silk/cd_eject.png b/media/img/icons/silk/cd_eject.png new file mode 100755 index 000000000..762932f6b Binary files /dev/null and b/media/img/icons/silk/cd_eject.png differ diff --git a/media/img/icons/silk/cd_go.png b/media/img/icons/silk/cd_go.png new file mode 100755 index 000000000..13e04991c Binary files /dev/null and b/media/img/icons/silk/cd_go.png differ diff --git a/media/img/icons/silk/chart_bar.png b/media/img/icons/silk/chart_bar.png new file mode 100755 index 000000000..9051fbc60 Binary files /dev/null and b/media/img/icons/silk/chart_bar.png differ diff --git a/media/img/icons/silk/chart_bar_add.png b/media/img/icons/silk/chart_bar_add.png new file mode 100755 index 000000000..d283e846a Binary files /dev/null and b/media/img/icons/silk/chart_bar_add.png differ diff --git a/media/img/icons/silk/chart_bar_delete.png b/media/img/icons/silk/chart_bar_delete.png new file mode 100755 index 000000000..259f68687 Binary files /dev/null and b/media/img/icons/silk/chart_bar_delete.png differ diff --git a/media/img/icons/silk/chart_bar_edit.png b/media/img/icons/silk/chart_bar_edit.png new file mode 100755 index 000000000..df64d97e6 Binary files /dev/null and b/media/img/icons/silk/chart_bar_edit.png differ diff --git a/media/img/icons/silk/chart_bar_error.png b/media/img/icons/silk/chart_bar_error.png new file mode 100755 index 000000000..bdacea5e5 Binary files /dev/null and b/media/img/icons/silk/chart_bar_error.png differ diff --git a/media/img/icons/silk/chart_bar_link.png b/media/img/icons/silk/chart_bar_link.png new file mode 100755 index 000000000..bf18aed48 Binary files /dev/null and b/media/img/icons/silk/chart_bar_link.png differ diff --git a/media/img/icons/silk/chart_curve.png b/media/img/icons/silk/chart_curve.png new file mode 100755 index 000000000..01e933a61 Binary files /dev/null and b/media/img/icons/silk/chart_curve.png differ diff --git a/media/img/icons/silk/chart_curve_add.png b/media/img/icons/silk/chart_curve_add.png new file mode 100755 index 000000000..f9e205046 Binary files /dev/null and b/media/img/icons/silk/chart_curve_add.png differ diff --git a/media/img/icons/silk/chart_curve_delete.png b/media/img/icons/silk/chart_curve_delete.png new file mode 100755 index 000000000..b411391c4 Binary files /dev/null and b/media/img/icons/silk/chart_curve_delete.png differ diff --git a/media/img/icons/silk/chart_curve_edit.png b/media/img/icons/silk/chart_curve_edit.png new file mode 100755 index 000000000..bd07673b5 Binary files /dev/null and b/media/img/icons/silk/chart_curve_edit.png differ diff --git a/media/img/icons/silk/chart_curve_error.png b/media/img/icons/silk/chart_curve_error.png new file mode 100755 index 000000000..906dd0383 Binary files /dev/null and b/media/img/icons/silk/chart_curve_error.png differ diff --git a/media/img/icons/silk/chart_curve_go.png b/media/img/icons/silk/chart_curve_go.png new file mode 100755 index 000000000..ac9eda51a Binary files /dev/null and b/media/img/icons/silk/chart_curve_go.png differ diff --git a/media/img/icons/silk/chart_curve_link.png b/media/img/icons/silk/chart_curve_link.png new file mode 100755 index 000000000..144eafe08 Binary files /dev/null and b/media/img/icons/silk/chart_curve_link.png differ diff --git a/media/img/icons/silk/chart_line.png b/media/img/icons/silk/chart_line.png new file mode 100755 index 000000000..85020f320 Binary files /dev/null and b/media/img/icons/silk/chart_line.png differ diff --git a/media/img/icons/silk/chart_line_add.png b/media/img/icons/silk/chart_line_add.png new file mode 100755 index 000000000..5571a5ebc Binary files /dev/null and b/media/img/icons/silk/chart_line_add.png differ diff --git a/media/img/icons/silk/chart_line_delete.png b/media/img/icons/silk/chart_line_delete.png new file mode 100755 index 000000000..5b0aa9012 Binary files /dev/null and b/media/img/icons/silk/chart_line_delete.png differ diff --git a/media/img/icons/silk/chart_line_edit.png b/media/img/icons/silk/chart_line_edit.png new file mode 100755 index 000000000..9cf660733 Binary files /dev/null and b/media/img/icons/silk/chart_line_edit.png differ diff --git a/media/img/icons/silk/chart_line_error.png b/media/img/icons/silk/chart_line_error.png new file mode 100755 index 000000000..ff23c03a8 Binary files /dev/null and b/media/img/icons/silk/chart_line_error.png differ diff --git a/media/img/icons/silk/chart_line_link.png b/media/img/icons/silk/chart_line_link.png new file mode 100755 index 000000000..f3727d22e Binary files /dev/null and b/media/img/icons/silk/chart_line_link.png differ diff --git a/media/img/icons/silk/chart_organisation.png b/media/img/icons/silk/chart_organisation.png new file mode 100755 index 000000000..c32d25c16 Binary files /dev/null and b/media/img/icons/silk/chart_organisation.png differ diff --git a/media/img/icons/silk/chart_organisation_add.png b/media/img/icons/silk/chart_organisation_add.png new file mode 100755 index 000000000..f0dba4ac4 Binary files /dev/null and b/media/img/icons/silk/chart_organisation_add.png differ diff --git a/media/img/icons/silk/chart_organisation_delete.png b/media/img/icons/silk/chart_organisation_delete.png new file mode 100755 index 000000000..7dc8dcac2 Binary files /dev/null and b/media/img/icons/silk/chart_organisation_delete.png differ diff --git a/media/img/icons/silk/chart_pie.png b/media/img/icons/silk/chart_pie.png new file mode 100755 index 000000000..fe00fa050 Binary files /dev/null and b/media/img/icons/silk/chart_pie.png differ diff --git a/media/img/icons/silk/chart_pie_add.png b/media/img/icons/silk/chart_pie_add.png new file mode 100755 index 000000000..bf0822ee1 Binary files /dev/null and b/media/img/icons/silk/chart_pie_add.png differ diff --git a/media/img/icons/silk/chart_pie_delete.png b/media/img/icons/silk/chart_pie_delete.png new file mode 100755 index 000000000..5ab9efd55 Binary files /dev/null and b/media/img/icons/silk/chart_pie_delete.png differ diff --git a/media/img/icons/silk/chart_pie_edit.png b/media/img/icons/silk/chart_pie_edit.png new file mode 100755 index 000000000..3debc12d5 Binary files /dev/null and b/media/img/icons/silk/chart_pie_edit.png differ diff --git a/media/img/icons/silk/chart_pie_error.png b/media/img/icons/silk/chart_pie_error.png new file mode 100755 index 000000000..7344174fe Binary files /dev/null and b/media/img/icons/silk/chart_pie_error.png differ diff --git a/media/img/icons/silk/chart_pie_link.png b/media/img/icons/silk/chart_pie_link.png new file mode 100755 index 000000000..c072f8e05 Binary files /dev/null and b/media/img/icons/silk/chart_pie_link.png differ diff --git a/media/img/icons/silk/clock.png b/media/img/icons/silk/clock.png new file mode 100755 index 000000000..e2672c206 Binary files /dev/null and b/media/img/icons/silk/clock.png differ diff --git a/media/img/icons/silk/clock_add.png b/media/img/icons/silk/clock_add.png new file mode 100755 index 000000000..598b839b8 Binary files /dev/null and b/media/img/icons/silk/clock_add.png differ diff --git a/media/img/icons/silk/clock_delete.png b/media/img/icons/silk/clock_delete.png new file mode 100755 index 000000000..8bf9efe4b Binary files /dev/null and b/media/img/icons/silk/clock_delete.png differ diff --git a/media/img/icons/silk/clock_edit.png b/media/img/icons/silk/clock_edit.png new file mode 100755 index 000000000..7d3571887 Binary files /dev/null and b/media/img/icons/silk/clock_edit.png differ diff --git a/media/img/icons/silk/clock_error.png b/media/img/icons/silk/clock_error.png new file mode 100755 index 000000000..a7c461ba9 Binary files /dev/null and b/media/img/icons/silk/clock_error.png differ diff --git a/media/img/icons/silk/clock_go.png b/media/img/icons/silk/clock_go.png new file mode 100755 index 000000000..a1a24d3f3 Binary files /dev/null and b/media/img/icons/silk/clock_go.png differ diff --git a/media/img/icons/silk/clock_link.png b/media/img/icons/silk/clock_link.png new file mode 100755 index 000000000..481cf04c1 Binary files /dev/null and b/media/img/icons/silk/clock_link.png differ diff --git a/media/img/icons/silk/clock_pause.png b/media/img/icons/silk/clock_pause.png new file mode 100755 index 000000000..ba7472585 Binary files /dev/null and b/media/img/icons/silk/clock_pause.png differ diff --git a/media/img/icons/silk/clock_play.png b/media/img/icons/silk/clock_play.png new file mode 100755 index 000000000..fb4ebc850 Binary files /dev/null and b/media/img/icons/silk/clock_play.png differ diff --git a/media/img/icons/silk/clock_red.png b/media/img/icons/silk/clock_red.png new file mode 100755 index 000000000..2842cc338 Binary files /dev/null and b/media/img/icons/silk/clock_red.png differ diff --git a/media/img/icons/silk/clock_stop.png b/media/img/icons/silk/clock_stop.png new file mode 100755 index 000000000..6fe8a6f95 Binary files /dev/null and b/media/img/icons/silk/clock_stop.png differ diff --git a/media/img/icons/silk/cog.png b/media/img/icons/silk/cog.png new file mode 100755 index 000000000..67de2c6cc Binary files /dev/null and b/media/img/icons/silk/cog.png differ diff --git a/media/img/icons/silk/cog_add.png b/media/img/icons/silk/cog_add.png new file mode 100755 index 000000000..04f22badc Binary files /dev/null and b/media/img/icons/silk/cog_add.png differ diff --git a/media/img/icons/silk/cog_delete.png b/media/img/icons/silk/cog_delete.png new file mode 100755 index 000000000..8ce71c475 Binary files /dev/null and b/media/img/icons/silk/cog_delete.png differ diff --git a/media/img/icons/silk/cog_edit.png b/media/img/icons/silk/cog_edit.png new file mode 100755 index 000000000..47b75a456 Binary files /dev/null and b/media/img/icons/silk/cog_edit.png differ diff --git a/media/img/icons/silk/cog_error.png b/media/img/icons/silk/cog_error.png new file mode 100755 index 000000000..47667430a Binary files /dev/null and b/media/img/icons/silk/cog_error.png differ diff --git a/media/img/icons/silk/cog_go.png b/media/img/icons/silk/cog_go.png new file mode 100755 index 000000000..3262767cd Binary files /dev/null and b/media/img/icons/silk/cog_go.png differ diff --git a/media/img/icons/silk/coins.png b/media/img/icons/silk/coins.png new file mode 100755 index 000000000..0ca9074d6 Binary files /dev/null and b/media/img/icons/silk/coins.png differ diff --git a/media/img/icons/silk/coins_add.png b/media/img/icons/silk/coins_add.png new file mode 100755 index 000000000..cdff5d3d4 Binary files /dev/null and b/media/img/icons/silk/coins_add.png differ diff --git a/media/img/icons/silk/coins_delete.png b/media/img/icons/silk/coins_delete.png new file mode 100755 index 000000000..18e0c0fd9 Binary files /dev/null and b/media/img/icons/silk/coins_delete.png differ diff --git a/media/img/icons/silk/color_swatch.png b/media/img/icons/silk/color_swatch.png new file mode 100755 index 000000000..6e6e85212 Binary files /dev/null and b/media/img/icons/silk/color_swatch.png differ diff --git a/media/img/icons/silk/color_wheel.png b/media/img/icons/silk/color_wheel.png new file mode 100755 index 000000000..809fb00e5 Binary files /dev/null and b/media/img/icons/silk/color_wheel.png differ diff --git a/media/img/icons/silk/comment.png b/media/img/icons/silk/comment.png new file mode 100755 index 000000000..7bc9233ea Binary files /dev/null and b/media/img/icons/silk/comment.png differ diff --git a/media/img/icons/silk/comment_add.png b/media/img/icons/silk/comment_add.png new file mode 100755 index 000000000..75e78dede Binary files /dev/null and b/media/img/icons/silk/comment_add.png differ diff --git a/media/img/icons/silk/comment_delete.png b/media/img/icons/silk/comment_delete.png new file mode 100755 index 000000000..643fdbe8d Binary files /dev/null and b/media/img/icons/silk/comment_delete.png differ diff --git a/media/img/icons/silk/comment_edit.png b/media/img/icons/silk/comment_edit.png new file mode 100755 index 000000000..73db110df Binary files /dev/null and b/media/img/icons/silk/comment_edit.png differ diff --git a/media/img/icons/silk/comments.png b/media/img/icons/silk/comments.png new file mode 100755 index 000000000..39433cf78 Binary files /dev/null and b/media/img/icons/silk/comments.png differ diff --git a/media/img/icons/silk/comments_add.png b/media/img/icons/silk/comments_add.png new file mode 100755 index 000000000..b32563442 Binary files /dev/null and b/media/img/icons/silk/comments_add.png differ diff --git a/media/img/icons/silk/comments_delete.png b/media/img/icons/silk/comments_delete.png new file mode 100755 index 000000000..6df7376d0 Binary files /dev/null and b/media/img/icons/silk/comments_delete.png differ diff --git a/media/img/icons/silk/compress.png b/media/img/icons/silk/compress.png new file mode 100755 index 000000000..8606ff0fd Binary files /dev/null and b/media/img/icons/silk/compress.png differ diff --git a/media/img/icons/silk/computer.png b/media/img/icons/silk/computer.png new file mode 100755 index 000000000..9bc37dce3 Binary files /dev/null and b/media/img/icons/silk/computer.png differ diff --git a/media/img/icons/silk/computer_add.png b/media/img/icons/silk/computer_add.png new file mode 100755 index 000000000..db604ee3b Binary files /dev/null and b/media/img/icons/silk/computer_add.png differ diff --git a/media/img/icons/silk/computer_delete.png b/media/img/icons/silk/computer_delete.png new file mode 100755 index 000000000..5e9b26836 Binary files /dev/null and b/media/img/icons/silk/computer_delete.png differ diff --git a/media/img/icons/silk/computer_edit.png b/media/img/icons/silk/computer_edit.png new file mode 100755 index 000000000..34c72fe52 Binary files /dev/null and b/media/img/icons/silk/computer_edit.png differ diff --git a/media/img/icons/silk/computer_error.png b/media/img/icons/silk/computer_error.png new file mode 100755 index 000000000..b2c3ed578 Binary files /dev/null and b/media/img/icons/silk/computer_error.png differ diff --git a/media/img/icons/silk/computer_go.png b/media/img/icons/silk/computer_go.png new file mode 100755 index 000000000..0b26144d8 Binary files /dev/null and b/media/img/icons/silk/computer_go.png differ diff --git a/media/img/icons/silk/computer_key.png b/media/img/icons/silk/computer_key.png new file mode 100755 index 000000000..eca543017 Binary files /dev/null and b/media/img/icons/silk/computer_key.png differ diff --git a/media/img/icons/silk/computer_link.png b/media/img/icons/silk/computer_link.png new file mode 100755 index 000000000..3859db214 Binary files /dev/null and b/media/img/icons/silk/computer_link.png differ diff --git a/media/img/icons/silk/connect.png b/media/img/icons/silk/connect.png new file mode 100755 index 000000000..024138eb3 Binary files /dev/null and b/media/img/icons/silk/connect.png differ diff --git a/media/img/icons/silk/contrast.png b/media/img/icons/silk/contrast.png new file mode 100755 index 000000000..adcc0046f Binary files /dev/null and b/media/img/icons/silk/contrast.png differ diff --git a/media/img/icons/silk/contrast_decrease.png b/media/img/icons/silk/contrast_decrease.png new file mode 100755 index 000000000..0155bf5c0 Binary files /dev/null and b/media/img/icons/silk/contrast_decrease.png differ diff --git a/media/img/icons/silk/contrast_high.png b/media/img/icons/silk/contrast_high.png new file mode 100755 index 000000000..d87c8cbb7 Binary files /dev/null and b/media/img/icons/silk/contrast_high.png differ diff --git a/media/img/icons/silk/contrast_increase.png b/media/img/icons/silk/contrast_increase.png new file mode 100755 index 000000000..a3e7f5200 Binary files /dev/null and b/media/img/icons/silk/contrast_increase.png differ diff --git a/media/img/icons/silk/contrast_low.png b/media/img/icons/silk/contrast_low.png new file mode 100755 index 000000000..dc9f4b100 Binary files /dev/null and b/media/img/icons/silk/contrast_low.png differ diff --git a/media/img/icons/silk/control_eject.png b/media/img/icons/silk/control_eject.png new file mode 100755 index 000000000..924d817bb Binary files /dev/null and b/media/img/icons/silk/control_eject.png differ diff --git a/media/img/icons/silk/control_eject_blue.png b/media/img/icons/silk/control_eject_blue.png new file mode 100755 index 000000000..2bd496383 Binary files /dev/null and b/media/img/icons/silk/control_eject_blue.png differ diff --git a/media/img/icons/silk/control_end.png b/media/img/icons/silk/control_end.png new file mode 100755 index 000000000..036e04dcd Binary files /dev/null and b/media/img/icons/silk/control_end.png differ diff --git a/media/img/icons/silk/control_end_blue.png b/media/img/icons/silk/control_end_blue.png new file mode 100755 index 000000000..720793576 Binary files /dev/null and b/media/img/icons/silk/control_end_blue.png differ diff --git a/media/img/icons/silk/control_equalizer.png b/media/img/icons/silk/control_equalizer.png new file mode 100755 index 000000000..46060872c Binary files /dev/null and b/media/img/icons/silk/control_equalizer.png differ diff --git a/media/img/icons/silk/control_equalizer_blue.png b/media/img/icons/silk/control_equalizer_blue.png new file mode 100755 index 000000000..1b2e6a374 Binary files /dev/null and b/media/img/icons/silk/control_equalizer_blue.png differ diff --git a/media/img/icons/silk/control_fastforward.png b/media/img/icons/silk/control_fastforward.png new file mode 100755 index 000000000..31f7fd3ad Binary files /dev/null and b/media/img/icons/silk/control_fastforward.png differ diff --git a/media/img/icons/silk/control_fastforward_blue.png b/media/img/icons/silk/control_fastforward_blue.png new file mode 100755 index 000000000..4a2f9d4e4 Binary files /dev/null and b/media/img/icons/silk/control_fastforward_blue.png differ diff --git a/media/img/icons/silk/control_pause.png b/media/img/icons/silk/control_pause.png new file mode 100755 index 000000000..2d9ce9c4e Binary files /dev/null and b/media/img/icons/silk/control_pause.png differ diff --git a/media/img/icons/silk/control_pause_blue.png b/media/img/icons/silk/control_pause_blue.png new file mode 100755 index 000000000..ec61099b0 Binary files /dev/null and b/media/img/icons/silk/control_pause_blue.png differ diff --git a/media/img/icons/silk/control_play.png b/media/img/icons/silk/control_play.png new file mode 100755 index 000000000..0846555d0 Binary files /dev/null and b/media/img/icons/silk/control_play.png differ diff --git a/media/img/icons/silk/control_play_blue.png b/media/img/icons/silk/control_play_blue.png new file mode 100755 index 000000000..f8c8ec683 Binary files /dev/null and b/media/img/icons/silk/control_play_blue.png differ diff --git a/media/img/icons/silk/control_repeat.png b/media/img/icons/silk/control_repeat.png new file mode 100755 index 000000000..1c4f57a16 Binary files /dev/null and b/media/img/icons/silk/control_repeat.png differ diff --git a/media/img/icons/silk/control_repeat_blue.png b/media/img/icons/silk/control_repeat_blue.png new file mode 100755 index 000000000..406ec333b Binary files /dev/null and b/media/img/icons/silk/control_repeat_blue.png differ diff --git a/media/img/icons/silk/control_rewind.png b/media/img/icons/silk/control_rewind.png new file mode 100755 index 000000000..c02944771 Binary files /dev/null and b/media/img/icons/silk/control_rewind.png differ diff --git a/media/img/icons/silk/control_rewind_blue.png b/media/img/icons/silk/control_rewind_blue.png new file mode 100755 index 000000000..15d1584bd Binary files /dev/null and b/media/img/icons/silk/control_rewind_blue.png differ diff --git a/media/img/icons/silk/control_start.png b/media/img/icons/silk/control_start.png new file mode 100755 index 000000000..7dd1c07fb Binary files /dev/null and b/media/img/icons/silk/control_start.png differ diff --git a/media/img/icons/silk/control_start_blue.png b/media/img/icons/silk/control_start_blue.png new file mode 100755 index 000000000..6f11fcb08 Binary files /dev/null and b/media/img/icons/silk/control_start_blue.png differ diff --git a/media/img/icons/silk/control_stop.png b/media/img/icons/silk/control_stop.png new file mode 100755 index 000000000..893bb60e5 Binary files /dev/null and b/media/img/icons/silk/control_stop.png differ diff --git a/media/img/icons/silk/control_stop_blue.png b/media/img/icons/silk/control_stop_blue.png new file mode 100755 index 000000000..e6f75d232 Binary files /dev/null and b/media/img/icons/silk/control_stop_blue.png differ diff --git a/media/img/icons/silk/controller.png b/media/img/icons/silk/controller.png new file mode 100755 index 000000000..5cf76ed02 Binary files /dev/null and b/media/img/icons/silk/controller.png differ diff --git a/media/img/icons/silk/controller_add.png b/media/img/icons/silk/controller_add.png new file mode 100755 index 000000000..efecb3871 Binary files /dev/null and b/media/img/icons/silk/controller_add.png differ diff --git a/media/img/icons/silk/controller_delete.png b/media/img/icons/silk/controller_delete.png new file mode 100755 index 000000000..3d83bc7b9 Binary files /dev/null and b/media/img/icons/silk/controller_delete.png differ diff --git a/media/img/icons/silk/controller_error.png b/media/img/icons/silk/controller_error.png new file mode 100755 index 000000000..7f17c0cb7 Binary files /dev/null and b/media/img/icons/silk/controller_error.png differ diff --git a/media/img/icons/silk/creditcards.png b/media/img/icons/silk/creditcards.png new file mode 100755 index 000000000..4eae583e1 Binary files /dev/null and b/media/img/icons/silk/creditcards.png differ diff --git a/media/img/icons/silk/cross.png b/media/img/icons/silk/cross.png new file mode 100755 index 000000000..1514d51a3 Binary files /dev/null and b/media/img/icons/silk/cross.png differ diff --git a/media/img/icons/silk/css.png b/media/img/icons/silk/css.png new file mode 100755 index 000000000..23f310181 Binary files /dev/null and b/media/img/icons/silk/css.png differ diff --git a/media/img/icons/silk/css_add.png b/media/img/icons/silk/css_add.png new file mode 100755 index 000000000..e8ea10fcd Binary files /dev/null and b/media/img/icons/silk/css_add.png differ diff --git a/media/img/icons/silk/css_delete.png b/media/img/icons/silk/css_delete.png new file mode 100755 index 000000000..326aba440 Binary files /dev/null and b/media/img/icons/silk/css_delete.png differ diff --git a/media/img/icons/silk/css_go.png b/media/img/icons/silk/css_go.png new file mode 100755 index 000000000..6cdf38c36 Binary files /dev/null and b/media/img/icons/silk/css_go.png differ diff --git a/media/img/icons/silk/css_valid.png b/media/img/icons/silk/css_valid.png new file mode 100755 index 000000000..4c72ca5a4 Binary files /dev/null and b/media/img/icons/silk/css_valid.png differ diff --git a/media/img/icons/silk/cup.png b/media/img/icons/silk/cup.png new file mode 100755 index 000000000..b7bfcd15f Binary files /dev/null and b/media/img/icons/silk/cup.png differ diff --git a/media/img/icons/silk/cup_add.png b/media/img/icons/silk/cup_add.png new file mode 100755 index 000000000..4ecaece29 Binary files /dev/null and b/media/img/icons/silk/cup_add.png differ diff --git a/media/img/icons/silk/cup_delete.png b/media/img/icons/silk/cup_delete.png new file mode 100755 index 000000000..59a6d9c61 Binary files /dev/null and b/media/img/icons/silk/cup_delete.png differ diff --git a/media/img/icons/silk/cup_edit.png b/media/img/icons/silk/cup_edit.png new file mode 100755 index 000000000..0b8f1e1ef Binary files /dev/null and b/media/img/icons/silk/cup_edit.png differ diff --git a/media/img/icons/silk/cup_error.png b/media/img/icons/silk/cup_error.png new file mode 100755 index 000000000..68798748d Binary files /dev/null and b/media/img/icons/silk/cup_error.png differ diff --git a/media/img/icons/silk/cup_go.png b/media/img/icons/silk/cup_go.png new file mode 100755 index 000000000..9527efbe4 Binary files /dev/null and b/media/img/icons/silk/cup_go.png differ diff --git a/media/img/icons/silk/cup_key.png b/media/img/icons/silk/cup_key.png new file mode 100755 index 000000000..7ae160ce2 Binary files /dev/null and b/media/img/icons/silk/cup_key.png differ diff --git a/media/img/icons/silk/cup_link.png b/media/img/icons/silk/cup_link.png new file mode 100755 index 000000000..41d1ace1a Binary files /dev/null and b/media/img/icons/silk/cup_link.png differ diff --git a/media/img/icons/silk/cursor.png b/media/img/icons/silk/cursor.png new file mode 100755 index 000000000..532f532d8 Binary files /dev/null and b/media/img/icons/silk/cursor.png differ diff --git a/media/img/icons/silk/cut.png b/media/img/icons/silk/cut.png new file mode 100755 index 000000000..f215d6f6b Binary files /dev/null and b/media/img/icons/silk/cut.png differ diff --git a/media/img/icons/silk/cut_red.png b/media/img/icons/silk/cut_red.png new file mode 100755 index 000000000..85bb2f0fd Binary files /dev/null and b/media/img/icons/silk/cut_red.png differ diff --git a/media/img/icons/silk/database.png b/media/img/icons/silk/database.png new file mode 100755 index 000000000..3d09261a2 Binary files /dev/null and b/media/img/icons/silk/database.png differ diff --git a/media/img/icons/silk/database_add.png b/media/img/icons/silk/database_add.png new file mode 100755 index 000000000..802bd6cde Binary files /dev/null and b/media/img/icons/silk/database_add.png differ diff --git a/media/img/icons/silk/database_connect.png b/media/img/icons/silk/database_connect.png new file mode 100755 index 000000000..3a111977c Binary files /dev/null and b/media/img/icons/silk/database_connect.png differ diff --git a/media/img/icons/silk/database_delete.png b/media/img/icons/silk/database_delete.png new file mode 100755 index 000000000..cce652e84 Binary files /dev/null and b/media/img/icons/silk/database_delete.png differ diff --git a/media/img/icons/silk/database_edit.png b/media/img/icons/silk/database_edit.png new file mode 100755 index 000000000..e501b668c Binary files /dev/null and b/media/img/icons/silk/database_edit.png differ diff --git a/media/img/icons/silk/database_error.png b/media/img/icons/silk/database_error.png new file mode 100755 index 000000000..578221aaa Binary files /dev/null and b/media/img/icons/silk/database_error.png differ diff --git a/media/img/icons/silk/database_gear.png b/media/img/icons/silk/database_gear.png new file mode 100755 index 000000000..7c0ab2b4c Binary files /dev/null and b/media/img/icons/silk/database_gear.png differ diff --git a/media/img/icons/silk/database_go.png b/media/img/icons/silk/database_go.png new file mode 100755 index 000000000..61a8556c4 Binary files /dev/null and b/media/img/icons/silk/database_go.png differ diff --git a/media/img/icons/silk/database_key.png b/media/img/icons/silk/database_key.png new file mode 100755 index 000000000..333414767 Binary files /dev/null and b/media/img/icons/silk/database_key.png differ diff --git a/media/img/icons/silk/database_lightning.png b/media/img/icons/silk/database_lightning.png new file mode 100755 index 000000000..d9eefc225 Binary files /dev/null and b/media/img/icons/silk/database_lightning.png differ diff --git a/media/img/icons/silk/database_link.png b/media/img/icons/silk/database_link.png new file mode 100755 index 000000000..4c8204af1 Binary files /dev/null and b/media/img/icons/silk/database_link.png differ diff --git a/media/img/icons/silk/database_refresh.png b/media/img/icons/silk/database_refresh.png new file mode 100755 index 000000000..ff803be12 Binary files /dev/null and b/media/img/icons/silk/database_refresh.png differ diff --git a/media/img/icons/silk/database_save.png b/media/img/icons/silk/database_save.png new file mode 100755 index 000000000..44c06dddf Binary files /dev/null and b/media/img/icons/silk/database_save.png differ diff --git a/media/img/icons/silk/database_table.png b/media/img/icons/silk/database_table.png new file mode 100755 index 000000000..693709cbc Binary files /dev/null and b/media/img/icons/silk/database_table.png differ diff --git a/media/img/icons/silk/date.png b/media/img/icons/silk/date.png new file mode 100755 index 000000000..783c83357 Binary files /dev/null and b/media/img/icons/silk/date.png differ diff --git a/media/img/icons/silk/date_add.png b/media/img/icons/silk/date_add.png new file mode 100755 index 000000000..6a7ae025f Binary files /dev/null and b/media/img/icons/silk/date_add.png differ diff --git a/media/img/icons/silk/date_delete.png b/media/img/icons/silk/date_delete.png new file mode 100755 index 000000000..969a6b723 Binary files /dev/null and b/media/img/icons/silk/date_delete.png differ diff --git a/media/img/icons/silk/date_edit.png b/media/img/icons/silk/date_edit.png new file mode 100755 index 000000000..e68106503 Binary files /dev/null and b/media/img/icons/silk/date_edit.png differ diff --git a/media/img/icons/silk/date_error.png b/media/img/icons/silk/date_error.png new file mode 100755 index 000000000..442cd97d0 Binary files /dev/null and b/media/img/icons/silk/date_error.png differ diff --git a/media/img/icons/silk/date_go.png b/media/img/icons/silk/date_go.png new file mode 100755 index 000000000..52dd9f3ae Binary files /dev/null and b/media/img/icons/silk/date_go.png differ diff --git a/media/img/icons/silk/date_link.png b/media/img/icons/silk/date_link.png new file mode 100755 index 000000000..9f0aada71 Binary files /dev/null and b/media/img/icons/silk/date_link.png differ diff --git a/media/img/icons/silk/date_magnify.png b/media/img/icons/silk/date_magnify.png new file mode 100755 index 000000000..cd05f1906 Binary files /dev/null and b/media/img/icons/silk/date_magnify.png differ diff --git a/media/img/icons/silk/date_next.png b/media/img/icons/silk/date_next.png new file mode 100755 index 000000000..48d740abf Binary files /dev/null and b/media/img/icons/silk/date_next.png differ diff --git a/media/img/icons/silk/date_previous.png b/media/img/icons/silk/date_previous.png new file mode 100755 index 000000000..e117a8374 Binary files /dev/null and b/media/img/icons/silk/date_previous.png differ diff --git a/media/img/icons/silk/delete.png b/media/img/icons/silk/delete.png new file mode 100755 index 000000000..08f249365 Binary files /dev/null and b/media/img/icons/silk/delete.png differ diff --git a/media/img/icons/silk/disconnect.png b/media/img/icons/silk/disconnect.png new file mode 100755 index 000000000..b335cb11c Binary files /dev/null and b/media/img/icons/silk/disconnect.png differ diff --git a/media/img/icons/silk/disk.png b/media/img/icons/silk/disk.png new file mode 100755 index 000000000..99d532e8b Binary files /dev/null and b/media/img/icons/silk/disk.png differ diff --git a/media/img/icons/silk/disk_multiple.png b/media/img/icons/silk/disk_multiple.png new file mode 100755 index 000000000..fc5a52f5e Binary files /dev/null and b/media/img/icons/silk/disk_multiple.png differ diff --git a/media/img/icons/silk/door.png b/media/img/icons/silk/door.png new file mode 100755 index 000000000..369fc46ed Binary files /dev/null and b/media/img/icons/silk/door.png differ diff --git a/media/img/icons/silk/door_in.png b/media/img/icons/silk/door_in.png new file mode 100755 index 000000000..41676a0a5 Binary files /dev/null and b/media/img/icons/silk/door_in.png differ diff --git a/media/img/icons/silk/door_open.png b/media/img/icons/silk/door_open.png new file mode 100755 index 000000000..64bab57dd Binary files /dev/null and b/media/img/icons/silk/door_open.png differ diff --git a/media/img/icons/silk/door_out.png b/media/img/icons/silk/door_out.png new file mode 100755 index 000000000..2541d2bcb Binary files /dev/null and b/media/img/icons/silk/door_out.png differ diff --git a/media/img/icons/silk/drink.png b/media/img/icons/silk/drink.png new file mode 100755 index 000000000..d98359c2a Binary files /dev/null and b/media/img/icons/silk/drink.png differ diff --git a/media/img/icons/silk/drink_empty.png b/media/img/icons/silk/drink_empty.png new file mode 100755 index 000000000..a40211ed4 Binary files /dev/null and b/media/img/icons/silk/drink_empty.png differ diff --git a/media/img/icons/silk/drive.png b/media/img/icons/silk/drive.png new file mode 100755 index 000000000..37b7c9b27 Binary files /dev/null and b/media/img/icons/silk/drive.png differ diff --git a/media/img/icons/silk/drive_add.png b/media/img/icons/silk/drive_add.png new file mode 100755 index 000000000..29a35d5aa Binary files /dev/null and b/media/img/icons/silk/drive_add.png differ diff --git a/media/img/icons/silk/drive_burn.png b/media/img/icons/silk/drive_burn.png new file mode 100755 index 000000000..80fd79f98 Binary files /dev/null and b/media/img/icons/silk/drive_burn.png differ diff --git a/media/img/icons/silk/drive_cd.png b/media/img/icons/silk/drive_cd.png new file mode 100755 index 000000000..1850b701c Binary files /dev/null and b/media/img/icons/silk/drive_cd.png differ diff --git a/media/img/icons/silk/drive_cd_empty.png b/media/img/icons/silk/drive_cd_empty.png new file mode 100755 index 000000000..8df38d9e6 Binary files /dev/null and b/media/img/icons/silk/drive_cd_empty.png differ diff --git a/media/img/icons/silk/drive_delete.png b/media/img/icons/silk/drive_delete.png new file mode 100755 index 000000000..e6eb18665 Binary files /dev/null and b/media/img/icons/silk/drive_delete.png differ diff --git a/media/img/icons/silk/drive_disk.png b/media/img/icons/silk/drive_disk.png new file mode 100755 index 000000000..5a51e8198 Binary files /dev/null and b/media/img/icons/silk/drive_disk.png differ diff --git a/media/img/icons/silk/drive_edit.png b/media/img/icons/silk/drive_edit.png new file mode 100755 index 000000000..7923fada4 Binary files /dev/null and b/media/img/icons/silk/drive_edit.png differ diff --git a/media/img/icons/silk/drive_error.png b/media/img/icons/silk/drive_error.png new file mode 100755 index 000000000..309f63962 Binary files /dev/null and b/media/img/icons/silk/drive_error.png differ diff --git a/media/img/icons/silk/drive_go.png b/media/img/icons/silk/drive_go.png new file mode 100755 index 000000000..fc53379ef Binary files /dev/null and b/media/img/icons/silk/drive_go.png differ diff --git a/media/img/icons/silk/drive_key.png b/media/img/icons/silk/drive_key.png new file mode 100755 index 000000000..d0b3c673a Binary files /dev/null and b/media/img/icons/silk/drive_key.png differ diff --git a/media/img/icons/silk/drive_link.png b/media/img/icons/silk/drive_link.png new file mode 100755 index 000000000..8679c4b5c Binary files /dev/null and b/media/img/icons/silk/drive_link.png differ diff --git a/media/img/icons/silk/drive_magnify.png b/media/img/icons/silk/drive_magnify.png new file mode 100755 index 000000000..0f0f4446b Binary files /dev/null and b/media/img/icons/silk/drive_magnify.png differ diff --git a/media/img/icons/silk/drive_network.png b/media/img/icons/silk/drive_network.png new file mode 100755 index 000000000..63d2d5d5b Binary files /dev/null and b/media/img/icons/silk/drive_network.png differ diff --git a/media/img/icons/silk/drive_rename.png b/media/img/icons/silk/drive_rename.png new file mode 100755 index 000000000..2a9f38b44 Binary files /dev/null and b/media/img/icons/silk/drive_rename.png differ diff --git a/media/img/icons/silk/drive_user.png b/media/img/icons/silk/drive_user.png new file mode 100755 index 000000000..0b4751ce4 Binary files /dev/null and b/media/img/icons/silk/drive_user.png differ diff --git a/media/img/icons/silk/drive_web.png b/media/img/icons/silk/drive_web.png new file mode 100755 index 000000000..8850a8355 Binary files /dev/null and b/media/img/icons/silk/drive_web.png differ diff --git a/media/img/icons/silk/dvd.png b/media/img/icons/silk/dvd.png new file mode 100755 index 000000000..9d94de5df Binary files /dev/null and b/media/img/icons/silk/dvd.png differ diff --git a/media/img/icons/silk/dvd_add.png b/media/img/icons/silk/dvd_add.png new file mode 100755 index 000000000..517d11215 Binary files /dev/null and b/media/img/icons/silk/dvd_add.png differ diff --git a/media/img/icons/silk/dvd_delete.png b/media/img/icons/silk/dvd_delete.png new file mode 100755 index 000000000..87bed2219 Binary files /dev/null and b/media/img/icons/silk/dvd_delete.png differ diff --git a/media/img/icons/silk/dvd_edit.png b/media/img/icons/silk/dvd_edit.png new file mode 100755 index 000000000..d6330aa99 Binary files /dev/null and b/media/img/icons/silk/dvd_edit.png differ diff --git a/media/img/icons/silk/dvd_error.png b/media/img/icons/silk/dvd_error.png new file mode 100755 index 000000000..8f6d4bee3 Binary files /dev/null and b/media/img/icons/silk/dvd_error.png differ diff --git a/media/img/icons/silk/dvd_go.png b/media/img/icons/silk/dvd_go.png new file mode 100755 index 000000000..ef6959f7b Binary files /dev/null and b/media/img/icons/silk/dvd_go.png differ diff --git a/media/img/icons/silk/dvd_key.png b/media/img/icons/silk/dvd_key.png new file mode 100755 index 000000000..da9307f66 Binary files /dev/null and b/media/img/icons/silk/dvd_key.png differ diff --git a/media/img/icons/silk/dvd_link.png b/media/img/icons/silk/dvd_link.png new file mode 100755 index 000000000..caad7263a Binary files /dev/null and b/media/img/icons/silk/dvd_link.png differ diff --git a/media/img/icons/silk/email.png b/media/img/icons/silk/email.png new file mode 100755 index 000000000..7348aed77 Binary files /dev/null and b/media/img/icons/silk/email.png differ diff --git a/media/img/icons/silk/email_add.png b/media/img/icons/silk/email_add.png new file mode 100755 index 000000000..6c933681f Binary files /dev/null and b/media/img/icons/silk/email_add.png differ diff --git a/media/img/icons/silk/email_attach.png b/media/img/icons/silk/email_attach.png new file mode 100755 index 000000000..1f994851c Binary files /dev/null and b/media/img/icons/silk/email_attach.png differ diff --git a/media/img/icons/silk/email_delete.png b/media/img/icons/silk/email_delete.png new file mode 100755 index 000000000..a9932b1ad Binary files /dev/null and b/media/img/icons/silk/email_delete.png differ diff --git a/media/img/icons/silk/email_edit.png b/media/img/icons/silk/email_edit.png new file mode 100755 index 000000000..244f04ae1 Binary files /dev/null and b/media/img/icons/silk/email_edit.png differ diff --git a/media/img/icons/silk/email_error.png b/media/img/icons/silk/email_error.png new file mode 100755 index 000000000..8bdd3304d Binary files /dev/null and b/media/img/icons/silk/email_error.png differ diff --git a/media/img/icons/silk/email_go.png b/media/img/icons/silk/email_go.png new file mode 100755 index 000000000..4a6c5d396 Binary files /dev/null and b/media/img/icons/silk/email_go.png differ diff --git a/media/img/icons/silk/email_link.png b/media/img/icons/silk/email_link.png new file mode 100755 index 000000000..2c49f78a6 Binary files /dev/null and b/media/img/icons/silk/email_link.png differ diff --git a/media/img/icons/silk/email_open.png b/media/img/icons/silk/email_open.png new file mode 100755 index 000000000..7b6f9813d Binary files /dev/null and b/media/img/icons/silk/email_open.png differ diff --git a/media/img/icons/silk/email_open_image.png b/media/img/icons/silk/email_open_image.png new file mode 100755 index 000000000..e588e2fb2 Binary files /dev/null and b/media/img/icons/silk/email_open_image.png differ diff --git a/media/img/icons/silk/emoticon_evilgrin.png b/media/img/icons/silk/emoticon_evilgrin.png new file mode 100755 index 000000000..817bd509b Binary files /dev/null and b/media/img/icons/silk/emoticon_evilgrin.png differ diff --git a/media/img/icons/silk/emoticon_grin.png b/media/img/icons/silk/emoticon_grin.png new file mode 100755 index 000000000..fc60c5e1c Binary files /dev/null and b/media/img/icons/silk/emoticon_grin.png differ diff --git a/media/img/icons/silk/emoticon_happy.png b/media/img/icons/silk/emoticon_happy.png new file mode 100755 index 000000000..6b7336e17 Binary files /dev/null and b/media/img/icons/silk/emoticon_happy.png differ diff --git a/media/img/icons/silk/emoticon_smile.png b/media/img/icons/silk/emoticon_smile.png new file mode 100755 index 000000000..ade431851 Binary files /dev/null and b/media/img/icons/silk/emoticon_smile.png differ diff --git a/media/img/icons/silk/emoticon_surprised.png b/media/img/icons/silk/emoticon_surprised.png new file mode 100755 index 000000000..4520cfc55 Binary files /dev/null and b/media/img/icons/silk/emoticon_surprised.png differ diff --git a/media/img/icons/silk/emoticon_tongue.png b/media/img/icons/silk/emoticon_tongue.png new file mode 100755 index 000000000..ecafd2ffc Binary files /dev/null and b/media/img/icons/silk/emoticon_tongue.png differ diff --git a/media/img/icons/silk/emoticon_unhappy.png b/media/img/icons/silk/emoticon_unhappy.png new file mode 100755 index 000000000..fd5d030ef Binary files /dev/null and b/media/img/icons/silk/emoticon_unhappy.png differ diff --git a/media/img/icons/silk/emoticon_waii.png b/media/img/icons/silk/emoticon_waii.png new file mode 100755 index 000000000..458f93611 Binary files /dev/null and b/media/img/icons/silk/emoticon_waii.png differ diff --git a/media/img/icons/silk/emoticon_wink.png b/media/img/icons/silk/emoticon_wink.png new file mode 100755 index 000000000..a631949b5 Binary files /dev/null and b/media/img/icons/silk/emoticon_wink.png differ diff --git a/media/img/icons/silk/error.png b/media/img/icons/silk/error.png new file mode 100755 index 000000000..628cf2dae Binary files /dev/null and b/media/img/icons/silk/error.png differ diff --git a/media/img/icons/silk/error_add.png b/media/img/icons/silk/error_add.png new file mode 100755 index 000000000..4c974840e Binary files /dev/null and b/media/img/icons/silk/error_add.png differ diff --git a/media/img/icons/silk/error_delete.png b/media/img/icons/silk/error_delete.png new file mode 100755 index 000000000..7f78bcc8e Binary files /dev/null and b/media/img/icons/silk/error_delete.png differ diff --git a/media/img/icons/silk/error_go.png b/media/img/icons/silk/error_go.png new file mode 100755 index 000000000..caa1838d7 Binary files /dev/null and b/media/img/icons/silk/error_go.png differ diff --git a/media/img/icons/silk/exclamation.png b/media/img/icons/silk/exclamation.png new file mode 100755 index 000000000..c37bd062e Binary files /dev/null and b/media/img/icons/silk/exclamation.png differ diff --git a/media/img/icons/silk/eye.png b/media/img/icons/silk/eye.png new file mode 100755 index 000000000..564a1a971 Binary files /dev/null and b/media/img/icons/silk/eye.png differ diff --git a/media/img/icons/silk/feed.png b/media/img/icons/silk/feed.png new file mode 100755 index 000000000..315c4f4fa Binary files /dev/null and b/media/img/icons/silk/feed.png differ diff --git a/media/img/icons/silk/feed_add.png b/media/img/icons/silk/feed_add.png new file mode 100755 index 000000000..e77d46e8a Binary files /dev/null and b/media/img/icons/silk/feed_add.png differ diff --git a/media/img/icons/silk/feed_delete.png b/media/img/icons/silk/feed_delete.png new file mode 100755 index 000000000..5e332b4cc Binary files /dev/null and b/media/img/icons/silk/feed_delete.png differ diff --git a/media/img/icons/silk/feed_disk.png b/media/img/icons/silk/feed_disk.png new file mode 100755 index 000000000..a158c998d Binary files /dev/null and b/media/img/icons/silk/feed_disk.png differ diff --git a/media/img/icons/silk/feed_edit.png b/media/img/icons/silk/feed_edit.png new file mode 100755 index 000000000..f1fde7a9c Binary files /dev/null and b/media/img/icons/silk/feed_edit.png differ diff --git a/media/img/icons/silk/feed_error.png b/media/img/icons/silk/feed_error.png new file mode 100755 index 000000000..c0a801c7f Binary files /dev/null and b/media/img/icons/silk/feed_error.png differ diff --git a/media/img/icons/silk/feed_go.png b/media/img/icons/silk/feed_go.png new file mode 100755 index 000000000..f2eed1ecf Binary files /dev/null and b/media/img/icons/silk/feed_go.png differ diff --git a/media/img/icons/silk/feed_key.png b/media/img/icons/silk/feed_key.png new file mode 100755 index 000000000..156bfa971 Binary files /dev/null and b/media/img/icons/silk/feed_key.png differ diff --git a/media/img/icons/silk/feed_link.png b/media/img/icons/silk/feed_link.png new file mode 100755 index 000000000..c45a53459 Binary files /dev/null and b/media/img/icons/silk/feed_link.png differ diff --git a/media/img/icons/silk/feed_magnify.png b/media/img/icons/silk/feed_magnify.png new file mode 100755 index 000000000..3023695d8 Binary files /dev/null and b/media/img/icons/silk/feed_magnify.png differ diff --git a/media/img/icons/silk/female.png b/media/img/icons/silk/female.png new file mode 100755 index 000000000..f92958e6a Binary files /dev/null and b/media/img/icons/silk/female.png differ diff --git a/media/img/icons/silk/film.png b/media/img/icons/silk/film.png new file mode 100755 index 000000000..b0ce7bb19 Binary files /dev/null and b/media/img/icons/silk/film.png differ diff --git a/media/img/icons/silk/film_add.png b/media/img/icons/silk/film_add.png new file mode 100755 index 000000000..40d681feb Binary files /dev/null and b/media/img/icons/silk/film_add.png differ diff --git a/media/img/icons/silk/film_delete.png b/media/img/icons/silk/film_delete.png new file mode 100755 index 000000000..23a2508c5 Binary files /dev/null and b/media/img/icons/silk/film_delete.png differ diff --git a/media/img/icons/silk/film_edit.png b/media/img/icons/silk/film_edit.png new file mode 100755 index 000000000..af66b73f2 Binary files /dev/null and b/media/img/icons/silk/film_edit.png differ diff --git a/media/img/icons/silk/film_error.png b/media/img/icons/silk/film_error.png new file mode 100755 index 000000000..88f3d69bc Binary files /dev/null and b/media/img/icons/silk/film_error.png differ diff --git a/media/img/icons/silk/film_go.png b/media/img/icons/silk/film_go.png new file mode 100755 index 000000000..dd0168ea9 Binary files /dev/null and b/media/img/icons/silk/film_go.png differ diff --git a/media/img/icons/silk/film_key.png b/media/img/icons/silk/film_key.png new file mode 100755 index 000000000..58921624e Binary files /dev/null and b/media/img/icons/silk/film_key.png differ diff --git a/media/img/icons/silk/film_link.png b/media/img/icons/silk/film_link.png new file mode 100755 index 000000000..0f24e86e4 Binary files /dev/null and b/media/img/icons/silk/film_link.png differ diff --git a/media/img/icons/silk/film_save.png b/media/img/icons/silk/film_save.png new file mode 100755 index 000000000..bc8c0d353 Binary files /dev/null and b/media/img/icons/silk/film_save.png differ diff --git a/media/img/icons/silk/find.png b/media/img/icons/silk/find.png new file mode 100755 index 000000000..154747964 Binary files /dev/null and b/media/img/icons/silk/find.png differ diff --git a/media/img/icons/silk/flag_blue.png b/media/img/icons/silk/flag_blue.png new file mode 100755 index 000000000..003924f5e Binary files /dev/null and b/media/img/icons/silk/flag_blue.png differ diff --git a/media/img/icons/silk/flag_green.png b/media/img/icons/silk/flag_green.png new file mode 100755 index 000000000..e4bc611f8 Binary files /dev/null and b/media/img/icons/silk/flag_green.png differ diff --git a/media/img/icons/silk/flag_orange.png b/media/img/icons/silk/flag_orange.png new file mode 100755 index 000000000..e63202420 Binary files /dev/null and b/media/img/icons/silk/flag_orange.png differ diff --git a/media/img/icons/silk/flag_pink.png b/media/img/icons/silk/flag_pink.png new file mode 100755 index 000000000..5f15e526c Binary files /dev/null and b/media/img/icons/silk/flag_pink.png differ diff --git a/media/img/icons/silk/flag_purple.png b/media/img/icons/silk/flag_purple.png new file mode 100755 index 000000000..d06986644 Binary files /dev/null and b/media/img/icons/silk/flag_purple.png differ diff --git a/media/img/icons/silk/flag_red.png b/media/img/icons/silk/flag_red.png new file mode 100755 index 000000000..e8a602da7 Binary files /dev/null and b/media/img/icons/silk/flag_red.png differ diff --git a/media/img/icons/silk/flag_yellow.png b/media/img/icons/silk/flag_yellow.png new file mode 100755 index 000000000..14c89a543 Binary files /dev/null and b/media/img/icons/silk/flag_yellow.png differ diff --git a/media/img/icons/silk/folder.png b/media/img/icons/silk/folder.png new file mode 100755 index 000000000..784e8fa48 Binary files /dev/null and b/media/img/icons/silk/folder.png differ diff --git a/media/img/icons/silk/folder_add.png b/media/img/icons/silk/folder_add.png new file mode 100755 index 000000000..529fe8fe0 Binary files /dev/null and b/media/img/icons/silk/folder_add.png differ diff --git a/media/img/icons/silk/folder_bell.png b/media/img/icons/silk/folder_bell.png new file mode 100755 index 000000000..d04dd7f51 Binary files /dev/null and b/media/img/icons/silk/folder_bell.png differ diff --git a/media/img/icons/silk/folder_brick.png b/media/img/icons/silk/folder_brick.png new file mode 100755 index 000000000..5dea9769a Binary files /dev/null and b/media/img/icons/silk/folder_brick.png differ diff --git a/media/img/icons/silk/folder_bug.png b/media/img/icons/silk/folder_bug.png new file mode 100755 index 000000000..4f791b684 Binary files /dev/null and b/media/img/icons/silk/folder_bug.png differ diff --git a/media/img/icons/silk/folder_camera.png b/media/img/icons/silk/folder_camera.png new file mode 100755 index 000000000..c9519416d Binary files /dev/null and b/media/img/icons/silk/folder_camera.png differ diff --git a/media/img/icons/silk/folder_database.png b/media/img/icons/silk/folder_database.png new file mode 100755 index 000000000..5193e2eff Binary files /dev/null and b/media/img/icons/silk/folder_database.png differ diff --git a/media/img/icons/silk/folder_delete.png b/media/img/icons/silk/folder_delete.png new file mode 100755 index 000000000..112b01638 Binary files /dev/null and b/media/img/icons/silk/folder_delete.png differ diff --git a/media/img/icons/silk/folder_edit.png b/media/img/icons/silk/folder_edit.png new file mode 100755 index 000000000..ad669cc78 Binary files /dev/null and b/media/img/icons/silk/folder_edit.png differ diff --git a/media/img/icons/silk/folder_error.png b/media/img/icons/silk/folder_error.png new file mode 100755 index 000000000..1af880951 Binary files /dev/null and b/media/img/icons/silk/folder_error.png differ diff --git a/media/img/icons/silk/folder_explore.png b/media/img/icons/silk/folder_explore.png new file mode 100755 index 000000000..0ba939184 Binary files /dev/null and b/media/img/icons/silk/folder_explore.png differ diff --git a/media/img/icons/silk/folder_feed.png b/media/img/icons/silk/folder_feed.png new file mode 100755 index 000000000..d06ee51e4 Binary files /dev/null and b/media/img/icons/silk/folder_feed.png differ diff --git a/media/img/icons/silk/folder_find.png b/media/img/icons/silk/folder_find.png new file mode 100755 index 000000000..c64e2ee67 Binary files /dev/null and b/media/img/icons/silk/folder_find.png differ diff --git a/media/img/icons/silk/folder_go.png b/media/img/icons/silk/folder_go.png new file mode 100755 index 000000000..34a736f70 Binary files /dev/null and b/media/img/icons/silk/folder_go.png differ diff --git a/media/img/icons/silk/folder_heart.png b/media/img/icons/silk/folder_heart.png new file mode 100755 index 000000000..56d7da1d6 Binary files /dev/null and b/media/img/icons/silk/folder_heart.png differ diff --git a/media/img/icons/silk/folder_image.png b/media/img/icons/silk/folder_image.png new file mode 100755 index 000000000..d5df75bb6 Binary files /dev/null and b/media/img/icons/silk/folder_image.png differ diff --git a/media/img/icons/silk/folder_key.png b/media/img/icons/silk/folder_key.png new file mode 100755 index 000000000..fb9b4c2bb Binary files /dev/null and b/media/img/icons/silk/folder_key.png differ diff --git a/media/img/icons/silk/folder_lightbulb.png b/media/img/icons/silk/folder_lightbulb.png new file mode 100755 index 000000000..f367a5117 Binary files /dev/null and b/media/img/icons/silk/folder_lightbulb.png differ diff --git a/media/img/icons/silk/folder_link.png b/media/img/icons/silk/folder_link.png new file mode 100755 index 000000000..b9b75f6c3 Binary files /dev/null and b/media/img/icons/silk/folder_link.png differ diff --git a/media/img/icons/silk/folder_magnify.png b/media/img/icons/silk/folder_magnify.png new file mode 100755 index 000000000..0a3e7985c Binary files /dev/null and b/media/img/icons/silk/folder_magnify.png differ diff --git a/media/img/icons/silk/folder_page.png b/media/img/icons/silk/folder_page.png new file mode 100755 index 000000000..1ef6e1143 Binary files /dev/null and b/media/img/icons/silk/folder_page.png differ diff --git a/media/img/icons/silk/folder_page_white.png b/media/img/icons/silk/folder_page_white.png new file mode 100755 index 000000000..14d6b6181 Binary files /dev/null and b/media/img/icons/silk/folder_page_white.png differ diff --git a/media/img/icons/silk/folder_palette.png b/media/img/icons/silk/folder_palette.png new file mode 100755 index 000000000..ba12fe8ac Binary files /dev/null and b/media/img/icons/silk/folder_palette.png differ diff --git a/media/img/icons/silk/folder_picture.png b/media/img/icons/silk/folder_picture.png new file mode 100755 index 000000000..052b33638 Binary files /dev/null and b/media/img/icons/silk/folder_picture.png differ diff --git a/media/img/icons/silk/folder_star.png b/media/img/icons/silk/folder_star.png new file mode 100755 index 000000000..448e46fd5 Binary files /dev/null and b/media/img/icons/silk/folder_star.png differ diff --git a/media/img/icons/silk/folder_table.png b/media/img/icons/silk/folder_table.png new file mode 100755 index 000000000..473cee355 Binary files /dev/null and b/media/img/icons/silk/folder_table.png differ diff --git a/media/img/icons/silk/folder_user.png b/media/img/icons/silk/folder_user.png new file mode 100755 index 000000000..f021c3e12 Binary files /dev/null and b/media/img/icons/silk/folder_user.png differ diff --git a/media/img/icons/silk/folder_wrench.png b/media/img/icons/silk/folder_wrench.png new file mode 100755 index 000000000..ea3404e03 Binary files /dev/null and b/media/img/icons/silk/folder_wrench.png differ diff --git a/media/img/icons/silk/font.png b/media/img/icons/silk/font.png new file mode 100755 index 000000000..b7960db9d Binary files /dev/null and b/media/img/icons/silk/font.png differ diff --git a/media/img/icons/silk/font_add.png b/media/img/icons/silk/font_add.png new file mode 100755 index 000000000..b709ebaef Binary files /dev/null and b/media/img/icons/silk/font_add.png differ diff --git a/media/img/icons/silk/font_delete.png b/media/img/icons/silk/font_delete.png new file mode 100755 index 000000000..1d6124d6e Binary files /dev/null and b/media/img/icons/silk/font_delete.png differ diff --git a/media/img/icons/silk/font_go.png b/media/img/icons/silk/font_go.png new file mode 100755 index 000000000..75eba80d6 Binary files /dev/null and b/media/img/icons/silk/font_go.png differ diff --git a/media/img/icons/silk/group.png b/media/img/icons/silk/group.png new file mode 100755 index 000000000..7fb4e1f1e Binary files /dev/null and b/media/img/icons/silk/group.png differ diff --git a/media/img/icons/silk/group_add.png b/media/img/icons/silk/group_add.png new file mode 100755 index 000000000..06c5350cb Binary files /dev/null and b/media/img/icons/silk/group_add.png differ diff --git a/media/img/icons/silk/group_delete.png b/media/img/icons/silk/group_delete.png new file mode 100755 index 000000000..4489ca238 Binary files /dev/null and b/media/img/icons/silk/group_delete.png differ diff --git a/media/img/icons/silk/group_edit.png b/media/img/icons/silk/group_edit.png new file mode 100755 index 000000000..c88b945b0 Binary files /dev/null and b/media/img/icons/silk/group_edit.png differ diff --git a/media/img/icons/silk/group_error.png b/media/img/icons/silk/group_error.png new file mode 100755 index 000000000..7364a13cb Binary files /dev/null and b/media/img/icons/silk/group_error.png differ diff --git a/media/img/icons/silk/group_gear.png b/media/img/icons/silk/group_gear.png new file mode 100755 index 000000000..2544f2e63 Binary files /dev/null and b/media/img/icons/silk/group_gear.png differ diff --git a/media/img/icons/silk/group_go.png b/media/img/icons/silk/group_go.png new file mode 100755 index 000000000..1f5233308 Binary files /dev/null and b/media/img/icons/silk/group_go.png differ diff --git a/media/img/icons/silk/group_key.png b/media/img/icons/silk/group_key.png new file mode 100755 index 000000000..257f111ca Binary files /dev/null and b/media/img/icons/silk/group_key.png differ diff --git a/media/img/icons/silk/group_link.png b/media/img/icons/silk/group_link.png new file mode 100755 index 000000000..c77ed8812 Binary files /dev/null and b/media/img/icons/silk/group_link.png differ diff --git a/media/img/icons/silk/heart.png b/media/img/icons/silk/heart.png new file mode 100755 index 000000000..d9ee53e59 Binary files /dev/null and b/media/img/icons/silk/heart.png differ diff --git a/media/img/icons/silk/heart_add.png b/media/img/icons/silk/heart_add.png new file mode 100755 index 000000000..d4195ff80 Binary files /dev/null and b/media/img/icons/silk/heart_add.png differ diff --git a/media/img/icons/silk/heart_delete.png b/media/img/icons/silk/heart_delete.png new file mode 100755 index 000000000..ce523e343 Binary files /dev/null and b/media/img/icons/silk/heart_delete.png differ diff --git a/media/img/icons/silk/help.png b/media/img/icons/silk/help.png new file mode 100755 index 000000000..5c870176d Binary files /dev/null and b/media/img/icons/silk/help.png differ diff --git a/media/img/icons/silk/hourglass.png b/media/img/icons/silk/hourglass.png new file mode 100755 index 000000000..57b03ce7a Binary files /dev/null and b/media/img/icons/silk/hourglass.png differ diff --git a/media/img/icons/silk/hourglass_add.png b/media/img/icons/silk/hourglass_add.png new file mode 100755 index 000000000..170dfff1e Binary files /dev/null and b/media/img/icons/silk/hourglass_add.png differ diff --git a/media/img/icons/silk/hourglass_delete.png b/media/img/icons/silk/hourglass_delete.png new file mode 100755 index 000000000..4b1337be8 Binary files /dev/null and b/media/img/icons/silk/hourglass_delete.png differ diff --git a/media/img/icons/silk/hourglass_go.png b/media/img/icons/silk/hourglass_go.png new file mode 100755 index 000000000..b2d3a98bc Binary files /dev/null and b/media/img/icons/silk/hourglass_go.png differ diff --git a/media/img/icons/silk/hourglass_link.png b/media/img/icons/silk/hourglass_link.png new file mode 100755 index 000000000..ecc59b0ab Binary files /dev/null and b/media/img/icons/silk/hourglass_link.png differ diff --git a/media/img/icons/silk/house.png b/media/img/icons/silk/house.png new file mode 100755 index 000000000..fed62219f Binary files /dev/null and b/media/img/icons/silk/house.png differ diff --git a/media/img/icons/silk/house_go.png b/media/img/icons/silk/house_go.png new file mode 100755 index 000000000..5457dbd3c Binary files /dev/null and b/media/img/icons/silk/house_go.png differ diff --git a/media/img/icons/silk/house_link.png b/media/img/icons/silk/house_link.png new file mode 100755 index 000000000..be2c2719e Binary files /dev/null and b/media/img/icons/silk/house_link.png differ diff --git a/media/img/icons/silk/html.png b/media/img/icons/silk/html.png new file mode 100755 index 000000000..55d1072ea Binary files /dev/null and b/media/img/icons/silk/html.png differ diff --git a/media/img/icons/silk/html_add.png b/media/img/icons/silk/html_add.png new file mode 100755 index 000000000..f1c08b7d6 Binary files /dev/null and b/media/img/icons/silk/html_add.png differ diff --git a/media/img/icons/silk/html_delete.png b/media/img/icons/silk/html_delete.png new file mode 100755 index 000000000..1bd28489e Binary files /dev/null and b/media/img/icons/silk/html_delete.png differ diff --git a/media/img/icons/silk/html_go.png b/media/img/icons/silk/html_go.png new file mode 100755 index 000000000..a95cede1e Binary files /dev/null and b/media/img/icons/silk/html_go.png differ diff --git a/media/img/icons/silk/html_valid.png b/media/img/icons/silk/html_valid.png new file mode 100755 index 000000000..71cec9249 Binary files /dev/null and b/media/img/icons/silk/html_valid.png differ diff --git a/media/img/icons/silk/image.png b/media/img/icons/silk/image.png new file mode 100755 index 000000000..fc3c393ca Binary files /dev/null and b/media/img/icons/silk/image.png differ diff --git a/media/img/icons/silk/image_add.png b/media/img/icons/silk/image_add.png new file mode 100755 index 000000000..fc5d6139e Binary files /dev/null and b/media/img/icons/silk/image_add.png differ diff --git a/media/img/icons/silk/image_delete.png b/media/img/icons/silk/image_delete.png new file mode 100755 index 000000000..c260e1d96 Binary files /dev/null and b/media/img/icons/silk/image_delete.png differ diff --git a/media/img/icons/silk/image_edit.png b/media/img/icons/silk/image_edit.png new file mode 100755 index 000000000..0aa4cc651 Binary files /dev/null and b/media/img/icons/silk/image_edit.png differ diff --git a/media/img/icons/silk/image_link.png b/media/img/icons/silk/image_link.png new file mode 100755 index 000000000..4bdb3541e Binary files /dev/null and b/media/img/icons/silk/image_link.png differ diff --git a/media/img/icons/silk/images.png b/media/img/icons/silk/images.png new file mode 100755 index 000000000..184860d1e Binary files /dev/null and b/media/img/icons/silk/images.png differ diff --git a/media/img/icons/silk/information.png b/media/img/icons/silk/information.png new file mode 100755 index 000000000..12cd1aef9 Binary files /dev/null and b/media/img/icons/silk/information.png differ diff --git a/media/img/icons/silk/ipod.png b/media/img/icons/silk/ipod.png new file mode 100755 index 000000000..3f768da50 Binary files /dev/null and b/media/img/icons/silk/ipod.png differ diff --git a/media/img/icons/silk/ipod_cast.png b/media/img/icons/silk/ipod_cast.png new file mode 100755 index 000000000..6f6d3406c Binary files /dev/null and b/media/img/icons/silk/ipod_cast.png differ diff --git a/media/img/icons/silk/ipod_cast_add.png b/media/img/icons/silk/ipod_cast_add.png new file mode 100755 index 000000000..c3257f5f1 Binary files /dev/null and b/media/img/icons/silk/ipod_cast_add.png differ diff --git a/media/img/icons/silk/ipod_cast_delete.png b/media/img/icons/silk/ipod_cast_delete.png new file mode 100755 index 000000000..377ab6950 Binary files /dev/null and b/media/img/icons/silk/ipod_cast_delete.png differ diff --git a/media/img/icons/silk/ipod_sound.png b/media/img/icons/silk/ipod_sound.png new file mode 100755 index 000000000..fef6e8bad Binary files /dev/null and b/media/img/icons/silk/ipod_sound.png differ diff --git a/media/img/icons/silk/joystick.png b/media/img/icons/silk/joystick.png new file mode 100755 index 000000000..62168f56f Binary files /dev/null and b/media/img/icons/silk/joystick.png differ diff --git a/media/img/icons/silk/joystick_add.png b/media/img/icons/silk/joystick_add.png new file mode 100755 index 000000000..77e710772 Binary files /dev/null and b/media/img/icons/silk/joystick_add.png differ diff --git a/media/img/icons/silk/joystick_delete.png b/media/img/icons/silk/joystick_delete.png new file mode 100755 index 000000000..5d44b5925 Binary files /dev/null and b/media/img/icons/silk/joystick_delete.png differ diff --git a/media/img/icons/silk/joystick_error.png b/media/img/icons/silk/joystick_error.png new file mode 100755 index 000000000..b32149e27 Binary files /dev/null and b/media/img/icons/silk/joystick_error.png differ diff --git a/media/img/icons/silk/key.png b/media/img/icons/silk/key.png new file mode 100755 index 000000000..4ec1a9281 Binary files /dev/null and b/media/img/icons/silk/key.png differ diff --git a/media/img/icons/silk/key_add.png b/media/img/icons/silk/key_add.png new file mode 100755 index 000000000..d40740396 Binary files /dev/null and b/media/img/icons/silk/key_add.png differ diff --git a/media/img/icons/silk/key_delete.png b/media/img/icons/silk/key_delete.png new file mode 100755 index 000000000..00dec80d8 Binary files /dev/null and b/media/img/icons/silk/key_delete.png differ diff --git a/media/img/icons/silk/key_go.png b/media/img/icons/silk/key_go.png new file mode 100755 index 000000000..30b0dc316 Binary files /dev/null and b/media/img/icons/silk/key_go.png differ diff --git a/media/img/icons/silk/keyboard.png b/media/img/icons/silk/keyboard.png new file mode 100755 index 000000000..898d402d7 Binary files /dev/null and b/media/img/icons/silk/keyboard.png differ diff --git a/media/img/icons/silk/keyboard_add.png b/media/img/icons/silk/keyboard_add.png new file mode 100755 index 000000000..26938dd0c Binary files /dev/null and b/media/img/icons/silk/keyboard_add.png differ diff --git a/media/img/icons/silk/keyboard_delete.png b/media/img/icons/silk/keyboard_delete.png new file mode 100755 index 000000000..1786ed5be Binary files /dev/null and b/media/img/icons/silk/keyboard_delete.png differ diff --git a/media/img/icons/silk/keyboard_magnify.png b/media/img/icons/silk/keyboard_magnify.png new file mode 100755 index 000000000..928fc17b4 Binary files /dev/null and b/media/img/icons/silk/keyboard_magnify.png differ diff --git a/media/img/icons/silk/layers.png b/media/img/icons/silk/layers.png new file mode 100755 index 000000000..00818f636 Binary files /dev/null and b/media/img/icons/silk/layers.png differ diff --git a/media/img/icons/silk/layout.png b/media/img/icons/silk/layout.png new file mode 100755 index 000000000..ea086b042 Binary files /dev/null and b/media/img/icons/silk/layout.png differ diff --git a/media/img/icons/silk/layout_add.png b/media/img/icons/silk/layout_add.png new file mode 100755 index 000000000..62037221c Binary files /dev/null and b/media/img/icons/silk/layout_add.png differ diff --git a/media/img/icons/silk/layout_content.png b/media/img/icons/silk/layout_content.png new file mode 100755 index 000000000..b4aaad9a4 Binary files /dev/null and b/media/img/icons/silk/layout_content.png differ diff --git a/media/img/icons/silk/layout_delete.png b/media/img/icons/silk/layout_delete.png new file mode 100755 index 000000000..4bd45f131 Binary files /dev/null and b/media/img/icons/silk/layout_delete.png differ diff --git a/media/img/icons/silk/layout_edit.png b/media/img/icons/silk/layout_edit.png new file mode 100755 index 000000000..ab3100b5c Binary files /dev/null and b/media/img/icons/silk/layout_edit.png differ diff --git a/media/img/icons/silk/layout_error.png b/media/img/icons/silk/layout_error.png new file mode 100755 index 000000000..5b5acea92 Binary files /dev/null and b/media/img/icons/silk/layout_error.png differ diff --git a/media/img/icons/silk/layout_header.png b/media/img/icons/silk/layout_header.png new file mode 100755 index 000000000..c6ea7f230 Binary files /dev/null and b/media/img/icons/silk/layout_header.png differ diff --git a/media/img/icons/silk/layout_link.png b/media/img/icons/silk/layout_link.png new file mode 100755 index 000000000..3445d4206 Binary files /dev/null and b/media/img/icons/silk/layout_link.png differ diff --git a/media/img/icons/silk/layout_sidebar.png b/media/img/icons/silk/layout_sidebar.png new file mode 100755 index 000000000..3be27bb9b Binary files /dev/null and b/media/img/icons/silk/layout_sidebar.png differ diff --git a/media/img/icons/silk/lightbulb.png b/media/img/icons/silk/lightbulb.png new file mode 100755 index 000000000..d22fde8ba Binary files /dev/null and b/media/img/icons/silk/lightbulb.png differ diff --git a/media/img/icons/silk/lightbulb_add.png b/media/img/icons/silk/lightbulb_add.png new file mode 100755 index 000000000..0dd848bd6 Binary files /dev/null and b/media/img/icons/silk/lightbulb_add.png differ diff --git a/media/img/icons/silk/lightbulb_delete.png b/media/img/icons/silk/lightbulb_delete.png new file mode 100755 index 000000000..f4781daa8 Binary files /dev/null and b/media/img/icons/silk/lightbulb_delete.png differ diff --git a/media/img/icons/silk/lightbulb_off.png b/media/img/icons/silk/lightbulb_off.png new file mode 100755 index 000000000..e95b8c5b1 Binary files /dev/null and b/media/img/icons/silk/lightbulb_off.png differ diff --git a/media/img/icons/silk/lightning.png b/media/img/icons/silk/lightning.png new file mode 100755 index 000000000..9680afd12 Binary files /dev/null and b/media/img/icons/silk/lightning.png differ diff --git a/media/img/icons/silk/lightning_add.png b/media/img/icons/silk/lightning_add.png new file mode 100755 index 000000000..dac3c9050 Binary files /dev/null and b/media/img/icons/silk/lightning_add.png differ diff --git a/media/img/icons/silk/lightning_delete.png b/media/img/icons/silk/lightning_delete.png new file mode 100755 index 000000000..dfe277050 Binary files /dev/null and b/media/img/icons/silk/lightning_delete.png differ diff --git a/media/img/icons/silk/lightning_go.png b/media/img/icons/silk/lightning_go.png new file mode 100755 index 000000000..29039e6a8 Binary files /dev/null and b/media/img/icons/silk/lightning_go.png differ diff --git a/media/img/icons/silk/link.png b/media/img/icons/silk/link.png new file mode 100755 index 000000000..25eacb7c2 Binary files /dev/null and b/media/img/icons/silk/link.png differ diff --git a/media/img/icons/silk/link_add.png b/media/img/icons/silk/link_add.png new file mode 100755 index 000000000..00be352c5 Binary files /dev/null and b/media/img/icons/silk/link_add.png differ diff --git a/media/img/icons/silk/link_break.png b/media/img/icons/silk/link_break.png new file mode 100755 index 000000000..523575306 Binary files /dev/null and b/media/img/icons/silk/link_break.png differ diff --git a/media/img/icons/silk/link_delete.png b/media/img/icons/silk/link_delete.png new file mode 100755 index 000000000..f66e2974e Binary files /dev/null and b/media/img/icons/silk/link_delete.png differ diff --git a/media/img/icons/silk/link_edit.png b/media/img/icons/silk/link_edit.png new file mode 100755 index 000000000..5b3aed090 Binary files /dev/null and b/media/img/icons/silk/link_edit.png differ diff --git a/media/img/icons/silk/link_error.png b/media/img/icons/silk/link_error.png new file mode 100755 index 000000000..ab694b1ac Binary files /dev/null and b/media/img/icons/silk/link_error.png differ diff --git a/media/img/icons/silk/link_go.png b/media/img/icons/silk/link_go.png new file mode 100755 index 000000000..ae8cae806 Binary files /dev/null and b/media/img/icons/silk/link_go.png differ diff --git a/media/img/icons/silk/lock.png b/media/img/icons/silk/lock.png new file mode 100755 index 000000000..2ebc4f6f9 Binary files /dev/null and b/media/img/icons/silk/lock.png differ diff --git a/media/img/icons/silk/lock_add.png b/media/img/icons/silk/lock_add.png new file mode 100755 index 000000000..a7b566b1f Binary files /dev/null and b/media/img/icons/silk/lock_add.png differ diff --git a/media/img/icons/silk/lock_break.png b/media/img/icons/silk/lock_break.png new file mode 100755 index 000000000..13578ab5a Binary files /dev/null and b/media/img/icons/silk/lock_break.png differ diff --git a/media/img/icons/silk/lock_delete.png b/media/img/icons/silk/lock_delete.png new file mode 100755 index 000000000..ecb50a93f Binary files /dev/null and b/media/img/icons/silk/lock_delete.png differ diff --git a/media/img/icons/silk/lock_edit.png b/media/img/icons/silk/lock_edit.png new file mode 100755 index 000000000..116aa5b7f Binary files /dev/null and b/media/img/icons/silk/lock_edit.png differ diff --git a/media/img/icons/silk/lock_go.png b/media/img/icons/silk/lock_go.png new file mode 100755 index 000000000..8c7c89b26 Binary files /dev/null and b/media/img/icons/silk/lock_go.png differ diff --git a/media/img/icons/silk/lock_open.png b/media/img/icons/silk/lock_open.png new file mode 100755 index 000000000..a471765ff Binary files /dev/null and b/media/img/icons/silk/lock_open.png differ diff --git a/media/img/icons/silk/lorry.png b/media/img/icons/silk/lorry.png new file mode 100755 index 000000000..8f95f5a5d Binary files /dev/null and b/media/img/icons/silk/lorry.png differ diff --git a/media/img/icons/silk/lorry_add.png b/media/img/icons/silk/lorry_add.png new file mode 100755 index 000000000..a2c512499 Binary files /dev/null and b/media/img/icons/silk/lorry_add.png differ diff --git a/media/img/icons/silk/lorry_delete.png b/media/img/icons/silk/lorry_delete.png new file mode 100755 index 000000000..66217f526 Binary files /dev/null and b/media/img/icons/silk/lorry_delete.png differ diff --git a/media/img/icons/silk/lorry_error.png b/media/img/icons/silk/lorry_error.png new file mode 100755 index 000000000..3619ead96 Binary files /dev/null and b/media/img/icons/silk/lorry_error.png differ diff --git a/media/img/icons/silk/lorry_flatbed.png b/media/img/icons/silk/lorry_flatbed.png new file mode 100755 index 000000000..8b20f5503 Binary files /dev/null and b/media/img/icons/silk/lorry_flatbed.png differ diff --git a/media/img/icons/silk/lorry_go.png b/media/img/icons/silk/lorry_go.png new file mode 100755 index 000000000..1c296a6aa Binary files /dev/null and b/media/img/icons/silk/lorry_go.png differ diff --git a/media/img/icons/silk/lorry_link.png b/media/img/icons/silk/lorry_link.png new file mode 100755 index 000000000..5e6663e5c Binary files /dev/null and b/media/img/icons/silk/lorry_link.png differ diff --git a/media/img/icons/silk/magifier_zoom_out.png b/media/img/icons/silk/magifier_zoom_out.png new file mode 100755 index 000000000..81f28199a Binary files /dev/null and b/media/img/icons/silk/magifier_zoom_out.png differ diff --git a/media/img/icons/silk/magnifier.png b/media/img/icons/silk/magnifier.png new file mode 100755 index 000000000..cf3d97f75 Binary files /dev/null and b/media/img/icons/silk/magnifier.png differ diff --git a/media/img/icons/silk/magnifier_zoom_in.png b/media/img/icons/silk/magnifier_zoom_in.png new file mode 100755 index 000000000..af4fe0747 Binary files /dev/null and b/media/img/icons/silk/magnifier_zoom_in.png differ diff --git a/media/img/icons/silk/male.png b/media/img/icons/silk/male.png new file mode 100755 index 000000000..25d6ea91d Binary files /dev/null and b/media/img/icons/silk/male.png differ diff --git a/media/img/icons/silk/map.png b/media/img/icons/silk/map.png new file mode 100755 index 000000000..f90ef25ec Binary files /dev/null and b/media/img/icons/silk/map.png differ diff --git a/media/img/icons/silk/map_add.png b/media/img/icons/silk/map_add.png new file mode 100755 index 000000000..2b72da06a Binary files /dev/null and b/media/img/icons/silk/map_add.png differ diff --git a/media/img/icons/silk/map_delete.png b/media/img/icons/silk/map_delete.png new file mode 100755 index 000000000..e74402f9c Binary files /dev/null and b/media/img/icons/silk/map_delete.png differ diff --git a/media/img/icons/silk/map_edit.png b/media/img/icons/silk/map_edit.png new file mode 100755 index 000000000..93d4d7e5f Binary files /dev/null and b/media/img/icons/silk/map_edit.png differ diff --git a/media/img/icons/silk/map_go.png b/media/img/icons/silk/map_go.png new file mode 100755 index 000000000..11eab26db Binary files /dev/null and b/media/img/icons/silk/map_go.png differ diff --git a/media/img/icons/silk/map_magnify.png b/media/img/icons/silk/map_magnify.png new file mode 100755 index 000000000..7184c9ddf Binary files /dev/null and b/media/img/icons/silk/map_magnify.png differ diff --git a/media/img/icons/silk/medal_bronze_1.png b/media/img/icons/silk/medal_bronze_1.png new file mode 100755 index 000000000..5f8a6d65d Binary files /dev/null and b/media/img/icons/silk/medal_bronze_1.png differ diff --git a/media/img/icons/silk/medal_bronze_2.png b/media/img/icons/silk/medal_bronze_2.png new file mode 100755 index 000000000..623d68c5d Binary files /dev/null and b/media/img/icons/silk/medal_bronze_2.png differ diff --git a/media/img/icons/silk/medal_bronze_3.png b/media/img/icons/silk/medal_bronze_3.png new file mode 100755 index 000000000..ed3f43eb0 Binary files /dev/null and b/media/img/icons/silk/medal_bronze_3.png differ diff --git a/media/img/icons/silk/medal_bronze_add.png b/media/img/icons/silk/medal_bronze_add.png new file mode 100755 index 000000000..8487b2c19 Binary files /dev/null and b/media/img/icons/silk/medal_bronze_add.png differ diff --git a/media/img/icons/silk/medal_bronze_delete.png b/media/img/icons/silk/medal_bronze_delete.png new file mode 100755 index 000000000..d32aed727 Binary files /dev/null and b/media/img/icons/silk/medal_bronze_delete.png differ diff --git a/media/img/icons/silk/medal_gold_1.png b/media/img/icons/silk/medal_gold_1.png new file mode 100755 index 000000000..87584dc95 Binary files /dev/null and b/media/img/icons/silk/medal_gold_1.png differ diff --git a/media/img/icons/silk/medal_gold_2.png b/media/img/icons/silk/medal_gold_2.png new file mode 100755 index 000000000..fa3a15dd6 Binary files /dev/null and b/media/img/icons/silk/medal_gold_2.png differ diff --git a/media/img/icons/silk/medal_gold_3.png b/media/img/icons/silk/medal_gold_3.png new file mode 100755 index 000000000..ef1b08b92 Binary files /dev/null and b/media/img/icons/silk/medal_gold_3.png differ diff --git a/media/img/icons/silk/medal_gold_add.png b/media/img/icons/silk/medal_gold_add.png new file mode 100755 index 000000000..dcade0d8d Binary files /dev/null and b/media/img/icons/silk/medal_gold_add.png differ diff --git a/media/img/icons/silk/medal_gold_delete.png b/media/img/icons/silk/medal_gold_delete.png new file mode 100755 index 000000000..84b06d5bf Binary files /dev/null and b/media/img/icons/silk/medal_gold_delete.png differ diff --git a/media/img/icons/silk/medal_silver_1.png b/media/img/icons/silk/medal_silver_1.png new file mode 100755 index 000000000..75a64da32 Binary files /dev/null and b/media/img/icons/silk/medal_silver_1.png differ diff --git a/media/img/icons/silk/medal_silver_2.png b/media/img/icons/silk/medal_silver_2.png new file mode 100755 index 000000000..2e0fe75c9 Binary files /dev/null and b/media/img/icons/silk/medal_silver_2.png differ diff --git a/media/img/icons/silk/medal_silver_3.png b/media/img/icons/silk/medal_silver_3.png new file mode 100755 index 000000000..e385b5467 Binary files /dev/null and b/media/img/icons/silk/medal_silver_3.png differ diff --git a/media/img/icons/silk/medal_silver_add.png b/media/img/icons/silk/medal_silver_add.png new file mode 100755 index 000000000..b0633fa0e Binary files /dev/null and b/media/img/icons/silk/medal_silver_add.png differ diff --git a/media/img/icons/silk/medal_silver_delete.png b/media/img/icons/silk/medal_silver_delete.png new file mode 100755 index 000000000..06cab4679 Binary files /dev/null and b/media/img/icons/silk/medal_silver_delete.png differ diff --git a/media/img/icons/silk/money.png b/media/img/icons/silk/money.png new file mode 100755 index 000000000..42c52d05f Binary files /dev/null and b/media/img/icons/silk/money.png differ diff --git a/media/img/icons/silk/money_add.png b/media/img/icons/silk/money_add.png new file mode 100755 index 000000000..588fa9d07 Binary files /dev/null and b/media/img/icons/silk/money_add.png differ diff --git a/media/img/icons/silk/money_delete.png b/media/img/icons/silk/money_delete.png new file mode 100755 index 000000000..eae2c524b Binary files /dev/null and b/media/img/icons/silk/money_delete.png differ diff --git a/media/img/icons/silk/money_dollar.png b/media/img/icons/silk/money_dollar.png new file mode 100755 index 000000000..59af16382 Binary files /dev/null and b/media/img/icons/silk/money_dollar.png differ diff --git a/media/img/icons/silk/money_euro.png b/media/img/icons/silk/money_euro.png new file mode 100755 index 000000000..b322ba929 Binary files /dev/null and b/media/img/icons/silk/money_euro.png differ diff --git a/media/img/icons/silk/money_pound.png b/media/img/icons/silk/money_pound.png new file mode 100755 index 000000000..b71136463 Binary files /dev/null and b/media/img/icons/silk/money_pound.png differ diff --git a/media/img/icons/silk/money_yen.png b/media/img/icons/silk/money_yen.png new file mode 100755 index 000000000..228a6778b Binary files /dev/null and b/media/img/icons/silk/money_yen.png differ diff --git a/media/img/icons/silk/monitor.png b/media/img/icons/silk/monitor.png new file mode 100755 index 000000000..d040bd022 Binary files /dev/null and b/media/img/icons/silk/monitor.png differ diff --git a/media/img/icons/silk/monitor_add.png b/media/img/icons/silk/monitor_add.png new file mode 100755 index 000000000..a81806640 Binary files /dev/null and b/media/img/icons/silk/monitor_add.png differ diff --git a/media/img/icons/silk/monitor_delete.png b/media/img/icons/silk/monitor_delete.png new file mode 100755 index 000000000..37332563f Binary files /dev/null and b/media/img/icons/silk/monitor_delete.png differ diff --git a/media/img/icons/silk/monitor_edit.png b/media/img/icons/silk/monitor_edit.png new file mode 100755 index 000000000..f772c562f Binary files /dev/null and b/media/img/icons/silk/monitor_edit.png differ diff --git a/media/img/icons/silk/monitor_error.png b/media/img/icons/silk/monitor_error.png new file mode 100755 index 000000000..270c5018d Binary files /dev/null and b/media/img/icons/silk/monitor_error.png differ diff --git a/media/img/icons/silk/monitor_go.png b/media/img/icons/silk/monitor_go.png new file mode 100755 index 000000000..8af3eda9f Binary files /dev/null and b/media/img/icons/silk/monitor_go.png differ diff --git a/media/img/icons/silk/monitor_lightning.png b/media/img/icons/silk/monitor_lightning.png new file mode 100755 index 000000000..06e53a9d1 Binary files /dev/null and b/media/img/icons/silk/monitor_lightning.png differ diff --git a/media/img/icons/silk/monitor_link.png b/media/img/icons/silk/monitor_link.png new file mode 100755 index 000000000..a014b025a Binary files /dev/null and b/media/img/icons/silk/monitor_link.png differ diff --git a/media/img/icons/silk/mouse.png b/media/img/icons/silk/mouse.png new file mode 100755 index 000000000..63a92fa91 Binary files /dev/null and b/media/img/icons/silk/mouse.png differ diff --git a/media/img/icons/silk/mouse_add.png b/media/img/icons/silk/mouse_add.png new file mode 100755 index 000000000..65bcab520 Binary files /dev/null and b/media/img/icons/silk/mouse_add.png differ diff --git a/media/img/icons/silk/mouse_delete.png b/media/img/icons/silk/mouse_delete.png new file mode 100755 index 000000000..72865668c Binary files /dev/null and b/media/img/icons/silk/mouse_delete.png differ diff --git a/media/img/icons/silk/mouse_error.png b/media/img/icons/silk/mouse_error.png new file mode 100755 index 000000000..bcc156238 Binary files /dev/null and b/media/img/icons/silk/mouse_error.png differ diff --git a/media/img/icons/silk/music.png b/media/img/icons/silk/music.png new file mode 100755 index 000000000..a8b3ede3d Binary files /dev/null and b/media/img/icons/silk/music.png differ diff --git a/media/img/icons/silk/new.png b/media/img/icons/silk/new.png new file mode 100755 index 000000000..6a9bf0370 Binary files /dev/null and b/media/img/icons/silk/new.png differ diff --git a/media/img/icons/silk/newspaper.png b/media/img/icons/silk/newspaper.png new file mode 100755 index 000000000..6a2ecce1b Binary files /dev/null and b/media/img/icons/silk/newspaper.png differ diff --git a/media/img/icons/silk/newspaper_add.png b/media/img/icons/silk/newspaper_add.png new file mode 100755 index 000000000..8140e8c10 Binary files /dev/null and b/media/img/icons/silk/newspaper_add.png differ diff --git a/media/img/icons/silk/newspaper_delete.png b/media/img/icons/silk/newspaper_delete.png new file mode 100755 index 000000000..bde96ce19 Binary files /dev/null and b/media/img/icons/silk/newspaper_delete.png differ diff --git a/media/img/icons/silk/newspaper_go.png b/media/img/icons/silk/newspaper_go.png new file mode 100755 index 000000000..fd6142871 Binary files /dev/null and b/media/img/icons/silk/newspaper_go.png differ diff --git a/media/img/icons/silk/newspaper_link.png b/media/img/icons/silk/newspaper_link.png new file mode 100755 index 000000000..99e57cba8 Binary files /dev/null and b/media/img/icons/silk/newspaper_link.png differ diff --git a/media/img/icons/silk/note.png b/media/img/icons/silk/note.png new file mode 100755 index 000000000..244e6ca04 Binary files /dev/null and b/media/img/icons/silk/note.png differ diff --git a/media/img/icons/silk/note_add.png b/media/img/icons/silk/note_add.png new file mode 100755 index 000000000..abdad91eb Binary files /dev/null and b/media/img/icons/silk/note_add.png differ diff --git a/media/img/icons/silk/note_delete.png b/media/img/icons/silk/note_delete.png new file mode 100755 index 000000000..8a1f0ff56 Binary files /dev/null and b/media/img/icons/silk/note_delete.png differ diff --git a/media/img/icons/silk/note_edit.png b/media/img/icons/silk/note_edit.png new file mode 100755 index 000000000..291bfc764 Binary files /dev/null and b/media/img/icons/silk/note_edit.png differ diff --git a/media/img/icons/silk/note_error.png b/media/img/icons/silk/note_error.png new file mode 100755 index 000000000..896dadfd1 Binary files /dev/null and b/media/img/icons/silk/note_error.png differ diff --git a/media/img/icons/silk/note_go.png b/media/img/icons/silk/note_go.png new file mode 100755 index 000000000..49e54fd87 Binary files /dev/null and b/media/img/icons/silk/note_go.png differ diff --git a/media/img/icons/silk/overlays.png b/media/img/icons/silk/overlays.png new file mode 100755 index 000000000..ab3100b5c Binary files /dev/null and b/media/img/icons/silk/overlays.png differ diff --git a/media/img/icons/silk/package.png b/media/img/icons/silk/package.png new file mode 100755 index 000000000..da3c2a2d7 Binary files /dev/null and b/media/img/icons/silk/package.png differ diff --git a/media/img/icons/silk/package_add.png b/media/img/icons/silk/package_add.png new file mode 100755 index 000000000..9c8a9da4a Binary files /dev/null and b/media/img/icons/silk/package_add.png differ diff --git a/media/img/icons/silk/package_delete.png b/media/img/icons/silk/package_delete.png new file mode 100755 index 000000000..86f7fbc29 Binary files /dev/null and b/media/img/icons/silk/package_delete.png differ diff --git a/media/img/icons/silk/package_go.png b/media/img/icons/silk/package_go.png new file mode 100755 index 000000000..aace63ad6 Binary files /dev/null and b/media/img/icons/silk/package_go.png differ diff --git a/media/img/icons/silk/package_green.png b/media/img/icons/silk/package_green.png new file mode 100755 index 000000000..25b28bb6a Binary files /dev/null and b/media/img/icons/silk/package_green.png differ diff --git a/media/img/icons/silk/package_link.png b/media/img/icons/silk/package_link.png new file mode 100755 index 000000000..48e7ab55f Binary files /dev/null and b/media/img/icons/silk/package_link.png differ diff --git a/media/img/icons/silk/page.png b/media/img/icons/silk/page.png new file mode 100755 index 000000000..03ddd799f Binary files /dev/null and b/media/img/icons/silk/page.png differ diff --git a/media/img/icons/silk/page_add.png b/media/img/icons/silk/page_add.png new file mode 100755 index 000000000..d5bfa0719 Binary files /dev/null and b/media/img/icons/silk/page_add.png differ diff --git a/media/img/icons/silk/page_attach.png b/media/img/icons/silk/page_attach.png new file mode 100755 index 000000000..89ee2da07 Binary files /dev/null and b/media/img/icons/silk/page_attach.png differ diff --git a/media/img/icons/silk/page_code.png b/media/img/icons/silk/page_code.png new file mode 100755 index 000000000..f7ea90419 Binary files /dev/null and b/media/img/icons/silk/page_code.png differ diff --git a/media/img/icons/silk/page_copy.png b/media/img/icons/silk/page_copy.png new file mode 100755 index 000000000..195dc6d6c Binary files /dev/null and b/media/img/icons/silk/page_copy.png differ diff --git a/media/img/icons/silk/page_delete.png b/media/img/icons/silk/page_delete.png new file mode 100755 index 000000000..3141467c6 Binary files /dev/null and b/media/img/icons/silk/page_delete.png differ diff --git a/media/img/icons/silk/page_edit.png b/media/img/icons/silk/page_edit.png new file mode 100755 index 000000000..046811ed7 Binary files /dev/null and b/media/img/icons/silk/page_edit.png differ diff --git a/media/img/icons/silk/page_error.png b/media/img/icons/silk/page_error.png new file mode 100755 index 000000000..f07f449a4 Binary files /dev/null and b/media/img/icons/silk/page_error.png differ diff --git a/media/img/icons/silk/page_excel.png b/media/img/icons/silk/page_excel.png new file mode 100755 index 000000000..eb6158eb5 Binary files /dev/null and b/media/img/icons/silk/page_excel.png differ diff --git a/media/img/icons/silk/page_find.png b/media/img/icons/silk/page_find.png new file mode 100755 index 000000000..2f193889f Binary files /dev/null and b/media/img/icons/silk/page_find.png differ diff --git a/media/img/icons/silk/page_gear.png b/media/img/icons/silk/page_gear.png new file mode 100755 index 000000000..8e83281c5 Binary files /dev/null and b/media/img/icons/silk/page_gear.png differ diff --git a/media/img/icons/silk/page_go.png b/media/img/icons/silk/page_go.png new file mode 100755 index 000000000..80fe1ed0c Binary files /dev/null and b/media/img/icons/silk/page_go.png differ diff --git a/media/img/icons/silk/page_green.png b/media/img/icons/silk/page_green.png new file mode 100755 index 000000000..de8e003f9 Binary files /dev/null and b/media/img/icons/silk/page_green.png differ diff --git a/media/img/icons/silk/page_key.png b/media/img/icons/silk/page_key.png new file mode 100755 index 000000000..d6626cb09 Binary files /dev/null and b/media/img/icons/silk/page_key.png differ diff --git a/media/img/icons/silk/page_lightning.png b/media/img/icons/silk/page_lightning.png new file mode 100755 index 000000000..7e568703d Binary files /dev/null and b/media/img/icons/silk/page_lightning.png differ diff --git a/media/img/icons/silk/page_link.png b/media/img/icons/silk/page_link.png new file mode 100755 index 000000000..312eab091 Binary files /dev/null and b/media/img/icons/silk/page_link.png differ diff --git a/media/img/icons/silk/page_paintbrush.png b/media/img/icons/silk/page_paintbrush.png new file mode 100755 index 000000000..246a2f0b4 Binary files /dev/null and b/media/img/icons/silk/page_paintbrush.png differ diff --git a/media/img/icons/silk/page_paste.png b/media/img/icons/silk/page_paste.png new file mode 100755 index 000000000..968f073fd Binary files /dev/null and b/media/img/icons/silk/page_paste.png differ diff --git a/media/img/icons/silk/page_red.png b/media/img/icons/silk/page_red.png new file mode 100755 index 000000000..0b18247da Binary files /dev/null and b/media/img/icons/silk/page_red.png differ diff --git a/media/img/icons/silk/page_refresh.png b/media/img/icons/silk/page_refresh.png new file mode 100755 index 000000000..cf347c7d4 Binary files /dev/null and b/media/img/icons/silk/page_refresh.png differ diff --git a/media/img/icons/silk/page_save.png b/media/img/icons/silk/page_save.png new file mode 100755 index 000000000..caea546af Binary files /dev/null and b/media/img/icons/silk/page_save.png differ diff --git a/media/img/icons/silk/page_white.png b/media/img/icons/silk/page_white.png new file mode 100755 index 000000000..8b8b1ca00 Binary files /dev/null and b/media/img/icons/silk/page_white.png differ diff --git a/media/img/icons/silk/page_white_acrobat.png b/media/img/icons/silk/page_white_acrobat.png new file mode 100755 index 000000000..8f8095e46 Binary files /dev/null and b/media/img/icons/silk/page_white_acrobat.png differ diff --git a/media/img/icons/silk/page_white_actionscript.png b/media/img/icons/silk/page_white_actionscript.png new file mode 100755 index 000000000..159b24075 Binary files /dev/null and b/media/img/icons/silk/page_white_actionscript.png differ diff --git a/media/img/icons/silk/page_white_add.png b/media/img/icons/silk/page_white_add.png new file mode 100755 index 000000000..aa23dde37 Binary files /dev/null and b/media/img/icons/silk/page_white_add.png differ diff --git a/media/img/icons/silk/page_white_c.png b/media/img/icons/silk/page_white_c.png new file mode 100755 index 000000000..34a05cccf Binary files /dev/null and b/media/img/icons/silk/page_white_c.png differ diff --git a/media/img/icons/silk/page_white_camera.png b/media/img/icons/silk/page_white_camera.png new file mode 100755 index 000000000..f501a593a Binary files /dev/null and b/media/img/icons/silk/page_white_camera.png differ diff --git a/media/img/icons/silk/page_white_cd.png b/media/img/icons/silk/page_white_cd.png new file mode 100755 index 000000000..848bdaf3f Binary files /dev/null and b/media/img/icons/silk/page_white_cd.png differ diff --git a/media/img/icons/silk/page_white_code.png b/media/img/icons/silk/page_white_code.png new file mode 100755 index 000000000..0c76bd129 Binary files /dev/null and b/media/img/icons/silk/page_white_code.png differ diff --git a/media/img/icons/silk/page_white_code_red.png b/media/img/icons/silk/page_white_code_red.png new file mode 100755 index 000000000..87a691450 Binary files /dev/null and b/media/img/icons/silk/page_white_code_red.png differ diff --git a/media/img/icons/silk/page_white_coldfusion.png b/media/img/icons/silk/page_white_coldfusion.png new file mode 100755 index 000000000..c66011fb0 Binary files /dev/null and b/media/img/icons/silk/page_white_coldfusion.png differ diff --git a/media/img/icons/silk/page_white_compressed.png b/media/img/icons/silk/page_white_compressed.png new file mode 100755 index 000000000..2b6b1007f Binary files /dev/null and b/media/img/icons/silk/page_white_compressed.png differ diff --git a/media/img/icons/silk/page_white_copy.png b/media/img/icons/silk/page_white_copy.png new file mode 100755 index 000000000..a9f31a278 Binary files /dev/null and b/media/img/icons/silk/page_white_copy.png differ diff --git a/media/img/icons/silk/page_white_cplusplus.png b/media/img/icons/silk/page_white_cplusplus.png new file mode 100755 index 000000000..a87cf847c Binary files /dev/null and b/media/img/icons/silk/page_white_cplusplus.png differ diff --git a/media/img/icons/silk/page_white_csharp.png b/media/img/icons/silk/page_white_csharp.png new file mode 100755 index 000000000..ffb8fc932 Binary files /dev/null and b/media/img/icons/silk/page_white_csharp.png differ diff --git a/media/img/icons/silk/page_white_cup.png b/media/img/icons/silk/page_white_cup.png new file mode 100755 index 000000000..0a7d6f4a6 Binary files /dev/null and b/media/img/icons/silk/page_white_cup.png differ diff --git a/media/img/icons/silk/page_white_database.png b/media/img/icons/silk/page_white_database.png new file mode 100755 index 000000000..bddba1f98 Binary files /dev/null and b/media/img/icons/silk/page_white_database.png differ diff --git a/media/img/icons/silk/page_white_delete.png b/media/img/icons/silk/page_white_delete.png new file mode 100755 index 000000000..af1ecaf29 Binary files /dev/null and b/media/img/icons/silk/page_white_delete.png differ diff --git a/media/img/icons/silk/page_white_dvd.png b/media/img/icons/silk/page_white_dvd.png new file mode 100755 index 000000000..4cc537af0 Binary files /dev/null and b/media/img/icons/silk/page_white_dvd.png differ diff --git a/media/img/icons/silk/page_white_edit.png b/media/img/icons/silk/page_white_edit.png new file mode 100755 index 000000000..b93e77600 Binary files /dev/null and b/media/img/icons/silk/page_white_edit.png differ diff --git a/media/img/icons/silk/page_white_error.png b/media/img/icons/silk/page_white_error.png new file mode 100755 index 000000000..9fc5a0a10 Binary files /dev/null and b/media/img/icons/silk/page_white_error.png differ diff --git a/media/img/icons/silk/page_white_excel.png b/media/img/icons/silk/page_white_excel.png new file mode 100755 index 000000000..b977d7e52 Binary files /dev/null and b/media/img/icons/silk/page_white_excel.png differ diff --git a/media/img/icons/silk/page_white_find.png b/media/img/icons/silk/page_white_find.png new file mode 100755 index 000000000..581843637 Binary files /dev/null and b/media/img/icons/silk/page_white_find.png differ diff --git a/media/img/icons/silk/page_white_flash.png b/media/img/icons/silk/page_white_flash.png new file mode 100755 index 000000000..5769120b1 Binary files /dev/null and b/media/img/icons/silk/page_white_flash.png differ diff --git a/media/img/icons/silk/page_white_freehand.png b/media/img/icons/silk/page_white_freehand.png new file mode 100755 index 000000000..8d719df52 Binary files /dev/null and b/media/img/icons/silk/page_white_freehand.png differ diff --git a/media/img/icons/silk/page_white_gear.png b/media/img/icons/silk/page_white_gear.png new file mode 100755 index 000000000..106f5aa36 Binary files /dev/null and b/media/img/icons/silk/page_white_gear.png differ diff --git a/media/img/icons/silk/page_white_get.png b/media/img/icons/silk/page_white_get.png new file mode 100755 index 000000000..e4a1ecba1 Binary files /dev/null and b/media/img/icons/silk/page_white_get.png differ diff --git a/media/img/icons/silk/page_white_go.png b/media/img/icons/silk/page_white_go.png new file mode 100755 index 000000000..7e62a924b Binary files /dev/null and b/media/img/icons/silk/page_white_go.png differ diff --git a/media/img/icons/silk/page_white_h.png b/media/img/icons/silk/page_white_h.png new file mode 100755 index 000000000..e902abb07 Binary files /dev/null and b/media/img/icons/silk/page_white_h.png differ diff --git a/media/img/icons/silk/page_white_horizontal.png b/media/img/icons/silk/page_white_horizontal.png new file mode 100755 index 000000000..1d2d0a498 Binary files /dev/null and b/media/img/icons/silk/page_white_horizontal.png differ diff --git a/media/img/icons/silk/page_white_key.png b/media/img/icons/silk/page_white_key.png new file mode 100755 index 000000000..d61648452 Binary files /dev/null and b/media/img/icons/silk/page_white_key.png differ diff --git a/media/img/icons/silk/page_white_lightning.png b/media/img/icons/silk/page_white_lightning.png new file mode 100755 index 000000000..7215d1e8b Binary files /dev/null and b/media/img/icons/silk/page_white_lightning.png differ diff --git a/media/img/icons/silk/page_white_link.png b/media/img/icons/silk/page_white_link.png new file mode 100755 index 000000000..bf7bd1c9b Binary files /dev/null and b/media/img/icons/silk/page_white_link.png differ diff --git a/media/img/icons/silk/page_white_magnify.png b/media/img/icons/silk/page_white_magnify.png new file mode 100755 index 000000000..f6b74cc40 Binary files /dev/null and b/media/img/icons/silk/page_white_magnify.png differ diff --git a/media/img/icons/silk/page_white_medal.png b/media/img/icons/silk/page_white_medal.png new file mode 100755 index 000000000..d3fffb6d9 Binary files /dev/null and b/media/img/icons/silk/page_white_medal.png differ diff --git a/media/img/icons/silk/page_white_office.png b/media/img/icons/silk/page_white_office.png new file mode 100755 index 000000000..a65bcb3e1 Binary files /dev/null and b/media/img/icons/silk/page_white_office.png differ diff --git a/media/img/icons/silk/page_white_paint.png b/media/img/icons/silk/page_white_paint.png new file mode 100755 index 000000000..23a37b891 Binary files /dev/null and b/media/img/icons/silk/page_white_paint.png differ diff --git a/media/img/icons/silk/page_white_paintbrush.png b/media/img/icons/silk/page_white_paintbrush.png new file mode 100755 index 000000000..f907e44b3 Binary files /dev/null and b/media/img/icons/silk/page_white_paintbrush.png differ diff --git a/media/img/icons/silk/page_white_paste.png b/media/img/icons/silk/page_white_paste.png new file mode 100755 index 000000000..5b2cbb3fd Binary files /dev/null and b/media/img/icons/silk/page_white_paste.png differ diff --git a/media/img/icons/silk/page_white_php.png b/media/img/icons/silk/page_white_php.png new file mode 100755 index 000000000..7868a2594 Binary files /dev/null and b/media/img/icons/silk/page_white_php.png differ diff --git a/media/img/icons/silk/page_white_picture.png b/media/img/icons/silk/page_white_picture.png new file mode 100755 index 000000000..134b66936 Binary files /dev/null and b/media/img/icons/silk/page_white_picture.png differ diff --git a/media/img/icons/silk/page_white_powerpoint.png b/media/img/icons/silk/page_white_powerpoint.png new file mode 100755 index 000000000..c4eff0387 Binary files /dev/null and b/media/img/icons/silk/page_white_powerpoint.png differ diff --git a/media/img/icons/silk/page_white_put.png b/media/img/icons/silk/page_white_put.png new file mode 100755 index 000000000..884ffd6f0 Binary files /dev/null and b/media/img/icons/silk/page_white_put.png differ diff --git a/media/img/icons/silk/page_white_ruby.png b/media/img/icons/silk/page_white_ruby.png new file mode 100755 index 000000000..f59b7c436 Binary files /dev/null and b/media/img/icons/silk/page_white_ruby.png differ diff --git a/media/img/icons/silk/page_white_stack.png b/media/img/icons/silk/page_white_stack.png new file mode 100755 index 000000000..44084add7 Binary files /dev/null and b/media/img/icons/silk/page_white_stack.png differ diff --git a/media/img/icons/silk/page_white_star.png b/media/img/icons/silk/page_white_star.png new file mode 100755 index 000000000..3a1441c9a Binary files /dev/null and b/media/img/icons/silk/page_white_star.png differ diff --git a/media/img/icons/silk/page_white_swoosh.png b/media/img/icons/silk/page_white_swoosh.png new file mode 100755 index 000000000..e7708292a Binary files /dev/null and b/media/img/icons/silk/page_white_swoosh.png differ diff --git a/media/img/icons/silk/page_white_text.png b/media/img/icons/silk/page_white_text.png new file mode 100755 index 000000000..813f712f7 Binary files /dev/null and b/media/img/icons/silk/page_white_text.png differ diff --git a/media/img/icons/silk/page_white_text_width.png b/media/img/icons/silk/page_white_text_width.png new file mode 100755 index 000000000..d9cf13256 Binary files /dev/null and b/media/img/icons/silk/page_white_text_width.png differ diff --git a/media/img/icons/silk/page_white_tux.png b/media/img/icons/silk/page_white_tux.png new file mode 100755 index 000000000..52699bfee Binary files /dev/null and b/media/img/icons/silk/page_white_tux.png differ diff --git a/media/img/icons/silk/page_white_vector.png b/media/img/icons/silk/page_white_vector.png new file mode 100755 index 000000000..4a05955b3 Binary files /dev/null and b/media/img/icons/silk/page_white_vector.png differ diff --git a/media/img/icons/silk/page_white_visualstudio.png b/media/img/icons/silk/page_white_visualstudio.png new file mode 100755 index 000000000..a0a433dfb Binary files /dev/null and b/media/img/icons/silk/page_white_visualstudio.png differ diff --git a/media/img/icons/silk/page_white_width.png b/media/img/icons/silk/page_white_width.png new file mode 100755 index 000000000..1eb880947 Binary files /dev/null and b/media/img/icons/silk/page_white_width.png differ diff --git a/media/img/icons/silk/page_white_word.png b/media/img/icons/silk/page_white_word.png new file mode 100755 index 000000000..ae8ecbf47 Binary files /dev/null and b/media/img/icons/silk/page_white_word.png differ diff --git a/media/img/icons/silk/page_white_world.png b/media/img/icons/silk/page_white_world.png new file mode 100755 index 000000000..6ed2490ed Binary files /dev/null and b/media/img/icons/silk/page_white_world.png differ diff --git a/media/img/icons/silk/page_white_wrench.png b/media/img/icons/silk/page_white_wrench.png new file mode 100755 index 000000000..fecadd08a Binary files /dev/null and b/media/img/icons/silk/page_white_wrench.png differ diff --git a/media/img/icons/silk/page_white_zip.png b/media/img/icons/silk/page_white_zip.png new file mode 100755 index 000000000..fd4bbccdf Binary files /dev/null and b/media/img/icons/silk/page_white_zip.png differ diff --git a/media/img/icons/silk/page_word.png b/media/img/icons/silk/page_word.png new file mode 100755 index 000000000..834cdfaf4 Binary files /dev/null and b/media/img/icons/silk/page_word.png differ diff --git a/media/img/icons/silk/page_world.png b/media/img/icons/silk/page_world.png new file mode 100755 index 000000000..b8895ddec Binary files /dev/null and b/media/img/icons/silk/page_world.png differ diff --git a/media/img/icons/silk/paintbrush.png b/media/img/icons/silk/paintbrush.png new file mode 100755 index 000000000..a3ecf8778 Binary files /dev/null and b/media/img/icons/silk/paintbrush.png differ diff --git a/media/img/icons/silk/paintcan.png b/media/img/icons/silk/paintcan.png new file mode 100755 index 000000000..f82a8865a Binary files /dev/null and b/media/img/icons/silk/paintcan.png differ diff --git a/media/img/icons/silk/palette.png b/media/img/icons/silk/palette.png new file mode 100755 index 000000000..73c5b3f24 Binary files /dev/null and b/media/img/icons/silk/palette.png differ diff --git a/media/img/icons/silk/paste_plain.png b/media/img/icons/silk/paste_plain.png new file mode 100755 index 000000000..c0490eb79 Binary files /dev/null and b/media/img/icons/silk/paste_plain.png differ diff --git a/media/img/icons/silk/paste_word.png b/media/img/icons/silk/paste_word.png new file mode 100755 index 000000000..f6b87f82c Binary files /dev/null and b/media/img/icons/silk/paste_word.png differ diff --git a/media/img/icons/silk/pencil.png b/media/img/icons/silk/pencil.png new file mode 100755 index 000000000..0bfecd50e Binary files /dev/null and b/media/img/icons/silk/pencil.png differ diff --git a/media/img/icons/silk/pencil_add.png b/media/img/icons/silk/pencil_add.png new file mode 100755 index 000000000..902bbe61b Binary files /dev/null and b/media/img/icons/silk/pencil_add.png differ diff --git a/media/img/icons/silk/pencil_delete.png b/media/img/icons/silk/pencil_delete.png new file mode 100755 index 000000000..d8944e6ea Binary files /dev/null and b/media/img/icons/silk/pencil_delete.png differ diff --git a/media/img/icons/silk/pencil_go.png b/media/img/icons/silk/pencil_go.png new file mode 100755 index 000000000..937bded9d Binary files /dev/null and b/media/img/icons/silk/pencil_go.png differ diff --git a/media/img/icons/silk/phone.png b/media/img/icons/silk/phone.png new file mode 100755 index 000000000..c39f162f8 Binary files /dev/null and b/media/img/icons/silk/phone.png differ diff --git a/media/img/icons/silk/phone_add.png b/media/img/icons/silk/phone_add.png new file mode 100755 index 000000000..d3555e024 Binary files /dev/null and b/media/img/icons/silk/phone_add.png differ diff --git a/media/img/icons/silk/phone_delete.png b/media/img/icons/silk/phone_delete.png new file mode 100755 index 000000000..bbe4f8aba Binary files /dev/null and b/media/img/icons/silk/phone_delete.png differ diff --git a/media/img/icons/silk/phone_sound.png b/media/img/icons/silk/phone_sound.png new file mode 100755 index 000000000..7fdf1c58c Binary files /dev/null and b/media/img/icons/silk/phone_sound.png differ diff --git a/media/img/icons/silk/photo.png b/media/img/icons/silk/photo.png new file mode 100755 index 000000000..6c2aaaaaf Binary files /dev/null and b/media/img/icons/silk/photo.png differ diff --git a/media/img/icons/silk/photo_add.png b/media/img/icons/silk/photo_add.png new file mode 100755 index 000000000..63cc355cb Binary files /dev/null and b/media/img/icons/silk/photo_add.png differ diff --git a/media/img/icons/silk/photo_delete.png b/media/img/icons/silk/photo_delete.png new file mode 100755 index 000000000..18b67df43 Binary files /dev/null and b/media/img/icons/silk/photo_delete.png differ diff --git a/media/img/icons/silk/photo_link.png b/media/img/icons/silk/photo_link.png new file mode 100755 index 000000000..e6bb35fbf Binary files /dev/null and b/media/img/icons/silk/photo_link.png differ diff --git a/media/img/icons/silk/photos.png b/media/img/icons/silk/photos.png new file mode 100755 index 000000000..8836fe6c0 Binary files /dev/null and b/media/img/icons/silk/photos.png differ diff --git a/media/img/icons/silk/picture.png b/media/img/icons/silk/picture.png new file mode 100755 index 000000000..4a158fef7 Binary files /dev/null and b/media/img/icons/silk/picture.png differ diff --git a/media/img/icons/silk/picture_add.png b/media/img/icons/silk/picture_add.png new file mode 100755 index 000000000..d6d3f8564 Binary files /dev/null and b/media/img/icons/silk/picture_add.png differ diff --git a/media/img/icons/silk/picture_delete.png b/media/img/icons/silk/picture_delete.png new file mode 100755 index 000000000..cca9f535d Binary files /dev/null and b/media/img/icons/silk/picture_delete.png differ diff --git a/media/img/icons/silk/picture_edit.png b/media/img/icons/silk/picture_edit.png new file mode 100755 index 000000000..9a70c3499 Binary files /dev/null and b/media/img/icons/silk/picture_edit.png differ diff --git a/media/img/icons/silk/picture_empty.png b/media/img/icons/silk/picture_empty.png new file mode 100755 index 000000000..abd2b9bb4 Binary files /dev/null and b/media/img/icons/silk/picture_empty.png differ diff --git a/media/img/icons/silk/picture_error.png b/media/img/icons/silk/picture_error.png new file mode 100755 index 000000000..d41d90d64 Binary files /dev/null and b/media/img/icons/silk/picture_error.png differ diff --git a/media/img/icons/silk/picture_go.png b/media/img/icons/silk/picture_go.png new file mode 100755 index 000000000..27c63c5af Binary files /dev/null and b/media/img/icons/silk/picture_go.png differ diff --git a/media/img/icons/silk/picture_key.png b/media/img/icons/silk/picture_key.png new file mode 100755 index 000000000..667086c0d Binary files /dev/null and b/media/img/icons/silk/picture_key.png differ diff --git a/media/img/icons/silk/picture_link.png b/media/img/icons/silk/picture_link.png new file mode 100755 index 000000000..42dca7440 Binary files /dev/null and b/media/img/icons/silk/picture_link.png differ diff --git a/media/img/icons/silk/picture_save.png b/media/img/icons/silk/picture_save.png new file mode 100755 index 000000000..777fb5d2e Binary files /dev/null and b/media/img/icons/silk/picture_save.png differ diff --git a/media/img/icons/silk/pictures.png b/media/img/icons/silk/pictures.png new file mode 100755 index 000000000..d9591c13f Binary files /dev/null and b/media/img/icons/silk/pictures.png differ diff --git a/media/img/icons/silk/pilcrow.png b/media/img/icons/silk/pilcrow.png new file mode 100755 index 000000000..95704fbab Binary files /dev/null and b/media/img/icons/silk/pilcrow.png differ diff --git a/media/img/icons/silk/pill.png b/media/img/icons/silk/pill.png new file mode 100755 index 000000000..f2bdef6be Binary files /dev/null and b/media/img/icons/silk/pill.png differ diff --git a/media/img/icons/silk/pill_add.png b/media/img/icons/silk/pill_add.png new file mode 100755 index 000000000..ac9c2df6a Binary files /dev/null and b/media/img/icons/silk/pill_add.png differ diff --git a/media/img/icons/silk/pill_delete.png b/media/img/icons/silk/pill_delete.png new file mode 100755 index 000000000..c61592e8d Binary files /dev/null and b/media/img/icons/silk/pill_delete.png differ diff --git a/media/img/icons/silk/pill_go.png b/media/img/icons/silk/pill_go.png new file mode 100755 index 000000000..e5c07d415 Binary files /dev/null and b/media/img/icons/silk/pill_go.png differ diff --git a/media/img/icons/silk/plugin.png b/media/img/icons/silk/plugin.png new file mode 100755 index 000000000..6187b15ae Binary files /dev/null and b/media/img/icons/silk/plugin.png differ diff --git a/media/img/icons/silk/plugin_add.png b/media/img/icons/silk/plugin_add.png new file mode 100755 index 000000000..ae43690ec Binary files /dev/null and b/media/img/icons/silk/plugin_add.png differ diff --git a/media/img/icons/silk/plugin_delete.png b/media/img/icons/silk/plugin_delete.png new file mode 100755 index 000000000..d9c3376d4 Binary files /dev/null and b/media/img/icons/silk/plugin_delete.png differ diff --git a/media/img/icons/silk/plugin_disabled.png b/media/img/icons/silk/plugin_disabled.png new file mode 100755 index 000000000..f4f6be59c Binary files /dev/null and b/media/img/icons/silk/plugin_disabled.png differ diff --git a/media/img/icons/silk/plugin_edit.png b/media/img/icons/silk/plugin_edit.png new file mode 100755 index 000000000..b6cb0ecf7 Binary files /dev/null and b/media/img/icons/silk/plugin_edit.png differ diff --git a/media/img/icons/silk/plugin_error.png b/media/img/icons/silk/plugin_error.png new file mode 100755 index 000000000..cff65d7fe Binary files /dev/null and b/media/img/icons/silk/plugin_error.png differ diff --git a/media/img/icons/silk/plugin_go.png b/media/img/icons/silk/plugin_go.png new file mode 100755 index 000000000..41da9913d Binary files /dev/null and b/media/img/icons/silk/plugin_go.png differ diff --git a/media/img/icons/silk/plugin_link.png b/media/img/icons/silk/plugin_link.png new file mode 100755 index 000000000..445c18868 Binary files /dev/null and b/media/img/icons/silk/plugin_link.png differ diff --git a/media/img/icons/silk/printer.png b/media/img/icons/silk/printer.png new file mode 100755 index 000000000..a350d1871 Binary files /dev/null and b/media/img/icons/silk/printer.png differ diff --git a/media/img/icons/silk/printer_add.png b/media/img/icons/silk/printer_add.png new file mode 100755 index 000000000..d228d0580 Binary files /dev/null and b/media/img/icons/silk/printer_add.png differ diff --git a/media/img/icons/silk/printer_delete.png b/media/img/icons/silk/printer_delete.png new file mode 100755 index 000000000..1d8605f2f Binary files /dev/null and b/media/img/icons/silk/printer_delete.png differ diff --git a/media/img/icons/silk/printer_empty.png b/media/img/icons/silk/printer_empty.png new file mode 100755 index 000000000..94e8c1618 Binary files /dev/null and b/media/img/icons/silk/printer_empty.png differ diff --git a/media/img/icons/silk/printer_error.png b/media/img/icons/silk/printer_error.png new file mode 100755 index 000000000..279ebb0e5 Binary files /dev/null and b/media/img/icons/silk/printer_error.png differ diff --git a/media/img/icons/silk/rainbow.png b/media/img/icons/silk/rainbow.png new file mode 100755 index 000000000..5ede989a4 Binary files /dev/null and b/media/img/icons/silk/rainbow.png differ diff --git a/media/img/icons/silk/report.png b/media/img/icons/silk/report.png new file mode 100755 index 000000000..779ad58ef Binary files /dev/null and b/media/img/icons/silk/report.png differ diff --git a/media/img/icons/silk/report_add.png b/media/img/icons/silk/report_add.png new file mode 100755 index 000000000..d5eac9bcc Binary files /dev/null and b/media/img/icons/silk/report_add.png differ diff --git a/media/img/icons/silk/report_delete.png b/media/img/icons/silk/report_delete.png new file mode 100755 index 000000000..dcce0b64d Binary files /dev/null and b/media/img/icons/silk/report_delete.png differ diff --git a/media/img/icons/silk/report_disk.png b/media/img/icons/silk/report_disk.png new file mode 100755 index 000000000..1c856cd61 Binary files /dev/null and b/media/img/icons/silk/report_disk.png differ diff --git a/media/img/icons/silk/report_edit.png b/media/img/icons/silk/report_edit.png new file mode 100755 index 000000000..c61a6d847 Binary files /dev/null and b/media/img/icons/silk/report_edit.png differ diff --git a/media/img/icons/silk/report_go.png b/media/img/icons/silk/report_go.png new file mode 100755 index 000000000..f35a97938 Binary files /dev/null and b/media/img/icons/silk/report_go.png differ diff --git a/media/img/icons/silk/report_key.png b/media/img/icons/silk/report_key.png new file mode 100755 index 000000000..90b758e8f Binary files /dev/null and b/media/img/icons/silk/report_key.png differ diff --git a/media/img/icons/silk/report_link.png b/media/img/icons/silk/report_link.png new file mode 100755 index 000000000..23f2611e9 Binary files /dev/null and b/media/img/icons/silk/report_link.png differ diff --git a/media/img/icons/silk/report_magnify.png b/media/img/icons/silk/report_magnify.png new file mode 100755 index 000000000..aeaa88953 Binary files /dev/null and b/media/img/icons/silk/report_magnify.png differ diff --git a/media/img/icons/silk/report_picture.png b/media/img/icons/silk/report_picture.png new file mode 100755 index 000000000..3a9a7e5eb Binary files /dev/null and b/media/img/icons/silk/report_picture.png differ diff --git a/media/img/icons/silk/report_user.png b/media/img/icons/silk/report_user.png new file mode 100755 index 000000000..7766edd74 Binary files /dev/null and b/media/img/icons/silk/report_user.png differ diff --git a/media/img/icons/silk/report_word.png b/media/img/icons/silk/report_word.png new file mode 100755 index 000000000..995134248 Binary files /dev/null and b/media/img/icons/silk/report_word.png differ diff --git a/media/img/icons/silk/resultset_first.png b/media/img/icons/silk/resultset_first.png new file mode 100755 index 000000000..b03eaf8b5 Binary files /dev/null and b/media/img/icons/silk/resultset_first.png differ diff --git a/media/img/icons/silk/resultset_last.png b/media/img/icons/silk/resultset_last.png new file mode 100755 index 000000000..8ec894784 Binary files /dev/null and b/media/img/icons/silk/resultset_last.png differ diff --git a/media/img/icons/silk/resultset_next.png b/media/img/icons/silk/resultset_next.png new file mode 100755 index 000000000..e252606d3 Binary files /dev/null and b/media/img/icons/silk/resultset_next.png differ diff --git a/media/img/icons/silk/resultset_previous.png b/media/img/icons/silk/resultset_previous.png new file mode 100755 index 000000000..18f9cc109 Binary files /dev/null and b/media/img/icons/silk/resultset_previous.png differ diff --git a/media/img/icons/silk/rosette.png b/media/img/icons/silk/rosette.png new file mode 100755 index 000000000..f233bc770 Binary files /dev/null and b/media/img/icons/silk/rosette.png differ diff --git a/media/img/icons/silk/rss.png b/media/img/icons/silk/rss.png new file mode 100755 index 000000000..1dc6ff30b Binary files /dev/null and b/media/img/icons/silk/rss.png differ diff --git a/media/img/icons/silk/rss_add.png b/media/img/icons/silk/rss_add.png new file mode 100755 index 000000000..b590beb73 Binary files /dev/null and b/media/img/icons/silk/rss_add.png differ diff --git a/media/img/icons/silk/rss_delete.png b/media/img/icons/silk/rss_delete.png new file mode 100755 index 000000000..9deb738de Binary files /dev/null and b/media/img/icons/silk/rss_delete.png differ diff --git a/media/img/icons/silk/rss_go.png b/media/img/icons/silk/rss_go.png new file mode 100755 index 000000000..43a86bff6 Binary files /dev/null and b/media/img/icons/silk/rss_go.png differ diff --git a/media/img/icons/silk/rss_valid.png b/media/img/icons/silk/rss_valid.png new file mode 100755 index 000000000..a6d0b0e87 Binary files /dev/null and b/media/img/icons/silk/rss_valid.png differ diff --git a/media/img/icons/silk/ruby.png b/media/img/icons/silk/ruby.png new file mode 100755 index 000000000..f763a1688 Binary files /dev/null and b/media/img/icons/silk/ruby.png differ diff --git a/media/img/icons/silk/ruby_add.png b/media/img/icons/silk/ruby_add.png new file mode 100755 index 000000000..a2cd648f6 Binary files /dev/null and b/media/img/icons/silk/ruby_add.png differ diff --git a/media/img/icons/silk/ruby_delete.png b/media/img/icons/silk/ruby_delete.png new file mode 100755 index 000000000..30022630d Binary files /dev/null and b/media/img/icons/silk/ruby_delete.png differ diff --git a/media/img/icons/silk/ruby_gear.png b/media/img/icons/silk/ruby_gear.png new file mode 100755 index 000000000..4a10590b4 Binary files /dev/null and b/media/img/icons/silk/ruby_gear.png differ diff --git a/media/img/icons/silk/ruby_get.png b/media/img/icons/silk/ruby_get.png new file mode 100755 index 000000000..f5203c7e9 Binary files /dev/null and b/media/img/icons/silk/ruby_get.png differ diff --git a/media/img/icons/silk/ruby_go.png b/media/img/icons/silk/ruby_go.png new file mode 100755 index 000000000..d8d276e3c Binary files /dev/null and b/media/img/icons/silk/ruby_go.png differ diff --git a/media/img/icons/silk/ruby_key.png b/media/img/icons/silk/ruby_key.png new file mode 100755 index 000000000..451cfebe2 Binary files /dev/null and b/media/img/icons/silk/ruby_key.png differ diff --git a/media/img/icons/silk/ruby_link.png b/media/img/icons/silk/ruby_link.png new file mode 100755 index 000000000..bf4be526f Binary files /dev/null and b/media/img/icons/silk/ruby_link.png differ diff --git a/media/img/icons/silk/ruby_put.png b/media/img/icons/silk/ruby_put.png new file mode 100755 index 000000000..e026323c2 Binary files /dev/null and b/media/img/icons/silk/ruby_put.png differ diff --git a/media/img/icons/silk/script.png b/media/img/icons/silk/script.png new file mode 100755 index 000000000..0f9ed4d48 Binary files /dev/null and b/media/img/icons/silk/script.png differ diff --git a/media/img/icons/silk/script_add.png b/media/img/icons/silk/script_add.png new file mode 100755 index 000000000..d650552d9 Binary files /dev/null and b/media/img/icons/silk/script_add.png differ diff --git a/media/img/icons/silk/script_code.png b/media/img/icons/silk/script_code.png new file mode 100755 index 000000000..63fe6ceff Binary files /dev/null and b/media/img/icons/silk/script_code.png differ diff --git a/media/img/icons/silk/script_code_red.png b/media/img/icons/silk/script_code_red.png new file mode 100755 index 000000000..8fcf0f09a Binary files /dev/null and b/media/img/icons/silk/script_code_red.png differ diff --git a/media/img/icons/silk/script_delete.png b/media/img/icons/silk/script_delete.png new file mode 100755 index 000000000..e6500ced7 Binary files /dev/null and b/media/img/icons/silk/script_delete.png differ diff --git a/media/img/icons/silk/script_edit.png b/media/img/icons/silk/script_edit.png new file mode 100755 index 000000000..b4d31ce28 Binary files /dev/null and b/media/img/icons/silk/script_edit.png differ diff --git a/media/img/icons/silk/script_error.png b/media/img/icons/silk/script_error.png new file mode 100755 index 000000000..04919548e Binary files /dev/null and b/media/img/icons/silk/script_error.png differ diff --git a/media/img/icons/silk/script_gear.png b/media/img/icons/silk/script_gear.png new file mode 100755 index 000000000..56fcf84a8 Binary files /dev/null and b/media/img/icons/silk/script_gear.png differ diff --git a/media/img/icons/silk/script_go.png b/media/img/icons/silk/script_go.png new file mode 100755 index 000000000..8e154e231 Binary files /dev/null and b/media/img/icons/silk/script_go.png differ diff --git a/media/img/icons/silk/script_key.png b/media/img/icons/silk/script_key.png new file mode 100755 index 000000000..49bb24d71 Binary files /dev/null and b/media/img/icons/silk/script_key.png differ diff --git a/media/img/icons/silk/script_lightning.png b/media/img/icons/silk/script_lightning.png new file mode 100755 index 000000000..b3fa18ce2 Binary files /dev/null and b/media/img/icons/silk/script_lightning.png differ diff --git a/media/img/icons/silk/script_link.png b/media/img/icons/silk/script_link.png new file mode 100755 index 000000000..bdeb9852b Binary files /dev/null and b/media/img/icons/silk/script_link.png differ diff --git a/media/img/icons/silk/script_palette.png b/media/img/icons/silk/script_palette.png new file mode 100755 index 000000000..6d46962dc Binary files /dev/null and b/media/img/icons/silk/script_palette.png differ diff --git a/media/img/icons/silk/script_save.png b/media/img/icons/silk/script_save.png new file mode 100755 index 000000000..36216d827 Binary files /dev/null and b/media/img/icons/silk/script_save.png differ diff --git a/media/img/icons/silk/server.png b/media/img/icons/silk/server.png new file mode 100755 index 000000000..720a237c7 Binary files /dev/null and b/media/img/icons/silk/server.png differ diff --git a/media/img/icons/silk/server_add.png b/media/img/icons/silk/server_add.png new file mode 100755 index 000000000..3f10a3a9f Binary files /dev/null and b/media/img/icons/silk/server_add.png differ diff --git a/media/img/icons/silk/server_chart.png b/media/img/icons/silk/server_chart.png new file mode 100755 index 000000000..1128d3f33 Binary files /dev/null and b/media/img/icons/silk/server_chart.png differ diff --git a/media/img/icons/silk/server_compressed.png b/media/img/icons/silk/server_compressed.png new file mode 100755 index 000000000..bf49fad9d Binary files /dev/null and b/media/img/icons/silk/server_compressed.png differ diff --git a/media/img/icons/silk/server_connect.png b/media/img/icons/silk/server_connect.png new file mode 100755 index 000000000..49b269145 Binary files /dev/null and b/media/img/icons/silk/server_connect.png differ diff --git a/media/img/icons/silk/server_database.png b/media/img/icons/silk/server_database.png new file mode 100755 index 000000000..b24e826c7 Binary files /dev/null and b/media/img/icons/silk/server_database.png differ diff --git a/media/img/icons/silk/server_delete.png b/media/img/icons/silk/server_delete.png new file mode 100755 index 000000000..61e740fe1 Binary files /dev/null and b/media/img/icons/silk/server_delete.png differ diff --git a/media/img/icons/silk/server_edit.png b/media/img/icons/silk/server_edit.png new file mode 100755 index 000000000..dc7625371 Binary files /dev/null and b/media/img/icons/silk/server_edit.png differ diff --git a/media/img/icons/silk/server_error.png b/media/img/icons/silk/server_error.png new file mode 100755 index 000000000..f64025639 Binary files /dev/null and b/media/img/icons/silk/server_error.png differ diff --git a/media/img/icons/silk/server_go.png b/media/img/icons/silk/server_go.png new file mode 100755 index 000000000..540c8e268 Binary files /dev/null and b/media/img/icons/silk/server_go.png differ diff --git a/media/img/icons/silk/server_key.png b/media/img/icons/silk/server_key.png new file mode 100755 index 000000000..ecd517425 Binary files /dev/null and b/media/img/icons/silk/server_key.png differ diff --git a/media/img/icons/silk/server_lightning.png b/media/img/icons/silk/server_lightning.png new file mode 100755 index 000000000..b0f4e46cd Binary files /dev/null and b/media/img/icons/silk/server_lightning.png differ diff --git a/media/img/icons/silk/server_link.png b/media/img/icons/silk/server_link.png new file mode 100755 index 000000000..e8821dfd8 Binary files /dev/null and b/media/img/icons/silk/server_link.png differ diff --git a/media/img/icons/silk/server_uncompressed.png b/media/img/icons/silk/server_uncompressed.png new file mode 100755 index 000000000..86e8325b9 Binary files /dev/null and b/media/img/icons/silk/server_uncompressed.png differ diff --git a/media/img/icons/silk/shading.png b/media/img/icons/silk/shading.png new file mode 100755 index 000000000..09275f9c0 Binary files /dev/null and b/media/img/icons/silk/shading.png differ diff --git a/media/img/icons/silk/shape_align_bottom.png b/media/img/icons/silk/shape_align_bottom.png new file mode 100755 index 000000000..55d269400 Binary files /dev/null and b/media/img/icons/silk/shape_align_bottom.png differ diff --git a/media/img/icons/silk/shape_align_center.png b/media/img/icons/silk/shape_align_center.png new file mode 100755 index 000000000..efe9a98e5 Binary files /dev/null and b/media/img/icons/silk/shape_align_center.png differ diff --git a/media/img/icons/silk/shape_align_left.png b/media/img/icons/silk/shape_align_left.png new file mode 100755 index 000000000..aaedc41b5 Binary files /dev/null and b/media/img/icons/silk/shape_align_left.png differ diff --git a/media/img/icons/silk/shape_align_middle.png b/media/img/icons/silk/shape_align_middle.png new file mode 100755 index 000000000..d350dd88f Binary files /dev/null and b/media/img/icons/silk/shape_align_middle.png differ diff --git a/media/img/icons/silk/shape_align_right.png b/media/img/icons/silk/shape_align_right.png new file mode 100755 index 000000000..ff556b6a9 Binary files /dev/null and b/media/img/icons/silk/shape_align_right.png differ diff --git a/media/img/icons/silk/shape_align_top.png b/media/img/icons/silk/shape_align_top.png new file mode 100755 index 000000000..1181b43fb Binary files /dev/null and b/media/img/icons/silk/shape_align_top.png differ diff --git a/media/img/icons/silk/shape_flip_horizontal.png b/media/img/icons/silk/shape_flip_horizontal.png new file mode 100755 index 000000000..8667c81f8 Binary files /dev/null and b/media/img/icons/silk/shape_flip_horizontal.png differ diff --git a/media/img/icons/silk/shape_flip_vertical.png b/media/img/icons/silk/shape_flip_vertical.png new file mode 100755 index 000000000..0bd66d19b Binary files /dev/null and b/media/img/icons/silk/shape_flip_vertical.png differ diff --git a/media/img/icons/silk/shape_group.png b/media/img/icons/silk/shape_group.png new file mode 100755 index 000000000..bb2ff516d Binary files /dev/null and b/media/img/icons/silk/shape_group.png differ diff --git a/media/img/icons/silk/shape_handles.png b/media/img/icons/silk/shape_handles.png new file mode 100755 index 000000000..ce27fe3a0 Binary files /dev/null and b/media/img/icons/silk/shape_handles.png differ diff --git a/media/img/icons/silk/shape_move_back.png b/media/img/icons/silk/shape_move_back.png new file mode 100755 index 000000000..a216ffd36 Binary files /dev/null and b/media/img/icons/silk/shape_move_back.png differ diff --git a/media/img/icons/silk/shape_move_backwards.png b/media/img/icons/silk/shape_move_backwards.png new file mode 100755 index 000000000..ee3f9b27a Binary files /dev/null and b/media/img/icons/silk/shape_move_backwards.png differ diff --git a/media/img/icons/silk/shape_move_forwards.png b/media/img/icons/silk/shape_move_forwards.png new file mode 100755 index 000000000..cfe44932c Binary files /dev/null and b/media/img/icons/silk/shape_move_forwards.png differ diff --git a/media/img/icons/silk/shape_move_front.png b/media/img/icons/silk/shape_move_front.png new file mode 100755 index 000000000..b4a4e3b78 Binary files /dev/null and b/media/img/icons/silk/shape_move_front.png differ diff --git a/media/img/icons/silk/shape_rotate_anticlockwise.png b/media/img/icons/silk/shape_rotate_anticlockwise.png new file mode 100755 index 000000000..07a30206c Binary files /dev/null and b/media/img/icons/silk/shape_rotate_anticlockwise.png differ diff --git a/media/img/icons/silk/shape_rotate_clockwise.png b/media/img/icons/silk/shape_rotate_clockwise.png new file mode 100755 index 000000000..b99db7d70 Binary files /dev/null and b/media/img/icons/silk/shape_rotate_clockwise.png differ diff --git a/media/img/icons/silk/shape_square.png b/media/img/icons/silk/shape_square.png new file mode 100755 index 000000000..33af04609 Binary files /dev/null and b/media/img/icons/silk/shape_square.png differ diff --git a/media/img/icons/silk/shape_square_add.png b/media/img/icons/silk/shape_square_add.png new file mode 100755 index 000000000..31edfce59 Binary files /dev/null and b/media/img/icons/silk/shape_square_add.png differ diff --git a/media/img/icons/silk/shape_square_delete.png b/media/img/icons/silk/shape_square_delete.png new file mode 100755 index 000000000..ede912de0 Binary files /dev/null and b/media/img/icons/silk/shape_square_delete.png differ diff --git a/media/img/icons/silk/shape_square_edit.png b/media/img/icons/silk/shape_square_edit.png new file mode 100755 index 000000000..d28dc6b1a Binary files /dev/null and b/media/img/icons/silk/shape_square_edit.png differ diff --git a/media/img/icons/silk/shape_square_error.png b/media/img/icons/silk/shape_square_error.png new file mode 100755 index 000000000..0d0dcfa9a Binary files /dev/null and b/media/img/icons/silk/shape_square_error.png differ diff --git a/media/img/icons/silk/shape_square_go.png b/media/img/icons/silk/shape_square_go.png new file mode 100755 index 000000000..5a2ad9019 Binary files /dev/null and b/media/img/icons/silk/shape_square_go.png differ diff --git a/media/img/icons/silk/shape_square_key.png b/media/img/icons/silk/shape_square_key.png new file mode 100755 index 000000000..c34b982a0 Binary files /dev/null and b/media/img/icons/silk/shape_square_key.png differ diff --git a/media/img/icons/silk/shape_square_link.png b/media/img/icons/silk/shape_square_link.png new file mode 100755 index 000000000..b885fcc60 Binary files /dev/null and b/media/img/icons/silk/shape_square_link.png differ diff --git a/media/img/icons/silk/shape_ungroup.png b/media/img/icons/silk/shape_ungroup.png new file mode 100755 index 000000000..3a6f369a5 Binary files /dev/null and b/media/img/icons/silk/shape_ungroup.png differ diff --git a/media/img/icons/silk/shield.png b/media/img/icons/silk/shield.png new file mode 100755 index 000000000..3cb4e2578 Binary files /dev/null and b/media/img/icons/silk/shield.png differ diff --git a/media/img/icons/silk/shield_add.png b/media/img/icons/silk/shield_add.png new file mode 100755 index 000000000..e20a1b4ab Binary files /dev/null and b/media/img/icons/silk/shield_add.png differ diff --git a/media/img/icons/silk/shield_delete.png b/media/img/icons/silk/shield_delete.png new file mode 100755 index 000000000..22823a70d Binary files /dev/null and b/media/img/icons/silk/shield_delete.png differ diff --git a/media/img/icons/silk/shield_go.png b/media/img/icons/silk/shield_go.png new file mode 100755 index 000000000..e9bd85224 Binary files /dev/null and b/media/img/icons/silk/shield_go.png differ diff --git a/media/img/icons/silk/sitemap.png b/media/img/icons/silk/sitemap.png new file mode 100755 index 000000000..ca779f323 Binary files /dev/null and b/media/img/icons/silk/sitemap.png differ diff --git a/media/img/icons/silk/sitemap_color.png b/media/img/icons/silk/sitemap_color.png new file mode 100755 index 000000000..c64582bcd Binary files /dev/null and b/media/img/icons/silk/sitemap_color.png differ diff --git a/media/img/icons/silk/sound.png b/media/img/icons/silk/sound.png new file mode 100755 index 000000000..6056d234a Binary files /dev/null and b/media/img/icons/silk/sound.png differ diff --git a/media/img/icons/silk/sound_add.png b/media/img/icons/silk/sound_add.png new file mode 100755 index 000000000..965c503c6 Binary files /dev/null and b/media/img/icons/silk/sound_add.png differ diff --git a/media/img/icons/silk/sound_delete.png b/media/img/icons/silk/sound_delete.png new file mode 100755 index 000000000..ab9577aa1 Binary files /dev/null and b/media/img/icons/silk/sound_delete.png differ diff --git a/media/img/icons/silk/sound_low.png b/media/img/icons/silk/sound_low.png new file mode 100755 index 000000000..4d918633f Binary files /dev/null and b/media/img/icons/silk/sound_low.png differ diff --git a/media/img/icons/silk/sound_mute.png b/media/img/icons/silk/sound_mute.png new file mode 100755 index 000000000..b652d2a71 Binary files /dev/null and b/media/img/icons/silk/sound_mute.png differ diff --git a/media/img/icons/silk/sound_none.png b/media/img/icons/silk/sound_none.png new file mode 100755 index 000000000..b497ebd54 Binary files /dev/null and b/media/img/icons/silk/sound_none.png differ diff --git a/media/img/icons/silk/spellcheck.png b/media/img/icons/silk/spellcheck.png new file mode 100755 index 000000000..ebc632d9b Binary files /dev/null and b/media/img/icons/silk/spellcheck.png differ diff --git a/media/img/icons/silk/sport_8ball.png b/media/img/icons/silk/sport_8ball.png new file mode 100755 index 000000000..4f627b768 Binary files /dev/null and b/media/img/icons/silk/sport_8ball.png differ diff --git a/media/img/icons/silk/sport_basketball.png b/media/img/icons/silk/sport_basketball.png new file mode 100755 index 000000000..f7a000b9a Binary files /dev/null and b/media/img/icons/silk/sport_basketball.png differ diff --git a/media/img/icons/silk/sport_football.png b/media/img/icons/silk/sport_football.png new file mode 100755 index 000000000..199f0f7f1 Binary files /dev/null and b/media/img/icons/silk/sport_football.png differ diff --git a/media/img/icons/silk/sport_golf.png b/media/img/icons/silk/sport_golf.png new file mode 100755 index 000000000..e21fa44c5 Binary files /dev/null and b/media/img/icons/silk/sport_golf.png differ diff --git a/media/img/icons/silk/sport_raquet.png b/media/img/icons/silk/sport_raquet.png new file mode 100755 index 000000000..f5e0f0c2c Binary files /dev/null and b/media/img/icons/silk/sport_raquet.png differ diff --git a/media/img/icons/silk/sport_shuttlecock.png b/media/img/icons/silk/sport_shuttlecock.png new file mode 100755 index 000000000..917287fa0 Binary files /dev/null and b/media/img/icons/silk/sport_shuttlecock.png differ diff --git a/media/img/icons/silk/sport_soccer.png b/media/img/icons/silk/sport_soccer.png new file mode 100755 index 000000000..3eb1828b1 Binary files /dev/null and b/media/img/icons/silk/sport_soccer.png differ diff --git a/media/img/icons/silk/sport_tennis.png b/media/img/icons/silk/sport_tennis.png new file mode 100755 index 000000000..e88a6efa1 Binary files /dev/null and b/media/img/icons/silk/sport_tennis.png differ diff --git a/media/img/icons/silk/star.png b/media/img/icons/silk/star.png new file mode 100755 index 000000000..b88c85789 Binary files /dev/null and b/media/img/icons/silk/star.png differ diff --git a/media/img/icons/silk/status_away.png b/media/img/icons/silk/status_away.png new file mode 100755 index 000000000..70bcbccaa Binary files /dev/null and b/media/img/icons/silk/status_away.png differ diff --git a/media/img/icons/silk/status_busy.png b/media/img/icons/silk/status_busy.png new file mode 100755 index 000000000..987c806ff Binary files /dev/null and b/media/img/icons/silk/status_busy.png differ diff --git a/media/img/icons/silk/status_offline.png b/media/img/icons/silk/status_offline.png new file mode 100755 index 000000000..a88261a65 Binary files /dev/null and b/media/img/icons/silk/status_offline.png differ diff --git a/media/img/icons/silk/status_online.png b/media/img/icons/silk/status_online.png new file mode 100755 index 000000000..947bd4b62 Binary files /dev/null and b/media/img/icons/silk/status_online.png differ diff --git a/media/img/icons/silk/stop.png b/media/img/icons/silk/stop.png new file mode 100755 index 000000000..0cfd58596 Binary files /dev/null and b/media/img/icons/silk/stop.png differ diff --git a/media/img/icons/silk/style.png b/media/img/icons/silk/style.png new file mode 100755 index 000000000..81e41de7d Binary files /dev/null and b/media/img/icons/silk/style.png differ diff --git a/media/img/icons/silk/style_add.png b/media/img/icons/silk/style_add.png new file mode 100755 index 000000000..e0369c6be Binary files /dev/null and b/media/img/icons/silk/style_add.png differ diff --git a/media/img/icons/silk/style_delete.png b/media/img/icons/silk/style_delete.png new file mode 100755 index 000000000..640f187ec Binary files /dev/null and b/media/img/icons/silk/style_delete.png differ diff --git a/media/img/icons/silk/style_edit.png b/media/img/icons/silk/style_edit.png new file mode 100755 index 000000000..25bb5b677 Binary files /dev/null and b/media/img/icons/silk/style_edit.png differ diff --git a/media/img/icons/silk/style_go.png b/media/img/icons/silk/style_go.png new file mode 100755 index 000000000..25d6181ac Binary files /dev/null and b/media/img/icons/silk/style_go.png differ diff --git a/media/img/icons/silk/sum.png b/media/img/icons/silk/sum.png new file mode 100755 index 000000000..fd7b32e43 Binary files /dev/null and b/media/img/icons/silk/sum.png differ diff --git a/media/img/icons/silk/tab.png b/media/img/icons/silk/tab.png new file mode 100755 index 000000000..3d8207fd7 Binary files /dev/null and b/media/img/icons/silk/tab.png differ diff --git a/media/img/icons/silk/tab_add.png b/media/img/icons/silk/tab_add.png new file mode 100755 index 000000000..d3b99364a Binary files /dev/null and b/media/img/icons/silk/tab_add.png differ diff --git a/media/img/icons/silk/tab_delete.png b/media/img/icons/silk/tab_delete.png new file mode 100755 index 000000000..100da2f1a Binary files /dev/null and b/media/img/icons/silk/tab_delete.png differ diff --git a/media/img/icons/silk/tab_edit.png b/media/img/icons/silk/tab_edit.png new file mode 100755 index 000000000..4c09c0fd7 Binary files /dev/null and b/media/img/icons/silk/tab_edit.png differ diff --git a/media/img/icons/silk/tab_go.png b/media/img/icons/silk/tab_go.png new file mode 100755 index 000000000..844ce04bd Binary files /dev/null and b/media/img/icons/silk/tab_go.png differ diff --git a/media/img/icons/silk/table.png b/media/img/icons/silk/table.png new file mode 100755 index 000000000..abcd93689 Binary files /dev/null and b/media/img/icons/silk/table.png differ diff --git a/media/img/icons/silk/table_add.png b/media/img/icons/silk/table_add.png new file mode 100755 index 000000000..2a3e5c4df Binary files /dev/null and b/media/img/icons/silk/table_add.png differ diff --git a/media/img/icons/silk/table_delete.png b/media/img/icons/silk/table_delete.png new file mode 100755 index 000000000..b85916d92 Binary files /dev/null and b/media/img/icons/silk/table_delete.png differ diff --git a/media/img/icons/silk/table_edit.png b/media/img/icons/silk/table_edit.png new file mode 100755 index 000000000..bfcb0249a Binary files /dev/null and b/media/img/icons/silk/table_edit.png differ diff --git a/media/img/icons/silk/table_error.png b/media/img/icons/silk/table_error.png new file mode 100755 index 000000000..589e92b55 Binary files /dev/null and b/media/img/icons/silk/table_error.png differ diff --git a/media/img/icons/silk/table_gear.png b/media/img/icons/silk/table_gear.png new file mode 100755 index 000000000..cfc2702ac Binary files /dev/null and b/media/img/icons/silk/table_gear.png differ diff --git a/media/img/icons/silk/table_go.png b/media/img/icons/silk/table_go.png new file mode 100755 index 000000000..0528dfa24 Binary files /dev/null and b/media/img/icons/silk/table_go.png differ diff --git a/media/img/icons/silk/table_key.png b/media/img/icons/silk/table_key.png new file mode 100755 index 000000000..34e23e24e Binary files /dev/null and b/media/img/icons/silk/table_key.png differ diff --git a/media/img/icons/silk/table_lightning.png b/media/img/icons/silk/table_lightning.png new file mode 100755 index 000000000..612612b5e Binary files /dev/null and b/media/img/icons/silk/table_lightning.png differ diff --git a/media/img/icons/silk/table_link.png b/media/img/icons/silk/table_link.png new file mode 100755 index 000000000..decac8a62 Binary files /dev/null and b/media/img/icons/silk/table_link.png differ diff --git a/media/img/icons/silk/table_multiple.png b/media/img/icons/silk/table_multiple.png new file mode 100755 index 000000000..d76448e34 Binary files /dev/null and b/media/img/icons/silk/table_multiple.png differ diff --git a/media/img/icons/silk/table_refresh.png b/media/img/icons/silk/table_refresh.png new file mode 100755 index 000000000..ab92010c2 Binary files /dev/null and b/media/img/icons/silk/table_refresh.png differ diff --git a/media/img/icons/silk/table_relationship.png b/media/img/icons/silk/table_relationship.png new file mode 100755 index 000000000..28b8505c0 Binary files /dev/null and b/media/img/icons/silk/table_relationship.png differ diff --git a/media/img/icons/silk/table_row_delete.png b/media/img/icons/silk/table_row_delete.png new file mode 100755 index 000000000..54c69691e Binary files /dev/null and b/media/img/icons/silk/table_row_delete.png differ diff --git a/media/img/icons/silk/table_row_insert.png b/media/img/icons/silk/table_row_insert.png new file mode 100755 index 000000000..ff5925efd Binary files /dev/null and b/media/img/icons/silk/table_row_insert.png differ diff --git a/media/img/icons/silk/table_save.png b/media/img/icons/silk/table_save.png new file mode 100755 index 000000000..25b74d18f Binary files /dev/null and b/media/img/icons/silk/table_save.png differ diff --git a/media/img/icons/silk/table_sort.png b/media/img/icons/silk/table_sort.png new file mode 100755 index 000000000..ed6785a6a Binary files /dev/null and b/media/img/icons/silk/table_sort.png differ diff --git a/media/img/icons/silk/tag.png b/media/img/icons/silk/tag.png new file mode 100755 index 000000000..e093032a7 Binary files /dev/null and b/media/img/icons/silk/tag.png differ diff --git a/media/img/icons/silk/tag_blue.png b/media/img/icons/silk/tag_blue.png new file mode 100755 index 000000000..9757fc6ed Binary files /dev/null and b/media/img/icons/silk/tag_blue.png differ diff --git a/media/img/icons/silk/tag_blue_add.png b/media/img/icons/silk/tag_blue_add.png new file mode 100755 index 000000000..f135248f8 Binary files /dev/null and b/media/img/icons/silk/tag_blue_add.png differ diff --git a/media/img/icons/silk/tag_blue_delete.png b/media/img/icons/silk/tag_blue_delete.png new file mode 100755 index 000000000..9fbae6725 Binary files /dev/null and b/media/img/icons/silk/tag_blue_delete.png differ diff --git a/media/img/icons/silk/tag_blue_edit.png b/media/img/icons/silk/tag_blue_edit.png new file mode 100755 index 000000000..2a9f6266e Binary files /dev/null and b/media/img/icons/silk/tag_blue_edit.png differ diff --git a/media/img/icons/silk/tag_green.png b/media/img/icons/silk/tag_green.png new file mode 100755 index 000000000..83ec984bd Binary files /dev/null and b/media/img/icons/silk/tag_green.png differ diff --git a/media/img/icons/silk/tag_orange.png b/media/img/icons/silk/tag_orange.png new file mode 100755 index 000000000..454a59f30 Binary files /dev/null and b/media/img/icons/silk/tag_orange.png differ diff --git a/media/img/icons/silk/tag_pink.png b/media/img/icons/silk/tag_pink.png new file mode 100755 index 000000000..76e2296cc Binary files /dev/null and b/media/img/icons/silk/tag_pink.png differ diff --git a/media/img/icons/silk/tag_purple.png b/media/img/icons/silk/tag_purple.png new file mode 100755 index 000000000..ebaf0e874 Binary files /dev/null and b/media/img/icons/silk/tag_purple.png differ diff --git a/media/img/icons/silk/tag_red.png b/media/img/icons/silk/tag_red.png new file mode 100755 index 000000000..6ebb37d25 Binary files /dev/null and b/media/img/icons/silk/tag_red.png differ diff --git a/media/img/icons/silk/tag_yellow.png b/media/img/icons/silk/tag_yellow.png new file mode 100755 index 000000000..83d12924f Binary files /dev/null and b/media/img/icons/silk/tag_yellow.png differ diff --git a/media/img/icons/silk/telephone.png b/media/img/icons/silk/telephone.png new file mode 100755 index 000000000..cecc436fb Binary files /dev/null and b/media/img/icons/silk/telephone.png differ diff --git a/media/img/icons/silk/telephone_add.png b/media/img/icons/silk/telephone_add.png new file mode 100755 index 000000000..5591cfc4a Binary files /dev/null and b/media/img/icons/silk/telephone_add.png differ diff --git a/media/img/icons/silk/telephone_delete.png b/media/img/icons/silk/telephone_delete.png new file mode 100755 index 000000000..0013268e9 Binary files /dev/null and b/media/img/icons/silk/telephone_delete.png differ diff --git a/media/img/icons/silk/telephone_edit.png b/media/img/icons/silk/telephone_edit.png new file mode 100755 index 000000000..bcf6d7ec1 Binary files /dev/null and b/media/img/icons/silk/telephone_edit.png differ diff --git a/media/img/icons/silk/telephone_error.png b/media/img/icons/silk/telephone_error.png new file mode 100755 index 000000000..d3ec3a110 Binary files /dev/null and b/media/img/icons/silk/telephone_error.png differ diff --git a/media/img/icons/silk/telephone_go.png b/media/img/icons/silk/telephone_go.png new file mode 100755 index 000000000..395c8fbf5 Binary files /dev/null and b/media/img/icons/silk/telephone_go.png differ diff --git a/media/img/icons/silk/telephone_key.png b/media/img/icons/silk/telephone_key.png new file mode 100755 index 000000000..cef5dec4e Binary files /dev/null and b/media/img/icons/silk/telephone_key.png differ diff --git a/media/img/icons/silk/telephone_link.png b/media/img/icons/silk/telephone_link.png new file mode 100755 index 000000000..ef1ee5dd7 Binary files /dev/null and b/media/img/icons/silk/telephone_link.png differ diff --git a/media/img/icons/silk/television.png b/media/img/icons/silk/television.png new file mode 100755 index 000000000..1738a4f10 Binary files /dev/null and b/media/img/icons/silk/television.png differ diff --git a/media/img/icons/silk/television_add.png b/media/img/icons/silk/television_add.png new file mode 100755 index 000000000..2baaad99e Binary files /dev/null and b/media/img/icons/silk/television_add.png differ diff --git a/media/img/icons/silk/television_delete.png b/media/img/icons/silk/television_delete.png new file mode 100755 index 000000000..b9a586025 Binary files /dev/null and b/media/img/icons/silk/television_delete.png differ diff --git a/media/img/icons/silk/text_align_center.png b/media/img/icons/silk/text_align_center.png new file mode 100755 index 000000000..57beb3813 Binary files /dev/null and b/media/img/icons/silk/text_align_center.png differ diff --git a/media/img/icons/silk/text_align_justify.png b/media/img/icons/silk/text_align_justify.png new file mode 100755 index 000000000..2fbdd6920 Binary files /dev/null and b/media/img/icons/silk/text_align_justify.png differ diff --git a/media/img/icons/silk/text_align_left.png b/media/img/icons/silk/text_align_left.png new file mode 100755 index 000000000..6c8fcc116 Binary files /dev/null and b/media/img/icons/silk/text_align_left.png differ diff --git a/media/img/icons/silk/text_align_right.png b/media/img/icons/silk/text_align_right.png new file mode 100755 index 000000000..a1502571c Binary files /dev/null and b/media/img/icons/silk/text_align_right.png differ diff --git a/media/img/icons/silk/text_allcaps.png b/media/img/icons/silk/text_allcaps.png new file mode 100755 index 000000000..280fd442b Binary files /dev/null and b/media/img/icons/silk/text_allcaps.png differ diff --git a/media/img/icons/silk/text_bold.png b/media/img/icons/silk/text_bold.png new file mode 100755 index 000000000..889ae80e3 Binary files /dev/null and b/media/img/icons/silk/text_bold.png differ diff --git a/media/img/icons/silk/text_columns.png b/media/img/icons/silk/text_columns.png new file mode 100755 index 000000000..97b2e0353 Binary files /dev/null and b/media/img/icons/silk/text_columns.png differ diff --git a/media/img/icons/silk/text_dropcaps.png b/media/img/icons/silk/text_dropcaps.png new file mode 100755 index 000000000..dd65786a7 Binary files /dev/null and b/media/img/icons/silk/text_dropcaps.png differ diff --git a/media/img/icons/silk/text_heading_1.png b/media/img/icons/silk/text_heading_1.png new file mode 100755 index 000000000..9c122e91e Binary files /dev/null and b/media/img/icons/silk/text_heading_1.png differ diff --git a/media/img/icons/silk/text_heading_2.png b/media/img/icons/silk/text_heading_2.png new file mode 100755 index 000000000..fbd87657f Binary files /dev/null and b/media/img/icons/silk/text_heading_2.png differ diff --git a/media/img/icons/silk/text_heading_3.png b/media/img/icons/silk/text_heading_3.png new file mode 100755 index 000000000..c7836cf09 Binary files /dev/null and b/media/img/icons/silk/text_heading_3.png differ diff --git a/media/img/icons/silk/text_heading_4.png b/media/img/icons/silk/text_heading_4.png new file mode 100755 index 000000000..4e929eaf5 Binary files /dev/null and b/media/img/icons/silk/text_heading_4.png differ diff --git a/media/img/icons/silk/text_heading_5.png b/media/img/icons/silk/text_heading_5.png new file mode 100755 index 000000000..30cabebf7 Binary files /dev/null and b/media/img/icons/silk/text_heading_5.png differ diff --git a/media/img/icons/silk/text_heading_6.png b/media/img/icons/silk/text_heading_6.png new file mode 100755 index 000000000..058170a20 Binary files /dev/null and b/media/img/icons/silk/text_heading_6.png differ diff --git a/media/img/icons/silk/text_horizontalrule.png b/media/img/icons/silk/text_horizontalrule.png new file mode 100755 index 000000000..8dd1da1c1 Binary files /dev/null and b/media/img/icons/silk/text_horizontalrule.png differ diff --git a/media/img/icons/silk/text_indent.png b/media/img/icons/silk/text_indent.png new file mode 100755 index 000000000..936453234 Binary files /dev/null and b/media/img/icons/silk/text_indent.png differ diff --git a/media/img/icons/silk/text_indent_remove.png b/media/img/icons/silk/text_indent_remove.png new file mode 100755 index 000000000..1651b074e Binary files /dev/null and b/media/img/icons/silk/text_indent_remove.png differ diff --git a/media/img/icons/silk/text_italic.png b/media/img/icons/silk/text_italic.png new file mode 100755 index 000000000..8482ac8cb Binary files /dev/null and b/media/img/icons/silk/text_italic.png differ diff --git a/media/img/icons/silk/text_kerning.png b/media/img/icons/silk/text_kerning.png new file mode 100755 index 000000000..377def645 Binary files /dev/null and b/media/img/icons/silk/text_kerning.png differ diff --git a/media/img/icons/silk/text_letter_omega.png b/media/img/icons/silk/text_letter_omega.png new file mode 100755 index 000000000..5075ec6b8 Binary files /dev/null and b/media/img/icons/silk/text_letter_omega.png differ diff --git a/media/img/icons/silk/text_letterspacing.png b/media/img/icons/silk/text_letterspacing.png new file mode 100755 index 000000000..41390f549 Binary files /dev/null and b/media/img/icons/silk/text_letterspacing.png differ diff --git a/media/img/icons/silk/text_linespacing.png b/media/img/icons/silk/text_linespacing.png new file mode 100755 index 000000000..1a91cbdd5 Binary files /dev/null and b/media/img/icons/silk/text_linespacing.png differ diff --git a/media/img/icons/silk/text_list_bullets.png b/media/img/icons/silk/text_list_bullets.png new file mode 100755 index 000000000..4a8672bde Binary files /dev/null and b/media/img/icons/silk/text_list_bullets.png differ diff --git a/media/img/icons/silk/text_list_numbers.png b/media/img/icons/silk/text_list_numbers.png new file mode 100755 index 000000000..33b0b8df3 Binary files /dev/null and b/media/img/icons/silk/text_list_numbers.png differ diff --git a/media/img/icons/silk/text_lowercase.png b/media/img/icons/silk/text_lowercase.png new file mode 100755 index 000000000..382a102e3 Binary files /dev/null and b/media/img/icons/silk/text_lowercase.png differ diff --git a/media/img/icons/silk/text_padding_bottom.png b/media/img/icons/silk/text_padding_bottom.png new file mode 100755 index 000000000..4880c43a1 Binary files /dev/null and b/media/img/icons/silk/text_padding_bottom.png differ diff --git a/media/img/icons/silk/text_padding_left.png b/media/img/icons/silk/text_padding_left.png new file mode 100755 index 000000000..b55482eee Binary files /dev/null and b/media/img/icons/silk/text_padding_left.png differ diff --git a/media/img/icons/silk/text_padding_right.png b/media/img/icons/silk/text_padding_right.png new file mode 100755 index 000000000..106edae52 Binary files /dev/null and b/media/img/icons/silk/text_padding_right.png differ diff --git a/media/img/icons/silk/text_padding_top.png b/media/img/icons/silk/text_padding_top.png new file mode 100755 index 000000000..c5c45b2d6 Binary files /dev/null and b/media/img/icons/silk/text_padding_top.png differ diff --git a/media/img/icons/silk/text_replace.png b/media/img/icons/silk/text_replace.png new file mode 100755 index 000000000..877f82fea Binary files /dev/null and b/media/img/icons/silk/text_replace.png differ diff --git a/media/img/icons/silk/text_signature.png b/media/img/icons/silk/text_signature.png new file mode 100755 index 000000000..c72fd8088 Binary files /dev/null and b/media/img/icons/silk/text_signature.png differ diff --git a/media/img/icons/silk/text_smallcaps.png b/media/img/icons/silk/text_smallcaps.png new file mode 100755 index 000000000..5b98a6e13 Binary files /dev/null and b/media/img/icons/silk/text_smallcaps.png differ diff --git a/media/img/icons/silk/text_strikethrough.png b/media/img/icons/silk/text_strikethrough.png new file mode 100755 index 000000000..612058a78 Binary files /dev/null and b/media/img/icons/silk/text_strikethrough.png differ diff --git a/media/img/icons/silk/text_subscript.png b/media/img/icons/silk/text_subscript.png new file mode 100755 index 000000000..1a2b01017 Binary files /dev/null and b/media/img/icons/silk/text_subscript.png differ diff --git a/media/img/icons/silk/text_superscript.png b/media/img/icons/silk/text_superscript.png new file mode 100755 index 000000000..2fb2a7c74 Binary files /dev/null and b/media/img/icons/silk/text_superscript.png differ diff --git a/media/img/icons/silk/text_underline.png b/media/img/icons/silk/text_underline.png new file mode 100755 index 000000000..90d0df286 Binary files /dev/null and b/media/img/icons/silk/text_underline.png differ diff --git a/media/img/icons/silk/text_uppercase.png b/media/img/icons/silk/text_uppercase.png new file mode 100755 index 000000000..8dcc2dbbb Binary files /dev/null and b/media/img/icons/silk/text_uppercase.png differ diff --git a/media/img/icons/silk/textfield.png b/media/img/icons/silk/textfield.png new file mode 100755 index 000000000..d37e7304e Binary files /dev/null and b/media/img/icons/silk/textfield.png differ diff --git a/media/img/icons/silk/textfield_add.png b/media/img/icons/silk/textfield_add.png new file mode 100755 index 000000000..204de7231 Binary files /dev/null and b/media/img/icons/silk/textfield_add.png differ diff --git a/media/img/icons/silk/textfield_delete.png b/media/img/icons/silk/textfield_delete.png new file mode 100755 index 000000000..c7bd58b21 Binary files /dev/null and b/media/img/icons/silk/textfield_delete.png differ diff --git a/media/img/icons/silk/textfield_key.png b/media/img/icons/silk/textfield_key.png new file mode 100755 index 000000000..a9d5e4f8c Binary files /dev/null and b/media/img/icons/silk/textfield_key.png differ diff --git a/media/img/icons/silk/textfield_rename.png b/media/img/icons/silk/textfield_rename.png new file mode 100755 index 000000000..4e3688edc Binary files /dev/null and b/media/img/icons/silk/textfield_rename.png differ diff --git a/media/img/icons/silk/thumb_down.png b/media/img/icons/silk/thumb_down.png new file mode 100755 index 000000000..3c832d4c8 Binary files /dev/null and b/media/img/icons/silk/thumb_down.png differ diff --git a/media/img/icons/silk/thumb_up.png b/media/img/icons/silk/thumb_up.png new file mode 100755 index 000000000..2bd16ccf2 Binary files /dev/null and b/media/img/icons/silk/thumb_up.png differ diff --git a/media/img/icons/silk/tick.png b/media/img/icons/silk/tick.png new file mode 100755 index 000000000..a9925a06a Binary files /dev/null and b/media/img/icons/silk/tick.png differ diff --git a/media/img/icons/silk/time.png b/media/img/icons/silk/time.png new file mode 100755 index 000000000..911da3f1d Binary files /dev/null and b/media/img/icons/silk/time.png differ diff --git a/media/img/icons/silk/time_add.png b/media/img/icons/silk/time_add.png new file mode 100755 index 000000000..dcc45cb22 Binary files /dev/null and b/media/img/icons/silk/time_add.png differ diff --git a/media/img/icons/silk/time_delete.png b/media/img/icons/silk/time_delete.png new file mode 100755 index 000000000..5bf8313c6 Binary files /dev/null and b/media/img/icons/silk/time_delete.png differ diff --git a/media/img/icons/silk/time_go.png b/media/img/icons/silk/time_go.png new file mode 100755 index 000000000..d451ee061 Binary files /dev/null and b/media/img/icons/silk/time_go.png differ diff --git a/media/img/icons/silk/timeline_marker.png b/media/img/icons/silk/timeline_marker.png new file mode 100755 index 000000000..a3fbddf88 Binary files /dev/null and b/media/img/icons/silk/timeline_marker.png differ diff --git a/media/img/icons/silk/transmit.png b/media/img/icons/silk/transmit.png new file mode 100755 index 000000000..f54bf736c Binary files /dev/null and b/media/img/icons/silk/transmit.png differ diff --git a/media/img/icons/silk/transmit_add.png b/media/img/icons/silk/transmit_add.png new file mode 100755 index 000000000..b7fd4e685 Binary files /dev/null and b/media/img/icons/silk/transmit_add.png differ diff --git a/media/img/icons/silk/transmit_blue.png b/media/img/icons/silk/transmit_blue.png new file mode 100755 index 000000000..7b1142fc7 Binary files /dev/null and b/media/img/icons/silk/transmit_blue.png differ diff --git a/media/img/icons/silk/transmit_delete.png b/media/img/icons/silk/transmit_delete.png new file mode 100755 index 000000000..3d72be2a3 Binary files /dev/null and b/media/img/icons/silk/transmit_delete.png differ diff --git a/media/img/icons/silk/transmit_edit.png b/media/img/icons/silk/transmit_edit.png new file mode 100755 index 000000000..eb9a3dd59 Binary files /dev/null and b/media/img/icons/silk/transmit_edit.png differ diff --git a/media/img/icons/silk/transmit_error.png b/media/img/icons/silk/transmit_error.png new file mode 100755 index 000000000..fd1d4499a Binary files /dev/null and b/media/img/icons/silk/transmit_error.png differ diff --git a/media/img/icons/silk/transmit_go.png b/media/img/icons/silk/transmit_go.png new file mode 100755 index 000000000..10137e55c Binary files /dev/null and b/media/img/icons/silk/transmit_go.png differ diff --git a/media/img/icons/silk/tux.png b/media/img/icons/silk/tux.png new file mode 100755 index 000000000..bbefe2ec4 Binary files /dev/null and b/media/img/icons/silk/tux.png differ diff --git a/media/img/icons/silk/user.png b/media/img/icons/silk/user.png new file mode 100755 index 000000000..79f35ccbd Binary files /dev/null and b/media/img/icons/silk/user.png differ diff --git a/media/img/icons/silk/user_add.png b/media/img/icons/silk/user_add.png new file mode 100755 index 000000000..deae99bcf Binary files /dev/null and b/media/img/icons/silk/user_add.png differ diff --git a/media/img/icons/silk/user_comment.png b/media/img/icons/silk/user_comment.png new file mode 100755 index 000000000..e54ebebaf Binary files /dev/null and b/media/img/icons/silk/user_comment.png differ diff --git a/media/img/icons/silk/user_delete.png b/media/img/icons/silk/user_delete.png new file mode 100755 index 000000000..acbb5630e Binary files /dev/null and b/media/img/icons/silk/user_delete.png differ diff --git a/media/img/icons/silk/user_edit.png b/media/img/icons/silk/user_edit.png new file mode 100755 index 000000000..c1974cda7 Binary files /dev/null and b/media/img/icons/silk/user_edit.png differ diff --git a/media/img/icons/silk/user_female.png b/media/img/icons/silk/user_female.png new file mode 100755 index 000000000..7c71de03b Binary files /dev/null and b/media/img/icons/silk/user_female.png differ diff --git a/media/img/icons/silk/user_go.png b/media/img/icons/silk/user_go.png new file mode 100755 index 000000000..0468cf08f Binary files /dev/null and b/media/img/icons/silk/user_go.png differ diff --git a/media/img/icons/silk/user_gray.png b/media/img/icons/silk/user_gray.png new file mode 100755 index 000000000..8fd539e9c Binary files /dev/null and b/media/img/icons/silk/user_gray.png differ diff --git a/media/img/icons/silk/user_green.png b/media/img/icons/silk/user_green.png new file mode 100755 index 000000000..30383c2de Binary files /dev/null and b/media/img/icons/silk/user_green.png differ diff --git a/media/img/icons/silk/user_orange.png b/media/img/icons/silk/user_orange.png new file mode 100755 index 000000000..b818127df Binary files /dev/null and b/media/img/icons/silk/user_orange.png differ diff --git a/media/img/icons/silk/user_red.png b/media/img/icons/silk/user_red.png new file mode 100755 index 000000000..c6f66e8b3 Binary files /dev/null and b/media/img/icons/silk/user_red.png differ diff --git a/media/img/icons/silk/user_suit.png b/media/img/icons/silk/user_suit.png new file mode 100755 index 000000000..b3454e15f Binary files /dev/null and b/media/img/icons/silk/user_suit.png differ diff --git a/media/img/icons/silk/vcard.png b/media/img/icons/silk/vcard.png new file mode 100755 index 000000000..c02f315d2 Binary files /dev/null and b/media/img/icons/silk/vcard.png differ diff --git a/media/img/icons/silk/vcard_add.png b/media/img/icons/silk/vcard_add.png new file mode 100755 index 000000000..2a6845381 Binary files /dev/null and b/media/img/icons/silk/vcard_add.png differ diff --git a/media/img/icons/silk/vcard_delete.png b/media/img/icons/silk/vcard_delete.png new file mode 100755 index 000000000..b194b971b Binary files /dev/null and b/media/img/icons/silk/vcard_delete.png differ diff --git a/media/img/icons/silk/vcard_edit.png b/media/img/icons/silk/vcard_edit.png new file mode 100755 index 000000000..ab0f6e73d Binary files /dev/null and b/media/img/icons/silk/vcard_edit.png differ diff --git a/media/img/icons/silk/vector.png b/media/img/icons/silk/vector.png new file mode 100755 index 000000000..a1291c2df Binary files /dev/null and b/media/img/icons/silk/vector.png differ diff --git a/media/img/icons/silk/vector_add.png b/media/img/icons/silk/vector_add.png new file mode 100755 index 000000000..988770f40 Binary files /dev/null and b/media/img/icons/silk/vector_add.png differ diff --git a/media/img/icons/silk/vector_delete.png b/media/img/icons/silk/vector_delete.png new file mode 100755 index 000000000..ca139e0f3 Binary files /dev/null and b/media/img/icons/silk/vector_delete.png differ diff --git a/media/img/icons/silk/wand.png b/media/img/icons/silk/wand.png new file mode 100755 index 000000000..44ccbf812 Binary files /dev/null and b/media/img/icons/silk/wand.png differ diff --git a/media/img/icons/silk/weather_clouds.png b/media/img/icons/silk/weather_clouds.png new file mode 100755 index 000000000..3f73eaa14 Binary files /dev/null and b/media/img/icons/silk/weather_clouds.png differ diff --git a/media/img/icons/silk/weather_cloudy.png b/media/img/icons/silk/weather_cloudy.png new file mode 100755 index 000000000..5856e1d05 Binary files /dev/null and b/media/img/icons/silk/weather_cloudy.png differ diff --git a/media/img/icons/silk/weather_lightning.png b/media/img/icons/silk/weather_lightning.png new file mode 100755 index 000000000..1d42b3673 Binary files /dev/null and b/media/img/icons/silk/weather_lightning.png differ diff --git a/media/img/icons/silk/weather_rain.png b/media/img/icons/silk/weather_rain.png new file mode 100755 index 000000000..cb3d54d06 Binary files /dev/null and b/media/img/icons/silk/weather_rain.png differ diff --git a/media/img/icons/silk/weather_snow.png b/media/img/icons/silk/weather_snow.png new file mode 100755 index 000000000..45bbdf19c Binary files /dev/null and b/media/img/icons/silk/weather_snow.png differ diff --git a/media/img/icons/silk/weather_sun.png b/media/img/icons/silk/weather_sun.png new file mode 100755 index 000000000..0156c266e Binary files /dev/null and b/media/img/icons/silk/weather_sun.png differ diff --git a/media/img/icons/silk/webcam.png b/media/img/icons/silk/webcam.png new file mode 100755 index 000000000..af71c3061 Binary files /dev/null and b/media/img/icons/silk/webcam.png differ diff --git a/media/img/icons/silk/webcam_add.png b/media/img/icons/silk/webcam_add.png new file mode 100755 index 000000000..f02fcfa99 Binary files /dev/null and b/media/img/icons/silk/webcam_add.png differ diff --git a/media/img/icons/silk/webcam_delete.png b/media/img/icons/silk/webcam_delete.png new file mode 100755 index 000000000..bd6277f51 Binary files /dev/null and b/media/img/icons/silk/webcam_delete.png differ diff --git a/media/img/icons/silk/webcam_error.png b/media/img/icons/silk/webcam_error.png new file mode 100755 index 000000000..2faa70679 Binary files /dev/null and b/media/img/icons/silk/webcam_error.png differ diff --git a/media/img/icons/silk/world.png b/media/img/icons/silk/world.png new file mode 100755 index 000000000..68f21d301 Binary files /dev/null and b/media/img/icons/silk/world.png differ diff --git a/media/img/icons/silk/world_add.png b/media/img/icons/silk/world_add.png new file mode 100755 index 000000000..6d0d7f74c Binary files /dev/null and b/media/img/icons/silk/world_add.png differ diff --git a/media/img/icons/silk/world_delete.png b/media/img/icons/silk/world_delete.png new file mode 100755 index 000000000..ffcd1156f Binary files /dev/null and b/media/img/icons/silk/world_delete.png differ diff --git a/media/img/icons/silk/world_edit.png b/media/img/icons/silk/world_edit.png new file mode 100755 index 000000000..00794d408 Binary files /dev/null and b/media/img/icons/silk/world_edit.png differ diff --git a/media/img/icons/silk/world_go.png b/media/img/icons/silk/world_go.png new file mode 100755 index 000000000..aee9c97f8 Binary files /dev/null and b/media/img/icons/silk/world_go.png differ diff --git a/media/img/icons/silk/world_link.png b/media/img/icons/silk/world_link.png new file mode 100755 index 000000000..b8edc1265 Binary files /dev/null and b/media/img/icons/silk/world_link.png differ diff --git a/media/img/icons/silk/wrench.png b/media/img/icons/silk/wrench.png new file mode 100755 index 000000000..5c8213fef Binary files /dev/null and b/media/img/icons/silk/wrench.png differ diff --git a/media/img/icons/silk/wrench_orange.png b/media/img/icons/silk/wrench_orange.png new file mode 100755 index 000000000..565a9330e Binary files /dev/null and b/media/img/icons/silk/wrench_orange.png differ diff --git a/media/img/icons/silk/xhtml.png b/media/img/icons/silk/xhtml.png new file mode 100755 index 000000000..da5dbf200 Binary files /dev/null and b/media/img/icons/silk/xhtml.png differ diff --git a/media/img/icons/silk/xhtml_add.png b/media/img/icons/silk/xhtml_add.png new file mode 100755 index 000000000..bbaf784f2 Binary files /dev/null and b/media/img/icons/silk/xhtml_add.png differ diff --git a/media/img/icons/silk/xhtml_delete.png b/media/img/icons/silk/xhtml_delete.png new file mode 100755 index 000000000..157b5201d Binary files /dev/null and b/media/img/icons/silk/xhtml_delete.png differ diff --git a/media/img/icons/silk/xhtml_go.png b/media/img/icons/silk/xhtml_go.png new file mode 100755 index 000000000..43cf81448 Binary files /dev/null and b/media/img/icons/silk/xhtml_go.png differ diff --git a/media/img/icons/silk/xhtml_valid.png b/media/img/icons/silk/xhtml_valid.png new file mode 100755 index 000000000..d2e1cfbe3 Binary files /dev/null and b/media/img/icons/silk/xhtml_valid.png differ diff --git a/media/img/icons/silk/zoom.png b/media/img/icons/silk/zoom.png new file mode 100755 index 000000000..908612e39 Binary files /dev/null and b/media/img/icons/silk/zoom.png differ diff --git a/media/img/icons/silk/zoom_in.png b/media/img/icons/silk/zoom_in.png new file mode 100755 index 000000000..cdf0a52fe Binary files /dev/null and b/media/img/icons/silk/zoom_in.png differ diff --git a/media/img/icons/silk/zoom_out.png b/media/img/icons/silk/zoom_out.png new file mode 100755 index 000000000..07bf98a79 Binary files /dev/null and b/media/img/icons/silk/zoom_out.png differ diff --git a/media/img/icons/things/bars.gif b/media/img/icons/things/bars.gif new file mode 100755 index 000000000..b77242177 Binary files /dev/null and b/media/img/icons/things/bars.gif differ diff --git a/media/img/icons/things/bars.png b/media/img/icons/things/bars.png new file mode 100755 index 000000000..96204e509 Binary files /dev/null and b/media/img/icons/things/bars.png differ diff --git a/media/img/icons/things/bomb.gif b/media/img/icons/things/bomb.gif new file mode 100755 index 000000000..40c03831b Binary files /dev/null and b/media/img/icons/things/bomb.gif differ diff --git a/media/img/icons/things/bomb.png b/media/img/icons/things/bomb.png new file mode 100755 index 000000000..3c3e07883 Binary files /dev/null and b/media/img/icons/things/bomb.png differ diff --git a/media/img/icons/things/briefcase.gif b/media/img/icons/things/briefcase.gif new file mode 100755 index 000000000..3f0266ea9 Binary files /dev/null and b/media/img/icons/things/briefcase.gif differ diff --git a/media/img/icons/things/briefcase.png b/media/img/icons/things/briefcase.png new file mode 100755 index 000000000..849d8815f Binary files /dev/null and b/media/img/icons/things/briefcase.png differ diff --git a/media/img/icons/things/calendar.gif b/media/img/icons/things/calendar.gif new file mode 100755 index 000000000..f7affd903 Binary files /dev/null and b/media/img/icons/things/calendar.gif differ diff --git a/media/img/icons/things/calendar.png b/media/img/icons/things/calendar.png new file mode 100755 index 000000000..2076a9d2d Binary files /dev/null and b/media/img/icons/things/calendar.png differ diff --git a/media/img/icons/things/cart.gif b/media/img/icons/things/cart.gif new file mode 100755 index 000000000..ea5b2362e Binary files /dev/null and b/media/img/icons/things/cart.gif differ diff --git a/media/img/icons/things/cart.png b/media/img/icons/things/cart.png new file mode 100755 index 000000000..d77ef1c73 Binary files /dev/null and b/media/img/icons/things/cart.png differ diff --git a/media/img/icons/things/caution.gif b/media/img/icons/things/caution.gif new file mode 100755 index 000000000..7b9e4a0e2 Binary files /dev/null and b/media/img/icons/things/caution.gif differ diff --git a/media/img/icons/things/caution.png b/media/img/icons/things/caution.png new file mode 100755 index 000000000..39f6c3a7f Binary files /dev/null and b/media/img/icons/things/caution.png differ diff --git a/media/img/icons/things/clock.gif b/media/img/icons/things/clock.gif new file mode 100755 index 000000000..e9869d82e Binary files /dev/null and b/media/img/icons/things/clock.gif differ diff --git a/media/img/icons/things/clock.png b/media/img/icons/things/clock.png new file mode 100755 index 000000000..f4f6da262 Binary files /dev/null and b/media/img/icons/things/clock.png differ diff --git a/media/img/icons/things/comment.gif b/media/img/icons/things/comment.gif new file mode 100755 index 000000000..6ee62f795 Binary files /dev/null and b/media/img/icons/things/comment.gif differ diff --git a/media/img/icons/things/comment.png b/media/img/icons/things/comment.png new file mode 100755 index 000000000..78f4d0cd4 Binary files /dev/null and b/media/img/icons/things/comment.png differ diff --git a/media/img/icons/things/detour.gif b/media/img/icons/things/detour.gif new file mode 100755 index 000000000..4463d79ae Binary files /dev/null and b/media/img/icons/things/detour.gif differ diff --git a/media/img/icons/things/detour.png b/media/img/icons/things/detour.png new file mode 100755 index 000000000..f082e1e60 Binary files /dev/null and b/media/img/icons/things/detour.png differ diff --git a/media/img/icons/things/document-new.gif b/media/img/icons/things/document-new.gif new file mode 100755 index 000000000..9611e18bf Binary files /dev/null and b/media/img/icons/things/document-new.gif differ diff --git a/media/img/icons/things/document-new.png b/media/img/icons/things/document-new.png new file mode 100755 index 000000000..aec66924b Binary files /dev/null and b/media/img/icons/things/document-new.png differ diff --git a/media/img/icons/things/document-txt-new.gif b/media/img/icons/things/document-txt-new.gif new file mode 100755 index 000000000..19c2c9e28 Binary files /dev/null and b/media/img/icons/things/document-txt-new.gif differ diff --git a/media/img/icons/things/document-txt-new.png b/media/img/icons/things/document-txt-new.png new file mode 100755 index 000000000..228373573 Binary files /dev/null and b/media/img/icons/things/document-txt-new.png differ diff --git a/media/img/icons/things/document-txt.gif b/media/img/icons/things/document-txt.gif new file mode 100755 index 000000000..d71bf8aa8 Binary files /dev/null and b/media/img/icons/things/document-txt.gif differ diff --git a/media/img/icons/things/document-txt.png b/media/img/icons/things/document-txt.png new file mode 100755 index 000000000..23d8f6a15 Binary files /dev/null and b/media/img/icons/things/document-txt.png differ diff --git a/media/img/icons/things/document.gif b/media/img/icons/things/document.gif new file mode 100755 index 000000000..605b37c00 Binary files /dev/null and b/media/img/icons/things/document.gif differ diff --git a/media/img/icons/things/document.png b/media/img/icons/things/document.png new file mode 100755 index 000000000..6d5f24ceb Binary files /dev/null and b/media/img/icons/things/document.png differ diff --git a/media/img/icons/things/documents.gif b/media/img/icons/things/documents.gif new file mode 100755 index 000000000..de2cff84e Binary files /dev/null and b/media/img/icons/things/documents.gif differ diff --git a/media/img/icons/things/documents.png b/media/img/icons/things/documents.png new file mode 100755 index 000000000..914674000 Binary files /dev/null and b/media/img/icons/things/documents.png differ diff --git a/media/img/icons/things/folder.gif b/media/img/icons/things/folder.gif new file mode 100755 index 000000000..53bc8aebb Binary files /dev/null and b/media/img/icons/things/folder.gif differ diff --git a/media/img/icons/things/folder.png b/media/img/icons/things/folder.png new file mode 100755 index 000000000..c3a2ad5e3 Binary files /dev/null and b/media/img/icons/things/folder.png differ diff --git a/media/img/icons/things/gear.gif b/media/img/icons/things/gear.gif new file mode 100755 index 000000000..e1c8b03d3 Binary files /dev/null and b/media/img/icons/things/gear.gif differ diff --git a/media/img/icons/things/gear.png b/media/img/icons/things/gear.png new file mode 100755 index 000000000..9829f209a Binary files /dev/null and b/media/img/icons/things/gear.png differ diff --git a/media/img/icons/things/heart.gif b/media/img/icons/things/heart.gif new file mode 100755 index 000000000..3517b1539 Binary files /dev/null and b/media/img/icons/things/heart.gif differ diff --git a/media/img/icons/things/heart.png b/media/img/icons/things/heart.png new file mode 100755 index 000000000..17cf06d3d Binary files /dev/null and b/media/img/icons/things/heart.png differ diff --git a/media/img/icons/things/house.gif b/media/img/icons/things/house.gif new file mode 100755 index 000000000..1245f5c89 Binary files /dev/null and b/media/img/icons/things/house.gif differ diff --git a/media/img/icons/things/house.png b/media/img/icons/things/house.png new file mode 100755 index 000000000..7e30a532f Binary files /dev/null and b/media/img/icons/things/house.png differ diff --git a/media/img/icons/things/icons.gif b/media/img/icons/things/icons.gif new file mode 100755 index 000000000..97d8ca369 Binary files /dev/null and b/media/img/icons/things/icons.gif differ diff --git a/media/img/icons/things/icons.png b/media/img/icons/things/icons.png new file mode 100755 index 000000000..1d9d8c31e Binary files /dev/null and b/media/img/icons/things/icons.png differ diff --git a/media/img/icons/things/key-yellow.gif b/media/img/icons/things/key-yellow.gif new file mode 100755 index 000000000..772f315e8 Binary files /dev/null and b/media/img/icons/things/key-yellow.gif differ diff --git a/media/img/icons/things/key-yellow.png b/media/img/icons/things/key-yellow.png new file mode 100755 index 000000000..bfb1ec80b Binary files /dev/null and b/media/img/icons/things/key-yellow.png differ diff --git a/media/img/icons/things/lightbulb.gif b/media/img/icons/things/lightbulb.gif new file mode 100755 index 000000000..e9cf05462 Binary files /dev/null and b/media/img/icons/things/lightbulb.gif differ diff --git a/media/img/icons/things/lightbulb.png b/media/img/icons/things/lightbulb.png new file mode 100755 index 000000000..a8f9fafe0 Binary files /dev/null and b/media/img/icons/things/lightbulb.png differ diff --git a/media/img/icons/things/mail.gif b/media/img/icons/things/mail.gif new file mode 100755 index 000000000..b5cb60820 Binary files /dev/null and b/media/img/icons/things/mail.gif differ diff --git a/media/img/icons/things/mail.png b/media/img/icons/things/mail.png new file mode 100755 index 000000000..3ef195853 Binary files /dev/null and b/media/img/icons/things/mail.png differ diff --git a/media/img/icons/things/musicnote.gif b/media/img/icons/things/musicnote.gif new file mode 100755 index 000000000..b35f5e7b0 Binary files /dev/null and b/media/img/icons/things/musicnote.gif differ diff --git a/media/img/icons/things/musicnote.png b/media/img/icons/things/musicnote.png new file mode 100755 index 000000000..f5161be61 Binary files /dev/null and b/media/img/icons/things/musicnote.png differ diff --git a/media/img/icons/things/people.gif b/media/img/icons/things/people.gif new file mode 100755 index 000000000..9e53180b0 Binary files /dev/null and b/media/img/icons/things/people.gif differ diff --git a/media/img/icons/things/people.png b/media/img/icons/things/people.png new file mode 100755 index 000000000..4bced2c94 Binary files /dev/null and b/media/img/icons/things/people.png differ diff --git a/media/img/icons/things/person.gif b/media/img/icons/things/person.gif new file mode 100755 index 000000000..14632c204 Binary files /dev/null and b/media/img/icons/things/person.gif differ diff --git a/media/img/icons/things/person.png b/media/img/icons/things/person.png new file mode 100755 index 000000000..55686618f Binary files /dev/null and b/media/img/icons/things/person.png differ diff --git a/media/img/icons/things/picture.gif b/media/img/icons/things/picture.gif new file mode 100755 index 000000000..6bae7b7a9 Binary files /dev/null and b/media/img/icons/things/picture.gif differ diff --git a/media/img/icons/things/picture.png b/media/img/icons/things/picture.png new file mode 100755 index 000000000..71071c223 Binary files /dev/null and b/media/img/icons/things/picture.png differ diff --git a/media/img/icons/things/polaroid.gif b/media/img/icons/things/polaroid.gif new file mode 100755 index 000000000..506728eb6 Binary files /dev/null and b/media/img/icons/things/polaroid.gif differ diff --git a/media/img/icons/things/polaroid.png b/media/img/icons/things/polaroid.png new file mode 100755 index 000000000..83d6c25a3 Binary files /dev/null and b/media/img/icons/things/polaroid.png differ diff --git a/media/img/icons/things/popcorn.gif b/media/img/icons/things/popcorn.gif new file mode 100755 index 000000000..ade3c2bdd Binary files /dev/null and b/media/img/icons/things/popcorn.gif differ diff --git a/media/img/icons/things/popcorn.png b/media/img/icons/things/popcorn.png new file mode 100755 index 000000000..391aab402 Binary files /dev/null and b/media/img/icons/things/popcorn.png differ diff --git a/media/img/icons/things/rss.gif b/media/img/icons/things/rss.gif new file mode 100755 index 000000000..4bc5926b4 Binary files /dev/null and b/media/img/icons/things/rss.gif differ diff --git a/media/img/icons/things/rss.png b/media/img/icons/things/rss.png new file mode 100755 index 000000000..a22901661 Binary files /dev/null and b/media/img/icons/things/rss.png differ diff --git a/media/img/icons/things/write.gif b/media/img/icons/things/write.gif new file mode 100755 index 000000000..790d92704 Binary files /dev/null and b/media/img/icons/things/write.gif differ diff --git a/media/img/icons/things/write.png b/media/img/icons/things/write.png new file mode 100755 index 000000000..5cc8d65a9 Binary files /dev/null and b/media/img/icons/things/write.png differ diff --git a/media/img/icons/weather/clouded.gif b/media/img/icons/weather/clouded.gif new file mode 100755 index 000000000..08061dfa3 Binary files /dev/null and b/media/img/icons/weather/clouded.gif differ diff --git a/media/img/icons/weather/clouded.png b/media/img/icons/weather/clouded.png new file mode 100755 index 000000000..3e9b76c0f Binary files /dev/null and b/media/img/icons/weather/clouded.png differ diff --git a/media/img/icons/weather/cloudy.gif b/media/img/icons/weather/cloudy.gif new file mode 100755 index 000000000..641755bf2 Binary files /dev/null and b/media/img/icons/weather/cloudy.gif differ diff --git a/media/img/icons/weather/cloudy.png b/media/img/icons/weather/cloudy.png new file mode 100755 index 000000000..2a95cbbe0 Binary files /dev/null and b/media/img/icons/weather/cloudy.png differ diff --git a/media/img/icons/weather/extreme-weather.gif b/media/img/icons/weather/extreme-weather.gif new file mode 100755 index 000000000..515140941 Binary files /dev/null and b/media/img/icons/weather/extreme-weather.gif differ diff --git a/media/img/icons/weather/extreme-weather.png b/media/img/icons/weather/extreme-weather.png new file mode 100755 index 000000000..5b7ff1fe0 Binary files /dev/null and b/media/img/icons/weather/extreme-weather.png differ diff --git a/media/img/icons/weather/lightning.gif b/media/img/icons/weather/lightning.gif new file mode 100755 index 000000000..36b8e4526 Binary files /dev/null and b/media/img/icons/weather/lightning.gif differ diff --git a/media/img/icons/weather/lightning.png b/media/img/icons/weather/lightning.png new file mode 100755 index 000000000..58556ef4f Binary files /dev/null and b/media/img/icons/weather/lightning.png differ diff --git a/media/img/icons/weather/raining.gif b/media/img/icons/weather/raining.gif new file mode 100755 index 000000000..d3a0b11e0 Binary files /dev/null and b/media/img/icons/weather/raining.gif differ diff --git a/media/img/icons/weather/raining.png b/media/img/icons/weather/raining.png new file mode 100755 index 000000000..d8c066f6a Binary files /dev/null and b/media/img/icons/weather/raining.png differ diff --git a/media/img/icons/weather/star.gif b/media/img/icons/weather/star.gif new file mode 100755 index 000000000..dda29b18d Binary files /dev/null and b/media/img/icons/weather/star.gif differ diff --git a/media/img/icons/weather/star.png b/media/img/icons/weather/star.png new file mode 100755 index 000000000..6b94cc7d7 Binary files /dev/null and b/media/img/icons/weather/star.png differ diff --git a/media/img/icons/weather/stormy.gif b/media/img/icons/weather/stormy.gif new file mode 100755 index 000000000..3931d49f9 Binary files /dev/null and b/media/img/icons/weather/stormy.gif differ diff --git a/media/img/icons/weather/stormy.png b/media/img/icons/weather/stormy.png new file mode 100755 index 000000000..52e37a95c Binary files /dev/null and b/media/img/icons/weather/stormy.png differ diff --git a/media/img/icons/weather/sunny.gif b/media/img/icons/weather/sunny.gif new file mode 100755 index 000000000..d342295e0 Binary files /dev/null and b/media/img/icons/weather/sunny.gif differ diff --git a/media/img/icons/weather/sunny.png b/media/img/icons/weather/sunny.png new file mode 100755 index 000000000..35daea892 Binary files /dev/null and b/media/img/icons/weather/sunny.png differ diff --git a/media/img/reader/resize_north.png b/media/img/reader/resize_north.png new file mode 100644 index 000000000..b266fa4bb Binary files /dev/null and b/media/img/reader/resize_north.png differ diff --git a/media/img/reader/resize_west.png b/media/img/reader/resize_west.png new file mode 100644 index 000000000..3ee0f0a0a Binary files /dev/null and b/media/img/reader/resize_west.png differ diff --git a/media/img/reader/resize_west_small.png b/media/img/reader/resize_west_small.png new file mode 100644 index 000000000..0b0c02d4d Binary files /dev/null and b/media/img/reader/resize_west_small.png differ diff --git a/media/img/reader/taskbar_background.png b/media/img/reader/taskbar_background.png new file mode 100644 index 000000000..1ef714714 Binary files /dev/null and b/media/img/reader/taskbar_background.png differ diff --git a/media/img/reader/user.png b/media/img/reader/user.png new file mode 100644 index 000000000..93b1805ad Binary files /dev/null and b/media/img/reader/user.png differ diff --git a/media/js/facebox/README.txt b/media/js/facebox/README.txt new file mode 100644 index 000000000..d4fc2d5e8 --- /dev/null +++ b/media/js/facebox/README.txt @@ -0,0 +1,4 @@ +Please visit http://famspam.com/facebox/ or open index.html in your favorite browser. + +Need help? Join our Google Groups mailing list: + http://groups.google.com/group/facebox/ diff --git a/media/js/facebox/b.png b/media/js/facebox/b.png new file mode 100644 index 000000000..f184e6269 Binary files /dev/null and b/media/js/facebox/b.png differ diff --git a/media/js/facebox/bl.png b/media/js/facebox/bl.png new file mode 100644 index 000000000..f6271859d Binary files /dev/null and b/media/js/facebox/bl.png differ diff --git a/media/js/facebox/br.png b/media/js/facebox/br.png new file mode 100644 index 000000000..31f204fc4 Binary files /dev/null and b/media/js/facebox/br.png differ diff --git a/media/js/facebox/closelabel.gif b/media/js/facebox/closelabel.gif new file mode 100755 index 000000000..87b4f8bd6 Binary files /dev/null and b/media/js/facebox/closelabel.gif differ diff --git a/media/js/facebox/facebox.css b/media/js/facebox/facebox.css new file mode 100644 index 000000000..230664f9f --- /dev/null +++ b/media/js/facebox/facebox.css @@ -0,0 +1,95 @@ +#facebox .b { + background:url(/media/js/facebox/b.png); +} + +#facebox .tl { + background:url(/media/js/facebox/tl.png); +} + +#facebox .tr { + background:url(/media/js/facebox/tr.png); +} + +#facebox .bl { + background:url(/media/js/facebox/bl.png); +} + +#facebox .br { + background:url(/media/js/facebox/br.png); +} + +#facebox { + position: absolute; + top: 0; + left: 0; + z-index: 100; + text-align: left; +} + +#facebox .popup { + position: relative; +} + +#facebox table { + border-collapse: collapse; +} + +#facebox td { + border-bottom: 0; + padding: 0; +} + +#facebox .body { + padding: 10px; + background: #fff; + width: 370px; +} + +#facebox .loading { + text-align: center; +} + +#facebox .image { + text-align: center; +} + +#facebox img { + border: 0; + margin: 0; +} + +#facebox .footer { + border-top: 1px solid #DDDDDD; + padding-top: 5px; + margin-top: 10px; + text-align: right; +} + +#facebox .tl, #facebox .tr, #facebox .bl, #facebox .br { + height: 10px; + width: 10px; + overflow: hidden; + padding: 0; +} + +#facebox_overlay { + position: fixed; + top: 0px; + left: 0px; + height:100%; + width:100%; +} + +.facebox_hide { + z-index:-100; +} + +.facebox_overlayBG { + background-color: #000; + z-index: 99; +} + +* html #facebox_overlay { /* ie6 hack */ + position: absolute; + height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); +} diff --git a/media/js/facebox/facebox.js b/media/js/facebox/facebox.js new file mode 100644 index 000000000..9fb96b339 --- /dev/null +++ b/media/js/facebox/facebox.js @@ -0,0 +1,319 @@ +/* + * Facebox (for jQuery) + * version: 1.2 (05/05/2008) + * @requires jQuery v1.2 or later + * + * Examples at http://famspam.com/facebox/ + * + * Licensed under the MIT: + * http://www.opensource.org/licenses/mit-license.php + * + * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ] + * + * Usage: + * + * jQuery(document).ready(function() { + * jQuery('a[rel*=facebox]').facebox() + * }) + * + * Terms + * Loads the #terms div in the box + * + * Terms + * Loads the terms.html page in the box + * + * Terms + * Loads the terms.png image in the box + * + * + * You can also use it programmatically: + * + * jQuery.facebox('some html') + * + * The above will open a facebox with "some html" as the content. + * + * jQuery.facebox(function($) { + * $.get('blah.html', function(data) { $.facebox(data) }) + * }) + * + * The above will show a loading screen before the passed function is called, + * allowing for a better ajaxy experience. + * + * The facebox function can also display an ajax page or image: + * + * jQuery.facebox({ ajax: 'remote.html' }) + * jQuery.facebox({ image: 'dude.jpg' }) + * + * Want to close the facebox? Trigger the 'close.facebox' document event: + * + * jQuery(document).trigger('close.facebox') + * + * Facebox also has a bunch of other hooks: + * + * loading.facebox + * beforeReveal.facebox + * reveal.facebox (aliased as 'afterReveal.facebox') + * init.facebox + * + * Simply bind a function to any of these hooks: + * + * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... }) + * + */ +(function($) { + $.facebox = function(data, klass) { + $.facebox.loading() + + if (data.ajax) fillFaceboxFromAjax(data.ajax) + else if (data.image) fillFaceboxFromImage(data.image) + else if (data.div) fillFaceboxFromHref(data.div) + else if ($.isFunction(data)) data.call($) + else $.facebox.reveal(data, klass) + } + + /* + * Public, $.facebox methods + */ + + $.extend($.facebox, { + settings: { + opacity : 0, + overlay : true, + loadingImage : '/media/js/facebox/loading.gif', + closeImage : '/media/js/facebox/closelabel.gif', + imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ], + faceboxHtml : '\ + ' + }, + + loading: function() { + init() + if ($('#facebox .loading').length == 1) return true + showOverlay() + + $('#facebox .content').empty() + $('#facebox .body').children().hide().end(). + append('
') + + $('#facebox').css({ + top: getPageScroll()[1] + (getPageHeight() / 10), + left: 385.5 + }).show() + + $(document).bind('keydown.facebox', function(e) { + if (e.keyCode == 27) $.facebox.close() + return true + }) + $(document).trigger('loading.facebox') + }, + + reveal: function(data, klass) { + $(document).trigger('beforeReveal.facebox') + if (klass) $('#facebox .content').addClass(klass) + $('#facebox .content').append(data) + $('#facebox .loading').remove() + $('#facebox .body').children().fadeIn('normal') + $('#facebox').css('left', $(window).width() / 2 - ($('#facebox table').width() / 2)) + $(document).trigger('reveal.facebox').trigger('afterReveal.facebox') + }, + + close: function() { + $(document).trigger('close.facebox') + return false + } + }) + + /* + * Public, $.fn methods + */ + + $.fn.facebox = function(settings) { + init(settings) + + function clickHandler() { + $.facebox.loading(true) + + // support for rel="facebox.inline_popup" syntax, to add a class + // also supports deprecated "facebox[.inline_popup]" syntax + var klass = this.rel.match(/facebox\[?\.(\w+)\]?/) + if (klass) klass = klass[1] + + fillFaceboxFromHref(this.href, klass) + return false + } + + return this.click(clickHandler) + } + + /* + * Private methods + */ + + // called one time to setup facebox on this page + function init(settings) { + if ($.facebox.settings.inited) return true + else $.facebox.settings.inited = true + + $(document).trigger('init.facebox') + makeCompatible() + + var imageTypes = $.facebox.settings.imageTypes.join('|') + $.facebox.settings.imageTypesRegexp = new RegExp('\.' + imageTypes + '$', 'i') + + if (settings) $.extend($.facebox.settings, settings) + $('body').append($.facebox.settings.faceboxHtml) + + var preload = [ new Image(), new Image() ] + preload[0].src = $.facebox.settings.closeImage + preload[1].src = $.facebox.settings.loadingImage + + $('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() { + preload.push(new Image()) + preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1') + }) + + $('#facebox .close').click($.facebox.close) + $('#facebox .close_image').attr('src', $.facebox.settings.closeImage) + } + + // getPageScroll() by quirksmode.com + function getPageScroll() { + var xScroll, yScroll; + if (self.pageYOffset) { + yScroll = self.pageYOffset; + xScroll = self.pageXOffset; + } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict + yScroll = document.documentElement.scrollTop; + xScroll = document.documentElement.scrollLeft; + } else if (document.body) {// all other Explorers + yScroll = document.body.scrollTop; + xScroll = document.body.scrollLeft; + } + return new Array(xScroll,yScroll) + } + + // Adapted from getPageSize() by quirksmode.com + function getPageHeight() { + var windowHeight + if (self.innerHeight) { // all except Explorer + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowHeight = document.body.clientHeight; + } + return windowHeight + } + + // Backwards compatibility + function makeCompatible() { + var $s = $.facebox.settings + + $s.loadingImage = $s.loading_image || $s.loadingImage + $s.closeImage = $s.close_image || $s.closeImage + $s.imageTypes = $s.image_types || $s.imageTypes + $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml + } + + // Figures out what you want to display and displays it + // formats are: + // div: #id + // image: blah.extension + // ajax: anything else + function fillFaceboxFromHref(href, klass) { + // div + if (href.match(/#/)) { + var url = window.location.href.split('#')[0] + var target = href.replace(url,'') + $.facebox.reveal($(target).clone().show(), klass) + + // image + } else if (href.match($.facebox.settings.imageTypesRegexp)) { + fillFaceboxFromImage(href, klass) + // ajax + } else { + fillFaceboxFromAjax(href, klass) + } + } + + function fillFaceboxFromImage(href, klass) { + var image = new Image() + image.onload = function() { + $.facebox.reveal('
', klass) + } + image.src = href + } + + function fillFaceboxFromAjax(href, klass) { + $.get(href, function(data) { $.facebox.reveal(data, klass) }) + } + + function skipOverlay() { + return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null + } + + function showOverlay() { + if (skipOverlay()) return + + if ($('facebox_overlay').length == 0) + $("body").append('
') + + $('#facebox_overlay').hide().addClass("facebox_overlayBG") + .css('opacity', $.facebox.settings.opacity) + .click(function() { $(document).trigger('close.facebox') }) + .fadeIn(200) + return false + } + + function hideOverlay() { + if (skipOverlay()) return + + $('#facebox_overlay').fadeOut(200, function(){ + $("#facebox_overlay").removeClass("facebox_overlayBG") + $("#facebox_overlay").addClass("facebox_hide") + $("#facebox_overlay").remove() + }) + + return false + } + + /* + * Bindings + */ + + $(document).bind('close.facebox', function() { + $(document).unbind('keydown.facebox') + $('#facebox').fadeOut(function() { + $('#facebox .content').removeClass().addClass('content') + hideOverlay() + $('#facebox .loading').remove() + }) + }) + +})(jQuery); diff --git a/media/js/facebox/loading.gif b/media/js/facebox/loading.gif new file mode 100755 index 000000000..f864d5fd3 Binary files /dev/null and b/media/js/facebox/loading.gif differ diff --git a/media/js/facebox/tl.png b/media/js/facebox/tl.png new file mode 100644 index 000000000..d99c8f6c6 Binary files /dev/null and b/media/js/facebox/tl.png differ diff --git a/media/js/facebox/tr.png b/media/js/facebox/tr.png new file mode 100644 index 000000000..e99b6ec83 Binary files /dev/null and b/media/js/facebox/tr.png differ diff --git a/media/js/jquery-1.3.2.min.js b/media/js/jquery-1.3.2.min.js new file mode 100644 index 000000000..b1ae21d8b --- /dev/null +++ b/media/js/jquery-1.3.2.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/media/js/jquery-ui-personalized-1.6rc6.js b/media/js/jquery-ui-personalized-1.6rc6.js new file mode 100755 index 000000000..0c82130de --- /dev/null +++ b/media/js/jquery-ui-personalized-1.6rc6.js @@ -0,0 +1,2736 @@ +/* + * jQuery UI 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI + */ +;(function($) { + +var _remove = $.fn.remove, + isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9); + +//Helper functions and ui object +$.ui = { + version: "1.6rc6", + + // $.ui.plugin is deprecated. Use the proxy pattern instead. + plugin: { + add: function(module, option, set) { + var proto = $.ui[module].prototype; + for(var i in set) { + proto.plugins[i] = proto.plugins[i] || []; + proto.plugins[i].push([option, set[i]]); + } + }, + call: function(instance, name, args) { + var set = instance.plugins[name]; + if(!set) { return; } + + for (var i = 0; i < set.length; i++) { + if (instance.options[set[i][0]]) { + set[i][1].apply(instance.element, args); + } + } + } + }, + + contains: function(a, b) { + return document.compareDocumentPosition + ? a.compareDocumentPosition(b) & 16 + : a !== b && a.contains(b); + }, + + cssCache: {}, + css: function(name) { + if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; } + var tmp = $('
').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body'); + + //if (!$.browser.safari) + //tmp.appendTo('body'); + + //Opera and Safari set width and height to 0px instead of auto + //Safari returns rgba(0,0,0,0) when bgcolor is not set + $.ui.cssCache[name] = !!( + (!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) || + !(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))) + ); + try { $('body').get(0).removeChild(tmp.get(0)); } catch(e){} + return $.ui.cssCache[name]; + }, + + hasScroll: function(el, a) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ($(el).css('overflow') == 'hidden') { return false; } + + var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop', + has = false; + + if (el[scroll] > 0) { return true; } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[scroll] = 1; + has = (el[scroll] > 0); + el[scroll] = 0; + return has; + }, + + isOverAxis: function(x, reference, size) { + //Determines when x coordinate is over "b" element axis + return (x > reference) && (x < (reference + size)); + }, + + isOver: function(y, x, top, left, height, width) { + //Determines when x, y coordinates is over "b" element + return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width); + }, + + keyCode: { + BACKSPACE: 8, + CAPS_LOCK: 20, + COMMA: 188, + CONTROL: 17, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + INSERT: 45, + LEFT: 37, + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SHIFT: 16, + SPACE: 32, + TAB: 9, + UP: 38 + } +}; + +// WAI-ARIA normalization +if (isFF2) { + var attr = $.attr, + removeAttr = $.fn.removeAttr, + ariaNS = "http://www.w3.org/2005/07/aaa", + ariaState = /^aria-/, + ariaRole = /^wairole:/; + + $.attr = function(elem, name, value) { + var set = value !== undefined; + + return (name == 'role' + ? (set + ? attr.call(this, elem, name, "wairole:" + value) + : (attr.apply(this, arguments) || "").replace(ariaRole, "")) + : (ariaState.test(name) + ? (set + ? elem.setAttributeNS(ariaNS, + name.replace(ariaState, "aaa:"), value) + : attr.call(this, elem, name.replace(ariaState, "aaa:"))) + : attr.apply(this, arguments))); + }; + + $.fn.removeAttr = function(name) { + return (ariaState.test(name) + ? this.each(function() { + this.removeAttributeNS(ariaNS, name.replace(ariaState, "")); + }) : removeAttr.call(this, name)); + }; +} + +//jQuery plugins +$.fn.extend({ + remove: function() { + // Safari has a native remove event which actually removes DOM elements, + // so we have to use triggerHandler instead of trigger (#3037). + $("*", this).add(this).each(function() { + $(this).triggerHandler("remove"); + }); + return _remove.apply(this, arguments ); + }, + + enableSelection: function() { + return this + .attr('unselectable', 'off') + .css('MozUserSelect', '') + .unbind('selectstart.ui'); + }, + + disableSelection: function() { + return this + .attr('unselectable', 'on') + .css('MozUserSelect', 'none') + .bind('selectstart.ui', function() { return false; }); + }, + + scrollParent: function() { + var scrollParent; + if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) { + scrollParent = this.parents().filter(function() { + return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); + }).eq(0); + } else { + scrollParent = this.parents().filter(function() { + return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); + }).eq(0); + } + + return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent; + } +}); + + +//Additional selectors +$.extend($.expr[':'], { + data: function(elem, i, match) { + return !!$.data(elem, match[3]); + }, + + focusable: function(element) { + var nodeName = element.nodeName.toLowerCase(), + tabIndex = $.attr(element, 'tabindex'); + return (/input|select|textarea|button|object/.test(nodeName) + ? !element.disabled + : 'a' == nodeName || 'area' == nodeName + ? element.href || !isNaN(tabIndex) + : !isNaN(tabIndex)) + // the element and all of its ancestors must be visible + // the browser may report that the area is hidden + && !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length; + }, + + tabbable: function(element) { + var tabIndex = $.attr(element, 'tabindex'); + return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable'); + } +}); + + +// $.widget is a factory to create jQuery plugins +// taking some boilerplate code out of the plugin code +function getter(namespace, plugin, method, args) { + function getMethods(type) { + var methods = $[namespace][plugin][type] || []; + return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods); + } + + var methods = getMethods('getter'); + if (args.length == 1 && typeof args[0] == 'string') { + methods = methods.concat(getMethods('getterSetter')); + } + return ($.inArray(method, methods) != -1); +} + +$.widget = function(name, prototype) { + var namespace = name.split(".")[0]; + name = name.split(".")[1]; + + // create plugin method + $.fn[name] = function(options) { + var isMethodCall = (typeof options == 'string'), + args = Array.prototype.slice.call(arguments, 1); + + // prevent calls to internal methods + if (isMethodCall && options.substring(0, 1) == '_') { + return this; + } + + // handle getter methods + if (isMethodCall && getter(namespace, name, options, args)) { + var instance = $.data(this[0], name); + return (instance ? instance[options].apply(instance, args) + : undefined); + } + + // handle initialization and non-getter methods + return this.each(function() { + var instance = $.data(this, name); + + // constructor + (!instance && !isMethodCall && + $.data(this, name, new $[namespace][name](this, options))._init()); + + // method call + (instance && isMethodCall && $.isFunction(instance[options]) && + instance[options].apply(instance, args)); + }); + }; + + // create widget constructor + $[namespace] = $[namespace] || {}; + $[namespace][name] = function(element, options) { + var self = this; + + this.namespace = namespace; + this.widgetName = name; + this.widgetEventPrefix = $[namespace][name].eventPrefix || name; + this.widgetBaseClass = namespace + '-' + name; + + this.options = $.extend({}, + $.widget.defaults, + $[namespace][name].defaults, + $.metadata && $.metadata.get(element)[name], + options); + + this.element = $(element) + .bind('setData.' + name, function(event, key, value) { + if (event.target == element) { + return self._setData(key, value); + } + }) + .bind('getData.' + name, function(event, key) { + if (event.target == element) { + return self._getData(key); + } + }) + .bind('remove', function() { + return self.destroy(); + }); + }; + + // add widget prototype + $[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype); + + // TODO: merge getter and getterSetter properties from widget prototype + // and plugin prototype + $[namespace][name].getterSetter = 'option'; +}; + +$.widget.prototype = { + _init: function() {}, + destroy: function() { + this.element.removeData(this.widgetName) + .removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled') + .removeAttr('aria-disabled'); + }, + + option: function(key, value) { + var options = key, + self = this; + + if (typeof key == "string") { + if (value === undefined) { + return this._getData(key); + } + options = {}; + options[key] = value; + } + + $.each(options, function(key, value) { + self._setData(key, value); + }); + }, + _getData: function(key) { + return this.options[key]; + }, + _setData: function(key, value) { + this.options[key] = value; + + if (key == 'disabled') { + this.element + [value ? 'addClass' : 'removeClass']( + this.widgetBaseClass + '-disabled' + ' ' + + this.namespace + '-state-disabled') + .attr("aria-disabled", value); + } + }, + + enable: function() { + this._setData('disabled', false); + }, + disable: function() { + this._setData('disabled', true); + }, + + _trigger: function(type, event, data) { + var callback = this.options[type], + eventName = (type == this.widgetEventPrefix + ? type : this.widgetEventPrefix + type); + + event = $.Event(event); + event.type = eventName; + + // copy original event properties over to the new event + // this would happen if we could call $.event.fix instead of $.Event + // but we don't have a way to force an event to be fixed multiple times + if (event.originalEvent) { + for (var i = $.event.props.length, prop; i;) { + prop = $.event.props[--i]; + event[prop] = event.originalEvent[prop]; + } + } + + this.element.trigger(event, data); + + return !($.isFunction(callback) && callback.call(this.element[0], event, data) === false + || event.isDefaultPrevented()); + } +}; + +$.widget.defaults = { + disabled: false +}; + + +/** Mouse Interaction Plugin **/ + +$.ui.mouse = { + _mouseInit: function() { + var self = this; + + this.element + .bind('mousedown.'+this.widgetName, function(event) { + return self._mouseDown(event); + }) + .bind('click.'+this.widgetName, function(event) { + if(self._preventClickEvent) { + self._preventClickEvent = false; + return false; + } + }); + + // Prevent text selection in IE + if ($.browser.msie) { + this._mouseUnselectable = this.element.attr('unselectable'); + this.element.attr('unselectable', 'on'); + } + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind('.'+this.widgetName); + + // Restore text selection in IE + ($.browser.msie + && this.element.attr('unselectable', this._mouseUnselectable)); + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if (event.originalEvent.mouseHandled) { return; } + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var self = this, + btnIsLeft = (event.which == 1), + elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + self.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return self._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return self._mouseUp(event); + }; + $(document) + .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + // preventDefault() is used to prevent the selection of text here - + // however, in Safari, this causes select boxes not to be selectable + // anymore, so this fix is needed + ($.browser.safari || event.preventDefault()); + + event.originalEvent.mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.browser.msie && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + this._preventClickEvent = true; + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(event) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(event) {}, + _mouseDrag: function(event) {}, + _mouseStop: function(event) {}, + _mouseCapture: function(event) { return true; } +}; + +$.ui.mouse.defaults = { + cancel: null, + distance: 1, + delay: 0 +}; + +})(jQuery); +/* + * jQuery UI Draggable 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * ui.core.js + */ +(function($) { + +$.widget("ui.draggable", $.extend({}, $.ui.mouse, { + + _init: function() { + + if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position"))) + this.element[0].style.position = 'relative'; + + (this.options.cssNamespace && this.element.addClass(this.options.cssNamespace+"-draggable")); + (this.options.disabled && this.element.addClass(this.options.cssNamespace+'-draggable-disabled')); + + this._mouseInit(); + + }, + + destroy: function() { + if(!this.element.data('draggable')) return; + this.element.removeData("draggable").unbind(".draggable").removeClass(this.options.cssNamespace+'-draggable '+this.options.cssNamespace+'-draggable-dragging '+this.options.cssNamespace+'-draggable-disabled'); + this._mouseDestroy(); + }, + + _mouseCapture: function(event) { + + var o = this.options; + + if (this.helper || o.disabled || $(event.target).is('.'+this.options.cssNamespace+'-resizable-handle')) + return false; + + //Quit if we're not on a valid handle + this.handle = this._getHandle(event); + if (!this.handle) + return false; + + return true; + + }, + + _mouseStart: function(event) { + + var o = this.options; + + //Create and append the visible helper + this.helper = this._createHelper(event); + + //Cache the helper size + this._cacheHelperProportions(); + + //If ddmanager is used for droppables, set the global draggable + if($.ui.ddmanager) + $.ui.ddmanager.current = this; + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Store the helper's css position + this.cssPosition = this.helper.css("position"); + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.element.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + $.extend(this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper + }); + + //Generate the original position + this.originalPosition = this._generatePosition(event); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied + if(o.cursorAt) + this._adjustOffsetFromHelper(o.cursorAt); + + //Set a containment if given in the options + if(o.containment) + this._setContainment(); + + //Call plugins and callbacks + this._trigger("start", event); + + //Recache the helper size + this._cacheHelperProportions(); + + //Prepare the droppable offsets + if ($.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(this, event); + + this.helper.addClass(o.cssNamespace+"-draggable-dragging"); + this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position + return true; + }, + + _mouseDrag: function(event, noPropagation) { + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + //Call plugins and callbacks and use the resulting position if something is returned + if (!noPropagation) { + var ui = this._uiHash(); + this._trigger('drag', event, ui); + this.position = ui.position; + } + + if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px'; + if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px'; + if($.ui.ddmanager) $.ui.ddmanager.drag(this, event); + + return false; + }, + + _mouseStop: function(event) { + + //If we are using droppables, inform the manager about the drop + var dropped = false; + if ($.ui.ddmanager && !this.options.dropBehaviour) + dropped = $.ui.ddmanager.drop(this, event); + + //if a drop comes from outside (a sortable) + if(this.dropped) { + dropped = this.dropped; + this.dropped = false; + } + + if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { + var self = this; + $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { + self._trigger("stop", event); + self._clear(); + }); + } else { + this._trigger("stop", event); + this._clear(); + } + + return false; + }, + + _getHandle: function(event) { + + var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false; + $(this.options.handle, this.element) + .find("*") + .andSelf() + .each(function() { + if(this == event.target) handle = true; + }); + + return handle; + + }, + + _createHelper: function(event) { + + var o = this.options; + var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element); + + if(!helper.parents('body').length) + helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); + + if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) + helper.css("position", "absolute"); + + return helper; + + }, + + _adjustOffsetFromHelper: function(obj) { + if(obj.left != undefined) this.offset.click.left = obj.left + this.margins.left; + if(obj.right != undefined) this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + if(obj.top != undefined) this.offset.click.top = obj.top + this.margins.top; + if(obj.bottom != undefined) this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + }, + + _getParentOffset: function() { + + //Get the offsetParent and cache its position + this.offsetParent = this.helper.offsetParent(); + var po = this.offsetParent.offset(); + + // This is a special case where we need to modify a offset calculated on start, since the following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that + // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag + if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + if((this.offsetParent[0] == document.body && $.browser.mozilla) //Ugly FF3 fix + || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix + po = { top: 0, left: 0 }; + + return { + top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), + left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) + }; + + }, + + _getRelativeOffset: function() { + + if(this.cssPosition == "relative") { + var p = this.element.position(); + return { + top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), + left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: (parseInt(this.element.css("marginLeft"),10) || 0), + top: (parseInt(this.element.css("marginTop"),10) || 0) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var o = this.options; + if(o.containment == 'parent') o.containment = this.helper[0].parentNode; + if(o.containment == 'document' || o.containment == 'window') this.containment = [ + 0 - this.offset.relative.left - this.offset.parent.left, + 0 - this.offset.relative.top - this.offset.parent.top, + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left, + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top + ]; + + if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) { + var ce = $(o.containment)[0]; if(!ce) return; + var co = $(o.containment).offset(); + var over = ($(ce).css("overflow") != 'hidden'); + + this.containment = [ + co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, + co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, + co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, + co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top + ]; + } else if(o.containment.constructor == Array) { + this.containment = o.containment; + } + + }, + + _convertPositionTo: function(d, pos) { + + if(!pos) pos = this.position; + var mod = d == "absolute" ? 1 : -1; + var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + return { + top: ( + pos.top // The absolute mouse position + + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border) + - ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod + ), + left: ( + pos.left // The absolute mouse position + + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border) + - ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod + ) + }; + + }, + + _generatePosition: function(event) { + + var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + // This is another very weird special case that only happens for relative elements: + // 1. If the css position is relative + // 2. and the scroll parent is the document or similar to the offset parent + // we have to refresh the relative offset during the scroll so there are no jumps + if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) { + this.offset.relative = this._getRelativeOffset(); + } + + var pageX = event.pageX; + var pageY = event.pageY; + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + if(this.originalPosition) { //If we are not dragging yet, we won't check for options + + if(this.containment) { + if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left; + if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top; + if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left; + if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top; + } + + if(o.grid) { + var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; + pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; + + var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; + pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; + } + + } + + return { + top: ( + pageY // The absolute mouse position + - this.offset.click.top // Click offset (relative to the element) + - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.top // The offsetParent's offset without borders (offset + border) + + ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) + ), + left: ( + pageX // The absolute mouse position + - this.offset.click.left // Click offset (relative to the element) + - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.left // The offsetParent's offset without borders (offset + border) + + ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) + ) + }; + + }, + + _clear: function() { + this.helper.removeClass(this.options.cssNamespace+"-draggable-dragging"); + if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove(); + //if($.ui.ddmanager) $.ui.ddmanager.current = null; + this.helper = null; + this.cancelHelperRemoval = false; + }, + + // From now on bulk stuff - mainly helpers + + _trigger: function(type, event, ui) { + ui = ui || this._uiHash(); + $.ui.plugin.call(this, type, [event, ui]); + if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins + return $.widget.prototype._trigger.call(this, type, event, ui); + }, + + plugins: {}, + + _uiHash: function(event) { + return { + helper: this.helper, + position: this.position, + absolutePosition: this.positionAbs, //deprecated + offset: this.positionAbs + }; + } + +})); + +$.extend($.ui.draggable, { + version: "1.6rc6", + eventPrefix: "drag", + defaults: { + appendTo: "parent", + axis: false, + cancel: ":input,option", + connectToSortable: false, + containment: false, + cssNamespace: "ui", + cursor: "default", + cursorAt: false, + delay: 0, + distance: 1, + grid: false, + handle: false, + helper: "original", + iframeFix: false, + opacity: false, + refreshPositions: false, + revert: false, + revertDuration: 500, + scope: "default", + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: false + } +}); + +$.ui.plugin.add("draggable", "connectToSortable", { + start: function(event, ui) { + + var inst = $(this).data("draggable"), o = inst.options; + inst.sortables = []; + $(o.connectToSortable).each(function() { + // 'this' points to a string, and should therefore resolved as query, but instead, if the string is assigned to a variable, it loops through the strings properties, + // so we have to append '' to make it anonymous again + $(typeof this == 'string' ? this+'': this).each(function() { + if($.data(this, 'sortable')) { + var sortable = $.data(this, 'sortable'); + inst.sortables.push({ + instance: sortable, + shouldRevert: sortable.options.revert + }); + sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache + sortable._trigger("activate", event, inst); + } + }); + }); + + }, + stop: function(event, ui) { + + //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper + var inst = $(this).data("draggable"); + + $.each(inst.sortables, function() { + if(this.instance.isOver) { + + this.instance.isOver = 0; + + inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance + this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) + + //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid' + if(this.shouldRevert) this.instance.options.revert = true; + + //Trigger the stop of the sortable + this.instance._mouseStop(event); + + this.instance.options.helper = this.instance.options._helper; + + //If the helper has been the original item, restore properties in the sortable + if(inst.options.helper == 'original') + this.instance.currentItem.css({ top: 'auto', left: 'auto' }); + + } else { + this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance + this.instance._trigger("deactivate", event, inst); + } + + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("draggable"), self = this; + + var checkPos = function(o) { + var dyClick = this.offset.click.top, dxClick = this.offset.click.left; + var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left; + var itemHeight = o.height, itemWidth = o.width; + var itemTop = o.top, itemLeft = o.left; + + return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth); + }; + + $.each(inst.sortables, function(i) { + + if(checkPos.call(inst, this.instance.containerCache)) { + + //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once + if(!this.instance.isOver) { + this.instance.isOver = 1; + //Now we fake the start of dragging for the sortable instance, + //by cloning the list group item, appending it to the sortable and using it as inst.currentItem + //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) + this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true); + this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it + this.instance.options.helper = function() { return ui.helper[0]; }; + + event.target = this.instance.currentItem[0]; + this.instance._mouseCapture(event, true); + this.instance._mouseStart(event, true, true); + + //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes + this.instance.offset.click.top = inst.offset.click.top; + this.instance.offset.click.left = inst.offset.click.left; + this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; + this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; + + inst._trigger("toSortable", event); + inst.dropped = this.instance.element; //draggable revert needs that + this.instance.fromOutside = inst; //Little hack so receive/update callbacks work + + } + + //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable + if(this.instance.currentItem) this.instance._mouseDrag(event); + + } else { + + //If it doesn't intersect with the sortable, and it intersected before, + //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval + if(this.instance.isOver) { + this.instance.isOver = 0; + this.instance.cancelHelperRemoval = true; + this.instance.options.revert = false; //No revert here + this.instance._mouseStop(event, true); + this.instance.options.helper = this.instance.options._helper; + + //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size + this.instance.currentItem.remove(); + if(this.instance.placeholder) this.instance.placeholder.remove(); + + inst._trigger("fromSortable", event); + inst.dropped = false; //draggable revert needs that + } + + }; + + }); + + } +}); + +$.ui.plugin.add("draggable", "cursor", { + start: function(event, ui) { + var t = $('body'), o = $(this).data('draggable').options; + if (t.css("cursor")) o._cursor = t.css("cursor"); + t.css("cursor", o.cursor); + }, + stop: function(event, ui) { + var o = $(this).data('draggable').options; + if (o._cursor) $('body').css("cursor", o._cursor); + } +}); + +$.ui.plugin.add("draggable", "iframeFix", { + start: function(event, ui) { + var o = $(this).data('draggable').options; + $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { + $('
') + .css({ + width: this.offsetWidth+"px", height: this.offsetHeight+"px", + position: "absolute", opacity: "0.001", zIndex: 1000 + }) + .css($(this).offset()) + .appendTo("body"); + }); + }, + stop: function(event, ui) { + $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers + } +}); + +$.ui.plugin.add("draggable", "opacity", { + start: function(event, ui) { + var t = $(ui.helper), o = $(this).data('draggable').options; + if(t.css("opacity")) o._opacity = t.css("opacity"); + t.css('opacity', o.opacity); + }, + stop: function(event, ui) { + var o = $(this).data('draggable').options; + if(o._opacity) $(ui.helper).css('opacity', o._opacity); + } +}); + +$.ui.plugin.add("draggable", "scroll", { + start: function(event, ui) { + var i = $(this).data("draggable"); + if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset(); + }, + drag: function(event, ui) { + + var i = $(this).data("draggable"), o = i.options, scrolled = false; + + if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') { + + if(!o.axis || o.axis != 'x') { + if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; + else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; + } + + if(!o.axis || o.axis != 'y') { + if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; + else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; + } + + } else { + + if(!o.axis || o.axis != 'x') { + if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); + else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); + } + + if(!o.axis || o.axis != 'y') { + if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); + else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); + } + + } + + if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(i, event); + + } +}); + +$.ui.plugin.add("draggable", "snap", { + start: function(event, ui) { + + var i = $(this).data("draggable"), o = i.options; + i.snapElements = []; + + $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() { + var $t = $(this); var $o = $t.offset(); + if(this != i.element[0]) i.snapElements.push({ + item: this, + width: $t.outerWidth(), height: $t.outerHeight(), + top: $o.top, left: $o.left + }); + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("draggable"), o = inst.options; + var d = o.snapTolerance; + + var x1 = ui.absolutePosition.left, x2 = x1 + inst.helperProportions.width, + y1 = ui.absolutePosition.top, y2 = y1 + inst.helperProportions.height; + + for (var i = inst.snapElements.length - 1; i >= 0; i--){ + + var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width, + t = inst.snapElements[i].top, b = t + inst.snapElements[i].height; + + //Yes, I know, this is insane ;) + if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) { + if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + inst.snapElements[i].snapping = false; + continue; + } + + if(o.snapMode != 'inner') { + var ts = Math.abs(t - y2) <= d; + var bs = Math.abs(b - y1) <= d; + var ls = Math.abs(l - x2) <= d; + var rs = Math.abs(r - x1) <= d; + if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; + if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; + if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; + if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; + } + + var first = (ts || bs || ls || rs); + + if(o.snapMode != 'outer') { + var ts = Math.abs(t - y1) <= d; + var bs = Math.abs(b - y2) <= d; + var ls = Math.abs(l - x1) <= d; + var rs = Math.abs(r - x2) <= d; + if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; + if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; + if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; + if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; + } + + if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) + (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + inst.snapElements[i].snapping = (ts || bs || ls || rs || first); + + }; + + } +}); + +$.ui.plugin.add("draggable", "stack", { + start: function(event, ui) { + + var o = $(this).data("draggable").options; + + var group = $.makeArray($(o.stack.group)).sort(function(a,b) { + return (parseInt($(a).css("zIndex"),10) || o.stack.min) - (parseInt($(b).css("zIndex"),10) || o.stack.min); + }); + + $(group).each(function(i) { + this.style.zIndex = o.stack.min + i; + }); + + this[0].style.zIndex = o.stack.min + group.length; + + } +}); + +$.ui.plugin.add("draggable", "zIndex", { + start: function(event, ui) { + var t = $(ui.helper), o = $(this).data("draggable").options; + if(t.css("zIndex")) o._zIndex = t.css("zIndex"); + t.css('zIndex', o.zIndex); + }, + stop: function(event, ui) { + var o = $(this).data("draggable").options; + if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex); + } +}); + +})(jQuery); +/* + * jQuery UI Resizable 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Resizables + * + * Depends: + * ui.core.js + */ +(function($) { + +$.widget("ui.resizable", $.extend({}, $.ui.mouse, { + + _init: function() { + + var self = this, o = this.options; + this.element.addClass("ui-resizable"); + + $.extend(this, { + _aspectRatio: !!(o.aspectRatio), + aspectRatio: o.aspectRatio, + originalElement: this.element, + proportionallyResize: o.proportionallyResize ? [o.proportionallyResize] : [], + _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null + }); + + //Wrap the element if it cannot hold child nodes + if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { + + //Opera fix for relative positioning + if (/relative/.test(this.element.css('position')) && $.browser.opera) + this.element.css({ position: 'relative', top: 'auto', left: 'auto' }); + + //Create a wrapper element and set the wrapper to the new current internal element + this.element.wrap( + $('
').css({ + position: this.element.css('position'), + width: this.element.outerWidth(), + height: this.element.outerHeight(), + top: this.element.css('top'), + left: this.element.css('left') + }) + ); + + //Overwrite the original this.element + this.element = this.element.parent(); + this.elementIsWrapper = true; + + //Move margins to the wrapper + this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); + this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); + + //Prevent Safari textarea resize + if ($.browser.safari && o.preventDefault) this.originalElement.css('resize', 'none'); + + //Push the actual element to our proportionallyResize internal array + this.proportionallyResize.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' })); + + // avoid IE jump (hard set the margin) + this.originalElement.css({ margin: this.originalElement.css('margin') }); + + // fix handlers offset + this._proportionallyResize(); + + } + + this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' }); + if(this.handles.constructor == String) { + + if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw'; + var n = this.handles.split(","); this.handles = {}; + + for(var i = 0; i < n.length; i++) { + + var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle; + var axis = $('
'); + + // increase zIndex of sw, se, ne, nw axis + //TODO : this modifies original option + if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex }); + + //TODO : What's going on here? + if ('se' == handle) { + axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se'); + }; + + //Insert into internal handles object and append to element + this.handles[handle] = '.ui-resizable-'+handle; + this.element.append(axis); + } + + } + + this._renderAxis = function(target) { + + target = target || this.element; + + for(var i in this.handles) { + + if(this.handles[i].constructor == String) + this.handles[i] = $(this.handles[i], this.element).show(); + + if (o.transparent) + this.handles[i].css({ opacity: 0 }); + + //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) + if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { + + var axis = $(this.handles[i], this.element), padWrapper = 0; + + //Checking the correct pad and border + padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); + + //The padding type i have to apply... + var padPos = [ 'padding', + /ne|nw|n/.test(i) ? 'Top' : + /se|sw|s/.test(i) ? 'Bottom' : + /^e$/.test(i) ? 'Right' : 'Left' ].join(""); + + if (!o.transparent) + target.css(padPos, padWrapper); + + this._proportionallyResize(); + + } + + //TODO: What's that good for? There's not anything to be executed left + if(!$(this.handles[i]).length) + continue; + + } + }; + + //TODO: make renderAxis a prototype function + this._renderAxis(this.element); + + this._handles = $('.ui-resizable-handle', this.element); + + if (o.disableSelection) + this._handles.disableSelection(); + + //Matching axis name + this._handles.mouseover(function() { + if (!self.resizing) { + if (this.className) + var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); + //Axis, default = se + self.axis = axis && axis[1] ? axis[1] : 'se'; + } + }); + + //If we want to auto hide the elements + if (o.autoHide) { + this._handles.hide(); + $(this.element) + .addClass("ui-resizable-autohide") + .hover(function() { + $(this).removeClass("ui-resizable-autohide"); + self._handles.show(); + }, + function(){ + if (!self.resizing) { + $(this).addClass("ui-resizable-autohide"); + self._handles.hide(); + } + }); + } + + //Initialize the mouse interaction + this._mouseInit(); + + }, + + destroy: function() { + + this._mouseDestroy(); + + var _destroy = function(exp) { + $(exp).removeClass("ui-resizable ui-resizable-disabled") + .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove(); + }; + + //TODO: Unwrap at same DOM position + if (this.elementIsWrapper) { + _destroy(this.element); + this.wrapper.parent().append( + this.originalElement.css({ + position: this.wrapper.css('position'), + width: this.wrapper.outerWidth(), + height: this.wrapper.outerHeight(), + top: this.wrapper.css('top'), + left: this.wrapper.css('left') + }) + ).end().remove(); + } + + _destroy(this.originalElement); + + }, + + _mouseCapture: function(event) { + + var handle = false; + for(var i in this.handles) { + if($(this.handles[i])[0] == event.target) handle = true; + } + + return this.options.disabled || !!handle; + + }, + + _mouseStart: function(event) { + + var o = this.options, iniPos = this.element.position(), el = this.element; + + this.resizing = true; + this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() }; + + // bugfix for http://dev.jquery.com/ticket/1749 + if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) { + el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left }); + } + + //Opera fixing relative position + if ($.browser.opera && (/relative/).test(el.css('position'))) + el.css({ position: 'relative', top: 'auto', left: 'auto' }); + + this._renderProxy(); + + var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top')); + + if (o.containment) { + curleft += $(o.containment).scrollLeft() || 0; + curtop += $(o.containment).scrollTop() || 0; + } + + //Store needed variables + this.offset = this.helper.offset(); + this.position = { left: curleft, top: curtop }; + this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; + this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; + this.originalPosition = { left: curleft, top: curtop }; + this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; + this.originalMousePosition = { left: event.pageX, top: event.pageY }; + + //Aspect Ratio + this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); + + if (o.preserveCursor) { + var cursor = $('.ui-resizable-' + this.axis).css('cursor'); + $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor); + } + + this._propagate("start", event); + return true; + }, + + _mouseDrag: function(event) { + + //Increase performance, avoid regex + var el = this.helper, o = this.options, props = {}, + self = this, smp = this.originalMousePosition, a = this.axis; + + var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0; + var trigger = this._change[a]; + if (!trigger) return false; + + // Calculate the attrs that will be change + var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff; + + if (this._aspectRatio || event.shiftKey) + data = this._updateRatio(data, event); + + data = this._respectSize(data, event); + + // plugins callbacks need to be called first + this._propagate("resize", event); + + el.css({ + top: this.position.top + "px", left: this.position.left + "px", + width: this.size.width + "px", height: this.size.height + "px" + }); + + if (!this._helper && this.proportionallyResize.length) + this._proportionallyResize(); + + this._updateCache(data); + + // calling the user callback at the end + this._trigger('resize', event, this.ui()); + + return false; + }, + + _mouseStop: function(event) { + + this.resizing = false; + var o = this.options, self = this; + + if(this._helper) { + var pr = this.proportionallyResize, ista = pr.length && (/textarea/i).test(pr[0].nodeName), + soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, + soffsetw = ista ? 0 : self.sizeDiff.width; + + var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) }, + left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, + top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; + + if (!o.animate) + this.element.css($.extend(s, { top: top, left: left })); + + if (this._helper && !o.animate) this._proportionallyResize(); + } + + if (o.preserveCursor) + $('body').css('cursor', 'auto'); + + this._propagate("stop", event); + + if (this._helper) this.helper.remove(); + return false; + + }, + + _updateCache: function(data) { + var o = this.options; + this.offset = this.helper.offset(); + if (data.left) this.position.left = data.left; + if (data.top) this.position.top = data.top; + if (data.height) this.size.height = data.height; + if (data.width) this.size.width = data.width; + }, + + _updateRatio: function(data, event) { + + var o = this.options, cpos = this.position, csize = this.size, a = this.axis; + + if (data.height) data.width = (csize.height * this.aspectRatio); + else if (data.width) data.height = (csize.width / this.aspectRatio); + + if (a == 'sw') { + data.left = cpos.left + (csize.width - data.width); + data.top = null; + } + if (a == 'nw') { + data.top = cpos.top + (csize.height - data.height); + data.left = cpos.left + (csize.width - data.width); + } + + return data; + }, + + _respectSize: function(data, event) { + + var isNumber = function(value) { + return !isNaN(parseInt(value, 10)) + }; + + var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis, + ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), + isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height); + + if (isminw) data.width = o.minWidth; + if (isminh) data.height = o.minHeight; + if (ismaxw) data.width = o.maxWidth; + if (ismaxh) data.height = o.maxHeight; + + var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height; + var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); + + if (isminw && cw) data.left = dw - o.minWidth; + if (ismaxw && cw) data.left = dw - o.maxWidth; + if (isminh && ch) data.top = dh - o.minHeight; + if (ismaxh && ch) data.top = dh - o.maxHeight; + + // fixing jump error on top/left - bug #2330 + var isNotwh = !data.width && !data.height; + if (isNotwh && !data.left && data.top) data.top = null; + else if (isNotwh && !data.top && data.left) data.left = null; + + return data; + }, + + _proportionallyResize: function() { + + var o = this.options; + if (!this.proportionallyResize.length) return; + var element = this.helper || this.element; + + for (var i=0; i < this.proportionallyResize.length; i++) { + + var prel = this.proportionallyResize[i]; + + if (!this.borderDif) { + var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')], + p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')]; + + this.borderDif = $.map(b, function(v, i) { + var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0; + return border + padding; + }); + } + + if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length))) + continue; + + prel.css({ + height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, + width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 + }); + + }; + + }, + + _renderProxy: function() { + + var el = this.element, o = this.options; + this.elementOffset = el.offset(); + + if(this._helper) { + + this.helper = this.helper || $('
'); + + // fix ie6 offset TODO: This seems broken + var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0), + pxyoffset = ( ie6 ? 2 : -1 ); + + this.helper.addClass(this._helper).css({ + width: this.element.outerWidth() + pxyoffset, + height: this.element.outerHeight() + pxyoffset, + position: 'absolute', + left: this.elementOffset.left - ie6offset +'px', + top: this.elementOffset.top - ie6offset +'px', + zIndex: ++o.zIndex //TODO: Don't modify option + }); + + this.helper.appendTo("body"); + + if (o.disableSelection) + this.helper.disableSelection(); + + } else { + this.helper = this.element; + } + + }, + + _change: { + e: function(event, dx, dy) { + return { width: this.originalSize.width + dx }; + }, + w: function(event, dx, dy) { + var o = this.options, cs = this.originalSize, sp = this.originalPosition; + return { left: sp.left + dx, width: cs.width - dx }; + }, + n: function(event, dx, dy) { + var o = this.options, cs = this.originalSize, sp = this.originalPosition; + return { top: sp.top + dy, height: cs.height - dy }; + }, + s: function(event, dx, dy) { + return { height: this.originalSize.height + dy }; + }, + se: function(event, dx, dy) { + return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); + }, + sw: function(event, dx, dy) { + return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); + }, + ne: function(event, dx, dy) { + return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); + }, + nw: function(event, dx, dy) { + return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); + } + }, + + _propagate: function(n, event) { + $.ui.plugin.call(this, n, [event, this.ui()]); + (n != "resize" && this._trigger(n, event, this.ui())); + }, + + plugins: {}, + + ui: function() { + return { + originalElement: this.originalElement, + element: this.element, + helper: this.helper, + position: this.position, + size: this.size, + originalSize: this.originalSize, + originalPosition: this.originalPosition + }; + } + +})); + +$.extend($.ui.resizable, { + version: "1.6rc6", + eventPrefix: "resize", + defaults: { + alsoResize: false, + animate: false, + animateDuration: "slow", + animateEasing: "swing", + aspectRatio: false, + autoHide: false, + cancel: ":input,option", + containment: false, + delay: 0, + disableSelection: true, + distance: 1, + ghost: false, + grid: false, + handles: "e,s,se", + helper: false, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + preserveCursor: true, + preventDefault: true, + proportionallyResize: false, + transparent: false, + zIndex: 1000 + } +}); + +/* + * Resizable Extensions + */ + +$.ui.plugin.add("resizable", "alsoResize", { + + start: function(event, ui) { + + var self = $(this).data("resizable"), o = self.options; + + _store = function(exp) { + $(exp).each(function() { + $(this).data("resizable-alsoresize", { + width: parseInt($(this).width(), 10), height: parseInt($(this).height(), 10), + left: parseInt($(this).css('left'), 10), top: parseInt($(this).css('top'), 10) + }); + }); + }; + + if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) { + if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } + else { $.each(o.alsoResize, function(exp, c) { _store(exp); }); } + }else{ + _store(o.alsoResize); + } + }, + + resize: function(event, ui){ + var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition; + + var delta = { + height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0, + top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0 + }, + + _alsoResize = function(exp, c) { + $(exp).each(function() { + var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left']; + + $.each(css || ['width', 'height', 'top', 'left'], function(i, prop) { + var sum = (start[prop]||0) + (delta[prop]||0); + if (sum && sum >= 0) + style[prop] = sum || null; + }); + + //Opera fixing relative position + if (/relative/.test(el.css('position')) && $.browser.opera) { + self._revertToRelativePosition = true; + el.css({ position: 'absolute', top: 'auto', left: 'auto' }); + } + + el.css(style); + }); + }; + + if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) { + $.each(o.alsoResize, function(exp, c) { _alsoResize(exp, c); }); + }else{ + _alsoResize(o.alsoResize); + } + }, + + stop: function(event, ui){ + var self = $(this).data("resizable"); + + //Opera fixing relative position + if (self._revertToRelativePosition && $.browser.opera) { + self._revertToRelativePosition = false; + el.css({ position: 'relative' }); + } + + $(this).removeData("resizable-alsoresize-start"); + } +}); + +$.ui.plugin.add("resizable", "animate", { + + stop: function(event, ui) { + var self = $(this).data("resizable"), o = self.options; + + var pr = o.proportionallyResize, ista = pr && (/textarea/i).test(pr.get(0).nodeName), + soffseth = ista && $.ui.hasScroll(pr.get(0), 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, + soffsetw = ista ? 0 : self.sizeDiff.width; + + var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) }, + left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, + top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; + + self.element.animate( + $.extend(style, top && left ? { top: top, left: left } : {}), { + duration: o.animateDuration, + easing: o.animateEasing, + step: function() { + + var data = { + width: parseInt(self.element.css('width'), 10), + height: parseInt(self.element.css('height'), 10), + top: parseInt(self.element.css('top'), 10), + left: parseInt(self.element.css('left'), 10) + }; + + if (pr) pr.css({ width: data.width, height: data.height }); + + // propagating resize, and updating values for each animation step + self._updateCache(data); + self._propagate("resize", event); + + } + } + ); + } + +}); + +$.ui.plugin.add("resizable", "containment", { + + start: function(event, ui) { + var self = $(this).data("resizable"), o = self.options, el = self.element; + var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; + if (!ce) return; + + self.containerElement = $(ce); + + if (/document/.test(oc) || oc == document) { + self.containerOffset = { left: 0, top: 0 }; + self.containerPosition = { left: 0, top: 0 }; + + self.parentData = { + element: $(document), left: 0, top: 0, + width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight + }; + } + + // i'm a node, so compute top, left, right, bottom + else { + var element = $(ce), p = []; + $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); + + self.containerOffset = element.offset(); + self.containerPosition = element.position(); + self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; + + var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width, + width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); + + self.parentData = { + element: ce, left: co.left, top: co.top, width: width, height: height + }; + } + }, + + resize: function(event, ui) { + var self = $(this).data("resizable"), o = self.options, + ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position, + pRatio = o._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement; + + if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co; + + if (cp.left < (self._helper ? co.left : 0)) { + self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left)); + if (pRatio) self.size.height = self.size.width / o.aspectRatio; + self.position.left = o.helper ? co.left : 0; + } + + if (cp.top < (self._helper ? co.top : 0)) { + self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top); + if (pRatio) self.size.width = self.size.height * o.aspectRatio; + self.position.top = self._helper ? co.top : 0; + } + + var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ), + hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height ); + + if (woset + self.size.width >= self.parentData.width) { + self.size.width = self.parentData.width - woset; + if (pRatio) self.size.height = self.size.width / o.aspectRatio; + } + + if (hoset + self.size.height >= self.parentData.height) { + self.size.height = self.parentData.height - hoset; + if (pRatio) self.size.width = self.size.height * o.aspectRatio; + } + }, + + stop: function(event, ui){ + var self = $(this).data("resizable"), o = self.options, cp = self.position, + co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement; + + var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height; + + if (self._helper && !o.animate && (/relative/).test(ce.css('position'))) + $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); + + if (self._helper && !o.animate && (/static/).test(ce.css('position'))) + $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); + + } +}); + +$.ui.plugin.add("resizable", "ghost", { + + start: function(event, ui) { + + var self = $(this).data("resizable"), o = self.options, pr = o.proportionallyResize, cs = self.size; + + self.ghost = self.originalElement.clone(); + self.ghost + .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) + .addClass('ui-resizable-ghost') + .addClass(typeof o.ghost == 'string' ? o.ghost : ''); + + self.ghost.appendTo(self.helper); + + }, + + resize: function(event, ui){ + var self = $(this).data("resizable"), o = self.options; + if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width }); + }, + + stop: function(event, ui){ + var self = $(this).data("resizable"), o = self.options; + if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0)); + } + +}); + +$.ui.plugin.add("resizable", "grid", { + + resize: function(event, ui) { + var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey; + o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid; + var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1); + + if (/^(se|s|e)$/.test(a)) { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + } + else if (/^(ne)$/.test(a)) { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + self.position.top = op.top - oy; + } + else if (/^(sw)$/.test(a)) { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + self.position.left = op.left - ox; + } + else { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + self.position.top = op.top - oy; + self.position.left = op.left - ox; + } + } + +}); + +var num = function(v) { + return parseInt(v, 10) || 0; +}; + +})(jQuery); +/* + * jQuery UI Progressbar 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * ui.core.js + */ +(function($) { + +$.widget("ui.progressbar", { + + _init: function() { + + var self = this, + options = this.options; + + this.element + .addClass("ui-progressbar" + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all") + .attr({ + role: "progressbar", + "aria-valuemin": this._valueMin(), + "aria-valuemax": this._valueMax(), + "aria-valuenow": this._value() + }); + + this.valueDiv = $('
').appendTo(this.element); + + this._refreshValue(); + + }, + + destroy: function() { + + this.element + .removeClass("ui-progressbar" + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all") + .removeAttr("role") + .removeAttr("aria-valuemin") + .removeAttr("aria-valuemax") + .removeAttr("aria-valuenow") + .removeData("progressbar") + .unbind(".progressbar"); + + this.valueDiv.remove(); + + $.widget.prototype.destroy.apply(this, arguments); + + }, + + value: function(newValue) { + arguments.length && this._setData("value", newValue); + + return this._value(); + }, + + _setData: function(key, value){ + switch (key) { + case 'value': + this.options.value = value; + this._refreshValue(); + this._trigger('change', null, {}); + break; + } + + $.widget.prototype._setData.apply(this, arguments); + }, + + _value: function() { + var val = this.options.value; + if (val < this._valueMin()) val = this._valueMin(); + if (val > this._valueMax()) val = this._valueMax(); + + return val; + }, + + _valueMin: function() { + var valueMin = 0; + + return valueMin; + }, + + _valueMax: function() { + var valueMax = 100; + + return valueMax; + }, + + _refreshValue: function() { + var value = this.value(); + this.valueDiv[value == this._valueMax() ? 'addClass' : 'removeClass']("ui-corner-right"); + this.valueDiv.width(value + '%'); + this.element.attr("aria-valuenow", value); + } + +}); + +$.extend($.ui.progressbar, { + version: "1.6rc6", + defaults: { + value: 0 + } +}); + +})(jQuery); +/* + * jQuery UI Effects 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/ + */ +;(function($) { + +$.effects = $.effects || {}; //Add the 'effects' scope + +$.extend($.effects, { + version: "1.6rc6", + + // Saves a set of properties in a data storage + save: function(element, set) { + for(var i=0; i < set.length; i++) { + if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]); + } + }, + + // Restores a set of previously saved properties from a data storage + restore: function(element, set) { + for(var i=0; i < set.length; i++) { + if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i])); + } + }, + + setMode: function(el, mode) { + if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle + return mode; + }, + + getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value + // this should be a little more flexible in the future to handle a string & hash + var y, x; + switch (origin[0]) { + case 'top': y = 0; break; + case 'middle': y = 0.5; break; + case 'bottom': y = 1; break; + default: y = origin[0] / original.height; + }; + switch (origin[1]) { + case 'left': x = 0; break; + case 'center': x = 0.5; break; + case 'right': x = 1; break; + default: x = origin[1] / original.width; + }; + return {x: x, y: y}; + }, + + // Wraps the element around a wrapper that copies position properties + createWrapper: function(element) { + + //if the element is already wrapped, return it + if (element.parent().is('.ui-effects-wrapper')) + return element.parent(); + + //Cache width,height and float properties of the element, and create a wrapper around it + var props = { width: element.outerWidth(true), height: element.outerHeight(true), 'float': element.css('float') }; + element.wrap('
'); + var wrapper = element.parent(); + + //Transfer the positioning of the element to the wrapper + if (element.css('position') == 'static') { + wrapper.css({ position: 'relative' }); + element.css({ position: 'relative'} ); + } else { + var top = element.css('top'); if(isNaN(parseInt(top,10))) top = 'auto'; + var left = element.css('left'); if(isNaN(parseInt(left,10))) left = 'auto'; + wrapper.css({ position: element.css('position'), top: top, left: left, zIndex: element.css('z-index') }).show(); + element.css({position: 'relative', top: 0, left: 0 }); + } + + wrapper.css(props); + return wrapper; + }, + + removeWrapper: function(element) { + if (element.parent().is('.ui-effects-wrapper')) + return element.parent().replaceWith(element); + return element; + }, + + setTransition: function(element, list, factor, value) { + value = value || {}; + $.each(list, function(i, x){ + unit = element.cssUnit(x); + if (unit[0] > 0) value[x] = unit[0] * factor + unit[1]; + }); + return value; + }, + + //Base function to animate from one class to another in a seamless transition + animateClass: function(value, duration, easing, callback) { + + var cb = (typeof easing == "function" ? easing : (callback ? callback : null)); + var ea = (typeof easing == "string" ? easing : null); + + return this.each(function() { + + var offset = {}; var that = $(this); var oldStyleAttr = that.attr("style") || ''; + if(typeof oldStyleAttr == 'object') oldStyleAttr = oldStyleAttr["cssText"]; /* Stupidly in IE, style is a object.. */ + if(value.toggle) { that.hasClass(value.toggle) ? value.remove = value.toggle : value.add = value.toggle; } + + //Let's get a style offset + var oldStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle)); + if(value.add) that.addClass(value.add); if(value.remove) that.removeClass(value.remove); + var newStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle)); + if(value.add) that.removeClass(value.add); if(value.remove) that.addClass(value.remove); + + // The main function to form the object for animation + for(var n in newStyle) { + if( typeof newStyle[n] != "function" && newStyle[n] /* No functions and null properties */ + && n.indexOf("Moz") == -1 && n.indexOf("length") == -1 /* No mozilla spezific render properties. */ + && newStyle[n] != oldStyle[n] /* Only values that have changed are used for the animation */ + && (n.match(/color/i) || (!n.match(/color/i) && !isNaN(parseInt(newStyle[n],10)))) /* Only things that can be parsed to integers or colors */ + && (oldStyle.position != "static" || (oldStyle.position == "static" && !n.match(/left|top|bottom|right/))) /* No need for positions when dealing with static positions */ + ) offset[n] = newStyle[n]; + } + + that.animate(offset, duration, ea, function() { // Animate the newly constructed offset object + // Change style attribute back to original. For stupid IE, we need to clear the damn object. + if(typeof $(this).attr("style") == 'object') { $(this).attr("style")["cssText"] = ""; $(this).attr("style")["cssText"] = oldStyleAttr; } else $(this).attr("style", oldStyleAttr); + if(value.add) $(this).addClass(value.add); if(value.remove) $(this).removeClass(value.remove); + if(cb) cb.apply(this, arguments); + }); + + }); + } +}); + + +function _normalizeArguments(a, m) { + + var o = a[1] && a[1].constructor == Object ? a[1] : {}; if(m) o.mode = m; + var speed = a[1] && a[1].constructor != Object ? a[1] : o.duration; //either comes from options.duration or the second argument + speed = $.fx.off ? 0 : typeof speed === "number" ? speed : $.fx.speeds[speed] || $.fx.speeds._default; + var callback = o.callback || ( $.isFunction(a[2]) && a[2] ) || ( $.isFunction(a[3]) && a[3] ); + + return [a[0], o, speed, callback]; + +} + +//Extend the methods of jQuery +$.fn.extend({ + + //Save old methods + _show: $.fn.show, + _hide: $.fn.hide, + __toggle: $.fn.toggle, + _addClass: $.fn.addClass, + _removeClass: $.fn.removeClass, + _toggleClass: $.fn.toggleClass, + + // New effect methods + effect: function(fx, options, speed, callback) { + return $.effects[fx] ? $.effects[fx].call(this, {method: fx, options: options || {}, duration: speed, callback: callback }) : null; + }, + + show: function() { + if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0]))) + return this._show.apply(this, arguments); + else { + return this.effect.apply(this, _normalizeArguments(arguments, 'show')); + } + }, + + hide: function() { + if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0]))) + return this._hide.apply(this, arguments); + else { + return this.effect.apply(this, _normalizeArguments(arguments, 'hide')); + } + }, + + toggle: function(){ + if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])) || (arguments[0].constructor == Function)) + return this.__toggle.apply(this, arguments); + else { + return this.effect.apply(this, _normalizeArguments(arguments, 'toggle')); + } + }, + + addClass: function(classNames, speed, easing, callback) { + return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames); + }, + removeClass: function(classNames,speed,easing,callback) { + return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames); + }, + toggleClass: function(classNames,speed,easing,callback) { + return ( (typeof speed !== "boolean") && speed ) ? $.effects.animateClass.apply(this, [{ toggle: classNames },speed,easing,callback]) : this._toggleClass(classNames, speed); + }, + morph: function(remove,add,speed,easing,callback) { + return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]); + }, + switchClass: function() { + return this.morph.apply(this, arguments); + }, + + // helper functions + cssUnit: function(key) { + var style = this.css(key), val = []; + $.each( ['em','px','%','pt'], function(i, unit){ + if(style.indexOf(unit) > 0) + val = [parseFloat(style), unit]; + }); + return val; + } +}); + +/* + * jQuery Color Animations + * Copyright 2007 John Resig + * Released under the MIT and GPL licenses. + */ + +// We override the animation for all of these color styles +$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){ + $.fx.step[attr] = function(fx) { + if ( fx.state == 0 ) { + fx.start = getColor( fx.elem, attr ); + fx.end = getRGB( fx.end ); + } + + fx.elem.style[attr] = "rgb(" + [ + Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0],10), 255), 0), + Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1],10), 255), 0), + Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2],10), 255), 0) + ].join(",") + ")"; + }; +}); + +// Color Conversion functions from highlightFade +// By Blair Mitchelmore +// http://jquery.offput.ca/highlightFade/ + +// Parse strings looking for color tuples [255,255,255] +function getRGB(color) { + var result; + + // Check if we're already dealing with an array of colors + if ( color && color.constructor == Array && color.length == 3 ) + return color; + + // Look for rgb(num,num,num) + if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) + return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)]; + + // Look for rgb(num%,num%,num%) + if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) + return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55]; + + // Look for #a0b1c2 + if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) + return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)]; + + // Look for #fff + if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) + return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)]; + + // Look for rgba(0, 0, 0, 0) == transparent in Safari 3 + if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) + return colors['transparent']; + + // Otherwise, we're most likely dealing with a named color + return colors[$.trim(color).toLowerCase()]; +} + +function getColor(elem, attr) { + var color; + + do { + color = $.curCSS(elem, attr); + + // Keep going until we find an element that has color, or we hit the body + if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") ) + break; + + attr = "backgroundColor"; + } while ( elem = elem.parentNode ); + + return getRGB(color); +}; + +// Some named colors to work with +// From Interface by Stefan Petre +// http://interface.eyecon.ro/ + +var colors = { + aqua:[0,255,255], + azure:[240,255,255], + beige:[245,245,220], + black:[0,0,0], + blue:[0,0,255], + brown:[165,42,42], + cyan:[0,255,255], + darkblue:[0,0,139], + darkcyan:[0,139,139], + darkgrey:[169,169,169], + darkgreen:[0,100,0], + darkkhaki:[189,183,107], + darkmagenta:[139,0,139], + darkolivegreen:[85,107,47], + darkorange:[255,140,0], + darkorchid:[153,50,204], + darkred:[139,0,0], + darksalmon:[233,150,122], + darkviolet:[148,0,211], + fuchsia:[255,0,255], + gold:[255,215,0], + green:[0,128,0], + indigo:[75,0,130], + khaki:[240,230,140], + lightblue:[173,216,230], + lightcyan:[224,255,255], + lightgreen:[144,238,144], + lightgrey:[211,211,211], + lightpink:[255,182,193], + lightyellow:[255,255,224], + lime:[0,255,0], + magenta:[255,0,255], + maroon:[128,0,0], + navy:[0,0,128], + olive:[128,128,0], + orange:[255,165,0], + pink:[255,192,203], + purple:[128,0,128], + violet:[128,0,128], + red:[255,0,0], + silver:[192,192,192], + white:[255,255,255], + yellow:[255,255,0], + transparent: [255,255,255] +}; + +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built in easing capabilities added In jQuery 1.1 + * to offer multiple easing options + * + * TERMS OF USE - jQuery Easing + * + * Open source under the BSD License. + * + * Copyright 2008 George McGinley Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +// t: current time, b: begInnIng value, c: change In value, d: duration +$.easing.jswing = $.easing.swing; + +$.extend($.easing, +{ + def: 'easeOutQuad', + swing: function (x, t, b, c, d) { + //alert($.easing.default); + return $.easing[$.easing.def](x, t, b, c, d); + }, + easeInQuad: function (x, t, b, c, d) { + return c*(t/=d)*t + b; + }, + easeOutQuad: function (x, t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + easeInOutQuad: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + easeInCubic: function (x, t, b, c, d) { + return c*(t/=d)*t*t + b; + }, + easeOutCubic: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; + }, + easeInOutCubic: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; + }, + easeInQuart: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + easeOutQuart: function (x, t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + easeInOutQuart: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + easeInQuint: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t*t + b; + }, + easeOutQuint: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; + }, + easeInOutQuint: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; + }, + easeInSine: function (x, t, b, c, d) { + return -c * Math.cos(t/d * (Math.PI/2)) + c + b; + }, + easeOutSine: function (x, t, b, c, d) { + return c * Math.sin(t/d * (Math.PI/2)) + b; + }, + easeInOutSine: function (x, t, b, c, d) { + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; + }, + easeInExpo: function (x, t, b, c, d) { + return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; + }, + easeOutExpo: function (x, t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; + }, + easeInOutExpo: function (x, t, b, c, d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; + return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; + }, + easeInCirc: function (x, t, b, c, d) { + return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; + }, + easeOutCirc: function (x, t, b, c, d) { + return c * Math.sqrt(1 - (t=t/d-1)*t) + b; + }, + easeInOutCirc: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; + return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; + }, + easeInElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + easeOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + easeInOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + easeInBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + easeOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + easeInOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + easeInBounce: function (x, t, b, c, d) { + return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b; + }, + easeOutBounce: function (x, t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + easeInOutBounce: function (x, t, b, c, d) { + if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; + return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; + } +}); + +/* + * + * TERMS OF USE - EASING EQUATIONS + * + * Open source under the BSD License. + * + * Copyright 2001 Robert Penner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +})(jQuery); diff --git a/media/js/jquery-ui-personalized-1.6rc6.min.js b/media/js/jquery-ui-personalized-1.6rc6.min.js new file mode 100755 index 000000000..86a30786c --- /dev/null +++ b/media/js/jquery-ui-personalized-1.6rc6.min.js @@ -0,0 +1,50 @@ +/* + * jQuery UI 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI + */ (function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.6rc6",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n){return}for(var m=0;m
').addClass(j).css({position:"absolute",top:"-5000px",left:"-5000px",display:"block"}).appendTo("body");c.ui.cssCache[j]=!!((!(/auto|default/).test(k.css("cursor"))||(/^[1-9]/).test(k.css("height"))||(/^[1-9]/).test(k.css("width"))||!(/none/).test(k.css("backgroundImage"))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(k.css("backgroundColor"))));try{c("body").get(0).removeChild(k.get(0))}catch(l){}return c.ui.cssCache[j]},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=true;this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/* + * jQuery UI Draggable 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * ui.core.js + */ (function(a){a.widget("ui.draggable",a.extend({},a.ui.mouse,{_init:function(){if(this.options.helper=="original"&&!(/^(?:r|a|f)/).test(this.element.css("position"))){this.element[0].style.position="relative"}(this.options.cssNamespace&&this.element.addClass(this.options.cssNamespace+"-draggable"));(this.options.disabled&&this.element.addClass(this.options.cssNamespace+"-draggable-disabled"));this._mouseInit()},destroy:function(){if(!this.element.data("draggable")){return}this.element.removeData("draggable").unbind(".draggable").removeClass(this.options.cssNamespace+"-draggable "+this.options.cssNamespace+"-draggable-dragging "+this.options.cssNamespace+"-draggable-disabled");this._mouseDestroy()},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is("."+this.options.cssNamespace+"-resizable-handle")){return false}this.handle=this._getHandle(b);if(!this.handle){return false}return true},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b);this._cacheHelperProportions();if(a.ui.ddmanager){a.ui.ddmanager.current=this}this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(b);this.originalPageX=b.pageX;this.originalPageY=b.pageY;if(c.cursorAt){this._adjustOffsetFromHelper(c.cursorAt)}if(c.containment){this._setContainment()}this._trigger("start",b);this._cacheHelperProportions();if(a.ui.ddmanager&&!c.dropBehaviour){a.ui.ddmanager.prepareOffsets(this,b)}this.helper.addClass(c.cssNamespace+"-draggable-dragging");this._mouseDrag(b,true);return true},_mouseDrag:function(b,d){this.position=this._generatePosition(b);this.positionAbs=this._convertPositionTo("absolute");if(!d){var c=this._uiHash();this._trigger("drag",b,c);this.position=c.position}if(!this.options.axis||this.options.axis!="y"){this.helper[0].style.left=this.position.left+"px"}if(!this.options.axis||this.options.axis!="x"){this.helper[0].style.top=this.position.top+"px"}if(a.ui.ddmanager){a.ui.ddmanager.drag(this,b)}return false},_mouseStop:function(c){var d=false;if(a.ui.ddmanager&&!this.options.dropBehaviour){d=a.ui.ddmanager.drop(this,c)}if(this.dropped){d=this.dropped;this.dropped=false}if((this.options.revert=="invalid"&&!d)||(this.options.revert=="valid"&&d)||this.options.revert===true||(a.isFunction(this.options.revert)&&this.options.revert.call(this.element,d))){var b=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){b._trigger("stop",c);b._clear()})}else{this._trigger("stop",c);this._clear()}return false},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?true:false;a(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==b.target){c=true}});return c},_createHelper:function(c){var d=this.options;var b=a.isFunction(d.helper)?a(d.helper.apply(this.element[0],[c])):(d.helper=="clone"?this.element.clone():this.element);if(!b.parents("body").length){b.appendTo((d.appendTo=="parent"?this.element[0].parentNode:d.appendTo))}if(b[0]!=this.element[0]&&!(/(fixed|absolute)/).test(b.css("position"))){b.css("position","absolute")}return b},_adjustOffsetFromHelper:function(b){if(b.left!=undefined){this.offset.click.left=b.left+this.margins.left}if(b.right!=undefined){this.offset.click.left=this.helperProportions.width-b.right+this.margins.left}if(b.top!=undefined){this.offset.click.top=b.top+this.margins.top}if(b.bottom!=undefined){this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top}},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])){b.left+=this.scrollParent.scrollLeft();b.top+=this.scrollParent.scrollTop()}if((this.offsetParent[0]==document.body&&a.browser.mozilla)||(this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)){b={top:0,left:0}}return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var b=this.element.position();return{top:b.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:b.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else{return{top:0,left:0}}},_cacheMargins:function(){this.margins={left:(parseInt(this.element.css("marginLeft"),10)||0),top:(parseInt(this.element.css("marginTop"),10)||0)}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e=this.options;if(e.containment=="parent"){e.containment=this.helper[0].parentNode}if(e.containment=="document"||e.containment=="window"){this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(e.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(e.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]}if(!(/^(document|window|parent)$/).test(e.containment)&&e.containment.constructor!=Array){var c=a(e.containment)[0];if(!c){return}var d=a(e.containment).offset();var b=(a(c).css("overflow")!="hidden");this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(b?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(b?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}else{if(e.containment.constructor==Array){this.containment=e.containment}}},_convertPositionTo:function(f,h){if(!h){h=this.position}var c=f=="absolute"?1:-1;var e=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=(/(html|body)/i).test(b[0].tagName);return{top:(h.top+this.offset.relative.top*c+this.offset.parent.top*c-(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():(g?0:b.scrollTop()))*c),left:(h.left+this.offset.relative.left*c+this.offset.parent.left*c-(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:b.scrollLeft())*c)}},_generatePosition:function(e){var h=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,i=(/(html|body)/i).test(b[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0])){this.offset.relative=this._getRelativeOffset()}var d=e.pageX;var c=e.pageY;if(this.originalPosition){if(this.containment){if(e.pageX-this.offset.click.leftthis.containment[2]){d=this.containment[2]+this.offset.click.left}if(e.pageY-this.offset.click.top>this.containment[3]){c=this.containment[3]+this.offset.click.top}}if(h.grid){var g=this.originalPageY+Math.round((c-this.originalPageY)/h.grid[1])*h.grid[1];c=this.containment?(!(g-this.offset.click.topthis.containment[3])?g:(!(g-this.offset.click.topthis.containment[2])?f:(!(f-this.offset.click.left').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1000}).css(a(this).offset()).appendTo("body")})},stop:function(b,c){a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});a.ui.plugin.add("draggable","opacity",{start:function(c,d){var b=a(d.helper),e=a(this).data("draggable").options;if(b.css("opacity")){e._opacity=b.css("opacity")}b.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._opacity){a(c.helper).css("opacity",d._opacity)}}});a.ui.plugin.add("draggable","scroll",{start:function(c,d){var b=a(this).data("draggable");if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){b.overflowOffset=b.scrollParent.offset()}},drag:function(d,e){var c=a(this).data("draggable"),f=c.options,b=false;if(c.scrollParent[0]!=document&&c.scrollParent[0].tagName!="HTML"){if(!f.axis||f.axis!="x"){if((c.overflowOffset.top+c.scrollParent[0].offsetHeight)-d.pageY=0;v--){var s=g.snapElements[v].left,n=s+g.snapElements[v].width,m=g.snapElements[v].top,A=m+g.snapElements[v].height;if(!((s-y').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent();this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});if(b.browser.safari&&h.preventDefault){this.originalElement.css("resize","none")}this.proportionallyResize.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=h.handles||(!b(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var j=this.handles.split(",");this.handles={};for(var e=0;e');if(/sw|se|ne|nw/.test(g)){f.css({zIndex:++h.zIndex})}if("se"==g){f.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[g]=".ui-resizable-"+g;this.element.append(f)}}this._renderAxis=function(o){o=o||this.element;for(var l in this.handles){if(this.handles[l].constructor==String){this.handles[l]=b(this.handles[l],this.element).show()}if(h.transparent){this.handles[l].css({opacity:0})}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var m=b(this.handles[l],this.element),n=0;n=/sw|ne|nw|se|n|s/.test(l)?m.outerHeight():m.outerWidth();var k=["padding",/ne|nw|n/.test(l)?"Top":/se|sw|s/.test(l)?"Bottom":/^e$/.test(l)?"Right":"Left"].join("");if(!h.transparent){o.css(k,n)}this._proportionallyResize()}if(!b(this.handles[l]).length){continue}}};this._renderAxis(this.element);this._handles=b(".ui-resizable-handle",this.element);if(h.disableSelection){this._handles.disableSelection()}this._handles.mouseover(function(){if(!d.resizing){if(this.className){var i=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}d.axis=i&&i[1]?i[1]:"se"}});if(h.autoHide){this._handles.hide();b(this.element).addClass("ui-resizable-autohide").hover(function(){b(this).removeClass("ui-resizable-autohide");d._handles.show()},function(){if(!d.resizing){b(this).addClass("ui-resizable-autohide");d._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var c=function(d){b(d).removeClass("ui-resizable ui-resizable-disabled").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){c(this.element);this.wrapper.parent().append(this.originalElement.css({position:this.wrapper.css("position"),width:this.wrapper.outerWidth(),height:this.wrapper.outerHeight(),top:this.wrapper.css("top"),left:this.wrapper.css("left")})).end().remove()}c(this.originalElement)},_mouseCapture:function(d){var e=false;for(var c in this.handles){if(b(this.handles[c])[0]==d.target){e=true}}return this.options.disabled||!!e},_mouseStart:function(e){var h=this.options,d=this.element.position(),c=this.element;this.resizing=true;this.documentScroll={top:b(document).scrollTop(),left:b(document).scrollLeft()};if(c.is(".ui-draggable")||(/absolute/).test(c.css("position"))){c.css({position:"absolute",top:d.top,left:d.left})}if(b.browser.opera&&(/relative/).test(c.css("position"))){c.css({position:"relative",top:"auto",left:"auto"})}this._renderProxy();var i=a(this.helper.css("left")),f=a(this.helper.css("top"));if(h.containment){i+=b(h.containment).scrollLeft()||0;f+=b(h.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:i,top:f};this.size=this._helper?{width:c.outerWidth(),height:c.outerHeight()}:{width:c.width(),height:c.height()};this.originalSize=this._helper?{width:c.outerWidth(),height:c.outerHeight()}:{width:c.width(),height:c.height()};this.originalPosition={left:i,top:f};this.sizeDiff={width:c.outerWidth()-c.width(),height:c.outerHeight()-c.height()};this.originalMousePosition={left:e.pageX,top:e.pageY};this.aspectRatio=(typeof h.aspectRatio=="number")?h.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);if(h.preserveCursor){var g=b(".ui-resizable-"+this.axis).css("cursor");b("body").css("cursor",g=="auto"?this.axis+"-resize":g)}this._propagate("start",e);return true},_mouseDrag:function(c){var f=this.helper,e=this.options,k={},n=this,h=this.originalMousePosition,l=this.axis;var p=(c.pageX-h.left)||0,m=(c.pageY-h.top)||0;var g=this._change[l];if(!g){return false}var j=g.apply(this,[c,p,m]),i=b.browser.msie&&b.browser.version<7,d=this.sizeDiff;if(this._aspectRatio||c.shiftKey){j=this._updateRatio(j,c)}j=this._respectSize(j,c);this._propagate("resize",c);f.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!this._helper&&this.proportionallyResize.length){this._proportionallyResize()}this._updateCache(j);this._trigger("resize",c,this.ui());return false},_mouseStop:function(f){this.resizing=false;var g=this.options,k=this;if(this._helper){var e=this.proportionallyResize,c=e.length&&(/textarea/i).test(e[0].nodeName),d=c&&b.ui.hasScroll(e[0],"left")?0:k.sizeDiff.height,i=c?0:k.sizeDiff.width;var l={width:(k.size.width-i),height:(k.size.height-d)},h=(parseInt(k.element.css("left"),10)+(k.position.left-k.originalPosition.left))||null,j=(parseInt(k.element.css("top"),10)+(k.position.top-k.originalPosition.top))||null;if(!g.animate){this.element.css(b.extend(l,{top:j,left:h}))}if(this._helper&&!g.animate){this._proportionallyResize()}}if(g.preserveCursor){b("body").css("cursor","auto")}this._propagate("stop",f);if(this._helper){this.helper.remove()}return false},_updateCache:function(c){var d=this.options;this.offset=this.helper.offset();if(c.left){this.position.left=c.left}if(c.top){this.position.top=c.top}if(c.height){this.size.height=c.height}if(c.width){this.size.width=c.width}},_updateRatio:function(f,e){var g=this.options,h=this.position,d=this.size,c=this.axis;if(f.height){f.width=(d.height*this.aspectRatio)}else{if(f.width){f.height=(d.width/this.aspectRatio)}}if(c=="sw"){f.left=h.left+(d.width-f.width);f.top=null}if(c=="nw"){f.top=h.top+(d.height-f.height);f.left=h.left+(d.width-f.width)}return f},_respectSize:function(j,e){var r=function(o){return !isNaN(parseInt(o,10))};var h=this.helper,g=this.options,p=this._aspectRatio||e.shiftKey,n=this.axis,s=r(j.width)&&g.maxWidth&&(g.maxWidthj.width),q=r(j.height)&&g.minHeight&&(g.minHeight>j.height);if(f){j.width=g.minWidth}if(q){j.height=g.minHeight}if(s){j.width=g.maxWidth}if(k){j.height=g.maxHeight}var d=this.originalPosition.left+this.originalSize.width,m=this.position.top+this.size.height;var i=/sw|nw|w/.test(n),c=/nw|ne|n/.test(n);if(f&&i){j.left=d-g.minWidth}if(s&&i){j.left=d-g.maxWidth}if(q&&c){j.top=m-g.minHeight}if(k&&c){j.top=m-g.maxHeight}var l=!j.width&&!j.height;if(l&&!j.left&&j.top){j.top=null}else{if(l&&!j.top&&j.left){j.left=null}}return j},_proportionallyResize:function(){var h=this.options;if(!this.proportionallyResize.length){return}var e=this.helper||this.element;for(var d=0;d');var c=b.browser.msie&&b.browser.version<7,e=(c?1:0),f=(c?2:-1);this.helper.addClass(this._helper).css({width:this.element.outerWidth()+f,height:this.element.outerHeight()+f,position:"absolute",left:this.elementOffset.left-e+"px",top:this.elementOffset.top-e+"px",zIndex:++g.zIndex});this.helper.appendTo("body");if(g.disableSelection){this.helper.disableSelection()}}else{this.helper=this.element}},_change:{e:function(e,d,c){return{width:this.originalSize.width+d}},w:function(f,d,c){var h=this.options,e=this.originalSize,g=this.originalPosition;return{left:g.left+d,width:e.width-d}},n:function(f,d,c){var h=this.options,e=this.originalSize,g=this.originalPosition;return{top:g.top+c,height:e.height-c}},s:function(e,d,c){return{height:this.originalSize.height+c}},se:function(e,d,c){return b.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,d,c]))},sw:function(e,d,c){return b.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,d,c]))},ne:function(e,d,c){return b.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,d,c]))},nw:function(e,d,c){return b.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,d,c]))}},_propagate:function(d,c){b.ui.plugin.call(this,d,[c,this.ui()]);(d!="resize"&&this._trigger(d,c,this.ui()))},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}));b.extend(b.ui.resizable,{version:"1.6rc6",eventPrefix:"resize",defaults:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,cancel:":input,option",containment:false,delay:0,disableSelection:true,distance:1,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,preserveCursor:true,preventDefault:true,proportionallyResize:false,transparent:false,zIndex:1000}});b.ui.plugin.add("resizable","alsoResize",{start:function(d,e){var c=b(this).data("resizable"),f=c.options;_store=function(g){b(g).each(function(){b(this).data("resizable-alsoresize",{width:parseInt(b(this).width(),10),height:parseInt(b(this).height(),10),left:parseInt(b(this).css("left"),10),top:parseInt(b(this).css("top"),10)})})};if(typeof(f.alsoResize)=="object"&&!f.alsoResize.parentNode){if(f.alsoResize.length){f.alsoResize=f.alsoResize[0];_store(f.alsoResize)}else{b.each(f.alsoResize,function(g,h){_store(g)})}}else{_store(f.alsoResize)}},resize:function(e,g){var d=b(this).data("resizable"),h=d.options,f=d.originalSize,j=d.originalPosition;var i={height:(d.size.height-f.height)||0,width:(d.size.width-f.width)||0,top:(d.position.top-j.top)||0,left:(d.position.left-j.left)||0},c=function(k,l){b(k).each(function(){var o=b(this),p=b(this).data("resizable-alsoresize"),n={},m=l&&l.length?l:["width","height","top","left"];b.each(m||["width","height","top","left"],function(q,s){var r=(p[s]||0)+(i[s]||0);if(r&&r>=0){n[s]=r||null}});if(/relative/.test(o.css("position"))&&b.browser.opera){d._revertToRelativePosition=true;o.css({position:"absolute",top:"auto",left:"auto"})}o.css(n)})};if(typeof(h.alsoResize)=="object"&&!h.alsoResize.nodeType){b.each(h.alsoResize,function(k,l){c(k,l)})}else{c(h.alsoResize)}},stop:function(d,e){var c=b(this).data("resizable");if(c._revertToRelativePosition&&b.browser.opera){c._revertToRelativePosition=false;el.css({position:"relative"})}b(this).removeData("resizable-alsoresize-start")}});b.ui.plugin.add("resizable","animate",{stop:function(g,l){var m=b(this).data("resizable"),h=m.options;var f=h.proportionallyResize,c=f&&(/textarea/i).test(f.get(0).nodeName),d=c&&b.ui.hasScroll(f.get(0),"left")?0:m.sizeDiff.height,j=c?0:m.sizeDiff.width;var e={width:(m.size.width-j),height:(m.size.height-d)},i=(parseInt(m.element.css("left"),10)+(m.position.left-m.originalPosition.left))||null,k=(parseInt(m.element.css("top"),10)+(m.position.top-m.originalPosition.top))||null;m.element.animate(b.extend(e,k&&i?{top:k,left:i}:{}),{duration:h.animateDuration,easing:h.animateEasing,step:function(){var n={width:parseInt(m.element.css("width"),10),height:parseInt(m.element.css("height"),10),top:parseInt(m.element.css("top"),10),left:parseInt(m.element.css("left"),10)};if(f){f.css({width:n.width,height:n.height})}m._updateCache(n);m._propagate("resize",g)}})}});b.ui.plugin.add("resizable","containment",{start:function(d,n){var r=b(this).data("resizable"),h=r.options,j=r.element;var e=h.containment,i=(e instanceof b)?e.get(0):(/parent/.test(e))?j.parent().get(0):e;if(!i){return}r.containerElement=b(i);if(/document/.test(e)||e==document){r.containerOffset={left:0,top:0};r.containerPosition={left:0,top:0};r.parentData={element:b(document),left:0,top:0,width:b(document).width(),height:b(document).height()||document.body.parentNode.scrollHeight}}else{var l=b(i),g=[];b(["Top","Right","Left","Bottom"]).each(function(p,o){g[p]=a(l.css("padding"+o))});r.containerOffset=l.offset();r.containerPosition=l.position();r.containerSize={height:(l.innerHeight()-g[3]),width:(l.innerWidth()-g[1])};var m=r.containerOffset,c=r.containerSize.height,k=r.containerSize.width,f=(b.ui.hasScroll(i,"left")?i.scrollWidth:k),q=(b.ui.hasScroll(i)?i.scrollHeight:c);r.parentData={element:i,left:m.left,top:m.top,width:f,height:q}}},resize:function(e,l){var p=b(this).data("resizable"),g=p.options,d=p.containerSize,k=p.containerOffset,i=p.size,j=p.position,m=g._aspectRatio||e.shiftKey,c={top:0,left:0},f=p.containerElement;if(f[0]!=document&&(/static/).test(f.css("position"))){c=k}if(j.left<(p._helper?k.left:0)){p.size.width=p.size.width+(p._helper?(p.position.left-k.left):(p.position.left-c.left));if(m){p.size.height=p.size.width/g.aspectRatio}p.position.left=g.helper?k.left:0}if(j.top<(p._helper?k.top:0)){p.size.height=p.size.height+(p._helper?(p.position.top-k.top):p.position.top);if(m){p.size.width=p.size.height*g.aspectRatio}p.position.top=p._helper?k.top:0}var h=Math.abs((p._helper?p.offset.left-c.left:(p.offset.left-c.left))+p.sizeDiff.width),n=Math.abs((p._helper?p.offset.top-c.top:(p.offset.top-k.top))+p.sizeDiff.height);if(h+p.size.width>=p.parentData.width){p.size.width=p.parentData.width-h;if(m){p.size.height=p.size.width/g.aspectRatio}}if(n+p.size.height>=p.parentData.height){p.size.height=p.parentData.height-n;if(m){p.size.width=p.size.height*g.aspectRatio}}},stop:function(d,l){var n=b(this).data("resizable"),e=n.options,j=n.position,k=n.containerOffset,c=n.containerPosition,f=n.containerElement;var g=b(n.helper),p=g.offset(),m=g.outerWidth()-n.sizeDiff.width,i=g.outerHeight()-n.sizeDiff.height;if(n._helper&&!e.animate&&(/relative/).test(f.css("position"))){b(this).css({left:p.left-c.left-k.left,width:m,height:i})}if(n._helper&&!e.animate&&(/static/).test(f.css("position"))){b(this).css({left:p.left-c.left-k.left,width:m,height:i})}}});b.ui.plugin.add("resizable","ghost",{start:function(e,f){var c=b(this).data("resizable"),g=c.options,h=g.proportionallyResize,d=c.size;c.ghost=c.originalElement.clone();c.ghost.css({opacity:0.25,display:"block",position:"relative",height:d.height,width:d.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof g.ghost=="string"?g.ghost:"");c.ghost.appendTo(c.helper)},resize:function(d,e){var c=b(this).data("resizable"),f=c.options;if(c.ghost){c.ghost.css({position:"relative",height:c.size.height,width:c.size.width})}},stop:function(d,e){var c=b(this).data("resizable"),f=c.options;if(c.ghost&&c.helper){c.helper.get(0).removeChild(c.ghost.get(0))}}});b.ui.plugin.add("resizable","grid",{resize:function(c,k){var m=b(this).data("resizable"),f=m.options,i=m.size,g=m.originalSize,h=m.originalPosition,l=m.axis,j=f._aspectRatio||c.shiftKey;f.grid=typeof f.grid=="number"?[f.grid,f.grid]:f.grid;var e=Math.round((i.width-g.width)/(f.grid[0]||1))*(f.grid[0]||1),d=Math.round((i.height-g.height)/(f.grid[1]||1))*(f.grid[1]||1);if(/^(se|s|e)$/.test(l)){m.size.width=g.width+e;m.size.height=g.height+d}else{if(/^(ne)$/.test(l)){m.size.width=g.width+e;m.size.height=g.height+d;m.position.top=h.top-d}else{if(/^(sw)$/.test(l)){m.size.width=g.width+e;m.size.height=g.height+d;m.position.left=h.left-e}else{m.size.width=g.width+e;m.size.height=g.height+d;m.position.top=h.top-d;m.position.left=h.left-e}}}}});var a=function(c){return parseInt(c,10)||0}})(jQuery);;/* + * jQuery UI Progressbar 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * ui.core.js + */ (function(a){a.widget("ui.progressbar",{_init:function(){var b=this,c=this.options;this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this._valueMin(),"aria-valuemax":this._valueMax(),"aria-valuenow":this._value()});this.valueDiv=a('
').appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow").removeData("progressbar").unbind(".progressbar");this.valueDiv.remove();a.widget.prototype.destroy.apply(this,arguments)},value:function(b){arguments.length&&this._setData("value",b);return this._value()},_setData:function(b,c){switch(b){case"value":this.options.value=c;this._refreshValue();this._trigger("change",null,{});break}a.widget.prototype._setData.apply(this,arguments)},_value:function(){var b=this.options.value;if(bthis._valueMax()){b=this._valueMax()}return b},_valueMin:function(){var b=0;return b},_valueMax:function(){var b=100;return b},_refreshValue:function(){var b=this.value();this.valueDiv[b==this._valueMax()?"addClass":"removeClass"]("ui-corner-right");this.valueDiv.width(b+"%");this.element.attr("aria-valuenow",b)}});a.extend(a.ui.progressbar,{version:"1.6rc6",defaults:{value:0}})})(jQuery);;/* + * jQuery UI Effects 1.6rc6 + * + * Copyright (c) 2009 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/ + */ (function(d){d.effects=d.effects||{};d.extend(d.effects,{version:"1.6rc6",save:function(g,h){for(var f=0;f');var j=f.parent();if(f.css("position")=="static"){j.css({position:"relative"});f.css({position:"relative"})}else{var i=f.css("top");if(isNaN(parseInt(i,10))){i="auto"}var h=f.css("left");if(isNaN(parseInt(h,10))){h="auto"}j.css({position:f.css("position"),top:i,left:h,zIndex:f.css("z-index")}).show();f.css({position:"relative",top:0,left:0})}j.css(g);return j},removeWrapper:function(f){if(f.parent().is(".ui-effects-wrapper")){return f.parent().replaceWith(f)}return f},setTransition:function(g,i,f,h){h=h||{};d.each(i,function(k,j){unit=g.cssUnit(j);if(unit[0]>0){h[j]=unit[0]*f+unit[1]}});return h},animateClass:function(h,i,k,j){var f=(typeof k=="function"?k:(j?j:null));var g=(typeof k=="string"?k:null);return this.each(function(){var q={};var o=d(this);var p=o.attr("style")||"";if(typeof p=="object"){p=p.cssText}if(h.toggle){o.hasClass(h.toggle)?h.remove=h.toggle:h.add=h.toggle}var l=d.extend({},(document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle));if(h.add){o.addClass(h.add)}if(h.remove){o.removeClass(h.remove)}var m=d.extend({},(document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle));if(h.add){o.removeClass(h.add)}if(h.remove){o.addClass(h.remove)}for(var r in m){if(typeof m[r]!="function"&&m[r]&&r.indexOf("Moz")==-1&&r.indexOf("length")==-1&&m[r]!=l[r]&&(r.match(/color/i)||(!r.match(/color/i)&&!isNaN(parseInt(m[r],10))))&&(l.position!="static"||(l.position=="static"&&!r.match(/left|top|bottom|right/)))){q[r]=m[r]}}o.animate(q,i,g,function(){if(typeof d(this).attr("style")=="object"){d(this).attr("style")["cssText"]="";d(this).attr("style")["cssText"]=p}else{d(this).attr("style",p)}if(h.add){d(this).addClass(h.add)}if(h.remove){d(this).removeClass(h.remove)}if(f){f.apply(this,arguments)}})})}});function c(g,f){var i=g[1]&&g[1].constructor==Object?g[1]:{};if(f){i.mode=f}var h=g[1]&&g[1].constructor!=Object?g[1]:i.duration;h=d.fx.off?0:typeof h==="number"?h:d.fx.speeds[h]||d.fx.speeds._default;var j=i.callback||(d.isFunction(g[2])&&g[2])||(d.isFunction(g[3])&&g[3]);return[g[0],i,h,j]}d.fn.extend({_show:d.fn.show,_hide:d.fn.hide,__toggle:d.fn.toggle,_addClass:d.fn.addClass,_removeClass:d.fn.removeClass,_toggleClass:d.fn.toggleClass,effect:function(g,f,h,i){return d.effects[g]?d.effects[g].call(this,{method:g,options:f||{},duration:h,callback:i}):null},show:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))){return this._show.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"show"))}},hide:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))){return this._hide.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"hide"))}},toggle:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))||(arguments[0].constructor==Function)){return this.__toggle.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"toggle"))}},addClass:function(g,f,i,h){return f?d.effects.animateClass.apply(this,[{add:g},f,i,h]):this._addClass(g)},removeClass:function(g,f,i,h){return f?d.effects.animateClass.apply(this,[{remove:g},f,i,h]):this._removeClass(g)},toggleClass:function(g,f,i,h){return((typeof f!=="boolean")&&f)?d.effects.animateClass.apply(this,[{toggle:g},f,i,h]):this._toggleClass(g,f)},morph:function(f,h,g,j,i){return d.effects.animateClass.apply(this,[{add:h,remove:f},g,j,i])},switchClass:function(){return this.morph.apply(this,arguments)},cssUnit:function(f){var g=this.css(f),h=[];d.each(["em","px","%","pt"],function(j,k){if(g.indexOf(k)>0){h=[parseFloat(g),k]}});return h}});d.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(g,f){d.fx.step[f]=function(h){if(h.state==0){h.start=e(h.elem,f);h.end=b(h.end)}h.elem.style[f]="rgb("+[Math.max(Math.min(parseInt((h.pos*(h.end[0]-h.start[0]))+h.start[0],10),255),0),Math.max(Math.min(parseInt((h.pos*(h.end[1]-h.start[1]))+h.start[1],10),255),0),Math.max(Math.min(parseInt((h.pos*(h.end[2]-h.start[2]))+h.start[2],10),255),0)].join(",")+")"}});function b(g){var f;if(g&&g.constructor==Array&&g.length==3){return g}if(f=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(g)){return[parseInt(f[1],10),parseInt(f[2],10),parseInt(f[3],10)]}if(f=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(g)){return[parseFloat(f[1])*2.55,parseFloat(f[2])*2.55,parseFloat(f[3])*2.55]}if(f=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(g)){return[parseInt(f[1],16),parseInt(f[2],16),parseInt(f[3],16)]}if(f=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(g)){return[parseInt(f[1]+f[1],16),parseInt(f[2]+f[2],16),parseInt(f[3]+f[3],16)]}if(f=/rgba\(0, 0, 0, 0\)/.exec(g)){return a.transparent}return a[d.trim(g).toLowerCase()]}function e(h,f){var g;do{g=d.curCSS(h,f);if(g!=""&&g!="transparent"||d.nodeName(h,"body")){break}f="backgroundColor"}while(h=h.parentNode);return b(g)}var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]};d.easing.jswing=d.easing.swing;d.extend(d.easing,{def:"easeOutQuad",swing:function(g,h,f,j,i){return d.easing[d.easing.def](g,h,f,j,i)},easeInQuad:function(g,h,f,j,i){return j*(h/=i)*h+f},easeOutQuad:function(g,h,f,j,i){return -j*(h/=i)*(h-2)+f},easeInOutQuad:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h+f}return -j/2*((--h)*(h-2)-1)+f},easeInCubic:function(g,h,f,j,i){return j*(h/=i)*h*h+f},easeOutCubic:function(g,h,f,j,i){return j*((h=h/i-1)*h*h+1)+f},easeInOutCubic:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h+f}return j/2*((h-=2)*h*h+2)+f},easeInQuart:function(g,h,f,j,i){return j*(h/=i)*h*h*h+f},easeOutQuart:function(g,h,f,j,i){return -j*((h=h/i-1)*h*h*h-1)+f},easeInOutQuart:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h*h+f}return -j/2*((h-=2)*h*h*h-2)+f},easeInQuint:function(g,h,f,j,i){return j*(h/=i)*h*h*h*h+f},easeOutQuint:function(g,h,f,j,i){return j*((h=h/i-1)*h*h*h*h+1)+f},easeInOutQuint:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h*h*h+f}return j/2*((h-=2)*h*h*h*h+2)+f},easeInSine:function(g,h,f,j,i){return -j*Math.cos(h/i*(Math.PI/2))+j+f},easeOutSine:function(g,h,f,j,i){return j*Math.sin(h/i*(Math.PI/2))+f},easeInOutSine:function(g,h,f,j,i){return -j/2*(Math.cos(Math.PI*h/i)-1)+f},easeInExpo:function(g,h,f,j,i){return(h==0)?f:j*Math.pow(2,10*(h/i-1))+f},easeOutExpo:function(g,h,f,j,i){return(h==i)?f+j:j*(-Math.pow(2,-10*h/i)+1)+f},easeInOutExpo:function(g,h,f,j,i){if(h==0){return f}if(h==i){return f+j}if((h/=i/2)<1){return j/2*Math.pow(2,10*(h-1))+f}return j/2*(-Math.pow(2,-10*--h)+2)+f},easeInCirc:function(g,h,f,j,i){return -j*(Math.sqrt(1-(h/=i)*h)-1)+f},easeOutCirc:function(g,h,f,j,i){return j*Math.sqrt(1-(h=h/i-1)*h)+f},easeInOutCirc:function(g,h,f,j,i){if((h/=i/2)<1){return -j/2*(Math.sqrt(1-h*h)-1)+f}return j/2*(Math.sqrt(1-(h-=2)*h)+1)+f},easeInElastic:function(g,i,f,m,l){var j=1.70158;var k=0;var h=m;if(i==0){return f}if((i/=l)==1){return f+m}if(!k){k=l*0.3}if(h= 0) webkitAvailable = false; + } catch(err) {} + var mozillaAvailable = false; + try { + mozillaAvailable = (document.body.style.MozBorderRadius !== undefined); + /* Firefox 2 corners look worse */ + var versionIndex = navigator.userAgent.indexOf('Firefox'); + if (versionIndex >= 0 && parseInt(navigator.userAgent.substring(versionIndex+8)) < 3) mozillaAvailable = false; + } catch(err) {} + return this.each(function(i,e){ + $e = jQuery(e); + if ($e.hasClass(doneClass)) return; + $e.addClass(doneClass); + var classScan = /{(.*)}/.exec(e.className); + var s = classScan ? parseOptions(classScan[1], settings) : settings; + var nodeName = e.nodeName.toLowerCase(); + if (nodeName=='input') e = changeInput(e); + if (webkitAvailable && s.webkit) roundWebkit(e, s); + else if(mozillaAvailable && s.mozilla && (s.sizex == s.sizey)) roundMozilla(e, s); + else { + var bgColor = backgroundColor(e.parentNode); + var fgColor = backgroundColor(e); + switch (nodeName) { + case 'a': + case 'input': + roundLink(e, s, bgColor, fgColor); + break; + default: + roundDiv(e, s, bgColor, fgColor); + break; + } + } + }); + + function roundWebkit(e, s) { + var radius = '' + s.sizex + 'px ' + s.sizey + 'px'; + var $e = jQuery(e); + if (s.tl) $e.css('WebkitBorderTopLeftRadius', radius); + if (s.tr) $e.css('WebkitBorderTopRightRadius', radius); + if (s.bl) $e.css('WebkitBorderBottomLeftRadius', radius); + if (s.br) $e.css('WebkitBorderBottomRightRadius', radius); + } + + function roundMozilla(e, s) + { + var radius = '' + s.sizex + 'px'; + var $e = jQuery(e); + if (s.tl) $e.css('-moz-border-radius-topleft', radius); + if (s.tr) $e.css('-moz-border-radius-topright', radius); + if (s.bl) $e.css('-moz-border-radius-bottomleft', radius); + if (s.br) $e.css('-moz-border-radius-bottomright', radius); + } + + function roundLink(e, s, bgColor, fgColor) { + var table = tableElement("table"); + var tbody = tableElement("tbody"); + table.appendChild(tbody); + var tr1 = tableElement("tr"); + var td1 = tableElement("td", "top"); + tr1.appendChild(td1); + var tr2 = tableElement("tr"); + var td2 = relocateContent(e, s, tableElement("td")); + tr2.appendChild(td2); + var tr3 = tableElement("tr"); + var td3 = tableElement("td", "bottom"); + tr3.appendChild(td3); + if (s.tl||s.tr) { + tbody.appendChild(tr1); + addCorners(td1, s, bgColor, fgColor, true); + } + tbody.appendChild(tr2); + if (s.bl||s.br) { + tbody.appendChild(tr3); + addCorners(td3, s, bgColor, fgColor, false); + } + e.appendChild(table); + /* Clicking on $('a>table') in IE will trigger onclick but not the href */ + if (jQuery.browser.msie) table.onclick = ieLinkBypass; + /* Firefox 2 will render garbage unless we hide the overflow here */ + e.style.overflow = 'hidden'; + } + + function ieLinkBypass() { + if (!this.parentNode.onclick) this.parentNode.click(); + } + + function changeInput(e) { + var a1 = document.createElement("a"); + a1.id = e.id; + a1.className = e.className; + if (e.onclick) { + a1.href = 'javascript:' + a1.onclick = e.onclick; + } else { + jQuery(e).parent('form').each(function() {a1.href = this.action;}); + a1.onclick = submitForm; + } + var a2 = document.createTextNode(e.value); + a1.appendChild(a2); + e.parentNode.replaceChild(a1, e); + return a1; + } + + function submitForm() { + jQuery(this).parent('form').each(function() {this.submit()}); + return false; + } + + function roundDiv(e, s, bgColor, fgColor) { + var div = relocateContent(e, s, document.createElement('div')); + e.appendChild(div); + if (s.tl||s.tr) addCorners(e, s, bgColor, fgColor, true); + if (s.bl||s.br) addCorners(e, s, bgColor, fgColor, false); + } + + function relocateContent(e, s, d) { + var $e = jQuery(e); + var c; + while(c=e.firstChild) d.appendChild(c); + if (e.style.height) { + var h = parseInt($e.css('height')); + d.style.height = h + 'px'; + h += parseInt($e.css('padding-top')) + parseInt($e.css('padding-bottom')); + e.style.height = h + 'px'; + } + if (e.style.width) { + var w = parseInt($e.css('width')); + d.style.width = w + 'px'; + w += parseInt($e.css('padding-left')) + parseInt($e.css('padding-right')); + e.style.width = w + 'px'; + } + d.style.paddingLeft = $e.css('padding-left'); + d.style.paddingRight = $e.css('padding-right'); + if (s.tl||s.tr) { + d.style.paddingTop = adjustedPadding(e, s, $e.css('padding-top'), true); + } else { + d.style.paddingTop = $e.css('padding-top'); + } + if (s.bl||s.br) { + d.style.paddingBottom = adjustedPadding(e, s, $e.css('padding-bottom'), false); + } else { + d.style.paddingBottom = $e.css('padding-bottom'); + } + e.style.padding = 0; + return d; + } + + function adjustedPadding(e, s, pad, top) { + if (pad.indexOf("px") < 0) { + try { + //TODO Make this check work otherwise remove it + console.error('%s padding not in pixels', (top ? 'top' : 'bottom'), e); + } + catch(err) {} + pad = s.sizey + 'px'; + } + pad = parseInt(pad); + if (pad - s.sizey < 0) { + try { + console.error('%s padding is %ipx for %ipx corner:', (top ? 'top' : 'bottom'), pad, s.sizey, e); + } + catch(err) {} + pad = s.sizey; + } + return pad - s.sizey + 'px'; + } + + function tableElement(kind, valign) { + var e = document.createElement(kind) + e.style.border = 'none'; + e.style.borderCollapse = 'collapse'; + e.style.borderSpacing = 0; + e.style.padding = 0; + e.style.margin = 0; + if (valign) e.style.verticalAlign = valign; + return e; + } + + function backgroundColor(e) { + try { + var c = jQuery.css(e, "background-color"); + if ( c.match(/^(transparent|rgba\(0,\s*0,\s*0,\s*0\))$/i) && e.parentNode ) + return backgroundColor(e.parentNode); + if (c==null) + return "#ffffff"; + if (c.indexOf("rgb") > -1) + c = rgb2hex(c); + if (c.length == 4) + c = hexShort2hex(c); + return c; + } catch(err) { + return "#ffffff"; + } + } + + function hexShort2hex(c) { + return '#' + + c.substring(1,2) + + c.substring(1,2) + + c.substring(2,3) + + c.substring(2,3) + + c.substring(3,4) + + c.substring(3,4); + } + + function rgb2hex(c) { + var x = 255; + var hex = ''; + var i; + var regexp=/([0-9]+)[, ]+([0-9]+)[, ]+([0-9]+)/; + var array=regexp.exec(c); + for(i=1;i<4;i++) hex += ('0'+parseInt(array[i]).toString(16)).slice(-2); + return '#'+hex; + } + + function parseOptions(options, settings) { + var options = options || ''; + var s = {sizex:5, sizey:5, tl: false, tr: false, bl: false, br: false, webkit:true, mozilla: true, transparent:false}; + if (settings) { + s.sizex = settings.sizex; + s.sizey = settings.sizey; + s.webkit = settings.webkit; + s.transparent = settings.transparent; + s.mozilla = settings.mozilla; + } + var sizex_set = false; + var corner_set = false; + jQuery.each(options.split(' '), function(idx, option) { + option = option.toLowerCase(); + var i = parseInt(option); + if (i > 0 && option == i + 'px') { + s.sizey = i; + if (!sizex_set) s.sizex = i; + sizex_set = true; + } else switch (option) { + case 'no-native': s.webkit = s.mozilla = false; break; + case 'webkit': s.webkit = true; break; + case 'no-webkit': s.webkit = false; break; + case 'mozilla': s.mozilla = true; break; + case 'no-mozilla': s.mozilla = false; break; + case 'anti-alias': s.transparent = false; break; + case 'transparent': s.transparent = true; break; + case 'top': corner_set = s.tl = s.tr = true; break; + case 'right': corner_set = s.tr = s.br = true; break; + case 'bottom': corner_set = s.bl = s.br = true; break; + case 'left': corner_set = s.tl = s.bl = true; break; + case 'top-left': corner_set = s.tl = true; break; + case 'top-right': corner_set = s.tr = true; break; + case 'bottom-left': corner_set = s.bl = true; break; + case 'bottom-right': corner_set = s.br = true; break; + } + }); + if (!corner_set) { + if (!settings) { + s.tl = s.tr = s.bl = s.br = true; + } else { + s.tl = settings.tl; + s.tr = settings.tr; + s.bl = settings.bl; + s.br = settings.br; + } + } + return s; + } + + function alphaBlend(a, b, alpha) { + var ca = Array( + parseInt('0x' + a.substring(1, 3)), + parseInt('0x' + a.substring(3, 5)), + parseInt('0x' + a.substring(5, 7)) + ); + var cb = Array( + parseInt('0x' + b.substring(1, 3)), + parseInt('0x' + b.substring(3, 5)), + parseInt('0x' + b.substring(5, 7)) + ); + r = '0' + Math.round(ca[0] + (cb[0] - ca[0])*alpha).toString(16); + g = '0' + Math.round(ca[1] + (cb[1] - ca[1])*alpha).toString(16); + b = '0' + Math.round(ca[2] + (cb[2] - ca[2])*alpha).toString(16); + return '#' + + r.substring(r.length - 2) + + g.substring(g.length - 2) + + b.substring(b.length - 2); + } + + function addCorners(e, s, bgColor, fgColor, top) { + if (s.transparent) addTransparentCorners(e, s, bgColor, top); + else addAntiAliasedCorners(e, s, bgColor, fgColor, top); + } + + function addAntiAliasedCorners(e, s, bgColor, fgColor, top) { + var i, j; + var d = document.createElement("div"); + d.style.fontSize = '1px'; + d.style.backgroundColor = bgColor; + var lastarc = 0; + for (i = 1; i <= s.sizey; i++) { + var coverage, arc2, arc3; + // Find intersection of arc with bottom of pixel row + arc = Math.sqrt(1.0 - Math.pow(1.0 - i / s.sizey, 2)) * s.sizex; + // Calculate how many pixels are bg, fg and blended. + var n_bg = s.sizex - Math.ceil(arc); + var n_fg = Math.floor(lastarc); + var n_aa = s.sizex - n_bg - n_fg; + // Create pixel row wrapper + var x = document.createElement("div"); + var y = d; + x.style.margin = "0px " + n_bg + "px"; + x.style.height = '1px'; + x.style.overflow = 'hidden'; + // Create the pixel divs for a row (at least one) + for (j = 1; j <= n_aa; j++) { + // Calculate coverage per pixel (approximates arc within the pixel) + if (j == 1) { + if (j == n_aa) { + // Single pixel + coverage = ((arc + lastarc) * .5) - n_fg; + } + else { + // First in a run + arc2 = Math.sqrt(1.0 - Math.pow(1.0 - (n_bg + 1) / s.sizex, 2)) * s.sizey; + coverage = (arc2 - (s.sizey - i)) * (arc - n_fg - n_aa + 1) * .5; + } + } + else if (j == n_aa) { + // Last in a run + arc2 = Math.sqrt(1.0 - Math.pow((s.sizex - n_bg - j + 1) / s.sizex, 2)) * s.sizey; + coverage = 1.0 - (1.0 - (arc2 - (s.sizey - i))) * (1.0 - (lastarc - n_fg)) * .5; + } + else { + // Middle of a run + arc3 = Math.sqrt(1.0 - Math.pow((s.sizex - n_bg - j) / s.sizex, 2)) * s.sizey; + arc2 = Math.sqrt(1.0 - Math.pow((s.sizex - n_bg - j + 1) / s.sizex, 2)) * s.sizey; + coverage = ((arc2 + arc3) * .5) - (s.sizey - i); + } + + addCornerDiv(s, x, y, top, alphaBlend(bgColor, fgColor, coverage)); + y = x; + var x = y.cloneNode(false); + x.style.margin = "0px 1px"; + } + addCornerDiv(s, x, y, top, fgColor); + lastarc = arc; + } + if (top) + e.insertBefore(d, e.firstChild); + else + e.appendChild(d); + } + + function addCornerDiv(s, x, y, top, color) { + if (top && !s.tl) x.style.marginLeft = 0; + if (top && !s.tr) x.style.marginRight = 0; + if (!top && !s.bl) x.style.marginLeft = 0; + if (!top && !s.br) x.style.marginRight = 0; + x.style.backgroundColor = color; + if (top) + y.appendChild(x); + else + y.insertBefore(x, y.firstChild); + } + + function addTransparentCorners(e, s, bgColor, top) { + var d = document.createElement("div"); + d.style.fontSize = '1px'; + var strip = document.createElement('div'); + strip.style.overflow = 'hidden'; + strip.style.height = '1px'; + strip.style.borderColor = bgColor; + strip.style.borderStyle = 'none solid'; + var sizex = s.sizex-1; + var sizey = s.sizey-1; + if (!sizey) sizey = 1; /* hint for 1x1 */ + for (var i=0; i < s.sizey; i++) { + var w = sizex - Math.floor(Math.sqrt(1.0 - Math.pow(1.0 - i / sizey, 2)) * sizex); + if (i==2 && s.sizex==6 && s.sizey==6) w = 2; /* hint for 6x6 */ + var x = strip.cloneNode(false); + x.style.borderWidth = '0 '+ w +'px'; + if (top) x.style.borderWidth = '0 '+(s.tr?w:0)+'px 0 '+(s.tl?w:0)+'px'; + else x.style.borderWidth = '0 '+(s.br?w:0)+'px 0 '+(s.bl?w:0)+'px'; + top ? d.appendChild(x) : d.insertBefore(x, d.firstChild); + } + if (top) + e.insertBefore(d, e.firstChild); + else + e.appendChild(d); + } + + +} diff --git a/media/js/jquery.hotkeys.js b/media/js/jquery.hotkeys.js new file mode 100644 index 000000000..15f1ce438 --- /dev/null +++ b/media/js/jquery.hotkeys.js @@ -0,0 +1,244 @@ +/* +(c) Copyrights 2007 - 2008 + +Original idea by by Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ + +jQuery Plugin by Tzury Bar Yochay +tzury.by@gmail.com +http://evalinux.wordpress.com +http://facebook.com/profile.php?id=513676303 + +Project's sites: +http://code.google.com/p/js-hotkeys/ +http://github.com/tzuryby/hotkeys/tree/master + +License: same as jQuery license. + +USAGE: + // simple usage + $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');}); + + // special options such as disableInIput + $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {}); + +Note: + This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind + +*/ + + +(function (jQuery){ + // keep reference to the original $.fn.bind and $.fn.unbind + jQuery.fn.__bind__ = jQuery.fn.bind; + jQuery.fn.__unbind__ = jQuery.fn.unbind; + jQuery.fn.__find__ = jQuery.fn.find; + + var hotkeys = { + version: '0.7.8', + override: /keydown|keypress|keyup/g, + triggersMap: {}, + + specialKeys: { 27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll', + 20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del', + 35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down', + 112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8', + 120:'f9', 121:'f10', 122:'f11', 123:'f12' }, + + shiftNums: { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&", + "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<", + ".":">", "/":"?", "\\":"|" }, + + newTrigger: function (type, combi, callback) { + // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}} + var result = {}; + result[type] = {}; + result[type][combi] = {cb: callback, disableInInput: false}; + return result; + } + }; + // add firefox num pad char codes + if (jQuery.browser.mozilla){ + hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, { 96: '0', 97:'1', 98: '2', 99: + '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9' }); + } + + // a wrapper around of $.fn.find + // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d + jQuery.fn.find = function( selector ) { + this.query=selector; + return jQuery.fn.__find__.apply(this, arguments); + }; + + jQuery.fn.unbind = function (type, combi, fn){ + if (jQuery.isFunction(combi)){ + fn = combi; + combi = null; + } + if (combi && typeof combi === 'string'){ + var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString(); + var hkTypes = type.split(' '); + for (var x=0; x)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this[0] = selector; + this.length = 1; + this.context = selector; + return this; + } + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + var match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) + selector = jQuery.clean( [ match[1] ], context ); + + // HANDLE: $("#id") + else { + var elem = document.getElementById( match[3] ); + + // Make sure an element was located + if ( elem ){ + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id != match[3] ) + return jQuery().find( selector ); + + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery( elem ); + ret.context = document; + ret.selector = selector; + return ret; + } + selector = []; + } + + // HANDLE: $(expr, [context]) + // (which is just equivalent to: $(content).find(expr) + } else + return jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) + return jQuery( document ).ready( selector ); + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return this.setArray(jQuery.makeArray(selector)); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.3", + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num === undefined ? + + // Return a 'clean' array + jQuery.makeArray( this ) : + + // Return just the object + this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = jQuery( elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) + ret.selector = this.selector + (this.selector ? " " : "") + selector; + else if ( name ) + ret.selector = this.selector + "." + name + "(" + selector + ")"; + + // Return the newly-formed element set + return ret; + }, + + // Force the current matched set of elements to become + // the specified array of elements (destroying the stack in the process) + // You should use pushStack() in order to do this, but maintain the stack + setArray: function( elems ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + this.length = 0; + Array.prototype.push.apply( this, elems ); + + return this; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem && elem.jquery ? elem[0] : elem + , this ); + }, + + attr: function( name, value, type ) { + var options = name; + + // Look for the case where we're accessing a style value + if ( typeof name === "string" ) + if ( value === undefined ) + return this[0] && jQuery[ type || "attr" ]( this[0], name ); + + else { + options = {}; + options[ name ] = value; + } + + // Check to see if we're setting style values + return this.each(function(i){ + // Set all the styles + for ( name in options ) + jQuery.attr( + type ? + this.style : + this, + name, jQuery.prop( this, options[ name ], type, i, name ) + ); + }); + }, + + css: function( key, value ) { + // ignore negative width and height values + if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) + value = undefined; + return this.attr( key, value, "curCSS" ); + }, + + text: function( text ) { + if ( typeof text !== "object" && text != null ) + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + + var ret = ""; + + jQuery.each( text || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + ret += this.nodeType != 1 ? + this.nodeValue : + jQuery.fn.text( [ this ] ); + }); + }); + + return ret; + }, + + wrapAll: function( html ) { + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).clone(); + + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); + + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } + + return this; + }, + + wrapInner: function( html ) { + return this.each(function(){ + jQuery( this ).contents().wrapAll( html ); + }); + }, + + wrap: function( html ) { + return this.each(function(){ + jQuery( this ).wrapAll( html ); + }); + }, + + append: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.appendChild( elem ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery( [] ); + }, + + // For internal use only. + // Behaves like an Array's .push method, not like a jQuery method. + push: [].push, + + find: function( selector ) { + if ( this.length === 1 && !/,/.test(selector) ) { + var ret = this.pushStack( [], "find", selector ); + ret.length = 0; + jQuery.find( selector, this[0], ret ); + return ret; + } else { + var elems = jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + }); + + return this.pushStack( /[^+>] [^+>]/.test( selector ) ? + jQuery.unique( elems ) : + elems, "find", selector ); + } + }, + + clone: function( events ) { + // Do the clone + var ret = this.map(function(){ + if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // attributes in IE that are actually only stored + // as properties will not be copied (such as the + // the name attribute on an input). + var clone = this.cloneNode(true), + container = document.createElement("div"); + container.appendChild(clone); + return jQuery.clean([container.innerHTML])[0]; + } else + return this.cloneNode(true); + }); + + // Need to set the expando to null on the cloned set if it exists + // removeData doesn't work here, IE removes it from the original as well + // this is primarily for IE but the data expando shouldn't be copied over in any browser + var clone = ret.find("*").andSelf().each(function(){ + if ( this[ expando ] !== undefined ) + this[ expando ] = null; + }); + + // Copy the events from the original to the clone + if ( events === true ) + this.find("*").andSelf().each(function(i){ + if (this.nodeType == 3) + return; + var events = jQuery.data( this, "events" ); + + for ( var type in events ) + for ( var handler in events[ type ] ) + jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); + }); + + // Return the cloned set + return ret; + }, + + filter: function( selector ) { + return this.pushStack( + jQuery.isFunction( selector ) && + jQuery.grep(this, function(elem, i){ + return selector.call( elem, i ); + }) || + + jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ + return elem.nodeType === 1; + }) ), "filter", selector ); + }, + + closest: function( selector ) { + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null; + + return this.map(function(){ + var cur = this; + while ( cur && cur.ownerDocument ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) + return cur; + cur = cur.parentNode; + } + }); + }, + + not: function( selector ) { + if ( typeof selector === "string" ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); + }, + + add: function( selector ) { + return this.pushStack( jQuery.unique( jQuery.merge( + this.get(), + typeof selector === "string" ? + jQuery( selector ) : + jQuery.makeArray( selector ) + ))); + }, + + is: function( selector ) { + return !!selector && jQuery.multiFilter( selector, this ).length > 0; + }, + + hasClass: function( selector ) { + return !!selector && this.is( "." + selector ); + }, + + val: function( value ) { + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if( jQuery.nodeName( elem, 'option' ) ) + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type == "select-one"; + + // Nothing was selected + if ( index < 0 ) + return null; + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) + return value; + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(/\r/g, ""); + + } + + return undefined; + } + + if ( typeof value === "number" ) + value += ''; + + return this.each(function(){ + if ( this.nodeType != 1 ) + return; + + if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); + + else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(value); + + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); + }, + + html: function( value ) { + return value === undefined ? + (this[0] ? + this[0].innerHTML : + null) : + this.empty().append( value ); + }, + + replaceWith: function( value ) { + return this.after( value ).remove(); + }, + + eq: function( i ) { + return this.slice( i, +i + 1 ); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ), + "slice", Array.prototype.slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function(elem, i){ + return callback.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + domManip: function( args, table, callback ) { + if ( this[0] ) { + var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), + scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), + first = fragment.firstChild, + extra = this.length > 1 ? fragment.cloneNode(true) : fragment; + + if ( first ) + for ( var i = 0, l = this.length; i < l; i++ ) + callback.call( root(this[i], first), i > 0 ? extra.cloneNode(true) : fragment ); + + if ( scripts ) + jQuery.each( scripts, evalScript ); + } + + return this; + + function root( elem, cur ) { + return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; + } + } +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { + if ( elem.src ) + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + + else + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + + if ( elem.parentNode ) + elem.parentNode.removeChild( elem ); +} + +function now(){ + return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) + target = {}; + + // extend jQuery itself if only one argument is passed + if ( length == i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + // Recurse if we're merging object values + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) + target[ name ] = jQuery.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + + // Don't bring in undefined values + else if ( copy !== undefined ) + target[ name ] = copy; + + } + + // Return the modified object + return target; +}; + +// exclude the following css properties to add px +var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, + // cache defaultView + defaultView = document.defaultView || {}, + toString = Object.prototype.toString; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) + window.jQuery = _jQuery; + + return jQuery; + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + // check if an element is in a (or is an) XML document + isXMLDoc: function( elem ) { + return elem.documentElement && !elem.body || + elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + }, + + // Evalulates a script in a global context + globalEval: function( data ) { + data = jQuery.trim( data ); + + if ( data ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + if ( jQuery.support.scriptEval ) + script.appendChild( document.createTextNode( data ) ); + else + script.text = data; + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, length = object.length; + + if ( args ) { + if ( length === undefined ) { + for ( name in object ) + if ( callback.apply( object[ name ], args ) === false ) + break; + } else + for ( ; i < length; ) + if ( callback.apply( object[ i++ ], args ) === false ) + break; + + // A special, fast, case for the most common use of each + } else { + if ( length === undefined ) { + for ( name in object ) + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + } + + return object; + }, + + prop: function( elem, value, type, i, name ) { + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, i ); + + // Handle passing in a number to a CSS property + return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, classNames ) { + jQuery.each((classNames || "").split(/\s+/), function(i, className){ + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) + elem.className += (elem.className ? " " : "") + className; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, classNames ) { + if (elem.nodeType == 1) + elem.className = classNames !== undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; + }, + + // internal only, use hasClass("class") + has: function( elem, className ) { + return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var old = {}; + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( var name in options ) + elem.style[ name ] = old[ name ]; + }, + + css: function( elem, name, force ) { + if ( name == "width" || name == "height" ) { + var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + + function getWH() { + val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + var padding = 0, border = 0; + jQuery.each( which, function() { + padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + val -= Math.round(padding + border); + } + + if ( jQuery(elem).is(":visible") ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, val); + } + + return jQuery.curCSS( elem, name, force ); + }, + + curCSS: function( elem, name, force ) { + var ret, style = elem.style; + + // We need to handle opacity special in IE + if ( name == "opacity" && !jQuery.support.opacity ) { + ret = jQuery.attr( style, "opacity" ); + + return ret == "" ? + "1" : + ret; + } + + // Make sure we're using the right name for getting the float value + if ( name.match( /float/i ) ) + name = styleFloat; + + if ( !force && style && style[ name ] ) + ret = style[ name ]; + + else if ( defaultView.getComputedStyle ) { + + // Only "float" is needed here + if ( name.match( /float/i ) ) + name = "float"; + + name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + + var computedStyle = defaultView.getComputedStyle( elem, null ); + + if ( computedStyle ) + ret = computedStyle.getPropertyValue( name ); + + // We should always get a number back from opacity + if ( name == "opacity" && ret == "" ) + ret = "1"; + + } else if ( elem.currentStyle ) { + var camelCase = name.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + + ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { + // Remember the original values + var left = style.left, rsLeft = elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + elem.runtimeStyle.left = elem.currentStyle.left; + style.left = ret || 0; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + elem.runtimeStyle.left = rsLeft; + } + } + + return ret; + }, + + clean: function( elems, context, fragment ) { + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { + var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); + if ( match ) + return [ context.createElement( match[1] ) ]; + } + + var ret = [], scripts = [], div = context.createElement("div"); + + jQuery.each(elems, function(i, elem){ + if ( typeof elem === "number" ) + elem += ''; + + if ( !elem ) + return; + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? + all : + front + ">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var tags = jQuery.trim( elem ).toLowerCase(); + + var wrap = + // option or optgroup + !tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
" ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and + + + + + + + + + + + + + + + + + + {% block header %}{% endblock %} + {% block content %}{% endblock %} + + + + + + diff --git a/templates/globals.html b/templates/globals.html new file mode 100644 index 000000000..eaae56cc1 --- /dev/null +++ b/templates/globals.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/templates/opml_import/import.xhtml b/templates/opml_import/import.xhtml new file mode 100644 index 000000000..409e08815 --- /dev/null +++ b/templates/opml_import/import.xhtml @@ -0,0 +1,23 @@ +{% extends 'base.html' %} + +{% block content %} +
+ +
+ Paste the OPML file here: + +
+ +
+ + +
+
+ + +
+ + +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/reader/feeds.xhtml b/templates/reader/feeds.xhtml new file mode 100644 index 000000000..2c2d1c9e8 --- /dev/null +++ b/templates/reader/feeds.xhtml @@ -0,0 +1,46 @@ +{% extends 'base.html' %} + +{% block content %} + +
+ +
+
+
+ +
+
+ + +
+
+
+ + +
+
+
+
+ +
+ + +
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/registration/activate.html b/templates/registration/activate.html new file mode 100644 index 000000000..f87d519b3 --- /dev/null +++ b/templates/registration/activate.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block title %} +registration_form.html | {{ block.super }} +{% endblock %} + +{% block header %} +

activate.html

+{% endblock %} + +{% block content %} +You are now activated. +{% endblock %} \ No newline at end of file diff --git a/templates/registration/activation_email.txt b/templates/registration/activation_email.txt new file mode 100644 index 000000000..9a31eba3b --- /dev/null +++ b/templates/registration/activation_email.txt @@ -0,0 +1,3 @@ +Activate your account in {{ expiration_days }} days... +{{ site }}{% url registration_activate activation_key %} + diff --git a/templates/registration/activation_email_subject.txt b/templates/registration/activation_email_subject.txt new file mode 100644 index 000000000..50bcb400c --- /dev/null +++ b/templates/registration/activation_email_subject.txt @@ -0,0 +1 @@ +[django-registration] Activation email \ No newline at end of file diff --git a/templates/registration/login.html b/templates/registration/login.html new file mode 100644 index 000000000..2b6efca70 --- /dev/null +++ b/templates/registration/login.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} + +{% if form.errors %} +

Your username and password didn't match. Please try again.

+{% endif %} + +
+ + + +
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
+ + + +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/registration/registration_complete.html b/templates/registration/registration_complete.html new file mode 100644 index 000000000..7ea57e097 --- /dev/null +++ b/templates/registration/registration_complete.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block title %} +registration_complete.html | {{ block.super }} +{% endblock %} + +{% block header %} +

registration_complete.html

+{% endblock %} + +{% block content %} +Thank you for signing up. An email with the activation code has been send to your inbox. +{% endblock %} \ No newline at end of file diff --git a/templates/registration/registration_form.html b/templates/registration/registration_form.html new file mode 100644 index 000000000..17bebf263 --- /dev/null +++ b/templates/registration/registration_form.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} + +{% block title %} +registration_form.html | {{ block.super }} +{% endblock %} + +{% block header %} +

registration_form.html

+{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + + + + + + + + + + +
Username: + {{ form.username }}
+ {% for error in form.username.errors %} + {{ error }} + {% endfor %} +
Email: + {{ form.email }}
+ {% for error in form.email.errors %} + {{ error }} + {% endfor %} +
Password: + {{ form.password1 }}
+ {% for error in form.password1.errors %} + {{ error }} + {% endfor %} +
Password (again): + {{ form.password2 }}
+ {% for error in form.password2.errors %} + {{ error }} + {% endfor %} +
 
+
+{% endblock %} \ No newline at end of file diff --git a/urls.py b/urls.py new file mode 100644 index 000000000..8c8f21972 --- /dev/null +++ b/urls.py @@ -0,0 +1,26 @@ +from django.conf.urls.defaults import * +from django.conf import settings + +# Uncomment the next two lines to enable the admin: +from django.contrib import admin +admin.autodiscover() + +urlpatterns = patterns('', + (r'^$', include('apps.reader.urls')), + (r'^accounts/', include('apps.registration.urls')), + (r'^reader/', include('apps.reader.urls')), + (r'^opml_import/', include('apps.opml_import.urls')), + (r'^admin/doc/', include('django.contrib.admindocs.urls')), + (r'^admin/(.*)', admin.site.root) +) + +if settings.DEBUG and settings.PRODUCTION: + urlpatterns += patterns('', + (r'^media/(?P.*)$', 'django.views.static.serve', + {'document_root': '/home/conesus/newsblur/media'}), + ) +if settings.DEBUG and settings.DEVELOPMENT: + urlpatterns += patterns('', + (r'^media/(?P.*)$', 'django.views.static.serve', + {'document_root': '/Users/conesus/Projects/newsblur/media'}), + ) \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/cache/__init__.py b/utils/cache/__init__.py new file mode 100644 index 000000000..4bfec3f25 --- /dev/null +++ b/utils/cache/__init__.py @@ -0,0 +1,2 @@ +from manager import CacheManager +from base import CachedModel diff --git a/utils/cache/base.py b/utils/cache/base.py new file mode 100644 index 000000000..1e623e774 --- /dev/null +++ b/utils/cache/base.py @@ -0,0 +1,166 @@ +from django.db.models.manager import Manager +from django.db.models.base import ModelBase, Model +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from django.db.models.fields import FieldDoesNotExist +from django.db.models.options import Options +from django.db.models import signals +from django.db.models.loading import register_models, get_model +from django.dispatch import dispatcher +from django.utils.functional import curry +from django.conf import settings + +from django.core.cache import cache + +import types +import sys + +from manager import CacheManager +from utils import get_cache_key_for_pk + +DEFAULT_CACHE_TIME = 60*60*60 # the maximum an item should be in the cache + +# Signals rundown: +# .cache(expire_on=['create', 'update', 'delete']) +# use namespaces possible so the cache key becomes key_name:expire_namespace(not always present):hash + +# for example, a call with no expires: +# db_table:hash + +# a call with a delete expires +# db_table:0,0,0:hash + +# the numbers represent our current namespace level for the 3 expiration methods +# in order to do this, we'd have to actually store another cache key per model +# and to support threading, query that cache key everytime we do any cache queryset +# hit +# e.g. cache.get('ns:db_table') = 0,0,0 + +# when a new row is created, we'd set that to 1,0,0 +# which would invalidate anything that had a create expiration set because the key is +# now invalid, because the namespace changed. + +# if you only had create expirations set +# your namespace would be :0: -- its all about the queryset call, you still have to +# call it the same way throughout your code + +# We can also add a table namespace, which says "delete everything" so our +# cache key now becomes db_table:ns_count:0,0,0:hash +# where the 0,0,0: is optional + +# ns_count would be stored in the same ns:db_table key and starts at 0 +# this would most likely only be incremented if you did a push to your site +# and needed to say wipe all articles because the dataset changed. + +class CachedModelBase(ModelBase): + # TODO: find a way to not overwrite __new__ like this + def __new__(cls, name, bases, attrs): + # If this isn't a subclass of CachedModel, don't do anything special. + try: + if not filter(lambda b: issubclass(b, CachedModel), bases): + return super(CachedModelBase, cls).__new__(cls, name, bases, attrs) + except NameError: + # 'CachedModel' isn't defined yet, meaning we're looking at Django's own + # Model class, defined below. + return super(CachedModelBase, cls).__new__(cls, name, bases, attrs) + + # Create the class. + new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')}) + new_class.add_to_class('_meta', Options(attrs.pop('Meta', None))) + new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {})) + new_class.add_to_class('MultipleObjectsReturned', + types.ClassType('MultipleObjectsReturned', (MultipleObjectsReturned, ), {})) + + # Build complete list of parents + for base in bases: + # TODO: Checking for the presence of '_meta' is hackish. + if '_meta' in dir(base): + new_class._meta.parents.append(base) + new_class._meta.parents.extend(base._meta.parents) + + + if getattr(new_class._meta, 'app_label', None) is None: + # Figure out the app_label by looking one level up. + # For 'django.contrib.sites.models', this would be 'sites'. + model_module = sys.modules[new_class.__module__] + new_class._meta.app_label = model_module.__name__.split('.')[-2] + + # Bail out early if we have already created this class. + m = get_model(new_class._meta.app_label, name, False) + if m is not None: + return m + + # Add all attributes to the class. + for obj_name, obj in attrs.items(): + new_class.add_to_class(obj_name, obj) + + # Add Fields inherited from parents + for parent in new_class._meta.parents: + for field in parent._meta.fields: + # Only add parent fields if they aren't defined for this class. + try: + new_class._meta.get_field(field.name) + except FieldDoesNotExist: + field.contribute_to_class(new_class, field.name) + + new_class._prepare() + + register_models(new_class._meta.app_label, new_class) + # Because of the way imports happen (recursively), we may or may not be + # the first class for this model to register with the framework. There + # should only be one class for each model, so we must always return the + # registered version. + return get_model(new_class._meta.app_label, name, False) + +class CachedModel(Model): + """ + docstring for CachedModel + """ + __metaclass__ = CachedModelBase + +# objects = CacheManager() +# nocache = Manager() + + # Maybe this would work? + @classmethod + def _prepare(cls): + # TODO: How do we extend the parent classes classmethod properly? + # super(CachedModel, cls)._prepare() errors + opts = cls._meta + opts._prepare(cls) + + if opts.order_with_respect_to: + cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True) + cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False) + setattr(opts.order_with_respect_to.rel.to, 'get_%s_order' % cls.__name__.lower(), curry(method_get_order, cls)) + setattr(opts.order_with_respect_to.rel.to, 'set_%s_order' % cls.__name__.lower(), curry(method_set_order, cls)) + + # Give the class a docstring -- its definition. + if cls.__doc__ is None: + cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join([f.attname for f in opts.fields])) + + if hasattr(cls, 'get_absolute_url'): + cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) + + cls.add_to_class('objects', CacheManager()) + cls.add_to_class('nocache', Manager()) + cls.add_to_class('_default_manager', cls.nocache) + dispatcher.send(signal=signals.class_prepared, sender=cls) + + @staticmethod + def _get_cache_key_for_pk(model, pk): + return get_cache_key_for_pk(model, pk) + + @property + def cache_key(self): + return self._get_cache_key_for_pk(self.__class__, self.pk) + + def save(self, *args, **kwargs): + cache.set(self._get_cache_key_for_pk(self.__class__, self.pk), self) + super(CachedModel, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + # TODO: create an option that tells the model whether or not it should + # do a cache.delete when the object is deleted. For memcached we + # wouldn't care about deleting. + cache.delete(self._get_cache_key_for_pk(self.__class__, self.pk)) + super(CachedModel, self).delete(*args, **kwargs) diff --git a/utils/cache/exceptions.py b/utils/cache/exceptions.py new file mode 100644 index 000000000..fec786bdf --- /dev/null +++ b/utils/cache/exceptions.py @@ -0,0 +1,19 @@ +class CachedModelException(Exception): pass + + +# Our invalidation classes +class CacheInvalidationWarning(CachedModelException): pass + +class CacheMissingWarning(CacheInvalidationWarning): + """ + CacheMissingWarning is thrown when we're trying to fetch a queryset + and it's missing objects in the database. + """ + pass + +class CacheExpiredWarning(CacheInvalidationWarning): + """ + CacheExpiredWarning is thrown when we're trying to fetch from the cache + but the pre-expiration has been hit. + """ + pass diff --git a/utils/cache/manager.py b/utils/cache/manager.py new file mode 100644 index 000000000..f64e0bdc7 --- /dev/null +++ b/utils/cache/manager.py @@ -0,0 +1,28 @@ +from django.db.models.manager import Manager +from query import CachedQuerySet + +class CacheManager(Manager): + """ + A manager to store and retrieve cached objects using CACHE_BACKEND + + -- the key prefix for all cached objects on this model. [default: db_table] + -- in seconds, the maximum time before data is invalidated. [default: DEFAULT_CACHE_TIME] + """ + def __init__(self, *args, **kwargs): + self.key_prefix = kwargs.pop('key_prefix', None) + self.timeout = kwargs.pop('timeout', None) + super(CacheManager, self).__init__(*args, **kwargs) + + def get_query_set(self): + return CachedQuerySet(model=self.model, timeout=self.timeout, key_prefix=self.key_prefix) + + def cache(self, *args, **kwargs): + return self.get_query_set().cache(*args, **kwargs) + + def clean(self, *args, **kwargs): + # Use reset instead if you are using memcached, as clean makes no sense (extra bandwidth when + # memcached will automatically clean iself). + return self.get_query_set().clean(*args, **kwargs) + + def reset(self, *args, **kwargs): + return self.get_query_set().reset(*args, **kwargs) diff --git a/utils/cache/query.py b/utils/cache/query.py new file mode 100644 index 000000000..28264cf84 --- /dev/null +++ b/utils/cache/query.py @@ -0,0 +1,303 @@ +from django.db.models.query import QuerySet +from django.db import backend, connection +from django.core.cache import cache +from django.conf import settings + +from utils import get_cache_key_for_pk +from exceptions import CacheMissingWarning + +# TODO: if the query is passing pks then we need to make it pull the cache key from the model +# and try to fetch that first +# if there are additional filters to apply beyond pks we then filter those after we're already pulling the pks + +# TODO: should we also run these additional filters each time we pull back a ref list to check for validation? + +# TODO: all related field calls need to be removed and replaced with cache key sets of some sorts +# (just remove the join and make it do another qs.filter(pk__in) to pull them, which would do a many cache get callb) + +DEFAULT_CACHE_TIME = 60*60*24 # 24 hours +GET_ITERATOR_CHUNK_SIZE = 100 + +class FauxCachedQuerySet(list): + """ + We generate a FauxCachedQuerySet when we are returning a + CachedQuerySet from a CachedModel. + """ + pass + +class CachedQuerySet(QuerySet): + """ + Extends the QuerySet object and caches results via CACHE_BACKEND. + """ + def __init__(self, model=None, key_prefix=None, timeout=None, key_name=None, *args, **kwargs): + self._cache_keys = {} + self._cache_reset = False + self._cache_clean = False + if key_prefix: + self.cache_key_prefix = key_prefix + else: + if model: + self.cache_key_prefix = model._meta.db_table + else: + self.cache_key_prefix = '' + self.cache_key_name = key_name + if timeout: + self.cache_timeout = timeout + else: + self.cache_timeout = getattr(cache, 'default_timeout', getattr(settings, 'DEFAULT_CACHE_TIME', DEFAULT_CACHE_TIME)) + QuerySet.__init__(self, model, *args, **kwargs) + + def _clone(self, klass=None, **kwargs): + c = QuerySet._clone(self, klass, **kwargs) + c._cache_clean = kwargs.pop('_cache_clean', self._cache_clean) + c._cache_reset = kwargs.pop('_cache_reset', self._cache_reset) + c.cache_key_prefix = kwargs.pop('cache_key_prefix', self.cache_key_prefix) + c.cache_timeout = kwargs.pop('cache_timeout', self.cache_timeout) + c._cache_keys = {} + return c + + def _get_sorted_clause_key(self): + return (isinstance(i, basestring) and i.lower().replace('`', '').replace("'", '') or str(tuple(sorted(i))) for i in self._get_sql_clause()) + + def _get_cache_key(self, extra=''): + # TODO: Need to figure out if this is the best use. + # Maybe we should use extra for cache_key_name, extra was planned for use + # in things like .count() as it's a different cache key than the normal queryset, + # but that also doesn't make sense because theoretically count() is already different + # sql so the sorted_sql_clause should have figured that out. + if self.cache_key_name is not None: + return '%s:%s' % (self.cache_key_prefix, self.cache_key_name) + if extra not in self._cache_keys: + self._cache_keys[extra] = '%s:%s:%s' % (self.cache_key_prefix, str(hash(''.join(self._get_sorted_clause_key()))), extra) + return self._cache_keys[extra] + + def _prepare_queryset_for_cache(self, queryset): + """ + This is where the magic happens. We need to first see if our result set + is in the cache. If it isn't, we need to do the query and set the cache + to (ModelClass, (*,), (*,), ). + """ + # TODO: make this split up large sets of data based on an option + # and sets the last param, keys, to how many datasets are stored + # in the cache to regenerate. + keys = tuple(obj.pk for obj in queryset) + if self._select_related: + if not self._max_related_depth: + fields = [f.name for f in opts.fields if f.rel and not f.null] + else: + # TODO: handle depth relate lookups + fields = () + else: + fields = () + + return (queryset[0].__class__, keys, fields, 1) + + def _get_queryset_from_cache(self, cache_object): + """ + We transform the cache storage into an actual QuerySet object + automagickly handling the keys depth and select_related fields (again, + using the recursive methods of CachedQuerySet. + + We effectively would just be doing a cache.multi_get(*pks), grabbing + the pks for each releation, e.g. user, and then doing a + CachedManager.objects.filter() on them. This also then makes that + queryset reusable. So the question is, should that queryset have been + reusable? It could be invalidated by some other code which we aren't + tieing directly into the parent queryset so maybe we can't do the + objects.filter() query here and we have to do it internally. + """ + # TODO: make this work for people who have, and who don't have, instance caching + model, keys, fields, length = cache_object + + results = self._get_objects_for_keys(model, keys) + + if fields: + # TODO: optimize this so it's only one get_many call instead of one per select_related field + # XXX: this probably isn't handling depth beyond 1, didn't test even depth of 1 yet + for f in fields: + field = model._meta.get_field(f) + field_results = dict((r.id, r) for r in self._get_objects_for_keys(f.rel.to, [getattr(r, field.db_column) for r in results])) + for r in results: + setattr(r, f.name, field_results[getattr(r, field.db_column)]) + return results + + def _get_objects_for_keys(self, model, keys): + # First we fetch any keys that we can from the cache + results = cache.get_many([get_cache_key_for_pk(model, k) for k in keys]).values() + + # Now we need to compute which keys weren't present in the cache + missing = [k for k in results.iterkeys() if not results[k]] + + # We no longer need to know what the keys were so turn it into a list + results = list(results) + # Query for any missing objects + # TODO: should this only be doing the cache.set if it's from a CachedModel? + # if not then we need to expire it, hook signals? + objects = list(model._default_manager.filter(pk__in=missing)) + for o in objects: + cache.set(o.cache_key, o) + results.extend(objects) + + # Do a simple len() lookup (maybe we shouldn't rely on it returning the right + # number of objects + cnt = len(missing) - len(objects) + if cnt: + raise CacheMissingWarning("%d objects missing in the database" % (cnt,)) + return results + + def _get_data(self): + ck = self._get_cache_key() + if self._result_cache is None or self._cache_clean or self._cache_reset: + if self._cache_clean: + cache.delete(ck) + return + if self._cache_reset: + result_cache = None + else: + result_cache = cache.get(ck) + if result_cache is None: + # We need to lookup the initial table queryset, without related + # fields selected. We then need to loop through each field which + # should be selected and doing another CachedQuerySet() call for + # each set of data. + + # This will allow it to transparently, and recursively, handle + # all calls to the cache. + + # We will use _prepare_queryset_for_cache to store it in the + # the cache, and _get_queryset_from_cache to pull it. + + # Maybe we should override getstate and setstate instead? + + # We first have to remove select_related values from the QuerySet + # as we don't want to pull these in to the dataset as they may already exist + # in memory. + + # TODO: create a function that works w/ our patch and Django trunk which will + # grab the select_related fields for us given X model and (Y list or N depth). + + # TODO: find a clean way to say "is this only matching pks?" if it is we wont + # need to store a result set in memory but we'll need to apply the filters by hand. + qs = QuerySet._clone(QuerySet(), **self.__dict__) + self._result_cache = qs._get_data() + self._cache_reset = False + cache.set(ck, self._prepare_queryset_for_cache(self._result_cache), self.cache_timeout*60) + else: + try: + self._result_cache = self._get_queryset_from_cache(result_cache) + except CacheMissingWarning: + # When an object is missing we reset the cached list. + # TODO: this should be some kind of option at a global and model level. + return self.reset()._get_data() + return FauxCachedQuerySet(self._result_cache) + + def execute(self): + """ + Forces execution on the queryset + """ + self._get_data() + return self + + def get(self, *args, **kwargs): + """ + Performs the SELECT and returns a single object matching the given + keyword arguments. + """ + if self._cache_clean: + clone = self.filter(*args, **kwargs) + if not clone._order_by: + clone._order_by = () + cache.delete(self._get_cache_key()) + else: + return QuerySet.get(self, *args, **kwargs) + + def clean(self): + """ + Removes queryset from the cache upon execution. + """ + return self._clone(_cache_clean=True) + + def count(self): + return QuerySet.count(self) + count = cache.get(self._get_cache_key('count')) + if count is None: + count = int(QuerySet.count(self)) + cache.set(self._get_cache_key('count'), count, self.cache_timeout) + return count + + def cache(self, *args, **kwargs): + """ + Overrides CacheManager's options for this QuerySet. + + -- the key prefix for all cached objects + on this model. [default: db_table] + -- in seconds, the maximum time before data is + invalidated. + -- the key suffix for this cached queryset + useful if you want to cache the same queryset with two expiration + methods. + """ + return self._clone(cache_key_prefix=kwargs.pop('key_prefix', self.cache_key_prefix), cache_timeout=kwargs.pop('timeout', self.cache_timeout), cache_key_name=kwargs.pop('key_name', self.cache_key_name)) + + def reset(self): + """ + Updates the queryset in the cache upon execution. + """ + return self._clone(_cache_reset=True) + + def values(self, *fields): + return self._clone(klass=CachedValuesQuerySet, _fields=fields) + +# need a better way to do this.. (will mix-ins work?) +class CachedValuesQuerySet(CachedQuerySet): + def __init__(self, *args, **kwargs): + super(CachedQuerySet, self).__init__(*args, **kwargs) + # select_related isn't supported in values(). + self._select_related = False + + def iterator(self): + try: + select, sql, params = self._get_sql_clause() + except EmptyResultSet: + raise StopIteration + + # self._fields is a list of field names to fetch. + if self._fields: + #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] + if not self._select: + columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] + else: + columns = [] + for f in self._fields: + if f in [field.name for field in self.model._meta.fields]: + columns.append( self.model._meta.get_field(f, many_to_many=False).column ) + elif not self._select.has_key( f ): + raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) + + field_names = self._fields + else: # Default to all fields. + columns = [f.column for f in self.model._meta.fields] + field_names = [f.column for f in self.model._meta.fields] + + select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] + + # Add any additional SELECTs. + if self._select: + select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) + + if getattr(self, '_db_use_master', False): + cursor = connection.write_cursor() + else: + cursor = connection.read_cursor() + cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) + while 1: + rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) + if not rows: + raise StopIteration + for row in rows: + yield dict(zip(field_names, row)) + + def _clone(self, klass=None, **kwargs): + c = super(CachedValuesQuerySet, self)._clone(klass, **kwargs) + c._fields = self._fields[:] + return c diff --git a/utils/cache/utils.py b/utils/cache/utils.py new file mode 100644 index 000000000..7e950664a --- /dev/null +++ b/utils/cache/utils.py @@ -0,0 +1,2 @@ +def get_cache_key_for_pk(model, pk): + return '%s:%s' % (model._meta.db_table, pk) diff --git a/utils/dateutil/__init__.py b/utils/dateutil/__init__.py new file mode 100644 index 000000000..8b4ac7dc8 --- /dev/null +++ b/utils/dateutil/__init__.py @@ -0,0 +1,9 @@ +""" +Copyright (c) 2003-2007 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" +__version__ = "1.4.1" diff --git a/utils/dateutil/easter.py b/utils/dateutil/easter.py new file mode 100644 index 000000000..d7944104b --- /dev/null +++ b/utils/dateutil/easter.py @@ -0,0 +1,92 @@ +""" +Copyright (c) 2003-2007 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" + +import datetime + +__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] + +EASTER_JULIAN = 1 +EASTER_ORTHODOX = 2 +EASTER_WESTERN = 3 + +def easter(year, method=EASTER_WESTERN): + """ + This method was ported from the work done by GM Arts, + on top of the algorithm by Claus Tondering, which was + based in part on the algorithm of Ouding (1940), as + quoted in "Explanatory Supplement to the Astronomical + Almanac", P. Kenneth Seidelmann, editor. + + This algorithm implements three different easter + calculation methods: + + 1 - Original calculation in Julian calendar, valid in + dates after 326 AD + 2 - Original method, with date converted to Gregorian + calendar, valid in years 1583 to 4099 + 3 - Revised method, in Gregorian calendar, valid in + years 1583 to 4099 as well + + These methods are represented by the constants: + + EASTER_JULIAN = 1 + EASTER_ORTHODOX = 2 + EASTER_WESTERN = 3 + + The default method is method 3. + + More about the algorithm may be found at: + + http://users.chariot.net.au/~gmarts/eastalg.htm + + and + + http://www.tondering.dk/claus/calendar.html + + """ + + if not (1 <= method <= 3): + raise ValueError, "invalid method" + + # g - Golden year - 1 + # c - Century + # h - (23 - Epact) mod 30 + # i - Number of days from March 21 to Paschal Full Moon + # j - Weekday for PFM (0=Sunday, etc) + # p - Number of days from March 21 to Sunday on or before PFM + # (-6 to 28 methods 1 & 3, to 56 for method 2) + # e - Extra days to add for method 2 (converting Julian + # date to Gregorian date) + + y = year + g = y % 19 + e = 0 + if method < 3: + # Old method + i = (19*g+15)%30 + j = (y+y//4+i)%7 + if method == 2: + # Extra dates to convert Julian to Gregorian date + e = 10 + if y > 1600: + e = e+y//100-16-(y//100-16)//4 + else: + # New method + c = y//100 + h = (c-c//4-(8*c+13)//25+19*g+15)%30 + i = h-(h//28)*(1-(h//28)*(29//(h+1))*((21-g)//11)) + j = (y+y//4+i+2-c+c//4)%7 + + # p can be from -6 to 56 corresponding to dates 22 March to 23 May + # (later dates apply to method 2, although 23 May never actually occurs) + p = i-j+e + d = 1+(p+27+(p+6)//40)%31 + m = 3+(p+26)//30 + return datetime.date(int(y),int(m),int(d)) + diff --git a/utils/dateutil/parser.py b/utils/dateutil/parser.py new file mode 100644 index 000000000..5d824e411 --- /dev/null +++ b/utils/dateutil/parser.py @@ -0,0 +1,886 @@ +# -*- coding:iso-8859-1 -*- +""" +Copyright (c) 2003-2007 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" + +import datetime +import string +import time +import sys +import os + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +import relativedelta +import tz + + +__all__ = ["parse", "parserinfo"] + + +# Some pointers: +# +# http://www.cl.cam.ac.uk/~mgk25/iso-time.html +# http://www.iso.ch/iso/en/prods-services/popstds/datesandtime.html +# http://www.w3.org/TR/NOTE-datetime +# http://ringmaster.arc.nasa.gov/tools/time_formats.html +# http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm +# http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html + + +class _timelex(object): + + def __init__(self, instream): + if isinstance(instream, basestring): + instream = StringIO(instream) + self.instream = instream + self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' + 'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' + 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ') + self.numchars = '0123456789' + self.whitespace = ' \t\r\n' + self.charstack = [] + self.tokenstack = [] + self.eof = False + + def get_token(self): + if self.tokenstack: + return self.tokenstack.pop(0) + seenletters = False + token = None + state = None + wordchars = self.wordchars + numchars = self.numchars + whitespace = self.whitespace + while not self.eof: + if self.charstack: + nextchar = self.charstack.pop(0) + else: + nextchar = self.instream.read(1) + while nextchar == '\x00': + nextchar = self.instream.read(1) + if not nextchar: + self.eof = True + break + elif not state: + token = nextchar + if nextchar in wordchars: + state = 'a' + elif nextchar in numchars: + state = '0' + elif nextchar in whitespace: + token = ' ' + break # emit token + else: + break # emit token + elif state == 'a': + seenletters = True + if nextchar in wordchars: + token += nextchar + elif nextchar == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0': + if nextchar in numchars: + token += nextchar + elif nextchar == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == 'a.': + seenletters = True + if nextchar == '.' or nextchar in wordchars: + token += nextchar + elif nextchar in numchars and token[-1] == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0.': + if nextchar == '.' or nextchar in numchars: + token += nextchar + elif nextchar in wordchars and token[-1] == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + if (state in ('a.', '0.') and + (seenletters or token.count('.') > 1 or token[-1] == '.')): + l = token.split('.') + token = l[0] + for tok in l[1:]: + self.tokenstack.append('.') + if tok: + self.tokenstack.append(tok) + return token + + def __iter__(self): + return self + + def next(self): + token = self.get_token() + if token is None: + raise StopIteration + return token + + def split(cls, s): + return list(cls(s)) + split = classmethod(split) + + +class _resultbase(object): + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def _repr(self, classname): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, `value`)) + return "%s(%s)" % (classname, ", ".join(l)) + + def __repr__(self): + return self._repr(self.__class__.__name__) + + +class parserinfo(object): + + # m from a.m/p.m, t from ISO T separator + JUMP = [" ", ".", ",", ";", "-", "/", "'", + "at", "on", "and", "ad", "m", "t", "of", + "st", "nd", "rd", "th"] + + WEEKDAYS = [("Mon", "Monday"), + ("Tue", "Tuesday"), + ("Wed", "Wednesday"), + ("Thu", "Thursday"), + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), + ("Mar", "March"), + ("Apr", "April"), + ("May", "May"), + ("Jun", "June"), + ("Jul", "July"), + ("Aug", "August"), + ("Sep", "September"), + ("Oct", "October"), + ("Nov", "November"), + ("Dec", "December")] + HMS = [("h", "hour", "hours"), + ("m", "minute", "minutes"), + ("s", "second", "seconds")] + AMPM = [("am", "a"), + ("pm", "p")] + UTCZONE = ["UTC", "GMT", "Z"] + PERTAIN = ["of"] + TZOFFSET = {} + + def __init__(self, dayfirst=False, yearfirst=False): + self._jump = self._convert(self.JUMP) + self._weekdays = self._convert(self.WEEKDAYS) + self._months = self._convert(self.MONTHS) + self._hms = self._convert(self.HMS) + self._ampm = self._convert(self.AMPM) + self._utczone = self._convert(self.UTCZONE) + self._pertain = self._convert(self.PERTAIN) + + self.dayfirst = dayfirst + self.yearfirst = yearfirst + + self._year = time.localtime().tm_year + self._century = self._year//100*100 + + def _convert(self, lst): + dct = {} + for i in range(len(lst)): + v = lst[i] + if isinstance(v, tuple): + for v in v: + dct[v.lower()] = i + else: + dct[v.lower()] = i + return dct + + def jump(self, name): + return name.lower() in self._jump + + def weekday(self, name): + if len(name) >= 3: + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + if len(name) >= 3: + try: + return self._months[name.lower()]+1 + except KeyError: + pass + return None + + def hms(self, name): + try: + return self._hms[name.lower()] + except KeyError: + return None + + def ampm(self, name): + try: + return self._ampm[name.lower()] + except KeyError: + return None + + def pertain(self, name): + return name.lower() in self._pertain + + def utczone(self, name): + return name.lower() in self._utczone + + def tzoffset(self, name): + if name in self._utczone: + return 0 + return self.TZOFFSET.get(name) + + def convertyear(self, year): + if year < 100: + year += self._century + if abs(year-self._year) >= 50: + if year < self._year: + year += 100 + else: + year -= 100 + return year + + def validate(self, res): + # move to info + if res.year is not None: + res.year = self.convertyear(res.year) + if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z': + res.tzname = "UTC" + res.tzoffset = 0 + elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): + res.tzoffset = 0 + return True + + +class parser(object): + + def __init__(self, info=None): + self.info = info or parserinfo() + + def parse(self, timestr, default=None, + ignoretz=False, tzinfos=None, + **kwargs): + if not default: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + res = self._parse(timestr, **kwargs) + if res is None: + raise ValueError, "unknown string format" + repl = {} + for attr in ["year", "month", "day", "hour", + "minute", "second", "microsecond"]: + value = getattr(res, attr) + if value is not None: + repl[attr] = value + ret = default.replace(**repl) + if res.weekday is not None and not res.day: + ret = ret+relativedelta.relativedelta(weekday=res.weekday) + if not ignoretz: + if callable(tzinfos) or tzinfos and res.tzname in tzinfos: + if callable(tzinfos): + tzdata = tzinfos(res.tzname, res.tzoffset) + else: + tzdata = tzinfos.get(res.tzname) + if isinstance(tzdata, datetime.tzinfo): + tzinfo = tzdata + elif isinstance(tzdata, basestring): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, int): + tzinfo = tz.tzoffset(res.tzname, tzdata) + else: + raise ValueError, "offset must be tzinfo subclass, " \ + "tz string, or int offset" + ret = ret.replace(tzinfo=tzinfo) + elif res.tzname and res.tzname in time.tzname: + ret = ret.replace(tzinfo=tz.tzlocal()) + elif res.tzoffset == 0: + ret = ret.replace(tzinfo=tz.tzutc()) + elif res.tzoffset: + ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + return ret + + class _result(_resultbase): + __slots__ = ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond", + "tzname", "tzoffset"] + + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): + info = self.info + if dayfirst is None: + dayfirst = info.dayfirst + if yearfirst is None: + yearfirst = info.yearfirst + res = self._result() + l = _timelex.split(timestr) + try: + + # year/month/day list + ymd = [] + + # Index of the month string in ymd + mstridx = -1 + + len_l = len(l) + i = 0 + while i < len_l: + + # Check if it's a number + try: + value_repr = l[i] + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Token is a number + len_li = len(l[i]) + i += 1 + if (len(ymd) == 3 and len_li in (2, 4) + and (i >= len_l or (l[i] != ':' and + info.hms(l[i]) is None))): + # 19990101T23[59] + s = l[i-1] + res.hour = int(s[:2]) + if len_li == 4: + res.minute = int(s[2:]) + elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = l[i-1] + if not ymd and l[i-1].find('.') == -1: + ymd.append(info.convertyear(int(s[:2]))) + ymd.append(int(s[2:4])) + ymd.append(int(s[4:])) + else: + # 19990101T235959[.59] + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = _parsems(s[4:]) + elif len_li == 8: + # YYYYMMDD + s = l[i-1] + ymd.append(int(s[:4])) + ymd.append(int(s[4:6])) + ymd.append(int(s[6:])) + elif len_li in (12, 14): + # YYYYMMDDhhmm[ss] + s = l[i-1] + ymd.append(int(s[:4])) + ymd.append(int(s[4:6])) + ymd.append(int(s[6:8])) + res.hour = int(s[8:10]) + res.minute = int(s[10:12]) + if len_li == 14: + res.second = int(s[12:]) + elif ((i < len_l and info.hms(l[i]) is not None) or + (i+1 < len_l and l[i] == ' ' and + info.hms(l[i+1]) is not None)): + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + if l[i] == ' ': + i += 1 + idx = info.hms(l[i]) + while True: + if idx == 0: + res.hour = int(value) + if value%1: + res.minute = int(60*(value%1)) + elif idx == 1: + res.minute = int(value) + if value%1: + res.second = int(60*(value%1)) + elif idx == 2: + res.second, res.microsecond = \ + _parsems(value_repr) + i += 1 + if i >= len_l or idx == 2: + break + # 12h00 + try: + value_repr = l[i] + value = float(value_repr) + except ValueError: + break + else: + i += 1 + idx += 1 + if i < len_l: + newidx = info.hms(l[i]) + if newidx is not None: + idx = newidx + elif i+1 < len_l and l[i] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + i += 1 + value = float(l[i]) + res.minute = int(value) + if value%1: + res.second = int(60*(value%1)) + i += 1 + if i < len_l and l[i] == ':': + res.second, res.microsecond = _parsems(l[i+1]) + i += 2 + elif i < len_l and l[i] in ('-', '/', '.'): + sep = l[i] + ymd.append(int(value)) + i += 1 + if i < len_l and not info.jump(l[i]): + try: + # 01-01[-01] + ymd.append(int(l[i])) + except ValueError: + # 01-Jan[-01] + value = info.month(l[i]) + if value is not None: + ymd.append(value) + assert mstridx == -1 + mstridx = len(ymd)-1 + else: + return None + i += 1 + if i < len_l and l[i] == sep: + # We have three members + i += 1 + value = info.month(l[i]) + if value is not None: + ymd.append(value) + mstridx = len(ymd)-1 + assert mstridx == -1 + else: + ymd.append(int(l[i])) + i += 1 + elif i >= len_l or info.jump(l[i]): + if i+1 < len_l and info.ampm(l[i+1]) is not None: + # 12 am + res.hour = int(value) + if res.hour < 12 and info.ampm(l[i+1]) == 1: + res.hour += 12 + elif res.hour == 12 and info.ampm(l[i+1]) == 0: + res.hour = 0 + i += 1 + else: + # Year, month or day + ymd.append(int(value)) + i += 1 + elif info.ampm(l[i]) is not None: + # 12am + res.hour = int(value) + if res.hour < 12 and info.ampm(l[i]) == 1: + res.hour += 12 + elif res.hour == 12 and info.ampm(l[i]) == 0: + res.hour = 0 + i += 1 + elif not fuzzy: + return None + else: + i += 1 + continue + + # Check weekday + value = info.weekday(l[i]) + if value is not None: + res.weekday = value + i += 1 + continue + + # Check month name + value = info.month(l[i]) + if value is not None: + ymd.append(value) + assert mstridx == -1 + mstridx = len(ymd)-1 + i += 1 + if i < len_l: + if l[i] in ('-', '/'): + # Jan-01[-99] + sep = l[i] + i += 1 + ymd.append(int(l[i])) + i += 1 + if i < len_l and l[i] == sep: + # Jan-01-99 + i += 1 + ymd.append(int(l[i])) + i += 1 + elif (i+3 < len_l and l[i] == l[i+2] == ' ' + and info.pertain(l[i+1])): + # Jan of 01 + # In this case, 01 is clearly year + try: + value = int(l[i+3]) + except ValueError: + # Wrong guess + pass + else: + # Convert it here to become unambiguous + ymd.append(info.convertyear(value)) + i += 4 + continue + + # Check am/pm + value = info.ampm(l[i]) + if value is not None: + if value == 1 and res.hour < 12: + res.hour += 12 + elif value == 0 and res.hour == 12: + res.hour = 0 + i += 1 + continue + + # Check for a timezone name + if (res.hour is not None and len(l[i]) <= 5 and + res.tzname is None and res.tzoffset is None and + not [x for x in l[i] if x not in string.ascii_uppercase]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + i += 1 + + # Check for something like GMT+3, or BRST+3. Notice + # that it doesn't mean "I am 3 hours after GMT", but + # "my time +3 is GMT". If found, we reverse the + # logic so that timezone parsing code will get it + # right. + if i < len_l and l[i] in ('+', '-'): + l[i] = ('+', '-')[l[i] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + continue + + # Check for a numbered timezone + if res.hour is not None and l[i] in ('+', '-'): + signal = (-1,1)[l[i] == '+'] + i += 1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + res.tzoffset = int(l[i][:2])*3600+int(l[i][2:])*60 + elif i+1 < len_l and l[i+1] == ':': + # -03:00 + res.tzoffset = int(l[i])*3600+int(l[i+2])*60 + i += 2 + elif len_li <= 2: + # -[0]3 + res.tzoffset = int(l[i][:2])*3600 + else: + return None + i += 1 + res.tzoffset *= signal + + # Look for a timezone name between parenthesis + if (i+3 < len_l and + info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and + 3 <= len(l[i+2]) <= 5 and + not [x for x in l[i+2] + if x not in string.ascii_uppercase]): + # -0300 (BRST) + res.tzname = l[i+2] + i += 4 + continue + + # Check jumps + if not (info.jump(l[i]) or fuzzy): + return None + + i += 1 + + # Process year/month/day + len_ymd = len(ymd) + if len_ymd > 3: + # More than three members!? + return None + elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2): + # One member, or two members with a month string + if mstridx != -1: + res.month = ymd[mstridx] + del ymd[mstridx] + if len_ymd > 1 or mstridx == -1: + if ymd[0] > 31: + res.year = ymd[0] + else: + res.day = ymd[0] + elif len_ymd == 2: + # Two members with numbers + if ymd[0] > 31: + # 99-01 + res.year, res.month = ymd + elif ymd[1] > 31: + # 01-99 + res.month, res.year = ymd + elif dayfirst and ymd[1] <= 12: + # 13-01 + res.day, res.month = ymd + else: + # 01-13 + res.month, res.day = ymd + if len_ymd == 3: + # Three members + if mstridx == 0: + res.month, res.day, res.year = ymd + elif mstridx == 1: + if ymd[0] > 31 or (yearfirst and ymd[2] <= 31): + # 99-Jan-01 + res.year, res.month, res.day = ymd + else: + # 01-Jan-01 + # Give precendence to day-first, since + # two-digit years is usually hand-written. + res.day, res.month, res.year = ymd + elif mstridx == 2: + # WTF!? + if ymd[1] > 31: + # 01-99-Jan + res.day, res.year, res.month = ymd + else: + # 99-01-Jan + res.year, res.day, res.month = ymd + else: + if ymd[0] > 31 or \ + (yearfirst and ymd[1] <= 12 and ymd[2] <= 31): + # 99-01-01 + res.year, res.month, res.day = ymd + elif ymd[0] > 12 or (dayfirst and ymd[1] <= 12): + # 13-01-01 + res.day, res.month, res.year = ymd + else: + # 01-13-01 + res.month, res.day, res.year = ymd + + except (IndexError, ValueError, AssertionError): + return None + + if not info.validate(res): + return None + return res + +DEFAULTPARSER = parser() +def parse(timestr, parserinfo=None, **kwargs): + if parserinfo: + return parser(parserinfo).parse(timestr, **kwargs) + else: + return DEFAULTPARSER.parse(timestr, **kwargs) + + +class _tzparser(object): + + class _result(_resultbase): + + __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", + "start", "end"] + + class _attr(_resultbase): + __slots__ = ["month", "week", "weekday", + "yday", "jyday", "day", "time"] + + def __repr__(self): + return self._repr("") + + def __init__(self): + _resultbase.__init__(self) + self.start = self._attr() + self.end = self._attr() + + def parse(self, tzstr): + res = self._result() + l = _timelex.split(tzstr) + try: + + len_l = len(l) + + i = 0 + while i < len_l: + # BRST+3[BRDT[+2]] + j = i + while j < len_l and not [x for x in l[j] + if x in "0123456789:,-+"]: + j += 1 + if j != i: + if not res.stdabbr: + offattr = "stdoffset" + res.stdabbr = "".join(l[i:j]) + else: + offattr = "dstoffset" + res.dstabbr = "".join(l[i:j]) + i = j + if (i < len_l and + (l[i] in ('+', '-') or l[i][0] in "0123456789")): + if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. + signal = (1,-1)[l[i] == '+'] + i += 1 + else: + signal = -1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + setattr(res, offattr, + (int(l[i][:2])*3600+int(l[i][2:])*60)*signal) + elif i+1 < len_l and l[i+1] == ':': + # -03:00 + setattr(res, offattr, + (int(l[i])*3600+int(l[i+2])*60)*signal) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2])*3600*signal) + else: + return None + i += 1 + if res.dstabbr: + break + else: + break + + if i < len_l: + for j in range(i, len_l): + if l[j] == ';': l[j] = ',' + + assert l[i] == ',' + + i += 1 + + if i >= len_l: + pass + elif (8 <= l.count(',') <= 9 and + not [y for x in l[i:] if x != ',' + for y in x if y not in "0123456789"]): + # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] + for x in (res.start, res.end): + x.month = int(l[i]) + i += 2 + if l[i] == '-': + value = int(l[i+1])*-1 + i += 1 + else: + value = int(l[i]) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i])-1)%7 + else: + x.day = int(l[i]) + i += 2 + x.time = int(l[i]) + i += 2 + if i < len_l: + if l[i] in ('-','+'): + signal = (-1,1)[l[i] == "+"] + i += 1 + else: + signal = 1 + res.dstoffset = (res.stdoffset+int(l[i]))*signal + elif (l.count(',') == 2 and l[i:].count('/') <= 2 and + not [y for x in l[i:] if x not in (',','/','J','M', + '.','-',':') + for y in x if y not in "0123456789"]): + for x in (res.start, res.end): + if l[i] == 'J': + # non-leap year day (1 based) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + i += 1 + x.month = int(l[i]) + i += 1 + assert l[i] in ('-', '.') + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + i += 1 + assert l[i] in ('-', '.') + i += 1 + x.weekday = (int(l[i])-1)%7 + else: + # year day (zero based) + x.yday = int(l[i])+1 + + i += 1 + + if i < len_l and l[i] == '/': + i += 1 + # start time + len_li = len(l[i]) + if len_li == 4: + # -0300 + x.time = (int(l[i][:2])*3600+int(l[i][2:])*60) + elif i+1 < len_l and l[i+1] == ':': + # -03:00 + x.time = int(l[i])*3600+int(l[i+2])*60 + i += 2 + if i+1 < len_l and l[i+1] == ':': + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2])*3600) + else: + return None + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + return res + + +DEFAULTTZPARSER = _tzparser() +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + + +def _parsems(value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + +# vim:ts=4:sw=4:et diff --git a/utils/dateutil/relativedelta.py b/utils/dateutil/relativedelta.py new file mode 100644 index 000000000..562a7d3c4 --- /dev/null +++ b/utils/dateutil/relativedelta.py @@ -0,0 +1,432 @@ +""" +Copyright (c) 2003-2007 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" + +import datetime +import calendar + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +class weekday(object): + __slots__ = ["weekday", "n"] + + def __init__(self, weekday, n=None): + self.weekday = weekday + self.n = n + + def __call__(self, n): + if n == self.n: + return self + else: + return self.__class__(self.weekday, n) + + def __eq__(self, other): + try: + if self.weekday != other.weekday or self.n != other.n: + return False + except AttributeError: + return False + return True + + def __repr__(self): + s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] + if not self.n: + return s + else: + return "%s(%+d)" % (s, self.n) + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) + +class relativedelta: + """ +The relativedelta type is based on the specification of the excelent +work done by M.-A. Lemburg in his mx.DateTime extension. However, +notice that this type does *NOT* implement the same algorithm as +his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + +There's two different ways to build a relativedelta instance. The +first one is passing it two date/datetime classes: + + relativedelta(datetime1, datetime2) + +And the other way is to use the following keyword arguments: + + year, month, day, hour, minute, second, microsecond: + Absolute information. + + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative. + + weekday: + One of the weekday instances (MO, TU, etc). These instances may + receive a parameter N, specifying the Nth weekday, which could + be positive or negative (like MO(+1) or MO(-2). Not specifying + it is the same as specifying +1. You can also use an integer, + where 0=MO. + + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. + + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. + +Here is the behavior of operations with relativedelta: + +1) Calculate the absolute year, using the 'year' argument, or the + original datetime year, if the argument is not present. + +2) Add the relative 'years' argument to the absolute year. + +3) Do steps 1 and 2 for month/months. + +4) Calculate the absolute day, using the 'day' argument, or the + original datetime day, if the argument is not present. Then, + subtract from the day until it fits in the year and month + found after their operations. + +5) Add the relative 'days' argument to the absolute day. Notice + that the 'weeks' argument is multiplied by 7 and added to + 'days'. + +6) Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds, + microsecond/microseconds. + +7) If the 'weekday' argument is present, calculate the weekday, + with the given (wday, nth) tuple. wday is the index of the + weekday (0-6, 0=Mon), and nth is the number of weeks to add + forward or backward, depending on its signal. Notice that if + the calculated date is already Monday, for example, using + (0, 1) or (0, -1) won't change the day. + """ + + def __init__(self, dt1=None, dt2=None, + years=0, months=0, days=0, leapdays=0, weeks=0, + hours=0, minutes=0, seconds=0, microseconds=0, + year=None, month=None, day=None, weekday=None, + yearday=None, nlyearday=None, + hour=None, minute=None, second=None, microsecond=None): + if dt1 and dt2: + if not isinstance(dt1, datetime.date) or \ + not isinstance(dt2, datetime.date): + raise TypeError, "relativedelta only diffs datetime/date" + if type(dt1) is not type(dt2): + if not isinstance(dt1, datetime.datetime): + dt1 = datetime.datetime.fromordinal(dt1.toordinal()) + elif not isinstance(dt2, datetime.datetime): + dt2 = datetime.datetime.fromordinal(dt2.toordinal()) + self.years = 0 + self.months = 0 + self.days = 0 + self.leapdays = 0 + self.hours = 0 + self.minutes = 0 + self.seconds = 0 + self.microseconds = 0 + self.year = None + self.month = None + self.day = None + self.weekday = None + self.hour = None + self.minute = None + self.second = None + self.microsecond = None + self._has_time = 0 + + months = (dt1.year*12+dt1.month)-(dt2.year*12+dt2.month) + self._set_months(months) + dtm = self.__radd__(dt2) + if dt1 < dt2: + while dt1 > dtm: + months += 1 + self._set_months(months) + dtm = self.__radd__(dt2) + else: + while dt1 < dtm: + months -= 1 + self._set_months(months) + dtm = self.__radd__(dt2) + delta = dt1 - dtm + self.seconds = delta.seconds+delta.days*86400 + self.microseconds = delta.microseconds + else: + self.years = years + self.months = months + self.days = days+weeks*7 + self.leapdays = leapdays + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.microseconds = microseconds + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.microsecond = microsecond + + if type(weekday) is int: + self.weekday = weekdays[weekday] + else: + self.weekday = weekday + + yday = 0 + if nlyearday: + yday = nlyearday + elif yearday: + yday = yearday + if yearday > 59: + self.leapdays = -1 + if yday: + ydayidx = [31,59,90,120,151,181,212,243,273,304,334,366] + for idx, ydays in enumerate(ydayidx): + if yday <= ydays: + self.month = idx+1 + if idx == 0: + self.day = ydays + else: + self.day = yday-ydayidx[idx-1] + break + else: + raise ValueError, "invalid year day (%d)" % yday + + self._fix() + + def _fix(self): + if abs(self.microseconds) > 999999: + s = self.microseconds//abs(self.microseconds) + div, mod = divmod(self.microseconds*s, 1000000) + self.microseconds = mod*s + self.seconds += div*s + if abs(self.seconds) > 59: + s = self.seconds//abs(self.seconds) + div, mod = divmod(self.seconds*s, 60) + self.seconds = mod*s + self.minutes += div*s + if abs(self.minutes) > 59: + s = self.minutes//abs(self.minutes) + div, mod = divmod(self.minutes*s, 60) + self.minutes = mod*s + self.hours += div*s + if abs(self.hours) > 23: + s = self.hours//abs(self.hours) + div, mod = divmod(self.hours*s, 24) + self.hours = mod*s + self.days += div*s + if abs(self.months) > 11: + s = self.months//abs(self.months) + div, mod = divmod(self.months*s, 12) + self.months = mod*s + self.years += div*s + if (self.hours or self.minutes or self.seconds or self.microseconds or + self.hour is not None or self.minute is not None or + self.second is not None or self.microsecond is not None): + self._has_time = 1 + else: + self._has_time = 0 + + def _set_months(self, months): + self.months = months + if abs(self.months) > 11: + s = self.months//abs(self.months) + div, mod = divmod(self.months*s, 12) + self.months = mod*s + self.years = div*s + else: + self.years = 0 + + def __radd__(self, other): + if not isinstance(other, datetime.date): + raise TypeError, "unsupported type for add operation" + elif self._has_time and not isinstance(other, datetime.datetime): + other = datetime.datetime.fromordinal(other.toordinal()) + year = (self.year or other.year)+self.years + month = self.month or other.month + if self.months: + assert 1 <= abs(self.months) <= 12 + month += self.months + if month > 12: + year += 1 + month -= 12 + elif month < 1: + year -= 1 + month += 12 + day = min(calendar.monthrange(year, month)[1], + self.day or other.day) + repl = {"year": year, "month": month, "day": day} + for attr in ["hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + repl[attr] = value + days = self.days + if self.leapdays and month > 2 and calendar.isleap(year): + days += self.leapdays + ret = (other.replace(**repl) + + datetime.timedelta(days=days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds, + microseconds=self.microseconds)) + if self.weekday: + weekday, nth = self.weekday.weekday, self.weekday.n or 1 + jumpdays = (abs(nth)-1)*7 + if nth > 0: + jumpdays += (7-ret.weekday()+weekday)%7 + else: + jumpdays += (ret.weekday()-weekday)%7 + jumpdays *= -1 + ret += datetime.timedelta(days=jumpdays) + return ret + + def __rsub__(self, other): + return self.__neg__().__radd__(other) + + def __add__(self, other): + if not isinstance(other, relativedelta): + raise TypeError, "unsupported type for add operation" + return relativedelta(years=other.years+self.years, + months=other.months+self.months, + days=other.days+self.days, + hours=other.hours+self.hours, + minutes=other.minutes+self.minutes, + seconds=other.seconds+self.seconds, + microseconds=other.microseconds+self.microseconds, + leapdays=other.leapdays or self.leapdays, + year=other.year or self.year, + month=other.month or self.month, + day=other.day or self.day, + weekday=other.weekday or self.weekday, + hour=other.hour or self.hour, + minute=other.minute or self.minute, + second=other.second or self.second, + microsecond=other.second or self.microsecond) + + def __sub__(self, other): + if not isinstance(other, relativedelta): + raise TypeError, "unsupported type for sub operation" + return relativedelta(years=other.years-self.years, + months=other.months-self.months, + days=other.days-self.days, + hours=other.hours-self.hours, + minutes=other.minutes-self.minutes, + seconds=other.seconds-self.seconds, + microseconds=other.microseconds-self.microseconds, + leapdays=other.leapdays or self.leapdays, + year=other.year or self.year, + month=other.month or self.month, + day=other.day or self.day, + weekday=other.weekday or self.weekday, + hour=other.hour or self.hour, + minute=other.minute or self.minute, + second=other.second or self.second, + microsecond=other.second or self.microsecond) + + def __neg__(self): + return relativedelta(years=-self.years, + months=-self.months, + days=-self.days, + hours=-self.hours, + minutes=-self.minutes, + seconds=-self.seconds, + microseconds=-self.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __nonzero__(self): + return not (not self.years and + not self.months and + not self.days and + not self.hours and + not self.minutes and + not self.seconds and + not self.microseconds and + not self.leapdays and + self.year is None and + self.month is None and + self.day is None and + self.weekday is None and + self.hour is None and + self.minute is None and + self.second is None and + self.microsecond is None) + + def __mul__(self, other): + f = float(other) + return relativedelta(years=self.years*f, + months=self.months*f, + days=self.days*f, + hours=self.hours*f, + minutes=self.minutes*f, + seconds=self.seconds*f, + microseconds=self.microseconds*f, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __eq__(self, other): + if not isinstance(other, relativedelta): + return False + if self.weekday or other.weekday: + if not self.weekday or not other.weekday: + return False + if self.weekday.weekday != other.weekday.weekday: + return False + n1, n2 = self.weekday.n, other.weekday.n + if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): + return False + return (self.years == other.years and + self.months == other.months and + self.days == other.days and + self.hours == other.hours and + self.minutes == other.minutes and + self.seconds == other.seconds and + self.leapdays == other.leapdays and + self.year == other.year and + self.month == other.month and + self.day == other.day and + self.hour == other.hour and + self.minute == other.minute and + self.second == other.second and + self.microsecond == other.microsecond) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + return self.__mul__(1/float(other)) + + def __repr__(self): + l = [] + for attr in ["years", "months", "days", "leapdays", + "hours", "minutes", "seconds", "microseconds"]: + value = getattr(self, attr) + if value: + l.append("%s=%+d" % (attr, value)) + for attr in ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, `value`)) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + +# vim:ts=4:sw=4:et diff --git a/utils/dateutil/rrule.py b/utils/dateutil/rrule.py new file mode 100644 index 000000000..4c21d2d1d --- /dev/null +++ b/utils/dateutil/rrule.py @@ -0,0 +1,1097 @@ +""" +Copyright (c) 2003-2007 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" + +import itertools +import datetime +import calendar +import thread +import sys + +__all__ = ["rrule", "rruleset", "rrulestr", + "YEARLY", "MONTHLY", "WEEKLY", "DAILY", + "HOURLY", "MINUTELY", "SECONDLY", + "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +# Every mask is 7 days longer to handle cross-year weekly periods. +M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30+ + [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) +M365MASK = list(M366MASK) +M29, M30, M31 = range(1,30), range(1,31), range(1,32) +MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +MDAY365MASK = list(MDAY366MASK) +M29, M30, M31 = range(-29,0), range(-30,0), range(-31,0) +NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +NMDAY365MASK = list(NMDAY366MASK) +M366RANGE = (0,31,60,91,121,152,182,213,244,274,305,335,366) +M365RANGE = (0,31,59,90,120,151,181,212,243,273,304,334,365) +WDAYMASK = [0,1,2,3,4,5,6]*55 +del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] +MDAY365MASK = tuple(MDAY365MASK) +M365MASK = tuple(M365MASK) + +(YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY) = range(7) + +# Imported on demand. +easter = None +parser = None + +class weekday(object): + __slots__ = ["weekday", "n"] + + def __init__(self, weekday, n=None): + if n == 0: + raise ValueError, "Can't create weekday with n == 0" + self.weekday = weekday + self.n = n + + def __call__(self, n): + if n == self.n: + return self + else: + return self.__class__(self.weekday, n) + + def __eq__(self, other): + try: + if self.weekday != other.weekday or self.n != other.n: + return False + except AttributeError: + return False + return True + + def __repr__(self): + s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] + if not self.n: + return s + else: + return "%s(%+d)" % (s, self.n) + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) + +class rrulebase: + def __init__(self, cache=False): + if cache: + self._cache = [] + self._cache_lock = thread.allocate_lock() + self._cache_gen = self._iter() + self._cache_complete = False + else: + self._cache = None + self._cache_complete = False + self._len = None + + def __iter__(self): + if self._cache_complete: + return iter(self._cache) + elif self._cache is None: + return self._iter() + else: + return self._iter_cached() + + def _iter_cached(self): + i = 0 + gen = self._cache_gen + cache = self._cache + acquire = self._cache_lock.acquire + release = self._cache_lock.release + while gen: + if i == len(cache): + acquire() + if self._cache_complete: + break + try: + for j in range(10): + cache.append(gen.next()) + except StopIteration: + self._cache_gen = gen = None + self._cache_complete = True + break + release() + yield cache[i] + i += 1 + while i < self._len: + yield cache[i] + i += 1 + + def __getitem__(self, item): + if self._cache_complete: + return self._cache[item] + elif isinstance(item, slice): + if item.step and item.step < 0: + return list(iter(self))[item] + else: + return list(itertools.islice(self, + item.start or 0, + item.stop or sys.maxint, + item.step or 1)) + elif item >= 0: + gen = iter(self) + try: + for i in range(item+1): + res = gen.next() + except StopIteration: + raise IndexError + return res + else: + return list(iter(self))[item] + + def __contains__(self, item): + if self._cache_complete: + return item in self._cache + else: + for i in self: + if i == item: + return True + elif i > item: + return False + return False + + # __len__() introduces a large performance penality. + def count(self): + if self._len is None: + for x in self: pass + return self._len + + def before(self, dt, inc=False): + if self._cache_complete: + gen = self._cache + else: + gen = self + last = None + if inc: + for i in gen: + if i > dt: + break + last = i + else: + for i in gen: + if i >= dt: + break + last = i + return last + + def after(self, dt, inc=False): + if self._cache_complete: + gen = self._cache + else: + gen = self + if inc: + for i in gen: + if i >= dt: + return i + else: + for i in gen: + if i > dt: + return i + return None + + def between(self, after, before, inc=False): + if self._cache_complete: + gen = self._cache + else: + gen = self + started = False + l = [] + if inc: + for i in gen: + if i > before: + break + elif not started: + if i >= after: + started = True + l.append(i) + else: + l.append(i) + else: + for i in gen: + if i >= before: + break + elif not started: + if i > after: + started = True + l.append(i) + else: + l.append(i) + return l + +class rrule(rrulebase): + def __init__(self, freq, dtstart=None, + interval=1, wkst=None, count=None, until=None, bysetpos=None, + bymonth=None, bymonthday=None, byyearday=None, byeaster=None, + byweekno=None, byweekday=None, + byhour=None, byminute=None, bysecond=None, + cache=False): + rrulebase.__init__(self, cache) + global easter + if not dtstart: + dtstart = datetime.datetime.now().replace(microsecond=0) + elif not isinstance(dtstart, datetime.datetime): + dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) + else: + dtstart = dtstart.replace(microsecond=0) + self._dtstart = dtstart + self._tzinfo = dtstart.tzinfo + self._freq = freq + self._interval = interval + self._count = count + if until and not isinstance(until, datetime.datetime): + until = datetime.datetime.fromordinal(until.toordinal()) + self._until = until + if wkst is None: + self._wkst = calendar.firstweekday() + elif type(wkst) is int: + self._wkst = wkst + else: + self._wkst = wkst.weekday + if bysetpos is None: + self._bysetpos = None + elif type(bysetpos) is int: + if bysetpos == 0 or not (-366 <= bysetpos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + self._bysetpos = (bysetpos,) + else: + self._bysetpos = tuple(bysetpos) + for pos in self._bysetpos: + if pos == 0 or not (-366 <= pos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + if not (byweekno or byyearday or bymonthday or + byweekday is not None or byeaster is not None): + if freq == YEARLY: + if not bymonth: + bymonth = dtstart.month + bymonthday = dtstart.day + elif freq == MONTHLY: + bymonthday = dtstart.day + elif freq == WEEKLY: + byweekday = dtstart.weekday() + # bymonth + if not bymonth: + self._bymonth = None + elif type(bymonth) is int: + self._bymonth = (bymonth,) + else: + self._bymonth = tuple(bymonth) + # byyearday + if not byyearday: + self._byyearday = None + elif type(byyearday) is int: + self._byyearday = (byyearday,) + else: + self._byyearday = tuple(byyearday) + # byeaster + if byeaster is not None: + if not easter: + from dateutil import easter + if type(byeaster) is int: + self._byeaster = (byeaster,) + else: + self._byeaster = tuple(byeaster) + else: + self._byeaster = None + # bymonthay + if not bymonthday: + self._bymonthday = () + self._bynmonthday = () + elif type(bymonthday) is int: + if bymonthday < 0: + self._bynmonthday = (bymonthday,) + self._bymonthday = () + else: + self._bymonthday = (bymonthday,) + self._bynmonthday = () + else: + self._bymonthday = tuple([x for x in bymonthday if x > 0]) + self._bynmonthday = tuple([x for x in bymonthday if x < 0]) + # byweekno + if byweekno is None: + self._byweekno = None + elif type(byweekno) is int: + self._byweekno = (byweekno,) + else: + self._byweekno = tuple(byweekno) + # byweekday / bynweekday + if byweekday is None: + self._byweekday = None + self._bynweekday = None + elif type(byweekday) is int: + self._byweekday = (byweekday,) + self._bynweekday = None + elif hasattr(byweekday, "n"): + if not byweekday.n or freq > MONTHLY: + self._byweekday = (byweekday.weekday,) + self._bynweekday = None + else: + self._bynweekday = ((byweekday.weekday, byweekday.n),) + self._byweekday = None + else: + self._byweekday = [] + self._bynweekday = [] + for wday in byweekday: + if type(wday) is int: + self._byweekday.append(wday) + elif not wday.n or freq > MONTHLY: + self._byweekday.append(wday.weekday) + else: + self._bynweekday.append((wday.weekday, wday.n)) + self._byweekday = tuple(self._byweekday) + self._bynweekday = tuple(self._bynweekday) + if not self._byweekday: + self._byweekday = None + elif not self._bynweekday: + self._bynweekday = None + # byhour + if byhour is None: + if freq < HOURLY: + self._byhour = (dtstart.hour,) + else: + self._byhour = None + elif type(byhour) is int: + self._byhour = (byhour,) + else: + self._byhour = tuple(byhour) + # byminute + if byminute is None: + if freq < MINUTELY: + self._byminute = (dtstart.minute,) + else: + self._byminute = None + elif type(byminute) is int: + self._byminute = (byminute,) + else: + self._byminute = tuple(byminute) + # bysecond + if bysecond is None: + if freq < SECONDLY: + self._bysecond = (dtstart.second,) + else: + self._bysecond = None + elif type(bysecond) is int: + self._bysecond = (bysecond,) + else: + self._bysecond = tuple(bysecond) + + if self._freq >= HOURLY: + self._timeset = None + else: + self._timeset = [] + for hour in self._byhour: + for minute in self._byminute: + for second in self._bysecond: + self._timeset.append( + datetime.time(hour, minute, second, + tzinfo=self._tzinfo)) + self._timeset.sort() + self._timeset = tuple(self._timeset) + + def _iter(self): + year, month, day, hour, minute, second, weekday, yearday, _ = \ + self._dtstart.timetuple() + + # Some local variables to speed things up a bit + freq = self._freq + interval = self._interval + wkst = self._wkst + until = self._until + bymonth = self._bymonth + byweekno = self._byweekno + byyearday = self._byyearday + byweekday = self._byweekday + byeaster = self._byeaster + bymonthday = self._bymonthday + bynmonthday = self._bynmonthday + bysetpos = self._bysetpos + byhour = self._byhour + byminute = self._byminute + bysecond = self._bysecond + + ii = _iterinfo(self) + ii.rebuild(year, month) + + getdayset = {YEARLY:ii.ydayset, + MONTHLY:ii.mdayset, + WEEKLY:ii.wdayset, + DAILY:ii.ddayset, + HOURLY:ii.ddayset, + MINUTELY:ii.ddayset, + SECONDLY:ii.ddayset}[freq] + + if freq < HOURLY: + timeset = self._timeset + else: + gettimeset = {HOURLY:ii.htimeset, + MINUTELY:ii.mtimeset, + SECONDLY:ii.stimeset}[freq] + if ((freq >= HOURLY and + self._byhour and hour not in self._byhour) or + (freq >= MINUTELY and + self._byminute and minute not in self._byminute) or + (freq >= SECONDLY and + self._bysecond and minute not in self._bysecond)): + timeset = () + else: + timeset = gettimeset(hour, minute, second) + + total = 0 + count = self._count + while True: + # Get dayset with the right frequency + dayset, start, end = getdayset(year, month, day) + + # Do the "hard" work ;-) + filtered = False + for i in dayset[start:end]: + if ((bymonth and ii.mmask[i] not in bymonth) or + (byweekno and not ii.wnomask[i]) or + (byweekday and ii.wdaymask[i] not in byweekday) or + (ii.nwdaymask and not ii.nwdaymask[i]) or + (byeaster and not ii.eastermask[i]) or + ((bymonthday or bynmonthday) and + ii.mdaymask[i] not in bymonthday and + ii.nmdaymask[i] not in bynmonthday) or + (byyearday and + ((i < ii.yearlen and i+1 not in byyearday + and -ii.yearlen+i not in byyearday) or + (i >= ii.yearlen and i+1-ii.yearlen not in byyearday + and -ii.nextyearlen+i-ii.yearlen + not in byyearday)))): + dayset[i] = None + filtered = True + + # Output results + if bysetpos and timeset: + poslist = [] + for pos in bysetpos: + if pos < 0: + daypos, timepos = divmod(pos, len(timeset)) + else: + daypos, timepos = divmod(pos-1, len(timeset)) + try: + i = [x for x in dayset[start:end] + if x is not None][daypos] + time = timeset[timepos] + except IndexError: + pass + else: + date = datetime.date.fromordinal(ii.yearordinal+i) + res = datetime.datetime.combine(date, time) + if res not in poslist: + poslist.append(res) + poslist.sort() + for res in poslist: + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + total += 1 + yield res + if count: + count -= 1 + if not count: + self._len = total + return + else: + for i in dayset[start:end]: + if i is not None: + date = datetime.date.fromordinal(ii.yearordinal+i) + for time in timeset: + res = datetime.datetime.combine(date, time) + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + total += 1 + yield res + if count: + count -= 1 + if not count: + self._len = total + return + + # Handle frequency and interval + fixday = False + if freq == YEARLY: + year += interval + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == MONTHLY: + month += interval + if month > 12: + div, mod = divmod(month, 12) + month = mod + year += div + if month == 0: + month = 12 + year -= 1 + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == WEEKLY: + if wkst > weekday: + day += -(weekday+1+(6-wkst))+self._interval*7 + else: + day += -(weekday-wkst)+self._interval*7 + weekday = wkst + fixday = True + elif freq == DAILY: + day += interval + fixday = True + elif freq == HOURLY: + if filtered: + # Jump to one iteration before next day + hour += ((23-hour)//interval)*interval + while True: + hour += interval + div, mod = divmod(hour, 24) + if div: + hour = mod + day += div + fixday = True + if not byhour or hour in byhour: + break + timeset = gettimeset(hour, minute, second) + elif freq == MINUTELY: + if filtered: + # Jump to one iteration before next day + minute += ((1439-(hour*60+minute))//interval)*interval + while True: + minute += interval + div, mod = divmod(minute, 60) + if div: + minute = mod + hour += div + div, mod = divmod(hour, 24) + if div: + hour = mod + day += div + fixday = True + filtered = False + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute)): + break + timeset = gettimeset(hour, minute, second) + elif freq == SECONDLY: + if filtered: + # Jump to one iteration before next day + second += (((86399-(hour*3600+minute*60+second)) + //interval)*interval) + while True: + second += self._interval + div, mod = divmod(second, 60) + if div: + second = mod + minute += div + div, mod = divmod(minute, 60) + if div: + minute = mod + hour += div + div, mod = divmod(hour, 24) + if div: + hour = mod + day += div + fixday = True + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute) and + (not bysecond or second in bysecond)): + break + timeset = gettimeset(hour, minute, second) + + if fixday and day > 28: + daysinmonth = calendar.monthrange(year, month)[1] + if day > daysinmonth: + while day > daysinmonth: + day -= daysinmonth + month += 1 + if month == 13: + month = 1 + year += 1 + if year > datetime.MAXYEAR: + self._len = total + return + daysinmonth = calendar.monthrange(year, month)[1] + ii.rebuild(year, month) + +class _iterinfo(object): + __slots__ = ["rrule", "lastyear", "lastmonth", + "yearlen", "nextyearlen", "yearordinal", "yearweekday", + "mmask", "mrange", "mdaymask", "nmdaymask", + "wdaymask", "wnomask", "nwdaymask", "eastermask"] + + def __init__(self, rrule): + for attr in self.__slots__: + setattr(self, attr, None) + self.rrule = rrule + + def rebuild(self, year, month): + # Every mask is 7 days longer to handle cross-year weekly periods. + rr = self.rrule + if year != self.lastyear: + self.yearlen = 365+calendar.isleap(year) + self.nextyearlen = 365+calendar.isleap(year+1) + firstyday = datetime.date(year, 1, 1) + self.yearordinal = firstyday.toordinal() + self.yearweekday = firstyday.weekday() + + wday = datetime.date(year, 1, 1).weekday() + if self.yearlen == 365: + self.mmask = M365MASK + self.mdaymask = MDAY365MASK + self.nmdaymask = NMDAY365MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M365RANGE + else: + self.mmask = M366MASK + self.mdaymask = MDAY366MASK + self.nmdaymask = NMDAY366MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M366RANGE + + if not rr._byweekno: + self.wnomask = None + else: + self.wnomask = [0]*(self.yearlen+7) + #no1wkst = firstwkst = self.wdaymask.index(rr._wkst) + no1wkst = firstwkst = (7-self.yearweekday+rr._wkst)%7 + if no1wkst >= 4: + no1wkst = 0 + # Number of days in the year, plus the days we got + # from last year. + wyearlen = self.yearlen+(self.yearweekday-rr._wkst)%7 + else: + # Number of days in the year, minus the days we + # left in last year. + wyearlen = self.yearlen-no1wkst + div, mod = divmod(wyearlen, 7) + numweeks = div+mod//4 + for n in rr._byweekno: + if n < 0: + n += numweeks+1 + if not (0 < n <= numweeks): + continue + if n > 1: + i = no1wkst+(n-1)*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + else: + i = no1wkst + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if 1 in rr._byweekno: + # Check week number 1 of next year as well + # TODO: Check -numweeks for next year. + i = no1wkst+numweeks*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + if i < self.yearlen: + # If week starts in next year, we + # don't care about it. + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if no1wkst: + # Check last week number of last year as + # well. If no1wkst is 0, either the year + # started on week start, or week number 1 + # got days from last year, so there are no + # days from last year's last week number in + # this year. + if -1 not in rr._byweekno: + lyearweekday = datetime.date(year-1,1,1).weekday() + lno1wkst = (7-lyearweekday+rr._wkst)%7 + lyearlen = 365+calendar.isleap(year-1) + if lno1wkst >= 4: + lno1wkst = 0 + lnumweeks = 52+(lyearlen+ + (lyearweekday-rr._wkst)%7)%7//4 + else: + lnumweeks = 52+(self.yearlen-no1wkst)%7//4 + else: + lnumweeks = -1 + if lnumweeks in rr._byweekno: + for i in range(no1wkst): + self.wnomask[i] = 1 + + if (rr._bynweekday and + (month != self.lastmonth or year != self.lastyear)): + ranges = [] + if rr._freq == YEARLY: + if rr._bymonth: + for month in rr._bymonth: + ranges.append(self.mrange[month-1:month+1]) + else: + ranges = [(0, self.yearlen)] + elif rr._freq == MONTHLY: + ranges = [self.mrange[month-1:month+1]] + if ranges: + # Weekly frequency won't get here, so we may not + # care about cross-year weekly periods. + self.nwdaymask = [0]*self.yearlen + for first, last in ranges: + last -= 1 + for wday, n in rr._bynweekday: + if n < 0: + i = last+(n+1)*7 + i -= (self.wdaymask[i]-wday)%7 + else: + i = first+(n-1)*7 + i += (7-self.wdaymask[i]+wday)%7 + if first <= i <= last: + self.nwdaymask[i] = 1 + + if rr._byeaster: + self.eastermask = [0]*(self.yearlen+7) + eyday = easter.easter(year).toordinal()-self.yearordinal + for offset in rr._byeaster: + self.eastermask[eyday+offset] = 1 + + self.lastyear = year + self.lastmonth = month + + def ydayset(self, year, month, day): + return range(self.yearlen), 0, self.yearlen + + def mdayset(self, year, month, day): + set = [None]*self.yearlen + start, end = self.mrange[month-1:month+1] + for i in range(start, end): + set[i] = i + return set, start, end + + def wdayset(self, year, month, day): + # We need to handle cross-year weeks here. + set = [None]*(self.yearlen+7) + i = datetime.date(year, month, day).toordinal()-self.yearordinal + start = i + for j in range(7): + set[i] = i + i += 1 + #if (not (0 <= i < self.yearlen) or + # self.wdaymask[i] == self.rrule._wkst): + # This will cross the year boundary, if necessary. + if self.wdaymask[i] == self.rrule._wkst: + break + return set, start, i + + def ddayset(self, year, month, day): + set = [None]*self.yearlen + i = datetime.date(year, month, day).toordinal()-self.yearordinal + set[i] = i + return set, i, i+1 + + def htimeset(self, hour, minute, second): + set = [] + rr = self.rrule + for minute in rr._byminute: + for second in rr._bysecond: + set.append(datetime.time(hour, minute, second, + tzinfo=rr._tzinfo)) + set.sort() + return set + + def mtimeset(self, hour, minute, second): + set = [] + rr = self.rrule + for second in rr._bysecond: + set.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) + set.sort() + return set + + def stimeset(self, hour, minute, second): + return (datetime.time(hour, minute, second, + tzinfo=self.rrule._tzinfo),) + + +class rruleset(rrulebase): + + class _genitem: + def __init__(self, genlist, gen): + try: + self.dt = gen() + genlist.append(self) + except StopIteration: + pass + self.genlist = genlist + self.gen = gen + + def next(self): + try: + self.dt = self.gen() + except StopIteration: + self.genlist.remove(self) + + def __cmp__(self, other): + return cmp(self.dt, other.dt) + + def __init__(self, cache=False): + rrulebase.__init__(self, cache) + self._rrule = [] + self._rdate = [] + self._exrule = [] + self._exdate = [] + + def rrule(self, rrule): + self._rrule.append(rrule) + + def rdate(self, rdate): + self._rdate.append(rdate) + + def exrule(self, exrule): + self._exrule.append(exrule) + + def exdate(self, exdate): + self._exdate.append(exdate) + + def _iter(self): + rlist = [] + self._rdate.sort() + self._genitem(rlist, iter(self._rdate).next) + for gen in [iter(x).next for x in self._rrule]: + self._genitem(rlist, gen) + rlist.sort() + exlist = [] + self._exdate.sort() + self._genitem(exlist, iter(self._exdate).next) + for gen in [iter(x).next for x in self._exrule]: + self._genitem(exlist, gen) + exlist.sort() + lastdt = None + total = 0 + while rlist: + ritem = rlist[0] + if not lastdt or lastdt != ritem.dt: + while exlist and exlist[0] < ritem: + exlist[0].next() + exlist.sort() + if not exlist or ritem != exlist[0]: + total += 1 + yield ritem.dt + lastdt = ritem.dt + ritem.next() + rlist.sort() + self._len = total + +class _rrulestr: + + _freq_map = {"YEARLY": YEARLY, + "MONTHLY": MONTHLY, + "WEEKLY": WEEKLY, + "DAILY": DAILY, + "HOURLY": HOURLY, + "MINUTELY": MINUTELY, + "SECONDLY": SECONDLY} + + _weekday_map = {"MO":0,"TU":1,"WE":2,"TH":3,"FR":4,"SA":5,"SU":6} + + def _handle_int(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = int(value) + + def _handle_int_list(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = [int(x) for x in value.split(',')] + + _handle_INTERVAL = _handle_int + _handle_COUNT = _handle_int + _handle_BYSETPOS = _handle_int_list + _handle_BYMONTH = _handle_int_list + _handle_BYMONTHDAY = _handle_int_list + _handle_BYYEARDAY = _handle_int_list + _handle_BYEASTER = _handle_int_list + _handle_BYWEEKNO = _handle_int_list + _handle_BYHOUR = _handle_int_list + _handle_BYMINUTE = _handle_int_list + _handle_BYSECOND = _handle_int_list + + def _handle_FREQ(self, rrkwargs, name, value, **kwargs): + rrkwargs["freq"] = self._freq_map[value] + + def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): + global parser + if not parser: + from dateutil import parser + try: + rrkwargs["until"] = parser.parse(value, + ignoretz=kwargs.get("ignoretz"), + tzinfos=kwargs.get("tzinfos")) + except ValueError: + raise ValueError, "invalid until date" + + def _handle_WKST(self, rrkwargs, name, value, **kwargs): + rrkwargs["wkst"] = self._weekday_map[value] + + def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwarsg): + l = [] + for wday in value.split(','): + for i in range(len(wday)): + if wday[i] not in '+-0123456789': + break + n = wday[:i] or None + w = wday[i:] + if n: n = int(n) + l.append(weekdays[self._weekday_map[w]](n)) + rrkwargs["byweekday"] = l + + _handle_BYDAY = _handle_BYWEEKDAY + + def _parse_rfc_rrule(self, line, + dtstart=None, + cache=False, + ignoretz=False, + tzinfos=None): + if line.find(':') != -1: + name, value = line.split(':') + if name != "RRULE": + raise ValueError, "unknown parameter name" + else: + value = line + rrkwargs = {} + for pair in value.split(';'): + name, value = pair.split('=') + name = name.upper() + value = value.upper() + try: + getattr(self, "_handle_"+name)(rrkwargs, name, value, + ignoretz=ignoretz, + tzinfos=tzinfos) + except AttributeError: + raise ValueError, "unknown parameter '%s'" % name + except (KeyError, ValueError): + raise ValueError, "invalid '%s': %s" % (name, value) + return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + s = s.upper() + if not s.strip(): + raise ValueError, "empty string" + if unfold: + lines = s.splitlines() + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + else: + lines = s.split() + if (not forceset and len(lines) == 1 and + (s.find(':') == -1 or s.startswith('RRULE:'))): + return self._parse_rfc_rrule(lines[0], cache=cache, + dtstart=dtstart, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + rrulevals = [] + rdatevals = [] + exrulevals = [] + exdatevals = [] + for line in lines: + if not line: + continue + if line.find(':') == -1: + name = "RRULE" + value = line + else: + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError, "empty property name" + name = parms[0] + parms = parms[1:] + if name == "RRULE": + for parm in parms: + raise ValueError, "unsupported RRULE parm: "+parm + rrulevals.append(value) + elif name == "RDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError, "unsupported RDATE parm: "+parm + rdatevals.append(value) + elif name == "EXRULE": + for parm in parms: + raise ValueError, "unsupported EXRULE parm: "+parm + exrulevals.append(value) + elif name == "EXDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError, "unsupported RDATE parm: "+parm + exdatevals.append(value) + elif name == "DTSTART": + for parm in parms: + raise ValueError, "unsupported DTSTART parm: "+parm + if not parser: + from dateutil import parser + dtstart = parser.parse(value, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + raise ValueError, "unsupported property: "+name + if (forceset or len(rrulevals) > 1 or + rdatevals or exrulevals or exdatevals): + if not parser and (rdatevals or exdatevals): + from dateutil import parser + set = rruleset(cache=cache) + for value in rrulevals: + set.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in rdatevals: + for datestr in value.split(','): + set.rdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exrulevals: + set.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exdatevals: + for datestr in value.split(','): + set.exdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + if compatible and dtstart: + set.rdate(dtstart) + return set + else: + return self._parse_rfc_rrule(rrulevals[0], + dtstart=dtstart, + cache=cache, + ignoretz=ignoretz, + tzinfos=tzinfos) + + def __call__(self, s, **kwargs): + return self._parse_rfc(s, **kwargs) + +rrulestr = _rrulestr() + +# vim:ts=4:sw=4:et diff --git a/utils/dateutil/tz.py b/utils/dateutil/tz.py new file mode 100644 index 000000000..0e28d6b33 --- /dev/null +++ b/utils/dateutil/tz.py @@ -0,0 +1,951 @@ +""" +Copyright (c) 2003-2007 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" + +import datetime +import struct +import time +import sys +import os + +relativedelta = None +parser = None +rrule = None + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"] + +try: + from dateutil.tzwin import tzwin, tzwinlocal +except (ImportError, OSError): + tzwin, tzwinlocal = None, None + +ZERO = datetime.timedelta(0) +EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() + +class tzutc(datetime.tzinfo): + + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def __eq__(self, other): + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + +class tzoffset(datetime.tzinfo): + + def __init__(self, name, offset): + self._name = name + self._offset = datetime.timedelta(seconds=offset) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + def tzname(self, dt): + return self._name + + def __eq__(self, other): + return (isinstance(other, tzoffset) and + self._offset == other._offset) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + `self._name`, + self._offset.days*86400+self._offset.seconds) + + __reduce__ = object.__reduce__ + +class tzlocal(datetime.tzinfo): + + _std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + _dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + _dst_offset = _std_offset + + def utcoffset(self, dt): + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if self._isdst(dt): + return self._dst_offset-self._std_offset + else: + return ZERO + + def tzname(self, dt): + return time.tzname[self._isdst(dt)] + + def _isdst(self, dt): + # We can't use mktime here. It is unstable when deciding if + # the hour near to a change is DST or not. + # + # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, + # dt.minute, dt.second, dt.weekday(), 0, -1)) + # return time.localtime(timestamp).tm_isdst + # + # The code above yields the following result: + # + #>>> import tz, datetime + #>>> t = tz.tzlocal() + #>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + #'BRDT' + #>>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() + #'BRST' + #>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + #'BRST' + #>>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() + #'BRDT' + #>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + #'BRDT' + # + # Here is a more stable implementation: + # + timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 + + dt.hour * 3600 + + dt.minute * 60 + + dt.second) + return time.localtime(timestamp+time.timezone).tm_isdst + + def __eq__(self, other): + if not isinstance(other, tzlocal): + return False + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"] + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def __repr__(self): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, `value`)) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + + def __eq__(self, other): + if not isinstance(other, _ttinfo): + return False + return (self.offset == other.offset and + self.delta == other.delta and + self.isdst == other.isdst and + self.abbr == other.abbr and + self.isstd == other.isstd and + self.isgmt == other.isgmt) + + def __ne__(self, other): + return not self.__eq__(other) + + def __getstate__(self): + state = {} + for name in self.__slots__: + state[name] = getattr(self, name, None) + return state + + def __setstate__(self, state): + for name in self.__slots__: + if name in state: + setattr(self, name, state[name]) + +class tzfile(datetime.tzinfo): + + # http://www.twinsun.com/tz/tz-link.htm + # ftp://elsie.nci.nih.gov/pub/tz*.tar.gz + + def __init__(self, fileobj): + if isinstance(fileobj, basestring): + self._filename = fileobj + fileobj = open(fileobj) + elif hasattr(fileobj, "name"): + self._filename = fileobj.name + else: + self._filename = `fileobj` + + # From tzfile(5): + # + # The time zone information files used by tzset(3) + # begin with the magic characters "TZif" to identify + # them as time zone information files, followed by + # sixteen bytes reserved for future use, followed by + # six four-byte values of type long, written in a + # ``standard'' byte order (the high-order byte + # of the value is written first). + + if fileobj.read(4) != "TZif": + raise ValueError, "magic not found" + + fileobj.read(16) + + ( + # The number of UTC/local indicators stored in the file. + ttisgmtcnt, + + # The number of standard/wall indicators stored in the file. + ttisstdcnt, + + # The number of leap seconds for which data is + # stored in the file. + leapcnt, + + # The number of "transition times" for which data + # is stored in the file. + timecnt, + + # The number of "local time types" for which data + # is stored in the file (must not be zero). + typecnt, + + # The number of characters of "time zone + # abbreviation strings" stored in the file. + charcnt, + + ) = struct.unpack(">6l", fileobj.read(24)) + + # The above header is followed by tzh_timecnt four-byte + # values of type long, sorted in ascending order. + # These values are written in ``standard'' byte order. + # Each is used as a transition time (as returned by + # time(2)) at which the rules for computing local time + # change. + + if timecnt: + self._trans_list = struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4)) + else: + self._trans_list = [] + + # Next come tzh_timecnt one-byte values of type unsigned + # char; each one tells which of the different types of + # ``local time'' types described in the file is associated + # with the same-indexed transition time. These values + # serve as indices into an array of ttinfo structures that + # appears next in the file. + + if timecnt: + self._trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + self._trans_idx = [] + + # Each ttinfo structure is written as a four-byte value + # for tt_gmtoff of type long, in a standard byte + # order, followed by a one-byte value for tt_isdst + # and a one-byte value for tt_abbrind. In each + # structure, tt_gmtoff gives the number of + # seconds to be added to UTC, tt_isdst tells whether + # tm_isdst should be set by localtime(3), and + # tt_abbrind serves as an index into the array of + # time zone abbreviation characters that follow the + # ttinfo structure(s) in the file. + + ttinfo = [] + + for i in range(typecnt): + ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) + + abbr = fileobj.read(charcnt) + + # Then there are tzh_leapcnt pairs of four-byte + # values, written in standard byte order; the + # first value of each pair gives the time (as + # returned by time(2)) at which a leap second + # occurs; the second gives the total number of + # leap seconds to be applied after the given time. + # The pairs of values are sorted in ascending order + # by time. + + # Not used, for now + if leapcnt: + leap = struct.unpack(">%dl" % (leapcnt*2), + fileobj.read(leapcnt*8)) + + # Then there are tzh_ttisstdcnt standard/wall + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as standard + # time or wall clock time, and are used when + # a time zone file is used in handling POSIX-style + # time zone environment variables. + + if ttisstdcnt: + isstd = struct.unpack(">%db" % ttisstdcnt, + fileobj.read(ttisstdcnt)) + + # Finally, there are tzh_ttisgmtcnt UTC/local + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as UTC or + # local time, and are used when a time zone file + # is used in handling POSIX-style time zone envi- + # ronment variables. + + if ttisgmtcnt: + isgmt = struct.unpack(">%db" % ttisgmtcnt, + fileobj.read(ttisgmtcnt)) + + # ** Everything has been read ** + + # Build ttinfo list + self._ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + # Round to full-minutes if that's not the case. Python's + # datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 for some information. + gmtoff = (gmtoff+30)//60*60 + tti = _ttinfo() + tti.offset = gmtoff + tti.delta = datetime.timedelta(seconds=gmtoff) + tti.isdst = isdst + tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] + tti.isstd = (ttisstdcnt > i and isstd[i] != 0) + tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) + self._ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + trans_idx = [] + for idx in self._trans_idx: + trans_idx.append(self._ttinfo_list[idx]) + self._trans_idx = tuple(trans_idx) + + # Set standard, dst, and before ttinfos. before will be + # used when a given time is before any transitions, + # and will be set to the first non-dst ttinfo, or to + # the first dst, if all of them are dst. + self._ttinfo_std = None + self._ttinfo_dst = None + self._ttinfo_before = None + if self._ttinfo_list: + if not self._trans_list: + self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0] + else: + for i in range(timecnt-1,-1,-1): + tti = self._trans_idx[i] + if not self._ttinfo_std and not tti.isdst: + self._ttinfo_std = tti + elif not self._ttinfo_dst and tti.isdst: + self._ttinfo_dst = tti + if self._ttinfo_std and self._ttinfo_dst: + break + else: + if self._ttinfo_dst and not self._ttinfo_std: + self._ttinfo_std = self._ttinfo_dst + + for tti in self._ttinfo_list: + if not tti.isdst: + self._ttinfo_before = tti + break + else: + self._ttinfo_before = self._ttinfo_list[0] + + # Now fix transition times to become relative to wall time. + # + # I'm not sure about this. In my tests, the tz source file + # is setup to wall time, and in the binary file isstd and + # isgmt are off, so it should be in wall time. OTOH, it's + # always in gmt time. Let me know if you have comments + # about this. + laststdoffset = 0 + self._trans_list = list(self._trans_list) + for i in range(len(self._trans_list)): + tti = self._trans_idx[i] + if not tti.isdst: + # This is std time. + self._trans_list[i] += tti.offset + laststdoffset = tti.offset + else: + # This is dst time. Convert to std. + self._trans_list[i] += laststdoffset + self._trans_list = tuple(self._trans_list) + + def _find_ttinfo(self, dt, laststd=0): + timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 + + dt.hour * 3600 + + dt.minute * 60 + + dt.second) + idx = 0 + for trans in self._trans_list: + if timestamp < trans: + break + idx += 1 + else: + return self._ttinfo_std + if idx == 0: + return self._ttinfo_before + if laststd: + while idx > 0: + tti = self._trans_idx[idx-1] + if not tti.isdst: + return tti + idx -= 1 + else: + return self._ttinfo_std + else: + return self._trans_idx[idx-1] + + def utcoffset(self, dt): + if not self._ttinfo_std: + return ZERO + return self._find_ttinfo(dt).delta + + def dst(self, dt): + if not self._ttinfo_dst: + return ZERO + tti = self._find_ttinfo(dt) + if not tti.isdst: + return ZERO + + # The documentation says that utcoffset()-dst() must + # be constant for every dt. + return tti.delta-self._find_ttinfo(dt, laststd=1).delta + + # An alternative for that would be: + # + # return self._ttinfo_dst.offset-self._ttinfo_std.offset + # + # However, this class stores historical changes in the + # dst offset, so I belive that this wouldn't be the right + # way to implement this. + + def tzname(self, dt): + if not self._ttinfo_std: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return False + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + def __ne__(self, other): + return not self.__eq__(other) + + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, `self._filename`) + + def __reduce__(self): + if not os.path.isfile(self._filename): + raise ValueError, "Unpickable %s class" % self.__class__.__name__ + return (self.__class__, (self._filename,)) + +class tzrange(datetime.tzinfo): + + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + global relativedelta + if not relativedelta: + from dateutil import relativedelta + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + if stdoffset is not None: + self._std_offset = datetime.timedelta(seconds=stdoffset) + else: + self._std_offset = ZERO + if dstoffset is not None: + self._dst_offset = datetime.timedelta(seconds=dstoffset) + elif dstabbr and stdoffset is not None: + self._dst_offset = self._std_offset+datetime.timedelta(hours=+1) + else: + self._dst_offset = ZERO + if dstabbr and start is None: + self._start_delta = relativedelta.relativedelta( + hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) + else: + self._start_delta = start + if dstabbr and end is None: + self._end_delta = relativedelta.relativedelta( + hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) + else: + self._end_delta = end + + def utcoffset(self, dt): + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if self._isdst(dt): + return self._dst_offset-self._std_offset + else: + return ZERO + + def tzname(self, dt): + if self._isdst(dt): + return self._dst_abbr + else: + return self._std_abbr + + def _isdst(self, dt): + if not self._start_delta: + return False + year = datetime.datetime(dt.year,1,1) + start = year+self._start_delta + end = year+self._end_delta + dt = dt.replace(tzinfo=None) + if start < end: + return dt >= start and dt < end + else: + return dt >= start or dt < end + + def __eq__(self, other): + if not isinstance(other, tzrange): + return False + return (self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr and + self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._start_delta == other._start_delta and + self._end_delta == other._end_delta) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + +class tzstr(tzrange): + + def __init__(self, s): + global parser + if not parser: + from dateutil import parser + self._s = s + + res = parser._parsetz(s) + if res is None: + raise ValueError, "unknown string format" + + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC"): + res.stdoffset *= -1 + + # We must initialize it first, since _delta() needs + # _std_offset and _dst_offset set. Use False in start/end + # to avoid building it two times. + tzrange.__init__(self, res.stdabbr, res.stdoffset, + res.dstabbr, res.dstoffset, + start=False, end=False) + + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) + + def _delta(self, x, isend=0): + kwargs = {} + if x.month is not None: + kwargs["month"] = x.month + if x.weekday is not None: + kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) + if x.week > 0: + kwargs["day"] = 1 + else: + kwargs["day"] = 31 + elif x.day: + kwargs["day"] = x.day + elif x.yday is not None: + kwargs["yearday"] = x.yday + elif x.jyday is not None: + kwargs["nlyearday"] = x.jyday + if not kwargs: + # Default is to start on first sunday of april, and end + # on last sunday of october. + if not isend: + kwargs["month"] = 4 + kwargs["day"] = 1 + kwargs["weekday"] = relativedelta.SU(+1) + else: + kwargs["month"] = 10 + kwargs["day"] = 31 + kwargs["weekday"] = relativedelta.SU(-1) + if x.time is not None: + kwargs["seconds"] = x.time + else: + # Default is 2AM. + kwargs["seconds"] = 7200 + if isend: + # Convert to standard time, to follow the documented way + # of working with the extra hour. See the documentation + # of the tzinfo class. + delta = self._dst_offset-self._std_offset + kwargs["seconds"] -= delta.seconds+delta.days*86400 + return relativedelta.relativedelta(**kwargs) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, `self._s`) + +class _tzicalvtzcomp: + def __init__(self, tzoffsetfrom, tzoffsetto, isdst, + tzname=None, rrule=None): + self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) + self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) + self.tzoffsetdiff = self.tzoffsetto-self.tzoffsetfrom + self.isdst = isdst + self.tzname = tzname + self.rrule = rrule + +class _tzicalvtz(datetime.tzinfo): + def __init__(self, tzid, comps=[]): + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + dt = dt.replace(tzinfo=None) + try: + return self._cachecomp[self._cachedate.index(dt)] + except ValueError: + pass + lastcomp = None + lastcompdt = None + for comp in self._comps: + if not comp.isdst: + # Handle the extra hour in DST -> STD + compdt = comp.rrule.before(dt-comp.tzoffsetdiff, inc=True) + else: + compdt = comp.rrule.before(dt, inc=True) + if compdt and (not lastcompdt or lastcompdt < compdt): + lastcompdt = compdt + lastcomp = comp + if not lastcomp: + # RFC says nothing about what to do when a given + # time is before the first onset date. We'll look for the + # first standard component, or the first component, if + # none is found. + for comp in self._comps: + if not comp.isdst: + lastcomp = comp + break + else: + lastcomp = comp[0] + self._cachedate.insert(0, dt) + self._cachecomp.insert(0, lastcomp) + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + return lastcomp + + def utcoffset(self, dt): + return self._find_comp(dt).tzoffsetto + + def dst(self, dt): + comp = self._find_comp(dt) + if comp.isdst: + return comp.tzoffsetdiff + else: + return ZERO + + def tzname(self, dt): + return self._find_comp(dt).tzname + + def __repr__(self): + return "" % `self._tzid` + + __reduce__ = object.__reduce__ + +class tzical: + def __init__(self, fileobj): + global rrule + if not rrule: + from dateutil import rrule + + if isinstance(fileobj, basestring): + self._s = fileobj + fileobj = open(fileobj) + elif hasattr(fileobj, "name"): + self._s = fileobj.name + else: + self._s = `fileobj` + + self._vtz = {} + + self._parse_rfc(fileobj.read()) + + def keys(self): + return self._vtz.keys() + + def get(self, tzid=None): + if tzid is None: + keys = self._vtz.keys() + if len(keys) == 0: + raise ValueError, "no timezones defined" + elif len(keys) > 1: + raise ValueError, "more than one timezone available" + tzid = keys[0] + return self._vtz.get(tzid) + + def _parse_offset(self, s): + s = s.strip() + if not s: + raise ValueError, "empty offset" + if s[0] in ('+', '-'): + signal = (-1,+1)[s[0]=='+'] + s = s[1:] + else: + signal = +1 + if len(s) == 4: + return (int(s[:2])*3600+int(s[2:])*60)*signal + elif len(s) == 6: + return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal + else: + raise ValueError, "invalid offset: "+s + + def _parse_rfc(self, s): + lines = s.splitlines() + if not lines: + raise ValueError, "empty string" + + # Unfold + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + + tzid = None + comps = [] + invtz = False + comptype = None + for line in lines: + if not line: + continue + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError, "empty property name" + name = parms[0].upper() + parms = parms[1:] + if invtz: + if name == "BEGIN": + if value in ("STANDARD", "DAYLIGHT"): + # Process component + pass + else: + raise ValueError, "unknown component: "+value + comptype = value + founddtstart = False + tzoffsetfrom = None + tzoffsetto = None + rrulelines = [] + tzname = None + elif name == "END": + if value == "VTIMEZONE": + if comptype: + raise ValueError, \ + "component not closed: "+comptype + if not tzid: + raise ValueError, \ + "mandatory TZID not found" + if not comps: + raise ValueError, \ + "at least one component is needed" + # Process vtimezone + self._vtz[tzid] = _tzicalvtz(tzid, comps) + invtz = False + elif value == comptype: + if not founddtstart: + raise ValueError, \ + "mandatory DTSTART not found" + if tzoffsetfrom is None: + raise ValueError, \ + "mandatory TZOFFSETFROM not found" + if tzoffsetto is None: + raise ValueError, \ + "mandatory TZOFFSETFROM not found" + # Process component + rr = None + if rrulelines: + rr = rrule.rrulestr("\n".join(rrulelines), + compatible=True, + ignoretz=True, + cache=True) + comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, + (comptype == "DAYLIGHT"), + tzname, rr) + comps.append(comp) + comptype = None + else: + raise ValueError, \ + "invalid component end: "+value + elif comptype: + if name == "DTSTART": + rrulelines.append(line) + founddtstart = True + elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): + rrulelines.append(line) + elif name == "TZOFFSETFROM": + if parms: + raise ValueError, \ + "unsupported %s parm: %s "%(name, parms[0]) + tzoffsetfrom = self._parse_offset(value) + elif name == "TZOFFSETTO": + if parms: + raise ValueError, \ + "unsupported TZOFFSETTO parm: "+parms[0] + tzoffsetto = self._parse_offset(value) + elif name == "TZNAME": + if parms: + raise ValueError, \ + "unsupported TZNAME parm: "+parms[0] + tzname = value + elif name == "COMMENT": + pass + else: + raise ValueError, "unsupported property: "+name + else: + if name == "TZID": + if parms: + raise ValueError, \ + "unsupported TZID parm: "+parms[0] + tzid = value + elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): + pass + else: + raise ValueError, "unsupported property: "+name + elif name == "BEGIN" and value == "VTIMEZONE": + tzid = None + comps = [] + invtz = True + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, `self._s`) + +if sys.platform != "win32": + TZFILES = ["/etc/localtime", "localtime"] + TZPATHS = ["/usr/share/zoneinfo", "/usr/lib/zoneinfo", "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + +def gettz(name=None): + tz = None + if not name: + try: + name = os.environ["TZ"] + except KeyError: + pass + if name is None or name == ":": + for filepath in TZFILES: + if not os.path.isabs(filepath): + filename = filepath + for path in TZPATHS: + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + break + else: + continue + if os.path.isfile(filepath): + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = tzlocal() + else: + if name.startswith(":"): + name = name[:-1] + if os.path.isabs(name): + if os.path.isfile(name): + tz = tzfile(name) + else: + tz = None + else: + for path in TZPATHS: + filepath = os.path.join(path, name) + if not os.path.isfile(filepath): + filepath = filepath.replace(' ','_') + if not os.path.isfile(filepath): + continue + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = None + if tzwin: + try: + tz = tzwin(name) + except OSError: + pass + if not tz: + from dateutil.zoneinfo import gettz + tz = gettz(name) + if not tz: + for c in name: + # name must have at least one offset to be a tzstr + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = tzutc() + elif name in time.tzname: + tz = tzlocal() + return tz + +# vim:ts=4:sw=4:et diff --git a/utils/dateutil/tzwin.py b/utils/dateutil/tzwin.py new file mode 100644 index 000000000..073e0ff68 --- /dev/null +++ b/utils/dateutil/tzwin.py @@ -0,0 +1,180 @@ +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct +import _winreg + +__author__ = "Jeffrey Harris & Gustavo Niemeyer " + +__all__ = ["tzwin", "tzwinlocal"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + +def _settzkeyname(): + global TZKEYNAME + handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + try: + _winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + +_settzkeyname() + +class tzwinbase(datetime.tzinfo): + """tzinfo class based on win32's timezones available in the registry.""" + + def utcoffset(self, dt): + if self._isdst(dt): + return datetime.timedelta(minutes=self._dstoffset) + else: + return datetime.timedelta(minutes=self._stdoffset) + + def dst(self, dt): + if self._isdst(dt): + minutes = self._dstoffset - self._stdoffset + return datetime.timedelta(minutes=minutes) + else: + return datetime.timedelta(0) + + def tzname(self, dt): + if self._isdst(dt): + return self._dstname + else: + return self._stdname + + def list(): + """Return a list of all time zones known to the system.""" + handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + tzkey = _winreg.OpenKey(handle, TZKEYNAME) + result = [_winreg.EnumKey(tzkey, i) + for i in range(_winreg.QueryInfoKey(tzkey)[0])] + tzkey.Close() + handle.Close() + return result + list = staticmethod(list) + + def display(self): + return self._display + + def _isdst(self, dt): + dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + if dston < dstoff: + return dston <= dt.replace(tzinfo=None) < dstoff + else: + return not dstoff <= dt.replace(tzinfo=None) < dston + + +class tzwin(tzwinbase): + + def __init__(self, name): + self._name = name + + handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + tzkey = _winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name)) + keydict = valuestodict(tzkey) + tzkey.Close() + handle.Close() + + self._stdname = keydict["Std"].encode("iso-8859-1") + self._dstname = keydict["Dlt"].encode("iso-8859-1") + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 + + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + + def __init__(self): + + handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + + tzlocalkey = _winreg.OpenKey(handle, TZLOCALKEYNAME) + keydict = valuestodict(tzlocalkey) + tzlocalkey.Close() + + self._stdname = keydict["StandardName"].encode("iso-8859-1") + self._dstname = keydict["DaylightName"].encode("iso-8859-1") + + try: + tzkey = _winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname)) + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + tzkey.Close() + except OSError: + self._display = None + + handle.Close() + + self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] + self._dstoffset = self._stdoffset-keydict["DaylightBias"] + + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:6] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:6] + + def __reduce__(self): + return (self.__class__, ()) + +def picknthweekday(year, month, dayofweek, hour, minute, whichweek): + """dayofweek == 0 means Sunday, whichweek 5 means last instance""" + first = datetime.datetime(year, month, 1, hour, minute) + weekdayone = first.replace(day=((dayofweek-first.isoweekday())%7+1)) + for n in xrange(whichweek): + dt = weekdayone+(whichweek-n)*ONEWEEK + if dt.month == month: + return dt + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dict = {} + size = _winreg.QueryInfoKey(key)[1] + for i in range(size): + data = _winreg.EnumValue(key, i) + dict[data[0]] = data[1] + return dict diff --git a/utils/dateutil/zoneinfo/__init__.py b/utils/dateutil/zoneinfo/__init__.py new file mode 100644 index 000000000..9bed6264c --- /dev/null +++ b/utils/dateutil/zoneinfo/__init__.py @@ -0,0 +1,87 @@ +""" +Copyright (c) 2003-2005 Gustavo Niemeyer + +This module offers extensions to the standard python 2.3+ +datetime module. +""" +from dateutil.tz import tzfile +from tarfile import TarFile +import os + +__author__ = "Gustavo Niemeyer " +__license__ = "PSF License" + +__all__ = ["setcachesize", "gettz", "rebuild"] + +CACHE = [] +CACHESIZE = 10 + +class tzfile(tzfile): + def __reduce__(self): + return (gettz, (self._filename,)) + +def getzoneinfofile(): + filenames = os.listdir(os.path.join(os.path.dirname(__file__))) + filenames.sort() + filenames.reverse() + for entry in filenames: + if entry.startswith("zoneinfo") and ".tar." in entry: + return os.path.join(os.path.dirname(__file__), entry) + return None + +ZONEINFOFILE = getzoneinfofile() + +del getzoneinfofile + +def setcachesize(size): + global CACHESIZE, CACHE + CACHESIZE = size + del CACHE[size:] + +def gettz(name): + tzinfo = None + if ZONEINFOFILE: + for cachedname, tzinfo in CACHE: + if cachedname == name: + break + else: + tf = TarFile.open(ZONEINFOFILE) + try: + zonefile = tf.extractfile(name) + except KeyError: + tzinfo = None + else: + tzinfo = tzfile(zonefile) + tf.close() + CACHE.insert(0, (name, tzinfo)) + del CACHE[CACHESIZE:] + return tzinfo + +def rebuild(filename, tag=None, format="gz"): + import tempfile, shutil + tmpdir = tempfile.mkdtemp() + zonedir = os.path.join(tmpdir, "zoneinfo") + moduledir = os.path.dirname(__file__) + if tag: tag = "-"+tag + targetname = "zoneinfo%s.tar.%s" % (tag, format) + try: + tf = TarFile.open(filename) + for name in tf.getnames(): + if not (name.endswith(".sh") or + name.endswith(".tab") or + name == "leapseconds"): + tf.extract(name, tmpdir) + filepath = os.path.join(tmpdir, name) + os.system("zic -d %s %s" % (zonedir, filepath)) + tf.close() + target = os.path.join(moduledir, targetname) + for entry in os.listdir(moduledir): + if entry.startswith("zoneinfo") and ".tar." in entry: + os.unlink(os.path.join(moduledir, entry)) + tf = TarFile.open(target, "w:%s" % format) + for entry in os.listdir(zonedir): + entrypath = os.path.join(zonedir, entry) + tf.add(entrypath, entry) + tf.close() + finally: + shutil.rmtree(tmpdir) diff --git a/utils/dateutil/zoneinfo/zoneinfo-2008e.tar.gz b/utils/dateutil/zoneinfo/zoneinfo-2008e.tar.gz new file mode 100644 index 000000000..65e4175f4 Binary files /dev/null and b/utils/dateutil/zoneinfo/zoneinfo-2008e.tar.gz differ diff --git a/utils/diff.py b/utils/diff.py new file mode 100644 index 000000000..bfabf7c5f --- /dev/null +++ b/utils/diff.py @@ -0,0 +1,145 @@ +"""HTML Diff: http://www.aaronsw.com/2002/diff +Rough code, badly documented. Send me comments and patches.""" + +__author__ = 'Aaron Swartz ' +__copyright__ = '(C) 2003 Aaron Swartz. GNU GPL 2.' +__version__ = '0.22' + +import difflib, string + +class HTMLDiff: + + def __init__(self, a, b): + self.original = a + self.revised = b + self.diffText = None + + self.num_delete = 0 + self.num_insert = 0 + self.num_replace = 0 + + self._textDiff(a, b) + + def getDiff(self): + return self.diffText + + def getStats(self): + return (self.num_insert, self.num_delete, self.num_replace) + + def isTag(self, x): return x[0] == "<" and x[-1] == ">" + + def _textDiff(self, a, b): + """Takes in strings a and b and returns a human-readable HTML diff.""" + + out = [] + a, b = self.html2list(a), self.html2list(b) + s = difflib.SequenceMatcher(None, a, b) + + for e in s.get_opcodes(): + if e[0] == "replace": + self.num_replace += 1 + out.append(''+''.join(a[e[1]:e[2]]) + ''+''.join(b[e[3]:e[4]])+"") + elif e[0] == "delete": + self.num_delete += 1 + out.append(''+ ''.join(a[e[1]:e[2]]) + "") + elif e[0] == "insert": + self.num_insert += 1 + out.append(''+''.join(b[e[3]:e[4]]) + "") + elif e[0] == "equal": + out.append(''.join(b[e[3]:e[4]])) + else: + raise "Um, something's broken. I didn't expect a '" + `e[0]` + "'." + + self.diffText = ''.join(out) + + def html2list(self, x, b=0): + mode = 'char' + cur = '' + out = [] + for c in x: + if mode == 'tag': + if c == '>': + if b: cur += ']' + else: cur += c + out.append(cur); cur = ''; mode = 'char' + else: cur += c + elif mode == 'char': + if c == '<': + out.append(cur) + if b: cur = '[' + else: cur = c + mode = 'tag' + elif c in string.whitespace: out.append(cur+c); cur = '' + else: cur += c + out.append(cur) + return filter(lambda x: x is not '', out) + + +from difflib import SequenceMatcher + +class TextDiff: + """Create diffs of text snippets.""" + + def __init__(self, source, target): + """source = source text - target = target text""" + self.nl = "" + self.delTag = "%s" + self.insTag = "%s" + self.source = source.replace("\n", "\n%s" % self.nl).split() + self.target = target.replace("\n", "\n%s" % self.nl).split() + self.deleteCount, self.insertCount, self.replaceCount = 0, 0, 0 + self.diffText = None + self.cruncher = SequenceMatcher(None, self.source, + self.target) + self._buildDiff() + + def _buildDiff(self): + """Create a tagged diff.""" + outputList = [] + for tag, alo, ahi, blo, bhi in self.cruncher.get_opcodes(): + if tag == 'replace': + # Text replaced = deletion + insertion + outputList.append(self.delTag % " ".join(self.source[alo:ahi])) + outputList.append(self.insTag % " ".join(self.target[blo:bhi])) + self.replaceCount += 1 + elif tag == 'delete': + # Text deleted + outputList.append(self.delTag % " ".join(self.source[alo:ahi])) + self.deleteCount += 1 + elif tag == 'insert': + # Text inserted + outputList.append(self.insTag % " ".join(self.target[blo:bhi])) + self.insertCount += 1 + elif tag == 'equal': + # No change + outputList.append(" ".join(self.source[alo:ahi])) + diffText = " ".join(outputList) + diffText = " ".join(diffText.split()) + self.diffText = diffText.replace(self.nl, "\n") + + def getStats(self): + "Return a tuple of stat values." + return (self.insertCount, self.deleteCount, self.replaceCount) + + def getDiff(self): + "Return the diff text." + return self.diffText + +if __name__ == "__main__": + ch1 = """Today, pythonistas raised in the shadows of the Cold + War assumes responsibilities in a world warmed by the sunshine of + spam and freedom""" + + ch2 = """Today, pythonistas raised in the shadows of the Cold + War assumes responsibilities in a world warmed by the sunshine of + spam and freedom.""" + + differ = TextDiff(ch1, ch2) + + print "%i insertion(s), %i deletion(s), %i replacement(s)" % differ.getStats() + print differ.getDiff() + + html_differ = HTMLDiff(ch1, ch2) + print html_differ.getDiff() + print html_differ.getStats() + \ No newline at end of file diff --git a/utils/django_command_extensions/LICENSE b/utils/django_command_extensions/LICENSE new file mode 100644 index 000000000..e2e53d56b --- /dev/null +++ b/utils/django_command_extensions/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2007 Michael Trier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/utils/django_command_extensions/MANIFEST.in b/utils/django_command_extensions/MANIFEST.in new file mode 100644 index 000000000..c2b0b7ecb --- /dev/null +++ b/utils/django_command_extensions/MANIFEST.in @@ -0,0 +1,3 @@ +recursive-include django_extensions/conf *.tmpl +recursive-include django_extensions/templates *.html +recursive-include django_extensions/media * diff --git a/utils/django_command_extensions/__init__.py b/utils/django_command_extensions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/build/lib/django_extensions/__init__.py b/utils/django_command_extensions/build/lib/django_extensions/__init__.py new file mode 100644 index 000000000..bf4897d3f --- /dev/null +++ b/utils/django_command_extensions/build/lib/django_extensions/__init__.py @@ -0,0 +1,10 @@ + +VERSION = (0, 4, 'pre') + +# Dynamically calculate the version based on VERSION tuple +if len(VERSION)>2 and VERSION[2] is not None: + str_version = "%d.%d_%s" % VERSION[:3] +else: + str_version = "%d.%d" % VERSION[:2] + +__version__ = str_version diff --git a/utils/django_command_extensions/build/lib/django_extensions/admin/__init__.py b/utils/django_command_extensions/build/lib/django_extensions/admin/__init__.py new file mode 100644 index 000000000..fe45c2355 --- /dev/null +++ b/utils/django_command_extensions/build/lib/django_extensions/admin/__init__.py @@ -0,0 +1,122 @@ +# +# Autocomplete feature for admin panel +# +# Most of the code has been written by Jannis Leidel and was updated a bit +# for django_extensions. +# http://jannisleidel.com/2008/11/autocomplete-form-widget-foreignkey-model-fields/ +# +# to_string_function, Satchmo adaptation and some comments added by emes +# (Michal Salaban) +# +import operator +from django.http import HttpResponse, HttpResponseNotFound +from django.contrib import admin +from django.db import models +from django.db.models.query import QuerySet +from django.utils.encoding import smart_str +from django.utils.translation import ugettext as _ +from django.utils.text import get_text_list + +from django_extensions.admin.widgets import ForeignKeySearchInput + +class ForeignKeyAutocompleteAdmin(admin.ModelAdmin): + """Admin class for models using the autocomplete feature. + + There are two additional fields: + - related_search_fields: defines fields of managed model that + have to be represented by autocomplete input, together with + a list of target model fields that are searched for + input string, e.g.: + + related_search_fields = { + 'author': ('first_name', 'email'), + } + + - related_string_functions: contains optional functions which + take target model instance as only argument and return string + representation. By default __unicode__() method of target + object is used. + """ + + related_search_fields = {} + related_string_functions = {} + + def __call__(self, request, url): + if url is None: + pass + elif url == 'foreignkey_autocomplete': + return self.foreignkey_autocomplete(request) + return super(ForeignKeyAutocompleteAdmin, self).__call__(request, url) + + def foreignkey_autocomplete(self, request): + """ + Searches in the fields of the given related model and returns the + result as a simple string to be used by the jQuery Autocomplete plugin + """ + query = request.GET.get('q', None) + app_label = request.GET.get('app_label', None) + model_name = request.GET.get('model_name', None) + search_fields = request.GET.get('search_fields', None) + object_pk = request.GET.get('object_pk', None) + try: + to_string_function = self.related_string_functions[model_name] + except KeyError: + to_string_function = lambda x: x.__unicode__() + if search_fields and app_label and model_name and (query or object_pk): + def construct_search(field_name): + # use different lookup methods depending on the notation + if field_name.startswith('^'): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith('='): + return "%s__iexact" % field_name[1:] + elif field_name.startswith('@'): + return "%s__search" % field_name[1:] + else: + return "%s__icontains" % field_name + model = models.get_model(app_label, model_name) + queryset = model._default_manager.all() + data = '' + if query: + for bit in query.split(): + or_queries = [models.Q(**{construct_search( + smart_str(field_name)): smart_str(bit)}) + for field_name in search_fields.split(',')] + other_qs = QuerySet(model) + other_qs.dup_select_related(queryset) + other_qs = other_qs.filter(reduce(operator.or_, or_queries)) + queryset = queryset & other_qs + data = ''.join([u'%s|%s\n' % ( + to_string_function(f), f.pk) for f in queryset]) + elif object_pk: + try: + obj = queryset.get(pk=object_pk) + except: + pass + else: + data = to_string_function(obj) + return HttpResponse(data) + return HttpResponseNotFound() + + def get_help_text(self, field_name, model_name): + searchable_fields = self.related_search_fields.get(field_name, None) + if searchable_fields: + return _('Use the left field to do %s lookups in the fields %s' % + (model_name, get_text_list(searchable_fields, _('and')))) + return '' + + def formfield_for_dbfield(self, db_field, **kwargs): + """ + Overrides the default widget for Foreignkey fields if they are + specified in the related_search_fields class attribute. + """ + if (isinstance(db_field, models.ForeignKey) and + db_field.name in self.related_search_fields): + model_name = db_field.rel.to._meta.object_name + help_text = self.get_help_text(db_field.name, model_name) + if kwargs.get('help_text'): + help_text = u'%s %s' % (kwargs['help_text'], help_text) + kwargs['widget'] = ForeignKeySearchInput(db_field.rel, + self.related_search_fields[db_field.name]) + kwargs['help_text'] = help_text + return super(ForeignKeyAutocompleteAdmin, + self).formfield_for_dbfield(db_field, **kwargs) diff --git a/utils/django_command_extensions/build/lib/django_extensions/admin/widgets.py b/utils/django_command_extensions/build/lib/django_extensions/admin/widgets.py new file mode 100644 index 000000000..77c53874e --- /dev/null +++ b/utils/django_command_extensions/build/lib/django_extensions/admin/widgets.py @@ -0,0 +1,76 @@ +from django import forms +from django.conf import settings +from django.utils.safestring import mark_safe +from django.utils.text import truncate_words +from django.template.loader import render_to_string +from django.contrib.admin.widgets import ForeignKeyRawIdWidget + +class ForeignKeySearchInput(ForeignKeyRawIdWidget): + """ + A Widget for displaying ForeignKeys in an autocomplete search input + instead in a box. + """ + # Set in subclass to render the widget with a different template + widget_template = None + # Set this to the patch of the search view + search_path = '../foreignkey_autocomplete/' + + class Media: + css = { + 'all': ('django_extensions/css/jquery.autocomplete.css',) + } + js = ( + 'django_extensions/js/jquery.js', + 'django_extensions/js/jquery.bgiframe.min.js', + 'django_extensions/js/jquery.ajaxQueue.js', + 'django_extensions/js/jquery.autocomplete.js', + ) + + def label_for_value(self, value): + key = self.rel.get_related_field().name + obj = self.rel.to._default_manager.get(**{key: value}) + return truncate_words(obj, 14) + + def __init__(self, rel, search_fields, attrs=None): + self.search_fields = search_fields + super(ForeignKeySearchInput, self).__init__(rel, attrs) + + def render(self, name, value, attrs=None): + if attrs is None: + attrs = {} + output = [super(ForeignKeySearchInput, self).render(name, value, attrs)] + opts = self.rel.to._meta + app_label = opts.app_label + model_name = opts.object_name.lower() + related_url = '../../../%s/%s/' % (app_label, model_name) + params = self.url_parameters() + if params: + url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) + else: + url = '' + if not attrs.has_key('class'): + attrs['class'] = 'vForeignKeyRawIdAdminField' + # Call the TextInput render method directly to have more control + output = [forms.TextInput.render(self, name, value, attrs)] + if value: + label = self.label_for_value(value) + else: + label = u'' + context = { + 'url': url, + 'related_url': related_url, + 'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX, + 'search_path': self.search_path, + 'search_fields': ','.join(self.search_fields), + 'model_name': model_name, + 'app_label': app_label, + 'label': label, + 'name': name, + } + output.append(render_to_string(self.widget_template or ( + 'django_extensions/widgets/%s/%s/foreignkey_searchinput.html' % (app_label, model_name), + 'django_extensions/widgets/%s/foreignkey_searchinput.html' % app_label, + 'django_extensions/widgets/foreignkey_searchinput.html', + ), context)) + output.reverse() + return mark_safe(u''.join(output)) diff --git a/utils/django_command_extensions/django_extensions/conf/app_template/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/app_template/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/app_template/forms.py.tmpl b/utils/django_command_extensions/django_extensions/conf/app_template/forms.py.tmpl new file mode 100644 index 000000000..6b477f9e4 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/conf/app_template/forms.py.tmpl @@ -0,0 +1,3 @@ +from django import forms + +# place form definition here \ No newline at end of file diff --git a/utils/django_command_extensions/django_extensions/conf/app_template/models.py.tmpl b/utils/django_command_extensions/django_extensions/conf/app_template/models.py.tmpl new file mode 100644 index 000000000..71a836239 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/conf/app_template/models.py.tmpl @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/utils/django_command_extensions/django_extensions/conf/app_template/urls.py.tmpl b/utils/django_command_extensions/django_extensions/conf/app_template/urls.py.tmpl new file mode 100644 index 000000000..5d79a9fdb --- /dev/null +++ b/utils/django_command_extensions/django_extensions/conf/app_template/urls.py.tmpl @@ -0,0 +1,3 @@ +from django.conf.urls.defaults import * + +# place app url patterns here \ No newline at end of file diff --git a/utils/django_command_extensions/django_extensions/conf/app_template/views.py.tmpl b/utils/django_command_extensions/django_extensions/conf/app_template/views.py.tmpl new file mode 100644 index 000000000..60f00ef0e --- /dev/null +++ b/utils/django_command_extensions/django_extensions/conf/app_template/views.py.tmpl @@ -0,0 +1 @@ +# Create your views here. diff --git a/utils/django_command_extensions/django_extensions/conf/command_template/management/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/command_template/management/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/command_template/management/commands/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/command_template/management/commands/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/command_template/management/commands/sample.py.tmpl b/utils/django_command_extensions/django_extensions/conf/command_template/management/commands/sample.py.tmpl new file mode 100644 index 000000000..eb9df1b9a --- /dev/null +++ b/utils/django_command_extensions/django_extensions/conf/command_template/management/commands/sample.py.tmpl @@ -0,0 +1,7 @@ +from django.core.management.base import {{ base_command }} + +class Command({{ base_command }}): + help = "My shiny new management command." + + def {{ handle_method }}: + raise NotImplementedError() \ No newline at end of file diff --git a/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/daily/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/daily/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/hourly/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/hourly/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/monthly/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/monthly/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/sample.py.tmpl b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/sample.py.tmpl new file mode 100644 index 000000000..d84800b18 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/sample.py.tmpl @@ -0,0 +1,8 @@ +from django_extensions.management.jobs import BaseJob + +class Job(BaseJob): + help = "My sample job." + + def execute(self): + # executing empty sample job + pass diff --git a/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/weekly/__init__.py.tmpl b/utils/django_command_extensions/django_extensions/conf/jobs_template/jobs/weekly/__init__.py.tmpl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/db/__init__.py b/utils/django_command_extensions/django_extensions/db/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/db/fields/__init__.py b/utils/django_command_extensions/django_extensions/db/fields/__init__.py new file mode 100644 index 000000000..24ee4dc33 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/db/fields/__init__.py @@ -0,0 +1,199 @@ +""" +Django Extensions additional model fields +""" + +from django.template.defaultfilters import slugify +from django.db.models import DateTimeField, CharField, SlugField +import datetime +import re + +try: + import uuid +except ImportError: + from django_extensions.utils import uuid + +class AutoSlugField(SlugField): + """ AutoSlugField + + By default, sets editable=False, blank=True. + + Required arguments: + + populate_from + Specifies which field the slug is populated from. + + Optional arguments: + + separator + Defines the used separator (default: '-') + + overwrite + If set to True, overwrites the slug on every save (default: False) + + Inspired by SmileyChris' Unique Slugify snippet: + http://www.djangosnippets.org/snippets/690/ + """ + def __init__(self, *args, **kwargs): + kwargs.setdefault('blank', True) + kwargs.setdefault('editable', False) + + populate_from = kwargs.pop('populate_from', None) + if populate_from is None: + raise ValueError("missing 'populate_from' argument") + else: + self._populate_from = populate_from + self.separator = kwargs.pop('separator', u'-') + self.overwrite = kwargs.pop('overwrite', False) + super(AutoSlugField, self).__init__(*args, **kwargs) + + def _slug_strip(self, value): + """ + Cleans up a slug by removing slug separator characters that occur at + the beginning or end of a slug. + + If an alternate separator is used, it will also replace any instances + of the default '-' separator with the new separator. + """ + re_sep = '(?:-|%s)' % re.escape(self.separator) + value = re.sub('%s+' % re_sep, self.separator, value) + return re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value) + + def create_slug(self, model_instance, add): + # get fields to populate from and slug field to set + populate_field = model_instance._meta.get_field(self._populate_from) + slug_field = model_instance._meta.get_field(self.attname) + if add or self.overwrite: + # slugify the original field content and set next step to 2 + slug = slugify(getattr(model_instance, populate_field.attname)) + next = 2 + else: + # get slug from the current model instance and calculate next + # step from its number, clean-up + slug = self._slug_strip(getattr(model_instance, self.attname)) + next = slug.split(self.separator)[-1] + if next.isdigit(): + slug = self.separator.join(slug.split(self.separator)[:-1]) + next = int(next) + else: + next = 2 + + # strip slug depending on max_length attribute of the slug field + # and clean-up + slug_len = slug_field.max_length + if slug_len: + slug = slug[:slug_len] + slug = self._slug_strip(slug) + original_slug = slug + + # exclude the current model instance from the queryset used in finding + # the next valid slug + queryset = model_instance.__class__._default_manager.all() + if model_instance.pk: + queryset = queryset.exclude(pk=model_instance.pk) + + # increases the number while searching for the next valid slug + # depending on the given slug, clean-up + while not slug or queryset.filter(**{self.attname: slug}): + slug = original_slug + end = '%s%s' % (self.separator, next) + end_len = len(end) + if slug_len and len(slug)+end_len > slug_len: + slug = slug[:slug_len-end_len] + slug = self._slug_strip(slug) + slug = '%s%s' % (slug, end) + next += 1 + return slug + + def pre_save(self, model_instance, add): + value = unicode(self.create_slug(model_instance, add)) + setattr(model_instance, self.attname, value) + return value + + def get_internal_type(self): + return "SlugField" + +class CreationDateTimeField(DateTimeField): + """ CreationDateTimeField + + By default, sets editable=False, blank=True, default=datetime.now + """ + + def __init__(self, *args, **kwargs): + kwargs.setdefault('editable', False) + kwargs.setdefault('blank', True) + kwargs.setdefault('default', datetime.datetime.now) + DateTimeField.__init__(self, *args, **kwargs) + + def get_internal_type(self): + return "DateTimeField" + +class ModificationDateTimeField(CreationDateTimeField): + """ ModificationDateTimeField + + By default, sets editable=False, blank=True, default=datetime.now + + Sets value to datetime.now() on each save of the model. + """ + + def pre_save(self, model, add): + value = datetime.datetime.now() + setattr(model, self.attname, value) + return value + + def get_internal_type(self): + return "DateTimeField" + +class UUIDVersionError(Exception): + pass + +class UUIDField(CharField): + """ UUIDField + + By default uses UUID version 1 (generate from host ID, sequence number and current time) + + The field support all uuid versions which are natively supported by the uuid python module. + For more information see: http://docs.python.org/lib/module-uuid.html + """ + + def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs): + kwargs['max_length'] = 36 + if auto: + kwargs['blank'] = True + kwargs.setdefault('editable', False) + self.auto = auto + self.version = version + if version==1: + self.node, self.clock_seq = node, clock_seq + elif version==3 or version==5: + self.namespace, self.name = namespace, name + CharField.__init__(self, verbose_name, name, **kwargs) + + def get_internal_type(self): + return CharField.__name__ + + def create_uuid(self): + if not self.version or self.version==4: + return uuid.uuid4() + elif self.version==1: + return uuid.uuid1(self.node, self.clock_seq) + elif self.version==2: + raise UUIDVersionError("UUID version 2 is not supported.") + elif self.version==3: + return uuid.uuid3(self.namespace, self.name) + elif self.version==5: + return uuid.uuid5(self.namespace, self.name) + else: + raise UUIDVersionError("UUID version %s is not valid." % self.version) + + def pre_save(self, model_instance, add): + if self.auto and add: + value = unicode(self.create_uuid()) + setattr(model_instance, self.attname, value) + return value + else: + value = super(UUIDField, self).pre_save(model_instance, add) + if self.auto and not value: + value = unicode(self.create_uuid()) + setattr(model_instance, self.attname, value) + return value + diff --git a/utils/django_command_extensions/django_extensions/db/models.py b/utils/django_command_extensions/django_extensions/db/models.py new file mode 100644 index 000000000..2aad09d19 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/db/models.py @@ -0,0 +1,17 @@ +""" +Django Extensions abstract base model classes. +""" + +from django.db import models +from django_extensions.db.fields import ModificationDateTimeField, CreationDateTimeField + +class TimeStampedModel(models.Model): + """ TimeStampedModel + An abstract base class model that provides self-managed "created" and + "modified" fields. + """ + created = CreationDateTimeField() + modified = ModificationDateTimeField() + + class Meta: + abstract = True diff --git a/utils/django_command_extensions/django_extensions/jobs/__init__.py b/utils/django_command_extensions/django_extensions/jobs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/jobs/daily/__init__.py b/utils/django_command_extensions/django_extensions/jobs/daily/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/jobs/daily/cache_cleanup.py b/utils/django_command_extensions/django_extensions/jobs/daily/cache_cleanup.py new file mode 100644 index 000000000..68bff5e85 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/jobs/daily/cache_cleanup.py @@ -0,0 +1,23 @@ +""" +Daily cleanup job. + +Can be run as a cronjob to clean out old data from the database (only expired +sessions at the moment). +""" + +from django_extensions.management.jobs import DailyJob + +class Job(DailyJob): + help = "Cache (db) cleanup Job" + + def execute(self): + from django.conf import settings + import os + + if settings.CACHE_BACKEND.startswith('db://'): + os.environ['TZ'] = settings.TIME_ZONE + table_name = settings.CACHE_BACKEND[5:] + cursor = connection.cursor() + cursor.execute("DELETE FROM %s WHERE %s < UTC_TIMESTAMP()" % \ + (backend.quote_name(table_name), backend.quote_name('expires'))) + transaction.commit_unless_managed() diff --git a/utils/django_command_extensions/django_extensions/jobs/daily/daily_cleanup.py b/utils/django_command_extensions/django_extensions/jobs/daily/daily_cleanup.py new file mode 100644 index 000000000..b85d08f2e --- /dev/null +++ b/utils/django_command_extensions/django_extensions/jobs/daily/daily_cleanup.py @@ -0,0 +1,22 @@ +""" +Daily cleanup job. + +Can be run as a cronjob to clean out old data from the database (only expired +sessions at the moment). +""" + +from django_extensions.management.jobs import DailyJob + +class Job(DailyJob): + help = "Django Daily Cleanup Job" + + def execute(self): + # TODO: Remove the old way when Django 1.0 lands + try: + # old way of doing cleanup (pre r7844 in svn) + from django.bin.daily_cleanup import clean_up + clean_up() + except: + # new way using the management.call_command function + from django.core import management + management.call_command("cleanup") diff --git a/utils/django_command_extensions/django_extensions/jobs/hourly/__init__.py b/utils/django_command_extensions/django_extensions/jobs/hourly/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/jobs/monthly/__init__.py b/utils/django_command_extensions/django_extensions/jobs/monthly/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/jobs/weekly/__init__.py b/utils/django_command_extensions/django_extensions/jobs/weekly/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/management/__init__.py b/utils/django_command_extensions/django_extensions/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/management/color.py b/utils/django_command_extensions/django_extensions/management/color.py new file mode 100644 index 000000000..f3de0518a --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/color.py @@ -0,0 +1,13 @@ +""" +Sets up the terminal color scheme. +""" + +from django.core.management import color +from django.utils import termcolors + +def color_style(): + style = color.color_style() + style.URL = termcolors.make_style(fg='green', opts=('bold',)) + style.MODULE = termcolors.make_style(fg='yellow') + style.MODULE_NAME = termcolors.make_style(opts=('bold',)) + return style diff --git a/utils/django_command_extensions/django_extensions/management/commands/__init__.py b/utils/django_command_extensions/django_extensions/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/django_command_extensions/django_extensions/management/commands/clean_pyc.py b/utils/django_command_extensions/django_extensions/management/commands/clean_pyc.py new file mode 100644 index 000000000..d01a87269 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/clean_pyc.py @@ -0,0 +1,41 @@ +from django.core.management.base import NoArgsCommand +from django_extensions.management.utils import get_project_root +from random import choice +from optparse import make_option +from os.path import join as _j +import os + +class Command(NoArgsCommand): + option_list = NoArgsCommand.option_list + ( + make_option('--optimize', '-o', '-O', action='store_true', dest='optimize', + help='Remove optimized python bytecode files'), + make_option('--path', '-p', action='store', dest='path', + help='Specify path to recurse into'), + ) + help = "Removes all python bytecode compiled files from the project." + + requires_model_validation = False + + def handle_noargs(self, **options): + project_root = options.get("path", None) + if not project_root: + project_root = get_project_root() + exts = options.get("optimize", False) and [".pyc", ".pyo"] or [".pyc"] + verbose = int(options.get("verbosity", 1))>1 + + for root, dirs, files in os.walk(project_root): + for file in files: + ext = os.path.splitext(file)[1] + if ext in exts: + full_path = _j(root, file) + if verbose: + print full_path + os.remove(full_path) + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/utils/django_command_extensions/django_extensions/management/commands/compile_pyc.py b/utils/django_command_extensions/django_extensions/management/commands/compile_pyc.py new file mode 100644 index 000000000..05296a152 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/compile_pyc.py @@ -0,0 +1,40 @@ +from django.core.management.base import NoArgsCommand +from django_extensions.management.utils import get_project_root +from random import choice +from optparse import make_option +from os.path import join as _j +import py_compile +import os + +class Command(NoArgsCommand): + option_list = NoArgsCommand.option_list + ( + make_option('--path', '-p', action='store', dest='path', + help='Specify path to recurse into'), + ) + help = "Compile python bytecode files for the project." + + requires_model_validation = False + + def handle_noargs(self, **options): + project_root = options.get("path", None) + if not project_root: + project_root = get_project_root() + verbose = int(options.get("verbosity", 1))>1 + + for root, dirs, files in os.walk(project_root): + for file in files: + ext = os.path.splitext(file)[1] + if ext==".py": + full_path = _j(root, file) + if verbose: + print "%sc" % full_path + py_compile.compile(full_path) + + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/utils/django_command_extensions/django_extensions/management/commands/create_app.py b/utils/django_command_extensions/django_extensions/management/commands/create_app.py new file mode 100644 index 000000000..3fcf7e605 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/create_app.py @@ -0,0 +1,72 @@ +import os +import re +import django_extensions +from django.core.management.base import CommandError, LabelCommand, _make_writeable +from optparse import make_option + +class Command(LabelCommand): + option_list = LabelCommand.option_list + ( + make_option('--template', '-t', action='store', dest='app_template', + help='The path to the app template'), + make_option('--parent_path', '-p', action='store', dest='parent_path', + help='The parent path of the app to be created'), + ) + + help = ("Creates a Django application directory structure based on the specified template directory.") + args = "[appname]" + label = 'application name' + + requires_model_validation = False + can_import_settings = True + + def handle_label(self, label, **options): + project_dir = os.getcwd() + project_name = os.path.split(project_dir)[-1] + app_name =label + app_template = options.get('app_template') or os.path.join(django_extensions.__path__[0], 'conf', 'app_template') + app_dir = os.path.join(options.get('parent_path') or project_dir, app_name) + + if not os.path.exists(app_template): + raise CommandError("The template path, %r, does not exist." % app_template) + + if not re.search(r'^\w+$', label): + raise CommandError("%r is not a valid application name. Please use only numbers, letters and underscores." % label) + try: + os.makedirs(app_dir) + except OSError, e: + raise CommandError(e) + + copy_template(app_template, app_dir, project_name, app_name) + +def copy_template(app_template, copy_to, project_name, app_name): + """copies the specified template directory to the copy_to location""" + import shutil + + # walks the template structure and copies it + for d, subdirs, files in os.walk(app_template): + relative_dir = d[len(app_template)+1:] + if relative_dir and not os.path.exists(os.path.join(copy_to, relative_dir)): + os.mkdir(os.path.join(copy_to, relative_dir)) + for i, subdir in enumerate(subdirs): + if subdir.startswith('.'): + del subdirs[i] + for f in files: + if f.endswith('.pyc') or f.startswith('.DS_Store'): + continue + path_old = os.path.join(d, f) + path_new = os.path.join(copy_to, relative_dir, f.replace('app_name', app_name)) + if os.path.exists(path_new): + path_new = os.path.join(copy_to, relative_dir, f) + if os.path.exists(path_new): + continue + path_new = path_new.rstrip(".tmpl") + fp_old = open(path_old, 'r') + fp_new = open(path_new, 'w') + fp_new.write(fp_old.read().replace('{{ app_name }}', app_name).replace('{{ project_name }}', project_name)) + fp_old.close() + fp_new.close() + try: + shutil.copymode(path_old, path_new) + _make_writeable(path_new) + except OSError: + sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new)) diff --git a/utils/django_command_extensions/django_extensions/management/commands/create_command.py b/utils/django_command_extensions/django_extensions/management/commands/create_command.py new file mode 100644 index 000000000..367799204 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/create_command.py @@ -0,0 +1,78 @@ +import os +from django.core.management.base import CommandError, AppCommand, _make_writeable +from optparse import make_option + +class Command(AppCommand): + option_list = AppCommand.option_list + ( + make_option('--name', '-n', action='store', dest='command_name', default='sample', + help='The name to use for the management command'), + make_option('--base', '-b', action='store', dest='base_command', default='Base', + help='The base class used for implementation of this command. Should be one of Base, App, Label, or NoArgs'), + ) + + help = ("Creates a Django management command directory structure for the given app name" + " in the current directory.") + args = "[appname]" + label = 'application name' + + requires_model_validation = False + # Can't import settings during this command, because they haven't + # necessarily been created. + can_import_settings = True + + def handle_app(self, app, **options): + directory = os.getcwd() + app_name = app.__name__.split('.')[-2] + project_dir = os.path.join(directory, app_name) + if not os.path.exists(project_dir): + try: + os.mkdir(project_dir) + except OSError, e: + raise CommandError(e) + + copy_template('command_template', project_dir, options.get('command_name'), '%sCommand' % options.get('base_command')) + +def copy_template(template_name, copy_to, command_name, base_command): + """copies the specified template directory to the copy_to location""" + import django_extensions + import re + import shutil + + template_dir = os.path.join(django_extensions.__path__[0], 'conf', template_name) + + handle_method = "handle(self, *args, **options)" + if base_command == 'AppCommand': + handle_method = "handle_app(self, app, **options)" + elif base_command == 'LabelCommand': + handle_method = "handle_label(self, label, **options)" + elif base_command == 'NoArgsCommand': + handle_method = "handle_noargs(self, **options)" + + # walks the template structure and copies it + for d, subdirs, files in os.walk(template_dir): + relative_dir = d[len(template_dir)+1:] + if relative_dir and not os.path.exists(os.path.join(copy_to, relative_dir)): + os.mkdir(os.path.join(copy_to, relative_dir)) + for i, subdir in enumerate(subdirs): + if subdir.startswith('.'): + del subdirs[i] + for f in files: + if f.endswith('.pyc') or f.startswith('.DS_Store'): + continue + path_old = os.path.join(d, f) + path_new = os.path.join(copy_to, relative_dir, f.replace('sample', command_name)) + if os.path.exists(path_new): + path_new = os.path.join(copy_to, relative_dir, f) + if os.path.exists(path_new): + continue + path_new = path_new.rstrip(".tmpl") + fp_old = open(path_old, 'r') + fp_new = open(path_new, 'w') + fp_new.write(fp_old.read().replace('{{ command_name }}', command_name).replace('{{ base_command }}', base_command).replace('{{ handle_method }}', handle_method)) + fp_old.close() + fp_new.close() + try: + shutil.copymode(path_old, path_new) + _make_writeable(path_new) + except OSError: + sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new)) diff --git a/utils/django_command_extensions/django_extensions/management/commands/create_jobs.py b/utils/django_command_extensions/django_extensions/management/commands/create_jobs.py new file mode 100644 index 000000000..16ece2c92 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/create_jobs.py @@ -0,0 +1,54 @@ +import os +import sys +from django.core.management.base import CommandError, AppCommand, _make_writeable + +class Command(AppCommand): + help = ("Creates a Django jobs command directory structure for the given app name in the current directory.") + args = "[appname]" + label = 'application name' + + requires_model_validation = False + # Can't import settings during this command, because they haven't + # necessarily been created. + can_import_settings = True + + def handle_app(self, app, **options): + app_dir = os.path.dirname(app.__file__) + copy_template('jobs_template', app_dir) + +def copy_template(template_name, copy_to): + """copies the specified template directory to the copy_to location""" + import django_extensions + import re + import shutil + + template_dir = os.path.join(django_extensions.__path__[0], 'conf', template_name) + + # walks the template structure and copies it + for d, subdirs, files in os.walk(template_dir): + relative_dir = d[len(template_dir)+1:] + if relative_dir and not os.path.exists(os.path.join(copy_to, relative_dir)): + os.mkdir(os.path.join(copy_to, relative_dir)) + for i, subdir in enumerate(subdirs): + if subdir.startswith('.'): + del subdirs[i] + for f in files: + if f.endswith('.pyc') or f.startswith('.DS_Store'): + continue + path_old = os.path.join(d, f) + path_new = os.path.join(copy_to, relative_dir, f) + if os.path.exists(path_new): + path_new = os.path.join(copy_to, relative_dir, f) + if os.path.exists(path_new): + continue + path_new = path_new.rstrip(".tmpl") + fp_old = open(path_old, 'r') + fp_new = open(path_new, 'w') + fp_new.write(fp_old.read()) + fp_old.close() + fp_new.close() + try: + shutil.copymode(path_old, path_new) + _make_writeable(path_new) + except OSError: + sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new)) diff --git a/utils/django_command_extensions/django_extensions/management/commands/describe_form.py b/utils/django_command_extensions/django_extensions/management/commands/describe_form.py new file mode 100644 index 000000000..4aa34919c --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/describe_form.py @@ -0,0 +1,64 @@ +from django.core.management.base import LabelCommand, CommandError +from django.utils.encoding import force_unicode + +class Command(LabelCommand): + help = "Outputs the specified model as a form definition to the shell." + args = "[app.model]" + label = 'application name and model name' + + requires_model_validation = True + can_import_settings = True + + def handle_label(self, label, **options): + return describe_form(label) + + +def describe_form(label, fields=None): + """ + Returns a string describing a form based on the model + """ + from django.db.models.loading import get_model + try: + app_name, model_name = label.split('.')[-2:] + except (IndexError, ValueError): + raise CommandError("Need application and model name in the form: appname.model") + model = get_model(app_name, model_name) + + opts = model._meta + field_list = [] + for f in opts.fields + opts.many_to_many: + if not f.editable: + continue + if fields and not f.name in fields: + continue + formfield = f.formfield() + if not '__dict__' in dir(formfield): + continue + attrs = {} + valid_fields = ['required', 'initial', 'max_length', 'min_length', 'max_value', 'min_value', 'max_digits', 'decimal_places', 'choices', 'help_text', 'label'] + for k,v in formfield.__dict__.items(): + if k in valid_fields and v != None: + # ignore defaults, to minimize verbosity + if k == 'required' and v: + continue + if k == 'help_text' and not v: + continue + if k == 'widget': + attrs[k] = v.__class__ + elif k in ['help_text', 'label']: + attrs[k] = force_unicode(v).strip() + else: + attrs[k] = v + + params = ', '.join(['%s=%r' % (k, v) for k, v in attrs.items()]) + field_list.append(' %(field_name)s = forms.%(field_type)s(%(params)s)' % { 'field_name': f.name, + 'field_type': formfield.__class__.__name__, + 'params': params }) + + return ''' +from django import forms +from %(app_name)s.models import %(object_name)s + +class %(object_name)sForm(forms.Form): +%(field_list)s +''' % { 'app_name': app_name, 'object_name': opts.object_name, 'field_list': '\n'.join(field_list) } diff --git a/utils/django_command_extensions/django_extensions/management/commands/dumpscript.py b/utils/django_command_extensions/django_extensions/management/commands/dumpscript.py new file mode 100644 index 000000000..703978607 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/dumpscript.py @@ -0,0 +1,515 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" + Title: Dumpscript management command + Project: Hardytools (queryset-refactor version) + Author: Will Hardy (http://willhardy.com.au) + Date: June 2008 + Usage: python manage.py dumpscript appname > scripts/scriptname.py + $Revision: 217 $ + +Description: + Generates a Python script that will repopulate the database using objects. + The advantage of this approach is that it is easy to understand, and more + flexible than directly populating the database, or using XML. + + * It also allows for new defaults to take effect and only transfers what is + needed. + * If a new database schema has a NEW ATTRIBUTE, it is simply not + populated (using a default value will make the transition smooth :) + * If a new database schema REMOVES AN ATTRIBUTE, it is simply ignored + and the data moves across safely (I'm assuming we don't want this + attribute anymore. + * Problems may only occur if there is a new model and is now a required + ForeignKey for an existing model. But this is easy to fix by editing the + populate script :) + +Improvements: + See TODOs and FIXMEs scattered throughout :-) + +""" + +import sys +from django.db import models +from django.core.exceptions import ObjectDoesNotExist +from django.core.management.base import BaseCommand +from django.utils.encoding import smart_unicode, force_unicode +from django.contrib.contenttypes.models import ContentType + +class Command(BaseCommand): + help = 'Dumps the data as a customised python script.' + args = '[appname ...]' + + def handle(self, *app_labels, **options): + + # Get the models we want to export + models = get_models(app_labels) + + # A dictionary is created to keep track of all the processed objects, + # so that foreign key references can be made using python variable names. + # This variable "context" will be passed around like the town bicycle. + context = {} + + # Create a dumpscript object and let it format itself as a string + print Script(models=models, context=context) + + +def get_models(app_labels): + """ Gets a list of models for the given app labels, with some exceptions. + TODO: If a required model is referenced, it should also be included. + Or at least discovered with a get_or_create() call. + """ + + from django.db.models import get_app, get_apps, get_model + from django.db.models import get_models as get_all_models + + # These models are not to be output, e.g. because they can be generated automatically + # TODO: This should be "appname.modelname" string + from django.contrib.contenttypes.models import ContentType + EXCLUDED_MODELS = (ContentType, ) + + models = [] + + # If no app labels are given, return all + if not app_labels: + for app in get_apps(): + models += [ m for m in get_all_models(app) if m not in EXCLUDED_MODELS ] + + # Get all relevant apps + for app_label in app_labels: + # If a specific model is mentioned, get only that model + if "." in app_label: + app_label, model_name = app_label.split(".", 1) + models.append(get_model(app_label, model_name)) + # Get all models for a given app + else: + models += [ m for m in get_all_models(get_app(app_label)) if m not in EXCLUDED_MODELS ] + + return models + + + +class Code(object): + """ A snippet of python script. + This keeps track of import statements and can be output to a string. + In the future, other features such as custom indentation might be included + in this class. + """ + + def __init__(self): + self.imports = {} + self.indent = -1 + + def __str__(self): + """ Returns a string representation of this script. + """ + if self.imports: + sys.stderr.write(repr(self.import_lines)) + return flatten_blocks([""] + self.import_lines + [""] + self.lines, num_indents=self.indent) + else: + return flatten_blocks(self.lines, num_indents=self.indent) + + def get_import_lines(self): + """ Takes the stored imports and converts them to lines + """ + if self.imports: + return [ "from %s import %s" % (value, key) for key, value in self.imports.items() ] + else: + return [] + import_lines = property(get_import_lines) + + +class ModelCode(Code): + " Produces a python script that can recreate data for a given model class. " + + def __init__(self, model, context={}): + self.model = model + self.context = context + self.instances = [] + self.indent = 0 + + def get_imports(self): + """ Returns a dictionary of import statements, with the variable being + defined as the key. + """ + return { self.model.__name__: smart_unicode(self.model.__module__) } + imports = property(get_imports) + + def get_lines(self): + """ Returns a list of lists or strings, representing the code body. + Each list is a block, each string is a statement. + """ + code = [] + + for counter, item in enumerate(self.model.objects.all()): + instance = InstanceCode(instance=item, id=counter+1, context=self.context) + self.instances.append(instance) + if instance.waiting_list: + code += instance.lines + + # After each instance has been processed, try again. + # This allows self referencing fields to work. + for instance in self.instances: + if instance.waiting_list: + code += instance.lines + + return code + + lines = property(get_lines) + + +class InstanceCode(Code): + " Produces a python script that can recreate data for a given model instance. " + + def __init__(self, instance, id, context={}): + """ We need the instance in question and an id """ + + self.instance = instance + self.model = self.instance.__class__ + self.context = context + self.variable_name = "%s_%s" % (self.instance._meta.db_table, id) + self.skip_me = None + self.instantiated = False + + self.indent = 0 + self.imports = {} + + self.waiting_list = list(self.model._meta.fields) + + self.many_to_many_waiting_list = {} + for field in self.model._meta.many_to_many: + self.many_to_many_waiting_list[field] = list(getattr(self.instance, field.name).all()) + + def get_lines(self, force=False): + """ Returns a list of lists or strings, representing the code body. + Each list is a block, each string is a statement. + + force (True or False): if an attribute object cannot be included, + it is usually skipped to be processed later. With 'force' set, there + will be no waiting: a get_or_create() call is written instead. + """ + code_lines = [] + + # Don't return anything if this is an instance that should be skipped + if self.skip(): + return [] + + # Initialise our new object + # e.g. model_name_35 = Model() + code_lines += self.instantiate() + + # Add each field + # e.g. model_name_35.field_one = 1034.91 + # model_name_35.field_two = "text" + code_lines += self.get_waiting_list() + + if force: + # TODO: Check that M2M are not affected + code_lines += self.get_waiting_list(force=force) + + # Print the save command for our new object + # e.g. model_name_35.save() + if code_lines: + code_lines.append("%s.save()\n" % (self.variable_name)) + + code_lines += self.get_many_to_many_lines(force=force) + + return code_lines + lines = property(get_lines) + + def skip(self): + """ Determine whether or not this object should be skipped. + If this model is a parent of a single subclassed instance, skip it. + The subclassed instance will create this parent instance for us. + + TODO: Allow the user to force its creation? + """ + + if self.skip_me is not None: + return self.skip_me + + try: + # Django trunk since r7722 uses CollectedObjects instead of dict + from django.db.models.query import CollectedObjects + sub_objects = CollectedObjects() + except ImportError: + # previous versions don't have CollectedObjects + sub_objects = {} + self.instance._collect_sub_objects(sub_objects) + if reduce(lambda x, y: x+y, [self.model in so._meta.parents for so in sub_objects.keys()]) == 1: + pk_name = self.instance._meta.pk.name + key = '%s_%s' % (self.model.__name__, getattr(self.instance, pk_name)) + self.context[key] = None + self.skip_me = True + else: + self.skip_me = False + + return self.skip_me + + def instantiate(self): + " Write lines for instantiation " + # e.g. model_name_35 = Model() + code_lines = [] + + if not self.instantiated: + code_lines.append("%s = %s()" % (self.variable_name, self.model.__name__)) + self.instantiated = True + + # Store our variable name for future foreign key references + pk_name = self.instance._meta.pk.name + key = '%s_%s' % (self.model.__name__, getattr(self.instance, pk_name)) + self.context[key] = self.variable_name + + return code_lines + + + def get_waiting_list(self, force=False): + " Add lines for any waiting fields that can be completed now. " + + code_lines = [] + + # Process normal fields + for field in list(self.waiting_list): + try: + # Find the value, add the line, remove from waiting list and move on + value = get_attribute_value(self.instance, field, self.context, force=force) + code_lines.append('%s.%s = %s' % (self.variable_name, field.name, value)) + self.waiting_list.remove(field) + except SkipValue, e: + # Remove from the waiting list and move on + self.waiting_list.remove(field) + continue + except DoLater, e: + # Move on, maybe next time + continue + + + return code_lines + + + def get_many_to_many_lines(self, force=False): + """ Generates lines that define many to many relations for this instance. """ + + lines = [] + + for field, rel_items in self.many_to_many_waiting_list.items(): + for rel_item in list(rel_items): + try: + pk_name = rel_item._meta.pk.name + key = '%s_%s' % (rel_item.__class__.__name__, getattr(rel_item, pk_name)) + value = "%s" % self.context[key] + lines.append('%s.%s.add(%s)' % (self.variable_name, field.name, value)) + self.many_to_many_waiting_list[field].remove(rel_item) + except KeyError: + if force: + value = "%s.objects.get(%s=%s)" % (rel_item._meta.object_name, pk_name, getattr(rel_item, pk_name)) + lines.append('%s.%s.add(%s)' % (self.variable_name, field.name, value)) + self.many_to_many_waiting_list[field].remove(rel_item) + + if lines: + lines.append("") + + return lines + + +class Script(Code): + " Produces a complete python script that can recreate data for the given apps. " + + def __init__(self, models, context={}): + self.models = models + self.context = context + + self.indent = -1 + self.imports = {} + + def get_lines(self): + """ Returns a list of lists or strings, representing the code body. + Each list is a block, each string is a statement. + """ + code = [ self.FILE_HEADER.strip() ] + + # Queue and process the required models + for model_class in queue_models(self.models, context=self.context): + sys.stderr.write('Processing model: %s\n' % model_class.model.__name__) + code.append(model_class.import_lines) + code.append("") + code.append(model_class.lines) + + # Process left over foreign keys from cyclic models + for model in self.models: + sys.stderr.write('Re-processing model: %s\n' % model.model.__name__) + for instance in model.instances: + if instance.waiting_list or instance.many_to_many_waiting_list: + code.append(instance.get_lines(force=True)) + + return code + + lines = property(get_lines) + + # A user-friendly file header + FILE_HEADER = """ + +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file has been automatically generated, changes may be lost if you +# go and generate it again. It was generated with the following command: +# %s + +import datetime +from decimal import Decimal +from django.contrib.contenttypes.models import ContentType + +def run(): + +""" % " ".join(sys.argv) + + + +# HELPER FUNCTIONS +#------------------------------------------------------------------------------- + +def flatten_blocks(lines, num_indents=-1): + """ Takes a list (block) or string (statement) and flattens it into a string + with indentation. + """ + + # The standard indent is four spaces + INDENTATION = " " * 4 + + if not lines: + return "" + + # If this is a string, add the indentation and finish here + if isinstance(lines, basestring): + return INDENTATION * num_indents + lines + + # If this is not a string, join the lines and recurse + return "\n".join([ flatten_blocks(line, num_indents+1) for line in lines ]) + + + + +def get_attribute_value(item, field, context, force=False): + """ Gets a string version of the given attribute's value, like repr() might. """ + + # Find the value of the field, catching any database issues + try: + value = getattr(item, field.name) + except ObjectDoesNotExist: + raise SkipValue('Could not find object for %s.%s, ignoring.\n' % (item.__class__.__name__, field.name)) + + # AutoField: We don't include the auto fields, they'll be automatically recreated + if isinstance(field, models.AutoField): + raise SkipValue() + + # Some databases (eg MySQL) might store boolean values as 0/1, this needs to be cast as a bool + elif isinstance(field, models.BooleanField) and value is not None: + return repr(bool(value)) + + # Post file-storage-refactor, repr() on File/ImageFields no longer returns the path + elif isinstance(field, models.FileField): + return repr(force_unicode(value)) + + # ForeignKey fields, link directly using our stored python variable name + elif isinstance(field, models.ForeignKey) and value is not None: + + # Special case for contenttype foreign keys: no need to output any + # content types in this script, as they can be generated again + # automatically. + # NB: Not sure if "is" will always work + if field.rel.to is ContentType: + return 'ContentType.objects.get(app_label="%s", model="%s")' % (value.app_label, value.model) + + # Generate an identifier (key) for this foreign object + pk_name = value._meta.pk.name + key = '%s_%s' % (value.__class__.__name__, getattr(value, pk_name)) + + if key in context: + variable_name = context[key] + # If the context value is set to None, this should be skipped. + # This identifies models that have been skipped (inheritance) + if variable_name is None: + raise SkipValue() + # Return the variable name listed in the context + return "%s" % variable_name + elif force: + return "%s.objects.get(%s=%s)" % (value._meta.object_name, pk_name, getattr(value, pk_name)) + else: + raise DoLater('(FK) %s.%s\n' % (item.__class__.__name__, field.name)) + + + # A normal field (e.g. a python built-in) + else: + return repr(value) + +def queue_models(models, context): + """ Works an an appropriate ordering for the models. + This isn't essential, but makes the script look nicer because + more instances can be defined on their first try. + """ + + # Max number of cycles allowed before we call it an infinite loop. + MAX_CYCLES = 5 + + model_queue = [] + number_remaining_models = len(models) + allowed_cycles = MAX_CYCLES + + while number_remaining_models > 0: + previous_number_remaining_models = number_remaining_models + + model = models.pop(0) + + # If the model is ready to be processed, add it to the list + if check_dependencies(model, model_queue): + model_class = ModelCode(model=model, context=context) + model_queue.append(model_class) + + # Otherwise put the model back at the end of the list + else: + models.append(model) + + # Check for infinite loops. + # This means there is a cyclic foreign key structure + # That cannot be resolved by re-ordering + number_remaining_models = len(models) + if number_remaining_models == previous_number_remaining_models: + allowed_cycles -= 1 + if allowed_cycles <= 0: + # Add the remaining models, but do not remove them from the model list + missing_models = [ ModelCode(model=m, context=context) for m in models ] + model_queue += missing_models + # Replace the models with the model class objects + # (sure, this is a little bit of hackery) + models[:] = missing_models + break + else: + allowed_cycles = MAX_CYCLES + + return model_queue + + +def check_dependencies(model, model_queue): + " Check that all the depenedencies for this model are already in the queue. " + + # A list of allowed links: existing fields, itself and the special case ContentType + allowed_links = [ m.model.__name__ for m in model_queue ] + [model.__name__, 'ContentType'] + + # For each ForeignKey or ManyToMany field, check that a link is possible + for field in model._meta.fields + model._meta.many_to_many: + if field.rel and field.rel.to.__name__ not in allowed_links: + return False + + return True + + + +# EXCEPTIONS +#------------------------------------------------------------------------------- + +class SkipValue(Exception): + """ Value could not be parsed or should simply be skipped. """ + +class DoLater(Exception): + """ Value could not be parsed or should simply be skipped. """ diff --git a/utils/django_command_extensions/django_extensions/management/commands/export_emails.py b/utils/django_command_extensions/django_extensions/management/commands/export_emails.py new file mode 100644 index 000000000..c95b82400 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/export_emails.py @@ -0,0 +1,114 @@ +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User, Group +from optparse import make_option +from sys import stdout +from csv import writer + +FORMATS = [ + 'address', + 'google', + 'outlook', + 'linkedin', + 'vcard', +] + +def full_name(first_name, last_name, username, **extra): + name = u" ".join(n for n in [first_name, last_name] if n) + if not name: return username + return name + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--group', '-g', action='store', dest='group', default=None, + help='Limit to users which are part of the supplied group name'), + make_option('--format', '-f', action='store', dest='format', default=FORMATS[0], + help="output format. May be one of '" + "', '".join(FORMATS) + "'."), + ) + + help = ("Export user email address list in one of a number of formats.") + args = "[output file]" + label = 'filename to save to' + + requires_model_validation = True + can_import_settings = True + encoding = 'utf-8' # RED_FLAG: add as an option -DougN + + def handle(self, *args, **options): + if len(args) > 1: + raise CommandError("extra arguments supplied") + group = options['group'] + if group and not Group.objects.filter(name=group).count()==1: + names = u"', '".join(g['name'] for g in Group.objects.values('name')).encode('utf-8') + if names: names = "'" + names + "'." + raise CommandError("Unknown group '" + group + "'. Valid group names are: " + names) + if len(args) and args[0] != '-': + outfile = file(args[0], 'w') + else: + outfile = stdout + + qs = User.objects.all().order_by('last_name', 'first_name', 'username', 'email') + if group: qs = qs.filter(group__name=group).distinct() + qs = qs.values('last_name', 'first_name', 'username', 'email') + getattr(self, options['format'])(qs, outfile) + + def address(self, qs, out): + """simple single entry per line in the format of: + "full name" ; + """ + out.write(u"\n".join(u'"%s" <%s>;' % (full_name(**ent), ent['email']) + for ent in qs).encode(self.encoding)) + out.write("\n") + + def google(self, qs, out): + """CSV format suitable for importing into google GMail + """ + csvf = writer(out) + csvf.writerow(['Name', 'Email']) + for ent in qs: + csvf.writerow([full_name(**ent).encode(self.encoding), + ent['email'].encode(self.encoding)]) + + def outlook(self, qs, out): + """CSV format suitable for importing into outlook + """ + csvf = writer(out) + columns = ['Name','E-mail Address','Notes','E-mail 2 Address','E-mail 3 Address', + 'Mobile Phone','Pager','Company','Job Title','Home Phone','Home Phone 2', + 'Home Fax','Home Address','Business Phone','Business Phone 2', + 'Business Fax','Business Address','Other Phone','Other Fax','Other Address'] + csvf.writerow(columns) + empty = [''] * (len(columns) - 2) + for ent in qs: + csvf.writerow([full_name(**ent).encode(self.encoding), + ent['email'].encode(self.encoding)] + empty) + + def linkedin(self, qs, out): + """CSV format suitable for importing into linkedin Groups. + perfect for pre-approving members of a linkedin group. + """ + csvf = writer(out) + csvf.writerow(['First Name', 'Last Name', 'Email']) + for ent in qs: + csvf.writerow([ent['first_name'].encode(self.encoding), + ent['last_name'].encode(self.encoding), + ent['email'].encode(self.encoding)]) + + def vcard(self, qs, out): + try: + import vobject + except ImportError: + print self.style.ERROR_OUTPUT("Please install python-vobject to use the vcard export format.") + import sys + sys.exit(1) + for ent in qs: + card = vobject.vCard() + card.add('fn').value = full_name(**ent) + if not ent['last_name'] and not ent['first_name']: + # fallback to fullname, if both first and lastname are not declared + card.add('n').value = vobject.vcard.Name(full_name(**ent)) + else: + card.add('n').value = vobject.vcard.Name(ent['last_name'], ent['first_name']) + emailpart = card.add('email') + emailpart.value = ent['email'] + emailpart.type_param = 'INTERNET' + out.write(card.serialize().encode(self.encoding)) diff --git a/utils/django_command_extensions/django_extensions/management/commands/generate_secret_key.py b/utils/django_command_extensions/django_extensions/management/commands/generate_secret_key.py new file mode 100644 index 000000000..e4a494d6e --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/generate_secret_key.py @@ -0,0 +1,10 @@ +from random import choice +from django.core.management.base import NoArgsCommand + +class Command(NoArgsCommand): + help = "Generates a new SECRET_KEY that can be used in a project settings file." + + requires_model_validation = False + + def handle_noargs(self, **options): + return ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)]) diff --git a/utils/django_command_extensions/django_extensions/management/commands/graph_models.py b/utils/django_command_extensions/django_extensions/management/commands/graph_models.py new file mode 100644 index 000000000..aa72af1f4 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/graph_models.py @@ -0,0 +1,60 @@ +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option +from django_extensions.management.modelviz import generate_dot + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--disable-fields', '-d', action='store_true', dest='disable_fields', + help='Do not show the class member fields'), + make_option('--group-models', '-g', action='store_true', dest='group_models', + help='Group models together respective to there application'), + make_option('--all-applications', '-a', action='store_true', dest='all_applications', + help='Automaticly include all applications from INSTALLED_APPS'), + make_option('--output', '-o', action='store', dest='outputfile', + help='Render output file. Type of output dependend on file extensions. Use png or jpg to render graph to image.'), + make_option('--layout', '-l', action='store', dest='layout', default='dot', + help='Layout to be used by GraphViz for visualization. Layouts: circo dot fdp neato nop nop1 nop2 twopi'), + ) + + help = ("Creates a GraphViz dot file for the specified app names. You can pass multiple app names and they will all be combined into a single model. Output is usually directed to a dot file.") + args = "[appname]" + label = 'application name' + + requires_model_validation = True + can_import_settings = True + + def handle(self, *args, **options): + if len(args) < 1 and not options['all_applications']: + raise CommandError("need one or more arguments for appname") + + dotdata = generate_dot(args, **options) + if options['outputfile']: + self.render_output(dotdata, **options) + else: + self.print_output(dotdata) + + def print_output(self, dotdata): + print dotdata + + def render_output(self, dotdata, **kwargs): + try: + import pygraphviz + except ImportError, e: + raise CommandError("need pygraphviz python module ( apt-get install python-pygraphviz )") + + vizdata = ' '.join(dotdata.split("\n")).strip() + version = pygraphviz.__version__.rstrip("-svn") + try: + if [int(v) for v in version.split('.')]<(0,36): + # HACK around old/broken AGraph before version 0.36 (ubuntu ships with this old version) + import tempfile + tmpfile = tempfile.NamedTemporaryFile() + tmpfile.write(vizdata) + tmpfile.seek(0) + vizdata = tmpfile.name + except ValueError: + pass + + graph = pygraphviz.AGraph(vizdata) + graph.layout(prog=kwargs['layout']) + graph.draw(kwargs['outputfile']) diff --git a/utils/django_command_extensions/django_extensions/management/commands/mail_debug.py b/utils/django_command_extensions/django_extensions/management/commands/mail_debug.py new file mode 100644 index 000000000..78532dfeb --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/mail_debug.py @@ -0,0 +1,41 @@ +from django.core.management.base import BaseCommand +import sys +import smtpd +import asyncore + +class Command(BaseCommand): + help = "Starts a test mail server for development." + args = '[optional port number or ippaddr:port]' + + requires_model_validation = False + + def handle(self, addrport='', *args, **options): + if args: + raise CommandError('Usage is runserver %s' % self.args) + if not addrport: + addr = '' + port = '1025' + else: + try: + addr, port = addrport.split(':') + except ValueError: + addr, port = '', addrport + if not addr: + addr = '127.0.0.1' + + if not port.isdigit(): + raise CommandError("%r is not a valid port number." % port) + else: + port = int(port) + + quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' + + def inner_run(): + print "Now accepting mail at %s:%s" % (addr, port) + server = smtpd.DebuggingServer((addr,port), None) + asyncore.loop() + + try: + inner_run() + except KeyboardInterrupt: + pass diff --git a/utils/django_command_extensions/django_extensions/management/commands/passwd.py b/utils/django_command_extensions/django_extensions/management/commands/passwd.py new file mode 100644 index 000000000..8296f92fc --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/passwd.py @@ -0,0 +1,37 @@ +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User +import getpass + +class Command(BaseCommand): + help = "Clone of the UNIX program ``passwd'', for django.contrib.auth." + + requires_model_validation = False + + def handle(self, *args, **options): + if len(args) > 1: + raise CommandError("need exactly one or zero arguments for username") + + if args: + username, = args + else: + username = getpass.getuser() + + try: + u = User.objects.get(username=username) + except User.DoesNotExist: + raise CommandError("user %s does not exist" % username) + + print "Changing password for user", u.username + p1 = p2 = "" + while "" in (p1, p2) or p1 != p2: + p1 = getpass.getpass() + p2 = getpass.getpass("Password (again): ") + if p1 != p2: + print "Passwords do not match, try again" + elif "" in (p1, p2): + raise CommandError("aborted") + + u.set_password(p1) + u.save() + + return "Password changed successfully for user", u.username diff --git a/utils/django_command_extensions/django_extensions/management/commands/print_user_for_session.py b/utils/django_command_extensions/django_extensions/management/commands/print_user_for_session.py new file mode 100644 index 000000000..6c8fda8f7 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/print_user_for_session.py @@ -0,0 +1,49 @@ +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.models import User +from django.contrib.sessions.models import Session +import re + +SESSION_RE = re.compile("^[0-9a-f]{20,40}$") + +class Command(BaseCommand): + help = ("print the user information for the provided session key. " + "this is very helpful when trying to track down the person who " + "experienced a site crash.") + args = "session_key" + label = 'session key for the user' + + requires_model_validation = True + can_import_settings = True + + def handle(self, *args, **options): + if len(args) > 1: + raise CommandError("extra arguments supplied") + if len(args) < 1: + raise CommandError("session_key argument missing") + key = args[0].lower() + if not SESSION_RE.match(key): + raise CommandError("malformed session key") + try: + session = Session.objects.get(pk=key) + except Session.DoesNotExist: + print "Session Key does not exist. Expired?" + return + + data = session.get_decoded() + print 'Session to Expire:', session.expire_date + print 'Raw Data:', data + uid = data.get('_auth_user_id', None) + if uid is None: + print 'No user associated with session' + return + print "User id:", uid + try: + user = User.objects.get(pk=uid) + except User.DoesNotExist: + print "No user associated with that id." + return + for key in ['username', 'email', 'first_name', 'last_name']: + print key+': ' + getattr(user, key) + + + diff --git a/utils/django_command_extensions/django_extensions/management/commands/reset_db.py b/utils/django_command_extensions/django_extensions/management/commands/reset_db.py new file mode 100644 index 000000000..fadb0764a --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/reset_db.py @@ -0,0 +1,117 @@ +""" +originally from http://www.djangosnippets.org/snippets/828/ by dnordberg + +""" + + +from django.conf import settings +from django.core.management.base import CommandError, BaseCommand +from django.db import connection +import logging +from optparse import make_option + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--noinput', action='store_false', + dest='interactive', default=True, + help='Tells Django to NOT prompt the user for input of any kind.'), + make_option('--no-utf8', action='store_true', + dest='no_utf8_support', default=False, + help='Tells Django to not create a UTF-8 charset database'), + ) + help = "Resets the database for this project." + + def handle(self, *args, **options): + """ + Resets the database for this project. + + Note: Transaction wrappers are in reverse as a work around for + autocommit, anybody know how to do this the right way? + """ + + if options.get('interactive'): + confirm = raw_input(""" +You have requested a database reset. +This will IRREVERSIBLY DESTROY +ALL data in the database "%s". +Are you sure you want to do this? + +Type 'yes' to continue, or 'no' to cancel: """ % (settings.DATABASE_NAME,)) + else: + confirm = 'yes' + + if confirm != 'yes': + print "Reset cancelled." + return + + engine = settings.DATABASE_ENGINE + + if engine == 'sqlite3': + import os + try: + logging.info("Unlinking sqlite3 database") + os.unlink(settings.DATABASE_NAME) + except OSError: + pass + elif engine == 'mysql': + import MySQLdb as Database + kwargs = { + 'user': settings.DATABASE_USER, + 'passwd': settings.DATABASE_PASSWORD, + } + if settings.DATABASE_HOST.startswith('/'): + kwargs['unix_socket'] = settings.DATABASE_HOST + else: + kwargs['host'] = settings.DATABASE_HOST + if settings.DATABASE_PORT: + kwargs['port'] = int(settings.DATABASE_PORT) + connection = Database.connect(**kwargs) + drop_query = 'DROP DATABASE IF EXISTS %s' % settings.DATABASE_NAME + utf8_support = options.get('no_utf8_support', False) and '' or 'CHARACTER SET utf8' + create_query = 'CREATE DATABASE %s %s' % (settings.DATABASE_NAME, utf8_support) + logging.info('Executing... "' + drop_query + '"') + connection.query(drop_query) + logging.info('Executing... "' + create_query + '"') + connection.query(create_query) + elif engine == 'postgresql' or engine == 'postgresql_psycopg2': + if engine == 'postgresql': + import psycopg as Database + elif engine == 'postgresql_psycopg2': + import psycopg2 as Database + + if settings.DATABASE_NAME == '': + from django.core.exceptions import ImproperlyConfigured + raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file." + if settings.DATABASE_USER: + conn_string = "user=%s" % (settings.DATABASE_USER) + if settings.DATABASE_PASSWORD: + conn_string += " password='%s'" % settings.DATABASE_PASSWORD + if settings.DATABASE_HOST: + conn_string += " host=%s" % settings.DATABASE_HOST + if settings.DATABASE_PORT: + conn_string += " port=%s" % settings.DATABASE_PORT + connection = Database.connect(conn_string) + connection.set_isolation_level(0) #autocommit false + cursor = connection.cursor() + drop_query = 'DROP DATABASE %s' % settings.DATABASE_NAME + logging.info('Executing... "' + drop_query + '"') + + try: + cursor.execute(drop_query) + except Database.ProgrammingError, e: + logging.info("Error: "+str(e)) + + # Encoding should be SQL_ASCII (7-bit postgres default) or prefered UTF8 (8-bit) + create_query = (""" +CREATE DATABASE %s + WITH OWNER = %s + ENCODING = 'UTF8' + TABLESPACE = pg_default; +""" % (settings.DATABASE_NAME, settings.DATABASE_USER)) + logging.info('Executing... "' + create_query + '"') + cursor.execute(create_query) + + else: + raise CommandError, "Unknown database engine %s", engine + + print "Reset successful." diff --git a/utils/django_command_extensions/django_extensions/management/commands/runjob.py b/utils/django_command_extensions/django_extensions/management/commands/runjob.py new file mode 100644 index 000000000..897f3f106 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/runjob.py @@ -0,0 +1,59 @@ +from django.core.management.base import LabelCommand +from optparse import make_option +from django_extensions.management.jobs import get_job, print_jobs + +class Command(LabelCommand): + option_list = LabelCommand.option_list + ( + make_option('--list', '-l', action="store_true", dest="list_jobs", + help="List all jobs with there description"), + ) + help = "Run a single maintenance job." + args = "[app_name] job_name" + label = "" + + requires_model_validation = True + + def runjob(self, app_name, job_name, options): + verbosity = int(options.get('verbosity', 1)) + if verbosity>1: + print "Executing job: %s (app: %s)" % (job_name, app_name) + try: + job = get_job(app_name, job_name) + except KeyError, e: + if app_name: + print "Error: Job %s for applabel %s not found" % (app_name, job_name) + else: + print "Error: Job %s not found" % job_name + print "Use -l option to view all the available jobs" + return + try: + job().execute() + except Exception, e: + import traceback + print "ERROR OCCURED IN JOB: %s (APP: %s)" % (job_name, app_name) + print "START TRACEBACK:" + traceback.print_exc() + print "END TRACEBACK\n" + + def handle(self, *args, **options): + app_name = None + job_name = None + if len(args)==1: + job_name = args[0] + elif len(args)==2: + app_name, job_name = args + if options.get('list_jobs'): + print_jobs(only_scheduled=False, show_when=True, show_appname=True) + else: + if not job_name: + print "Run a single maintenance job. Please specify the name of the job." + return + self.runjob(app_name, job_name, options) + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/utils/django_command_extensions/django_extensions/management/commands/runjobs.py b/utils/django_command_extensions/django_extensions/management/commands/runjobs.py new file mode 100644 index 000000000..2b658b857 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/runjobs.py @@ -0,0 +1,90 @@ +from django.core.management.base import LabelCommand +from optparse import make_option +from django_extensions.management.jobs import get_jobs, print_jobs + +class Command(LabelCommand): + option_list = LabelCommand.option_list + ( + make_option('--list', '-l', action="store_true", dest="list_jobs", + help="List all jobs with there description"), + ) + help = "Runs scheduled maintenance jobs." + args = "[hourly daily weekly monthly]" + label = "" + + requires_model_validation = True + + def usage_msg(self): + print "Run scheduled jobs. Please specify 'hourly', 'daily', 'weekly' or 'monthly'" + + def runjobs(self, when, options): + verbosity = int(options.get('verbosity', 1)) + jobs = get_jobs(when, only_scheduled=True) + list = jobs.keys() + list.sort() + for app_name, job_name in list: + job = jobs[(app_name, job_name)] + if verbosity>1: + print "Executing %s job: %s (app: %s)" % (when, job_name, app_name) + try: + job().execute() + except Exception, e: + import traceback + print "ERROR OCCURED IN %s JOB: %s (APP: %s)" % (when.upper(), job_name, app_name) + print "START TRACEBACK:" + traceback.print_exc() + print "END TRACEBACK\n" + + def runjobs_by_signals(self, when, options): + """ Run jobs from the signals """ + # Thanks for Ian Holsman for the idea and code + from django_extensions.management import signals + from django.db import models + from django.conf import settings + + verbosity = int(options.get('verbosity', 1)) + for app_name in settings.INSTALLED_APPS: + try: + __import__(app_name + '.management', '', '', ['']) + except ImportError: + pass + + for app in models.get_apps(): + if verbosity>1: + app_name = '.'.join(app.__name__.rsplit('.')[:-1]) + print "Sending %s job signal for: %s" % (when, app_name) + if when == 'hourly': + signals.run_hourly_jobs.send(sender=app, app=app) + elif when == 'daily': + signals.run_daily_jobs.send(sender=app, app=app) + elif when == 'weekly': + signals.run_weekly_jobs.send(sender=app, app=app) + elif when == 'monthly': + signals.run_monthly_jobs.send(sender=app, app=app) + + def handle(self, *args, **options): + when = None + if len(args)>1: + self.usage_msg() + return + elif len(args)==1: + if not args[0] in ['hourly', 'daily', 'weekly', 'monthly']: + self.usage_msg() + return + else: + when = args[0] + if options.get('list_jobs'): + print_jobs(when, only_scheduled=True, show_when=True, show_appname=True) + else: + if not when: + self.usage_msg() + return + self.runjobs(when, options) + self.runjobs_by_signals(when, options) + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/utils/django_command_extensions/django_extensions/management/commands/runprofileserver.py b/utils/django_command_extensions/django_extensions/management/commands/runprofileserver.py new file mode 100644 index 000000000..5eed35dd6 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/runprofileserver.py @@ -0,0 +1,213 @@ +""" +runprofileserver.py + + Starts a lightweight Web server with profiling enabled. + +Credits for kcachegrind support taken from lsprofcalltree.py go to: + David Allouche + Jp Calderone & Itamar Shtull-Trauring + Johan Dahlin +""" + +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option +import os +import sys + +def label(code): + if isinstance(code, str): + return ('~', 0, code) # built-in functions ('~' sorts at the end) + else: + return '%s %s:%d' % (code.co_name, + code.co_filename, + code.co_firstlineno) + +class KCacheGrind(object): + def __init__(self, profiler): + self.data = profiler.getstats() + self.out_file = None + + def output(self, out_file): + self.out_file = out_file + print >> out_file, 'events: Ticks' + self._print_summary() + for entry in self.data: + self._entry(entry) + + def _print_summary(self): + max_cost = 0 + for entry in self.data: + totaltime = int(entry.totaltime * 1000) + max_cost = max(max_cost, totaltime) + print >> self.out_file, 'summary: %d' % (max_cost,) + + def _entry(self, entry): + out_file = self.out_file + + code = entry.code + #print >> out_file, 'ob=%s' % (code.co_filename,) + if isinstance(code, str): + print >> out_file, 'fi=~' + else: + print >> out_file, 'fi=%s' % (code.co_filename,) + print >> out_file, 'fn=%s' % (label(code),) + + inlinetime = int(entry.inlinetime * 1000) + if isinstance(code, str): + print >> out_file, '0 ', inlinetime + else: + print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime) + + # recursive calls are counted in entry.calls + if entry.calls: + calls = entry.calls + else: + calls = [] + + if isinstance(code, str): + lineno = 0 + else: + lineno = code.co_firstlineno + + for subentry in calls: + self._subentry(lineno, subentry) + print >> out_file + + def _subentry(self, lineno, subentry): + out_file = self.out_file + code = subentry.code + #print >> out_file, 'cob=%s' % (code.co_filename,) + print >> out_file, 'cfn=%s' % (label(code),) + if isinstance(code, str): + print >> out_file, 'cfi=~' + print >> out_file, 'calls=%d 0' % (subentry.callcount,) + else: + print >> out_file, 'cfi=%s' % (code.co_filename,) + print >> out_file, 'calls=%d %d' % ( + subentry.callcount, code.co_firstlineno) + + totaltime = int(subentry.totaltime * 1000) + print >> out_file, '%d %d' % (lineno, totaltime) + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--noreload', action='store_false', dest='use_reloader', default=True, + help='Tells Django to NOT use the auto-reloader.'), + make_option('--adminmedia', dest='admin_media_path', default='', + help='Specifies the directory from which to serve admin media.'), + make_option('--prof-path', dest='prof_path', default='/tmp', + help='Specifies the directory which to save profile information in.'), + make_option('--nomedia', action='store_true', dest='no_media', default=False, + help='Do not profile MEDIA_URL and ADMIN_MEDIA_URL'), + make_option('--use-cprofile', action='store_true', dest='use_cprofile', default=False, + help='Use cProfile if available, this is disabled per default because of incompatibilities.'), + make_option('--kcachegrind', action='store_true', dest='use_lsprof', default=False, + help='Create kcachegrind compatible lsprof files, this requires and automatically enables cProfile.'), + ) + help = "Starts a lightweight Web server with profiling enabled." + args = '[optional port number, or ipaddr:port]' + + # Validation is called explicitly each time the server is reloaded. + requires_model_validation = False + + def handle(self, addrport='', *args, **options): + import django + from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException + from django.core.handlers.wsgi import WSGIHandler + if args: + raise CommandError('Usage is runserver %s' % self.args) + if not addrport: + addr = '' + port = '8000' + else: + try: + addr, port = addrport.split(':') + except ValueError: + addr, port = '', addrport + if not addr: + addr = '127.0.0.1' + + if not port.isdigit(): + raise CommandError("%r is not a valid port number." % port) + + use_reloader = options.get('use_reloader', True) + admin_media_path = options.get('admin_media_path', '') + shutdown_message = options.get('shutdown_message', '') + no_media = options.get('no_media', False) + quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' + + def inner_run(): + from django.conf import settings + + import hotshot, time, os + USE_CPROFILE = options.get('use_cprofile', False) + USE_LSPROF = options.get('use_lsprof', False) + if USE_LSPROF: + USE_CPROFILE = True + if USE_CPROFILE: + try: + import cProfile + USE_CPROFILE = True + except ImportError: + print "cProfile disabled, module cannot be imported!" + USE_CPROFILE = False + if USE_LSPROF and not USE_CPROFILE: + raise SystemExit("Kcachegrind compatible output format required cProfile from Python 2.5") + prof_path = options.get('prof_path', '/tmp') + def make_profiler_handler(inner_handler): + def handler(environ, start_response): + path_info = environ['PATH_INFO'] + # normally /media/ is MEDIA_URL, but in case still check it in case it's differently + # should be hardly a penalty since it's an OR expression. + # TODO: fix this to check the configuration settings and not make assumpsions about where media are on the url + if no_media and (path_info.startswith('/media') or path_info.startswith(settings.MEDIA_URL)): + return inner_handler(environ, start_response) + path_name = path_info.strip("/").replace('/', '.') or "root" + profname = "%s.%.3f.prof" % (path_name, time.time()) + profname = os.path.join(prof_path, profname) + if USE_CPROFILE: + prof = cProfile.Profile() + else: + prof = hotshot.Profile(profname) + try: + return prof.runcall(inner_handler, environ, start_response) + finally: + if USE_LSPROF: + kg = KCacheGrind(prof) + kg.output(file(profname, 'w')) + elif USE_CPROFILE: + prof.dump_stats(profname) + return handler + + print "Validating models..." + self.validate(display_num_errors=True) + print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE) + print "Development server is running at http://%s:%s/" % (addr, port) + print "Quit the server with %s." % quit_command + try: + path = admin_media_path or django.__path__[0] + '/contrib/admin/media' + handler = make_profiler_handler(AdminMediaHandler(WSGIHandler(), path)) + run(addr, int(port), handler) + except WSGIServerException, e: + # Use helpful error messages instead of ugly tracebacks. + ERRORS = { + 13: "You don't have permission to access that port.", + 98: "That port is already in use.", + 99: "That IP address can't be assigned-to.", + } + try: + error_text = ERRORS[e.args[0].args[0]] + except (AttributeError, KeyError): + error_text = str(e) + sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n') + # Need to use an OS exit because sys.exit doesn't work in a thread + os._exit(1) + except KeyboardInterrupt: + if shutdown_message: + print shutdown_message + sys.exit(0) + if use_reloader: + from django.utils import autoreload + autoreload.main(inner_run) + else: + inner_run() diff --git a/utils/django_command_extensions/django_extensions/management/commands/runscript.py b/utils/django_command_extensions/django_extensions/management/commands/runscript.py new file mode 100644 index 000000000..ebd25c234 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/runscript.py @@ -0,0 +1,95 @@ +from django.core.management.base import BaseCommand +from django.core.management.color import no_style +from optparse import make_option +import sys +import os + +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--fixtures', action='store_true', dest='infixtures', default=False, + help='Only look in app.fixtures subdir'), + make_option('--noscripts', action='store_true', dest='noscripts', default=False, + help='Look in app.scripts subdir'), + ) + help = 'Runs a script in django context.' + args = "script [script ...]" + + def handle(self, *scripts, **options): + from django.db.models import get_apps + + subdirs = [] + + if not options.get('noscripts'): + subdirs.append('scripts') + if options.get('infixtures'): + subdirs.append('fixtures') + verbosity = int(options.get('verbosity', 1)) + show_traceback = options.get('traceback', False) + + if len(subdirs) < 1: + print "No subdirs to run left." + return + + if len(scripts) < 1: + print "Script name required." + return + + def run_script(name): + if verbosity > 1: + print "check for %s" % name + try: + t = __import__(name, [], [], [" "]) + + if verbosity > 0: + print "Found script %s ..." %name + if hasattr(t, "run"): + if verbosity > 1: + print "found run() in %s. executing..." % name + # TODO: add arguments to run + try: + t.run() + except Exception, e: + if verbosity > 0: + print "Exception while running run() in %s" %name + if show_traceback: + raise + else: + if verbosity > 1: + print "no run() function found." + + except ImportError: + pass + + + for app in get_apps(): + app_name = app.__name__.split(".")[:-1] # + ['fixtures'] + + for subdir in subdirs: + for script in scripts: + run_script(".".join(app_name + [subdir, script])) + + # try app.DIR.script import + for script in scripts: + sa = script.split(".") + for subdir in subdirs: + nn = ".".join(sa[:-1] + [subdir, sa[-1]]) + run_script(nn) + + # try direct import + if script.find(".") != -1: + run_script(script) + + + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/utils/django_command_extensions/django_extensions/management/commands/runserver_plus.py b/utils/django_command_extensions/django_extensions/management/commands/runserver_plus.py new file mode 100644 index 000000000..f1bf4beda --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/runserver_plus.py @@ -0,0 +1,75 @@ +from django.core.management.base import BaseCommand, CommandError +from optparse import make_option +import os +import sys + +def null_technical_500_response(request, exc_type, exc_value, tb): + raise exc_type, exc_value, tb + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--noreload', action='store_false', dest='use_reloader', default=True, + help='Tells Django to NOT use the auto-reloader.'), + make_option('--browser', action='store_true', dest='open_browser', + help='Tells Django to open a browser.'), + make_option('--adminmedia', dest='admin_media_path', default='', + help='Specifies the directory from which to serve admin media.'), + ) + help = "Starts a lightweight Web server for development." + args = '[optional port number, or ipaddr:port]' + + # Validation is called explicitly each time the server is reloaded. + requires_model_validation = False + + def handle(self, addrport='', *args, **options): + import django + from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException + from django.core.handlers.wsgi import WSGIHandler + try: + from werkzeug import run_simple, DebuggedApplication + except: + raise CommandError("Werkzeug is required to use runserver_plus. Please visit http://werkzeug.pocoo.org/download") + + # usurp django's handler + from django.views import debug + debug.technical_500_response = null_technical_500_response + + if args: + raise CommandError('Usage is runserver %s' % self.args) + if not addrport: + addr = '' + port = '8000' + else: + try: + addr, port = addrport.split(':') + except ValueError: + addr, port = '', addrport + if not addr: + addr = '127.0.0.1' + + if not port.isdigit(): + raise CommandError("%r is not a valid port number." % port) + + use_reloader = options.get('use_reloader', True) + open_browser = options.get('open_browser', False) + admin_media_path = options.get('admin_media_path', '') + shutdown_message = options.get('shutdown_message', '') + quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' + + def inner_run(): + from django.conf import settings + print "Validating models..." + self.validate(display_num_errors=True) + print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE) + print "Development server is running at http://%s:%s/" % (addr, port) + print "Using the Werkzeug debugger (http://werkzeug.pocoo.org/)" + print "Quit the server with %s." % quit_command + path = admin_media_path or django.__path__[0] + '/contrib/admin/media' + handler = AdminMediaHandler(WSGIHandler(), path) + if open_browser: + import webbrowser + url = "http://%s:%s/" % (addr, port) + webbrowser.open(url) + run_simple(addr, int(port), DebuggedApplication(handler, True), + use_reloader=use_reloader, use_debugger=True) + inner_run() diff --git a/utils/django_command_extensions/django_extensions/management/commands/set_fake_emails.py b/utils/django_command_extensions/django_extensions/management/commands/set_fake_emails.py new file mode 100644 index 000000000..e0baa1c9a --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/set_fake_emails.py @@ -0,0 +1,76 @@ +""" +set_fake_emails.py + + Give all users a new email account. Useful for testing in a + development environment. As such, this command is only available when + setting.DEBUG is True. + +""" +from optparse import make_option + +from django.conf import settings +from django.core.management.base import NoArgsCommand, CommandError + +DEFAULT_FAKE_EMAIL = '%(username)s@example.com' + +class Command(NoArgsCommand): + option_list = NoArgsCommand.option_list + ( + make_option('--email', dest='default_email', default=DEFAULT_FAKE_EMAIL, + help='Use this as the new email format.'), + make_option('-a', '--no-admin', action="store_true", dest='no_admin', default=False, + help='Do not change administrator accounts'), + make_option('-s', '--no-staff', action="store_true", dest='no_staff', default=False, + help='Do not change staff accounts'), + make_option('--include', dest='include_regexp', default=None, + help='Include usernames matching this regexp.'), + make_option('--exclude', dest='exclude_regexp', default=None, + help='Exclude usernames matching this regexp.'), + make_option('--include-groups', dest='include_groups', default=None, + help='Include users matching this group. (use comma seperation for multiple groups)'), + make_option('--exclude-groups', dest='exclude_groups', default=None, + help='Exclude users matching this group. (use comma seperation for multiple groups)'), + ) + help = '''DEBUG only: give all users a new email based on their account data ("%s" by default). Possible parameters are: username, first_name, last_name''' % (DEFAULT_FAKE_EMAIL, ) + requires_model_validation = False + + def handle_noargs(self, **options): + if not settings.DEBUG: + raise CommandError('Only available in debug mode') + + from django.contrib.auth.models import User, Group + email = options.get('default_email', DEFAULT_FAKE_EMAIL) + include_regexp = options.get('include_regexp', None) + exclude_regexp = options.get('exclude_regexp', None) + include_groups = options.get('include_groups', None) + exclude_groups = options.get('exclude_groups', None) + no_admin = options.get('no_admin', False) + no_staff = options.get('no_staff', False) + + users = User.objects.all() + if no_admin: + users = users.exclude(is_superuser=True) + if no_staff: + users = users.exclude(is_staff=True) + if exclude_groups: + groups = Group.objects.filter(name__in=exclude_groups.split(",")) + if groups: + users = users.exclude(groups__in=groups) + else: + raise CommandError("No group matches filter: %s" % exclude_groups) + if include_groups: + groups = Group.objects.filter(name__in=include_groups.split(",")) + if groups: + users = users.filter(groups__in=groups) + else: + raise CommandError("No groups matches filter: %s" % include_groups) + if exclude_regexp: + users = users.exclude(username__regex=exclude_regexp) + if include_regexp: + users = users.filter(username__regex=include_regexp) + for user in users: + user.email = email % {'username': user.username, + 'first_name': user.first_name, + 'last_name': user.last_name} + user.save() + + print 'Changed %d emails' % users.count() diff --git a/utils/django_command_extensions/django_extensions/management/commands/set_fake_passwords.py b/utils/django_command_extensions/django_extensions/management/commands/set_fake_passwords.py new file mode 100644 index 000000000..2a65ef4d4 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/set_fake_passwords.py @@ -0,0 +1,44 @@ +""" +set_fake_passwords.py + + Reset all user passwords to a common value. Useful for testing in a + development environment. As such, this command is only available when + setting.DEBUG is True. + +""" +from optparse import make_option + +from django.conf import settings +from django.core.management.base import NoArgsCommand, CommandError + +DEFAULT_FAKE_PASSWORD = 'password' + +class Command(NoArgsCommand): + option_list = NoArgsCommand.option_list + ( + make_option('--prompt', dest='prompt_passwd', default=False, action='store_true', + help='Prompts for the new password to apply to all users'), + make_option('--password', dest='default_passwd', default=DEFAULT_FAKE_PASSWORD, + help='Use this as default password.'), + ) + help = 'DEBUG only: sets all user passwords to a common value ("%s" by default)' % (DEFAULT_FAKE_PASSWORD, ) + requires_model_validation = False + + def handle_noargs(self, **options): + if not settings.DEBUG: + raise CommandError('Only available in debug mode') + + from django.contrib.auth.models import User + if options.get('prompt_passwd', False): + from getpass import getpass + passwd = getpass('Password: ') + if not passwd: + raise CommandError('You must enter a valid password') + else: + passwd = options.get('default_passwd', DEFAULT_FAKE_PASSWORD) + + users = User.objects.all() + for user in users: + user.set_password(passwd) + user.save() + + print 'Reset %d passwords' % users.count() diff --git a/utils/django_command_extensions/django_extensions/management/commands/shell_plus.py b/utils/django_command_extensions/django_extensions/management/commands/shell_plus.py new file mode 100644 index 000000000..9672349fa --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/shell_plus.py @@ -0,0 +1,77 @@ +import os +from django.core.management.base import NoArgsCommand +from optparse import make_option + +class Command(NoArgsCommand): + option_list = NoArgsCommand.option_list + ( + make_option('--plain', action='store_true', dest='plain', + help='Tells Django to use plain Python, not IPython.'), + make_option('--no-pythonrc', action='store_true', dest='no_pythonrc', + help='Tells Django to use plain Python, not IPython.'), + ) + help = "Like the 'shell' command but autoloads the models of all installed Django apps." + + requires_model_validation = True + + def handle_noargs(self, **options): + # XXX: (Temporary) workaround for ticket #1796: force early loading of all + # models from installed apps. (this is fixed by now, but leaving it here + # for people using 0.96 or older trunk (pre [5919]) versions. + from django.db.models.loading import get_models, get_apps + loaded_models = get_models() + + use_plain = options.get('plain', False) + use_pythonrc = not options.get('no_pythonrc', True) + + # Set up a dictionary to serve as the environment for the shell, so + # that tab completion works on objects that are imported at runtime. + # See ticket 5082. + from django.conf import settings + imported_objects = {'settings': settings} + for app_mod in get_apps(): + app_models = get_models(app_mod) + if not app_models: + continue + model_labels = ", ".join([model.__name__ for model in app_models]) + print self.style.SQL_COLTYPE("From '%s' autoload: %s" % (app_mod.__name__.split('.')[-2], model_labels)) + for model in app_models: + try: + imported_objects[model.__name__] = getattr(__import__(app_mod.__name__, {}, {}, model.__name__), model.__name__) + except AttributeError, e: + print self.style.ERROR_OUTPUT("Failed to import '%s' from '%s' reason: %s" % (model.__name__, app_mod.__name__.split('.')[-2], str(e))) + continue + try: + if use_plain: + # Don't bother loading IPython, because the user wants plain Python. + raise ImportError + import IPython + # Explicitly pass an empty list as arguments, because otherwise IPython + # would use sys.argv from this script. + shell = IPython.Shell.IPShell(argv=[], user_ns=imported_objects) + shell.mainloop() + except ImportError: + # Using normal Python shell + import code + try: # Try activating rlcompleter, because it's handy. + import readline + except ImportError: + pass + else: + # We don't have to wrap the following import in a 'try', because + # we already know 'readline' was imported successfully. + import rlcompleter + readline.set_completer(rlcompleter.Completer(imported_objects).complete) + readline.parse_and_bind("tab:complete") + + # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system + # conventions and get $PYTHONSTARTUP first then import user. + if use_pythonrc: + pythonrc = os.environ.get("PYTHONSTARTUP") + if pythonrc and os.path.isfile(pythonrc): + try: + execfile(pythonrc) + except NameError: + pass + # This will import .pythonrc.py as a side-effect + import user + code.interact(local=imported_objects) diff --git a/utils/django_command_extensions/django_extensions/management/commands/show_urls.py b/utils/django_command_extensions/django_extensions/management/commands/show_urls.py new file mode 100644 index 000000000..d026f28e0 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/show_urls.py @@ -0,0 +1,45 @@ +from django.conf import settings +from django.core.management.base import BaseCommand +try: + # 2008-05-30 admindocs found in newforms-admin brand + from django.contrib.admindocs.views import extract_views_from_urlpatterns, simplify_regex +except ImportError: + # fall back to trunk, pre-NFA merge + from django.contrib.admin.views.doc import extract_views_from_urlpatterns, simplify_regex + +from django_extensions.management.color import color_style + +class Command(BaseCommand): + help = "Displays all of the url matching routes for the project." + + requires_model_validation = True + + def handle(self, *args, **options): + if args: + appname, = args + + style = color_style() + + if settings.ADMIN_FOR: + settings_modules = [__import__(m, {}, {}, ['']) for m in settings.ADMIN_FOR] + else: + settings_modules = [settings] + + views = [] + for settings_mod in settings_modules: + try: + urlconf = __import__(settings_mod.ROOT_URLCONF, {}, {}, ['']) + except Exception, e: + if options.get('traceback', None): + import traceback + traceback.print_exc() + print style.ERROR("Error occurred while trying to load %s: %s" % (settings_mod.ROOT_URLCONF, str(e))) + continue + view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns) + for (func, regex) in view_functions: + func_name = hasattr(func, '__name__') and func.__name__ or repr(func) + views.append("%(url)s\t%(module)s.%(name)s" % {'name': style.MODULE_NAME(func_name), + 'module': style.MODULE(func.__module__), + 'url': style.URL(simplify_regex(regex))}) + + return "\n".join([v for v in views]) diff --git a/utils/django_command_extensions/django_extensions/management/commands/sqldiff.py b/utils/django_command_extensions/django_extensions/management/commands/sqldiff.py new file mode 100644 index 000000000..3e722346c --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/sqldiff.py @@ -0,0 +1,276 @@ +""" +sqldiff.py - Prints the (approximated) difference between models and database + +TODO: + - seperate out PostgreSQL specific introspection hacks, to facilitate easier + writing backend specific code. (like the constrains check's) + - general cleanup + - better support for relations + +KNOWN ISSUES: + - MySQL has numerous problems with introspection. It's not recommanded to use + sqldiff in conjuction with MySQL. But if you do, expect to see a hole lot + of false positives. Mainly: + - Booleans are reported back as Integers, so there's know way to know if + there was a real change. + - Varchar sizes are reported back without unicode support so there size + may change in comparison to the real length of the varchar. +""" + +from django.core.management.base import AppCommand +from django.db import transaction +from optparse import make_option + + +class Command(AppCommand): + option_list = AppCommand.option_list + ( + make_option('--all-applications', '-a', action='store_true', dest='all_applications', + help="Automaticly include all application from INSTALLED_APPS."), + make_option('--not-only-existing', '-e', action='store_false', dest='only_existing', + help="Check all tables that exist in the database, not only tables that should exist based on models."), + make_option('--dense-output', '-d', action='store_true', dest='dense_output', + help="Shows the output in dense format, normally output is spreaded over multiple lines."), + make_option('--output_sql', '-s', action='store_true', dest='sql', + help="Outputs the differences as SQL when available"), + ) + + help = """Prints the (approximated) difference between models and fields in the database for the given app name(s). + +It indicates how columns in the database are different from the sql that would +be generated by Django. This command is not a database migration tool. (Though +it can certainly help) It's purpose is to show the current differences as a way +to check/debug ur models compared to the real database tables and columns.""" + + output_transaction = False + + def handle(self, *app_labels, **options): + if options.get('all_applications', False) and not app_labels: + from django.db import models + app_labels = [app.__name__.split('.')[-2] for app in models.get_apps()] + super(Command, self).handle(*app_labels, **options) + + def handle_app(self, app, **options): + from django.conf import settings + self.is_pgsql = settings.DATABASE_ENGINE.startswith("postgresql") + self.is_sqlite = settings.DATABASE_ENGINE.startswith("sqlite") + self.is_mysql = settings.DATABASE_ENGINE.startswith("mysql") + self.handle_diff(app, **options) + + @transaction.commit_manually + def handle_diff(self, app, **options): + from django.db import models, connection + from django.core.management import sql as _sql + + app_name = app.__name__.split('.')[-2] + + try: + django_tables = connection.introspection.django_table_names(only_existing=options.get('only_existing', True)) + except AttributeError: + # backwards compatibility for before introspection refactoring (r8296) + try: + django_tables = _sql.django_table_names(only_existing=options.get('only_existing', True)) + except AttributeError: + # backwards compatibility for before svn r7568 + django_tables = _sql.django_table_list(only_existing=options.get('only_existing', True)) + django_tables = [django_table for django_table in django_tables if django_table.startswith(app_name)] + + app_models = models.get_models(app) + if not app_models: + return + + try: + from django.db import get_introspection_module + introspection_module = get_introspection_module() + except ImportError: + introspection_module = connection.introspection + + cursor = connection.cursor() + model_diffs = [] + for app_model in app_models: + _constraints = None + _meta = app_model._meta + table_name = _meta.db_table + table_indexes = introspection_module.get_indexes(cursor, table_name) + + fieldmap = dict([(field.get_attname(), field) for field in _meta.fields]) + try: + table_description = introspection_module.get_table_description(cursor, table_name) + except Exception, e: + model_diffs.append((app_model.__name__, [str(e).strip()])) + transaction.rollback() # reset transaction + continue + diffs = [] + for i, row in enumerate(table_description): + att_name = row[0].lower() + try: + db_field_reverse_type = introspection_module.data_types_reverse[row[1]] + except AttributeError: + # backwards compatibility for before introspection refactoring (r8296) + db_field_reverse_type = introspection_module.DATA_TYPES_REVERSE.get(row[1]) + kwargs = {} + if isinstance(db_field_reverse_type, tuple): + kwargs.update(db_field_reverse_type[1]) + db_field_reverse_type = db_field_reverse_type[0] + + if db_field_reverse_type == "CharField" and row[3]: + kwargs['max_length'] = row[3] + + if db_field_reverse_type == "DecimalField": + kwargs['max_digits'] = row[4] + kwargs['decimal_places'] = row[5] + + if row[6]: + kwargs['blank'] = True + if not db_field_reverse_type in ('TextField', 'CharField'): + kwargs['null'] = True + + if fieldmap.has_key(att_name): + field = fieldmap.pop(att_name) + # check type + def clean(s): + s = s.split(" ")[0] + s = s.split("(")[0] + return s + def cmp_or_serialcmp(x, y): + result = x==y + if result: + return result + is_serial = lambda x,y: x.startswith("serial") and y.startswith("integer") + strip_serial = lambda x: x.lstrip("serial").lstrip("integer") + serial_logic = is_serial(x, y) or is_serial(y, x) + if result==False and serial_logic: + # use alternate serial logic + result = strip_serial(x)==strip_serial(y) + return result + db_field_type = getattr(models, db_field_reverse_type)(**kwargs).db_type() + model_type = field.db_type() + + # remove mysql's auto_increment keyword + if self.is_mysql and model_type.endswith("AUTO_INCREMENT"): + model_type = model_type.rsplit(' ', 1)[0].strip() + + # check if we can for constraints (only enabled on postgresql atm) + if self.is_pgsql: + if _constraints==None: + sql = """ + SELECT + pg_constraint.conname, pg_get_constraintdef(pg_constraint.oid) + FROM + pg_constraint, pg_attribute + WHERE + pg_constraint.conrelid = pg_attribute.attrelid + AND pg_attribute.attnum = any(pg_constraint.conkey) + AND pg_constraint.conname ~ %s""" + cursor.execute(sql, [table_name]) + _constraints = [r for r in cursor.fetchall() if r[0].endswith("_check")] + for r_name, r_check in _constraints: + if table_name+"_"+att_name==r_name.rsplit("_check")[0]: + r_check = r_check.replace("((", "(").replace("))", ")") + pos = r_check.find("(") + r_check = "%s\"%s" % (r_check[:pos+1], r_check[pos+1:]) + pos = pos+r_check[pos:].find(" ") + r_check = "%s\" %s" % (r_check[:pos], r_check[pos+1:]) + db_field_type += " "+r_check + else: + # remove constraints + model_type = model_type.split("CHECK")[0].strip() + c_db_field_type = clean(db_field_type) + c_model_type = clean(model_type) + + if self.is_sqlite and (c_db_field_type=="varchar" and c_model_type=="char"): + c_db_field_type = "char" + db_field_type = db_field_type.lstrip("var") + + if not cmp_or_serialcmp(c_model_type, c_db_field_type): + diffs.append({ + 'text' : "field '%s' not of same type: db=%s, model=%s" % (att_name, c_db_field_type, c_model_type), + 'type' : 'type', + 'data' : (table_name, att_name, c_db_field_type, c_model_type) + }) + continue + if not cmp_or_serialcmp(db_field_type, model_type): + diffs.append({ + 'text' : "field '%s' parameters differ: db=%s, model=%s" % (att_name, db_field_type, model_type), + 'type' : 'param', + 'data' : (table_name, att_name, db_field_type, model_type) + }) + continue + else: + diffs.append({ + 'text' : "field '%s' missing in model: %s" % (att_name, model_type), + 'type' : 'missing-in-model', + 'data' : (table_name, att_name, db_field_type, model_type) + }) + for field in _meta.fields: + if field.db_index: + if not field.attname in table_indexes and not field.unique: + diffs.append({ + 'text' : "field '%s' INDEX defined in model missing in database" % (field.attname), + }) + if fieldmap: + for att_name, field in fieldmap.items(): + diffs.append({ + 'text' : "field '%s' missing in database: %s" % (att_name, field.db_type()), + 'type' : 'missing-in-db', + 'data' : (table_name, att_name, field.db_type()) + }) + if diffs: + model_diffs.append((app_model.__name__, diffs)) + + if model_diffs: + NOTICE = self.style.NOTICE + ERROR_OUTPUT = self.style.ERROR_OUTPUT + SQL_TABLE = self.style.SQL_TABLE + SQL_FIELD = self.style.SQL_FIELD + SQL_COLTYPE = self.style.SQL_COLTYPE + SQL_KEYWORD = self.style.SQL_KEYWORD + modify_command = self.is_pgsql and "TYPE" or "MODIFY" + + if self.is_mysql: + print ERROR_OUTPUT("""\ +Using sqldiff in conjuction with MySQL has known problems. +Please see the explanations about these problems in source +code of sqldiff.py. + +Use at your own risk, and but sure to tripple check every +result. This program will continue in 5 seconds. + """) + import time + time.sleep(5) + + if options.get('sql', False): + lines = ["", SQL_KEYWORD("BEGIN;")] + + for model_name, diffs in model_diffs: + for diff in diffs: + if not diff: continue + if not diff.get('data', False): continue + + if self.is_sqlite and diff['type'] == 'param': + lines.append(NOTICE('-- %s' % diff['text'])) + lines.append(NOTICE('-- SQLite does not feature type alteration on columns')) + continue + lines.append('%s %s' % (SQL_KEYWORD('ALTER TABLE'), SQL_TABLE(diff['data'][0])) ) + if diff['type'] == 'missing-in-db': + lines.append('\t%s %s %s;' % (SQL_KEYWORD('ADD'), SQL_FIELD(diff['data'][1]), SQL_COLTYPE(diff['data'][2]),) ) + if diff['type'] == 'missing-in-model': + lines.append('\t%s %s;' % (SQL_KEYWORD('DROP COLUMN') , SQL_FIELD(diff['data'][1]) )) + if diff['type'] in ['type', 'param']: + lines.append('\t%s %s %s %s;' % (SQL_KEYWORD('ALTER'), SQL_FIELD(diff['data'][1]), SQL_KEYWORD(modify_command), SQL_COLTYPE(diff['data'][3]))) + lines.append(SQL_KEYWORD("COMMIT;")) + + print "\n".join(lines) + else: + dense = options.get('dense_output', False) + if not dense: + print NOTICE("+ Application:"), SQL_TABLE(app_name) + for model_name, diffs in model_diffs: + if not diffs: continue + if not dense: + print NOTICE("|-+ Differences for model:"), SQL_TABLE(model_name) + for diff in diffs: + if not dense: + print NOTICE("|--+"), ERROR_OUTPUT(diff['text']) + else: + print NOTICE("App"), SQL_TABLE(app_name), NOTICE('Model'), SQL_TABLE(model_name), ERROR_OUTPUT(diff['text']) + diff --git a/utils/django_command_extensions/django_extensions/management/commands/sync_media_s3.py b/utils/django_command_extensions/django_extensions/management/commands/sync_media_s3.py new file mode 100644 index 000000000..92f242f91 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/sync_media_s3.py @@ -0,0 +1,249 @@ +""" +Sync Media to S3 +================ + +Django command that scans all files in your settings.MEDIA_ROOT folder and +uploads them to S3 with the same directory structure. + +This command can optionally do the following but it is off by default: +* gzip compress any CSS and Javascript files it finds and adds the appropriate + 'Content-Encoding' header. +* set a far future 'Expires' header for optimal caching. + +Note: This script requires the Python boto library and valid Amazon Web +Services API keys. + +Required settings.py variables: +AWS_ACCESS_KEY_ID = '' +AWS_SECRET_ACCESS_KEY = '' +AWS_BUCKET_NAME = '' + +Command options are: + -p PREFIX, --prefix=PREFIX + The prefix to prepend to the path on S3. + --gzip Enables gzipping CSS and Javascript files. + --expires Enables setting a far future expires header. + --force Skip the file mtime check to force upload of all + files. + +TODO: +* Make FILTER_LIST an optional argument + +""" +import datetime +import email +import mimetypes +import optparse +import os +import sys +import time + +from django.core.management.base import BaseCommand, CommandError + +# Make sure boto is available +try: + import boto + import boto.exception +except ImportError: + raise ImportError, "The boto Python library is not installed." + +class Command(BaseCommand): + + # Extra variables to avoid passing these around + AWS_ACCESS_KEY_ID = '' + AWS_SECRET_ACCESS_KEY = '' + AWS_BUCKET_NAME = '' + DIRECTORY = '' + FILTER_LIST = ['.DS_Store',] + GZIP_CONTENT_TYPES = ( + 'text/css', + 'application/javascript', + 'application/x-javascript' + ) + + upload_count = 0 + skip_count = 0 + + option_list = BaseCommand.option_list + ( + optparse.make_option('-p', '--prefix', + dest='prefix', default='', + help="The prefix to prepend to the path on S3."), + optparse.make_option('--gzip', + action='store_true', dest='gzip', default=False, + help="Enables gzipping CSS and Javascript files."), + optparse.make_option('--expires', + action='store_true', dest='expires', default=False, + help="Enables setting a far future expires header."), + optparse.make_option('--force', + action='store_true', dest='force', default=False, + help="Skip the file mtime check to force upload of all files.") + ) + + help = 'Syncs the complete MEDIA_ROOT structure and files to S3 into the given bucket name.' + args = 'bucket_name' + + can_import_settings = True + + def handle(self, *args, **options): + from django.conf import settings + + # Check for AWS keys in settings + if not hasattr(settings, 'AWS_ACCESS_KEY_ID') or \ + not hasattr(settings, 'AWS_SECRET_ACCESS_KEY'): + raise CommandError('Missing AWS keys from settings file. Please' + + 'supply both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.') + else: + self.AWS_ACCESS_KEY_ID = settings.AWS_ACCESS_KEY_ID + self.AWS_SECRET_ACCESS_KEY = settings.AWS_SECRET_ACCESS_KEY + + if not hasattr(settings, 'AWS_BUCKET_NAME'): + raise CommandError('Missing bucket name from settings file. Please' + + ' add the AWS_BUCKET_NAME to your settings file.') + else: + if not settings.AWS_BUCKET_NAME: + raise CommandError('AWS_BUCKET_NAME cannot be empty.') + self.AWS_BUCKET_NAME = settings.AWS_BUCKET_NAME + + if not hasattr(settings, 'MEDIA_ROOT'): + raise CommandError('MEDIA_ROOT must be set in your settings.') + else: + if not settings.MEDIA_ROOT: + raise CommandError('MEDIA_ROOT must be set in your settings.') + self.DIRECTORY = settings.MEDIA_ROOT + + self.verbosity = int(options.get('verbosity')) + self.prefix = options.get('prefix') + self.do_gzip = options.get('gzip') + self.do_expires = options.get('expires') + self.do_force = options.get('force') + + # Now call the syncing method to walk the MEDIA_ROOT directory and + # upload all files found. + self.sync_s3() + + print + print "%d files uploaded." % (self.upload_count) + print "%d files skipped." % (self.skip_count) + + def sync_s3(self): + """ + Walks the media directory and syncs files to S3 + """ + bucket, key = self.open_s3() + os.path.walk(self.DIRECTORY, self.upload_s3, + (bucket, key, self.AWS_BUCKET_NAME, self.DIRECTORY)) + + def compress_string(self, s): + """Gzip a given string.""" + import cStringIO, gzip + zbuf = cStringIO.StringIO() + zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf) + zfile.write(s) + zfile.close() + return zbuf.getvalue() + + def open_s3(self): + """ + Opens connection to S3 returning bucket and key + """ + conn = boto.connect_s3(self.AWS_ACCESS_KEY_ID, self.AWS_SECRET_ACCESS_KEY) + try: + bucket = conn.get_bucket(self.AWS_BUCKET_NAME) + except boto.exception.S3ResponseError: + bucket = conn.create_bucket(self.AWS_BUCKET_NAME) + return bucket, boto.s3.key.Key(bucket) + + def upload_s3(self, arg, dirname, names): + """ + This is the callback to os.path.walk and where much of the work happens + """ + bucket, key, bucket_name, root_dir = arg # expand arg tuple + + if root_dir == dirname: + return # We're in the root media folder + + # Later we assume the MEDIA_ROOT ends with a trailing slash + # TODO: Check if we should check os.path.sep for Windows + if not root_dir.endswith('/'): + root_dir = root_dir + '/' + + for file in names: + headers = {} + + if file in self.FILTER_LIST: + continue # Skip files we don't want to sync + + filename = os.path.join(dirname, file) + if os.path.isdir(filename): + continue # Don't try to upload directories + + file_key = filename[len(root_dir):] + if self.prefix: + file_key = '%s/%s' % (self.prefix, file_key) + + # Check if file on S3 is older than local file, if so, upload + if not self.do_force: + s3_key = bucket.get_key(file_key) + if s3_key: + s3_datetime = datetime.datetime(*time.strptime( + s3_key.last_modified, '%a, %d %b %Y %H:%M:%S %Z')[0:6]) + local_datetime = datetime.datetime.utcfromtimestamp( + os.stat(filename).st_mtime) + if local_datetime < s3_datetime: + self.skip_count += 1 + if self.verbosity > 1: + print "File %s hasn't been modified since last " \ + "being uploaded" % (file_key) + continue + + # File is newer, let's process and upload + if self.verbosity > 0: + print "Uploading %s..." % (file_key) + + content_type = mimetypes.guess_type(filename)[0] + if content_type: + headers['Content-Type'] = content_type + file_obj = open(filename, 'rb') + file_size = os.fstat(file_obj.fileno()).st_size + filedata = file_obj.read() + if self.do_gzip: + # Gzipping only if file is large enough (>1K is recommended) + # and only if file is a common text type (not a binary file) + if file_size > 1024 and content_type in self.GZIP_CONTENT_TYPES: + filedata = self.compress_string(filedata) + headers['Content-Encoding'] = 'gzip' + if self.verbosity > 1: + print "\tgzipped: %dk to %dk" % \ + (file_size/1024, len(filedata)/1024) + if self.do_expires: + # HTTP/1.0 + headers['Expires'] = '%s GMT' % (email.Utils.formatdate( + time.mktime((datetime.datetime.now() + + datetime.timedelta(days=365*2)).timetuple()))) + # HTTP/1.1 + headers['Cache-Control'] = 'max-age %d' % (3600 * 24 * 365 * 2) + if self.verbosity > 1: + print "\texpires: %s" % (headers['Expires']) + print "\tcache-control: %s" % (headers['Cache-Control']) + + try: + key.name = file_key + key.set_contents_from_string(filedata, headers, replace=True) + key.make_public() + except boto.s3.connection.S3CreateError, e: + print "Failed: %s" % e + except Exception, e: + print e + raise + else: + self.upload_count += 1 + + file_obj.close() + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + optparse.make_option('-v', '--verbosity', + dest='verbosity', default=1, action='count', + help="Verbose mode. Multiple -v options increase the verbosity."), + ) diff --git a/utils/django_command_extensions/django_extensions/management/commands/syncdata.py b/utils/django_command_extensions/django_extensions/management/commands/syncdata.py new file mode 100644 index 000000000..e6e5dcd02 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/commands/syncdata.py @@ -0,0 +1,221 @@ +""" +SyncData +======== + +Django command similar to 'loaddata' but also deletes. +After 'syncdata' has run, the database will have the same data as the fixture - anything +missing will of been added, anything different will of been updated, +and anything extra will of been deleted. +""" + +from django.core.management.base import BaseCommand +from django.core.management.color import no_style +from optparse import make_option +import sys +import os + +class Command(BaseCommand): + """ syncdata command """ + + help = 'Makes the current database have the same data as the fixture(s), no more, no less.' + args = "fixture [fixture ...]" + + def remove_objects_not_in(self, objects_to_keep, verbosity): + """ + Deletes all the objects in the database that are not in objects_to_keep. + - objects_to_keep: A map where the keys are classes, and the values are a + set of the objects of that class we should keep. + """ + for class_ in objects_to_keep.keys(): + + current = class_.objects.all() + current_ids = set( [x.id for x in current] ) + keep_ids = set( [x.id for x in objects_to_keep[class_]] ) + + remove_these_ones = current_ids.difference(keep_ids) + if remove_these_ones: + + for obj in current: + if obj.id in remove_these_ones: + obj.delete() + if verbosity >= 2: + print "Deleted object: "+ unicode(obj) + + if verbosity > 0 and remove_these_ones: + num_deleted = len(remove_these_ones) + if num_deleted > 1: + type_deleted = unicode(class_._meta.verbose_name_plural) + else: + type_deleted = unicode(class_._meta.verbose_name) + + print "Deleted "+ str(num_deleted) +" "+ type_deleted + + def handle(self, *fixture_labels, **options): + """ Main method of a Django command """ + from django.db.models import get_apps + from django.core import serializers + from django.db import connection, transaction + from django.conf import settings + + self.style = no_style() + + verbosity = int(options.get('verbosity', 1)) + show_traceback = options.get('traceback', False) + + # Keep a count of the installed objects and fixtures + fixture_count = 0 + object_count = 0 + objects_per_fixture = [] + models = set() + + humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' + + # Get a cursor (even though we don't need one yet). This has + # the side effect of initializing the test database (if + # it isn't already initialized). + cursor = connection.cursor() + + # Start transaction management. All fixtures are installed in a + # single transaction to ensure that all references are resolved. + transaction.commit_unless_managed() + transaction.enter_transaction_management() + transaction.managed(True) + + app_fixtures = [os.path.join(os.path.dirname(app.__file__), 'fixtures') \ + for app in get_apps()] + for fixture_label in fixture_labels: + parts = fixture_label.split('.') + if len(parts) == 1: + fixture_name = fixture_label + formats = serializers.get_public_serializer_formats() + else: + fixture_name, format = '.'.join(parts[:-1]), parts[-1] + if format in serializers.get_public_serializer_formats(): + formats = [format] + else: + formats = [] + + if formats: + if verbosity > 1: + print "Loading '%s' fixtures..." % fixture_name + else: + sys.stderr.write( + self.style.ERROR("Problem installing fixture '%s': %s is not a known "+ \ + "serialization format." % (fixture_name, format)) + ) + transaction.rollback() + transaction.leave_transaction_management() + return + + if os.path.isabs(fixture_name): + fixture_dirs = [fixture_name] + else: + fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + [''] + + for fixture_dir in fixture_dirs: + if verbosity > 1: + print "Checking %s for fixtures..." % humanize(fixture_dir) + + label_found = False + for format in formats: + serializer = serializers.get_serializer(format) + if verbosity > 1: + print "Trying %s for %s fixture '%s'..." % \ + (humanize(fixture_dir), format, fixture_name) + try: + full_path = os.path.join(fixture_dir, '.'.join([fixture_name, format])) + fixture = open(full_path, 'r') + if label_found: + fixture.close() + print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." % + (fixture_name, humanize(fixture_dir))) + transaction.rollback() + transaction.leave_transaction_management() + return + else: + fixture_count += 1 + objects_per_fixture.append(0) + if verbosity > 0: + print "Installing %s fixture '%s' from %s." % \ + (format, fixture_name, humanize(fixture_dir)) + try: + objects_to_keep = {} + objects = serializers.deserialize(format, fixture) + for obj in objects: + object_count += 1 + objects_per_fixture[-1] += 1 + + class_ = obj.object.__class__ + if not class_ in objects_to_keep: + objects_to_keep[class_] = set() + objects_to_keep[class_].add(obj.object) + + models.add(class_) + obj.save() + + self.remove_objects_not_in(objects_to_keep, verbosity) + + label_found = True + except (SystemExit, KeyboardInterrupt): + raise + except Exception: + import traceback + fixture.close() + transaction.rollback() + transaction.leave_transaction_management() + if show_traceback: + traceback.print_exc() + else: + sys.stderr.write( + self.style.ERROR("Problem installing fixture '%s': %s\n" % + (full_path, traceback.format_exc()))) + return + fixture.close() + except: + if verbosity > 1: + print "No %s fixture '%s' in %s." % \ + (format, fixture_name, humanize(fixture_dir)) + + # If any of the fixtures we loaded contain 0 objects, assume that an + # error was encountered during fixture loading. + if 0 in objects_per_fixture: + sys.stderr.write( + self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" % + (fixture_name))) + transaction.rollback() + transaction.leave_transaction_management() + return + + # If we found even one object in a fixture, we need to reset the + # database sequences. + if object_count > 0: + sequence_sql = connection.ops.sequence_reset_sql(self.style, models) + if sequence_sql: + if verbosity > 1: + print "Resetting sequences" + for line in sequence_sql: + cursor.execute(line) + + transaction.commit() + transaction.leave_transaction_management() + + if object_count == 0: + if verbosity > 1: + print "No fixtures found." + else: + if verbosity > 0: + print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count) + + # Close the DB connection. This is required as a workaround for an + # edge case in MySQL: if the same connection is used to + # create tables, load data, and query, the query can return + # incorrect results. See Django #7572, MySQL #37735. + connection.close() + +# Backwards compatibility for Django r9110 +if not [opt for opt in Command.option_list if opt.dest=='verbosity']: + Command.option_list += ( + make_option('--verbosity', '-v', action="store", dest="verbosity", + default='1', type='choice', choices=['0', '1', '2'], + help="Verbosity level; 0=minimal output, 1=normal output, 2=all output"), + ) diff --git a/utils/django_command_extensions/django_extensions/management/jobs.py b/utils/django_command_extensions/django_extensions/management/jobs.py new file mode 100644 index 000000000..6515375d4 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/jobs.py @@ -0,0 +1,154 @@ +""" +django_extensions.management.jobs +""" + +import os +from imp import find_module + +_jobs = None + +def noneimplementation(meth): + return None + +class JobError(Exception): + pass + +class BaseJob(object): + help = "undefined job description." + when = None + + def execute(self): + raise NotImplementedError("Job needs to implement the execute method") + +class HourlyJob(BaseJob): + when = "hourly" + +class DailyJob(BaseJob): + when = "daily" + +class WeeklyJob(BaseJob): + when = "weekly" + +class MonthlyJob(BaseJob): + when = "monthly" + +def my_import(name): + imp = __import__(name) + mods = name.split('.') + if len(mods)>1: + for mod in mods[1:]: + imp = getattr(imp, mod) + return imp + +def find_jobs(jobs_dir): + try: + return [f[:-3] for f in os.listdir(jobs_dir) \ + if not f.startswith('_') and f.endswith(".py")] + except OSError: + return [] + +def find_job_module(app_name, when=None): + parts = app_name.split('.') + parts.append('jobs') + if when: + parts.append(when) + parts.reverse() + path = None + while parts: + part = parts.pop() + f, path, descr = find_module(part, path and [path] or None) + return path + +def import_job(app_name, name, when=None): + jobmodule = "%s.jobs.%s%s" % (app_name, when and "%s." % when or "", name) + job_mod = my_import(jobmodule) + # todo: more friendly message for AttributeError if job_mod does not exist + try: + job = job_mod.Job + except: + raise JobError("Job module %s does not contain class instance named 'Job'" % jobmodule) + if when and not (job.when == when or job.when == None): + raise JobError("Job %s is not a %s job." % (jobmodule, when)) + return job + +def get_jobs(when=None, only_scheduled=False): + """ + Returns a dictionary mapping of job names together with there respective + application class. + """ + global _jobs + # FIXME: HACK: make sure the project dir is on the path when executed as ./manage.py + import sys + try: + cpath = os.path.dirname(os.path.realpath(sys.argv[0])) + ppath = os.path.dirname(cpath) + if ppath not in sys.path: + sys.path.append(ppath) + except: + pass + if _jobs is None: + _jobs = {} + if True: + from django.conf import settings + for app_name in settings.INSTALLED_APPS: + scandirs = (None, 'hourly', 'daily', 'weekly', 'monthly') + if when: + scandirs = None, when + for subdir in scandirs: + try: + path = find_job_module(app_name, subdir) + for name in find_jobs(path): + if (app_name, name) in _jobs: + raise JobError("Duplicate job %s" % name) + job = import_job(app_name, name, subdir) + if only_scheduled and job.when == None: + # only include jobs which are scheduled + continue + if when and job.when != when: + # generic job not in same schedule + continue + _jobs[(app_name, name)] = job + except ImportError: + pass # No job module -- continue scanning + return _jobs + +def get_job(app_name, job_name): + jobs = get_jobs() + if app_name: + return jobs[(app_name, job_name)] + else: + for a, j in jobs.keys(): + if j==job_name: + return jobs[(a, j)] + raise KeyError("Job not found: %s" % job_name) + +def print_jobs(when=None, only_scheduled=False, show_when=True, \ + show_appname=False, show_header=True): + jobmap = get_jobs(when, only_scheduled=only_scheduled) + print "Job List: %i jobs" % len(jobmap) + jlist = jobmap.keys() + jlist.sort() + appname_spacer = "%%-%is" % max(len(e[0]) for e in jlist) + name_spacer = "%%-%is" % max(len(e[1]) for e in jlist) + when_spacer = "%%-%is" % max(len(e.when) for e in jobmap.values() if e.when) + if show_header: + line = " " + if show_appname: + line += appname_spacer % "appname" + " - " + line += name_spacer % "jobname" + if show_when: + line += " - " + when_spacer % "when" + line += " - help" + print line + print "-"*80 + + for app_name, job_name in jlist: + job = jobmap[(app_name, job_name)] + line = " " + if show_appname: + line += appname_spacer % app_name + " - " + line += name_spacer % job_name + if show_when: + line += " - " + when_spacer % (job.when and job.when or "") + line += " - " + job.help + print line diff --git a/utils/django_command_extensions/django_extensions/management/modelviz.py b/utils/django_command_extensions/django_extensions/management/modelviz.py new file mode 100644 index 000000000..5a9454bf7 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/modelviz.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +"""Django model to DOT (Graphviz) converter +by Antonio Cavedoni + +Make sure your DJANGO_SETTINGS_MODULE is set to your project or +place this script in the same directory of the project and call +the script like this: + +$ python modelviz.py [-h] [-a] [-d] [-g] [-i ] ... > .dot +$ dot .dot -Tpng -o .png + +options: + -h, --help + show this help message and exit. + + -a, --all_applications + show models from all applications. + + -d, --disable_fields + don't show the class member fields. + + -g, --group_models + draw an enclosing box around models from the same app. + + -i, --include_models=User,Person,Car + only include selected models in graph. +""" +__version__ = "0.9" +__svnid__ = "$Id$" +__license__ = "Python" +__author__ = "Antonio Cavedoni " +__contributors__ = [ + "Stefano J. Attardi ", + "limodou ", + "Carlo C8E Miron", + "Andre Campos ", + "Justin Findlay ", + "Alexander Houben ", + "Bas van Oostveen ", +] + +import getopt, sys + +from django.core.management import setup_environ + +try: + import settings +except ImportError: + pass +else: + setup_environ(settings) + +from django.utils.safestring import mark_safe +from django.template import Template, Context +from django.db import models +from django.db.models import get_models +from django.db.models.fields.related import \ + ForeignKey, OneToOneField, ManyToManyField + +try: + from django.db.models.fields.generic import GenericRelation +except ImportError: + from django.contrib.contenttypes.generic import GenericRelation + +head_template = """ +digraph name { + fontname = "Helvetica" + fontsize = 8 + + node [ + fontname = "Helvetica" + fontsize = 8 + shape = "plaintext" + ] + edge [ + fontname = "Helvetica" + fontsize = 8 + ] + +""" + +body_template = """ +{% if use_subgraph %} +subgraph {{ cluster_app_name }} { + label=< + + +
{{ app_name }}
+ > + color=olivedrab4 + style="rounded" +{% endif %} + + {% for model in models %} + {{ model.app_name }}_{{ model.name }} [label=< + + + + {% if not disable_fields %} + {% for field in model.fields %} + + + {% endfor %} + {% endif %} +
{{ model.name }}{% if model.abstracts %}
<{{ model.abstracts|join:"," }}>{% endif %}
{{ field.name }}{{ field.type }}
+ >] + {% endfor %} + +{% if use_subgraph %} +} +{% endif %} +""" + +rel_template = """ + {% for model in models %} + {% for relation in model.relations %} + {% if relation.needs_node %} + {{ relation.target_app }}_{{ relation.target }} [label=< + + +
{{ relation.target }}
+ >] + {% endif %} + {{ model.app_name }}_{{ model.name }} -> {{ relation.target_app }}_{{ relation.target }} + [label="{{ relation.name }}"] {{ relation.arrows }}; + {% endfor %} + {% endfor %} +""" + +tail_template = """ +} +""" + +def generate_dot(app_labels, **kwargs): + disable_fields = kwargs.get('disable_fields', False) + include_models = kwargs.get('include_models', []) + all_applications = kwargs.get('all_applications', False) + use_subgraph = kwargs.get('group_models', False) + + dot = head_template + + apps = [] + if all_applications: + apps = models.get_apps() + + for app_label in app_labels: + app = models.get_app(app_label) + if not app in apps: + apps.append(app) + + graphs = [] + for app in apps: + graph = Context({ + 'name': '"%s"' % app.__name__, + 'app_name': "%s" % '.'.join(app.__name__.split('.')[:-1]), + 'cluster_app_name': "cluster_%s" % app.__name__.replace(".", "_"), + 'disable_fields': disable_fields, + 'use_subgraph': use_subgraph, + 'models': [] + }) + + for appmodel in get_models(app): + abstracts = [e.__name__ for e in appmodel.__bases__ if hasattr(e, '_meta') and e._meta.abstract] + abstract_fields = [] + for e in appmodel.__bases__: + if hasattr(e, '_meta') and e._meta.abstract: + abstract_fields.extend(e._meta.fields) + model = { + 'app_name': app.__name__.replace(".", "_"), + 'name': appmodel.__name__, + 'abstracts': abstracts, + 'fields': [], + 'relations': [] + } + + # consider given model name ? + def consider(model_name): + return not include_models or model_name in include_models + + if not consider(appmodel._meta.object_name): + continue + + # model attributes + def add_attributes(field): + model['fields'].append({ + 'name': field.name, + 'type': type(field).__name__, + 'blank': field.blank, + 'abstract': field in abstract_fields, + }) + + for field in appmodel._meta.fields: + add_attributes(field) + + if appmodel._meta.many_to_many: + for field in appmodel._meta.many_to_many: + add_attributes(field) + + # relations + def add_relation(field, extras=""): + _rel = { + 'target_app': field.rel.to.__module__.replace('.','_'), + 'target': field.rel.to.__name__, + 'type': type(field).__name__, + 'name': field.name, + 'arrows': extras, + 'needs_node': True + } + if _rel not in model['relations'] and consider(_rel['target']): + model['relations'].append(_rel) + + for field in appmodel._meta.fields: + if isinstance(field, ForeignKey): + add_relation(field) + elif isinstance(field, OneToOneField): + add_relation(field, '[arrowhead=none arrowtail=none]') + + if appmodel._meta.many_to_many: + for field in appmodel._meta.many_to_many: + if isinstance(field, ManyToManyField) and getattr(field, 'creates_table', False): + add_relation(field, '[arrowhead=normal arrowtail=normal]') + elif isinstance(field, GenericRelation): + add_relation(field, mark_safe('[style="dotted"] [arrowhead=normal arrowtail=normal]')) + graph['models'].append(model) + graphs.append(graph) + + nodes = [] + for graph in graphs: + nodes.extend([e['name'] for e in graph['models']]) + + for graph in graphs: + # don't draw duplication nodes because of relations + for model in graph['models']: + for relation in model['relations']: + if relation['target'] in nodes: + relation['needs_node'] = False + # render templates + t = Template(body_template) + dot += '\n' + t.render(graph) + + for graph in graphs: + t = Template(rel_template) + dot += '\n' + t.render(graph) + + dot += '\n' + tail_template + return dot + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:], "hadgi:", + ["help", "all_applications", "disable_fields", "group_models", "include_models="]) + except getopt.GetoptError, error: + print __doc__ + sys.exit(error) + + kwargs = {} + for opt, arg in opts: + if opt in ("-h", "--help"): + print __doc__ + sys.exit() + if opt in ("-a", "--all_applications"): + kwargs['all_applications'] = True + if opt in ("-d", "--disable_fields"): + kwargs['disable_fields'] = True + if opt in ("-g", "--group_models"): + kwargs['group_models'] = True + if opt in ("-i", "--include_models"): + kwargs['include_models'] = arg.split(',') + + if not args and not kwargs.get('all_applications', False): + print __doc__ + sys.exit() + + print generate_dot(args, **kwargs) + +if __name__ == "__main__": + main() diff --git a/utils/django_command_extensions/django_extensions/management/signals.py b/utils/django_command_extensions/django_extensions/management/signals.py new file mode 100644 index 000000000..e3e763d61 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/signals.py @@ -0,0 +1,9 @@ +""" +signals we use to trigger regular batch jobs +""" +from django.dispatch import Signal + +run_hourly_jobs = Signal() +run_daily_jobs = Signal() +run_weekly_jobs = Signal() +run_monthly_jobs = Signal() diff --git a/utils/django_command_extensions/django_extensions/management/utils.py b/utils/django_command_extensions/django_extensions/management/utils.py new file mode 100644 index 000000000..c757988dc --- /dev/null +++ b/utils/django_command_extensions/django_extensions/management/utils.py @@ -0,0 +1,7 @@ +from django.conf import settings +import os + +def get_project_root(): + """ get the project root directory """ + settings_mod = __import__(settings.SETTINGS_MODULE, {}, {}, ['']) + return os.path.dirname(os.path.abspath(settings_mod.__file__)) diff --git a/utils/django_command_extensions/django_extensions/media/django_extensions/css/jquery.autocomplete.css b/utils/django_command_extensions/django_extensions/media/django_extensions/css/jquery.autocomplete.css new file mode 100644 index 000000000..27a58523e --- /dev/null +++ b/utils/django_command_extensions/django_extensions/media/django_extensions/css/jquery.autocomplete.css @@ -0,0 +1,43 @@ +/***************************************************************************** + * jQuery autocomplete + ****************************************************************************/ +.ac_results { + padding: 0px; + border: 1px solid #ccc; + background-color: #fff; + overflow: hidden; + z-index: 99999; + text-align: left; +} + +.ac_results ul { + width: 100%; + list-style-position: outside; + list-style: none; + padding: 0; + margin: 0; +} + +.ac_results li { + margin: 0px; + padding: 3px 5px; + cursor: default; + display: block; + font: menu; + font-size: 12px; + line-height: 14px; + overflow: hidden; +} + +.ac_loading { + background: white url('../img/indicator.gif') right center no-repeat; +} + +.ac_odd { + background-color: #eee; +} + +.ac_over { + background-color: #999; + color: white; +} diff --git a/utils/django_command_extensions/django_extensions/media/django_extensions/img/indicator.gif b/utils/django_command_extensions/django_extensions/media/django_extensions/img/indicator.gif new file mode 100644 index 000000000..085ccaeca Binary files /dev/null and b/utils/django_command_extensions/django_extensions/media/django_extensions/img/indicator.gif differ diff --git a/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.ajaxQueue.js b/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.ajaxQueue.js new file mode 100644 index 000000000..bdd2e4f82 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.ajaxQueue.js @@ -0,0 +1,116 @@ +/** + * Ajax Queue Plugin + * + * Homepage: http://jquery.com/plugins/project/ajaxqueue + * Documentation: http://docs.jquery.com/AjaxQueue + */ + +/** + + +
    + + */ +/* + * Queued Ajax requests. + * A new Ajax request won't be started until the previous queued + * request has finished. + */ + +/* + * Synced Ajax requests. + * The Ajax request will happen as soon as you call this method, but + * the callbacks (success/error/complete) won't fire until all previous + * synced requests have been completed. + */ + + +(function($) { + + var ajax = $.ajax; + + var pendingRequests = {}; + + var synced = []; + var syncedData = []; + + $.ajax = function(settings) { + // create settings for compatibility with ajaxSetup + settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings)); + + var port = settings.port; + + switch(settings.mode) { + case "abort": + if ( pendingRequests[port] ) { + pendingRequests[port].abort(); + } + return pendingRequests[port] = ajax.apply(this, arguments); + case "queue": + var _old = settings.complete; + settings.complete = function(){ + if ( _old ) + _old.apply( this, arguments ); + jQuery([ajax]).dequeue("ajax" + port );; + }; + + jQuery([ ajax ]).queue("ajax" + port, function(){ + ajax( settings ); + }); + return; + case "sync": + var pos = synced.length; + + synced[ pos ] = { + error: settings.error, + success: settings.success, + complete: settings.complete, + done: false + }; + + syncedData[ pos ] = { + error: [], + success: [], + complete: [] + }; + + settings.error = function(){ syncedData[ pos ].error = arguments; }; + settings.success = function(){ syncedData[ pos ].success = arguments; }; + settings.complete = function(){ + syncedData[ pos ].complete = arguments; + synced[ pos ].done = true; + + if ( pos == 0 || !synced[ pos-1 ] ) + for ( var i = pos; i < synced.length && synced[i].done; i++ ) { + if ( synced[i].error ) synced[i].error.apply( jQuery, syncedData[i].error ); + if ( synced[i].success ) synced[i].success.apply( jQuery, syncedData[i].success ); + if ( synced[i].complete ) synced[i].complete.apply( jQuery, syncedData[i].complete ); + + synced[i] = null; + syncedData[i] = null; + } + }; + } + return ajax.apply(this, arguments); + }; + +})(jQuery); \ No newline at end of file diff --git a/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.autocomplete.js b/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.autocomplete.js new file mode 100644 index 000000000..5ad9178f8 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.autocomplete.js @@ -0,0 +1,759 @@ +/* + * Autocomplete - jQuery plugin 1.0.2 + * + * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $ + * + */ + +;(function($) { + +$.fn.extend({ + autocomplete: function(urlOrData, options) { + var isUrl = typeof urlOrData == "string"; + options = $.extend({}, $.Autocompleter.defaults, { + url: isUrl ? urlOrData : null, + data: isUrl ? null : urlOrData, + delay: isUrl ? $.Autocompleter.defaults.delay : 10, + max: options && !options.scroll ? 10 : 150 + }, options); + + // if highlight is set to false, replace it with a do-nothing function + options.highlight = options.highlight || function(value) { return value; }; + + // if the formatMatch option is not specified, then use formatItem for backwards compatibility + options.formatMatch = options.formatMatch || options.formatItem; + + return this.each(function() { + new $.Autocompleter(this, options); + }); + }, + result: function(handler) { + return this.bind("result", handler); + }, + search: function(handler) { + return this.trigger("search", [handler]); + }, + flushCache: function() { + return this.trigger("flushCache"); + }, + setOptions: function(options){ + return this.trigger("setOptions", [options]); + }, + unautocomplete: function() { + return this.trigger("unautocomplete"); + } +}); + +$.Autocompleter = function(input, options) { + + var KEY = { + UP: 38, + DOWN: 40, + DEL: 46, + TAB: 9, + RETURN: 13, + ESC: 27, + COMMA: 188, + PAGEUP: 33, + PAGEDOWN: 34, + BACKSPACE: 8 + }; + + // Create $ object for input element + var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); + + var timeout; + var previousValue = ""; + var cache = $.Autocompleter.Cache(options); + var hasFocus = 0; + var lastKeyPressCode; + var config = { + mouseDownOnSelect: false + }; + var select = $.Autocompleter.Select(options, input, selectCurrent, config); + + var blockSubmit; + + // prevent form submit in opera when selecting with return key + $.browser.opera && $(input.form).bind("submit.autocomplete", function() { + if (blockSubmit) { + blockSubmit = false; + return false; + } + }); + + // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all + $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { + // track last key pressed + lastKeyPressCode = event.keyCode; + switch(event.keyCode) { + + case KEY.UP: + event.preventDefault(); + if ( select.visible() ) { + select.prev(); + } else { + onChange(0, true); + } + break; + + case KEY.DOWN: + event.preventDefault(); + if ( select.visible() ) { + select.next(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEUP: + event.preventDefault(); + if ( select.visible() ) { + select.pageUp(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEDOWN: + event.preventDefault(); + if ( select.visible() ) { + select.pageDown(); + } else { + onChange(0, true); + } + break; + + // matches also semicolon + case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: + case KEY.TAB: + case KEY.RETURN: + if( selectCurrent() ) { + // stop default to prevent a form submit, Opera needs special handling + event.preventDefault(); + blockSubmit = true; + return false; + } + break; + + case KEY.ESC: + select.hide(); + break; + + default: + clearTimeout(timeout); + timeout = setTimeout(onChange, options.delay); + break; + } + }).focus(function(){ + // track whether the field has focus, we shouldn't process any + // results if the field no longer has focus + hasFocus++; + }).blur(function() { + hasFocus = 0; + if (!config.mouseDownOnSelect) { + hideResults(); + } + }).click(function() { + // show select when clicking in a focused field + if ( hasFocus++ > 1 && !select.visible() ) { + onChange(0, true); + } + }).bind("search", function() { + // TODO why not just specifying both arguments? + var fn = (arguments.length > 1) ? arguments[1] : null; + function findValueCallback(q, data) { + var result; + if( data && data.length ) { + for (var i=0; i < data.length; i++) { + if( data[i].result.toLowerCase() == q.toLowerCase() ) { + result = data[i]; + break; + } + } + } + if( typeof fn == "function" ) fn(result); + else $input.trigger("result", result && [result.data, result.value]); + } + $.each(trimWords($input.val()), function(i, value) { + request(value, findValueCallback, findValueCallback); + }); + }).bind("flushCache", function() { + cache.flush(); + }).bind("setOptions", function() { + $.extend(options, arguments[1]); + // if we've updated the data, repopulate + if ( "data" in arguments[1] ) + cache.populate(); + }).bind("unautocomplete", function() { + select.unbind(); + $input.unbind(); + $(input.form).unbind(".autocomplete"); + }); + + + function selectCurrent() { + var selected = select.selected(); + if( !selected ) + return false; + + var v = selected.result; + previousValue = v; + + if ( options.multiple ) { + var words = trimWords($input.val()); + if ( words.length > 1 ) { + v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v; + } + v += options.multipleSeparator; + } + + $input.val(v); + hideResultsNow(); + $input.trigger("result", [selected.data, selected.value]); + return true; + } + + function onChange(crap, skipPrevCheck) { + if( lastKeyPressCode == KEY.DEL ) { + select.hide(); + return; + } + + var currentValue = $input.val(); + + if ( !skipPrevCheck && currentValue == previousValue ) + return; + + previousValue = currentValue; + + currentValue = lastWord(currentValue); + if ( currentValue.length >= options.minChars) { + $input.addClass(options.loadingClass); + if (!options.matchCase) + currentValue = currentValue.toLowerCase(); + request(currentValue, receiveData, hideResultsNow); + } else { + stopLoading(); + select.hide(); + } + }; + + function trimWords(value) { + if ( !value ) { + return [""]; + } + var words = value.split( options.multipleSeparator ); + var result = []; + $.each(words, function(i, value) { + if ( $.trim(value) ) + result[i] = $.trim(value); + }); + return result; + } + + function lastWord(value) { + if ( !options.multiple ) + return value; + var words = trimWords(value); + return words[words.length - 1]; + } + + // fills in the input box w/the first match (assumed to be the best match) + // q: the term entered + // sValue: the first matching result + function autoFill(q, sValue){ + // autofill in the complete box w/the first match as long as the user hasn't entered in more data + // if the last user key pressed was backspace, don't autofill + if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { + // fill in the value (keep the case the user has typed) + $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); + // select the portion of the value not typed by the user (so the next character will erase) + $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length); + } + }; + + function hideResults() { + clearTimeout(timeout); + timeout = setTimeout(hideResultsNow, 200); + }; + + function hideResultsNow() { + var wasVisible = select.visible(); + select.hide(); + clearTimeout(timeout); + stopLoading(); + if (options.mustMatch) { + // call search and run callback + $input.search( + function (result){ + // if no value found, clear the input box + if( !result ) { + if (options.multiple) { + var words = trimWords($input.val()).slice(0, -1); + $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); + } + else + $input.val( "" ); + } + } + ); + } + if (wasVisible) + // position cursor at end of input field + $.Autocompleter.Selection(input, input.value.length, input.value.length); + }; + + function receiveData(q, data) { + if ( data && data.length && hasFocus ) { + stopLoading(); + select.display(data, q); + autoFill(q, data[0].value); + select.show(); + } else { + hideResultsNow(); + } + }; + + function request(term, success, failure) { + if (!options.matchCase) + term = term.toLowerCase(); + var data = cache.load(term); + // recieve the cached data + if (data && data.length) { + success(term, data); + // if an AJAX url has been supplied, try loading the data now + } else if( (typeof options.url == "string") && (options.url.length > 0) ){ + + var extraParams = { + timestamp: +new Date() + }; + $.each(options.extraParams, function(key, param) { + extraParams[key] = typeof param == "function" ? param() : param; + }); + + $.ajax({ + // try to leverage ajaxQueue plugin to abort previous requests + mode: "abort", + // limit abortion to this input + port: "autocomplete" + input.name, + dataType: options.dataType, + url: options.url, + data: $.extend({ + q: lastWord(term), + limit: options.max + }, extraParams), + success: function(data) { + var parsed = options.parse && options.parse(data) || parse(data); + cache.add(term, parsed); + success(term, parsed); + } + }); + } else { + // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match + select.emptyList(); + failure(term); + } + }; + + function parse(data) { + var parsed = []; + var rows = data.split("\n"); + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + row = row.split("|"); + parsed[parsed.length] = { + data: row, + value: row[0], + result: options.formatResult && options.formatResult(row, row[0]) || row[0] + }; + } + } + return parsed; + }; + + function stopLoading() { + $input.removeClass(options.loadingClass); + }; + +}; + +$.Autocompleter.defaults = { + inputClass: "ac_input", + resultsClass: "ac_results", + loadingClass: "ac_loading", + minChars: 1, + delay: 400, + matchCase: false, + matchSubset: true, + matchContains: false, + cacheLength: 10, + max: 100, + mustMatch: false, + extraParams: {}, + selectFirst: true, + formatItem: function(row) { return row[0]; }, + formatMatch: null, + autoFill: false, + width: 0, + multiple: false, + multipleSeparator: ", ", + highlight: function(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + }, + scroll: true, + scrollHeight: 180 +}; + +$.Autocompleter.Cache = function(options) { + + var data = {}; + var length = 0; + + function matchSubset(s, sub) { + if (!options.matchCase) + s = s.toLowerCase(); + var i = s.indexOf(sub); + if (i == -1) return false; + return i == 0 || options.matchContains; + }; + + function add(q, value) { + if (length > options.cacheLength){ + flush(); + } + if (!data[q]){ + length++; + } + data[q] = value; + } + + function populate(){ + if( !options.data ) return false; + // track the matches + var stMatchSets = {}, + nullData = 0; + + // no url was specified, we need to adjust the cache length to make sure it fits the local data store + if( !options.url ) options.cacheLength = 1; + + // track all options for minChars = 0 + stMatchSets[""] = []; + + // loop through the array and create a lookup structure + for ( var i = 0, ol = options.data.length; i < ol; i++ ) { + var rawValue = options.data[i]; + // if rawValue is a string, make an array otherwise just reference the array + rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; + + var value = options.formatMatch(rawValue, i+1, options.data.length); + if ( value === false ) + continue; + + var firstChar = value.charAt(0).toLowerCase(); + // if no lookup array for this character exists, look it up now + if( !stMatchSets[firstChar] ) + stMatchSets[firstChar] = []; + + // if the match is a string + var row = { + value: value, + data: rawValue, + result: options.formatResult && options.formatResult(rawValue) || value + }; + + // push the current match into the set list + stMatchSets[firstChar].push(row); + + // keep track of minChars zero items + if ( nullData++ < options.max ) { + stMatchSets[""].push(row); + } + }; + + // add the data items to the cache + $.each(stMatchSets, function(i, value) { + // increase the cache size + options.cacheLength++; + // add to the cache + add(i, value); + }); + } + + // populate any existing data + setTimeout(populate, 25); + + function flush(){ + data = {}; + length = 0; + } + + return { + flush: flush, + add: add, + populate: populate, + load: function(q) { + if (!options.cacheLength || !length) + return null; + /* + * if dealing w/local data and matchContains than we must make sure + * to loop through all the data collections looking for matches + */ + if( !options.url && options.matchContains ){ + // track all matches + var csub = []; + // loop through all the data grids for matches + for( var k in data ){ + // don't search through the stMatchSets[""] (minChars: 0) cache + // this prevents duplicates + if( k.length > 0 ){ + var c = data[k]; + $.each(c, function(i, x) { + // if we've got a match, add it to the array + if (matchSubset(x.value, q)) { + csub.push(x); + } + }); + } + } + return csub; + } else + // if the exact item exists, use it + if (data[q]){ + return data[q]; + } else + if (options.matchSubset) { + for (var i = q.length - 1; i >= options.minChars; i--) { + var c = data[q.substr(0, i)]; + if (c) { + var csub = []; + $.each(c, function(i, x) { + if (matchSubset(x.value, q)) { + csub[csub.length] = x; + } + }); + return csub; + } + } + } + return null; + } + }; +}; + +$.Autocompleter.Select = function (options, input, select, config) { + var CLASSES = { + ACTIVE: "ac_over" + }; + + var listItems, + active = -1, + data, + term = "", + needsInit = true, + element, + list; + + // Create results + function init() { + if (!needsInit) + return; + element = $("
    ") + .hide() + .addClass(options.resultsClass) + .css("position", "absolute") + .appendTo(document.body); + + list = $("
      ").appendTo(element).mouseover( function(event) { + if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { + active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); + $(target(event)).addClass(CLASSES.ACTIVE); + } + }).click(function(event) { + $(target(event)).addClass(CLASSES.ACTIVE); + select(); + // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus + input.focus(); + return false; + }).mousedown(function() { + config.mouseDownOnSelect = true; + }).mouseup(function() { + config.mouseDownOnSelect = false; + }); + + if( options.width > 0 ) + element.css("width", options.width); + + needsInit = false; + } + + function target(event) { + var element = event.target; + while(element && element.tagName != "LI") + element = element.parentNode; + // more fun with IE, sometimes event.target is empty, just ignore it then + if(!element) + return []; + return element; + } + + function moveSelect(step) { + listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); + movePosition(step); + var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); + if(options.scroll) { + var offset = 0; + listItems.slice(0, active).each(function() { + offset += this.offsetHeight; + }); + if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { + list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); + } else if(offset < list.scrollTop()) { + list.scrollTop(offset); + } + } + }; + + function movePosition(step) { + active += step; + if (active < 0) { + active = listItems.size() - 1; + } else if (active >= listItems.size()) { + active = 0; + } + } + + function limitNumberOfItems(available) { + return options.max && options.max < available + ? options.max + : available; + } + + function fillList() { + list.empty(); + var max = limitNumberOfItems(data.length); + for (var i=0; i < max; i++) { + if (!data[i]) + continue; + var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); + if ( formatted === false ) + continue; + var li = $("
    • ").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; + $.data(li, "ac_data", data[i]); + } + listItems = list.find("li"); + if ( options.selectFirst ) { + listItems.slice(0, 1).addClass(CLASSES.ACTIVE); + active = 0; + } + // apply bgiframe if available + if ( $.fn.bgiframe ) + list.bgiframe(); + } + + return { + display: function(d, q) { + init(); + data = d; + term = q; + fillList(); + }, + next: function() { + moveSelect(1); + }, + prev: function() { + moveSelect(-1); + }, + pageUp: function() { + if (active != 0 && active - 8 < 0) { + moveSelect( -active ); + } else { + moveSelect(-8); + } + }, + pageDown: function() { + if (active != listItems.size() - 1 && active + 8 > listItems.size()) { + moveSelect( listItems.size() - 1 - active ); + } else { + moveSelect(8); + } + }, + hide: function() { + element && element.hide(); + listItems && listItems.removeClass(CLASSES.ACTIVE); + active = -1; + }, + visible : function() { + return element && element.is(":visible"); + }, + current: function() { + return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); + }, + show: function() { + var offset = $(input).offset(); + element.css({ + width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), + top: offset.top + input.offsetHeight, + left: offset.left + }).show(); + if(options.scroll) { + list.scrollTop(0); + list.css({ + maxHeight: options.scrollHeight, + overflow: 'auto' + }); + + if($.browser.msie && typeof document.body.style.maxHeight === "undefined") { + var listHeight = 0; + listItems.each(function() { + listHeight += this.offsetHeight; + }); + var scrollbarsVisible = listHeight > options.scrollHeight; + list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); + if (!scrollbarsVisible) { + // IE doesn't recalculate width when scrollbar disappears + listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); + } + } + + } + }, + selected: function() { + var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); + return selected && selected.length && $.data(selected[0], "ac_data"); + }, + emptyList: function (){ + list && list.empty(); + }, + unbind: function() { + element && element.remove(); + } + }; +}; + +$.Autocompleter.Selection = function(field, start, end) { + if( field.createTextRange ){ + var selRange = field.createTextRange(); + selRange.collapse(true); + selRange.moveStart("character", start); + selRange.moveEnd("character", end); + selRange.select(); + } else if( field.setSelectionRange ){ + field.setSelectionRange(start, end); + } else { + if( field.selectionStart ){ + field.selectionStart = start; + field.selectionEnd = end; + } + } + field.focus(); +}; + +})(jQuery); \ No newline at end of file diff --git a/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.bgiframe.min.js b/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.bgiframe.min.js new file mode 100644 index 000000000..7faef4b33 --- /dev/null +++ b/utils/django_command_extensions/django_extensions/media/django_extensions/js/jquery.bgiframe.min.js @@ -0,0 +1,10 @@ +/* Copyright (c) 2006 Brandon Aaron (http://brandonaaron.net) + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * $LastChangedDate: 2007-07-22 01:45:56 +0200 (Son, 22 Jul 2007) $ + * $Rev: 2447 $ + * + * Version 2.1.1 + */ +(function($){$.fn.bgIframe=$.fn.bgiframe=function(s){if($.browser.msie&&/6.0/.test(navigator.userAgent)){s=$.extend({top:'auto',left:'auto',width:'auto',height:'auto',opacity:true,src:'javascript:false;'},s||{});var prop=function(n){return n&&n.constructor==Number?n+'px':n;},html='