Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma
authorzuber <marek@stepniowski.com>
Thu, 15 Oct 2009 16:33:42 +0000 (18:33 +0200)
committerzuber <marek@stepniowski.com>
Thu, 15 Oct 2009 16:33:42 +0000 (18:33 +0200)
Conflicts:
platforma/templates/explorer/edit_dc.html
platforma/templates/explorer/edit_text.html
platforma/templates/explorer/file_html.html
platforma/templates/explorer/split.html
platforma/templates/explorer/split_success.html

130 files changed:
fabfile.py
pip-options.txt [deleted file]
platforma/__init__.py [new file with mode: 0644]
platforma/dispatch.wsgi [new file with mode: 0755]
platforma/localsettings.sample [new file with mode: 0644]
platforma/manage.py [new file with mode: 0755]
platforma/settings.py [new file with mode: 0644]
platforma/static/css/autumn.css [new file with mode: 0644]
platforma/static/css/filelist.css [new file with mode: 0755]
platforma/static/css/html.css [new file with mode: 0644]
platforma/static/css/html_print.css [new file with mode: 0644]
platforma/static/css/jquery.modal.css [new file with mode: 0755]
platforma/static/css/managment.css [new file with mode: 0644]
platforma/static/css/master.css [new file with mode: 0644]
platforma/static/css/toolbar.css [new file with mode: 0644]
platforma/static/css/xmlcolors.css [new file with mode: 0644]
platforma/static/icons/go-next.png [new file with mode: 0644]
platforma/static/icons/go-previous.png [new file with mode: 0644]
platforma/static/icons/zoom.png [new file with mode: 0644]
platforma/static/icons/zoom_in.png [new file with mode: 0644]
platforma/static/icons/zoom_out.png [new file with mode: 0644]
platforma/static/img/spinner.gif [new file with mode: 0644]
platforma/static/js/app.js [new file with mode: 0644]
platforma/static/js/button_scripts.js [new file with mode: 0644]
platforma/static/js/editor.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/codemirror.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/editor.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/parsexml.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/select.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/stringstream.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/tokenize.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/undo.js [new file with mode: 0644]
platforma/static/js/lib/codemirror/util.js [new file with mode: 0644]
platforma/static/js/lib/jquery.cookie.js [new file with mode: 0644]
platforma/static/js/lib/jquery.hpanel.js [new file with mode: 0644]
platforma/static/js/lib/jquery.js [new file with mode: 0644]
platforma/static/js/lib/jquery.json.js [new file with mode: 0644]
platforma/static/js/lib/jquery.lazyload.js [new file with mode: 0755]
platforma/static/js/lib/jquery.logging.js [new file with mode: 0644]
platforma/static/js/lib/jquery.modal.js [new file with mode: 0755]
platforma/static/js/lib/jquery.wtooltip.js [new file with mode: 0644]
platforma/static/js/messages.js [new file with mode: 0644]
platforma/static/js/models.js [new file with mode: 0644]
platforma/static/js/views/button_toolbar.js [new file with mode: 0644]
platforma/static/js/views/editor.js [new file with mode: 0644]
platforma/static/js/views/flash.js [new file with mode: 0644]
platforma/static/js/views/gallery.js [new file with mode: 0644]
platforma/static/js/views/html.js [new file with mode: 0644]
platforma/static/js/views/panel_container.js [new file with mode: 0644]
platforma/static/js/views/split.js [new file with mode: 0644]
platforma/static/js/views/view.js [new file with mode: 0644]
platforma/static/js/views/xml.js [new file with mode: 0644]
platforma/templates/503.html [new file with mode: 0644]
platforma/templates/base.html [new file with mode: 0644]
platforma/templates/explorer/editor.html [new file with mode: 0644]
platforma/templates/explorer/file_list.html [new file with mode: 0644]
platforma/templates/explorer/file_tree_part.html [new file with mode: 0644]
platforma/templates/explorer/file_upload.html [new file with mode: 0755]
platforma/templates/explorer/folder_images.html [new file with mode: 0644]
platforma/templates/explorer/split.html [new file with mode: 0755]
platforma/templates/explorer/split_success.html [new file with mode: 0755]
platforma/templates/html4print.html [new file with mode: 0644]
platforma/templates/manager/pull_request.html [new file with mode: 0644]
platforma/templates/registration/head_login.html [new file with mode: 0644]
platforma/templates/registration/login.html [new file with mode: 0644]
platforma/templates/wysiwyg.html [new file with mode: 0644]
platforma/urls.py [new file with mode: 0644]
project/__init__.py [deleted file]
project/dispatch.wsgi [deleted file]
project/localsettings.sample [deleted file]
project/manage.py [deleted file]
project/settings.py [deleted file]
project/static/css/autumn.css [deleted file]
project/static/css/filelist.css [deleted file]
project/static/css/html.css [deleted file]
project/static/css/html_print.css [deleted file]
project/static/css/jquery.modal.css [deleted file]
project/static/css/managment.css [deleted file]
project/static/css/master.css [deleted file]
project/static/css/toolbar.css [deleted file]
project/static/css/xmlcolors.css [deleted file]
project/static/icons/go-next.png [deleted file]
project/static/icons/go-previous.png [deleted file]
project/static/icons/zoom.png [deleted file]
project/static/icons/zoom_in.png [deleted file]
project/static/icons/zoom_out.png [deleted file]
project/static/js/app.js [deleted file]
project/static/js/button_scripts.js [deleted file]
project/static/js/editor.js [deleted file]
project/static/js/lib/codemirror/codemirror.js [deleted file]
project/static/js/lib/codemirror/editor.js [deleted file]
project/static/js/lib/codemirror/parsexml.js [deleted file]
project/static/js/lib/codemirror/select.js [deleted file]
project/static/js/lib/codemirror/stringstream.js [deleted file]
project/static/js/lib/codemirror/tokenize.js [deleted file]
project/static/js/lib/codemirror/undo.js [deleted file]
project/static/js/lib/codemirror/util.js [deleted file]
project/static/js/lib/jquery.cookie.js [deleted file]
project/static/js/lib/jquery.hpanel.js [deleted file]
project/static/js/lib/jquery.js [deleted file]
project/static/js/lib/jquery.json.js [deleted file]
project/static/js/lib/jquery.lazyload.js [deleted file]
project/static/js/lib/jquery.logging.js [deleted file]
project/static/js/lib/jquery.modal.js [deleted file]
project/static/js/lib/jquery.wtooltip.js [deleted file]
project/static/js/messages.js [deleted file]
project/static/js/models.js [deleted file]
project/static/js/views/button_toolbar.js [deleted file]
project/static/js/views/editor.js [deleted file]
project/static/js/views/flash.js [deleted file]
project/static/js/views/gallery.js [deleted file]
project/static/js/views/html.js [deleted file]
project/static/js/views/panel_container.js [deleted file]
project/static/js/views/split.js [deleted file]
project/static/js/views/view.js [deleted file]
project/static/js/views/xml.js [deleted file]
project/templates/503.html [deleted file]
project/templates/base.html [deleted file]
project/templates/explorer/editor.html [deleted file]
project/templates/explorer/file_list.html [deleted file]
project/templates/explorer/file_tree_part.html [deleted file]
project/templates/explorer/file_upload.html [deleted file]
project/templates/explorer/folder_images.html [deleted file]
project/templates/html4print.html [deleted file]
project/templates/manager/pull_request.html [deleted file]
project/templates/registration/head_login.html [deleted file]
project/templates/registration/login.html [deleted file]
project/templates/wysiwyg.html [deleted file]
project/urls.py [deleted file]
requirements.txt

index 8b55e58..1180e6f 100644 (file)
@@ -1,5 +1,6 @@
 from __future__ import with_statement # needed for python 2.5
 from fabric.api import *
+from fabric.contrib import files
 
 import os
 
@@ -63,8 +64,10 @@ def deploy():
     env.release = time.strftime('%Y-%m-%dT%H%M')
 
     upload_tar_from_git()
-    upload_pybundle()
+    upload_wsgi_script()
+    upload_vhost_sample()
     install_requirements()
+    copy_localsettings()
     symlink_current_release()
     migrate()
     restart_webserver()
@@ -106,33 +109,31 @@ def upload_tar_from_git():
     run('cd %(path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
     local('rm %(release)s.tar.gz' % env)
 
+def upload_vhost_sample():
+    "Create and upload Apache virtual host configuration sample"
+    print ">>> upload vhost sample"
+    files.upload_template('%(project_name)s.vhost.template' % env, '%(path)s/%(project_name)s.vhost.sample' % env, context=env)
+
+def upload_wsgi_script():
+    "Create and upload a wsgi script sample"
+    print ">>> upload wsgi script sample"
+    files.upload_template('%(project_name)s.wsgi.template' % env, '%(path)s/%(project_name)s.wsgi' % env, context=env)
+    run('chmod ug+x %(path)s/%(project_name)s.wsgi' % env)
+
 def install_requirements():
     "Install the required packages from the requirements file using pip"
     print '>>> install requirements'
     require('release', provided_by=[deploy])
-    run('cd %(path)s; %(pip)s install -E . requirements.pybundle' % env)
-
-def upload_pybundle():
-    "Create pybundle with required libraries and upload it"
-    print ">>> upload pybundle"
+    run('cd %(path)s; %(pip)s install -E . -r %(path)s/releases/%(release)s/requirements.txt' % env, pty=True)
+    
+def copy_localsettings():
+    "Copy localsettings.py from root directory to release directory (if this file exists)"
+    print ">>> copy localsettings"
     require('release', provided_by=[deploy])
+    require('path', provided_by=[staging, production])
+
     with settings(warn_only=True):
-        pip_options = run('cat %(path)s/releases/%(release)s/pip-options.txt' % env)
-        if pip_options.failed:
-            env.pip_options = ''
-        else:
-            env.pip_options = pip_options
-            
-    requirements_mtime = os.path.getmtime('requirements.txt')
-    bundle_mtime = 0
-    try:
-        bundle_mtime = os.path.getmtime('requirements.pybundle')
-    except os.error:
-        pass
-
-    if requirements_mtime > bundle_mtime:
-        local('pip bundle requirements.pybundle %(pip_options)s -r requirements.txt' % env)
-        put('requirements.pybundle', '%(path)s' % env)
+        run('cp %(path)s/localsettings.py %(path)s/releases/%(release)s/%(project_name)s' % env)
 
 def symlink_current_release():
     "Symlink our current release"
diff --git a/pip-options.txt b/pip-options.txt
deleted file mode 100644 (file)
index a0993ce..0000000
+++ /dev/null
@@ -1 +0,0 @@
--f http://redmine.nowoczesnapolska.org.pl/projects/librarian/files
\ No newline at end of file
diff --git a/platforma/__init__.py b/platforma/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/platforma/dispatch.wsgi b/platforma/dispatch.wsgi
new file mode 100755 (executable)
index 0000000..5724b23
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+import os
+from os import path
+import sys
+
+PROJECT_ROOT = path.realpath(path.dirname(__file__))
+
+# Redirect sys.stdout to sys.stderr for bad libraries like geopy that use
+# print statements for optional import exceptions.
+sys.stdout = sys.stderr
+
+# Add apps and lib directories to PYTHONPATH
+sys.path.insert(0, path.join(PROJECT_ROOT, '../apps'))
+sys.path.insert(0, path.join(PROJECT_ROOT, '../lib'))
+
+# Emulate manage.py path hacking.
+sys.path.insert(0, path.join(PROJECT_ROOT, ".."))
+sys.path.insert(0, PROJECT_ROOT)
+
+# Run Django
+os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+
+from django.core.handlers.wsgi import WSGIHandler
+application = WSGIHandler()
+
diff --git a/platforma/localsettings.sample b/platforma/localsettings.sample
new file mode 100644 (file)
index 0000000..1bac955
--- /dev/null
@@ -0,0 +1,39 @@
+# 
+#  localsettings template for Platforma
+#
+#  Duplicate this file as localsettings.py and change it to your liking.
+#  Settings defined in localsettings.py will override settings from 
+#  settings.py file. localsettings.py should never be commited 
+#  to a version control system. Please make changes to settings.py 
+#  or localsettings.sample instead.
+#
+
+# Path to repository with managed documents
+REPOSITORY_PATH = '/home/user/repository'
+
+# Subdirectory of STATIC_ROOT containing images
+IMAGE_DIR = 'images'
+
+# Authentication via Central Authentication Server
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'django.contrib.admindocs',
+    
+    'cas_consumer',
+    'explorer',
+    'toolbar',
+    'api',
+    'wysiwyg',
+)
+
+AUTHENTICATION_BACKENDS = (
+    'cas_consumer.backends.CASBackend',
+)
+
+CAS_BASE = 'http://localhost:7000/'
+CAS_SERVICE = 'http://localhost:8000/accounts/login/'
+
diff --git a/platforma/manage.py b/platforma/manage.py
new file mode 100755 (executable)
index 0000000..8e180f0
--- /dev/null
@@ -0,0 +1,18 @@
+#!/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__":
+    # Append lib and apps directories to PYTHONPATH
+    from os import path
+    import sys
+    
+    PROJECT_ROOT = path.realpath(path.dirname(__file__))
+    sys.path += [PROJECT_ROOT + '/../apps', PROJECT_ROOT + '/../lib']
+    
+    execute_manager(settings)
diff --git a/platforma/settings.py b/platforma/settings.py
new file mode 100644 (file)
index 0000000..3a1f3ad
--- /dev/null
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+from os import path
+
+PROJECT_ROOT = path.realpath(path.dirname(__file__))
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    (u'Marek Stępniowski', 'marek@stepniowski.com'),
+    (u'Łukasz Rekucki', 'lrekucki@gmail.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = 'sqlite3'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+DATABASE_NAME = PROJECT_ROOT + '/dev.sqlite'             # Or path to database file if using sqlite3.
+DATABASE_USER = ''             # Not used with sqlite3.
+DATABASE_PASSWORD = ''         # Not used with sqlite3.
+DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
+DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Warsaw Poland'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'pl'
+
+#import locale
+#locale.setlocale(locale.LC_ALL, '')
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = PROJECT_ROOT + '/media/'
+STATIC_ROOT = PROJECT_ROOT + '/static/'
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = '/media/'
+STATIC_URL = '/static/'
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/admin-media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'ife@x^_lak+x84=lxtr!-ur$5g$+s6xt85gbbm@e_fk6q3r8=+'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+#     'django.template.loaders.eggs.load_template_source',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    "django.core.context_processors.auth",
+    "django.core.context_processors.debug",
+    "django.core.context_processors.i18n",
+    "explorer.context_processors.settings",
+    "django.core.context_processors.request",
+)
+
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'explorer.middleware.EditorSettingsMiddleware',
+    'django.middleware.doc.XViewMiddleware',
+
+    'maintenancemode.middleware.MaintenanceModeMiddleware',
+)
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_DIRS = (
+    PROJECT_ROOT + '/templates',
+)
+
+# CSS and JS files to compress
+# COMPRESS_CSS = {
+#     'all': {
+#         'source_filenames': ('css/master.css', 'css/jquery.date_input.css', 'css/jquery.countdown.css',),
+#         'output_filename': 'css/all.min.css',
+#     }
+# }
+# 
+# COMPRESS_JS = {
+#     'all': {
+#         'source_filenames': ('js/jquery.js', 'js/jquery.date_input.js', 'js/jquery.date_input-pl.js',
+#             'js/jquery.countdown.js', 'js/jquery.countdown-pl.js',),
+#         'output_filename': 'js/all.min.js',
+#     }
+# }
+# 
+# COMPRESS_CSS_FILTERS = None
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'django.contrib.admindocs',
+
+    'piston',
+    'explorer',
+    'toolbar',
+    'api',
+    'wysiwyg',
+)
+
+
+# REPOSITORY_PATH = '/Users/zuber/Projekty/platforma/files/books'
+IMAGE_DIR = 'images'
+EDITOR_COOKIE_NAME = 'options'
+EDITOR_DEFAULT_SETTINGS = {
+    'panels': [
+        {'name': 'htmleditor', 'ratio': 0.5},
+        {'name': 'gallery', 'ratio': 0.5}
+    ],
+}
+
+# Python logging settings
+import logging
+
+log = logging.getLogger('platforma')
+log.setLevel(logging.DEBUG)
+ch = logging.StreamHandler()
+ch.setLevel(logging.DEBUG)
+formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+ch.setFormatter(formatter)
+log.addHandler(ch)
+
+
+# Import localsettings file, which may override settings defined here
+try:
+    from localsettings import *
+except ImportError:
+    pass
+
diff --git a/platforma/static/css/autumn.css b/platforma/static/css/autumn.css
new file mode 100644 (file)
index 0000000..dddd899
--- /dev/null
@@ -0,0 +1,49 @@
+/* 
+    Document   : autumn
+    Created on : 2009-10-01, 00:23:51
+    Author     : lreqc
+    Description:
+        Autumn colors for PR.
+*/
+
+body {
+    background-color: #C28800;
+}
+
+#header {
+    background-color: #E2CF62;
+    border-bottom-color: #860000;
+}
+
+.panel-main-toolbar {
+    background-color: #412F1D;    
+}
+
+.splitview-splitbar {
+    /* border-color: #412F1D; */
+    border: none;
+    background-color: #860000;
+}
+
+.image-gallery-header {
+    background-color: #E2CF62;
+}
+
+a:link, a:visited, a:active {
+    color: #860000;
+    text-decoration: none;
+    font-weight: bold;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+.toolbar-buttons-container {
+    background-color: #E2CF62;
+}
+
+.toolbar-tabs-container {
+    background-color: #860000;
+}
+
diff --git a/platforma/static/css/filelist.css b/platforma/static/css/filelist.css
new file mode 100755 (executable)
index 0000000..91e9b8d
--- /dev/null
@@ -0,0 +1,47 @@
+/* 
+    Document   : filelist
+    Created on : 2009-09-04, 20:44:44
+    Author     : lreqc
+    Description:
+        Dodatkowe style dla listy plików na stronie głównej.
+*/
+
+#main-page-widgets > div {
+    float: left;
+    border: 1px solid black;
+    padding: 0.5em 2em;
+    margin: 1em;    
+}
+
+.file-list-widget {
+    background: #DDF;
+    max-width: 60%;
+}
+
+.file-list-widget .page-nav-wrap button {
+    width: 2.5em;
+}
+
+.upload-file-widget {
+    min-width: 20%;
+    width: 35%;
+}
+
+ul.file-tree-part {    
+    margin: 0.5em 1em;
+    padding: 0em;
+}
+
+ul.file-tree-part li {
+    list-style: square;    
+    padding: 0em;    
+}
+
+ul.file-tree-part a {
+    padding: 0em;
+}
+
+
+
+
+
diff --git a/platforma/static/css/html.css b/platforma/static/css/html.css
new file mode 100644 (file)
index 0000000..336a365
--- /dev/null
@@ -0,0 +1,227 @@
+/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
+.htmlview {
+    font-size: 16px;
+    font: Georgia, "Times New Roman", serif;
+    line-height: 1.5em;
+    padding: 3em;    
+}
+
+.htmlview div {
+    max-width: 36em;
+}
+
+.htmlview #toc {
+    display: none;
+}
+
+.htmlview a {
+    color: blue;
+    text-decoration: none;
+}
+
+.htmlview h1 {
+    font-size: 3em;
+    margin: 1.5em 0;
+    text-align: center;
+    line-height: 1.5em;
+    font-weight: bold;
+}
+
+.htmlview h2 {
+    font-size: 2em;
+    margin: 1.5em 0 0;
+    font-weight: bold;
+    line-height: 1.5em;
+}
+
+.htmlview h3 {
+    font-size: 1.5em;
+    margin: 1.5em 0 0;
+    font-weight: normal;
+    line-height: 1.5em;
+}
+
+.htmlview h4 {
+    font-size: 1em;
+    margin: 1.5em 0 0;
+    line-height: 1.5em;
+}
+
+.htmlview p {
+    margin: 0;
+}
+
+/* ======================== */
+/* = Footnotes and themes = */
+/* ======================== */
+.htmlview .theme-begin {
+    border-left: 0.1em solid #DDDDDD;
+    color: #777;
+    padding: 0 0.5em;
+    width: 7.5em;
+    font-style: normal;
+    font-weight: normal;
+    font-size: 16px;
+    float: right;
+    margin-right: -9.5em;
+    clear: both;
+    left: 40em;
+    line-height: 1.5em;
+    text-align: left;
+}
+
+.htmlview .annotation {
+    font-style: normal;
+    font-weight: normal;
+    font-size: 12px;
+}
+
+.htmlview #footnotes .annotation {
+    display: block;
+    float: left;
+    width: 2.5em;
+    clear: both;
+}
+
+.htmlview #footnotes div {
+    margin: 1.5em 0 0 0;
+}
+
+.htmlview #footnotes p {
+    margin-left: 2.5em;
+    font-size: 0.875em;
+}
+
+.htmlview blockquote {
+    font-size: 0.875em;
+}
+
+/* ============= */
+/* = Numbering = */
+/* ============= */
+.htmlview .anchor {
+    position: relative;
+    margin: 0em;
+    left: -2.2em;
+    color: #777;
+    font-size: 12px;
+    width: 2em;
+    text-align: center;
+    padding: 0.25em 0.7em;
+    line-height: 1.5em;
+}
+
+.htmlview .anchor:hover, .htmlview .anchor:active {
+    color: #FFF;
+    background-color: #CCC;
+}
+
+/* =================== */
+/* = Custom elements = */
+/* =================== */
+.htmlview .autor_utwor {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: 0.25em;
+}
+
+.htmlview .dzielo_nadrzedne {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: -0.25em;
+}
+
+.htmlview .podtytul {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: -0.25em;
+}
+
+.htmlview .didaskalia {
+    font-style: italic;
+    margin: 0.5em 0 0 1.5em;
+}
+
+.htmlview .kwestia {
+    margin: 0.5em 0 0;
+}
+
+.htmlview .strofa {
+    margin: 1.5em 0 0;
+}
+
+.htmlview .kwestia .strofa {
+    margin: 0;
+}
+
+.htmlview .akap, .htmlview .akap_cd, .htmlview .akap_dialog {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+.htmlview p.motto {
+    text-align: justify;
+    font-style: italic;
+    margin: 1.5em 0 0;
+}
+
+.htmlview p.motto_podpis {
+    font-size: 0.875em;
+    text-align: right;
+}
+
+.htmlview div.fragment {
+    border-bottom: 0.1em solid #999;
+    padding-bottom: 1.5em;
+}
+
+.htmlview div.nota p,
+.htmlview div.dedykacja p {
+    text-align: right;
+    font-style: italic;
+}
+
+.htmlview br.sekcja_swiatlo {
+    height: 3em;
+    /* visibility: hidden; */
+}
+
+.htmlview hr.separator_linia {
+    margin: 1.5em 0;
+    border: none;
+    border-bottom: 0.1em solid #000;
+}
+
+.htmlview p.sekcja_asterysk {
+    padding: 0;
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+.htmlview div.lista_osob ol {
+    list-style: none;
+    padding: 0 0 0 1.5em;
+}
+
+.htmlview p.miejsce_czas {
+    font-style: italic;
+}
+
+.htmlview .mat,
+.htmlview .slowo_obce,
+.htmlview .tytul_dziela,
+.htmlview .didaskalia {
+    font-style: italic;
+}
+
+.htmlview .wyroznienie {
+    letter-spacing: 0.1em;
+}
+
+.htmlview .osoba {
+    font-style: normal;
+    font-variant: small-caps;
+}
diff --git a/platforma/static/css/html_print.css b/platforma/static/css/html_print.css
new file mode 100644 (file)
index 0000000..38cb596
--- /dev/null
@@ -0,0 +1,229 @@
+/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
+.htmlview {
+    font-size: 16px;
+    font: Georgia, "Times New Roman", serif;
+    line-height: 1.5em;
+    padding: 3em;    
+}
+
+.htmlview div {
+    max-width: 36em;
+}
+
+.htmlview #toc {
+    display: none;
+}
+
+.htmlview a {
+    color: blue;
+    text-decoration: none;
+}
+
+.htmlview h1 {
+    font-size: 3em;
+    margin: 1.5em 0;
+    text-align: center;
+    line-height: 1.5em;
+    font-weight: bold;
+}
+
+.htmlview h2 {
+    font-size: 2em;
+    margin: 1.5em 0 0;
+    font-weight: bold;
+    line-height: 1.5em;
+}
+
+.htmlview h3 {
+    font-size: 1.5em;
+    margin: 1.5em 0 0;
+    font-weight: normal;
+    line-height: 1.5em;
+}
+
+.htmlview h4 {
+    font-size: 1em;
+    margin: 1.5em 0 0;
+    line-height: 1.5em;
+}
+
+.htmlview p {
+    margin: 0;
+}
+
+/* ======================== */
+/* = Footnotes and themes = */
+/* ======================== */
+.htmlview .theme-begin {
+    border-left: 0.1em solid #DDDDDD;
+    color: #777;
+    padding: 0 0.5em;
+    width: 7.5em;
+    font-style: normal;
+    font-weight: normal;
+    font-size: 16px;
+    float: right;
+    margin-right: -9.5em;
+    clear: both;
+    left: 40em;
+    line-height: 1.5em;
+    text-align: left;
+}
+
+.htmlview .annotation {
+    font-style: normal;
+    font-weight: normal;
+    font-size: 12px;
+}
+
+.htmlview #footnotes .annotation {
+    display: block;
+    float: left;
+    width: 2.5em;
+    clear: both;
+}
+
+.htmlview #footnotes div {
+    margin: 1.5em 0 0 0;
+}
+
+.htmlview #footnotes p {
+    margin-left: 2.5em;
+    font-size: 0.875em;
+}
+
+.htmlview blockquote {
+    font-size: 0.875em;
+}
+
+/* ============= */
+/* = Numbering = */
+/* ============= */
+.htmlview p {
+    position: relative;
+}
+
+.htmlview .anchor {
+    position: absolute;
+    margin: 0em;
+    left: -3em;
+    color: #777;
+    font-size: 12px;
+    width: 2em;
+    text-align: center;
+    padding: 0.25em 0.5em;
+    line-height: 1.5em;
+}
+
+.htmlview .anchor:hover, .htmlview .anchor:active {
+    color: #FFF;
+    background-color: #CCC;
+}
+
+/* =================== */
+/* = Custom elements = */
+/* =================== */
+.htmlview span.author {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: 0.25em;
+}
+
+.htmlview span.collection {
+    font-size: 0.375em;
+    display: block;
+    line-height: 1.5em;
+    margin-bottom: -0.25em;
+}
+
+.htmlview span.subtitle {
+    font-size: 0.5em;
+    display: block;
+    line-height: 1.5em;
+    margin-top: -0.25em;
+}
+
+.htmlview div.didaskalia {
+    font-style: italic;
+    margin: 0.5em 0 0 1.5em;
+}
+
+.htmlview div.kwestia {
+    margin: 0.5em 0 0;
+}
+
+.htmlview div.stanza {
+    margin: 1.5em 0 0;
+}
+
+.htmlview div.kwestia div.stanza {
+    margin: 0;
+}
+
+.htmlview p.paragraph {
+    text-align: justify;
+    margin: 1.5em 0 0;
+}
+
+.htmlview p.motto {
+    text-align: justify;
+    font-style: italic;
+    margin: 1.5em 0 0;
+}
+
+.htmlview p.motto_podpis {
+    font-size: 0.875em;
+    text-align: right;
+}
+
+.htmlview div.fragment {
+    border-bottom: 0.1em solid #999;
+    padding-bottom: 1.5em;
+}
+
+.htmlview div.note p, .htmlview div.dedication p,
+.htmlview div.note p.paragraph, .htmlview div.dedication p.paragraph {
+    text-align: right;
+    font-style: italic;
+}
+
+.htmlview hr.spacer {
+    height: 3em;
+    visibility: hidden;
+}
+
+.htmlview hr.spacer-line {
+    margin: 1.5em 0;
+    border: none;
+    border-bottom: 0.1em solid #000;
+}
+
+.htmlview p.spacer-asterisk {
+    padding: 0;
+    margin: 1.5em 0;
+    text-align: center;
+}
+
+.htmlview div.person-list ol {
+    list-style: none;
+    padding: 0 0 0 1.5em;
+}
+
+.htmlview p.place-and-time {
+    font-style: italic;
+}
+
+.htmlview em.math, .htmlview em.foreign-word,
+.htmlview em.book-title, .htmlview em.didaskalia {
+    font-style: italic;
+}
+
+.htmlview em.author-emphasis {
+    letter-spacing: 0.1em;
+}
+
+.htmlview em.person {
+    font-style: normal;
+    font-variant: small-caps;
+}
diff --git a/platforma/static/css/jquery.modal.css b/platforma/static/css/jquery.modal.css
new file mode 100755 (executable)
index 0000000..4717ad4
--- /dev/null
@@ -0,0 +1,28 @@
+/* jqModal base Styling courtesy of;\r
+       Brice Burgess <bhb@iceburg.net> */\r
+\r
+/* The Window's CSS z-index value is respected (takes priority). If none is supplied,\r
+       the Window's z-index value will be set to 3000 by default (via jqModal.js). */\r
+       \r
+.jqmWindow {\r
+    display: none;\r
+    \r
+    position: absolute;\r
+    top: 40px;\r
+    left: 25%;\r
+    bottom: auto;\r
+    height: auto;\r
+    width: auto;\r
+\r
+    max-width: 80%;\r
+    max-height: 80%;\r
+\r
+    overflow: auto;\r
+    \r
+    background-color: #EEE;\r
+    color: #333;\r
+    border: 1px solid black;\r
+    padding: 1em;\r
+}\r
+\r
+.jqmOverlay { background-color: #000; }\r
diff --git a/platforma/static/css/managment.css b/platforma/static/css/managment.css
new file mode 100644 (file)
index 0000000..3777bcf
--- /dev/null
@@ -0,0 +1,36 @@
+table.request-report 
+{
+    border: 2px groove black;
+    font-size: 12pt;
+
+    margin-top: 3em;
+    margin-bottom: 2em;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.request-report td, .request-report th  {
+    vertical-align: top;
+    border-right: 1px solid black;
+    border-bottom: 1px solid black;
+
+    padding: 0.4em 1em;
+    margin: 0em;
+}
+
+.request-report th {
+    background-color: black;
+    color: white;
+}
+
+.request-report .status-N {
+    background-color: teal;
+}
+
+.request-report .status-R {
+    background-color: red;
+}
+
+.request-report .status-A {
+    background-color: gray;
+}
\ No newline at end of file
diff --git a/platforma/static/css/master.css b/platforma/static/css/master.css
new file mode 100644 (file)
index 0000000..cebb0dc
--- /dev/null
@@ -0,0 +1,336 @@
+body {
+    margin: 0;    
+
+    font: 12px Helvetica, Verdana, sans-serif;
+    overflow: hidden;
+    background: #AAA;
+}
+
+/*  fix icon buttons */
+
+button img {
+    vertical-align: middle;
+    margin: 0px;
+}
+
+/* default form style hacks */
+select {
+    border: none;
+    margin-left: 0.1em;f
+}
+
+#body-wrap {
+    margin: 0px;
+    padding: 0px;
+    position: fixed;
+    left: 0px;
+    right: 0px;
+    bottom: 0px;
+    top: 0px;
+}
+
+#header { 
+    position: absolute;
+    padding: 2px 0.5em;
+    background-color: #CDCDCD; /* !unused */
+    border-bottom: 2px solid black;
+
+    font-size: 14px;
+
+    line-height: 26px;
+    vertical-align: middle;
+
+    /* height: 30px; */
+    top: 0px; left: 0px; right: 0px; 
+    z-index: 300;
+}
+
+#content {
+    position: absolute;
+    top: 32px; left: 0px; right: 0px; bottom: 0px;
+    overflow: auto;
+    background-color: white;
+}
+
+#header #breadcrumbs {
+}
+
+#header-right-toolbar {
+    position: absolute;
+    right: 1em;
+}
+
+#header button {
+    vertical-align: middle;
+}
+
+/* Commit dialog */
+#commit-dialog-error-empty-message {
+    color: red;
+    display: none;
+    font-weight: bold;
+}
+
+text#commit-dialog-message {
+    width: 80%;
+    margin: auto;
+}
+
+#split-dialog .container-box form {
+    margin: 0.2em 1em;
+}
+
+#split-dialog .container-box fieldset {
+    margin: 0.5em;
+}
+
+
+
+
+/* ======= */
+/* = New = */
+/* ======= */
+#splitview {
+    width: 100%;
+    height: 100%;
+    padding: 0;
+    margin: 0;
+}
+
+.splitview-splitbar {
+    width: 5px;
+    border-left: 1px solid #999;
+    border-right: 1px solid #999;
+    height: 100%;
+    background-color: #CCC;
+    z-index: 100;
+}
+
+.splitview-overlay {
+    z-index: 90;
+    background: #FFF;
+    opacity: 0.5;
+}
+
+.panel-container {
+    height: 100%;
+    position: relative;
+}
+
+.content-view {
+    position: absolute;
+    top: 25px;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    overflow: hidden;
+}
+
+.panel-main-toolbar {
+    z-index: 1100;    
+    position: absolute;
+
+    top: 0px;
+    right: 0px;
+    left: 0px;
+    height: 24px;
+
+    border-bottom: 1px solid black;
+    background: gray;
+
+    padding: 0px;
+}
+
+.panel-main-toolbar p {
+    margin: 0px;
+    padding: 2px;
+    line-height: 20px;
+    font-size: 13px;    
+}
+
+.xmlview {
+    height: 100%;
+}
+
+.view-overlay {
+    z-index: 1000;
+    background: #FFF;
+    opacity: 0.8;
+    text-align: center;
+    vertical-align: middle;
+
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+
+    user-select: 'none';
+    -webkit-user-select: 'none';
+    -khtml-user-select: 'none';
+    -moz-user-select: 'none';
+    overflow: 'hidden';    
+}
+
+.view-overlay div {
+    position: absolute;
+}
+
+/* .buttontoolbarview {
+    display: block;
+    background-color: #CCC;
+}
+
+.buttontoolbarview a {
+    color: #000;
+    text-decoration: none;
+} */ /* Similar classes already exist in toolbar.css */
+
+
+/* ================= */
+/* = Gallery panel = */
+/* ================= */
+.image-gallery-view-template {
+    position: absolute;
+    top: 0px; left: 0px; right: 0px; bottom: 0px;
+    overflow: hidden;
+}
+
+.image-gallery-header {
+    position: absolute;
+    bottom: 0px;
+    left: 0px;
+    right: 0px;
+    height: 30px;
+
+    background: gray;
+    border-top: 1px solid #780000;
+    z-index: 100;
+}
+
+input.image-gallery-current-page {
+    text-align: center;
+}
+
+.image-gallery-header p {
+    margin: 0px;
+    padding: 3px 1em;
+    height: 30px;
+    line-height: 24px;
+    text-align: center;
+    white-space: nowrap;
+}
+
+.image-gallery-page-list {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    right: 0px;
+    bottom: 31px;
+    background: black;
+    z-index: 0;
+
+    overflow: hidden;
+}
+
+.image-gallery-page-container {
+    display: none;
+    border: none;
+
+    position: absolute;
+    top: 0px; left: 0px;
+
+    text-align: center;
+    padding: 0px;
+}
+
+.htmlview {
+    position: absolute;
+    top: 25px;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    overflow: auto;
+    margin: 0;
+}
+
+.image-gallery-page-container img {
+    /* border: 2px solid green; */
+    margin: 0px;
+}
+
+body#base button {
+    background-color: #DDD;
+    border-width: 1px;
+    padding: 0px 0.5em;    
+    font-family: Sans-Serif;
+    /* color: #000; */
+    margin: 2px 4px;
+}
+
+body#base button:hover {
+    background-color: #EEE;
+}
+
+/* HTML editor interactive elements */
+
+.html-editarea {
+    border: 2px solid black;
+    background-color: gray;
+    padding: 1px;
+
+    z-index: 2000;
+}
+
+.html-editarea textarea
+{   
+   
+    border: 0px;
+    margin: 0px;    
+    padding: 0px;
+    
+    width: 100%;
+    height: 100%;
+    
+    z-index: 0;
+    font-size: 10pt;
+    background-color: ivory;
+}
+
+.html-editarea p.html-editarea-toolbar {
+    position: absolute;
+    background: gray;
+
+    bottom: -26px;
+    height: 24px;
+    
+    left: 0px;
+    right: 0px;    
+        
+    border: 2px solid black;
+
+    margin: 0px;
+    padding: 0px;
+
+    z-index: 100;
+}
+
+/* ================= */
+/* = Message boxes = */
+/* ================= */
+.info {
+    background-color: gray;
+}
+
+.success {
+    background-color: green;
+}
+
+.error {
+    background-color: yellow;
+}
+
+.critical {
+    background-color: red;
+}
+
diff --git a/platforma/static/css/toolbar.css b/platforma/static/css/toolbar.css
new file mode 100644 (file)
index 0000000..b5df66f
--- /dev/null
@@ -0,0 +1,5 @@
+.toolbar-buttons-container {    
+    padding-top: 2px;
+    padding-bottom: 2px;
+}
+
diff --git a/platforma/static/css/xmlcolors.css b/platforma/static/css/xmlcolors.css
new file mode 100644 (file)
index 0000000..aa26579
--- /dev/null
@@ -0,0 +1,51 @@
+.editbox {
+  margin: .4em;
+  padding: 0;
+  font-family: monospace;
+  font-size: 10pt;
+  color: black;
+}
+
+.editbox p {
+  margin: 0;
+}
+
+span.xml-tagname {
+  color: #A0B;
+}
+
+span.xml-attribute {
+  color: #281;
+}
+
+span.xml-punctuation {
+  color: black;
+}
+
+span.xml-attname {
+  color: #00F;
+}
+
+span.xml-comment {
+  color: #A70;
+}
+
+span.xml-cdata {
+  color: #48A;
+}
+
+span.xml-processing {
+  color: #999;
+}
+
+span.xml-entity {
+  color: #A22;
+}
+
+span.xml-error {
+  color: #F00;
+}
+
+span.xml-text {
+  color: black;
+}
diff --git a/platforma/static/icons/go-next.png b/platforma/static/icons/go-next.png
new file mode 100644 (file)
index 0000000..6ef8de7
Binary files /dev/null and b/platforma/static/icons/go-next.png differ
diff --git a/platforma/static/icons/go-previous.png b/platforma/static/icons/go-previous.png
new file mode 100644 (file)
index 0000000..659cd90
Binary files /dev/null and b/platforma/static/icons/go-previous.png differ
diff --git a/platforma/static/icons/zoom.png b/platforma/static/icons/zoom.png
new file mode 100644 (file)
index 0000000..6033b4d
Binary files /dev/null and b/platforma/static/icons/zoom.png differ
diff --git a/platforma/static/icons/zoom_in.png b/platforma/static/icons/zoom_in.png
new file mode 100644 (file)
index 0000000..c7feedc
Binary files /dev/null and b/platforma/static/icons/zoom_in.png differ
diff --git a/platforma/static/icons/zoom_out.png b/platforma/static/icons/zoom_out.png
new file mode 100644 (file)
index 0000000..fdd7124
Binary files /dev/null and b/platforma/static/icons/zoom_out.png differ
diff --git a/platforma/static/img/spinner.gif b/platforma/static/img/spinner.gif
new file mode 100644 (file)
index 0000000..f864d5f
Binary files /dev/null and b/platforma/static/img/spinner.gif differ
diff --git a/platforma/static/js/app.js b/platforma/static/js/app.js
new file mode 100644 (file)
index 0000000..e8b439d
--- /dev/null
@@ -0,0 +1,207 @@
+/*global Class*/
+var editor;
+var panel_hooks;
+
+
+// prevent a console.log from blowing things up if we are on a browser that
+// does not support it
+if (typeof console === 'undefined') {
+  window.console = {} ;
+  console.log = console.info = console.warn = console.error = function(){};
+}
+
+
+(function(){
+  // Classes
+  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+  this.Class = function(){};
+  Class.extend = function(prop) {
+    var _super = this.prototype;
+    initializing = true;
+    var prototype = new this();
+    initializing = false;
+    for (var name in prop) {
+      prototype[name] = typeof prop[name] == "function" &&
+        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+        (function(name, fn){
+          return function() {
+            var tmp = this._super;
+            this._super = _super[name];
+            var ret = fn.apply(this, arguments);       
+            this._super = tmp;           
+            return ret;
+          };
+        })(name, prop[name]) :
+        prop[name];
+    }   
+    function Class() {
+      if ( !initializing && this.init )
+        this.init.apply(this, arguments);
+    }
+    Class.prototype = prototype;
+    Class.constructor = Class;
+    Class.extend = arguments.callee;   
+    return Class;
+  };
+  
+  // Templates
+  var cache = {};
+
+  this.render_template = function render_template(str, data){
+    // Figure out if we're getting a template, or if we need to
+    // load the template - and be sure to cache the result.    
+    var fn = !/^[\d\s-_]/.test(str) ?
+      cache[str] = cache[str] ||
+        render_template(document.getElementById(str).innerHTML) :
+
+      // Generate a reusable function that will serve as a template
+      // generator (and which will be cached).
+      
+      new Function("obj",
+        "var p=[],print=function(){p.push.apply(p,arguments);};" +
+
+        // Introduce the data as local variables using with(){}
+        "with(obj){p.push('" +
+
+        // Convert the template into pure JavaScript       
+        str
+          .replace(/[\r\t\n]/g, " ")
+          .split("<%").join("\t")
+          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
+          .replace(/\t=(.*?)%>/g, "',$1,'")
+          .split("\t").join("');")
+          .split("%>").join("p.push('")
+          .split("\r").join("\\'")
+      + "');}return p.join('');");
+
+      // Provide some basic currying to the user
+    return data ? fn( data ) : fn;
+  };
+})();
+
+
+(function() {
+  var slice = Array.prototype.slice;
+  
+  function update(array, args) {
+    var arrayLength = array.length, length = args.length;
+    while (length--) array[arrayLength + length] = args[length];
+    return array;
+  };
+  
+  function merge(array, args) {
+    array = slice.call(array, 0);
+    return update(array, args);
+  };
+  
+  Function.prototype.bind = function(context) {
+    if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
+      return this;
+    } 
+    var __method = this;
+    var args = slice.call(arguments, 1);
+    return function() {
+      var a = merge(args, arguments);
+      return __method.apply(context, a);
+    }
+  }
+  
+})();
+
+
+var Editor = Editor || {};
+
+// Obiekt implementujący wzorzec KVC/KVO
+Editor.Object = Class.extend({
+  _className: 'Editor.Object',
+  _observers: {},
+  _guid: null,
+  
+  init: function() {
+    this._observers = {};
+  },
+  
+  description: function() {
+    return this._className + '(guid = ' + this.guid() + ')';
+  },
+  
+  addObserver: function(observer, property, callback) {
+    // console.log('Add observer', observer.description(), 'to', this.description(), '[', property, ']');
+    if (!this._observers[property]) {
+      this._observers[property] = {}
+    }
+    this._observers[property][observer.guid()] = callback;
+    return this;
+  },
+  
+  removeObserver: function(observer, property) {
+    if (!property) {
+      for (var property in this._observers) {
+        this.removeObserver(observer, property)
+      }
+    } else {
+      // console.log('Remove observer', observer.description(), 'from', this.description(), '[', property, ']');
+      delete this._observers[property][observer.guid()];
+    }
+    return this;
+  },
+  
+  notifyObservers: function(property) {
+    var currentValue = this[property];
+    for (var guid in this._observers[property]) {
+      // console.log(this._observers[property][guid]);
+      // console.log('Notifying', guid, 'of', this.description(), '[', property, ']');
+      this._observers[property][guid](property, currentValue, this);
+    }
+    return this;
+  },
+  
+  guid: function() {
+    if (!this._guid) {
+      this._guid = ('editor-' + Editor.Object._lastGuid++);
+    }
+    return this._guid;
+  },
+  
+  get: function(property) {
+    return this[property];
+  },
+  
+  set: function(property, value) {
+    if (this[property] != value) {
+      this[property] = value;
+      this.notifyObservers(property);
+    }
+    return this;
+  },
+  
+  dispose: function() {
+    delete this._observers;
+  }
+});
+
+// Handle JSON error responses in uniform way
+function parseXHRError(response)
+{
+    var message = ""
+    try {
+      var json = $.evalJSON(response.responseText);
+
+      if(json.reason == 'xml-parse-error') {
+          message = json_response.message.replace(/(line\s+)(\d+)(\s+)/i,
+            "<a class='xml-editor-ref' href='#xml-$2-1'>$1$2$3</a>");
+
+          message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i,
+            "<a class='xml-editor-ref' href='#xml-$2-$4'>$1$2$3$4</a>");
+      }
+      message = json_response.message || json_response.reason || "Nieznany błąd :((";
+    } catch(e) {
+        // not a valid JSON response
+        message = response.statusText;
+    }
+    return message;
+}
+
+Editor.Object._lastGuid = 0;
+
+var panels = [];
\ No newline at end of file
diff --git a/platforma/static/js/button_scripts.js b/platforma/static/js/button_scripts.js
new file mode 100644 (file)
index 0000000..40f45f6
--- /dev/null
@@ -0,0 +1,210 @@
+function ScriptletCenter()
+{
+    this.scriptlets = {};
+
+    this.scriptlets['insert_tag'] = function(context, params)
+    {
+        var text = this.XMLEditorSelectedText(context);
+        var start_tag = '<'+params.tag;
+
+        for (var attr in params.attrs) {
+            start_tag += ' '+attr+'="' + params.attrs[attr] + '"';
+        }
+
+        start_tag += '>';
+        var end_tag = '</'+params.tag+'>';
+
+        if(text.length > 0) {
+            // tokenize
+            var output = '';
+            var token = '';
+            for(var index=0; index < text.length; index++)
+            {
+                if (text[index].match(/\s/)) { // whitespace
+                    token += text[index];
+                }
+                else { // character
+                    output += token;
+                    if(output == token) output += start_tag;
+                    token = '';
+                    output += text[index];
+                }
+            }
+
+            if( output[output.length-1] == '\\' ) {
+                output = output.substr(0, output.length-1) + end_tag + '\\';
+            } else {
+                output += end_tag;
+            }
+            output += token;
+        }
+        else {
+            output = start_tag + end_tag;
+        }
+
+        this.XMLEditorReplaceSelectedText(context, output);
+
+        if (text.length == 0) {
+            this.XMLEditorMoveCursorForward(context, -params.tag.length-3);
+        }        
+    }.bind(this);
+
+    this.scriptlets['lineregexp'] = function(context, params) {
+
+        var exprs = $.map(params.exprs, function(expr) {
+            var opts = "g";
+            if(expr.length > 2) {
+                opts = expr[2];
+            } return {
+                rx: new RegExp(expr[0], opts),
+                repl: expr[1]
+                };
+        });
+
+        var partial = true;
+        var text = this.XMLEditorSelectedText(context);
+        if(!text) return;
+
+        var changed = 0;
+        var lines = text.split('\n');
+        lines = $.map(lines, function(line) {
+            var old_line = line;
+            $(exprs).each(function() {
+                var expr = this;
+                line = line.replace(expr.rx, expr.repl);
+            });
+
+            if(old_line != line) changed += 1;
+            return line;
+        });
+
+        if(changed > 0) {
+            this.XMLEditorReplaceSelectedText(context, lines.join('\n') );
+        }
+    }.bind(this);
+
+    this.scriptlets['codemirror_fontsize'] = function(context, params) {
+        var frameBody = this.XMLEditorBody(context);
+
+        if(params.fontSize) {
+            frameBody.css('font-size', params.fontSize);
+        }
+        else {
+            var old_size = parseInt(frameBody.css('font-size'), 10);
+            frameBody.css('font-size', old_size + (params.change || 0) );
+        }
+        
+    }.bind(this);
+
+    this.scriptlets['fulltextregexp'] = function(context, params) {
+        var exprs = $.map(params.exprs, function(expr) {
+            var opts = "mg";
+            if(expr.length > 2) {
+                opts = expr[2];
+            }
+            return {
+                rx: new RegExp(expr[0], opts),
+                repl: expr[1]
+                };
+        });
+
+        var text = this.XMLEditorSelectedText(context);
+        if(!text) return;
+        var original = text;
+        $(exprs).each(function() {
+            text = text.replace(this.rx, this.repl);
+        });
+
+        if( original != text) {
+            this.XMLEditorReplaceSelectedText(context, text);
+        }
+    }.bind(this);
+
+    this.scriptlets['macro'] = function(context, params) {
+        var self = this;
+
+        $(params).each(function() {
+            $.log(this[0], this[1]);
+            self.scriptlets[this[0]](context, this[1]);
+        });
+    }.bind(this);
+
+    this.scriptlets['lowercase'] = function(context, params)
+    {
+        var text = this.XMLEditorSelectedText(context);
+
+        if(!text) return;
+
+        var repl = '';
+        var lcase = text.toLowerCase();
+        var ucase = text.toUpperCase();
+
+        if(lcase == text) repl = ucase; /* was lowercase */
+        else if(ucase != text) repl = lcase; /* neither lower- or upper-case */
+        else { /* upper case -> camel-case */
+            var words = $(lcase.split(/\s/)).map(function() {
+                if(this.length > 0) { 
+                    return this[0].toUpperCase() + this.slice(1);
+                } else {
+                    return '';
+                }
+            });
+            repl = words.join(' ');
+        }
+
+        if(repl != text) this.XMLEditorReplaceSelectedText(context, repl);
+    }.bind(this);
+
+
+    this.scriptlets["insert_stanza"] = function(context, params) {
+        var text = this.XMLEditorSelectedText(context);
+
+        if(text) {
+            var verses = text.split('\n');
+            text = ''; var buf = ''; var ebuf = '';
+            var first = true;
+
+            for(var i=0;  i < verses.length; i++) {
+                var verse = verses[i].replace(/^\s+/, "").replace(/\s+$/, "");
+                if(verse) {
+                    text += (buf ? buf + '/\n' : '') + ebuf;
+                    buf = (first ? '<strofa>\n' : '') + verses[i];
+                    ebuf = '';
+                    first = false;
+                } else {
+                    ebuf += '\n' + verses[i];
+                }
+            }
+            text = text + buf + '\n</strofa>' + ebuf;
+            this.XMLEditorReplaceSelectedText(context, text);
+        }
+
+        if (!text) {
+            this.XMLEditorMoveCursorForward(context, params.tag.length + 2);
+        }
+        
+    }.bind(this);
+
+}
+
+ScriptletCenter.prototype.XMLEditorSelectedText = function(panel) {
+    return panel.contentView.editor.selection();
+};
+
+ScriptletCenter.prototype.XMLEditorReplaceSelectedText = function(panel, replacement)
+{
+    panel.contentView.editor.replaceSelection(replacement);
+    // Tell XML view that it's data has changed
+    panel.contentView.editorDataChanged();
+};
+
+ScriptletCenter.prototype.XMLEditorMoveCursorForward = function(panel, n) {
+    var pos = panel.contentView.editor.cursorPosition();
+    panel.contentView.editor.selectLines(pos.line, pos.character + n);
+};
+
+var scriptletCenter;
+
+$(function() {
+    scriptletCenter = new ScriptletCenter();
+});
\ No newline at end of file
diff --git a/platforma/static/js/editor.js b/platforma/static/js/editor.js
new file mode 100644 (file)
index 0000000..f52950f
--- /dev/null
@@ -0,0 +1,578 @@
+var editor;
+var panel_hooks;
+
+function Hotkey(code) {
+    this.code = code;
+    this.has_alt = ((code & 0x01 << 8) !== 0);
+    this.has_ctrl = ((code & 0x01 << 9) !== 0);
+    this.has_shift = ((code & 0x01 << 10) !== 0);
+    this.character = String.fromCharCode(code & 0xff);
+}
+
+Hotkey.prototype.toString = function() {
+    var mods = [];
+    if(this.has_alt) mods.push('Alt');
+    if(this.has_ctrl) mods.push('Ctrl');
+    if(this.has_shift) mods.push('Shift');
+    mods.push('"'+this.character+'"');
+    return mods.join('+');
+};
+
+function Panel(panelWrap) {
+    var self = this;
+    self.hotkeys = [];
+    self.wrap = panelWrap;
+    self.contentDiv = $('.panel-content', panelWrap);
+    self.instanceId = Math.ceil(Math.random() * 1000000000);
+    // $.log('new panel - wrap: ', self.wrap);
+       
+    $(document).bind('panel:unload.' + self.instanceId,
+        function(event, data) {
+            self.unload(event, data);
+        });
+
+    $(document).bind('panel:contentChanged', function(event, data) {
+        $.log(self, ' got changed event from: ', data);
+        if(self != data) {
+            self.otherPanelChanged(event.target);
+        } else {
+            self.markChanged();
+        }
+        return false;
+    });
+}
+
+Panel.prototype.callHook = function() {
+    var args = $.makeArray(arguments);
+    var hookName = args.splice(0,1)[0];
+    var noHookAction = args.splice(0,1)[0];
+    var result = false;
+
+    $.log('calling hook: ', hookName, 'with args: ', args);
+    if(this.hooks && this.hooks[hookName]) {
+        result = this.hooks[hookName].apply(this, args);
+    } else if (noHookAction instanceof Function) {
+        result = noHookAction(args);
+    }
+    return result;
+};
+
+Panel.prototype._endload = function () {
+    // this needs to be here, so we
+    this.connectToolbar();
+    this.callHook('toolbarResized');
+};  
+
+Panel.prototype.load = function (url) {
+    // $.log('preparing xhr load: ', this.wrap);
+    $(document).trigger('panel:unload', this);
+    var self = this;
+    self.current_url = url;
+
+    $.ajax({
+        url: url,
+        dataType: 'html',
+        success: function(data, tstat) {
+            panel_hooks = null;
+            $(self.contentDiv).html(data);
+            self.hooks = panel_hooks;
+            panel_hooks = null;            
+            self.callHook('load');           
+        },
+        error: function(request, textStatus, errorThrown) {
+            $.log('ajax', url, this.target, 'error:', textStatus, errorThrown);
+            $(self.contentDiv).html("<p>Wystapił błąd podczas wczytywania panelu.</p>");
+        }
+    });
+};
+
+Panel.prototype.unload = function(event, data) {
+    // $.log('got unload signal', this, ' target: ', data);
+    if( data == this ) {        
+        $(this.contentDiv).html('');
+
+        // disconnect the toolbar
+        $('div.panel-toolbar span.panel-toolbar-extra', this.wrap).html(
+            '<span />');
+        
+        this.callHook('unload');
+        this.hooks = null; // flush the hooks
+        return false;
+    }
+};
+
+Panel.prototype.refresh = function(event, data) {
+    var self = this;
+    var reload = function() {
+        $.log('hard reload for panel ', self.current_url);
+        self.load(self.current_url);
+        return true;
+    };
+
+    if( this.callHook('refresh', reload) ) {
+        $('.change-notification', this.wrap).fadeOut();
+    }
+}; 
+
+Panel.prototype.otherPanelChanged = function(other) {
+    $.log('Panel ', this, ' is aware that ', other, ' changed.');
+    if(!this.callHook('dirty')) {
+        $('.change-notification', this.wrap).fadeIn();
+    }
+};     
+
+Panel.prototype.markChanged = function () {
+    this.wrap.addClass('changed');
+};
+
+Panel.prototype.changed = function () {
+    return this.wrap.hasClass('changed');
+};
+
+Panel.prototype.unmarkChanged = function () {
+    this.wrap.removeClass('changed');
+};
+
+Panel.prototype.saveInfo = function() {
+    var saveInfo = {};
+    this.callHook('saveInfo', null, saveInfo);
+    return saveInfo;
+};
+
+Panel.prototype.connectToolbar = function()
+{
+    var self = this;
+    self.hotkeys = [];
+    
+    // check if there is a one
+    var toolbar = $("div.toolbar", this.contentDiv);
+    // $.log('Connecting toolbar', toolbar);
+    if(toolbar.length === 0) return;
+
+    // move the extra
+    var extra_buttons = $('span.panel-toolbar-extra button', toolbar);
+    var placeholder = $('div.panel-toolbar span.panel-toolbar-extra > span', this.wrap);
+    placeholder.replaceWith(extra_buttons);       
+
+    // connect group-switch buttons
+    var group_buttons = $('*.toolbar-tabs-container button', toolbar);
+
+    // $.log('Found groups:', group_buttons);
+
+    group_buttons.each(function() {
+        var group = $(this);
+        var group_name = group.attr('ui:group');
+        // $.log('Connecting group: ' + group_name);
+
+        group.click(function() {
+            // change the active group
+            var active = $("*.toolbar-tabs-container button.active", toolbar);
+            if (active != group) {
+                active.removeClass('active');                
+                group.addClass('active');
+                $(".toolbar-button-groups-container p", toolbar).each(function() {
+                    if ( $(this).attr('ui:group') != group_name) {
+                        $(this).hide();
+                    } else {
+                        $(this).show();
+                    }
+                });
+                self.callHook('toolbarResized');
+            }
+        });        
+    });
+
+    // connect action buttons
+    var allbuttons = $.makeArray(extra_buttons);
+    $.merge(allbuttons,
+        $.makeArray($('*.toolbar-button-groups-container button', toolbar)) );
+        
+    $(allbuttons).each(function() {
+        var button = $(this);
+        var hk = button.attr('ui:hotkey');
+        if(hk) hk = new Hotkey( parseInt(hk) );
+
+        try {
+            var params = $.evalJSON(button.attr('ui:action-params'));
+        } catch(object) {
+            $.log('JSON exception in ', button, ': ', object);
+            button.attr('disabled', 'disabled');
+            return;
+        }
+
+        var callback = function() {
+            editor.callScriptlet(button.attr('ui:action'), self, params);
+        };
+
+        // connect button
+        button.click(callback);
+       
+        // connect hotkey
+        if(hk) {
+            self.hotkeys[hk.code] = callback;
+        // $.log('hotkey', hk);
+        }
+        
+        // tooltip
+        if (button.attr('ui:tooltip') )
+        {
+            var tooltip = button.attr('ui:tooltip');
+            if(hk) tooltip += ' ['+hk+']';
+
+            button.wTooltip({
+                delay: 1000,
+                style: {
+                    border: "1px solid #7F7D67",
+                    opacity: 0.9,
+                    background: "#FBFBC6",
+                    padding: "1px",
+                    fontSize: "12px"
+                },
+                content: tooltip
+            });
+        }
+    });
+};
+
+Panel.prototype.hotkeyPressed = function(event)
+{
+    var code = event.keyCode;
+    if(event.altKey) code = code | 0x100;
+    if(event.ctrlKey) code = code | 0x200;
+    if(event.shiftKey) code = code | 0x400;
+
+    var callback = this.hotkeys[code];
+    if(callback) callback();
+};
+
+Panel.prototype.isHotkey = function(event) {
+    var code = event.keyCode;
+    if(event.altKey) code = code | 0x100;
+    if(event.ctrlKey) code = code | 0x200;
+    if(event.shiftKey) code = code | 0x400;
+
+    $.log(event.character, this.hotkeys[code]);
+
+    if(this.hotkeys[code]) {
+        return true;
+    }
+    return false;
+};
+
+Panel.prototype.fireEvent = function(name) {
+    $(document).trigger('panel:'+name, this);
+};
+
+function Editor()
+{
+    this.rootDiv = $('#panels');
+    this.popupQueue = [];
+    this.autosaveTimer = null;
+    this.scriplets = {};
+}
+
+Editor.prototype.loadConfig = function() {
+    // Load options from cookie
+    var defaultOptions = {
+        panels: [
+        {
+            name: 'htmleditor',
+            ratio: 0.5
+        },
+
+        {
+            name: 'gallery',
+            ratio: 0.5
+        }
+        ],
+        recentFiles: [],
+        lastUpdate: 0
+    };
+    
+    try {
+        var cookie = $.cookie('options');
+        this.options = $.secureEvalJSON(cookie);
+        if (!this.options) {
+            this.options = defaultOptions;
+        }
+    } catch (e) {    
+        this.options = defaultOptions;
+    }
+    
+    this.fileOptions = this.options;
+    var self = this;
+
+    if(!this.options.recentFiles)
+        this.options.recentFiles = [];
+
+    $.each(this.options.recentFiles, function(index) {
+        if (fileId == self.options.recentFiles[index].fileId) {
+            $.log('Found options for', fileId);
+            self.fileOptions = self.options.recentFiles[index];
+        }
+    });
+    
+    $.log(this.options);
+    $.log('fileOptions', this.fileOptions);
+    
+    this.loadPanelOptions();
+    this.savePanelOptions();
+};
+
+Editor.prototype.loadPanelOptions = function() {
+    // var self = this;
+    // var totalWidth = 0;
+    // 
+    // $('.panel-wrap', self.rootDiv).each(function(index) {
+    //     var panelWidth = self.fileOptions.panels[index].ratio * self.rootDiv.width();
+    //     if ($(this).hasClass('last-panel')) {
+    //         $(this).css({
+    //             left: totalWidth,
+    //             right: 0
+    //         });
+    //     } else {
+    //         $(this).css({
+    //             left: totalWidth,
+    //             width: panelWidth
+    //         });
+    //         totalWidth += panelWidth;               
+    //     }
+    //     $.log('panel:', this, $(this).css('left'));
+    //     $('.panel-toolbar option', this).each(function() {
+    //         if ($(this).attr('p:panel-name') == self.fileOptions.panels[index].name) {
+    //             $(this).parent('select').val($(this).attr('value'));
+    //         }
+    //     });
+    // });   
+};
+
+Editor.prototype.savePanelOptions = function() {
+    var self = this;
+    var panels = [];
+    $('.panel-wrap', self.rootDiv).not('.panel-content-overlay').each(function() {
+        panels.push({
+            name: $('.panel-toolbar option:selected', this).attr('p:panel-name'),
+            ratio: $(this).width() / self.rootDiv.width()
+        });
+    });
+    self.options.panels = panels;
+
+    // Dodaj obecnie oglądany plik do listy recentFiles
+    var recentFiles = [{fileId: fileId, panels: panels}];
+    var count = 1;
+    $.each(self.options.recentFiles, function(index) {
+        if (count < 5 && fileId != self.options.recentFiles[index].fileId) {
+            recentFiles.push(self.options.recentFiles[index]);
+            count++;
+        }
+    });
+    self.options.recentFiles = recentFiles;
+    
+    self.options.lastUpdate = new Date().getTime() / 1000;
+    $.log($.toJSON(self.options));    
+    $.cookie('options', $.toJSON(self.options), {
+        expires: 7,
+        path: '/'
+    });
+};
+
+Editor.prototype.saveToBranch = function(msg) 
+{
+    var changed_panel = $('.panel-wrap.changed');
+    var self = this;
+    $.log('Saving to local branch - panel:', changed_panel);
+
+    if(!msg) msg = "Szybki zapis z edytora platformy.";
+
+    if( changed_panel.length === 0) {
+        $.log('Nothing to save.');
+        return true; /* no changes */
+    }
+
+    if( changed_panel.length > 1) {
+        alert('Błąd: więcej niż jeden panel został zmodyfikowany. Nie można zapisać.');
+        return false;
+    }
+
+    var saveInfo = changed_panel.data('ctrl').saveInfo();
+    var postData = '';
+    
+    if (saveInfo.postData instanceof Object) {
+        postData = $.param(saveInfo.postData);
+    } else {
+        postData = saveInfo.postData;
+    }
+    
+    postData += '&' + $.param({
+        'commit_message': msg
+    });
+
+    self.showPopup('save-waiting', '', -1);
+
+    $.ajax({
+        url: saveInfo.url,
+        dataType: 'json',
+        success: function(data, textStatus) {
+            if (data.result != 'ok') {
+                self.showPopup('save-error', (data.errors && data.errors[0]) || 'Nieznany błąd X_X.');
+            }
+            else {
+                self.refreshPanels();
+
+
+                if(self.autosaveTimer) {
+                    clearTimeout(self.autosaveTimer);
+                }
+                if (data.warnings === null || data.warning === undefined) {
+                    self.showPopup('save-successful');
+                } else {
+                    self.showPopup('save-warn', data.warnings[0]);
+                }
+            }
+            
+            self.advancePopupQueue();
+        },
+        error: function(rq, tstat, err) {
+            self.showPopup('save-error', '- bład wewnętrzny serwera.');
+            self.advancePopupQueue();
+        },
+        type: 'POST',
+        data: postData
+    });
+
+    return true;
+};
+
+Editor.prototype.autoSave = function() 
+{
+    this.autosaveTimer = null;
+    // first check if there is anything to save
+    $.log('Autosave');
+    this.saveToBranch("Automatyczny zapis z edytora platformy.");
+};
+
+Editor.prototype.onContentChanged = function(event, data) {
+    var self = this;
+
+    $('button.provides-save').removeAttr('disabled');
+    $('button.requires-save').attr('disabled', 'disabled');
+    
+    if(this.autosaveTimer) return;
+    this.autosaveTimer = setTimeout( function() {
+        self.autoSave();
+    }, 300000 );
+};
+
+Editor.prototype.updateUserBranch = function() {
+    if($('.panel-wrap.changed').length !== 0) {
+        alert("There are unsaved changes - can't update.");
+    }
+
+    var self = this;
+    $.ajax({
+        url: $('#toolbar-button-update').attr('ui:ajax-action'),
+        dataType: 'json',
+        success: function(data, textStatus) {
+            switch(data.result) {
+                case 'done':
+                    self.showPopup('generic-yes', 'Plik uaktualniony.');
+                    self.refreshPanels();
+                    break;
+                case 'nothing-to-do':
+                    self.showPopup('generic-info', 'Brak zmian do uaktualnienia.');
+                    break;
+                default:
+                    self.showPopup('generic-error', data.errors && data.errors[0]);
+            }
+        },
+        error: function(rq, tstat, err) {
+            self.showPopup('generic-error', 'Błąd serwera: ' + err);
+        },
+        type: 'POST',
+        data: {}
+    });
+};
+
+Editor.prototype.sendMergeRequest = function (message) {
+    if( $('.panel-wrap.changed').length !== 0) {
+        alert("There are unsaved changes - can't commit.");
+    }
+
+    var self =  this;    
+        
+    $.ajax({        
+        url: $('#commit-dialog form').attr('action'),
+        dataType: 'json',
+        success: function(data, textStatus) {
+            switch(data.result) {
+                case 'done':
+                    self.showPopup('generic-yes', 'Łączenie zmian powiodło się.');
+
+                    if(data.localmodified) {
+                        self.refreshPanels();
+                    }
+                        
+                    break;
+                case 'nothing-to-do':
+                    self.showPopup('generic-info', 'Brak zmian do połaczenia.');
+                    break;
+                default:
+                    self.showPopup('generic-error', data.errors && data.errors[0]);
+            }
+        },
+        error: function(rq, tstat, err) {
+            self.showPopup('generic-error', 'Błąd serwera: ' + err);
+        },
+        type: 'POST',
+        data: {
+            'message': message
+        }
+    }); 
+};
+
+Editor.prototype.postSplitRequest = function(s, f)
+{
+    $.ajax({
+        url: $('#split-dialog form').attr('action'),
+        dataType: 'html',
+        success: s,
+        error: f,
+        type: 'POST',
+        data: $('#split-dialog form').serialize()
+    });
+};
+
+
+Editor.prototype.allPanels = function() {
+    return $('#' + this.rootDiv.attr('id') +' > *.panel-wrap', this.rootDiv.parent());
+};
+
+Editor.prototype.registerScriptlet = function(scriptlet_id, scriptlet_func)
+{
+    // I briefly assume, that it's verified not to break the world on SS
+    if (!this[scriptlet_id]) {
+        this[scriptlet_id] = scriptlet_func;
+    }
+};
+
+Editor.prototype.callScriptlet = function(scriptlet_id, panel, params) {
+    var func = this[scriptlet_id];
+    if(!func) {
+        throw 'No scriptlet named "' + scriptlet_id + '" found.';
+    }
+    return func(this, panel, params);
+};
+
+$(function() {
+    $.fbind = function (self, func) {
+        return function() { 
+            return func.apply(self, arguments);
+        };
+    };
+    
+    editor = new Editor();
+
+    // do the layout
+    editor.loadConfig();
+   editor.setupUI();
+});
diff --git a/platforma/static/js/lib/codemirror/codemirror.js b/platforma/static/js/lib/codemirror/codemirror.js
new file mode 100644 (file)
index 0000000..f63ed07
--- /dev/null
@@ -0,0 +1,309 @@
+/* CodeMirror main module
+ *
+ * Implements the CodeMirror constructor and prototype, which take care
+ * of initializing the editor frame, and providing the outside interface.
+ */
+
+// The CodeMirrorConfig object is used to specify a default
+// configuration. If you specify such an object before loading this
+// file, the values you put into it will override the defaults given
+// below. You can also assign to it after loading.
+var CodeMirrorConfig = window.CodeMirrorConfig || {};
+
+var CodeMirror = (function(){
+  function setDefaults(object, defaults) {
+    for (var option in defaults) {
+      if (!object.hasOwnProperty(option))
+        object[option] = defaults[option];
+    }
+  }
+  function forEach(array, action) {
+    for (var i = 0; i < array.length; i++)
+      action(array[i]);
+  }
+
+  // These default options can be overridden by passing a set of
+  // options to a specific CodeMirror constructor. See manual.html for
+  // their meaning.
+  setDefaults(CodeMirrorConfig, {
+    stylesheet: "",
+    path: "",
+    parserfile: [],
+    basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"],
+    iframeClass: null,
+    passDelay: 200,
+    passTime: 50,
+    continuousScanning: false,
+    saveFunction: null,
+    onChange: null,
+    undoDepth: 50,
+    undoDelay: 800,
+    disableSpellcheck: true,
+    textWrapping: true,
+    readOnly: false,
+    width: "100%",
+    height: "300px",
+    autoMatchParens: false,
+    parserConfig: null,
+    tabMode: "indent", // or "spaces", "default", "shift"
+    reindentOnLoad: false,
+    activeTokens: null,
+    cursorActivity: null,
+    lineNumbers: false,
+    indentUnit: 2
+  });
+
+  function wrapLineNumberDiv(place) {
+    return function(node) {
+      var container = document.createElement("DIV"),
+          nums = document.createElement("DIV"),
+          scroller = document.createElement("DIV");
+      container.style.position = "relative";
+      nums.style.position = "absolute";
+      nums.style.height = "100%";
+      if (nums.style.setExpression) {
+        try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
+        catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
+      }
+      nums.style.top = "0px";
+      nums.style.overflow = "hidden";
+      place(container);
+      container.appendChild(node);
+      container.appendChild(nums);
+      scroller.className = "CodeMirror-line-numbers";
+      nums.appendChild(scroller);
+    }
+  }
+
+  function applyLineNumbers(frame) {
+    var win = frame.contentWindow, doc = win.document,
+        nums = frame.nextSibling, scroller = nums.firstChild;
+
+    var nextNum = 1, barWidth = null;
+    function sizeBar() {
+      for (var root = frame; root.parentNode; root = root.parentNode);
+      if (root != document || !win.Editor) {
+        clearInterval(sizeInterval);
+        return;
+      }
+
+      if (nums.offsetWidth != barWidth) {
+        barWidth = nums.offsetWidth;
+        nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px");
+      }
+    }
+    function update() {
+      var diff = 20 + Math.max(doc.body.offsetHeight, frame.offsetHeight) - scroller.offsetHeight;
+      for (var n = Math.ceil(diff / 10); n > 0; n--) {
+        var div = document.createElement("DIV");
+        div.appendChild(document.createTextNode(nextNum++));
+        scroller.appendChild(div);
+      }
+      nums.scrollTop = doc.body.scrollTop || doc.documentElement.scrollTop || 0;
+    }
+    sizeBar();
+    update();
+    win.addEventHandler(win, "scroll", update);
+    win.addEventHandler(win, "resize", update);
+    var sizeInterval = setInterval(sizeBar, 500);
+  }
+
+  function CodeMirror(place, options) {
+    // Backward compatibility for deprecated options.
+    if (options.dumbTabs) options.tabMode = "spaces";
+    else if (options.normalTab) options.tabMode = "default";
+
+    // Use passed options, if any, to override defaults.
+    this.options = options = options || {};
+    setDefaults(options, CodeMirrorConfig);
+
+    var frame = this.frame = document.createElement("IFRAME");
+    if (options.iframeClass) frame.className = options.iframeClass;
+    frame.frameBorder = 0;
+    frame.src = "javascript:false;";
+    frame.style.border = "0";
+    frame.style.width = options.width;
+    frame.style.height = options.height;
+    // display: block occasionally suppresses some Firefox bugs, so we
+    // always add it, redundant as it sounds.
+    frame.style.display = "block";
+
+    if (place.appendChild) {
+      var node = place;
+      place = function(n){node.appendChild(n);};
+    }
+    
+    if (options.lineNumbers) place = wrapLineNumberDiv(place);
+    place(frame);
+
+    // Link back to this object, so that the editor can fetch options
+    // and add a reference to itself.
+    frame.CodeMirror = this;
+    this.win = frame.contentWindow;
+
+    if (typeof options.parserfile == "string")
+      options.parserfile = [options.parserfile];
+    if (typeof options.stylesheet == "string")
+      options.stylesheet = [options.stylesheet];
+
+    var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><head>"];
+    // Hack to work around a bunch of IE8-specific problems.
+    html.push("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/>");
+    forEach(options.stylesheet, function(file) {
+      html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
+    });
+    forEach(options.basefiles.concat(options.parserfile), function(file) {
+      html.push("<script type=\"text/javascript\" src=\"" + options.path + file + "\"></script>");
+    });
+    html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
+              (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
+
+    var doc = this.win.document;
+    doc.open();
+    doc.write(html.join(""));
+    doc.close();
+  }
+
+  CodeMirror.prototype = {
+    init: function() {
+      if (this.options.initCallback) this.options.initCallback(this);
+      if (this.options.lineNumbers) applyLineNumbers(this.frame);
+      if (this.options.reindentOnLoad) this.reindent();
+    },
+
+    getCode: function() {return this.editor.getCode();},
+    setCode: function(code) {this.editor.importCode(code);},
+    selection: function() {this.focusIfIE(); return this.editor.selectedText();},
+    reindent: function() {this.editor.reindent();},
+    reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);},
+
+    focusIfIE: function() {
+      // in IE, a lot of selection-related functionality only works when the frame is focused
+      if (this.win.select.ie_selection) this.focus();
+    },
+    focus: function() {
+      this.win.focus();
+      if (this.editor.selectionSnapshot) // IE hack
+        this.win.select.selectCoords(this.win, this.editor.selectionSnapshot);
+    },
+    replaceSelection: function(text) {
+      this.focus();
+      this.editor.replaceSelection(text);
+      return true;
+    },
+    replaceChars: function(text, start, end) {
+      this.editor.replaceChars(text, start, end);
+    },
+    getSearchCursor: function(string, fromCursor) {
+      return this.editor.getSearchCursor(string, fromCursor);
+    },
+
+    undo: function() {this.editor.history.undo();},
+    redo: function() {this.editor.history.redo();},
+    historySize: function() {return this.editor.history.historySize();},
+    clearHistory: function() {this.editor.history.clear();},
+
+    grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);},
+    ungrabKeys: function() {this.editor.ungrabKeys();},
+
+    setParser: function(name) {this.editor.setParser(name);},
+
+    cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);},
+    firstLine: function() {return this.editor.firstLine();},
+    lastLine: function() {return this.editor.lastLine();},
+    nextLine: function(line) {return this.editor.nextLine(line);},
+    prevLine: function(line) {return this.editor.prevLine(line);},
+    lineContent: function(line) {return this.editor.lineContent(line);},
+    setLineContent: function(line, content) {this.editor.setLineContent(line, content);},
+    insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);},
+    selectLines: function(startLine, startOffset, endLine, endOffset) {
+      this.win.focus();
+      this.editor.selectLines(startLine, startOffset, endLine, endOffset);
+    },
+    nthLine: function(n) {
+      var line = this.firstLine();
+      for (; n > 1 && line !== false; n--)
+        line = this.nextLine(line);
+      return line;
+    },
+    lineNumber: function(line) {
+      var num = 0;
+      while (line !== false) {
+        num++;
+        line = this.prevLine(line);
+      }
+      return num;
+    },
+
+    // Old number-based line interface
+    jumpToLine: function(n) {
+      this.selectLines(this.nthLine(n), 0);
+      this.win.focus();
+    },
+    currentLine: function() {
+      return this.lineNumber(this.cursorPosition().line);
+    }
+  };
+
+  CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}};
+
+  CodeMirror.replace = function(element) {
+    if (typeof element == "string")
+      element = document.getElementById(element);
+    return function(newElement) {
+      element.parentNode.replaceChild(newElement, element);
+    };
+  };
+
+  CodeMirror.fromTextArea = function(area, options) {
+    if (typeof area == "string")
+      area = document.getElementById(area);
+
+    options = options || {};
+    if (area.style.width && options.width == null)
+      options.width = area.style.width;
+    if (area.style.height && options.height == null)
+      options.height = area.style.height;
+    if (options.content == null) options.content = area.value;
+
+    if (area.form) {
+      function updateField() {
+        area.value = mirror.getCode();
+      }
+      if (typeof area.form.addEventListener == "function")
+        area.form.addEventListener("submit", updateField, false);
+      else
+        area.form.attachEvent("onsubmit", updateField);
+    }
+
+    function insert(frame) {
+      if (area.nextSibling)
+        area.parentNode.insertBefore(frame, area.nextSibling);
+      else
+        area.parentNode.appendChild(frame);
+    }
+
+    area.style.display = "none";
+    var mirror = new CodeMirror(insert, options);
+    return mirror;
+  };
+
+  CodeMirror.isProbablySupported = function() {
+    // This is rather awful, but can be useful.
+    var match;
+    if (window.opera)
+      return Number(window.opera.version()) >= 9.52;
+    else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
+      return Number(match[1]) >= 3;
+    else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/)))
+      return Number(match[1]) >= 6;
+    else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i))
+      return Number(match[1]) >= 20050901;
+    else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/))
+      return Number(match[1]) >= 525;
+    else
+      return null;
+  };
+
+  return CodeMirror;
+})();
diff --git a/platforma/static/js/lib/codemirror/editor.js b/platforma/static/js/lib/codemirror/editor.js
new file mode 100644 (file)
index 0000000..3d7a205
--- /dev/null
@@ -0,0 +1,1314 @@
+/* The Editor object manages the content of the editable frame. It
+ * catches events, colours nodes, and indents lines. This file also
+ * holds some functions for transforming arbitrary DOM structures into
+ * plain sequences of <span> and <br> elements
+ */
+
+// Make sure a string does not contain two consecutive 'collapseable'
+// whitespace characters.
+function makeWhiteSpace(n) {
+  var buffer = [], nb = true;
+  for (; n > 0; n--) {
+    buffer.push((nb || n == 1) ? nbsp : " ");
+    nb = !nb;
+  }
+  return buffer.join("");
+}
+
+// Create a set of white-space characters that will not be collapsed
+// by the browser, but will not break text-wrapping either.
+function fixSpaces(string) {
+  if (string.charAt(0) == " ") string = nbsp + string.slice(1);
+  return string.replace(/\t/g, function(){return makeWhiteSpace(indentUnit);})
+    .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);});
+}
+
+function cleanText(text) {
+  return text.replace(/\u00a0/g, " ").replace(/\u200b/g, "");
+}
+
+// Create a SPAN node with the expected properties for document part
+// spans.
+function makePartSpan(value, doc) {
+  var text = value;
+  if (value.nodeType == 3) text = value.nodeValue;
+  else value = doc.createTextNode(text);
+
+  var span = doc.createElement("SPAN");
+  span.isPart = true;
+  span.appendChild(value);
+  span.currentText = text;
+  return span;
+}
+
+// On webkit, when the last BR of the document does not have text
+// behind it, the cursor can not be put on the line after it. This
+// makes pressing enter at the end of the document occasionally do
+// nothing (or at least seem to do nothing). To work around it, this
+// function makes sure the document ends with a span containing a
+// zero-width space character. The traverseDOM iterator filters such
+// character out again, so that the parsers won't see them. This
+// function is called from a few strategic places to make sure the
+// zwsp is restored after the highlighting process eats it.
+var webkitLastLineHack = webkit ?
+  function(container) {
+    var last = container.lastChild;
+    if (!last || !last.isPart || last.textContent != "\u200b")
+      container.appendChild(makePartSpan("\u200b", container.ownerDocument));
+  } : function() {};
+
+var Editor = (function(){
+  // The HTML elements whose content should be suffixed by a newline
+  // when converting them to flat text.
+  var newlineElements = {"P": true, "DIV": true, "LI": true};
+
+  function asEditorLines(string) {
+    var tab = makeWhiteSpace(indentUnit);
+    return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces);
+  }
+
+  // Helper function for traverseDOM. Flattens an arbitrary DOM node
+  // into an array of textnodes and <br> tags.
+  function simplifyDOM(root, atEnd) {
+    var doc = root.ownerDocument;
+    var result = [];
+    var leaving = true;
+
+    function simplifyNode(node, top) {
+      if (node.nodeType == 3) {
+        var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/[\r\u200b]/g, "").replace(/\n/g, " "));
+        if (text.length) leaving = false;
+        result.push(node);
+      }
+      else if (isBR(node) && node.childNodes.length == 0) {
+        leaving = true;
+        result.push(node);
+      }
+      else {
+        forEach(node.childNodes, simplifyNode);
+        if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
+          leaving = true;
+          if (!atEnd || !top)
+            result.push(doc.createElement("BR"));
+        }
+      }
+    }
+
+    simplifyNode(root, true);
+    return result;
+  }
+
+  // Creates a MochiKit-style iterator that goes over a series of DOM
+  // nodes. The values it yields are strings, the textual content of
+  // the nodes. It makes sure that all nodes up to and including the
+  // one whose text is being yielded have been 'normalized' to be just
+  // <span> and <br> elements.
+  // See the story.html file for some short remarks about the use of
+  // continuation-passing style in this iterator.
+  function traverseDOM(start){
+    function yield(value, c){cc = c; return value;}
+    function push(fun, arg, c){return function(){return fun(arg, c);};}
+    function stop(){cc = stop; throw StopIteration;};
+    var cc = push(scanNode, start, stop);
+    var owner = start.ownerDocument;
+    var nodeQueue = [];
+
+    // Create a function that can be used to insert nodes after the
+    // one given as argument.
+    function pointAt(node){
+      var parent = node.parentNode;
+      var next = node.nextSibling;
+      return function(newnode) {
+        parent.insertBefore(newnode, next);
+      };
+    }
+    var point = null;
+
+    // Insert a normalized node at the current point. If it is a text
+    // node, wrap it in a <span>, and give that span a currentText
+    // property -- this is used to cache the nodeValue, because
+    // directly accessing nodeValue is horribly slow on some browsers.
+    // The dirty property is used by the highlighter to determine
+    // which parts of the document have to be re-highlighted.
+    function insertPart(part){
+      var text = "\n";
+      if (part.nodeType == 3) {
+        select.snapshotChanged();
+        part = makePartSpan(part, owner);
+        text = part.currentText;
+      }
+      part.dirty = true;
+      nodeQueue.push(part);
+      point(part);
+      return text;
+    }
+
+    // Extract the text and newlines from a DOM node, insert them into
+    // the document, and yield the textual content. Used to replace
+    // non-normalized nodes.
+    function writeNode(node, c, end) {
+      var toYield = [];
+      forEach(simplifyDOM(node, end), function(part) {
+        toYield.push(insertPart(part));
+      });
+      return yield(toYield.join(""), c);
+    }
+
+    // Check whether a node is a normalized <span> element.
+    function partNode(node){
+      if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
+        node.currentText = node.firstChild.nodeValue;
+        return !/[\n\t\r]/.test(node.currentText);
+      }
+      return false;
+    }
+
+    // Handle a node. Add its successor to the continuation if there
+    // is one, find out whether the node is normalized. If it is,
+    // yield its content, otherwise, normalize it (writeNode will take
+    // care of yielding).
+    function scanNode(node, c){
+      if (node.nextSibling)
+        c = push(scanNode, node.nextSibling, c);
+
+      if (partNode(node)){
+        nodeQueue.push(node);
+        return yield(node.currentText, c);
+      }
+      else if (isBR(node)) {
+        nodeQueue.push(node);
+        return yield("\n", c);
+      }
+      else {
+        var end = !node.nextSibling;
+        point = pointAt(node);
+        removeElement(node);
+        return writeNode(node, c, end);
+      }
+    }
+
+    // MochiKit iterators are objects with a next function that
+    // returns the next value or throws StopIteration when there are
+    // no more values.
+    return {next: function(){return cc();}, nodes: nodeQueue};
+  }
+
+  // Determine the text size of a processed node.
+  function nodeSize(node) {
+    return isBR(node) ? 1 : node.currentText.length;
+  }
+
+  // Search backwards through the top-level nodes until the next BR or
+  // the start of the frame.
+  function startOfLine(node) {
+    while (node && !isBR(node)) node = node.previousSibling;
+    return node;
+  }
+  function endOfLine(node, container) {
+    if (!node) node = container.firstChild;
+    else if (isBR(node)) node = node.nextSibling;
+
+    while (node && !isBR(node)) node = node.nextSibling;
+    return node;
+  }
+
+  function time() {return new Date().getTime();}
+
+  // Client interface for searching the content of the editor. Create
+  // these by calling CodeMirror.getSearchCursor. To use, call
+  // findNext on the resulting object -- this returns a boolean
+  // indicating whether anything was found, and can be called again to
+  // skip to the next find. Use the select and replace methods to
+  // actually do something with the found locations.
+  function SearchCursor(editor, string, fromCursor) {
+    this.editor = editor;
+    this.history = editor.history;
+    this.history.commit();
+
+    // Are we currently at an occurrence of the search string?
+    this.atOccurrence = false;
+    // The object stores a set of nodes coming after its current
+    // position, so that when the current point is taken out of the
+    // DOM tree, we can still try to continue.
+    this.fallbackSize = 15;
+    var cursor;
+    // Start from the cursor when specified and a cursor can be found.
+    if (fromCursor && (cursor = select.cursorPos(this.editor.container))) {
+      this.line = cursor.node;
+      this.offset = cursor.offset;
+    }
+    else {
+      this.line = null;
+      this.offset = 0;
+    }
+    this.valid = !!string;
+
+    // Create a matcher function based on the kind of string we have.
+    var target = string.split("\n"), self = this;
+    this.matches = (target.length == 1) ?
+      // For one-line strings, searching can be done simply by calling
+      // indexOf on the current line.
+      function() {
+        var match = cleanText(self.history.textAfter(self.line).slice(self.offset)).indexOf(string);
+        if (match > -1)
+          return {from: {node: self.line, offset: self.offset + match},
+                  to: {node: self.line, offset: self.offset + match + string.length}};
+      } :
+      // Multi-line strings require internal iteration over lines, and
+      // some clunky checks to make sure the first match ends at the
+      // end of the line and the last match starts at the start.
+      function() {
+        var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset));
+        var match = firstLine.lastIndexOf(target[0]);
+        if (match == -1 || match != firstLine.length - target[0].length)
+          return false;
+        var startOffset = self.offset + match;
+
+        var line = self.history.nodeAfter(self.line);
+        for (var i = 1; i < target.length - 1; i++) {
+          if (cleanText(self.history.textAfter(line)) != target[i])
+            return false;
+          line = self.history.nodeAfter(line);
+        }
+
+        if (cleanText(self.history.textAfter(line)).indexOf(target[target.length - 1]) != 0)
+          return false;
+
+        return {from: {node: self.line, offset: startOffset},
+                to: {node: line, offset: target[target.length - 1].length}};
+      };
+  }
+
+  SearchCursor.prototype = {
+    findNext: function() {
+      if (!this.valid) return false;
+      this.atOccurrence = false;
+      var self = this;
+
+      // Go back to the start of the document if the current line is
+      // no longer in the DOM tree.
+      if (this.line && !this.line.parentNode) {
+        this.line = null;
+        this.offset = 0;
+      }
+
+      // Set the cursor's position one character after the given
+      // position.
+      function saveAfter(pos) {
+        if (self.history.textAfter(pos.node).length > pos.offset) {
+          self.line = pos.node;
+          self.offset = pos.offset + 1;
+        }
+        else {
+          self.line = self.history.nodeAfter(pos.node);
+          self.offset = 0;
+        }
+      }
+
+      while (true) {
+        var match = this.matches();
+        // Found the search string.
+        if (match) {
+          this.atOccurrence = match;
+          saveAfter(match.from);
+          return true;
+        }
+        this.line = this.history.nodeAfter(this.line);
+        this.offset = 0;
+        // End of document.
+        if (!this.line) {
+          this.valid = false;
+          return false;
+        }
+      }
+    },
+
+    select: function() {
+      if (this.atOccurrence) {
+        select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to);
+        select.scrollToCursor(this.editor.container);
+      }
+    },
+
+    replace: function(string) {
+      if (this.atOccurrence) {
+        var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string);
+        this.line = end.node;
+        this.offset = end.offset;
+        this.atOccurrence = false;
+      }
+    }
+  };
+
+  // The Editor object is the main inside-the-iframe interface.
+  function Editor(options) {
+    this.options = options;
+    window.indentUnit = options.indentUnit;
+    this.parent = parent;
+    this.doc = document;
+    var container = this.container = this.doc.body;
+    this.win = window;
+    this.history = new History(container, options.undoDepth, options.undoDelay,
+                               this, options.onChange);
+    var self = this;
+
+    if (!Editor.Parser)
+      throw "No parser loaded.";
+    if (options.parserConfig && Editor.Parser.configure)
+      Editor.Parser.configure(options.parserConfig);
+
+    if (!options.readOnly)
+      select.setCursorPos(container, {node: null, offset: 0});
+
+    this.dirty = [];
+    if (options.content)
+      this.importCode(options.content);
+
+    if (!options.readOnly) {
+      if (options.continuousScanning !== false) {
+        this.scanner = this.documentScanner(options.passTime);
+        this.delayScanning();
+      }
+
+      function setEditable() {
+        // In IE, designMode frames can not run any scripts, so we use
+        // contentEditable instead.
+        if (document.body.contentEditable != undefined && internetExplorer)
+          document.body.contentEditable = "true";
+        else
+          document.designMode = "on";
+
+        document.documentElement.style.borderWidth = "0";
+        if (!options.textWrapping)
+          container.style.whiteSpace = "nowrap";
+      }
+
+      // If setting the frame editable fails, try again when the user
+      // focus it (happens when the frame is not visible on
+      // initialisation, in Firefox).
+      try {
+        setEditable();
+      }
+      catch(e) {
+        var focusEvent = addEventHandler(document, "focus", function() {
+          focusEvent();
+          setEditable();
+        }, true);
+      }
+
+      addEventHandler(document, "keydown", method(this, "keyDown"));
+      addEventHandler(document, "keypress", method(this, "keyPress"));
+      addEventHandler(document, "keyup", method(this, "keyUp"));
+
+      function cursorActivity() {self.cursorActivity(false);}
+      addEventHandler(document.body, "mouseup", cursorActivity);
+      addEventHandler(document.body, "cut", cursorActivity);
+
+      addEventHandler(document.body, "paste", function(event) {
+        cursorActivity();
+        var text = null;
+        try {
+          var clipboardData = event.clipboardData || window.clipboardData;
+          if (clipboardData) text = clipboardData.getData('Text');
+        }
+        catch(e) {}
+        if (text !== null) {
+          self.replaceSelection(text);
+          event.stop();
+        }
+      });
+
+      addEventHandler(document.body, "beforepaste", method(this, "reroutePasteEvent"));
+
+      if (this.options.autoMatchParens)
+        addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
+    }
+    else if (!options.textWrapping) {
+      container.style.whiteSpace = "nowrap";
+    }
+  }
+
+  function isSafeKey(code) {
+    return (code >= 16 && code <= 18) || // shift, control, alt
+           (code >= 33 && code <= 40); // arrows, home, end
+  }
+
+  Editor.prototype = {
+    // Import a piece of code into the editor.
+    importCode: function(code) {
+      this.history.push(null, null, asEditorLines(code));
+      this.history.reset();
+    },
+
+    // Extract the code from the editor.
+    getCode: function() {
+      if (!this.container.firstChild)
+        return "";
+
+      var accum = [];
+      select.markSelection(this.win);
+      forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
+      webkitLastLineHack(this.container);
+      select.selectMarked();
+      return cleanText(accum.join(""));
+    },
+
+    checkLine: function(node) {
+      if (node === false || !(node == null || node.parentNode == this.container))
+        throw parent.CodeMirror.InvalidLineHandle;
+    },
+
+    cursorPosition: function(start) {
+      if (start == null) start = true;
+      var pos = select.cursorPos(this.container, start);
+      if (pos) return {line: pos.node, character: pos.offset};
+      else return {line: null, character: 0};
+    },
+
+    firstLine: function() {
+      return null;
+    },
+
+    lastLine: function() {
+      if (this.container.lastChild) return startOfLine(this.container.lastChild);
+      else return null;
+    },
+
+    nextLine: function(line) {
+      this.checkLine(line);
+      var end = endOfLine(line, this.container);
+      return end || false;
+    },
+
+    prevLine: function(line) {
+      this.checkLine(line);
+      if (line == null) return false;
+      return startOfLine(line.previousSibling);
+    },
+
+    selectLines: function(startLine, startOffset, endLine, endOffset) {
+      this.checkLine(startLine);
+      var start = {node: startLine, offset: startOffset}, end = null;
+      if (endOffset !== undefined) {
+        this.checkLine(endLine);
+        end = {node: endLine, offset: endOffset};
+      }
+      select.setCursorPos(this.container, start, end);
+      select.scrollToCursor(this.container);
+    },
+
+    lineContent: function(line) {
+      this.checkLine(line);
+      var accum = [];
+      for (line = line ? line.nextSibling : this.container.firstChild;
+           line && !isBR(line); line = line.nextSibling)
+        accum.push(nodeText(line));
+      return cleanText(accum.join(""));
+    },
+
+    setLineContent: function(line, content) {
+      this.history.commit();
+      this.replaceRange({node: line, offset: 0},
+                        {node: line, offset: this.history.textAfter(line).length},
+                        content);
+      this.addDirtyNode(line);
+      this.scheduleHighlight();
+    },
+
+    insertIntoLine: function(line, position, content) {
+      var before = null;
+      if (position == "end") {
+        before = endOfLine(line, this.container);
+      }
+      else {
+        for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) {
+          if (position == 0) {
+            before = cur;
+            break;
+          }
+          var text = nodeText(cur);
+          if (text.length > position) {
+            before = cur.nextSibling;
+            content = text.slice(0, position) + content + text.slice(position);
+            removeElement(cur);
+            break;
+          }
+          position -= text.length;
+        }
+      }
+
+      var lines = asEditorLines(content), doc = this.container.ownerDocument;
+      for (var i = 0; i < lines.length; i++) {
+        if (i > 0) this.container.insertBefore(doc.createElement("BR"), before);
+        this.container.insertBefore(makePartSpan(lines[i], doc), before);
+      }
+      this.addDirtyNode(line);
+      this.scheduleHighlight();
+    },
+
+    // Retrieve the selected text.
+    selectedText: function() {
+      var h = this.history;
+      h.commit();
+
+      var start = select.cursorPos(this.container, true),
+          end = select.cursorPos(this.container, false);
+      if (!start || !end) return "";
+
+      if (start.node == end.node)
+        return h.textAfter(start.node).slice(start.offset, end.offset);
+
+      var text = [h.textAfter(start.node).slice(start.offset)];
+      for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos))
+        text.push(h.textAfter(pos));
+      text.push(h.textAfter(end.node).slice(0, end.offset));
+      return cleanText(text.join("\n"));
+    },
+
+    // Replace the selection with another piece of text.
+    replaceSelection: function(text) {
+      this.history.commit();
+
+      var start = select.cursorPos(this.container, true),
+          end = select.cursorPos(this.container, false);
+      if (!start || !end) return;
+
+      end = this.replaceRange(start, end, text);
+      select.setCursorPos(this.container, end);
+      webkitLastLineHack(this.container);
+    },
+
+    reroutePasteEvent: function() {
+      if (this.capturingPaste || window.opera) return;
+      this.capturingPaste = true;
+      var te = parent.document.createElement("TEXTAREA");
+      te.style.position = "absolute";
+      te.style.left = "-500px";
+      te.style.width = "10px";
+      te.style.top = nodeTop(frameElement) + "px";
+      parent.document.body.appendChild(te);
+      parent.focus();
+      te.focus();
+
+      var self = this;
+      this.parent.setTimeout(function() {
+        self.capturingPaste = false;
+        self.win.focus();
+        if (self.selectionSnapshot) // IE hack
+          self.win.select.selectCoords(self.win, self.selectionSnapshot);
+        var text = te.value;
+        if (text) self.replaceSelection(text);
+        removeElement(te);
+      }, 10);
+    },
+
+    replaceRange: function(from, to, text) {
+      var lines = asEditorLines(text);
+      lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0];
+      var lastLine = lines[lines.length - 1];
+      lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset);
+      var end = this.history.nodeAfter(to.node);
+      this.history.push(from.node, end, lines);
+      return {node: this.history.nodeBefore(end),
+              offset: lastLine.length};
+    },
+
+    getSearchCursor: function(string, fromCursor) {
+      return new SearchCursor(this, string, fromCursor);
+    },
+
+    // Re-indent the whole buffer
+    reindent: function() {
+      if (this.container.firstChild)
+        this.indentRegion(null, this.container.lastChild);
+    },
+
+    reindentSelection: function(direction) {
+      if (!select.somethingSelected(this.win)) {
+        this.indentAtCursor(direction);
+      }
+      else {
+        var start = select.selectionTopNode(this.container, true),
+            end = select.selectionTopNode(this.container, false);
+        if (start === false || end === false) return;
+        this.indentRegion(start, end, direction);
+      }
+    },
+
+    grabKeys: function(eventHandler, filter) {
+      this.frozen = eventHandler;
+      this.keyFilter = filter;
+    },
+    ungrabKeys: function() {
+      this.frozen = "leave";
+      this.keyFilter = null;
+    },
+
+    setParser: function(name) {
+      Editor.Parser = window[name];
+      if (this.container.firstChild) {
+        forEach(this.container.childNodes, function(n) {
+          if (n.nodeType != 3) n.dirty = true;
+        });
+        this.addDirtyNode(this.firstChild);
+        this.scheduleHighlight();
+      }
+    },
+
+    // Intercept enter and tab, and assign their new functions.
+    keyDown: function(event) {
+      if (this.frozen == "leave") this.frozen = null;
+      if (this.frozen && (!this.keyFilter || this.keyFilter(event))) {
+        event.stop();
+        this.frozen(event);
+        return;
+      }
+
+      var code = event.keyCode;
+      // Don't scan when the user is typing.
+      this.delayScanning();
+      // Schedule a paren-highlight event, if configured.
+      if (this.options.autoMatchParens)
+        this.scheduleParenBlink();
+
+      // The various checks for !altKey are there because AltGr sets both
+      // ctrlKey and altKey to true, and should not be recognised as
+      // Control.
+      if (code == 13) { // enter
+        if (event.ctrlKey && !event.altKey) {
+          this.reparseBuffer();
+        }
+        else {
+          select.insertNewlineAtCursor(this.win);
+          this.indentAtCursor();
+          select.scrollToCursor(this.container);
+        }
+        event.stop();
+      }
+      else if (code == 9 && this.options.tabMode != "default") { // tab
+        this.handleTab(!event.ctrlKey && !event.shiftKey);
+        event.stop();
+      }
+      else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space
+        this.handleTab(true);
+        event.stop();
+      }
+      else if (code == 36 && !event.shiftKey && !event.ctrlKey) { // home
+        if (this.home())
+          event.stop();
+      }
+      else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ]
+        this.blinkParens(event.shiftKey);
+        event.stop();
+      }
+      else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right
+        var cursor = select.selectionTopNode(this.container);
+        if (cursor === false || !this.container.firstChild) return;
+
+        if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container);
+        else {
+          var end = endOfLine(cursor, this.container);
+          select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container);
+        }
+        event.stop();
+      }
+      else if ((event.ctrlKey || event.metaKey) && !event.altKey) {
+        if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y
+          select.scrollToNode(this.history.redo());
+          event.stop();
+        }
+        else if (code == 90 || (safari && code == 8)) { // Z, backspace
+          select.scrollToNode(this.history.undo());
+          event.stop();
+        }
+        else if (code == 83 && this.options.saveFunction) { // S
+          this.options.saveFunction();
+          event.stop();
+        }
+      }
+    },
+
+    // Check for characters that should re-indent the current line,
+    // and prevent Opera from handling enter and tab anyway.
+    keyPress: function(event) {
+      var electric = Editor.Parser.electricChars, self = this;
+      // Hack for Opera, and Firefox on OS X, in which stopping a
+      // keydown event does not prevent the associated keypress event
+      // from happening, so we have to cancel enter and tab again
+      // here.
+      if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) ||
+          event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
+          (event.keyCode == 32 && event.shiftKey && this.options.tabMode == "default"))
+        event.stop();
+      else if (electric && electric.indexOf(event.character) != -1)
+        this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
+      else if ((event.character == "v" || event.character == "V")
+               && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V
+        this.reroutePasteEvent();
+    },
+
+    // Mark the node at the cursor dirty when a non-safe key is
+    // released.
+    keyUp: function(event) {
+      this.cursorActivity(isSafeKey(event.keyCode));
+    },
+
+    // Indent the line following a given <br>, or null for the first
+    // line. If given a <br> element, this must have been highlighted
+    // so that it has an indentation method. Returns the whitespace
+    // element that has been modified or created (if any).
+    indentLineAfter: function(start, direction) {
+      // whiteSpace is the whitespace span at the start of the line,
+      // or null if there is no such node.
+      var whiteSpace = start ? start.nextSibling : this.container.firstChild;
+      if (whiteSpace && !hasClass(whiteSpace, "whitespace"))
+        whiteSpace = null;
+
+      // Sometimes the start of the line can influence the correct
+      // indentation, so we retrieve it.
+      var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
+      var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
+
+      // Ask the lexical context for the correct indentation, and
+      // compute how much this differs from the current indentation.
+      var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0;
+      if (direction != null && this.options.tabMode == "shift")
+        newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit)
+      else if (start)
+        newIndent = start.indentation(nextChars, curIndent, direction);
+      else if (Editor.Parser.firstIndentation)
+        newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction);
+      var indentDiff = newIndent - curIndent;
+
+      // If there is too much, this is just a matter of shrinking a span.
+      if (indentDiff < 0) {
+        if (newIndent == 0) {
+          if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0);
+          removeElement(whiteSpace);
+          whiteSpace = null;
+        }
+        else {
+          select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
+          whiteSpace.currentText = makeWhiteSpace(newIndent);
+          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+        }
+      }
+      // Not enough...
+      else if (indentDiff > 0) {
+        // If there is whitespace, we grow it.
+        if (whiteSpace) {
+          whiteSpace.currentText = makeWhiteSpace(newIndent);
+          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+        }
+        // Otherwise, we have to add a new whitespace node.
+        else {
+          whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc);
+          whiteSpace.className = "whitespace";
+          if (start) insertAfter(whiteSpace, start);
+          else this.container.insertBefore(whiteSpace, this.container.firstChild);
+        }
+        if (firstText) select.snapshotMove(firstText.firstChild, whiteSpace.firstChild, curIndent, false, true);
+      }
+      if (indentDiff != 0) this.addDirtyNode(start);
+      return whiteSpace;
+    },
+
+    // Re-highlight the selected part of the document.
+    highlightAtCursor: function() {
+      var pos = select.selectionTopNode(this.container, true);
+      var to = select.selectionTopNode(this.container, false);
+      if (pos === false || to === false) return;
+
+      select.markSelection(this.win);
+      if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false)
+        return false;
+      select.selectMarked();
+      return true;
+    },
+
+    // When tab is pressed with text selected, the whole selection is
+    // re-indented, when nothing is selected, the line with the cursor
+    // is re-indented.
+    handleTab: function(direction) {
+      if (this.options.tabMode == "spaces")
+        select.insertTabAtCursor(this.win);
+      else
+        this.reindentSelection(direction);
+    },
+
+    home: function() {
+      var cur = select.selectionTopNode(this.container, true), start = cur;
+      if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild)
+        return false;
+
+      while (cur && !isBR(cur)) cur = cur.previousSibling;
+      var next = cur ? cur.nextSibling : this.container.firstChild;
+      if (next && next != start && next.isPart && hasClass(next, "whitespace"))
+        select.focusAfterNode(next, this.container);
+      else
+        select.focusAfterNode(cur, this.container);
+
+      select.scrollToCursor(this.container);
+      return true;
+    },
+
+    // Delay (or initiate) the next paren blink event.
+    scheduleParenBlink: function() {
+      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+      var self = this;
+      this.parenEvent = this.parent.setTimeout(function(){self.blinkParens();}, 300);
+    },
+
+    // Take the token before the cursor. If it contains a character in
+    // '()[]{}', search for the matching paren/brace/bracket, and
+    // highlight them in green for a moment, or red if no proper match
+    // was found.
+    blinkParens: function(jump) {
+      if (!window.select) return;
+      // Clear the event property.
+      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+      this.parenEvent = null;
+
+      // Extract a 'paren' from a piece of text.
+      function paren(node) {
+        if (node.currentText) {
+          var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/);
+          return match && match[1];
+        }
+      }
+      // Determine the direction a paren is facing.
+      function forward(ch) {
+        return /[\(\[\{]/.test(ch);
+      }
+
+      var ch, self = this, cursor = select.selectionTopNode(this.container, true);
+      if (!cursor || !this.highlightAtCursor()) return;
+      cursor = select.selectionTopNode(this.container, true);
+      if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor)))))
+        return;
+      // We only look for tokens with the same className.
+      var className = cursor.className, dir = forward(ch), match = matching[ch];
+
+      // Since parts of the document might not have been properly
+      // highlighted, and it is hard to know in advance which part we
+      // have to scan, we just try, and when we find dirty nodes we
+      // abort, parse them, and re-try.
+      function tryFindMatch() {
+        var stack = [], ch, ok = true;;
+        for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
+          if (runner.className == className && isSpan(runner) && (ch = paren(runner))) {
+            if (forward(ch) == dir)
+              stack.push(ch);
+            else if (!stack.length)
+              ok = false;
+            else if (stack.pop() != matching[ch])
+              ok = false;
+            if (!stack.length) break;
+          }
+          else if (runner.dirty || !isSpan(runner) && !isBR(runner)) {
+            return {node: runner, status: "dirty"};
+          }
+        }
+        return {node: runner, status: runner && ok};
+      }
+      // Temporarily give the relevant nodes a colour.
+      function blink(node, ok) {
+        node.style.fontWeight = "bold";
+        node.style.color = ok ? "#8F8" : "#F88";
+        self.parent.setTimeout(function() {node.style.fontWeight = ""; node.style.color = "";}, 500);
+      }
+
+      while (true) {
+        var found = tryFindMatch();
+        if (found.status == "dirty") {
+          this.highlight(found.node, endOfLine(found.node));
+          // Needed because in some corner cases a highlight does not
+          // reach a node.
+          found.node.dirty = false;
+          continue;
+        }
+        else {
+          blink(cursor, found.status);
+          if (found.node) {
+            blink(found.node, found.status);
+            if (jump) select.focusAfterNode(found.node.previousSibling, this.container);
+          }
+          break;
+        }
+      }
+    },
+
+    // Adjust the amount of whitespace at the start of the line that
+    // the cursor is on so that it is indented properly.
+    indentAtCursor: function(direction) {
+      if (!this.container.firstChild) return;
+      // The line has to have up-to-date lexical information, so we
+      // highlight it first.
+      if (!this.highlightAtCursor()) return;
+      var cursor = select.selectionTopNode(this.container, false);
+      // If we couldn't determine the place of the cursor,
+      // there's nothing to indent.
+      if (cursor === false)
+        return;
+      var lineStart = startOfLine(cursor);
+      var whiteSpace = this.indentLineAfter(lineStart, direction);
+      if (cursor == lineStart && whiteSpace)
+          cursor = whiteSpace;
+      // This means the indentation has probably messed up the cursor.
+      if (cursor == whiteSpace)
+        select.focusAfterNode(cursor, this.container);
+    },
+
+    // Indent all lines whose start falls inside of the current
+    // selection.
+    indentRegion: function(start, end, direction) {
+      var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling);
+      if (!isBR(end)) end = endOfLine(end, this.container);
+
+      do {
+        var next = endOfLine(current, this.container);
+        if (current) this.highlight(before, next, true);
+        this.indentLineAfter(current, direction);
+        before = current;
+        current = next;
+      } while (current != end);
+      select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0});
+    },
+
+    // Find the node that the cursor is in, mark it as dirty, and make
+    // sure a highlight pass is scheduled.
+    cursorActivity: function(safe) {
+      if (internetExplorer) {
+        this.container.createTextRange().execCommand("unlink");
+        this.selectionSnapshot = select.selectionCoords(this.win);
+      }
+
+      var activity = this.options.cursorActivity;
+      if (!safe || activity) {
+        var cursor = select.selectionTopNode(this.container, false);
+        if (cursor === false || !this.container.firstChild) return;
+        cursor = cursor || this.container.firstChild;
+        if (activity) activity(cursor);
+        if (!safe) {
+          this.scheduleHighlight();
+          this.addDirtyNode(cursor);
+        }
+      }
+    },
+
+    reparseBuffer: function() {
+      forEach(this.container.childNodes, function(node) {node.dirty = true;});
+      if (this.container.firstChild)
+        this.addDirtyNode(this.container.firstChild);
+    },
+
+    // Add a node to the set of dirty nodes, if it isn't already in
+    // there.
+    addDirtyNode: function(node) {
+      node = node || this.container.firstChild;
+      if (!node) return;
+
+      for (var i = 0; i < this.dirty.length; i++)
+        if (this.dirty[i] == node) return;
+
+      if (node.nodeType != 3)
+        node.dirty = true;
+      this.dirty.push(node);
+    },
+
+    // Cause a highlight pass to happen in options.passDelay
+    // milliseconds. Clear the existing timeout, if one exists. This
+    // way, the passes do not happen while the user is typing, and
+    // should as unobtrusive as possible.
+    scheduleHighlight: function() {
+      // Timeouts are routed through the parent window, because on
+      // some browsers designMode windows do not fire timeouts.
+      var self = this;
+      this.parent.clearTimeout(this.highlightTimeout);
+      this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay);
+    },
+
+    // Fetch one dirty node, and remove it from the dirty set.
+    getDirtyNode: function() {
+      while (this.dirty.length > 0) {
+        var found = this.dirty.pop();
+        // IE8 sometimes throws an unexplainable 'invalid argument'
+        // exception for found.parentNode
+        try {
+          // If the node has been coloured in the meantime, or is no
+          // longer in the document, it should not be returned.
+          while (found && found.parentNode != this.container)
+            found = found.parentNode
+          if (found && (found.dirty || found.nodeType == 3))
+            return found;
+        } catch (e) {}
+      }
+      return null;
+    },
+
+    // Pick dirty nodes, and highlight them, until options.passTime
+    // milliseconds have gone by. The highlight method will continue
+    // to next lines as long as it finds dirty nodes. It returns
+    // information about the place where it stopped. If there are
+    // dirty nodes left after this function has spent all its lines,
+    // it shedules another highlight to finish the job.
+    highlightDirty: function(force) {
+      // Prevent FF from raising an error when it is firing timeouts
+      // on a page that's no longer loaded.
+      if (!window.select) return;
+
+      if (!this.options.readOnly) select.markSelection(this.win);
+      var start, endTime = force ? null : time() + this.options.passTime;
+      while ((time() < endTime || force) && (start = this.getDirtyNode())) {
+        var result = this.highlight(start, endTime);
+        if (result && result.node && result.dirty)
+          this.addDirtyNode(result.node);
+      }
+      if (!this.options.readOnly) select.selectMarked();
+      if (start) this.scheduleHighlight();
+      return this.dirty.length == 0;
+    },
+
+    // Creates a function that, when called through a timeout, will
+    // continuously re-parse the document.
+    documentScanner: function(passTime) {
+      var self = this, pos = null;
+      return function() {
+        // FF timeout weirdness workaround.
+        if (!window.select) return;
+        // If the current node is no longer in the document... oh
+        // well, we start over.
+        if (pos && pos.parentNode != self.container)
+          pos = null;
+        select.markSelection(self.win);
+        var result = self.highlight(pos, time() + passTime, true);
+        select.selectMarked();
+        var newPos = result ? (result.node && result.node.nextSibling) : null;
+        pos = (pos == newPos) ? null : newPos;
+        self.delayScanning();
+      };
+    },
+
+    // Starts the continuous scanning process for this document after
+    // a given interval.
+    delayScanning: function() {
+      if (this.scanner) {
+        this.parent.clearTimeout(this.documentScan);
+        this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning);
+      }
+    },
+
+    // The function that does the actual highlighting/colouring (with
+    // help from the parser and the DOM normalizer). Its interface is
+    // rather overcomplicated, because it is used in different
+    // situations: ensuring that a certain line is highlighted, or
+    // highlighting up to X milliseconds starting from a certain
+    // point. The 'from' argument gives the node at which it should
+    // start. If this is null, it will start at the beginning of the
+    // document. When a timestamp is given with the 'target' argument,
+    // it will stop highlighting at that time. If this argument holds
+    // a DOM node, it will highlight until it reaches that node. If at
+    // any time it comes across two 'clean' lines (no dirty nodes), it
+    // will stop, except when 'cleanLines' is true. maxBacktrack is
+    // the maximum number of lines to backtrack to find an existing
+    // parser instance. This is used to give up in situations where a
+    // highlight would take too long and freeze the browser interface.
+    highlight: function(from, target, cleanLines, maxBacktrack){
+      var container = this.container, self = this, active = this.options.activeTokens;
+      var endTime = (typeof target == "number" ? target : null);
+
+      if (!container.firstChild)
+        return;
+      // Backtrack to the first node before from that has a partial
+      // parse stored.
+      while (from && (!from.parserFromHere || from.dirty)) {
+        if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0)
+          return false;
+        from = from.previousSibling;
+      }
+      // If we are at the end of the document, do nothing.
+      if (from && !from.nextSibling)
+        return;
+
+      // Check whether a part (<span> node) and the corresponding token
+      // match.
+      function correctPart(token, part){
+        return !part.reduced && part.currentText == token.value && part.className == token.style;
+      }
+      // Shorten the text associated with a part by chopping off
+      // characters from the front. Note that only the currentText
+      // property gets changed. For efficiency reasons, we leave the
+      // nodeValue alone -- we set the reduced flag to indicate that
+      // this part must be replaced.
+      function shortenPart(part, minus){
+        part.currentText = part.currentText.substring(minus);
+        part.reduced = true;
+      }
+      // Create a part corresponding to a given token.
+      function tokenPart(token){
+        var part = makePartSpan(token.value, self.doc);     
+        part.className = token.style;
+        return part;
+      }
+
+      function maybeTouch(node) {
+        if (node) {
+          var old = node.oldNextSibling;
+          if (lineDirty || old === undefined || node.nextSibling != old)
+            self.history.touch(node);
+          node.oldNextSibling = node.nextSibling;
+        }
+        else {
+          var old = self.container.oldFirstChild;
+          if (lineDirty || old === undefined || self.container.firstChild != old)
+            self.history.touch(null);
+          self.container.oldFirstChild = self.container.firstChild;
+        }
+      }
+
+      // Get the token stream. If from is null, we start with a new
+      // parser from the start of the frame, otherwise a partial parse
+      // is resumed.
+      var traversal = traverseDOM(from ? from.nextSibling : container.firstChild),
+          stream = stringStream(traversal),
+          parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream);
+
+      // parts is an interface to make it possible to 'delay' fetching
+      // the next DOM node until we are completely done with the one
+      // before it. This is necessary because often the next node is
+      // not yet available when we want to proceed past the current
+      // one.
+      var parts = {
+        current: null,
+        // Fetch current node.
+        get: function(){
+          if (!this.current)
+            this.current = traversal.nodes.shift();
+          return this.current;
+        },
+        // Advance to the next part (do not fetch it yet).
+        next: function(){
+          this.current = null;
+        },
+        // Remove the current part from the DOM tree, and move to the
+        // next.
+        remove: function(){
+          container.removeChild(this.get());
+          this.current = null;
+        },
+        // Advance to the next part that is not empty, discarding empty
+        // parts.
+        getNonEmpty: function(){
+          var part = this.get();
+          // Allow empty nodes when they are alone on a line, needed
+          // for the FF cursor bug workaround (see select.js,
+          // insertNewlineAtCursor).
+          while (part && isSpan(part) && part.currentText == "") {
+            var old = part;
+            this.remove();
+            part = this.get();
+            // Adjust selection information, if any. See select.js for details.
+            select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0);
+          }
+          return part;
+        }
+      };
+
+      var lineDirty = false, prevLineDirty = true, lineNodes = 0;
+
+      // This forEach loops over the tokens from the parsed stream, and
+      // at the same time uses the parts object to proceed through the
+      // corresponding DOM nodes.
+      forEach(parsed, function(token){
+        var part = parts.getNonEmpty();
+
+        if (token.value == "\n"){
+          // The idea of the two streams actually staying synchronized
+          // is such a long shot that we explicitly check.
+          if (!isBR(part))
+            throw "Parser out of sync. Expected BR.";
+
+          if (part.dirty || !part.indentation) lineDirty = true;
+          maybeTouch(from);
+          from = part;
+
+          // Every <br> gets a copy of the parser state and a lexical
+          // context assigned to it. The first is used to be able to
+          // later resume parsing from this point, the second is used
+          // for indentation.
+          part.parserFromHere = parsed.copy();
+          part.indentation = token.indentation;
+          part.dirty = false;
+
+          // If the target argument wasn't an integer, go at least
+          // until that node.
+          if (endTime == null && part == target) throw StopIteration;
+
+          // A clean line with more than one node means we are done.
+          // Throwing a StopIteration is the way to break out of a
+          // MochiKit forEach loop.
+          if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines))
+            throw StopIteration;
+          prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0;
+          parts.next();
+        }
+        else {
+          if (!isSpan(part))
+            throw "Parser out of sync. Expected SPAN.";
+          if (part.dirty)
+            lineDirty = true;
+          lineNodes++;
+
+          // If the part matches the token, we can leave it alone.
+          if (correctPart(token, part)){
+            part.dirty = false;
+            parts.next();
+          }
+          // Otherwise, we have to fix it.
+          else {
+            lineDirty = true;
+            // Insert the correct part.
+            var newPart = tokenPart(token);
+            container.insertBefore(newPart, part);
+            if (active) active(newPart, token, self);
+            var tokensize = token.value.length;
+            var offset = 0;
+            // Eat up parts until the text for this token has been
+            // removed, adjusting the stored selection info (see
+            // select.js) in the process.
+            while (tokensize > 0) {
+              part = parts.get();
+              var partsize = part.currentText.length;
+              select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset);
+              if (partsize > tokensize){
+                shortenPart(part, tokensize);
+                tokensize = 0;
+              }
+              else {
+                tokensize -= partsize;
+                offset += partsize;
+                parts.remove();
+              }
+            }
+          }
+        }
+      });
+      maybeTouch(from);
+      webkitLastLineHack(this.container);
+
+      // The function returns some status information that is used by
+      // hightlightDirty to determine whether and where it has to
+      // continue.
+      return {node: parts.getNonEmpty(),
+              dirty: lineDirty};
+    }
+  };
+
+  return Editor;
+})();
+
+addEventHandler(window, "load", function() {
+  var CodeMirror = window.frameElement.CodeMirror;
+  CodeMirror.editor = new Editor(CodeMirror.options);
+  this.parent.setTimeout(method(CodeMirror, "init"), 0);
+});
diff --git a/platforma/static/js/lib/codemirror/parsexml.js b/platforma/static/js/lib/codemirror/parsexml.js
new file mode 100644 (file)
index 0000000..95a8099
--- /dev/null
@@ -0,0 +1,292 @@
+/* This file defines an XML parser, with a few kludges to make it
+ * useable for HTML. autoSelfClosers defines a set of tag names that
+ * are expected to not have a closing tag, and doNotIndent specifies
+ * the tags inside of which no indentation should happen (see Config
+ * object). These can be disabled by passing the editor an object like
+ * {useHTMLKludges: false} as parserConfig option.
+ */
+
+var XMLParser = Editor.Parser = (function() {
+  var Kludges = {
+    autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
+                      "meta": true, "col": true, "frame": true, "base": true, "area": true},
+    doNotIndent: {"pre": true, "!cdata": true}
+  };
+  var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}};
+  var UseKludges = Kludges;
+  var alignCDATA = false;
+
+  // Simple stateful tokenizer for XML documents. Returns a
+  // MochiKit-style iterator, with a state property that contains a
+  // function encapsulating the current state. See tokenize.js.
+  var tokenizeXML = (function() {
+    function inText(source, setState) {
+      var ch = source.next();
+      if (ch == "<") {
+        if (source.equals("!")) {
+          source.next();
+          if (source.equals("[")) {
+            if (source.lookAhead("[CDATA[", true)) {
+              setState(inBlock("xml-cdata", "]]>"));
+              return null;
+            }
+            else {
+              return "xml-text";
+            }
+          }
+          else if (source.lookAhead("--", true)) {
+            setState(inBlock("xml-comment", "-->"));
+            return null;
+          }
+          else {
+            return "xml-text";
+          }
+        }
+        else if (source.equals("?")) {
+          source.next();
+          source.nextWhileMatches(/[\w\._\-]/);
+          setState(inBlock("xml-processing", "?>"));
+          return "xml-processing";
+        }
+        else {
+          if (source.equals("/")) source.next();
+          setState(inTag);
+          return "xml-punctuation";
+        }
+      }
+      else if (ch == "&") {
+        while (!source.endOfLine()) {
+          if (source.next() == ";")
+            break;
+        }
+        return "xml-entity";
+      }
+      else {
+        source.nextWhileMatches(/[^&<\n]/);
+        return "xml-text";
+      }
+    }
+
+    function inTag(source, setState) {
+      var ch = source.next();
+      if (ch == ">") {
+        setState(inText);
+        return "xml-punctuation";
+      }
+      else if (/[?\/]/.test(ch) && source.equals(">")) {
+        source.next();
+        setState(inText);
+        return "xml-punctuation";
+      }
+      else if (ch == "=") {
+        return "xml-punctuation";
+      }
+      else if (/[\'\"]/.test(ch)) {
+        setState(inAttribute(ch));
+        return null;
+      }
+      else {
+        source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/);
+        return "xml-name";
+      }
+    }
+
+    function inAttribute(quote) {
+      return function(source, setState) {
+        while (!source.endOfLine()) {
+          if (source.next() == quote) {
+            setState(inTag);
+            break;
+          }
+        }
+        return "xml-attribute";
+      };
+    }
+
+    function inBlock(style, terminator) {
+      return function(source, setState) {
+        while (!source.endOfLine()) {
+          if (source.lookAhead(terminator, true)) {
+            setState(inText);
+            break;
+          }
+          source.next();
+        }
+        return style;
+      };
+    }
+
+    return function(source, startState) {
+      return tokenizer(source, startState || inText);
+    };
+  })();
+
+  // The parser. The structure of this function largely follows that of
+  // parseJavaScript in parsejavascript.js (there is actually a bit more
+  // shared code than I'd like), but it is quite a bit simpler.
+  function parseXML(source) {
+    var tokens = tokenizeXML(source);
+    var cc = [base];
+    var tokenNr = 0, indented = 0;
+    var currentTag = null, context = null;
+    var consume, marked;
+    
+    function push(fs) {
+      for (var i = fs.length - 1; i >= 0; i--)
+        cc.push(fs[i]);
+    }
+    function cont() {
+      push(arguments);
+      consume = true;
+    }
+    function pass() {
+      push(arguments);
+      consume = false;
+    }
+
+    function mark(style) {
+      marked = style;
+    }
+    function expect(text) {
+      return function(style, content) {
+        if (content == text) cont();
+        else mark("xml-error") || cont(arguments.callee);
+      };
+    }
+
+    function pushContext(tagname, startOfLine) {
+      var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent);
+      context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent};
+    }
+    function popContext() {
+      context = context.prev;
+    }
+    function computeIndentation(baseContext) {
+      return function(nextChars, current) {
+        var context = baseContext;
+        if (context && context.noIndent)
+          return current;
+        if (alignCDATA && /<!\[CDATA\[/.test(nextChars))
+          return 0;
+        if (context && /^<\//.test(nextChars))
+          context = context.prev;
+        while (context && !context.startOfLine)
+          context = context.prev;
+        if (context)
+          return context.indent + indentUnit;
+        else
+          return 0;
+      };
+    }
+
+    function base() {
+      return pass(element, base);
+    }
+    var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true};
+    function element(style, content) {
+      if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1));
+      else if (content == "</") cont(closetagname, expect(">"));
+      else if (style == "xml-cdata") {
+        if (!context || context.name != "!cdata") pushContext("!cdata");
+        if (/\]\]>$/.test(content)) popContext();
+        cont();
+      }
+      else if (harmlessTokens.hasOwnProperty(style)) cont();
+      else mark("xml-error") || cont();
+    }
+    function tagname(style, content) {
+      if (style == "xml-name") {
+        currentTag = content.toLowerCase();
+        mark("xml-tagname");
+        cont();
+      }
+      else {
+        currentTag = null;
+        pass();
+      }
+    }
+    function closetagname(style, content) {
+      if (style == "xml-name" && context && content.toLowerCase() == context.name) {
+        popContext();
+        mark("xml-tagname");
+      }
+      else {
+        mark("xml-error");
+      }
+      cont();
+    }
+    function endtag(startOfLine) {
+      return function(style, content) {
+        if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont();
+        else if (content == ">") pushContext(currentTag, startOfLine) || cont();
+        else mark("xml-error") || cont(arguments.callee);
+      };
+    }
+    function attributes(style) {
+      if (style == "xml-name") mark("xml-attname") || cont(attribute, attributes);
+      else pass();
+    }
+    function attribute(style, content) {
+      if (content == "=") cont(value);
+      else if (content == ">" || content == "/>") pass(endtag);
+      else pass();
+    }
+    function value(style) {
+      if (style == "xml-attribute") cont(value);
+      else pass();
+    }
+
+    return {
+      indentation: function() {return indented;},
+
+      next: function(){
+        var token = tokens.next();
+        if (token.style == "whitespace" && tokenNr == 0)
+          indented = token.value.length;
+        else
+          tokenNr++;
+        if (token.content == "\n") {
+          indented = tokenNr = 0;
+          token.indentation = computeIndentation(context);
+        }
+
+        if (token.style == "whitespace" || token.type == "xml-comment")
+          return token;
+
+        while(true){
+          consume = marked = false;
+          cc.pop()(token.style, token.content);
+          if (consume){
+            if (marked)
+              token.style = marked;
+            return token;
+          }
+        }
+      },
+
+      copy: function(){
+        var _cc = cc.concat([]), _tokenState = tokens.state, _context = context;
+        var parser = this;
+        
+        return function(input){
+          cc = _cc.concat([]);
+          tokenNr = indented = 0;
+          context = _context;
+          tokens = tokenizeXML(input, _tokenState);
+          return parser;
+        };
+      }
+    };
+  }
+
+  return {
+    make: parseXML,
+    electricChars: "/",
+    configure: function(config) {
+      if (config.useHTMLKludges != null)
+        UseKludges = config.useHTMLKludges ? Kludges : NoKludges;
+      if (config.alignCDATA)
+        alignCDATA = config.alignCDATA;
+    }
+  };
+})();
diff --git a/platforma/static/js/lib/codemirror/select.js b/platforma/static/js/lib/codemirror/select.js
new file mode 100644 (file)
index 0000000..7746240
--- /dev/null
@@ -0,0 +1,624 @@
+/* Functionality for finding, storing, and restoring selections
+ *
+ * This does not provide a generic API, just the minimal functionality
+ * required by the CodeMirror system.
+ */
+
+// Namespace object.
+var select = {};
+
+(function() {
+  select.ie_selection = document.selection && document.selection.createRangeCollection;
+
+  // Find the 'top-level' (defined as 'a direct child of the node
+  // passed as the top argument') node that the given node is
+  // contained in. Return null if the given node is not inside the top
+  // node.
+  function topLevelNodeAt(node, top) {
+    while (node && node.parentNode != top)
+      node = node.parentNode;
+    return node;
+  }
+
+  // Find the top-level node that contains the node before this one.
+  function topLevelNodeBefore(node, top) {
+    while (!node.previousSibling && node.parentNode != top)
+      node = node.parentNode;
+    return topLevelNodeAt(node.previousSibling, top);
+  }
+
+  var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
+
+  select.scrollToNode = function(element) {
+    if (!element) return;
+    var doc = element.ownerDocument, body = doc.body,
+        win = (doc.defaultView || doc.parentWindow),
+        html = doc.documentElement,
+        atEnd = !element.nextSibling || !element.nextSibling.nextSibling
+                || !element.nextSibling.nextSibling.nextSibling;
+    // In Opera (and recent Webkit versions), BR elements *always*
+    // have a scrollTop property of zero.
+    var compensateHack = 0;
+    while (element && !element.offsetTop) {
+      compensateHack++;
+      element = element.previousSibling;
+    }
+    // atEnd is another kludge for these browsers -- if the cursor is
+    // at the end of the document, and the node doesn't have an
+    // offset, just scroll to the end.
+    if (compensateHack == 0) atEnd = false;
+
+    var y = compensateHack * (element ? element.offsetHeight : 0), x = 0, pos = element;
+    while (pos && pos.offsetParent) {
+      y += pos.offsetTop;
+      // Don't count X offset for <br> nodes
+      if (!isBR(pos))
+        x += pos.offsetLeft;
+      pos = pos.offsetParent;
+    }
+
+    var scroll_x = body.scrollLeft || html.scrollLeft || 0,
+        scroll_y = body.scrollTop || html.scrollTop || 0,
+        screen_x = x - scroll_x, screen_y = y - scroll_y, scroll = false;
+
+    if (screen_x < 0 || screen_x > (win.innerWidth || html.clientWidth || 0)) {
+      scroll_x = x;
+      scroll = true;
+    }
+    if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) {
+      scroll_y = atEnd ? 1e10 : y;
+      scroll = true;
+    }
+    if (scroll) win.scrollTo(scroll_x, scroll_y);
+  };
+
+  select.scrollToCursor = function(container) {
+    select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild);
+  };
+
+  // Used to prevent restoring a selection when we do not need to.
+  var currentSelection = null;
+
+  select.snapshotChanged = function() {
+    if (currentSelection) currentSelection.changed = true;
+  };
+
+  // This is called by the code in editor.js whenever it is replacing
+  // a text node. The function sees whether the given oldNode is part
+  // of the current selection, and updates this selection if it is.
+  // Because nodes are often only partially replaced, the length of
+  // the part that gets replaced has to be taken into account -- the
+  // selection might stay in the oldNode if the newNode is smaller
+  // than the selection's offset. The offset argument is needed in
+  // case the selection does move to the new object, and the given
+  // length is not the whole length of the new node (part of it might
+  // have been used to replace another node).
+  select.snapshotReplaceNode = function(from, to, length, offset) {
+    if (!currentSelection) return;
+
+    function replace(point) {
+      if (from == point.node) {
+        currentSelection.changed = true;
+        if (length && point.offset > length) {
+          point.offset -= length;
+        }
+        else {
+          point.node = to;
+          point.offset += (offset || 0);
+        }
+      }
+    }
+    replace(currentSelection.start);
+    replace(currentSelection.end);
+  };
+
+  select.snapshotMove = function(from, to, distance, relative, ifAtStart) {
+    if (!currentSelection) return;
+
+    function move(point) {
+      if (from == point.node && (!ifAtStart || point.offset == 0)) {
+        currentSelection.changed = true;
+        point.node = to;
+        if (relative) point.offset = Math.max(0, point.offset + distance);
+        else point.offset = distance;
+      }
+    }
+    move(currentSelection.start);
+    move(currentSelection.end);
+  };
+
+  // Most functions are defined in two ways, one for the IE selection
+  // model, one for the W3C one.
+  if (select.ie_selection) {
+    function selectionNode(win, start) {
+      var range = win.document.selection.createRange();
+      range.collapse(start);
+
+      function nodeAfter(node) {
+        var found = null;
+        while (!found && node) {
+          found = node.nextSibling;
+          node = node.parentNode;
+        }
+        return nodeAtStartOf(found);
+      }
+
+      function nodeAtStartOf(node) {
+        while (node && node.firstChild) node = node.firstChild;
+        return {node: node, offset: 0};
+      }
+
+      var containing = range.parentElement();
+      if (!isAncestor(win.document.body, containing)) return null;
+      if (!containing.firstChild) return nodeAtStartOf(containing);
+
+      var working = range.duplicate();
+      working.moveToElementText(containing);
+      working.collapse(true);
+      for (var cur = containing.firstChild; cur; cur = cur.nextSibling) {
+        if (cur.nodeType == 3) {
+          var size = cur.nodeValue.length;
+          working.move("character", size);
+        }
+        else {
+          working.moveToElementText(cur);
+          working.collapse(false);
+        }
+
+        var dir = range.compareEndPoints("StartToStart", working);
+        if (dir == 0) return nodeAfter(cur);
+        if (dir == 1) continue;
+        if (cur.nodeType != 3) return nodeAtStartOf(cur);
+
+        working.setEndPoint("StartToEnd", range);
+        return {node: cur, offset: size - working.text.length};
+      }
+      return nodeAfter(containing);
+    }
+
+    select.markSelection = function(win) {
+      currentSelection = null;
+      var sel = win.document.selection;
+      if (!sel) return;
+      var start = selectionNode(win, true),
+          end = selectionNode(win, false);
+      if (!start || !end) return;
+      currentSelection = {start: start, end: end, window: win, changed: false};
+    };
+
+    select.selectMarked = function() {
+      if (!currentSelection || !currentSelection.changed) return;
+      var win = currentSelection.window, doc = win.document;
+
+      function makeRange(point) {
+        var range = doc.body.createTextRange(),
+            node = point.node;
+        if (!node) {
+          range.moveToElementText(currentSelection.window.document.body);
+          range.collapse(false);
+        }
+        else if (node.nodeType == 3) {
+          range.moveToElementText(node.parentNode);
+          var offset = point.offset;
+          while (node.previousSibling) {
+            node = node.previousSibling;
+            offset += (node.innerText || "").length;
+          }
+          range.move("character", offset);
+        }
+        else {
+          range.moveToElementText(node);
+          range.collapse(true);
+        }
+        return range;
+      }
+
+      var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end);
+      start.setEndPoint("StartToEnd", end);
+      start.select();
+    };
+
+    // Get the top-level node that one end of the cursor is inside or
+    // after. Note that this returns false for 'no cursor', and null
+    // for 'start of document'.
+    select.selectionTopNode = function(container, start) {
+      var selection = container.ownerDocument.selection;
+      if (!selection) return false;
+
+      var range = selection.createRange(), range2 = range.duplicate();
+      range.collapse(start);
+      var around = range.parentElement();
+      if (around && isAncestor(container, around)) {
+        // Only use this node if the selection is not at its start.
+        range2.moveToElementText(around);
+        if (range.compareEndPoints("StartToStart", range2) == 1)
+          return topLevelNodeAt(around, container);
+      }
+
+      // Move the start of a range to the start of a node,
+      // compensating for the fact that you can't call
+      // moveToElementText with text nodes.
+      function moveToNodeStart(range, node) {
+        if (node.nodeType == 3) {
+          var count = 0, cur = node.previousSibling;
+          while (cur && cur.nodeType == 3) {
+            count += cur.nodeValue.length;
+            cur = cur.previousSibling;
+          }
+          if (cur) {
+            try{range.moveToElementText(cur);}
+            catch(e){return false;}
+            range.collapse(false);
+          }
+          else range.moveToElementText(node.parentNode);
+          if (count) range.move("character", count);
+        }
+        else {
+          try{range.moveToElementText(node);}
+          catch(e){return false;}
+        }
+        return true;
+      }
+
+      // Do a binary search through the container object, comparing
+      // the start of each node to the selection
+      var start = 0, end = container.childNodes.length - 1;
+      while (start < end) {
+        var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
+        if (!node) return false; // Don't ask. IE6 manages this sometimes.
+        if (!moveToNodeStart(range2, node)) return false;
+        if (range.compareEndPoints("StartToStart", range2) == 1)
+          start = middle;
+        else
+          end = middle - 1;
+      }
+      return container.childNodes[start] || null;
+    };
+
+    // Place the cursor after this.start. This is only useful when
+    // manually moving the cursor instead of restoring it to its old
+    // position.
+    select.focusAfterNode = function(node, container) {
+      var range = container.ownerDocument.body.createTextRange();
+      range.moveToElementText(node || container);
+      range.collapse(!node);
+      range.select();
+    };
+
+    select.somethingSelected = function(win) {
+      var sel = win.document.selection;
+      return sel && (sel.createRange().text != "");
+    };
+
+    function insertAtCursor(window, html) {
+      var selection = window.document.selection;
+      if (selection) {
+        var range = selection.createRange();
+        range.pasteHTML(html);
+        range.collapse(false);
+        range.select();
+      }
+    }
+
+    // Used to normalize the effect of the enter key, since browsers
+    // do widely different things when pressing enter in designMode.
+    select.insertNewlineAtCursor = function(window) {
+      insertAtCursor(window, "<br>");
+    };
+
+    select.insertTabAtCursor = function(window) {
+      insertAtCursor(window, fourSpaces);
+    };
+
+    // Get the BR node at the start of the line on which the cursor
+    // currently is, and the offset into the line. Returns null as
+    // node if cursor is on first line.
+    select.cursorPos = function(container, start) {
+      var selection = container.ownerDocument.selection;
+      if (!selection) return null;
+
+      var topNode = select.selectionTopNode(container, start);
+      while (topNode && !isBR(topNode))
+        topNode = topNode.previousSibling;
+
+      var range = selection.createRange(), range2 = range.duplicate();
+      range.collapse(start);
+      if (topNode) {
+        range2.moveToElementText(topNode);
+        range2.collapse(false);
+      }
+      else {
+        // When nothing is selected, we can get all kinds of funky errors here.
+        try { range2.moveToElementText(container); }
+        catch (e) { return null; }
+        range2.collapse(true);
+      }
+      range.setEndPoint("StartToStart", range2);
+
+      return {node: topNode, offset: range.text.length};
+    };
+
+    select.setCursorPos = function(container, from, to) {
+      function rangeAt(pos) {
+        var range = container.ownerDocument.body.createTextRange();
+        if (!pos.node) {
+          range.moveToElementText(container);
+          range.collapse(true);
+        }
+        else {
+          range.moveToElementText(pos.node);
+          range.collapse(false);
+        }
+        range.move("character", pos.offset);
+        return range;
+      }
+
+      var range = rangeAt(from);
+      if (to && to != from)
+        range.setEndPoint("EndToEnd", rangeAt(to));
+      range.select();
+    }
+
+    // Some hacks for storing and re-storing the selection when the editor loses and regains focus.
+    select.selectionCoords = function (win) {
+      var selection = win.document.selection;
+      if (!selection) return null;
+      var start = selection.createRange(), end = start.duplicate();
+      start.collapse(true);
+      end.collapse(false);
+
+      var body = win.document.body;
+      return {start: {x: start.boundingLeft + body.scrollLeft - 1,
+                      y: start.boundingTop + body.scrollTop},
+              end: {x: end.boundingLeft + body.scrollLeft - 1,
+                    y: end.boundingTop + body.scrollTop}};
+    };
+
+    // Restore a stored selection.
+    select.selectCoords = function(win, coords) {
+      if (!coords) return;
+
+      var range1 = win.document.body.createTextRange(), range2 = range1.duplicate();
+      // This can fail for various hard-to-handle reasons.
+      try {
+        range1.moveToPoint(coords.start.x, coords.start.y);
+        range2.moveToPoint(coords.end.x, coords.end.y);
+        range1.setEndPoint("EndToStart", range2);
+        range1.select();
+      } catch(e) {}
+    };
+  }
+  // W3C model
+  else {
+    // Store start and end nodes, and offsets within these, and refer
+    // back to the selection object from those nodes, so that this
+    // object can be updated when the nodes are replaced before the
+    // selection is restored.
+    select.markSelection = function (win) {
+      var selection = win.getSelection();
+      if (!selection || selection.rangeCount == 0)
+        return (currentSelection = null);
+      var range = selection.getRangeAt(0);
+
+      currentSelection = {
+        start: {node: range.startContainer, offset: range.startOffset},
+        end: {node: range.endContainer, offset: range.endOffset},
+        window: win,
+        changed: false
+      };
+
+      // We want the nodes right at the cursor, not one of their
+      // ancestors with a suitable offset. This goes down the DOM tree
+      // until a 'leaf' is reached (or is it *up* the DOM tree?).
+      function normalize(point){
+        while (point.node.nodeType != 3 && !isBR(point.node)) {
+          var newNode = point.node.childNodes[point.offset] || point.node.nextSibling;
+          point.offset = 0;
+          while (!newNode && point.node.parentNode) {
+            point.node = point.node.parentNode;
+            newNode = point.node.nextSibling;
+          }
+          point.node = newNode;
+          if (!newNode)
+            break;
+        }
+      }
+
+      normalize(currentSelection.start);
+      normalize(currentSelection.end);
+    };
+
+    select.selectMarked = function () {
+      var cs = currentSelection;
+      if (!(cs && (cs.changed || (webkit && cs.start.node == cs.end.node)))) return;
+      var win = cs.window, range = win.document.createRange();
+
+      function setPoint(point, which) {
+        if (point.node) {
+          // Some magic to generalize the setting of the start and end
+          // of a range.
+          if (point.offset == 0)
+            range["set" + which + "Before"](point.node);
+          else
+            range["set" + which](point.node, point.offset);
+        }
+        else {
+          range.setStartAfter(win.document.body.lastChild || win.document.body);
+        }
+      }
+
+      setPoint(cs.end, "End");
+      setPoint(cs.start, "Start");
+      selectRange(range, win);
+    };
+
+    // Helper for selecting a range object.
+    function selectRange(range, window) {
+      var selection = window.getSelection();
+      selection.removeAllRanges();
+      selection.addRange(range);
+    };
+    function selectionRange(window) {
+      var selection = window.getSelection();
+      if (!selection || selection.rangeCount == 0)
+        return false;
+      else
+        return selection.getRangeAt(0);
+    }
+
+    // Finding the top-level node at the cursor in the W3C is, as you
+    // can see, quite an involved process.
+    select.selectionTopNode = function(container, start) {
+      var range = selectionRange(container.ownerDocument.defaultView);
+      if (!range) return false;
+
+      var node = start ? range.startContainer : range.endContainer;
+      var offset = start ? range.startOffset : range.endOffset;
+      // Work around (yet another) bug in Opera's selection model.
+      if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
+          container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
+        offset--;
+
+      // For text nodes, we look at the node itself if the cursor is
+      // inside, or at the node before it if the cursor is at the
+      // start.
+      if (node.nodeType == 3){
+        if (offset > 0)
+          return topLevelNodeAt(node, container);
+        else
+          return topLevelNodeBefore(node, container);
+      }
+      // Occasionally, browsers will return the HTML node as
+      // selection. If the offset is 0, we take the start of the frame
+      // ('after null'), otherwise, we take the last node.
+      else if (node.nodeName.toUpperCase() == "HTML") {
+        return (offset == 1 ? null : container.lastChild);
+      }
+      // If the given node is our 'container', we just look up the
+      // correct node by using the offset.
+      else if (node == container) {
+        return (offset == 0) ? null : node.childNodes[offset - 1];
+      }
+      // In any other case, we have a regular node. If the cursor is
+      // at the end of the node, we use the node itself, if it is at
+      // the start, we use the node before it, and in any other
+      // case, we look up the child before the cursor and use that.
+      else {
+        if (offset == node.childNodes.length)
+          return topLevelNodeAt(node, container);
+        else if (offset == 0)
+          return topLevelNodeBefore(node, container);
+        else
+          return topLevelNodeAt(node.childNodes[offset - 1], container);
+      }
+    };
+
+    select.focusAfterNode = function(node, container) {
+      var win = container.ownerDocument.defaultView,
+          range = win.document.createRange();
+      range.setStartBefore(container.firstChild || container);
+      // In Opera, setting the end of a range at the end of a line
+      // (before a BR) will cause the cursor to appear on the next
+      // line, so we set the end inside of the start node when
+      // possible.
+      if (node && !node.firstChild)
+        range.setEndAfter(node);
+      else if (node)
+        range.setEnd(node, node.childNodes.length);
+      else
+        range.setEndBefore(container.firstChild || container);
+      range.collapse(false);
+      selectRange(range, win);
+    };
+
+    select.somethingSelected = function(win) {
+      var range = selectionRange(win);
+      return range && !range.collapsed;
+    };
+
+    function insertNodeAtCursor(window, node) {
+      var range = selectionRange(window);
+      if (!range) return;
+
+      range.deleteContents();
+      range.insertNode(node);
+      webkitLastLineHack(window.document.body);
+      range = window.document.createRange();
+      range.selectNode(node);
+      range.collapse(false);
+      selectRange(range, window);
+    }
+
+    select.insertNewlineAtCursor = function(window) {
+      insertNodeAtCursor(window, window.document.createElement("BR"));
+    };
+
+    select.insertTabAtCursor = function(window) {
+      insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
+    };
+
+    select.cursorPos = function(container, start) {
+      var range = selectionRange(window);
+      if (!range) return;
+
+      var topNode = select.selectionTopNode(container, start);
+      while (topNode && !isBR(topNode))
+        topNode = topNode.previousSibling;
+
+      range = range.cloneRange();
+      range.collapse(start);
+      if (topNode)
+        range.setStartAfter(topNode);
+      else
+        range.setStartBefore(container);
+      return {node: topNode, offset: range.toString().length};
+    };
+
+    select.setCursorPos = function(container, from, to) {
+      var win = container.ownerDocument.defaultView,
+          range = win.document.createRange();
+
+      function setPoint(node, offset, side) {
+        if (!node)
+          node = container.firstChild;
+        else
+          node = node.nextSibling;
+
+        if (!node)
+          return;
+
+        if (offset == 0) {
+          range["set" + side + "Before"](node);
+          return true;
+        }
+
+        var backlog = []
+        function decompose(node) {
+          if (node.nodeType == 3)
+            backlog.push(node);
+          else
+            forEach(node.childNodes, decompose);
+        }
+        while (true) {
+          while (node && !backlog.length) {
+            decompose(node);
+            node = node.nextSibling;
+          }
+          var cur = backlog.shift();
+          if (!cur) return false;
+
+          var length = cur.nodeValue.length;
+          if (length >= offset) {
+            range["set" + side](cur, offset);
+            return true;
+          }
+          offset -= length;
+        }
+      }
+
+      to = to || from;
+      if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
+        selectRange(range, win);
+    };
+  }
+})();
diff --git a/platforma/static/js/lib/codemirror/stringstream.js b/platforma/static/js/lib/codemirror/stringstream.js
new file mode 100644 (file)
index 0000000..8c1c042
--- /dev/null
@@ -0,0 +1,140 @@
+/* String streams are the things fed to parsers (which can feed them
+ * to a tokenizer if they want). They provide peek and next methods
+ * for looking at the current character (next 'consumes' this
+ * character, peek does not), and a get method for retrieving all the
+ * text that was consumed since the last time get was called.
+ *
+ * An easy mistake to make is to let a StopIteration exception finish
+ * the token stream while there are still characters pending in the
+ * string stream (hitting the end of the buffer while parsing a
+ * token). To make it easier to detect such errors, the stringstreams
+ * throw an exception when this happens.
+ */
+
+// Make a stringstream stream out of an iterator that returns strings.
+// This is applied to the result of traverseDOM (see codemirror.js),
+// and the resulting stream is fed to the parser.
+window.stringStream = function(source){
+  // String that's currently being iterated over.
+  var current = "";
+  // Position in that string.
+  var pos = 0;
+  // Accumulator for strings that have been iterated over but not
+  // get()-ed yet.
+  var accum = "";
+  // Make sure there are more characters ready, or throw
+  // StopIteration.
+  function ensureChars() {
+    while (pos == current.length) {
+      accum += current;
+      current = ""; // In case source.next() throws
+      pos = 0;
+      try {current = source.next();}
+      catch (e) {
+        if (e != StopIteration) throw e;
+        else return false;
+      }
+    }
+    return true;
+  }
+
+  return {
+    // Return the next character in the stream.
+    peek: function() {
+      if (!ensureChars()) return null;
+      return current.charAt(pos);
+    },
+    // Get the next character, throw StopIteration if at end, check
+    // for unused content.
+    next: function() {
+      if (!ensureChars()) {
+        if (accum.length > 0)
+          throw "End of stringstream reached without emptying buffer ('" + accum + "').";
+        else
+          throw StopIteration;
+      }
+      return current.charAt(pos++);
+    },
+    // Return the characters iterated over since the last call to
+    // .get().
+    get: function() {
+      var temp = accum;
+      accum = "";
+      if (pos > 0){
+        temp += current.slice(0, pos);
+        current = current.slice(pos);
+        pos = 0;
+      }
+      return temp;
+    },
+    // Push a string back into the stream.
+    push: function(str) {
+      current = current.slice(0, pos) + str + current.slice(pos);
+    },
+    lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
+      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
+      str = cased(str);
+      var found = false;
+
+      var _accum = accum, _pos = pos;
+      if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
+
+      while (true) {
+        var end = pos + str.length, left = current.length - pos;
+        if (end <= current.length) {
+          found = str == cased(current.slice(pos, end));
+          pos = end;
+          break;
+        }
+        else if (str.slice(0, left) == cased(current.slice(pos))) {
+          accum += current; current = "";
+          try {current = source.next();}
+          catch (e) {break;}
+          pos = 0;
+          str = str.slice(left);
+        }
+        else {
+          break;
+        }
+      }
+
+      if (!(found && consume)) {
+        current = accum.slice(_accum.length) + current;
+        pos = _pos;
+        accum = _accum;
+      }
+
+      return found;
+    },
+
+    // Utils built on top of the above
+    more: function() {
+      return this.peek() !== null;
+    },
+    applies: function(test) {
+      var next = this.peek();
+      return (next !== null && test(next));
+    },
+    nextWhile: function(test) {
+      var next;
+      while ((next = this.peek()) !== null && test(next))
+        this.next();
+    },
+    matches: function(re) {
+      var next = this.peek();
+      return (next !== null && re.test(next));
+    },
+    nextWhileMatches: function(re) {
+      var next;
+      while ((next = this.peek()) !== null && re.test(next))
+        this.next();
+    },
+    equals: function(ch) {
+      return ch === this.peek();
+    },
+    endOfLine: function() {
+      var next = this.peek();
+      return next == null || next == "\n";
+    }
+  };
+};
diff --git a/platforma/static/js/lib/codemirror/tokenize.js b/platforma/static/js/lib/codemirror/tokenize.js
new file mode 100644 (file)
index 0000000..071970c
--- /dev/null
@@ -0,0 +1,57 @@
+// A framework for simple tokenizers. Takes care of newlines and
+// white-space, and of getting the text from the source stream into
+// the token object. A state is a function of two arguments -- a
+// string stream and a setState function. The second can be used to
+// change the tokenizer's state, and can be ignored for stateless
+// tokenizers. This function should advance the stream over a token
+// and return a string or object containing information about the next
+// token, or null to pass and have the (new) state be called to finish
+// the token. When a string is given, it is wrapped in a {style, type}
+// object. In the resulting object, the characters consumed are stored
+// under the content property. Any whitespace following them is also
+// automatically consumed, and added to the value property. (Thus,
+// content is the actual meaningful part of the token, while value
+// contains all the text it spans.)
+
+function tokenizer(source, state) {
+  // Newlines are always a separate token.
+  function isWhiteSpace(ch) {
+    // The messy regexp is because IE's regexp matcher is of the
+    // opinion that non-breaking spaces are no whitespace.
+    return ch != "\n" && /^[\s\u00a0]*$/.test(ch);
+  }
+
+  var tokenizer = {
+    state: state,
+
+    take: function(type) {
+      if (typeof(type) == "string")
+        type = {style: type, type: type};
+
+      type.content = (type.content || "") + source.get();
+      if (!/\n$/.test(type.content))
+        source.nextWhile(isWhiteSpace);
+      type.value = type.content + source.get();
+      return type;
+    },
+
+    next: function () {
+      if (!source.more()) throw StopIteration;
+
+      var type;
+      if (source.equals("\n")) {
+        source.next();
+        return this.take("whitespace");
+      }
+      
+      if (source.applies(isWhiteSpace))
+        type = "whitespace";
+      else
+        while (!type)
+          type = this.state(source, function(s) {tokenizer.state = s;});
+
+      return this.take(type);
+    }
+  };
+  return tokenizer;
+}
diff --git a/platforma/static/js/lib/codemirror/undo.js b/platforma/static/js/lib/codemirror/undo.js
new file mode 100644 (file)
index 0000000..97daf59
--- /dev/null
@@ -0,0 +1,403 @@
+/**
+ * Storage and control for undo information within a CodeMirror
+ * editor. 'Why on earth is such a complicated mess required for
+ * that?', I hear you ask. The goal, in implementing this, was to make
+ * the complexity of storing and reverting undo information depend
+ * only on the size of the edited or restored content, not on the size
+ * of the whole document. This makes it necessary to use a kind of
+ * 'diff' system, which, when applied to a DOM tree, causes some
+ * complexity and hackery.
+ *
+ * In short, the editor 'touches' BR elements as it parses them, and
+ * the History stores these. When nothing is touched in commitDelay
+ * milliseconds, the changes are committed: It goes over all touched
+ * nodes, throws out the ones that did not change since last commit or
+ * are no longer in the document, and assembles the rest into zero or
+ * more 'chains' -- arrays of adjacent lines. Links back to these
+ * chains are added to the BR nodes, while the chain that previously
+ * spanned these nodes is added to the undo history. Undoing a change
+ * means taking such a chain off the undo history, restoring its
+ * content (text is saved per line) and linking it back into the
+ * document.
+ */
+
+// A history object needs to know about the DOM container holding the
+// document, the maximum amount of undo levels it should store, the
+// delay (of no input) after which it commits a set of changes, and,
+// unfortunately, the 'parent' window -- a window that is not in
+// designMode, and on which setTimeout works in every browser.
+function History(container, maxDepth, commitDelay, editor, onChange) {
+  this.container = container;
+  this.maxDepth = maxDepth; this.commitDelay = commitDelay;
+  this.editor = editor; this.parent = editor.parent;
+  this.onChange = onChange;
+  // This line object represents the initial, empty editor.
+  var initial = {text: "", from: null, to: null};
+  // As the borders between lines are represented by BR elements, the
+  // start of the first line and the end of the last one are
+  // represented by null. Since you can not store any properties
+  // (links to line objects) in null, these properties are used in
+  // those cases.
+  this.first = initial; this.last = initial;
+  // Similarly, a 'historyTouched' property is added to the BR in
+  // front of lines that have already been touched, and 'firstTouched'
+  // is used for the first line.
+  this.firstTouched = false;
+  // History is the set of committed changes, touched is the set of
+  // nodes touched since the last commit.
+  this.history = []; this.redoHistory = []; this.touched = [];
+}
+
+History.prototype = {
+  // Schedule a commit (if no other touches come in for commitDelay
+  // milliseconds).
+  scheduleCommit: function() {
+    var self = this;
+    this.parent.clearTimeout(this.commitTimeout);
+    this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
+  },
+
+  // Mark a node as touched. Null is a valid argument.
+  touch: function(node) {
+    this.setTouched(node);
+    this.scheduleCommit();
+  },
+
+  // Undo the last change.
+  undo: function() {
+    // Make sure pending changes have been committed.
+    this.commit();
+
+    if (this.history.length) {
+      // Take the top diff from the history, apply it, and store its
+      // shadow in the redo history.
+      var item = this.history.pop();
+      this.redoHistory.push(this.updateTo(item, "applyChain"));
+      if (this.onChange) this.onChange();
+      return this.chainNode(item);
+    }
+  },
+
+  // Redo the last undone change.
+  redo: function() {
+    this.commit();
+    if (this.redoHistory.length) {
+      // The inverse of undo, basically.
+      var item = this.redoHistory.pop();
+      this.addUndoLevel(this.updateTo(item, "applyChain"));
+      if (this.onChange) this.onChange();
+      return this.chainNode(item);
+    }
+  },
+
+  clear: function() {
+    this.history = [];
+    this.redoHistory = [];
+  },
+
+  // Ask for the size of the un/redo histories.
+  historySize: function() {
+    return {undo: this.history.length, redo: this.redoHistory.length};
+  },
+
+  // Push a changeset into the document.
+  push: function(from, to, lines) {
+    var chain = [];
+    for (var i = 0; i < lines.length; i++) {
+      var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
+      chain.push({from: from, to: end, text: cleanText(lines[i])});
+      from = end;
+    }
+    this.pushChains([chain], from == null && to == null);
+  },
+
+  pushChains: function(chains, doNotHighlight) {
+    this.commit(doNotHighlight);
+    this.addUndoLevel(this.updateTo(chains, "applyChain"));
+    this.redoHistory = [];
+  },
+
+  // Retrieve a DOM node from a chain (for scrolling to it after undo/redo).
+  chainNode: function(chains) {
+    for (var i = 0; i < chains.length; i++) {
+      var start = chains[i][0], node = start && (start.from || start.to);
+      if (node) return node;
+    }
+  },
+
+  // Clear the undo history, make the current document the start
+  // position.
+  reset: function() {
+    this.history = []; this.redoHistory = [];
+  },
+
+  textAfter: function(br) {
+    return this.after(br).text;
+  },
+
+  nodeAfter: function(br) {
+    return this.after(br).to;
+  },
+
+  nodeBefore: function(br) {
+    return this.before(br).from;
+  },
+
+  // Commit unless there are pending dirty nodes.
+  tryCommit: function() {
+    if (!window.History) return; // Stop when frame has been unloaded
+    if (this.editor.highlightDirty()) this.commit(true);
+    else this.scheduleCommit();
+  },
+
+  // Check whether the touched nodes hold any changes, if so, commit
+  // them.
+  commit: function(doNotHighlight) {
+    this.parent.clearTimeout(this.commitTimeout);
+    // Make sure there are no pending dirty nodes.
+    if (!doNotHighlight) this.editor.highlightDirty(true);
+    // Build set of chains.
+    var chains = this.touchedChains(), self = this;
+
+    if (chains.length) {
+      this.addUndoLevel(this.updateTo(chains, "linkChain"));
+      this.redoHistory = [];
+      if (this.onChange) this.onChange();
+    }
+  },
+
+  // [ end of public interface ]
+
+  // Update the document with a given set of chains, return its
+  // shadow. updateFunc should be "applyChain" or "linkChain". In the
+  // second case, the chains are taken to correspond the the current
+  // document, and only the state of the line data is updated. In the
+  // first case, the content of the chains is also pushed iinto the
+  // document.
+  updateTo: function(chains, updateFunc) {
+    var shadows = [], dirty = [];
+    for (var i = 0; i < chains.length; i++) {
+      shadows.push(this.shadowChain(chains[i]));
+      dirty.push(this[updateFunc](chains[i]));
+    }
+    if (updateFunc == "applyChain")
+      this.notifyDirty(dirty);
+    return shadows;
+  },
+
+  // Notify the editor that some nodes have changed.
+  notifyDirty: function(nodes) {
+    forEach(nodes, method(this.editor, "addDirtyNode"))
+    this.editor.scheduleHighlight();
+  },
+
+  // Link a chain into the DOM nodes (or the first/last links for null
+  // nodes).
+  linkChain: function(chain) {
+    for (var i = 0; i < chain.length; i++) {
+      var line = chain[i];
+      if (line.from) line.from.historyAfter = line;
+      else this.first = line;
+      if (line.to) line.to.historyBefore = line;
+      else this.last = line;
+    }
+  },
+
+  // Get the line object after/before a given node.
+  after: function(node) {
+    return node ? node.historyAfter : this.first;
+  },
+  before: function(node) {
+    return node ? node.historyBefore : this.last;
+  },
+
+  // Mark a node as touched if it has not already been marked.
+  setTouched: function(node) {
+    if (node) {
+      if (!node.historyTouched) {
+        this.touched.push(node);
+        node.historyTouched = true;
+      }
+    }
+    else {
+      this.firstTouched = true;
+    }
+  },
+
+  // Store a new set of undo info, throw away info if there is more of
+  // it than allowed.
+  addUndoLevel: function(diffs) {
+    this.history.push(diffs);
+    if (this.history.length > this.maxDepth)
+      this.history.shift();
+  },
+
+  // Build chains from a set of touched nodes.
+  touchedChains: function() {
+    var self = this;
+
+    // The temp system is a crummy hack to speed up determining
+    // whether a (currently touched) node has a line object associated
+    // with it. nullTemp is used to store the object for the first
+    // line, other nodes get it stored in their historyTemp property.
+    var nullTemp = null;
+    function temp(node) {return node ? node.historyTemp : nullTemp;}
+    function setTemp(node, line) {
+      if (node) node.historyTemp = line;
+      else nullTemp = line;
+    }
+
+    function buildLine(node) {
+      var text = [];
+      for (var cur = node ? node.nextSibling : self.container.firstChild;
+           cur && !isBR(cur); cur = cur.nextSibling)
+        if (cur.currentText) text.push(cur.currentText);
+      return {from: node, to: cur, text: cleanText(text.join(""))};
+    }
+
+    // Filter out unchanged lines and nodes that are no longer in the
+    // document. Build up line objects for remaining nodes.
+    var lines = [];
+    if (self.firstTouched) self.touched.push(null);
+    forEach(self.touched, function(node) {
+      if (node && node.parentNode != self.container) return;
+
+      if (node) node.historyTouched = false;
+      else self.firstTouched = false;
+
+      var line = buildLine(node), shadow = self.after(node);
+      if (!shadow || shadow.text != line.text || shadow.to != line.to) {
+        lines.push(line);
+        setTemp(node, line);
+      }
+    });
+
+    // Get the BR element after/before the given node.
+    function nextBR(node, dir) {
+      var link = dir + "Sibling", search = node[link];
+      while (search && !isBR(search))
+        search = search[link];
+      return search;
+    }
+
+    // Assemble line objects into chains by scanning the DOM tree
+    // around them.
+    var chains = []; self.touched = [];
+    forEach(lines, function(line) {
+      // Note that this makes the loop skip line objects that have
+      // been pulled into chains by lines before them.
+      if (!temp(line.from)) return;
+
+      var chain = [], curNode = line.from, safe = true;
+      // Put any line objects (referred to by temp info) before this
+      // one on the front of the array.
+      while (true) {
+        var curLine = temp(curNode);
+        if (!curLine) {
+          if (safe) break;
+          else curLine = buildLine(curNode);
+        }
+        chain.unshift(curLine);
+        setTemp(curNode, null);
+        if (!curNode) break;
+        safe = self.after(curNode);
+        curNode = nextBR(curNode, "previous");
+      }
+      curNode = line.to; safe = self.before(line.from);
+      // Add lines after this one at end of array.
+      while (true) {
+        if (!curNode) break;
+        var curLine = temp(curNode);
+        if (!curLine) {
+          if (safe) break;
+          else curLine = buildLine(curNode);
+        }
+        chain.push(curLine);
+        setTemp(curNode, null);
+        safe = self.before(curNode);
+        curNode = nextBR(curNode, "next");
+      }
+      chains.push(chain);
+    });
+
+    return chains;
+  },
+
+  // Find the 'shadow' of a given chain by following the links in the
+  // DOM nodes at its start and end.
+  shadowChain: function(chain) {
+    var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
+    while (true) {
+      shadows.push(next);
+      var nextNode = next.to;
+      if (!nextNode || nextNode == end)
+        break;
+      else
+        next = nextNode.historyAfter || this.before(end);
+      // (The this.before(end) is a hack -- FF sometimes removes
+      // properties from BR nodes, in which case the best we can hope
+      // for is to not break.)
+    }
+    return shadows;
+  },
+
+  // Update the DOM tree to contain the lines specified in a given
+  // chain, link this chain into the DOM nodes.
+  applyChain: function(chain) {
+    // Some attempt is made to prevent the cursor from jumping
+    // randomly when an undo or redo happens. It still behaves a bit
+    // strange sometimes.
+    var cursor = select.cursorPos(this.container, false), self = this;
+
+    // Remove all nodes in the DOM tree between from and to (null for
+    // start/end of container).
+    function removeRange(from, to) {
+      var pos = from ? from.nextSibling : self.container.firstChild;
+      while (pos != to) {
+        var temp = pos.nextSibling;
+        removeElement(pos);
+        pos = temp;
+      }
+    }
+
+    var start = chain[0].from, end = chain[chain.length - 1].to;
+    // Clear the space where this change has to be made.
+    removeRange(start, end);
+
+    // Insert the content specified by the chain into the DOM tree.
+    for (var i = 0; i < chain.length; i++) {
+      var line = chain[i];
+      // The start and end of the space are already correct, but BR
+      // tags inside it have to be put back.
+      if (i > 0)
+        self.container.insertBefore(line.from, end);
+
+      // Add the text.
+      var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument);
+      self.container.insertBefore(node, end);
+      // See if the cursor was on this line. Put it back, adjusting
+      // for changed line length, if it was.
+      if (cursor && cursor.node == line.from) {
+        var cursordiff = 0;
+        var prev = this.after(line.from);
+        if (prev && i == chain.length - 1) {
+          // Only adjust if the cursor is after the unchanged part of
+          // the line.
+          for (var match = 0; match < cursor.offset &&
+               line.text.charAt(match) == prev.text.charAt(match); match++);
+          if (cursor.offset > match)
+            cursordiff = line.text.length - prev.text.length;
+        }
+        select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
+      }
+      // Cursor was in removed line, this is last new line.
+      else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
+        select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
+      }
+    }
+
+    // Anchor the chain in the DOM tree.
+    this.linkChain(chain);
+    return start;
+  }
+};
diff --git a/platforma/static/js/lib/codemirror/util.js b/platforma/static/js/lib/codemirror/util.js
new file mode 100644 (file)
index 0000000..0cd91d4
--- /dev/null
@@ -0,0 +1,134 @@
+/* A few useful utility functions. */
+
+var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
+var webkit = /AppleWebKit/.test(navigator.userAgent);
+var safari = /Apple Computers, Inc/.test(navigator.vendor);
+
+// Capture a method on an object.
+function method(obj, name) {
+  return function() {obj[name].apply(obj, arguments);};
+}
+
+// The value used to signal the end of a sequence in iterators.
+var StopIteration = {toString: function() {return "StopIteration"}};
+
+// Apply a function to each element in a sequence.
+function forEach(iter, f) {
+  if (iter.next) {
+    try {while (true) f(iter.next());}
+    catch (e) {if (e != StopIteration) throw e;}
+  }
+  else {
+    for (var i = 0; i < iter.length; i++)
+      f(iter[i]);
+  }
+}
+
+// Map a function over a sequence, producing an array of results.
+function map(iter, f) {
+  var accum = [];
+  forEach(iter, function(val) {accum.push(f(val));});
+  return accum;
+}
+
+// Create a predicate function that tests a string againsts a given
+// regular expression. No longer used but might be used by 3rd party
+// parsers.
+function matcher(regexp){
+  return function(value){return regexp.test(value);};
+}
+
+// Test whether a DOM node has a certain CSS class. Much faster than
+// the MochiKit equivalent, for some reason.
+function hasClass(element, className){
+  var classes = element.className;
+  return classes && new RegExp("(^| )" + className + "($| )").test(classes);
+}
+
+// Insert a DOM node after another node.
+function insertAfter(newNode, oldNode) {
+  var parent = oldNode.parentNode;
+  parent.insertBefore(newNode, oldNode.nextSibling);
+  return newNode;
+}
+
+function removeElement(node) {
+  if (node.parentNode)
+    node.parentNode.removeChild(node);
+}
+
+function clearElement(node) {
+  while (node.firstChild)
+    node.removeChild(node.firstChild);
+}
+
+// Check whether a node is contained in another one.
+function isAncestor(node, child) {
+  while (child = child.parentNode) {
+    if (node == child)
+      return true;
+  }
+  return false;
+}
+
+// The non-breaking space character.
+var nbsp = "\u00a0";
+var matching = {"{": "}", "[": "]", "(": ")",
+                "}": "{", "]": "[", ")": "("};
+
+// Standardize a few unportable event properties.
+function normalizeEvent(event) {
+  if (!event.stopPropagation) {
+    event.stopPropagation = function() {this.cancelBubble = true;};
+    event.preventDefault = function() {this.returnValue = false;};
+  }
+  if (!event.stop) {
+    event.stop = function() {
+      this.stopPropagation();
+      this.preventDefault();
+    };
+  }
+
+  if (event.type == "keypress") {
+    event.code = (event.charCode == null) ? event.keyCode : event.charCode;
+    event.character = String.fromCharCode(event.code);
+  }
+  return event;
+}
+
+// Portably register event handlers.
+function addEventHandler(node, type, handler, removeFunc) {
+  function wrapHandler(event) {
+    handler(normalizeEvent(event || window.event));
+  }
+  if (typeof node.addEventListener == "function") {
+    node.addEventListener(type, wrapHandler, false);
+    if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);};
+  }
+  else {
+    node.attachEvent("on" + type, wrapHandler);
+    if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);};
+  }
+}
+
+function nodeText(node) {
+  return node.textContent || node.innerText || node.nodeValue || "";
+}
+
+function nodeTop(node) {
+  var top = 0;
+  while (node.offsetParent) {
+    top += node.offsetTop;
+    node = node.offsetParent;
+  }
+  return top;
+}
+
+function isBR(node) {
+  var nn = node.nodeName;
+  return nn == "BR" || nn == "br";
+}
+function isSpan(node) {
+  var nn = node.nodeName;
+  return nn == "SPAN" || nn == "span";
+}
diff --git a/platforma/static/js/lib/jquery.cookie.js b/platforma/static/js/lib/jquery.cookie.js
new file mode 100644 (file)
index 0000000..6df1fac
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Cookie plugin
+ *
+ * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ */
+
+/**
+ * Create a cookie with the given name and value and other optional parameters.
+ *
+ * @example $.cookie('the_cookie', 'the_value');
+ * @desc Set the value of a cookie.
+ * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
+ * @desc Create a cookie with all available options.
+ * @example $.cookie('the_cookie', 'the_value');
+ * @desc Create a session cookie.
+ * @example $.cookie('the_cookie', null);
+ * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
+ *       used when the cookie was set.
+ *
+ * @param String name The name of the cookie.
+ * @param String value The value of the cookie.
+ * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
+ * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
+ *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
+ *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
+ *                             when the the browser exits.
+ * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
+ * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
+ * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
+ *                        require a secure protocol (like HTTPS).
+ * @type undefined
+ *
+ * @name $.cookie
+ * @cat Plugins/Cookie
+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
+ */
+
+/**
+ * Get the value of a cookie with the given name.
+ *
+ * @example $.cookie('the_cookie');
+ * @desc Get the value of a cookie.
+ *
+ * @param String name The name of the cookie.
+ * @return The value of the cookie.
+ * @type String
+ *
+ * @name $.cookie
+ * @cat Plugins/Cookie
+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
+ */
+jQuery.cookie = function(name, value, options) {
+    if (typeof value != 'undefined') { // name and value given, set cookie
+        options = options || {};
+        if (value === null) {
+            value = '';
+            options.expires = -1;
+        }
+        var expires = '';
+        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
+            var date;
+            if (typeof options.expires == 'number') {
+                date = new Date();
+                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+            } else {
+                date = options.expires;
+            }
+            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
+        }
+        // CAUTION: Needed to parenthesize options.path and options.domain
+        // in the following expressions, otherwise they evaluate to undefined
+        // in the packed version for some reason...
+        var path = options.path ? '; path=' + (options.path) : '';
+        var domain = options.domain ? '; domain=' + (options.domain) : '';
+        var secure = options.secure ? '; secure' : '';
+        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
+    } else { // only name given, get cookie
+        var cookieValue = null;
+        if (document.cookie && document.cookie != '') {
+            var cookies = document.cookie.split(';');
+            for (var i = 0; i < cookies.length; i++) {
+                var cookie = jQuery.trim(cookies[i]);
+                // Does this cookie string begin with the name we want?
+                if (cookie.substring(0, name.length + 1) == (name + '=')) {
+                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+                    break;
+                }
+            }
+        }
+        return cookieValue;
+    }
+};
\ No newline at end of file
diff --git a/platforma/static/js/lib/jquery.hpanel.js b/platforma/static/js/lib/jquery.hpanel.js
new file mode 100644 (file)
index 0000000..1ad0d17
--- /dev/null
@@ -0,0 +1,94 @@
+(function($){
+    
+       /* behaviour */
+       $.hpanel = {
+        settings: {},
+               current_data: {},
+        resize_start: function(event, mydata) {
+                       $(document).bind('mousemove', mydata, $.hpanel.resize_changed).
+                               bind('mouseup', mydata, $.hpanel.resize_stop); 
+
+                       $('.panel-overlay', mydata.root).css('display', 'block');
+                       return false;
+               },
+       resize_changed: function(event) {
+                       var old_width = parseInt(event.data.overlay.css('width'));
+                       var delta = event.pageX + event.data.hotspot_x - old_width;
+                       event.data.overlay.css({'width': old_width + delta});
+
+                       if(event.data.overlay.next) {
+                               var left = parseInt(event.data.overlay.next.css('left'));
+                               event.data.overlay.next.css('left', left+delta);
+                       }
+
+            return false; 
+        },
+        resize_stop: function(event) {
+            $(document).unbind('mousemove', $.hpanel.resize_changed).unbind('mouseup', $.hpanel.resize_stop);
+                       // $('.panel-content', event.data.root).css('display', 'block');
+                       var overlays = $('.panel-content-overlay', event.data.root);
+                       $('.panel-content-overlay', event.data.root).each(function(i) {
+                               if( $(this).data('panel').hasClass('last-panel') )
+                                       $(this).data('panel').css({
+                                               'left': $(this).css('left'), 'right': $(this).css('right')}); 
+                               else
+                                       $(this).data('panel').css({
+                                               'left': $(this).css('left'), 'width': $(this).css('width')}); 
+                       });
+                       $('.panel-overlay', event.data.root).css('display', 'none');
+            $(event.data.root).trigger('stopResize');
+        }
+    };
+    
+    $.fn.makeHorizPanel = function(options) 
+       {
+               var root = $(this)
+
+               /* create an overlay */
+               var overlay_root = $("<div class='panel-overlay'></div>");
+               root.append(overlay_root);
+
+               var prev = null;
+
+               $('*.panel-wrap', root).each( function() 
+        {
+                       var panel = $(this);
+                       var handle = $('.panel-slider', panel);
+                       var overlay = $("<div class='panel-content-overlay panel-wrap'>&nbsp;</div>");
+                       overlay_root.append(overlay);
+                       overlay.data('panel', panel);
+                       overlay.data('next', null);
+
+                       if (prev) prev.next = overlay;
+
+                       if( panel.hasClass('last-panel') )                              
+            {
+                               overlay.css({'left': panel.css('left'), 'right': panel.css('right')});
+            }
+                       else {
+                               overlay.css({'left': panel.css('left'), 'width': panel.css('width')});
+                               $.log('Has handle: ' + panel.attr('id'));
+                               overlay.append(handle.clone());
+                               /* attach the trigger */
+                               handle.mousedown(function(event) {
+                                       var touch_data = {
+                                               root: root, overlay: overlay,
+                                               hotspot_x: event.pageX - handle.position().left
+                                       };
+
+                                       $(this).trigger('hpanel:panel-resize-start', touch_data);
+                                       return false;
+                               });
+                               $('.panel-content', panel).css('right', 
+                                       (handle.outerWidth() || 10) + 'px');
+                               $('.panel-content-overlay', panel).css('right',
+                                       (handle.outerWidth() || 10) + 'px');
+                       };                              
+
+                       prev = overlay;
+        });
+
+       root.bind('hpanel:panel-resize-start', $.hpanel.resize_start);
+    };
+})(jQuery);
+
diff --git a/platforma/static/js/lib/jquery.js b/platforma/static/js/lib/jquery.js
new file mode 100644 (file)
index 0000000..9263574
--- /dev/null
@@ -0,0 +1,4376 @@
+/*!
+ * 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 
+       // Will speed up references to window, and allows munging its name.
+       window = this,
+       // Will speed up references to undefined, and allows munging its name.
+       undefined,
+       // Map over jQuery in case of overwrite
+       _jQuery = window.jQuery,
+       // Map over the $ in case of overwrite
+       _$ = window.$,
+
+       jQuery = window.jQuery = window.$ = function( selector, context ) {
+               // The jQuery object is actually just the init constructor 'enhanced'
+               return new jQuery.fn.init( selector, context );
+       },
+
+       // A simple way to check for HTML strings or ID strings
+       // (both of which we optimize for)
+       quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\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] );
+
+                                       // Handle the case where IE and Opera return items
+                                       // by name instead of ID
+                                       if ( elem && 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;
+                               }
+
+                       // 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.isArray( selector ) ?
+                       selector :
+                       jQuery.makeArray(selector));
+       },
+
+       // Start with an empty selector
+       selector: "",
+
+       // The current version of jQuery being used
+       jquery: "1.3.2",
+
+       // 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
+                       Array.prototype.slice.call( 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 method, not like a jQuery method.
+       push: [].push,
+       sort: [].sort,
+       splice: [].splice,
+
+       find: function( selector ) {
+               if ( this.length === 1 ) {
+                       var ret = this.pushStack( [], "find", selector );
+                       ret.length = 0;
+                       jQuery.find( selector, this[0], ret );
+                       return ret;
+               } else {
+                       return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
+                               return jQuery.find( selector, elem );
+                       })), "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 html = this.outerHTML;
+                               if ( !html ) {
+                                       var div = this.ownerDocument.createElement("div");
+                                       div.appendChild( this.cloneNode(true) );
+                                       html = div.innerHTML;
+                               }
+
+                               return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
+                       } else
+                               return this.cloneNode(true);
+               });
+
+               // Copy the events from the original to the clone
+               if ( events === true ) {
+                       var orig = this.find("*").andSelf(), i = 0;
+
+                       ret.find("*").andSelf().each(function(){
+                               if ( this.nodeName !== orig[i].nodeName )
+                                       return;
+
+                               var events = jQuery.data( orig[i], "events" );
+
+                               for ( var type in events ) {
+                                       for ( var handler in events[ type ] ) {
+                                               jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+                                       }
+                               }
+
+                               i++;
+                       });
+               }
+
+               // 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,
+                       closer = 0;
+
+               return this.map(function(){
+                       var cur = this;
+                       while ( cur && cur.ownerDocument ) {
+                               if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
+                                       jQuery.data(cur, "closest", closer);
+                                       return cur;
+                               }
+                               cur = cur.parentNode;
+                               closer++;
+                       }
+               });
+       },
+
+       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.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
+                               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;
+
+                       if ( first )
+                               for ( var i = 0, l = this.length; i < l; i++ )
+                                       callback.call( root(this[i], first), this.length > 1 || i > 0 ?
+                                                       fragment.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.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+                       !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
+       },
+
+       // Evalulates a script in a global context
+       globalEval: function( data ) {
+               if ( data && /\S/.test(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 elem && 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, extra ) {
+               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;
+
+                               if ( extra === "border" )
+                                       return;
+
+                               jQuery.each( which, function() {
+                                       if ( !extra )
+                                               val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+                                       if ( extra === "margin" )
+                                               val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+                                       else
+                                               val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+                               });
+                       }
+
+                       if ( elem.offsetWidth !== 0 )
+                               getWH();
+                       else
+                               jQuery.swap( elem, props, getWH );
+
+                       return Math.max(0, Math.round(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 + "></" + tag + ">";
+                               });
+
+                               // Trim whitespace, otherwise indexOf won't work as expected
+                               var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
+
+                               var wrap =
+                                       // option or optgroup
+                                       !tags.indexOf("<opt") &&
+                                       [ 1, "<select multiple='multiple'>", "</select>" ] ||
+
+                                       !tags.indexOf("<leg") &&
+                                       [ 1, "<fieldset>", "</fieldset>" ] ||
+
+                                       tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
+                                       [ 1, "<table>", "</table>" ] ||
+
+                                       !tags.indexOf("<tr") &&
+                                       [ 2, "<table><tbody>", "</tbody></table>" ] ||
+
+                                       // <thead> matched above
+                                       (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
+                                       [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
+
+                                       !tags.indexOf("<col") &&
+                                       [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
+
+                                       // IE can't serialize <link> and <script> tags normally
+                                       !jQuery.support.htmlSerialize &&
+                                       [ 1, "div<div>", "</div>" ] ||
+
+                                       [ 0, "", "" ];
+
+                               // Go to html and back, then peel off extra wrappers
+                               div.innerHTML = wrap[1] + elem + wrap[2];
+
+                               // Move to the right depth
+                               while ( wrap[0]-- )
+                                       div = div.lastChild;
+
+                               // Remove IE's autoinserted <tbody> from table fragments
+                               if ( !jQuery.support.tbody ) {
+
+                                       // String was a <table>, *may* have spurious <tbody>
+                                       var hasBody = /<tbody/i.test(elem),
+                                               tbody = !tags.indexOf("<table") && !hasBody ?
+                                                       div.firstChild && div.firstChild.childNodes :
+
+                                               // String was a bare <thead> or <tfoot>
+                                               wrap[1] == "<table>" && !hasBody ?
+                                                       div.childNodes :
+                                                       [];
+
+                                       for ( var j = tbody.length - 1; j >= 0 ; --j )
+                                               if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
+                                                       tbody[ j ].parentNode.removeChild( tbody[ j ] );
+
+                                       }
+
+                               // IE completely kills leading whitespace when innerHTML is used
+                               if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
+                                       div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
+                               
+                               elem = jQuery.makeArray( div.childNodes );
+                       }
+
+                       if ( elem.nodeType )
+                               ret.push( elem );
+                       else
+                               ret = jQuery.merge( ret, elem );
+
+               });
+
+               if ( fragment ) {
+                       for ( var i = 0; ret[i]; i++ ) {
+                               if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+                                       scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+                               } else {
+                                       if ( ret[i].nodeType === 1 )
+                                               ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+                                       fragment.appendChild( ret[i] );
+                               }
+                       }
+                       
+                       return scripts;
+               }
+
+               return ret;
+       },
+
+       attr: function( elem, name, value ) {
+               // don't set attributes on text and comment nodes
+               if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
+                       return undefined;
+
+               var notxml = !jQuery.isXMLDoc( elem ),
+                       // Whether we are setting (or getting)
+                       set = value !== undefined;
+
+               // Try to normalize/fix the name
+               name = notxml && jQuery.props[ name ] || name;
+
+               // Only do all the following if this is a node (faster for style)
+               // IE elem.getAttribute passes even for style
+               if ( elem.tagName ) {
+
+                       // These attributes require special treatment
+                       var special = /href|src|style/.test( name );
+
+                       // Safari mis-reports the default selected property of a hidden option
+                       // Accessing the parent's selectedIndex property fixes it
+                       if ( name == "selected" && elem.parentNode )
+                               elem.parentNode.selectedIndex;
+
+                       // If applicable, access the attribute via the DOM 0 way
+                       if ( name in elem && notxml && !special ) {
+                               if ( set ){
+                                       // We can't allow the type property to be changed (since it causes problems in IE)
+                                       if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
+                                               throw "type property can't be changed";
+
+                                       elem[ name ] = value;
+                               }
+
+                               // browsers index elements by id/name on forms, give priority to attributes.
+                               if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
+                                       return elem.getAttributeNode( name ).nodeValue;
+
+                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+                               if ( name == "tabIndex" ) {
+                                       var attributeNode = elem.getAttributeNode( "tabIndex" );
+                                       return attributeNode && attributeNode.specified
+                                               ? attributeNode.value
+                                               : elem.nodeName.match(/(button|input|object|select|textarea)/i)
+                                                       ? 0
+                                                       : elem.nodeName.match(/^(a|area)$/i) && elem.href
+                                                               ? 0
+                                                               : undefined;
+                               }
+
+                               return elem[ name ];
+                       }
+
+                       if ( !jQuery.support.style && notxml &&  name == "style" )
+                               return jQuery.attr( elem.style, "cssText", value );
+
+                       if ( set )
+                               // convert the value to a string (all browsers do this but IE) see #1070
+                               elem.setAttribute( name, "" + value );
+
+                       var attr = !jQuery.support.hrefNormalized && notxml && special
+                                       // Some attributes require a special call on IE
+                                       ? elem.getAttribute( name, 2 )
+                                       : elem.getAttribute( name );
+
+                       // Non-existent attributes return null, we normalize to undefined
+                       return attr === null ? undefined : attr;
+               }
+
+               // elem is actually elem.style ... set the style
+
+               // IE uses filters for opacity
+               if ( !jQuery.support.opacity && name == "opacity" ) {
+                       if ( set ) {
+                               // IE has trouble with opacity if it does not have layout
+                               // Force it by setting the zoom level
+                               elem.zoom = 1;
+
+                               // Set the alpha filter to set the opacity
+                               elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
+                                       (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+                       }
+
+                       return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
+                               (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
+                               "";
+               }
+
+               name = name.replace(/-([a-z])/ig, function(all, letter){
+                       return letter.toUpperCase();
+               });
+
+               if ( set )
+                       elem[ name ] = value;
+
+               return elem[ name ];
+       },
+
+       trim: function( text ) {
+               return (text || "").replace( /^\s+|\s+$/g, "" );
+       },
+
+       makeArray: function( array ) {
+               var ret = [];
+
+               if( array != null ){
+                       var i = array.length;
+                       // The window, strings (and functions) also have 'length'
+                       if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
+                               ret[0] = array;
+                       else
+                               while( i )
+                                       ret[--i] = array[i];
+               }
+
+               return ret;
+       },
+
+       inArray: function( elem, array ) {
+               for ( var i = 0, length = array.length; i < length; i++ )
+               // Use === because on IE, window == document
+                       if ( array[ i ] === elem )
+                               return i;
+
+               return -1;
+       },
+
+       merge: function( first, second ) {
+               // We have to loop this way because IE & Opera overwrite the length
+               // expando of getElementsByTagName
+               var i = 0, elem, pos = first.length;
+               // Also, we need to make sure that the correct elements are being returned
+               // (IE returns comment nodes in a '*' query)
+               if ( !jQuery.support.getAll ) {
+                       while ( (elem = second[ i++ ]) != null )
+                               if ( elem.nodeType != 8 )
+                                       first[ pos++ ] = elem;
+
+               } else
+                       while ( (elem = second[ i++ ]) != null )
+                               first[ pos++ ] = elem;
+
+               return first;
+       },
+
+       unique: function( array ) {
+               var ret = [], done = {};
+
+               try {
+
+                       for ( var i = 0, length = array.length; i < length; i++ ) {
+                               var id = jQuery.data( array[ i ] );
+
+                               if ( !done[ id ] ) {
+                                       done[ id ] = true;
+                                       ret.push( array[ i ] );
+                               }
+                       }
+
+               } catch( e ) {
+                       ret = array;
+               }
+
+               return ret;
+       },
+
+       grep: function( elems, callback, inv ) {
+               var ret = [];
+
+               // Go through the array, only saving the items
+               // that pass the validator function
+               for ( var i = 0, length = elems.length; i < length; i++ )
+                       if ( !inv != !callback( elems[ i ], i ) )
+                               ret.push( elems[ i ] );
+
+               return ret;
+       },
+
+       map: function( elems, callback ) {
+               var ret = [];
+
+               // Go through the array, translating each of the items to their
+               // new value (or values).
+               for ( var i = 0, length = elems.length; i < length; i++ ) {
+                       var value = callback( elems[ i ], i );
+
+                       if ( value != null )
+                               ret[ ret.length ] = value;
+               }
+
+               return ret.concat.apply( [], ret );
+       }
+});
+
+// Use of jQuery.browser is deprecated.
+// It's included for backwards compatibility and plugins,
+// although they should work to migrate away.
+
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+       version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
+       safari: /webkit/.test( userAgent ),
+       opera: /opera/.test( userAgent ),
+       msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
+       mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
+};
+
+jQuery.each({
+       parent: function(elem){return elem.parentNode;},
+       parents: function(elem){return jQuery.dir(elem,"parentNode");},
+       next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
+       prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
+       nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
+       prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
+       siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
+       children: function(elem){return jQuery.sibling(elem.firstChild);},
+       contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
+}, function(name, fn){
+       jQuery.fn[ name ] = function( selector ) {
+               var ret = jQuery.map( this, fn );
+
+               if ( selector && typeof selector == "string" )
+                       ret = jQuery.multiFilter( selector, ret );
+
+               return this.pushStack( jQuery.unique( ret ), name, selector );
+       };
+});
+
+jQuery.each({
+       appendTo: "append",
+       prependTo: "prepend",
+       insertBefore: "before",
+       insertAfter: "after",
+       replaceAll: "replaceWith"
+}, function(name, original){
+       jQuery.fn[ name ] = function( selector ) {
+               var ret = [], insert = jQuery( selector );
+
+               for ( var i = 0, l = insert.length; i < l; i++ ) {
+                       var elems = (i > 0 ? this.clone(true) : this).get();
+                       jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+                       ret = ret.concat( elems );
+               }
+
+               return this.pushStack( ret, name, selector );
+       };
+});
+
+jQuery.each({
+       removeAttr: function( name ) {
+               jQuery.attr( this, name, "" );
+               if (this.nodeType == 1)
+                       this.removeAttribute( name );
+       },
+
+       addClass: function( classNames ) {
+               jQuery.className.add( this, classNames );
+       },
+
+       removeClass: function( classNames ) {
+               jQuery.className.remove( this, classNames );
+       },
+
+       toggleClass: function( classNames, state ) {
+               if( typeof state !== "boolean" )
+                       state = !jQuery.className.has( this, classNames );
+               jQuery.className[ state ? "add" : "remove" ]( this, classNames );
+       },
+
+       remove: function( selector ) {
+               if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
+                       // Prevent memory leaks
+                       jQuery( "*", this ).add([this]).each(function(){
+                               jQuery.event.remove(this);
+                               jQuery.removeData(this);
+                       });
+                       if (this.parentNode)
+                               this.parentNode.removeChild( this );
+               }
+       },
+
+       empty: function() {
+               // Remove element nodes and prevent memory leaks
+               jQuery(this).children().remove();
+
+               // Remove any remaining nodes
+               while ( this.firstChild )
+                       this.removeChild( this.firstChild );
+       }
+}, function(name, fn){
+       jQuery.fn[ name ] = function(){
+               return this.each( fn, arguments );
+       };
+});
+
+// Helper function used by the dimensions and offset modules
+function num(elem, prop) {
+       return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
+}
+var expando = "jQuery" + now(), uuid = 0, windowData = {};\r
+\r
+jQuery.extend({\r
+       cache: {},\r
+\r
+       data: function( elem, name, data ) {\r
+               elem = elem == window ?\r
+                       windowData :\r
+                       elem;\r
+\r
+               var id = elem[ expando ];\r
+\r
+               // Compute a unique ID for the element\r
+               if ( !id )\r
+                       id = elem[ expando ] = ++uuid;\r
+\r
+               // Only generate the data cache if we're\r
+               // trying to access or manipulate it\r
+               if ( name && !jQuery.cache[ id ] )\r
+                       jQuery.cache[ id ] = {};\r
+\r
+               // Prevent overriding the named cache with undefined values\r
+               if ( data !== undefined )\r
+                       jQuery.cache[ id ][ name ] = data;\r
+\r
+               // Return the named cache data, or the ID for the element\r
+               return name ?\r
+                       jQuery.cache[ id ][ name ] :\r
+                       id;\r
+       },\r
+\r
+       removeData: function( elem, name ) {\r
+               elem = elem == window ?\r
+                       windowData :\r
+                       elem;\r
+\r
+               var id = elem[ expando ];\r
+\r
+               // If we want to remove a specific section of the element's data\r
+               if ( name ) {\r
+                       if ( jQuery.cache[ id ] ) {\r
+                               // Remove the section of cache data\r
+                               delete jQuery.cache[ id ][ name ];\r
+\r
+                               // If we've removed all the data, remove the element's cache\r
+                               name = "";\r
+\r
+                               for ( name in jQuery.cache[ id ] )\r
+                                       break;\r
+\r
+                               if ( !name )\r
+                                       jQuery.removeData( elem );\r
+                       }\r
+\r
+               // Otherwise, we want to remove all of the element's data\r
+               } else {\r
+                       // Clean up the element expando\r
+                       try {\r
+                               delete elem[ expando ];\r
+                       } catch(e){\r
+                               // IE has trouble directly removing the expando\r
+                               // but it's ok with using removeAttribute\r
+                               if ( elem.removeAttribute )\r
+                                       elem.removeAttribute( expando );\r
+                       }\r
+\r
+                       // Completely remove the data cache\r
+                       delete jQuery.cache[ id ];\r
+               }\r
+       },\r
+       queue: function( elem, type, data ) {\r
+               if ( elem ){\r
+       \r
+                       type = (type || "fx") + "queue";\r
+       \r
+                       var q = jQuery.data( elem, type );\r
+       \r
+                       if ( !q || jQuery.isArray(data) )\r
+                               q = jQuery.data( elem, type, jQuery.makeArray(data) );\r
+                       else if( data )\r
+                               q.push( data );\r
+       \r
+               }\r
+               return q;\r
+       },\r
+\r
+       dequeue: function( elem, type ){\r
+               var queue = jQuery.queue( elem, type ),\r
+                       fn = queue.shift();\r
+               \r
+               if( !type || type === "fx" )\r
+                       fn = queue[0];\r
+                       \r
+               if( fn !== undefined )\r
+                       fn.call(elem);\r
+       }\r
+});\r
+\r
+jQuery.fn.extend({\r
+       data: function( key, value ){\r
+               var parts = key.split(".");\r
+               parts[1] = parts[1] ? "." + parts[1] : "";\r
+\r
+               if ( value === undefined ) {\r
+                       var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);\r
+\r
+                       if ( data === undefined && this.length )\r
+                               data = jQuery.data( this[0], key );\r
+\r
+                       return data === undefined && parts[1] ?\r
+                               this.data( parts[0] ) :\r
+                               data;\r
+               } else\r
+                       return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){\r
+                               jQuery.data( this, key, value );\r
+                       });\r
+       },\r
+\r
+       removeData: function( key ){\r
+               return this.each(function(){\r
+                       jQuery.removeData( this, key );\r
+               });\r
+       },\r
+       queue: function(type, data){\r
+               if ( typeof type !== "string" ) {\r
+                       data = type;\r
+                       type = "fx";\r
+               }\r
+\r
+               if ( data === undefined )\r
+                       return jQuery.queue( this[0], type );\r
+\r
+               return this.each(function(){\r
+                       var queue = jQuery.queue( this, type, data );\r
+                       \r
+                        if( type == "fx" && queue.length == 1 )\r
+                               queue[0].call(this);\r
+               });\r
+       },\r
+       dequeue: function(type){\r
+               return this.each(function(){\r
+                       jQuery.dequeue( this, type );\r
+               });\r
+       }\r
+});/*!
+ * 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 chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
+       done = 0,
+       toString = Object.prototype.toString;
+
+var Sizzle = function(selector, context, results, seed) {
+       results = results || [];
+       context = context || document;
+
+       if ( context.nodeType !== 1 && context.nodeType !== 9 )
+               return [];
+       
+       if ( !selector || typeof selector !== "string" ) {
+               return results;
+       }
+
+       var parts = [], m, set, checkSet, check, mode, extra, prune = true;
+       
+       // Reset the position of the chunker regexp (start from head)
+       chunker.lastIndex = 0;
+       
+       while ( (m = chunker.exec(selector)) !== null ) {
+               parts.push( m[1] );
+               
+               if ( m[2] ) {
+                       extra = RegExp.rightContext;
+                       break;
+               }
+       }
+
+       if ( parts.length > 1 && origPOS.exec( selector ) ) {
+               if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+                       set = posProcess( parts[0] + parts[1], context );
+               } else {
+                       set = Expr.relative[ parts[0] ] ?
+                               [ context ] :
+                               Sizzle( parts.shift(), context );
+
+                       while ( parts.length ) {
+                               selector = parts.shift();
+
+                               if ( Expr.relative[ selector ] )
+                                       selector += parts.shift();
+
+                               set = posProcess( selector, set );
+                       }
+               }
+       } else {
+               var ret = seed ?
+                       { expr: parts.pop(), set: makeArray(seed) } :
+                       Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
+               set = Sizzle.filter( ret.expr, ret.set );
+
+               if ( parts.length > 0 ) {
+                       checkSet = makeArray(set);
+               } else {
+                       prune = false;
+               }
+
+               while ( parts.length ) {
+                       var cur = parts.pop(), pop = cur;
+
+                       if ( !Expr.relative[ cur ] ) {
+                               cur = "";
+                       } else {
+                               pop = parts.pop();
+                       }
+
+                       if ( pop == null ) {
+                               pop = context;
+                       }
+
+                       Expr.relative[ cur ]( checkSet, pop, isXML(context) );
+               }
+       }
+
+       if ( !checkSet ) {
+               checkSet = set;
+       }
+
+       if ( !checkSet ) {
+               throw "Syntax error, unrecognized expression: " + (cur || selector);
+       }
+
+       if ( toString.call(checkSet) === "[object Array]" ) {
+               if ( !prune ) {
+                       results.push.apply( results, checkSet );
+               } else if ( context.nodeType === 1 ) {
+                       for ( var i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               } else {
+                       for ( var i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               }
+       } else {
+               makeArray( checkSet, results );
+       }
+
+       if ( extra ) {
+               Sizzle( extra, context, results, seed );
+
+               if ( sortOrder ) {
+                       hasDuplicate = false;
+                       results.sort(sortOrder);
+
+                       if ( hasDuplicate ) {
+                               for ( var i = 1; i < results.length; i++ ) {
+                                       if ( results[i] === results[i-1] ) {
+                                               results.splice(i--, 1);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return results;
+};
+
+Sizzle.matches = function(expr, set){
+       return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+       var set, match;
+
+       if ( !expr ) {
+               return [];
+       }
+
+       for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+               var type = Expr.order[i], match;
+               
+               if ( (match = Expr.match[ type ].exec( expr )) ) {
+                       var left = RegExp.leftContext;
+
+                       if ( left.substr( left.length - 1 ) !== "\\" ) {
+                               match[1] = (match[1] || "").replace(/\\/g, "");
+                               set = Expr.find[ type ]( match, context, isXML );
+                               if ( set != null ) {
+                                       expr = expr.replace( Expr.match[ type ], "" );
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if ( !set ) {
+               set = context.getElementsByTagName("*");
+       }
+
+       return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+       var old = expr, result = [], curLoop = set, match, anyFound,
+               isXMLFilter = set && set[0] && isXML(set[0]);
+
+       while ( expr && set.length ) {
+               for ( var type in Expr.filter ) {
+                       if ( (match = Expr.match[ type ].exec( expr )) != null ) {
+                               var filter = Expr.filter[ type ], found, item;
+                               anyFound = false;
+
+                               if ( curLoop == result ) {
+                                       result = [];
+                               }
+
+                               if ( Expr.preFilter[ type ] ) {
+                                       match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+                                       if ( !match ) {
+                                               anyFound = found = true;
+                                       } else if ( match === true ) {
+                                               continue;
+                                       }
+                               }
+
+                               if ( match ) {
+                                       for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+                                               if ( item ) {
+                                                       found = filter( item, match, i, curLoop );
+                                                       var pass = not ^ !!found;
+
+                                                       if ( inplace && found != null ) {
+                                                               if ( pass ) {
+                                                                       anyFound = true;
+                                                               } else {
+                                                                       curLoop[i] = false;
+                                                               }
+                                                       } else if ( pass ) {
+                                                               result.push( item );
+                                                               anyFound = true;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               if ( found !== undefined ) {
+                                       if ( !inplace ) {
+                                               curLoop = result;
+                                       }
+
+                                       expr = expr.replace( Expr.match[ type ], "" );
+
+                                       if ( !anyFound ) {
+                                               return [];
+                                       }
+
+                                       break;
+                               }
+                       }
+               }
+
+               // Improper expression
+               if ( expr == old ) {
+                       if ( anyFound == null ) {
+                               throw "Syntax error, unrecognized expression: " + expr;
+                       } else {
+                               break;
+                       }
+               }
+
+               old = expr;
+       }
+
+       return curLoop;
+};
+
+var Expr = Sizzle.selectors = {
+       order: [ "ID", "NAME", "TAG" ],
+       match: {
+               ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+               CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
+               NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
+               ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+               TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
+               CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+               POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+               PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
+       },
+       attrMap: {
+               "class": "className",
+               "for": "htmlFor"
+       },
+       attrHandle: {
+               href: function(elem){
+                       return elem.getAttribute("href");
+               }
+       },
+       relative: {
+               "+": function(checkSet, part, isXML){
+                       var isPartStr = typeof part === "string",
+                               isTag = isPartStr && !/\W/.test(part),
+                               isPartStrNotTag = isPartStr && !isTag;
+
+                       if ( isTag && !isXML ) {
+                               part = part.toUpperCase();
+                       }
+
+                       for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+                               if ( (elem = checkSet[i]) ) {
+                                       while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+                                       checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
+                                               elem || false :
+                                               elem === part;
+                               }
+                       }
+
+                       if ( isPartStrNotTag ) {
+                               Sizzle.filter( part, checkSet, true );
+                       }
+               },
+               ">": function(checkSet, part, isXML){
+                       var isPartStr = typeof part === "string";
+
+                       if ( isPartStr && !/\W/.test(part) ) {
+                               part = isXML ? part : part.toUpperCase();
+
+                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+                                       var elem = checkSet[i];
+                                       if ( elem ) {
+                                               var parent = elem.parentNode;
+                                               checkSet[i] = parent.nodeName === part ? parent : false;
+                                       }
+                               }
+                       } else {
+                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+                                       var elem = checkSet[i];
+                                       if ( elem ) {
+                                               checkSet[i] = isPartStr ?
+                                                       elem.parentNode :
+                                                       elem.parentNode === part;
+                                       }
+                               }
+
+                               if ( isPartStr ) {
+                                       Sizzle.filter( part, checkSet, true );
+                               }
+                       }
+               },
+               "": function(checkSet, part, isXML){
+                       var doneName = done++, checkFn = dirCheck;
+
+                       if ( !part.match(/\W/) ) {
+                               var nodeCheck = part = isXML ? part : part.toUpperCase();
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+               },
+               "~": function(checkSet, part, isXML){
+                       var doneName = done++, checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !part.match(/\W/) ) {
+                               var nodeCheck = part = isXML ? part : part.toUpperCase();
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+               }
+       },
+       find: {
+               ID: function(match, context, isXML){
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               return m ? [m] : [];
+                       }
+               },
+               NAME: function(match, context, isXML){
+                       if ( typeof context.getElementsByName !== "undefined" ) {
+                               var ret = [], results = context.getElementsByName(match[1]);
+
+                               for ( var i = 0, l = results.length; i < l; i++ ) {
+                                       if ( results[i].getAttribute("name") === match[1] ) {
+                                               ret.push( results[i] );
+                                       }
+                               }
+
+                               return ret.length === 0 ? null : ret;
+                       }
+               },
+               TAG: function(match, context){
+                       return context.getElementsByTagName(match[1]);
+               }
+       },
+       preFilter: {
+               CLASS: function(match, curLoop, inplace, result, not, isXML){
+                       match = " " + match[1].replace(/\\/g, "") + " ";
+
+                       if ( isXML ) {
+                               return match;
+                       }
+
+                       for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+                               if ( elem ) {
+                                       if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
+                                               if ( !inplace )
+                                                       result.push( elem );
+                                       } else if ( inplace ) {
+                                               curLoop[i] = false;
+                                       }
+                               }
+                       }
+
+                       return false;
+               },
+               ID: function(match){
+                       return match[1].replace(/\\/g, "");
+               },
+               TAG: function(match, curLoop){
+                       for ( var i = 0; curLoop[i] === false; i++ ){}
+                       return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+               },
+               CHILD: function(match){
+                       if ( match[1] == "nth" ) {
+                               // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+                               var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+                                       match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
+                                       !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+                               // calculate the numbers (first)n+(last) including if they are negative
+                               match[2] = (test[1] + (test[2] || 1)) - 0;
+                               match[3] = test[3] - 0;
+                       }
+
+                       // TODO: Move to normal caching system
+                       match[0] = done++;
+
+                       return match;
+               },
+               ATTR: function(match, curLoop, inplace, result, not, isXML){
+                       var name = match[1].replace(/\\/g, "");
+                       
+                       if ( !isXML && Expr.attrMap[name] ) {
+                               match[1] = Expr.attrMap[name];
+                       }
+
+                       if ( match[2] === "~=" ) {
+                               match[4] = " " + match[4] + " ";
+                       }
+
+                       return match;
+               },
+               PSEUDO: function(match, curLoop, inplace, result, not){
+                       if ( match[1] === "not" ) {
+                               // If we're dealing with a complex expression, or a simple one
+                               if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
+                                       match[3] = Sizzle(match[3], null, null, curLoop);
+                               } else {
+                                       var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+                                       if ( !inplace ) {
+                                               result.push.apply( result, ret );
+                                       }
+                                       return false;
+                               }
+                       } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+                               return true;
+                       }
+                       
+                       return match;
+               },
+               POS: function(match){
+                       match.unshift( true );
+                       return match;
+               }
+       },
+       filters: {
+               enabled: function(elem){
+                       return elem.disabled === false && elem.type !== "hidden";
+               },
+               disabled: function(elem){
+                       return elem.disabled === true;
+               },
+               checked: function(elem){
+                       return elem.checked === true;
+               },
+               selected: function(elem){
+                       // Accessing this property makes selected-by-default
+                       // options in Safari work properly
+                       elem.parentNode.selectedIndex;
+                       return elem.selected === true;
+               },
+               parent: function(elem){
+                       return !!elem.firstChild;
+               },
+               empty: function(elem){
+                       return !elem.firstChild;
+               },
+               has: function(elem, i, match){
+                       return !!Sizzle( match[3], elem ).length;
+               },
+               header: function(elem){
+                       return /h\d/i.test( elem.nodeName );
+               },
+               text: function(elem){
+                       return "text" === elem.type;
+               },
+               radio: function(elem){
+                       return "radio" === elem.type;
+               },
+               checkbox: function(elem){
+                       return "checkbox" === elem.type;
+               },
+               file: function(elem){
+                       return "file" === elem.type;
+               },
+               password: function(elem){
+                       return "password" === elem.type;
+               },
+               submit: function(elem){
+                       return "submit" === elem.type;
+               },
+               image: function(elem){
+                       return "image" === elem.type;
+               },
+               reset: function(elem){
+                       return "reset" === elem.type;
+               },
+               button: function(elem){
+                       return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
+               },
+               input: function(elem){
+                       return /input|select|textarea|button/i.test(elem.nodeName);
+               }
+       },
+       setFilters: {
+               first: function(elem, i){
+                       return i === 0;
+               },
+               last: function(elem, i, match, array){
+                       return i === array.length - 1;
+               },
+               even: function(elem, i){
+                       return i % 2 === 0;
+               },
+               odd: function(elem, i){
+                       return i % 2 === 1;
+               },
+               lt: function(elem, i, match){
+                       return i < match[3] - 0;
+               },
+               gt: function(elem, i, match){
+                       return i > match[3] - 0;
+               },
+               nth: function(elem, i, match){
+                       return match[3] - 0 == i;
+               },
+               eq: function(elem, i, match){
+                       return match[3] - 0 == i;
+               }
+       },
+       filter: {
+               PSEUDO: function(elem, match, i, array){
+                       var name = match[1], filter = Expr.filters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       } else if ( name === "contains" ) {
+                               return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
+                       } else if ( name === "not" ) {
+                               var not = match[3];
+
+                               for ( var i = 0, l = not.length; i < l; i++ ) {
+                                       if ( not[i] === elem ) {
+                                               return false;
+                                       }
+                               }
+
+                               return true;
+                       }
+               },
+               CHILD: function(elem, match){
+                       var type = match[1], node = elem;
+                       switch (type) {
+                               case 'only':
+                               case 'first':
+                                       while (node = node.previousSibling)  {
+                                               if ( node.nodeType === 1 ) return false;
+                                       }
+                                       if ( type == 'first') return true;
+                                       node = elem;
+                               case 'last':
+                                       while (node = node.nextSibling)  {
+                                               if ( node.nodeType === 1 ) return false;
+                                       }
+                                       return true;
+                               case 'nth':
+                                       var first = match[2], last = match[3];
+
+                                       if ( first == 1 && last == 0 ) {
+                                               return true;
+                                       }
+                                       
+                                       var doneName = match[0],
+                                               parent = elem.parentNode;
+       
+                                       if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+                                               var count = 0;
+                                               for ( node = parent.firstChild; node; node = node.nextSibling ) {
+                                                       if ( node.nodeType === 1 ) {
+                                                               node.nodeIndex = ++count;
+                                                       }
+                                               } 
+                                               parent.sizcache = doneName;
+                                       }
+                                       
+                                       var diff = elem.nodeIndex - last;
+                                       if ( first == 0 ) {
+                                               return diff == 0;
+                                       } else {
+                                               return ( diff % first == 0 && diff / first >= 0 );
+                                       }
+                       }
+               },
+               ID: function(elem, match){
+                       return elem.nodeType === 1 && elem.getAttribute("id") === match;
+               },
+               TAG: function(elem, match){
+                       return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
+               },
+               CLASS: function(elem, match){
+                       return (" " + (elem.className || elem.getAttribute("class")) + " ")
+                               .indexOf( match ) > -1;
+               },
+               ATTR: function(elem, match){
+                       var name = match[1],
+                               result = Expr.attrHandle[ name ] ?
+                                       Expr.attrHandle[ name ]( elem ) :
+                                       elem[ name ] != null ?
+                                               elem[ name ] :
+                                               elem.getAttribute( name ),
+                               value = result + "",
+                               type = match[2],
+                               check = match[4];
+
+                       return result == null ?
+                               type === "!=" :
+                               type === "=" ?
+                               value === check :
+                               type === "*=" ?
+                               value.indexOf(check) >= 0 :
+                               type === "~=" ?
+                               (" " + value + " ").indexOf(check) >= 0 :
+                               !check ?
+                               value && result !== false :
+                               type === "!=" ?
+                               value != check :
+                               type === "^=" ?
+                               value.indexOf(check) === 0 :
+                               type === "$=" ?
+                               value.substr(value.length - check.length) === check :
+                               type === "|=" ?
+                               value === check || value.substr(0, check.length + 1) === check + "-" :
+                               false;
+               },
+               POS: function(elem, match, i, array){
+                       var name = match[2], filter = Expr.setFilters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       }
+               }
+       }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+       Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+}
+
+var makeArray = function(array, results) {
+       array = Array.prototype.slice.call( array );
+
+       if ( results ) {
+               results.push.apply( results, array );
+               return results;
+       }
+       
+       return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+try {
+       Array.prototype.slice.call( document.documentElement.childNodes );
+
+// Provide a fallback method if it does not work
+} catch(e){
+       makeArray = function(array, results) {
+               var ret = results || [];
+
+               if ( toString.call(array) === "[object Array]" ) {
+                       Array.prototype.push.apply( ret, array );
+               } else {
+                       if ( typeof array.length === "number" ) {
+                               for ( var i = 0, l = array.length; i < l; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       } else {
+                               for ( var i = 0; array[i]; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       }
+               }
+
+               return ret;
+       };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+       sortOrder = function( a, b ) {
+               var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+} else if ( "sourceIndex" in document.documentElement ) {
+       sortOrder = function( a, b ) {
+               var ret = a.sourceIndex - b.sourceIndex;
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+} else if ( document.createRange ) {
+       sortOrder = function( a, b ) {
+               var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+               aRange.selectNode(a);
+               aRange.collapse(true);
+               bRange.selectNode(b);
+               bRange.collapse(true);
+               var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+       // We're going to inject a fake input element with a specified name
+       var form = document.createElement("form"),
+               id = "script" + (new Date).getTime();
+       form.innerHTML = "<input name='" + id + "'/>";
+
+       // Inject it into the root element, check its status, and remove it quickly
+       var root = document.documentElement;
+       root.insertBefore( form, root.firstChild );
+
+       // The workaround has to do additional checks after a getElementById
+       // Which slows things down for other browsers (hence the branching)
+       if ( !!document.getElementById( id ) ) {
+               Expr.find.ID = function(match, context, isXML){
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+                       }
+               };
+
+               Expr.filter.ID = function(elem, match){
+                       var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+                       return elem.nodeType === 1 && node && node.nodeValue === match;
+               };
+       }
+
+       root.removeChild( form );
+})();
+
+(function(){
+       // Check to see if the browser returns only elements
+       // when doing getElementsByTagName("*")
+
+       // Create a fake element
+       var div = document.createElement("div");
+       div.appendChild( document.createComment("") );
+
+       // Make sure no comments are found
+       if ( div.getElementsByTagName("*").length > 0 ) {
+               Expr.find.TAG = function(match, context){
+                       var results = context.getElementsByTagName(match[1]);
+
+                       // Filter out possible comments
+                       if ( match[1] === "*" ) {
+                               var tmp = [];
+
+                               for ( var i = 0; results[i]; i++ ) {
+                                       if ( results[i].nodeType === 1 ) {
+                                               tmp.push( results[i] );
+                                       }
+                               }
+
+                               results = tmp;
+                       }
+
+                       return results;
+               };
+       }
+
+       // Check to see if an attribute returns normalized href attributes
+       div.innerHTML = "<a href='#'></a>";
+       if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+                       div.firstChild.getAttribute("href") !== "#" ) {
+               Expr.attrHandle.href = function(elem){
+                       return elem.getAttribute("href", 2);
+               };
+       }
+})();
+
+if ( document.querySelectorAll ) (function(){
+       var oldSizzle = Sizzle, div = document.createElement("div");
+       div.innerHTML = "<p class='TEST'></p>";
+
+       // Safari can't handle uppercase or unicode characters when
+       // in quirks mode.
+       if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+               return;
+       }
+       
+       Sizzle = function(query, context, extra, seed){
+               context = context || document;
+
+               // Only use querySelectorAll on non-XML documents
+               // (ID selectors don't work in non-HTML documents)
+               if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+                       try {
+                               return makeArray( context.querySelectorAll(query), extra );
+                       } catch(e){}
+               }
+               
+               return oldSizzle(query, context, extra, seed);
+       };
+
+       Sizzle.find = oldSizzle.find;
+       Sizzle.filter = oldSizzle.filter;
+       Sizzle.selectors = oldSizzle.selectors;
+       Sizzle.matches = oldSizzle.matches;
+})();
+
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
+       var div = document.createElement("div");
+       div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+       // Opera can't find a second classname (in 9.6)
+       if ( div.getElementsByClassName("e").length === 0 )
+               return;
+
+       // Safari caches class attributes, doesn't catch changes (in 3.2)
+       div.lastChild.className = "e";
+
+       if ( div.getElementsByClassName("e").length === 1 )
+               return;
+
+       Expr.order.splice(1, 0, "CLASS");
+       Expr.find.CLASS = function(match, context, isXML) {
+               if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+                       return context.getElementsByClassName(match[1]);
+               }
+       };
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       var sibDir = dir == "previousSibling" && !isXML;
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+               if ( elem ) {
+                       if ( sibDir && elem.nodeType === 1 ){
+                               elem.sizcache = doneName;
+                               elem.sizset = i;
+                       }
+                       elem = elem[dir];
+                       var match = false;
+
+                       while ( elem ) {
+                               if ( elem.sizcache === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 && !isXML ){
+                                       elem.sizcache = doneName;
+                                       elem.sizset = i;
+                               }
+
+                               if ( elem.nodeName === cur ) {
+                                       match = elem;
+                                       break;
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       var sibDir = dir == "previousSibling" && !isXML;
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+               if ( elem ) {
+                       if ( sibDir && elem.nodeType === 1 ) {
+                               elem.sizcache = doneName;
+                               elem.sizset = i;
+                       }
+                       elem = elem[dir];
+                       var match = false;
+
+                       while ( elem ) {
+                               if ( elem.sizcache === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !isXML ) {
+                                               elem.sizcache = doneName;
+                                               elem.sizset = i;
+                                       }
+                                       if ( typeof cur !== "string" ) {
+                                               if ( elem === cur ) {
+                                                       match = true;
+                                                       break;
+                                               }
+
+                                       } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+                                               match = elem;
+                                               break;
+                                       }
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+var contains = document.compareDocumentPosition ?  function(a, b){
+       return a.compareDocumentPosition(b) & 16;
+} : function(a, b){
+       return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+       return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
+               !!elem.ownerDocument && isXML( elem.ownerDocument );
+};
+
+var posProcess = function(selector, context){
+       var tmpSet = [], later = "", match,
+               root = context.nodeType ? [context] : context;
+
+       // Position selectors must be done after the filter
+       // And so must :not(positional) so we move all PSEUDOs to the end
+       while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+               later += match[0];
+               selector = selector.replace( Expr.match.PSEUDO, "" );
+       }
+
+       selector = Expr.relative[selector] ? selector + "*" : selector;
+
+       for ( var i = 0, l = root.length; i < l; i++ ) {
+               Sizzle( selector, root[i], tmpSet );
+       }
+
+       return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.filter = Sizzle.filter;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+
+Sizzle.selectors.filters.hidden = function(elem){
+       return elem.offsetWidth === 0 || elem.offsetHeight === 0;
+};
+
+Sizzle.selectors.filters.visible = function(elem){
+       return elem.offsetWidth > 0 || elem.offsetHeight > 0;
+};
+
+Sizzle.selectors.filters.animated = function(elem){
+       return jQuery.grep(jQuery.timers, function(fn){
+               return elem === fn.elem;
+       }).length;
+};
+
+jQuery.multiFilter = function( expr, elems, not ) {
+       if ( not ) {
+               expr = ":not(" + expr + ")";
+       }
+
+       return Sizzle.matches(expr, elems);
+};
+
+jQuery.dir = function( elem, dir ){
+       var matched = [], cur = elem[dir];
+       while ( cur && cur != document ) {
+               if ( cur.nodeType == 1 )
+                       matched.push( cur );
+               cur = cur[dir];
+       }
+       return matched;
+};
+
+jQuery.nth = function(cur, result, dir, elem){
+       result = result || 1;
+       var num = 0;
+
+       for ( ; cur; cur = cur[dir] )
+               if ( cur.nodeType == 1 && ++num == result )
+                       break;
+
+       return cur;
+};
+
+jQuery.sibling = function(n, elem){
+       var r = [];
+
+       for ( ; n; n = n.nextSibling ) {
+               if ( n.nodeType == 1 && n != elem )
+                       r.push( n );
+       }
+
+       return r;
+};
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+       // Bind an event to an element
+       // Original by Dean Edwards
+       add: function(elem, types, handler, data) {
+               if ( elem.nodeType == 3 || elem.nodeType == 8 )
+                       return;
+
+               // For whatever reason, IE has trouble passing the window object
+               // around, causing it to be cloned in the process
+               if ( elem.setInterval && elem != window )
+                       elem = window;
+
+               // Make sure that the function being executed has a unique ID
+               if ( !handler.guid )
+                       handler.guid = this.guid++;
+
+               // if data is passed, bind to handler
+               if ( data !== undefined ) {
+                       // Create temporary function pointer to original handler
+                       var fn = handler;
+
+                       // Create unique handler function, wrapped around original handler
+                       handler = this.proxy( fn );
+
+                       // Store data in unique handler
+                       handler.data = data;
+               }
+
+               // Init the element's event structure
+               var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+                       handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
+                               // Handle the second event of a trigger and when
+                               // an event is called after a page has unloaded
+                               return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+                                       jQuery.event.handle.apply(arguments.callee.elem, arguments) :
+                                       undefined;
+                       });
+               // Add elem as a property of the handle function
+               // This is to prevent a memory leak with non-native
+               // event in IE.
+               handle.elem = elem;
+
+               // Handle multiple events separated by a space
+               // jQuery(...).bind("mouseover mouseout", fn);
+               jQuery.each(types.split(/\s+/), function(index, type) {
+                       // Namespaced event handlers
+                       var namespaces = type.split(".");
+                       type = namespaces.shift();
+                       handler.type = namespaces.slice().sort().join(".");
+
+                       // Get the current list of functions bound to this event
+                       var handlers = events[type];
+                       
+                       if ( jQuery.event.specialAll[type] )
+                               jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
+
+                       // Init the event handler queue
+                       if (!handlers) {
+                               handlers = events[type] = {};
+
+                               // Check for a special event handler
+                               // Only use addEventListener/attachEvent if the special
+                               // events handler returns false
+                               if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
+                                       // Bind the global event handler to the element
+                                       if (elem.addEventListener)
+                                               elem.addEventListener(type, handle, false);
+                                       else if (elem.attachEvent)
+                                               elem.attachEvent("on" + type, handle);
+                               }
+                       }
+
+                       // Add the function to the element's handler list
+                       handlers[handler.guid] = handler;
+
+                       // Keep track of which events have been used, for global triggering
+                       jQuery.event.global[type] = true;
+               });
+
+               // Nullify elem to prevent memory leaks in IE
+               elem = null;
+       },
+
+       guid: 1,
+       global: {},
+
+       // Detach an event or set of events from an element
+       remove: function(elem, types, handler) {
+               // don't do events on text and comment nodes
+               if ( elem.nodeType == 3 || elem.nodeType == 8 )
+                       return;
+
+               var events = jQuery.data(elem, "events"), ret, index;
+
+               if ( events ) {
+                       // Unbind all events for the element
+                       if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
+                               for ( var type in events )
+                                       this.remove( elem, type + (types || "") );
+                       else {
+                               // types is actually an event object here
+                               if ( types.type ) {
+                                       handler = types.handler;
+                                       types = types.type;
+                               }
+
+                               // Handle multiple events seperated by a space
+                               // jQuery(...).unbind("mouseover mouseout", fn);
+                               jQuery.each(types.split(/\s+/), function(index, type){
+                                       // Namespaced event handlers
+                                       var namespaces = type.split(".");
+                                       type = namespaces.shift();
+                                       var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+                                       if ( events[type] ) {
+                                               // remove the given handler for the given type
+                                               if ( handler )
+                                                       delete events[type][handler.guid];
+
+                                               // remove all handlers for the given type
+                                               else
+                                                       for ( var handle in events[type] )
+                                                               // Handle the removal of namespaced events
+                                                               if ( namespace.test(events[type][handle].type) )
+                                                                       delete events[type][handle];
+                                                                       
+                                               if ( jQuery.event.specialAll[type] )
+                                                       jQuery.event.specialAll[type].teardown.call(elem, namespaces);
+
+                                               // remove generic event handler if no more handlers exist
+                                               for ( ret in events[type] ) break;
+                                               if ( !ret ) {
+                                                       if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
+                                                               if (elem.removeEventListener)
+                                                                       elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+                                                               else if (elem.detachEvent)
+                                                                       elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+                                                       }
+                                                       ret = null;
+                                                       delete events[type];
+                                               }
+                                       }
+                               });
+                       }
+
+                       // Remove the expando if it's no longer used
+                       for ( ret in events ) break;
+                       if ( !ret ) {
+                               var handle = jQuery.data( elem, "handle" );
+                               if ( handle ) handle.elem = null;
+                               jQuery.removeData( elem, "events" );
+                               jQuery.removeData( elem, "handle" );
+                       }
+               }
+       },
+
+       // bubbling is internal
+       trigger: function( event, data, elem, bubbling ) {
+               // Event object or event type
+               var type = event.type || event;
+
+               if( !bubbling ){
+                       event = typeof event === "object" ?
+                               // jQuery.Event object
+                               event[expando] ? event :
+                               // Object literal
+                               jQuery.extend( jQuery.Event(type), event ) :
+                               // Just the event type (string)
+                               jQuery.Event(type);
+
+                       if ( type.indexOf("!") >= 0 ) {
+                               event.type = type = type.slice(0, -1);
+                               event.exclusive = true;
+                       }
+
+                       // Handle a global trigger
+                       if ( !elem ) {
+                               // Don't bubble custom events when global (to avoid too much overhead)
+                               event.stopPropagation();
+                               // Only trigger if we've ever bound an event for it
+                               if ( this.global[type] )
+                                       jQuery.each( jQuery.cache, function(){
+                                               if ( this.events && this.events[type] )
+                                                       jQuery.event.trigger( event, data, this.handle.elem );
+                                       });
+                       }
+
+                       // Handle triggering a single element
+
+                       // don't do events on text and comment nodes
+                       if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
+                               return undefined;
+                       
+                       // Clean up in case it is reused
+                       event.result = undefined;
+                       event.target = elem;
+                       
+                       // Clone the incoming data, if any
+                       data = jQuery.makeArray(data);
+                       data.unshift( event );
+               }
+
+               event.currentTarget = elem;
+
+               // Trigger the event, it is assumed that "handle" is a function
+               var handle = jQuery.data(elem, "handle");
+               if ( handle )
+                       handle.apply( elem, data );
+
+               // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
+               if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
+                       event.result = false;
+
+               // Trigger the native events (except for clicks on links)
+               if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
+                       this.triggered = true;
+                       try {
+                               elem[ type ]();
+                       // prevent IE from throwing an error for some hidden elements
+                       } catch (e) {}
+               }
+
+               this.triggered = false;
+
+               if ( !event.isPropagationStopped() ) {
+                       var parent = elem.parentNode || elem.ownerDocument;
+                       if ( parent )
+                               jQuery.event.trigger(event, data, parent, true);
+               }
+       },
+
+       handle: function(event) {
+               // returned undefined or false
+               var all, handlers;
+
+               event = arguments[0] = jQuery.event.fix( event || window.event );
+               event.currentTarget = this;
+               
+               // Namespaced event handlers
+               var namespaces = event.type.split(".");
+               event.type = namespaces.shift();
+
+               // Cache this now, all = true means, any handler
+               all = !namespaces.length && !event.exclusive;
+               
+               var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
+
+               handlers = ( jQuery.data(this, "events") || {} )[event.type];
+
+               for ( var j in handlers ) {
+                       var handler = handlers[j];
+
+                       // Filter the functions by class
+                       if ( all || namespace.test(handler.type) ) {
+                               // Pass in a reference to the handler function itself
+                               // So that we can later remove it
+                               event.handler = handler;
+                               event.data = handler.data;
+
+                               var ret = handler.apply(this, arguments);
+
+                               if( ret !== undefined ){
+                                       event.result = ret;
+                                       if ( ret === false ) {
+                                               event.preventDefault();
+                                               event.stopPropagation();
+                                       }
+                               }
+
+                               if( event.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(event) {
+               if ( event[expando] )
+                       return event;
+
+               // store a copy of the original event object
+               // and "clone" to set read-only properties
+               var originalEvent = event;
+               event = jQuery.Event( originalEvent );
+
+               for ( var i = this.props.length, prop; i; ){
+                       prop = this.props[ --i ];
+                       event[ prop ] = originalEvent[ prop ];
+               }
+
+               // Fix target property, if necessary
+               if ( !event.target )
+                       event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+
+               // check if target is a textnode (safari)
+               if ( event.target.nodeType == 3 )
+                       event.target = event.target.parentNode;
+
+               // Add relatedTarget, if necessary
+               if ( !event.relatedTarget && event.fromElement )
+                       event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
+
+               // Calculate pageX/Y if missing and clientX/Y available
+               if ( event.pageX == null && event.clientX != null ) {
+                       var doc = document.documentElement, body = document.body;
+                       event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+                       event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+               }
+
+               // Add which for key events
+               if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
+                       event.which = event.charCode || event.keyCode;
+
+               // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+               if ( !event.metaKey && event.ctrlKey )
+                       event.metaKey = event.ctrlKey;
+
+               // Add which for click: 1 == left; 2 == middle; 3 == right
+               // Note: button is not normalized, so don't use it
+               if ( !event.which && event.button )
+                       event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+
+               return event;
+       },
+
+       proxy: function( fn, proxy ){
+               proxy = proxy || function(){ return fn.apply(this, arguments); };
+               // Set the guid of unique handler to the same of original handler, so it can be removed
+               proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
+               // So proxy can be declared as an argument
+               return proxy;
+       },
+
+       special: {
+               ready: {
+                       // Make sure the ready event is setup
+                       setup: bindReady,
+                       teardown: function() {}
+               }
+       },
+       
+       specialAll: {
+               live: {
+                       setup: function( selector, namespaces ){
+                               jQuery.event.add( this, namespaces[0], liveHandler );
+                       },
+                       teardown:  function( namespaces ){
+                               if ( namespaces.length ) {
+                                       var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
+                                       
+                                       jQuery.each( (jQuery.data(this, "events").live || {}), function(){
+                                               if ( name.test(this.type) )
+                                                       remove++;
+                                       });
+                                       
+                                       if ( remove < 1 )
+                                               jQuery.event.remove( this, namespaces[0], liveHandler );
+                               }
+                       }
+               }
+       }
+};
+
+jQuery.Event = function( src ){
+       // Allow instantiation without the 'new' keyword
+       if( !this.preventDefault )
+               return new jQuery.Event(src);
+       
+       // Event object
+       if( src && src.type ){
+               this.originalEvent = src;
+               this.type = src.type;
+       // Event type
+       }else
+               this.type = src;
+
+       // timeStamp is buggy for some events on Firefox(#3843)
+       // So we won't rely on the native value
+       this.timeStamp = now();
+       
+       // Mark it as fixed
+       this[expando] = true;
+};
+
+function returnFalse(){
+       return false;
+}
+function returnTrue(){
+       return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+       preventDefault: function() {
+               this.isDefaultPrevented = returnTrue;
+
+               var e = this.originalEvent;
+               if( !e )
+                       return;
+               // if preventDefault exists run it on the original event
+               if (e.preventDefault)
+                       e.preventDefault();
+               // otherwise set the returnValue property of the original event to false (IE)
+               e.returnValue = false;
+       },
+       stopPropagation: function() {
+               this.isPropagationStopped = returnTrue;
+
+               var e = this.originalEvent;
+               if( !e )
+                       return;
+               // if stopPropagation exists run it on the original event
+               if (e.stopPropagation)
+                       e.stopPropagation();
+               // otherwise set the cancelBubble property of the original event to true (IE)
+               e.cancelBubble = true;
+       },
+       stopImmediatePropagation:function(){
+               this.isImmediatePropagationStopped = returnTrue;
+               this.stopPropagation();
+       },
+       isDefaultPrevented: returnFalse,
+       isPropagationStopped: returnFalse,
+       isImmediatePropagationStopped: returnFalse
+};
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function(event) {
+       // Check if mouse(over|out) are still within the same parent element
+       var parent = event.relatedTarget;
+       // Traverse up the tree
+       while ( parent && parent != this )
+               try { parent = parent.parentNode; }
+               catch(e) { parent = this; }
+       
+       if( parent != this ){
+               // set the correct event type
+               event.type = event.data;
+               // handle event if we actually just moused on to a non sub-element
+               jQuery.event.handle.apply( this, arguments );
+       }
+};
+       
+jQuery.each({ 
+       mouseover: 'mouseenter', 
+       mouseout: 'mouseleave'
+}, function( orig, fix ){
+       jQuery.event.special[ fix ] = {
+               setup: function(){
+                       jQuery.event.add( this, orig, withinElement, fix );
+               },
+               teardown: function(){
+                       jQuery.event.remove( this, orig, withinElement );
+               }
+       };                         
+});
+
+jQuery.fn.extend({
+       bind: function( type, data, fn ) {
+               return type == "unload" ? this.one(type, data, fn) : this.each(function(){
+                       jQuery.event.add( this, type, fn || data, fn && data );
+               });
+       },
+
+       one: function( type, data, fn ) {
+               var one = jQuery.event.proxy( fn || data, function(event) {
+                       jQuery(this).unbind(event, one);
+                       return (fn || data).apply( this, arguments );
+               });
+               return this.each(function(){
+                       jQuery.event.add( this, type, one, fn && data);
+               });
+       },
+
+       unbind: function( type, fn ) {
+               return this.each(function(){
+                       jQuery.event.remove( this, type, fn );
+               });
+       },
+
+       trigger: function( type, data ) {
+               return this.each(function(){
+                       jQuery.event.trigger( type, data, this );
+               });
+       },
+
+       triggerHandler: function( type, data ) {
+               if( this[0] ){
+                       var event = jQuery.Event(type);
+                       event.preventDefault();
+                       event.stopPropagation();
+                       jQuery.event.trigger( event, data, this[0] );
+                       return event.result;
+               }               
+       },
+
+       toggle: function( fn ) {
+               // Save reference to arguments for access in closure
+               var args = arguments, i = 1;
+
+               // link all the functions, so any of them can unbind this click handler
+               while( i < args.length )
+                       jQuery.event.proxy( fn, args[i++] );
+
+               return this.click( jQuery.event.proxy( fn, function(event) {
+                       // Figure out which function to execute
+                       this.lastToggle = ( this.lastToggle || 0 ) % i;
+
+                       // Make sure that clicks stop
+                       event.preventDefault();
+
+                       // and execute the function
+                       return args[ this.lastToggle++ ].apply( this, arguments ) || false;
+               }));
+       },
+
+       hover: function(fnOver, fnOut) {
+               return this.mouseenter(fnOver).mouseleave(fnOut);
+       },
+
+       ready: function(fn) {
+               // Attach the listeners
+               bindReady();
+
+               // If the DOM is already ready
+               if ( jQuery.isReady )
+                       // Execute the function immediately
+                       fn.call( document, jQuery );
+
+               // Otherwise, remember the function for later
+               else
+                       // Add the function to the wait list
+                       jQuery.readyList.push( fn );
+
+               return this;
+       },
+       
+       live: function( type, fn ){
+               var proxy = jQuery.event.proxy( fn );
+               proxy.guid += this.selector + type;
+
+               jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
+
+               return this;
+       },
+       
+       die: function( type, fn ){
+               jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
+               return this;
+       }
+});
+
+function liveHandler( event ){
+       var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
+               stop = true,
+               elems = [];
+
+       jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
+               if ( check.test(fn.type) ) {
+                       var elem = jQuery(event.target).closest(fn.data)[0];
+                       if ( elem )
+                               elems.push({ elem: elem, fn: fn });
+               }
+       });
+
+       elems.sort(function(a,b) {
+               return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
+       });
+       
+       jQuery.each(elems, function(){
+               if ( this.fn.call(this.elem, event, this.fn.data) === false )
+                       return (stop = false);
+       });
+
+       return stop;
+}
+
+function liveConvert(type, selector){
+       return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
+}
+
+jQuery.extend({
+       isReady: false,
+       readyList: [],
+       // Handle when the DOM is ready
+       ready: function() {
+               // Make sure that the DOM is not already loaded
+               if ( !jQuery.isReady ) {
+                       // Remember that the DOM is ready
+                       jQuery.isReady = true;
+
+                       // If there are functions bound, to execute
+                       if ( jQuery.readyList ) {
+                               // Execute all of them
+                               jQuery.each( jQuery.readyList, function(){
+                                       this.call( document, jQuery );
+                               });
+
+                               // Reset the list of functions
+                               jQuery.readyList = null;
+                       }
+
+                       // Trigger any bound ready events
+                       jQuery(document).triggerHandler("ready");
+               }
+       }
+});
+
+var readyBound = false;
+
+function bindReady(){
+       if ( readyBound ) return;
+       readyBound = true;
+
+       // Mozilla, Opera and webkit nightlies currently support this event
+       if ( document.addEventListener ) {
+               // Use the handy event callback
+               document.addEventListener( "DOMContentLoaded", function(){
+                       document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
+                       jQuery.ready();
+               }, false );
+
+       // If IE event model is used
+       } else if ( document.attachEvent ) {
+               // ensure firing before onload,
+               // maybe late but safe also for iframes
+               document.attachEvent("onreadystatechange", function(){
+                       if ( document.readyState === "complete" ) {
+                               document.detachEvent( "onreadystatechange", arguments.callee );
+                               jQuery.ready();
+                       }
+               });
+
+               // If IE and not an iframe
+               // continually check to see if the document is ready
+               if ( document.documentElement.doScroll && window == window.top ) (function(){
+                       if ( jQuery.isReady ) return;
+
+                       try {
+                               // If IE is used, use the trick by Diego Perini
+                               // http://javascript.nwbox.com/IEContentLoaded/
+                               document.documentElement.doScroll("left");
+                       } catch( error ) {
+                               setTimeout( arguments.callee, 0 );
+                               return;
+                       }
+
+                       // and execute any waiting functions
+                       jQuery.ready();
+               })();
+       }
+
+       // A fallback to window.onload, that will always work
+       jQuery.event.add( window, "load", jQuery.ready );
+}
+
+jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+       "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
+       "change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
+
+       // Handle event binding
+       jQuery.fn[name] = function(fn){
+               return fn ? this.bind(name, fn) : this.trigger(name);
+       };
+});
+
+// Prevent memory leaks in IE
+// And prevent errors on refresh with events like mouseover in other browsers
+// Window isn't included so as not to unbind existing unload events
+jQuery( window ).bind( 'unload', function(){ 
+       for ( var id in jQuery.cache )
+               // Skip the window
+               if ( id != 1 && jQuery.cache[ id ].handle )
+                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+}); 
+(function(){
+
+       jQuery.support = {};
+
+       var root = document.documentElement,
+               script = document.createElement("script"),
+               div = document.createElement("div"),
+               id = "script" + (new Date).getTime();
+
+       div.style.display = "none";
+       div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
+
+       var all = div.getElementsByTagName("*"),
+               a = div.getElementsByTagName("a")[0];
+
+       // Can't get basic test support
+       if ( !all || !all.length || !a ) {
+               return;
+       }
+
+       jQuery.support = {
+               // IE strips leading whitespace when .innerHTML is used
+               leadingWhitespace: div.firstChild.nodeType == 3,
+               
+               // Make sure that tbody elements aren't automatically inserted
+               // IE will insert them into empty tables
+               tbody: !div.getElementsByTagName("tbody").length,
+               
+               // Make sure that you can get all elements in an <object> element
+               // IE 7 always returns no results
+               objectAll: !!div.getElementsByTagName("object")[0]
+                       .getElementsByTagName("*").length,
+               
+               // Make sure that link elements get serialized correctly by innerHTML
+               // This requires a wrapper element in IE
+               htmlSerialize: !!div.getElementsByTagName("link").length,
+               
+               // Get the style information from getAttribute
+               // (IE uses .cssText insted)
+               style: /red/.test( a.getAttribute("style") ),
+               
+               // Make sure that URLs aren't manipulated
+               // (IE normalizes it by default)
+               hrefNormalized: a.getAttribute("href") === "/a",
+               
+               // Make sure that element opacity exists
+               // (IE uses filter instead)
+               opacity: a.style.opacity === "0.5",
+               
+               // Verify style float existence
+               // (IE uses styleFloat instead of cssFloat)
+               cssFloat: !!a.style.cssFloat,
+
+               // Will be defined later
+               scriptEval: false,
+               noCloneEvent: true,
+               boxModel: null
+       };
+       
+       script.type = "text/javascript";
+       try {
+               script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+       } catch(e){}
+
+       root.insertBefore( script, root.firstChild );
+       
+       // Make sure that the execution of code works by injecting a script
+       // tag with appendChild/createTextNode
+       // (IE doesn't support this, fails, and uses .text instead)
+       if ( window[ id ] ) {
+               jQuery.support.scriptEval = true;
+               delete window[ id ];
+       }
+
+       root.removeChild( script );
+
+       if ( div.attachEvent && div.fireEvent ) {
+               div.attachEvent("onclick", function(){
+                       // Cloning a node shouldn't copy over any
+                       // bound event handlers (IE does this)
+                       jQuery.support.noCloneEvent = false;
+                       div.detachEvent("onclick", arguments.callee);
+               });
+               div.cloneNode(true).fireEvent("onclick");
+       }
+
+       // Figure out if the W3C box model works as expected
+       // document.body must exist before we can do this
+       jQuery(function(){
+               var div = document.createElement("div");
+               div.style.width = div.style.paddingLeft = "1px";
+
+               document.body.appendChild( div );
+               jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+               document.body.removeChild( div ).style.display = 'none';
+       });
+})();
+
+var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
+
+jQuery.props = {
+       "for": "htmlFor",
+       "class": "className",
+       "float": styleFloat,
+       cssFloat: styleFloat,
+       styleFloat: styleFloat,
+       readonly: "readOnly",
+       maxlength: "maxLength",
+       cellspacing: "cellSpacing",
+       rowspan: "rowSpan",
+       tabindex: "tabIndex"
+};
+jQuery.fn.extend({
+       // Keep a copy of the old load
+       _load: jQuery.fn.load,
+
+       load: function( url, params, callback ) {
+               if ( typeof url !== "string" )
+                       return this._load( url );
+
+               var off = url.indexOf(" ");
+               if ( off >= 0 ) {
+                       var selector = url.slice(off, url.length);
+                       url = url.slice(0, off);
+               }
+
+               // Default to a GET request
+               var type = "GET";
+
+               // If the second parameter was provided
+               if ( params )
+                       // If it's a function
+                       if ( jQuery.isFunction( params ) ) {
+                               // We assume that it's the callback
+                               callback = params;
+                               params = null;
+
+                       // Otherwise, build a param string
+                       } else if( typeof params === "object" ) {
+                               params = jQuery.param( params );
+                               type = "POST";
+                       }
+
+               var self = this;
+
+               // Request the remote document
+               jQuery.ajax({
+                       url: url,
+                       type: type,
+                       dataType: "html",
+                       data: params,
+                       complete: function(res, status){
+                               // If successful, inject the HTML into all the matched elements
+                               if ( status == "success" || status == "notmodified" )
+                                       // See if a selector was specified
+                                       self.html( selector ?
+                                               // Create a dummy div to hold the results
+                                               jQuery("<div/>")
+                                                       // inject the contents of the document in, removing the scripts
+                                                       // to avoid any 'Permission Denied' errors in IE
+                                                       .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
+
+                                                       // Locate the specified elements
+                                                       .find(selector) :
+
+                                               // If not, just inject the full result
+                                               res.responseText );
+
+                               if( callback )
+                                       self.each( callback, [res.responseText, status, res] );
+                       }
+               });
+               return this;
+       },
+
+       serialize: function() {
+               return jQuery.param(this.serializeArray());
+       },
+       serializeArray: function() {
+               return this.map(function(){
+                       return this.elements ? jQuery.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(i, elem){
+                       var val = jQuery(this).val();
+                       return val == null ? null :
+                               jQuery.isArray(val) ?
+                                       jQuery.map( val, function(val, i){
+                                               return {name: elem.name, value: val};
+                                       }) :
+                                       {name: elem.name, value: val};
+               }).get();
+       }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
+       jQuery.fn[o] = function(f){
+               return this.bind(o, f);
+       };
+});
+
+var jsc = now();
+
+jQuery.extend({
+  
+       get: function( url, data, callback, type ) {
+               // shift arguments if data argument was ommited
+               if ( jQuery.isFunction( data ) ) {
+                       callback = data;
+                       data = null;
+               }
+
+               return jQuery.ajax({
+                       type: "GET",
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       },
+
+       getScript: function( url, callback ) {
+               return jQuery.get(url, null, callback, "script");
+       },
+
+       getJSON: function( url, data, callback ) {
+               return jQuery.get(url, data, callback, "json");
+       },
+
+       post: function( url, data, callback, type ) {
+               if ( jQuery.isFunction( data ) ) {
+                       callback = data;
+                       data = {};
+               }
+
+               return jQuery.ajax({
+                       type: "POST",
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       },
+
+       ajaxSetup: function( settings ) {
+               jQuery.extend( jQuery.ajaxSettings, settings );
+       },
+
+       ajaxSettings: {
+               url: location.href,
+               global: true,
+               type: "GET",
+               contentType: "application/x-www-form-urlencoded",
+               processData: true,
+               async: true,
+               /*
+               timeout: 0,
+               data: null,
+               username: null,
+               password: null,
+               */
+               // Create the request object; Microsoft failed to properly
+               // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
+               // This function can be overriden by calling jQuery.ajaxSetup
+               xhr:function(){
+                       return window.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: "*/*"
+               }
+       },
+
+       // Last-Modified header cache for next request
+       lastModified: {},
+
+       ajax: function( s ) {
+               // Extend the settings, but re-extend 's' so that it can be
+               // checked again later (in the test suite, specifically)
+               s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+
+               var jsonp, jsre = /=\?(&|$)/g, status, data,
+                       type = s.type.toUpperCase();
+
+               // convert data if not already a string
+               if ( s.data && s.processData && typeof s.data !== "string" )
+                       s.data = jQuery.param(s.data);
+
+               // Handle JSONP Parameter Callbacks
+               if ( s.dataType == "jsonp" ) {
+                       if ( type == "GET" ) {
+                               if ( !s.url.match(jsre) )
+                                       s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+                       } else if ( !s.data || !s.data.match(jsre) )
+                               s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+                       s.dataType = "json";
+               }
+
+               // Build temporary JSONP function
+               if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
+                       jsonp = "jsonp" + jsc++;
+
+                       // Replace the =? sequence both in the query string and the data
+                       if ( s.data )
+                               s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+                       s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+                       // We need to make sure
+                       // that a JSONP style response is executed properly
+                       s.dataType = "script";
+
+                       // Handle JSONP-style loading
+                       window[ jsonp ] = function(tmp){
+                               data = tmp;
+                               success();
+                               complete();
+                               // Garbage collect
+                               window[ jsonp ] = undefined;
+                               try{ delete window[ jsonp ]; } catch(e){}
+                               if ( head )
+                                       head.removeChild( script );
+                       };
+               }
+
+               if ( s.dataType == "script" && s.cache == null )
+                       s.cache = false;
+
+               if ( s.cache === false && type == "GET" ) {
+                       var ts = now();
+                       // try replacing _= if it is there
+                       var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
+                       // if nothing was replaced, add timestamp to the end
+                       s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
+               }
+
+               // If data is available, append data to url for get requests
+               if ( s.data && type == "GET" ) {
+                       s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
+
+                       // IE likes to send both get and post data, prevent this
+                       s.data = null;
+               }
+
+               // Watch for a new set of requests
+               if ( s.global && ! jQuery.active++ )
+                       jQuery.event.trigger( "ajaxStart" );
+
+               // Matches an absolute URL, and saves the domain
+               var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
+
+               // If we're requesting a remote document
+               // and trying to load JSON or Script with a GET
+               if ( s.dataType == "script" && type == "GET" && parts
+                       && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
+
+                       var head = document.getElementsByTagName("head")[0];
+                       var script = document.createElement("script");
+                       script.src = s.url;
+                       if (s.scriptCharset)
+                               script.charset = s.scriptCharset;
+
+                       // Handle Script loading
+                       if ( !jsonp ) {
+                               var done = false;
+
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function(){
+                                       if ( !done && (!this.readyState ||
+                                                       this.readyState == "loaded" || this.readyState == "complete") ) {
+                                               done = true;
+                                               success();
+                                               complete();
+
+                                               // Handle memory leak in IE
+                                               script.onload = script.onreadystatechange = null;
+                                               head.removeChild( script );
+                                       }
+                               };
+                       }
+
+                       head.appendChild(script);
+
+                       // We handle everything using the script element injection
+                       return undefined;
+               }
+
+               var requestDone = false;
+
+               // Create the request object
+               var xhr = s.xhr();
+
+               // Open the socket
+               // Passing null username, generates a login popup on Opera (#2865)
+               if( s.username )
+                       xhr.open(type, s.url, s.async, s.username, s.password);
+               else
+                       xhr.open(type, s.url, s.async);
+
+               // Need an extra try/catch for cross domain requests in Firefox 3
+               try {
+                       // Set the correct header, if data is being sent
+                       if ( s.data )
+                               xhr.setRequestHeader("Content-Type", s.contentType);
+
+                       // Set the If-Modified-Since header, if ifModified mode.
+                       if ( s.ifModified )
+                               xhr.setRequestHeader("If-Modified-Since",
+                                       jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+                       // Set header so the called script knows that it's an XMLHttpRequest
+                       xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+                       // Set the Accepts header for the server, depending on the dataType
+                       xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+                               s.accepts[ s.dataType ] + ", */*" :
+                               s.accepts._default );
+               } catch(e){}
+
+               // Allow custom headers/mimetypes and early abort
+               if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active )
+                               jQuery.event.trigger( "ajaxStop" );
+                       // close opended socket
+                       xhr.abort();
+                       return false;
+               }
+
+               if ( s.global )
+                       jQuery.event.trigger("ajaxSend", [xhr, s]);
+
+               // Wait for a response to come back
+               var onreadystatechange = function(isTimeout){
+                       // The request was aborted, clear the interval and decrement jQuery.active
+                       if (xhr.readyState == 0) {
+                               if (ival) {
+                                       // clear poll interval
+                                       clearInterval(ival);
+                                       ival = null;
+                                       // Handle the global AJAX counter
+                                       if ( s.global && ! --jQuery.active )
+                                               jQuery.event.trigger( "ajaxStop" );
+                               }
+                       // The transfer is complete and the data is available, or the request timed out
+                       } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
+                               requestDone = true;
+
+                               // clear poll interval
+                               if (ival) {
+                                       clearInterval(ival);
+                                       ival = null;
+                               }
+
+                               status = isTimeout == "timeout" ? "timeout" :
+                                       !jQuery.httpSuccess( xhr ) ? "error" :
+                                       s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
+                                       "success";
+
+                               if ( status == "success" ) {
+                                       // Watch for, and catch, XML document parse errors
+                                       try {
+                                               // process the data (runs the xml through httpData regardless of callback)
+                                               data = jQuery.httpData( xhr, s.dataType, s );
+                                       } catch(e) {
+                                               status = "parsererror";
+                                       }
+                               }
+
+                               // Make sure that the request was successful or notmodified
+                               if ( status == "success" ) {
+                                       // Cache Last-Modified header, if ifModified mode.
+                                       var modRes;
+                                       try {
+                                               modRes = xhr.getResponseHeader("Last-Modified");
+                                       } catch(e) {} // swallow exception thrown by FF if header is not available
+
+                                       if ( s.ifModified && modRes )
+                                               jQuery.lastModified[s.url] = modRes;
+
+                                       // JSONP handles its own success callback
+                                       if ( !jsonp )
+                                               success();
+                               } else
+                                       jQuery.handleError(s, xhr, status);
+
+                               // Fire the complete handlers
+                               complete();
+
+                               if ( isTimeout )
+                                       xhr.abort();
+
+                               // Stop memory leaks
+                               if ( s.async )
+                                       xhr = null;
+                       }
+               };
+
+               if ( s.async ) {
+                       // don't attach the handler to the request, just poll it instead
+                       var ival = setInterval(onreadystatechange, 13);
+
+                       // Timeout checker
+                       if ( s.timeout > 0 )
+                               setTimeout(function(){
+                                       // Check to see if the request is still happening
+                                       if ( xhr && !requestDone )
+                                               onreadystatechange( "timeout" );
+                               }, s.timeout);
+               }
+
+               // Send the data
+               try {
+                       xhr.send(s.data);
+               } catch(e) {
+                       jQuery.handleError(s, xhr, null, e);
+               }
+
+               // firefox 1.5 doesn't fire statechange for sync requests
+               if ( !s.async )
+                       onreadystatechange();
+
+               function success(){
+                       // If a local callback was specified, fire it and pass it the data
+                       if ( s.success )
+                               s.success( data, status );
+
+                       // Fire the global callback
+                       if ( s.global )
+                               jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
+               }
+
+               function complete(){
+                       // Process result
+                       if ( s.complete )
+                               s.complete(xhr, status);
+
+                       // The request was completed
+                       if ( s.global )
+                               jQuery.event.trigger( "ajaxComplete", [xhr, s] );
+
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active )
+                               jQuery.event.trigger( "ajaxStop" );
+               }
+
+               // return XMLHttpRequest to allow aborting the request etc.
+               return xhr;
+       },
+
+       handleError: function( s, xhr, status, e ) {
+               // If a local callback was specified, fire it
+               if ( s.error ) s.error( xhr, status, e );
+
+               // Fire the global callback
+               if ( s.global )
+                       jQuery.event.trigger( "ajaxError", [xhr, s, e] );
+       },
+
+       // Counter for holding the number of active queries
+       active: 0,
+
+       // Determines if an XMLHttpRequest was successful or not
+       httpSuccess: function( xhr ) {
+               try {
+                       // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+                       return !xhr.status && location.protocol == "file:" ||
+                               ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
+               } catch(e){}
+               return false;
+       },
+
+       // Determines if an XMLHttpRequest returns NotModified
+       httpNotModified: function( xhr, url ) {
+               try {
+                       var xhrRes = xhr.getResponseHeader("Last-Modified");
+
+                       // Firefox always returns 200. check Last-Modified date
+                       return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
+               } catch(e){}
+               return false;
+       },
+
+       httpData: function( xhr, type, s ) {
+               var ct = xhr.getResponseHeader("content-type"),
+                       xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
+                       data = xml ? xhr.responseXML : xhr.responseText;
+
+               if ( xml && data.documentElement.tagName == "parsererror" )
+                       throw "parsererror";
+                       
+               // Allow a pre-filtering function to sanitize the response
+               // s != null is checked to keep backwards compatibility
+               if( s && s.dataFilter )
+                       data = s.dataFilter( data, type );
+
+               // The filter can actually parse the response
+               if( typeof data === "string" ){
+
+                       // If the type is "script", eval it in global context
+                       if ( type == "script" )
+                               jQuery.globalEval( data );
+
+                       // Get the JavaScript object, if JSON is used.
+                       if ( type == "json" )
+                               data = window["eval"]("(" + data + ")");
+               }
+               
+               return data;
+       },
+
+       // Serialize an array of form elements or a set of
+       // key/values into a query string
+       param: function( a ) {
+               var s = [ ];
+
+               function add( key, value ){
+                       s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
+               };
+
+               // If an array was passed in, assume that it is an array
+               // of form elements
+               if ( jQuery.isArray(a) || a.jquery )
+                       // Serialize the form elements
+                       jQuery.each( a, function(){
+                               add( this.name, this.value );
+                       });
+
+               // Otherwise, assume that it's an object of key/value pairs
+               else
+                       // Serialize the key/values
+                       for ( var j in a )
+                               // If the value is an array then the key names need to be repeated
+                               if ( jQuery.isArray(a[j]) )
+                                       jQuery.each( a[j], function(){
+                                               add( j, this );
+                                       });
+                               else
+                                       add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
+
+               // Return the resulting serialization
+               return s.join("&").replace(/%20/g, "+");
+       }
+
+});
+var elemdisplay = {},
+       timerId,
+       fxAttrs = [
+               // height animations
+               [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+               // width animations
+               [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+               // opacity animations
+               [ "opacity" ]
+       ];
+
+function genFx( type, num ){
+       var obj = {};
+       jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
+               obj[ this ] = type;
+       });
+       return obj;
+}
+
+jQuery.fn.extend({
+       show: function(speed,callback){
+               if ( speed ) {
+                       return this.animate( genFx("show", 3), speed, callback);
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ){
+                               var old = jQuery.data(this[i], "olddisplay");
+                               
+                               this[i].style.display = old || "";
+                               
+                               if ( jQuery.css(this[i], "display") === "none" ) {
+                                       var tagName = this[i].tagName, display;
+                                       
+                                       if ( elemdisplay[ tagName ] ) {
+                                               display = elemdisplay[ tagName ];
+                                       } else {
+                                               var elem = jQuery("<" + tagName + " />").appendTo("body");
+                                               
+                                               display = elem.css("display");
+                                               if ( display === "none" )
+                                                       display = "block";
+                                               
+                                               elem.remove();
+                                               
+                                               elemdisplay[ tagName ] = display;
+                                       }
+                                       
+                                       jQuery.data(this[i], "olddisplay", display);
+                               }
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( var i = 0, l = this.length; i < l; i++ ){
+                               this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
+                       }
+                       
+                       return this;
+               }
+       },
+
+       hide: function(speed,callback){
+               if ( speed ) {
+                       return this.animate( genFx("hide", 3), speed, callback);
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ){
+                               var old = jQuery.data(this[i], "olddisplay");
+                               if ( !old && old !== "none" )
+                                       jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( var i = 0, l = this.length; i < l; i++ ){
+                               this[i].style.display = "none";
+                       }
+
+                       return this;
+               }
+       },
+
+       // Save the old toggle function
+       _toggle: jQuery.fn.toggle,
+
+       toggle: function( fn, fn2 ){
+               var bool = typeof fn === "boolean";
+
+               return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
+                       this._toggle.apply( this, arguments ) :
+                       fn == null || bool ?
+                               this.each(function(){
+                                       var state = bool ? fn : jQuery(this).is(":hidden");
+                                       jQuery(this)[ state ? "show" : "hide" ]();
+                               }) :
+                               this.animate(genFx("toggle", 3), fn, fn2);
+       },
+
+       fadeTo: function(speed,to,callback){
+               return this.animate({opacity: to}, speed, callback);
+       },
+
+       animate: function( prop, speed, easing, callback ) {
+               var optall = jQuery.speed(speed, easing, callback);
+
+               return this[ optall.queue === false ? "each" : "queue" ](function(){
+               
+                       var opt = jQuery.extend({}, optall), p,
+                               hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
+                               self = this;
+       
+                       for ( p in prop ) {
+                               if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
+                                       return opt.complete.call(this);
+
+                               if ( ( p == "height" || p == "width" ) && this.style ) {
+                                       // Store display property
+                                       opt.display = jQuery.css(this, "display");
+
+                                       // Make sure that nothing sneaks out
+                                       opt.overflow = this.style.overflow;
+                               }
+                       }
+
+                       if ( opt.overflow != null )
+                               this.style.overflow = "hidden";
+
+                       opt.curAnim = jQuery.extend({}, prop);
+
+                       jQuery.each( prop, function(name, val){
+                               var e = new jQuery.fx( self, opt, name );
+
+                               if ( /toggle|show|hide/.test(val) )
+                                       e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+                               else {
+                                       var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
+                                               start = e.cur(true) || 0;
+
+                                       if ( parts ) {
+                                               var end = parseFloat(parts[2]),
+                                                       unit = parts[3] || "px";
+
+                                               // We need to compute starting value
+                                               if ( unit != "px" ) {
+                                                       self.style[ name ] = (end || 1) + unit;
+                                                       start = ((end || 1) / e.cur(true)) * start;
+                                                       self.style[ name ] = start + unit;
+                                               }
+
+                                               // If a +=/-= token was provided, we're doing a relative animation
+                                               if ( parts[1] )
+                                                       end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
+
+                                               e.custom( start, end, unit );
+                                       } else
+                                               e.custom( start, val, "" );
+                               }
+                       });
+
+                       // For JS strict compliance
+                       return true;
+               });
+       },
+
+       stop: function(clearQueue, gotoEnd){
+               var timers = jQuery.timers;
+
+               if (clearQueue)
+                       this.queue([]);
+
+               this.each(function(){
+                       // go in reverse order so anything added to the queue during the loop is ignored
+                       for ( var i = timers.length - 1; i >= 0; i-- )
+                               if ( timers[i].elem == this ) {
+                                       if (gotoEnd)
+                                               // force the next step to be the last
+                                               timers[i](true);
+                                       timers.splice(i, 1);
+                               }
+               });
+
+               // start the next in the queue if the last step wasn't forced
+               if (!gotoEnd)
+                       this.dequeue();
+
+               return this;
+       }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+       slideDown: genFx("show", 1),
+       slideUp: genFx("hide", 1),
+       slideToggle: genFx("toggle", 1),
+       fadeIn: { opacity: "show" },
+       fadeOut: { opacity: "hide" }
+}, function( name, props ){
+       jQuery.fn[ name ] = function( speed, callback ){
+               return this.animate( props, speed, callback );
+       };
+});
+
+jQuery.extend({
+
+       speed: function(speed, easing, fn) {
+               var opt = typeof speed === "object" ? speed : {
+                       complete: fn || !fn && easing ||
+                               jQuery.isFunction( speed ) && speed,
+                       duration: speed,
+                       easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+               };
+
+               opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+                       jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+               // Queueing
+               opt.old = opt.complete;
+               opt.complete = function(){
+                       if ( opt.queue !== false )
+                               jQuery(this).dequeue();
+                       if ( jQuery.isFunction( opt.old ) )
+                               opt.old.call( this );
+               };
+
+               return opt;
+       },
+
+       easing: {
+               linear: function( p, n, firstNum, diff ) {
+                       return firstNum + diff * p;
+               },
+               swing: function( p, n, firstNum, diff ) {
+                       return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+               }
+       },
+
+       timers: [],
+
+       fx: function( elem, options, prop ){
+               this.options = options;
+               this.elem = elem;
+               this.prop = prop;
+
+               if ( !options.orig )
+                       options.orig = {};
+       }
+
+});
+
+jQuery.fx.prototype = {
+
+       // Simple function for setting a style value
+       update: function(){
+               if ( this.options.step )
+                       this.options.step.call( this.elem, this.now, this );
+
+               (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+               // Set display property to block for height/width animations
+               if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
+                       this.elem.style.display = "block";
+       },
+
+       // Get the current size
+       cur: function(force){
+               if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
+                       return this.elem[ this.prop ];
+
+               var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+               return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+       },
+
+       // Start an animation from one number to another
+       custom: function(from, to, unit){
+               this.startTime = now();
+               this.start = from;
+               this.end = to;
+               this.unit = unit || this.unit || "px";
+               this.now = this.start;
+               this.pos = this.state = 0;
+
+               var self = this;
+               function t(gotoEnd){
+                       return self.step(gotoEnd);
+               }
+
+               t.elem = this.elem;
+
+               if ( t() && jQuery.timers.push(t) && !timerId ) {
+                       timerId = setInterval(function(){
+                               var timers = jQuery.timers;
+
+                               for ( var i = 0; i < timers.length; i++ )
+                                       if ( !timers[i]() )
+                                               timers.splice(i--, 1);
+
+                               if ( !timers.length ) {
+                                       clearInterval( timerId );
+                                       timerId = undefined;
+                               }
+                       }, 13);
+               }
+       },
+
+       // Simple 'show' function
+       show: function(){
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+               this.options.show = true;
+
+               // Begin the animation
+               // Make sure that we start at a small width/height to avoid any
+               // flash of content
+               this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
+
+               // Start by showing the element
+               jQuery(this.elem).show();
+       },
+
+       // Simple 'hide' function
+       hide: function(){
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
+               this.options.hide = true;
+
+               // Begin the animation
+               this.custom(this.cur(), 0);
+       },
+
+       // Each step of an animation
+       step: function(gotoEnd){
+               var t = now();
+
+               if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+                       this.now = this.end;
+                       this.pos = this.state = 1;
+                       this.update();
+
+                       this.options.curAnim[ this.prop ] = true;
+
+                       var done = true;
+                       for ( var i in this.options.curAnim )
+                               if ( this.options.curAnim[i] !== true )
+                                       done = false;
+
+                       if ( done ) {
+                               if ( this.options.display != null ) {
+                                       // Reset the overflow
+                                       this.elem.style.overflow = this.options.overflow;
+
+                                       // Reset the display
+                                       this.elem.style.display = this.options.display;
+                                       if ( jQuery.css(this.elem, "display") == "none" )
+                                               this.elem.style.display = "block";
+                               }
+
+                               // Hide the element if the "hide" operation was done
+                               if ( this.options.hide )
+                                       jQuery(this.elem).hide();
+
+                               // Reset the properties, if the item has been hidden or shown
+                               if ( this.options.hide || this.options.show )
+                                       for ( var p in this.options.curAnim )
+                                               jQuery.attr(this.elem.style, p, this.options.orig[p]);
+                                       
+                               // Execute the complete function
+                               this.options.complete.call( this.elem );
+                       }
+
+                       return false;
+               } else {
+                       var n = t - this.startTime;
+                       this.state = n / this.options.duration;
+
+                       // Perform the easing function, defaults to swing
+                       this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
+                       this.now = this.start + ((this.end - this.start) * this.pos);
+
+                       // Perform the next step of the animation
+                       this.update();
+               }
+
+               return true;
+       }
+
+};
+
+jQuery.extend( jQuery.fx, {
+       speeds:{
+               slow: 600,
+               fast: 200,
+               // Default speed
+               _default: 400
+       },
+       step: {
+
+               opacity: function(fx){
+                       jQuery.attr(fx.elem.style, "opacity", fx.now);
+               },
+
+               _default: function(fx){
+                       if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
+                               fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+                       else
+                               fx.elem[ fx.prop ] = fx.now;
+               }
+       }
+});
+if ( document.documentElement["getBoundingClientRect"] )
+       jQuery.fn.offset = function() {
+               if ( !this[0] ) return { top: 0, left: 0 };
+               if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+               var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
+                       clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+                       top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
+                       left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+               return { top: top, left: left };
+       };
+else 
+       jQuery.fn.offset = function() {
+               if ( !this[0] ) return { top: 0, left: 0 };
+               if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
+               jQuery.offset.initialized || jQuery.offset.initialize();
+
+               var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
+                       doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+                       body = doc.body, defaultView = doc.defaultView,
+                       prevComputedStyle = defaultView.getComputedStyle(elem, null),
+                       top = elem.offsetTop, left = elem.offsetLeft;
+
+               while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+                       computedStyle = defaultView.getComputedStyle(elem, null);
+                       top -= elem.scrollTop, left -= elem.scrollLeft;
+                       if ( elem === offsetParent ) {
+                               top += elem.offsetTop, left += elem.offsetLeft;
+                               if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
+                                       top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
+                                       left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+                               prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+                       }
+                       if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
+                               top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
+                               left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
+                       prevComputedStyle = computedStyle;
+               }
+
+               if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
+                       top  += body.offsetTop,
+                       left += body.offsetLeft;
+
+               if ( prevComputedStyle.position === "fixed" )
+                       top  += Math.max(docElem.scrollTop, body.scrollTop),
+                       left += Math.max(docElem.scrollLeft, body.scrollLeft);
+
+               return { top: top, left: left };
+       };
+
+jQuery.offset = {
+       initialize: function() {
+               if ( this.initialized ) return;
+               var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
+                       html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
+
+               rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
+               for ( prop in rules ) container.style[prop] = rules[prop];
+
+               container.innerHTML = html;
+               body.insertBefore(container, body.firstChild);
+               innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
+
+               this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+               this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+               innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
+               this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+               body.style.marginTop = '1px';
+               this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
+               body.style.marginTop = bodyMarginTop;
+
+               body.removeChild(container);
+               this.initialized = true;
+       },
+
+       bodyOffset: function(body) {
+               jQuery.offset.initialized || jQuery.offset.initialize();
+               var top = body.offsetTop, left = body.offsetLeft;
+               if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
+                       top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
+                       left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
+               return { top: top, left: left };
+       }
+};
+
+
+jQuery.fn.extend({
+       position: function() {
+               var left = 0, top = 0, results;
+
+               if ( this[0] ) {
+                       // Get *real* offsetParent
+                       var offsetParent = this.offsetParent(),
+
+                       // Get correct offsets
+                       offset       = this.offset(),
+                       parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+                       // Subtract element margins
+                       // note: when an element has margin: auto the offsetLeft and marginLeft 
+                       // are the same in Safari causing offset.left to incorrectly be 0
+                       offset.top  -= num( this, 'marginTop'  );
+                       offset.left -= num( this, 'marginLeft' );
+
+                       // Add offsetParent borders
+                       parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
+                       parentOffset.left += num( offsetParent, 'borderLeftWidth' );
+
+                       // Subtract the two offsets
+                       results = {
+                               top:  offset.top  - parentOffset.top,
+                               left: offset.left - parentOffset.left
+                       };
+               }
+
+               return results;
+       },
+
+       offsetParent: function() {
+               var offsetParent = this[0].offsetParent || document.body;
+               while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
+                       offsetParent = offsetParent.offsetParent;
+               return jQuery(offsetParent);
+       }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ['Left', 'Top'], function(i, name) {
+       var method = 'scroll' + name;
+       
+       jQuery.fn[ method ] = function(val) {
+               if (!this[0]) return null;
+
+               return val !== undefined ?
+
+                       // Set the scroll offset
+                       this.each(function() {
+                               this == window || this == document ?
+                                       window.scrollTo(
+                                               !i ? val : jQuery(window).scrollLeft(),
+                                                i ? val : jQuery(window).scrollTop()
+                                       ) :
+                                       this[ method ] = val;
+                       }) :
+
+                       // Return the scroll offset
+                       this[0] == window || this[0] == document ?
+                               self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
+                                       jQuery.boxModel && document.documentElement[ method ] ||
+                                       document.body[ method ] :
+                               this[0][ method ];
+       };
+});
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function(i, name){
+
+       var tl = i ? "Left"  : "Top",  // top or left
+               br = i ? "Right" : "Bottom", // bottom or right
+               lower = name.toLowerCase();
+
+       // innerHeight and innerWidth
+       jQuery.fn["inner" + name] = function(){
+               return this[0] ?
+                       jQuery.css( this[0], lower, false, "padding" ) :
+                       null;
+       };
+
+       // outerHeight and outerWidth
+       jQuery.fn["outer" + name] = function(margin) {
+               return this[0] ?
+                       jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
+                       null;
+       };
+       
+       var type = name.toLowerCase();
+
+       jQuery.fn[ type ] = function( size ) {
+               // Get window width or height
+               return this[0] == window ?
+                       // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+                       document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
+                       document.body[ "client" + name ] :
+
+                       // Get document width or height
+                       this[0] == document ?
+                               // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+                               Math.max(
+                                       document.documentElement["client" + name],
+                                       document.body["scroll" + name], document.documentElement["scroll" + name],
+                                       document.body["offset" + name], document.documentElement["offset" + name]
+                               ) :
+
+                               // Get or set width or height on the element
+                               size === undefined ?
+                                       // Get width or height on the element
+                                       (this.length ? jQuery.css( this[0], type ) : null) :
+
+                                       // Set the width or height on the element (default to pixels if value is unitless)
+                                       this.css( type, typeof size === "string" ? size : size + "px" );
+       };
+
+});
+})();
diff --git a/platforma/static/js/lib/jquery.json.js b/platforma/static/js/lib/jquery.json.js
new file mode 100644 (file)
index 0000000..87f5d01
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * jQuery JSON Plugin
+ * version: 2.1 (2009-08-14)
+ *
+ * This document is licensed as free software under the terms of the
+ * MIT License: http://www.opensource.org/licenses/mit-license.php
+ *
+ * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org 
+ * website's http://www.json.org/json2.js, which proclaims:
+ * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
+ * I uphold.
+ *
+ * It is also influenced heavily by MochiKit's serializeJSON, which is 
+ * copyrighted 2005 by Bob Ippolito.
+ */
+(function($) {
+    /** jQuery.toJSON( json-serializble )
+        Converts the given argument into a JSON respresentation.
+
+        If an object has a "toJSON" function, that will be used to get the representation.
+        Non-integer/string keys are skipped in the object, as are keys that point to a function.
+
+        json-serializble:
+            The *thing* to be converted.
+     **/
+    $.toJSON = function(o)
+    {
+        if (typeof(JSON) == 'object' && JSON.stringify)
+            return JSON.stringify(o);
+        
+        var type = typeof(o);
+    
+        if (o === null)
+            return "null";
+    
+        if (type == "undefined")
+            return undefined;
+        
+        if (type == "number" || type == "boolean")
+            return o + "";
+    
+        if (type == "string")
+            return $.quoteString(o);
+    
+        if (type == 'object')
+        {
+            if (typeof o.toJSON == "function") 
+                return $.toJSON( o.toJSON() );
+            
+            if (o.constructor === Date)
+            {
+                var month = o.getUTCMonth() + 1;
+                if (month < 10) month = '0' + month;
+
+                var day = o.getUTCDate();
+                if (day < 10) day = '0' + day;
+
+                var year = o.getUTCFullYear();
+                
+                var hours = o.getUTCHours();
+                if (hours < 10) hours = '0' + hours;
+                
+                var minutes = o.getUTCMinutes();
+                if (minutes < 10) minutes = '0' + minutes;
+                
+                var seconds = o.getUTCSeconds();
+                if (seconds < 10) seconds = '0' + seconds;
+                
+                var milli = o.getUTCMilliseconds();
+                if (milli < 100) milli = '0' + milli;
+                if (milli < 10) milli = '0' + milli;
+
+                return '"' + year + '-' + month + '-' + day + 'T' +
+                             hours + ':' + minutes + ':' + seconds + 
+                             '.' + milli + 'Z"'; 
+            }
+
+            if (o.constructor === Array) 
+            {
+                var ret = [];
+                for (var i = 0; i < o.length; i++)
+                    ret.push( $.toJSON(o[i]) || "null" );
+
+                return "[" + ret.join(",") + "]";
+            }
+        
+            var pairs = [];
+            for (var k in o) {
+                var name;
+                var type = typeof k;
+
+                if (type == "number")
+                    name = '"' + k + '"';
+                else if (type == "string")
+                    name = $.quoteString(k);
+                else
+                    continue;  //skip non-string or number keys
+            
+                if (typeof o[k] == "function") 
+                    continue;  //skip pairs where the value is a function.
+            
+                var val = $.toJSON(o[k]);
+            
+                pairs.push(name + ":" + val);
+            }
+
+            return "{" + pairs.join(", ") + "}";
+        }
+    };
+
+    /** jQuery.evalJSON(src)
+        Evaluates a given piece of json source.
+     **/
+    $.evalJSON = function(src)
+    {
+        if (typeof(JSON) == 'object' && JSON.parse)
+            return JSON.parse(src);
+        return eval("(" + src + ")");
+    };
+    
+    /** jQuery.secureEvalJSON(src)
+        Evals JSON in a way that is *more* secure.
+    **/
+    $.secureEvalJSON = function(src)
+    {
+        if (typeof(JSON) == 'object' && JSON.parse)
+            return JSON.parse(src);
+        
+        var filtered = src;
+        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
+        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+        
+        if (/^[\],:{}\s]*$/.test(filtered))
+            return eval("(" + src + ")");
+        else
+            throw new SyntaxError("Error parsing JSON, source is not valid.");
+    };
+
+    /** jQuery.quoteString(string)
+        Returns a string-repr of a string, escaping quotes intelligently.  
+        Mostly a support function for toJSON.
+    
+        Examples:
+            >>> jQuery.quoteString("apple")
+            "apple"
+        
+            >>> jQuery.quoteString('"Where are we going?", she asked.')
+            "\"Where are we going?\", she asked."
+     **/
+    $.quoteString = function(string)
+    {
+        if (string.match(_escapeable))
+        {
+            return '"' + string.replace(_escapeable, function (a) 
+            {
+                var c = _meta[a];
+                if (typeof c === 'string') return c;
+                c = a.charCodeAt();
+                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
+            }) + '"';
+        }
+        return '"' + string + '"';
+    };
+    
+    var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
+    
+    var _meta = {
+        '\b': '\\b',
+        '\t': '\\t',
+        '\n': '\\n',
+        '\f': '\\f',
+        '\r': '\\r',
+        '"' : '\\"',
+        '\\': '\\\\'
+    };
+})(jQuery);
diff --git a/platforma/static/js/lib/jquery.lazyload.js b/platforma/static/js/lib/jquery.lazyload.js
new file mode 100755 (executable)
index 0000000..c15167b
--- /dev/null
@@ -0,0 +1,52 @@
+(function($) {
+    jQuery.fn.lazyload = function(pattern, options) {
+        var settings = {
+            threshold: 0,
+            scrollThreshold: 300,
+            placeholder: 'loading...',
+            checkInterval: 2000
+        };
+        
+        if (options) {
+            $.extend(settings, options);
+        }
+        
+        var container = this;
+        container.data('lazyload:lastCheckedScrollTop', -10000);
+        
+        function aboveViewport(container, element, threshold) {
+            return $(container).offset().top >= $(element).offset().top + $(element).height() + threshold;
+        }
+        
+        function belowViewport(container, element, threshold) {
+            return $(container).offset().top + $(container).height() + threshold <= $(element).offset().top;
+        }
+        
+        function checkScroll() {
+            if (container.data('lazyload:lastCheckedScrollTop') == undefined) {
+                return;
+            }
+            if (Math.abs(container.scrollTop() - container.data('lazyload:lastCheckedScrollTop')) > settings.scrollThreshold) {
+                container.data('lazyload:lastCheckedScrollTop', container.scrollTop());
+                
+                $(pattern, container).each(function() {
+                    if (aboveViewport(container, this, settings.threshold)
+                        || belowViewport(container, this, settings.threshold)) {
+                        $(this).html(settings.placeholder);
+                    } else {
+                        $(this).html('');
+                        var self = this;
+                        $('<img src="' + $(this).attr('src') + '" width="' + $(this).width() + '" />').load(function() {
+                            if ($(this).height() > $(self).height()) {
+                                $(self).height($(this).height());
+                            }
+                        }).appendTo(this);
+                    }
+                })
+            }
+            setTimeout(checkScroll, settings.checkInterval);
+        }
+        
+        checkScroll();
+    };
+})(jQuery);
diff --git a/platforma/static/js/lib/jquery.logging.js b/platforma/static/js/lib/jquery.logging.js
new file mode 100644 (file)
index 0000000..d6cee14
--- /dev/null
@@ -0,0 +1,41 @@
+(function($) {
+       var LEVEL_DEBUG = 1;
+       var LEVEL_INFO = 2;
+       var LEVEL_WARN = 3;
+       var LOG_LEVEL = LEVEL_DEBUG;
+       
+        var standardLog = function() {
+            if (window.console)
+                console.log.apply(console, arguments);
+        };
+    
+       var operaLog = function() {
+               opera.postError(arguments.join(' '));
+       };
+
+        var msieLog = function() {
+            var args = $.makeArray(arguments);
+            var vals = $.map(args, function(n) {
+                try {
+                    return JSON.stringify(n);
+                } catch(e) {
+                    return ('' + n);
+                }
+            });
+
+            if (window.console)
+                console.log(vals.join(" "));
+        };
+
+       $.log = function() {
+               return $.log.browserLog.apply(this, arguments);
+       };
+
+        if($.browser.opera)
+            $.log.browserLog = operaLog;
+        else if($.browser.msie)
+            $.log.browserLog = msieLog;
+        else
+            $.log.browserLog = standardLog;
+
+})(jQuery);
diff --git a/platforma/static/js/lib/jquery.modal.js b/platforma/static/js/lib/jquery.modal.js
new file mode 100755 (executable)
index 0000000..3aac816
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * jqModal - Minimalist Modaling with jQuery
+ *   (http://dev.iceburg.net/jquery/jqModal/)
+ *
+ * Copyright (c) 2007,2008 Brice Burgess <bhb@iceburg.net>
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ * 
+ * $Version: 03/01/2009 +r14
+ */
+(function($) {
+$.fn.jqm=function(o){
+var p={
+overlay: 50,
+overlayClass: 'jqmOverlay',
+closeClass: 'jqmClose',
+trigger: '.jqModal',
+ajax: F,
+ajaxText: '',
+target: F,
+modal: F,
+toTop: F,
+onShow: F,
+onHide: F,
+onLoad: F
+};
+return this.each(function(){if(this._jqm)return H[this._jqm].c=$.extend({},H[this._jqm].c,o);s++;this._jqm=s;
+H[s]={c:$.extend(p,$.jqm.params,o),a:F,w:$(this).addClass('jqmID'+s),s:s};
+if(p.trigger)$(this).jqmAddTrigger(p.trigger);
+});};
+
+$.fn.jqmAddClose=function(e){return hs(this,e,'jqmHide');};
+$.fn.jqmAddTrigger=function(e){return hs(this,e,'jqmShow');};
+$.fn.jqmShow=function(t){return this.each(function(){t=t||window.event;$.jqm.open(this._jqm,t);});};
+$.fn.jqmHide=function(t){return this.each(function(){t=t||window.event;$.jqm.close(this._jqm,t)});};
+
+$.jqm = {
+hash:{},
+open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=(parseInt(h.w.css('z-index'))),z=(z>0)?z:3000,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});if(h.a)return F;h.t=t;h.a=true;h.w.css('z-index',z);
+ if(c.modal) {if(!A[0])L('bind');A.push(s);}
+ else if(c.overlay > 0)h.w.jqmAddClose(o);
+ else o=F;
+
+ h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):F;
+ if(ie6){$('html,body').css({height:'100%',width:'100%'});if(o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}}
+
+ if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
+  r.html(c.ajaxText).load(u,function(){if(c.onLoad)c.onLoad.call(this,h);if(cc)h.w.jqmAddClose($(cc,h.w));e(h);});}
+ else if(cc)h.w.jqmAddClose($(cc,h.w));
+
+ if(c.toTop&&h.o)h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);        
+ (c.onShow)?c.onShow(h):h.w.show();e(h);return F;
+},
+close:function(s){var h=H[s];if(!h.a)return F;h.a=F;
+ if(A[0]){A.pop();if(!A[0])L('unbind');}
+ if(h.c.toTop&&h.o)$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
+ if(h.c.onHide)h.c.onHide(h);else{h.w.hide();if(h.o)h.o.remove();} return F;
+},
+params:{}};
+var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),F=false,
+i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
+e=function(h){if(ie6)if(h.o)h.o.html('<p style="width:100%;height:100%"/>').prepend(i);else if(!$('iframe.jqm',h.w)[0])h.w.prepend(i); f(h);},
+f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(_){}},
+L=function(t){$()[t]("keypress",m)[t]("keydown",m)[t]("mousedown",m);},
+m=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);if(r)f(h);return !r;},
+hs=function(w,t,c){return w.each(function(){var s=this._jqm;$(t).each(function() {
+ if(!this[c]){this[c]=[];$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return F;});}this[c].push(s);});});};
+})(jQuery);
\ No newline at end of file
diff --git a/platforma/static/js/lib/jquery.wtooltip.js b/platforma/static/js/lib/jquery.wtooltip.js
new file mode 100644 (file)
index 0000000..01233ba
--- /dev/null
@@ -0,0 +1,234 @@
+/* Wayfarer Tooltip\r
+ * Version 1.0.4\r
+ * Author Abel Mohler\r
+ * URI: http://www.wayfarerweb.com/wtooltip.php\r
+ * Released with the MIT License: http://www.wayfarerweb.com/mit.php\r
+ */\r
+(function($){ //jQuery.noConflict()compliant\r
+    $.fn.wTooltip = function(o, callback){\r
+        o = $.extend({ //defaults, can be overidden\r
+            content: null, //string content for tooltip.\r
+            ajax: null, //path to content for tooltip\r
+            follow: true, //does tooltip follow the cursor?\r
+            auto: true, //If false, tooltip won't automatically transition, it must be manually shown/hidden\r
+            fadeIn: 0, //fade in, in milliseconds ("fast, "slow", etc may also be used)\r
+            fadeOut: 0, //fade out, in milliseconds ("fast, "slow", etc may also be used)\r
+            appendTip: document.body, //should probably not need to be overridden\r
+            degrade: false, //if true, in IE6 tooltip will degrade to a title attribute message\r
+            offsetY: 10, //offsetY and offsetX properties designate position from the cursor\r
+            offsetX: 1,\r
+            style: {},\r
+            className: null, //to style the tooltip externally, pass a className or id\r
+            id: null,\r
+            callBefore: function(tooltip, node, settings){\r
+            }, //called when mouse enters the area\r
+            callAfter: function(tooltip, node, settings){\r
+            }, //called when mouse leaves the area (same as "callback" option)\r
+            clickAction: function(tooltip, node){\r
+                $(tooltip).hide();\r
+            }, //called when the element is clicked, with access to tooltip\r
+            delay: 0, //delay (in milliseconds)before tooltip appears and callBefore executes\r
+            timeout: 0, //delay (in milliseconds)before tooltip transitions away, and callAfter executes\r
+            cloneable: false //UNWORKING.  Requires $().wClone plugin. If true, tooltip may be dragged and placed anywhere on the screen.\r
+        }, o ||\r
+        {});\r
+        \r
+        if (!o.style && typeof o.style != "object") {\r
+            o.style = {};\r
+            o.style.zIndex = "1000";\r
+        }\r
+        else {\r
+            o.style = $.extend({ //the default style rules of the tooltip\r
+                border: "1px solid gray",\r
+                background: "#edeef0",\r
+                color: "#000",\r
+                padding: "10px",\r
+                zIndex: "1000",\r
+                textAlign: "left"\r
+            }, o.style ||\r
+            {});\r
+        }\r
+        \r
+        if (typeof callback == "function") \r
+            o.callAfter = callback || o.callAfter;\r
+        \r
+        o.style.display = "none", o.style.position = "absolute"; //permanent defaults\r
+        //private settings\r
+        var title, timeout, timeout2, iId, over = {}, firstMove = true, hovered = false, maxed = false, tooltip = document.createElement('div'), ie6 = (typeof document.body.style.maxWidth == "undefined") ? true : false, talk = (typeof $.talk == "function" && typeof $.listen == "function") ? true : false;\r
+        \r
+        if (o.id) \r
+            tooltip.id = o.id;\r
+        if (o.className) \r
+            tooltip.className = o.className;\r
+        \r
+        o.degrade = (o.degrade && ie6) ? true : false; //only degrades if also IE6\r
+        for (var p in o.style)//apply styles to tooltip\r
+             tooltip.style[p] = o.style[p];\r
+        \r
+        function fillTooltip(condition){\r
+            if (condition) {\r
+                if (o.degrade)//replace html characters for proper degradation to title attribute\r
+                    $(tooltip).html(o.content.replace(/<\/?[^>]+>/gi, ''));\r
+                else //otherwise just fill the tooltip with content\r
+                     $(tooltip).html(o.content);\r
+            }\r
+        }\r
+        \r
+        if (o.ajax) { //if o.ajax is selected, this will fill and thus override o.content\r
+            $.get(o.ajax, function(data){\r
+                if (data) \r
+                    o.content = data;\r
+                fillTooltip(o.content);\r
+            });\r
+        }\r
+        \r
+        function offConditions(that){\r
+            function _offActions(that){\r
+                if (title && !o.content) {\r
+                    that.title = title;\r
+                    title = null;\r
+                }\r
+            }\r
+            function _execute(){\r
+                if (!hovered && o.auto) {\r
+                    clearInterval(iId);\r
+                    if (o.fadeOut) {\r
+                        $(tooltip).fadeOut(o.fadeOut, function(){\r
+                            _offActions(that);\r
+                        });\r
+                    }\r
+                    else {\r
+                        _offActions(that);\r
+                        tooltip.style.display = "none";\r
+                    }\r
+                }\r
+                if (typeof o.callAfter == "function") \r
+                    o.callAfter(tooltip, that, o);\r
+                if (talk) \r
+                    o = $.listen(o);\r
+            }\r
+            if (o.timeout > 0) {\r
+                timeout2 = setTimeout(function(){\r
+                    _execute();\r
+                }, o.timeout);\r
+            }\r
+            else {\r
+                _execute();\r
+            }\r
+        }\r
+        \r
+        $(tooltip).hover(function(){\r
+            hovered = true;\r
+        }, function(){\r
+            hovered = false;\r
+            offConditions(over);\r
+        });\r
+        \r
+        //initialize\r
+        if (talk) { //A "channel" for plugins to "talk" to each other, and callbacks to manipulate settings\r
+            o.key = tooltip;\r
+            o.plugin = "wTooltip";\r
+            o.channel = "wayfarer";\r
+            $.talk(o);\r
+        }\r
+        \r
+        fillTooltip(o.content && !o.ajax);\r
+        $(tooltip).appendTo(o.appendTip);\r
+        \r
+        return this.each(function(){ //returns the element chain\r
+            this.onmouseover = function(ev){\r
+                var that = this;\r
+                clearTimeout(timeout2);\r
+                if (this.title && !o.degrade && !o.content) {\r
+                    title = this.title;\r
+                    this.title = "";\r
+                }\r
+                if (o.content && o.degrade) \r
+                    this.title = tooltip.innerHTML;\r
+                \r
+                function _execute(){\r
+                    if (typeof o.callBefore == "function") \r
+                        o.callBefore(tooltip, that, o);\r
+                    if (talk) \r
+                        o = $.listen(o); //ping for new settings\r
+                    if (o.auto) {\r
+                        var display;\r
+                        if (o.content) {\r
+                            if (!o.degrade) \r
+                                display = "block";\r
+                        }\r
+                        else \r
+                            if (title && !o.degrade) {\r
+                                $(tooltip).html(unescape(title));\r
+                                display = "block";\r
+                            }\r
+                            else {\r
+                                display = "none";\r
+                            }\r
+                        if (display == "block" && o.fadeIn) \r
+                            $(tooltip).fadeIn(o.fadeIn);\r
+                        else \r
+                            tooltip.style.display = display;\r
+                    }\r
+                }\r
+                \r
+                if (o.delay > 0) {\r
+                    timeout = setTimeout(function(){\r
+                        _execute();\r
+                    }, o.delay);\r
+                }\r
+                else {\r
+                    _execute();\r
+                }\r
+            }\r
+            \r
+            this.onmousemove = function(ev){\r
+                var e = (ev) ? ev : window.event, that = this;\r
+                over = this; //tracks the event trigger in the plugin-global "over"\r
+                if (o.follow || firstMove) {\r
+                    var scrollY = $(window).scrollTop(), scrollX = $(window).scrollLeft(), top = e.clientY + scrollY + o.offsetY, left = e.clientX + scrollX + o.offsetX, outerH = $(o.appendTip).outerHeight(), innerH = $(o.appendTip).innerHeight(), maxLeft = $(window).width() + scrollX - $(tooltip).outerWidth(), maxTop = $(window).height() + scrollY - $(tooltip).outerHeight();\r
+                    \r
+                    top = (outerH > innerH) ? top - (outerH - innerH) : top; //if appended area (usually BODY) has a border on top, adjust\r
+                    maxed = (top > maxTop || left > maxLeft) ? true : false;\r
+                    \r
+                    if (left - scrollX <= 0 && o.offsetX < 0) \r
+                        left = scrollX;\r
+                    else \r
+                        if (left > maxLeft) \r
+                            left = maxLeft;\r
+                    if (top - scrollY <= 0 && o.offsetY < 0) \r
+                        top = scrollY;\r
+                    else \r
+                        if (top > maxTop) \r
+                            top = maxTop;\r
+                    \r
+                    tooltip.style.top = top + "px";\r
+                    tooltip.style.left = left + "px";\r
+                    firstMove = false;\r
+                }\r
+            }\r
+            \r
+            this.onmouseout = function(){\r
+                clearTimeout(timeout);\r
+                var that = this;\r
+                firstMove = true;\r
+                if (!o.follow || maxed || (o.offsetX < 0 && o.offsetY < 0)) {\r
+                    setTimeout(function(){\r
+                        iId = setInterval(function(){\r
+                            offConditions(that)\r
+                        }, 1)\r
+                    }, 1);\r
+                }\r
+                else {\r
+                    offConditions(this);\r
+                }\r
+            }\r
+            \r
+            if (typeof o.clickAction == "function") {\r
+                this.onclick = function(){\r
+                    o.clickAction(tooltip, this);\r
+                }\r
+            }\r
+        });\r
+    }\r
+})(jQuery);
\ No newline at end of file
diff --git a/platforma/static/js/messages.js b/platforma/static/js/messages.js
new file mode 100644 (file)
index 0000000..eddb46e
--- /dev/null
@@ -0,0 +1,71 @@
+/*global Editor*/
+Editor.MessageCenter = Editor.Object.extend({
+    init: function() {
+        this.messages = [];
+        this.flashMessages = [];
+        this.firstFlashMessage = null;
+        this.timeout = null;
+        console.log("MSC-init:", Date(), this);
+    },
+  
+    addMessage: function(type, tag, text, flash)
+    {
+        if (!tag) tag = '#default'
+        
+        if (!flash) {
+            flash = text;
+        }
+
+        this.messages.push({
+            type: type,
+            text: text
+        });
+
+        this.flashMessages.push({
+            type: type,
+            text: flash,
+            tag: tag
+        });
+
+        if(this.timeout) {
+            if(this.flashMessages[0] && (this.flashMessages[0].tag == tag))
+            {
+                clearTimeout(this.timeout);
+                this.timeout = null;
+                this.changeFlashMessage();
+            }
+        }       
+        
+        else {
+            /* queue was empty at the start */
+            if (this.flashMessages.length == 1) {
+                console.log("MSC-added-fisrt", Date(), this);
+                this.set('firstFlashMessage', this.flashMessages[0]);
+                this.timeout = setTimeout(this.changeFlashMessage.bind(this), 3000);
+            }
+
+        }
+        
+    },
+  
+    changeFlashMessage: function() 
+    {
+        console.log("MSC-change", Date(), this);
+        var previous = this.flashMessages.splice(0, 1);
+        
+        if (this.flashMessages.length > 0) 
+        {
+            console.log("MSC-chaning-first", Date(), this);
+            this.set('firstFlashMessage', this.flashMessages[0]);            
+            this.timeout = setTimeout(this.changeFlashMessage.bind(this), 3000);
+        } else {
+            console.log("MSC-emptying", Date(), this);
+            this.set('firstFlashMessage', null);
+        }
+    }
+  
+});
+
+
+var messageCenter = new Editor.MessageCenter();
+
diff --git a/platforma/static/js/models.js b/platforma/static/js/models.js
new file mode 100644 (file)
index 0000000..6399c03
--- /dev/null
@@ -0,0 +1,604 @@
+/*globals Editor fileId SplitView PanelContainerView EditorView FlashView messageCenter*/
+Editor.Model = Editor.Object.extend({
+    synced: false,
+    data: null
+});
+
+Editor.ToolbarButtonsModel = Editor.Model.extend({
+    className: 'Editor.ToolbarButtonsModel',
+    buttons: {},
+  
+    init: function() {
+        this._super();
+    },
+  
+    load: function() {
+        if (!this.get('buttons').length) {
+            $.ajax({
+                url: documentInfo.toolbarURL,
+                dataType: 'json',
+                success: this.loadSucceeded.bind(this)
+            });
+        }
+    },
+  
+    loadSucceeded: function(data)
+    {
+        // do some escaping
+        $.each(data, function() {
+            $.each(this.buttons, function() {
+                //do some lame escapes
+                this.tooltip = this.tooltip.replace(/"/g, "&#34;");
+            });
+        });
+        this.set('buttons', data);
+    }
+});
+
+
+// Stany modelu:
+//
+//                  -> error -> loading
+//                 /
+// empty -> loading -> synced -> unsynced -> loading
+//                           \
+//                            -> dirty -> updating -> updated -> synced
+//
+Editor.XMLModel = Editor.Model.extend({
+    _className: 'Editor.XMLModel',
+    serverURL: null,
+    data: '',
+    state: 'empty',
+  
+    init: function(document, serverURL) {
+        this._super();
+        this.set('state', 'empty');
+        this.set('revision', document.get('revision'));
+        this.document = document;
+        this.serverURL = serverURL;
+        this.toolbarButtonsModel = new Editor.ToolbarButtonsModel();
+        this.addObserver(this, 'data', this.dataChanged.bind(this));
+    },
+  
+    load: function(force) {
+        if (force || this.get('state') == 'empty') {
+            this.set('state', 'loading');
+            messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...');
+            $.ajax({
+                url: this.serverURL,
+                dataType: 'text',
+                data: {
+                    revision: this.get('revision'),
+                    user: this.document.get('user')
+                    },
+                success: this.loadingSucceeded.bind(this),
+                error: this.loadingFailed.bind(this)
+            });
+            return true;
+        }
+        return false;
+    },
+  
+    loadingSucceeded: function(data) {
+        if (this.get('state') != 'loading') {
+            alert('erroneous state:', this.get('state'));
+        }
+        this.set('data', data);
+        this.set('state', 'synced');
+        messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)');
+    },
+  
+    loadingFailed: function() {
+        if (this.get('state') != 'loading') {
+            alert('erroneous state:', this.get('state'));
+        }
+        var message = parseXHRError(response);
+        
+        this.set('error', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
+        this.set('state', 'error');
+        messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-(');
+    },
+  
+    save: function(message) {
+        if (this.get('state') == 'dirty') {
+            this.set('state', 'updating');
+            messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...');
+      
+            var payload = {
+                contents: this.get('data'),
+                revision: this.get('revision'),
+                user: this.document.get('user')
+            };
+            if (message) {
+                payload.message = message;
+            }
+      
+            $.ajax({
+                url: this.serverURL,
+                type: 'post',
+                dataType: 'json',
+                data: payload,
+                success: this.saveSucceeded.bind(this),
+                error: this.saveFailed.bind(this)
+            });
+            return true;
+        }
+        return false;
+    },
+  
+    saveSucceeded: function(data) {
+        if (this.get('state') != 'updating') {
+            alert('erroneous state:', this.get('state'));
+        }
+        this.set('revision', data.revision);
+        this.set('state', 'updated');
+        messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)');
+    },
+  
+    saveFailed: function() {
+        if (this.get('state') != 'updating') {
+            alert('erroneous state:', this.get('state'));
+        }
+        messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-(');
+        this.set('state', 'dirty');
+    },
+  
+    // For debbuging
+    set: function(property, value) {
+        if (property == 'state') {
+            console.log(this.description(), ':', property, '=', value);
+        }
+        return this._super(property, value);
+    },
+  
+    dataChanged: function(property, value) {
+        if (this.get('state') == 'synced') {
+            this.set('state', 'dirty');
+        }
+    },
+  
+    dispose: function() {
+        this.removeObserver(this);
+        this._super();
+    }
+});
+
+
+Editor.HTMLModel = Editor.Model.extend({
+    _className: 'Editor.HTMLModel',
+    dataURL: null,
+    htmlURL: null,
+    renderURL: null,
+    displaData: '',
+    xmlParts: {},
+    state: 'empty',
+  
+    init: function(document, dataURL, htmlURL) {
+        this._super();
+        this.set('state', 'empty');
+        this.set('revision', document.get('revision'));        
+        
+        this.document = document;
+        this.htmlURL = htmlURL;
+        this.dataURL = dataURL;
+        this.renderURL = documentInfo.renderURL;
+        this.xmlParts = {};
+    },
+  
+    load: function(force) {
+        if (force || this.get('state') == 'empty') {
+            this.set('state', 'loading');
+
+            // load the transformed data
+            // messageCenter.addMessage('info', 'Wczytuję HTML...');
+
+            $.ajax({
+                url: this.htmlURL,
+                dataType: 'text',
+                data: {
+                    revision: this.get('revision'),
+                    user: this.document.get('user')
+                    },
+                success: this.loadingSucceeded.bind(this),
+                error: this.loadingFailed.bind(this)
+            });
+        }
+    },    
+  
+    loadingSucceeded: function(data) {
+        if (this.get('state') != 'loading') {
+            alert('erroneous state:', this.get('state'));
+        }
+        this.set('data', data);
+        this.set('state', 'synced');
+    },
+  
+    loadingFailed: function(response) {
+        if (this.get('state') != 'loading') {
+            alert('erroneous state:', this.get('state'));
+        }
+        
+        var message = parseXHRError(response);
+        
+        this.set('error', '<p>Nie udało się wczytać widoku HTML: </p>' + message);
+        this.set('state', 'error');        
+    },
+
+    getXMLPart: function(elem, callback)
+    {
+        var path = elem.attr('wl2o:path');
+        if(!this.xmlParts[path])
+            this.loadXMLPart(elem, callback);
+        else
+            callback(path, this.xmlParts[path]);
+    },
+
+    loadXMLPart: function(elem, callback)
+    {
+        var path = elem.attr('wl2o:path');
+        var self = this;
+
+        $.ajax({
+            url: this.dataURL,
+            dataType: 'text',
+            data: {
+                revision: this.get('revision'),
+                user: this.document.get('user'),
+                part: path
+            },
+            success: function(data) {
+                self.xmlParts[path] = data;
+                callback(path, data);
+            },
+            // TODO: error handling
+            error: function(data) {
+                console.log('Failed to load fragment');
+                callback(undefined, undefined);
+            }
+        });
+    },
+
+    putXMLPart: function(elem, data) {
+        var self = this;
+      
+        var path = elem.attr('wl2o:path');
+        this.xmlParts[path] = data;
+
+        this.set('state', 'unsynced');
+
+        /* re-render the changed fragment */
+        $.ajax({
+            url: this.renderURL,
+            type: "POST",
+            dataType: 'text; charset=utf-8',
+            data: {
+                fragment: data,
+                part: path
+            },
+            success: function(htmldata) {
+                elem.replaceWith(htmldata);
+                self.set('state', 'dirty');
+            }
+        });
+    },
+
+    save: function(message) {
+        if (this.get('state') == 'dirty') {
+            this.set('state', 'updating');
+
+            var payload = {
+                chunks: $.toJSON(this.xmlParts),
+                revision: this.get('revision'),
+                user: this.document.get('user')
+            };
+
+            if (message) {
+                payload.message = message;
+            }
+
+            console.log(payload)
+
+            $.ajax({
+                url: this.dataURL,
+                type: 'post',
+                dataType: 'json',
+                data: payload,
+                success: this.saveSucceeded.bind(this),
+                error: this.saveFailed.bind(this)
+            });
+            return true;
+        }
+        return false;
+      
+    },
+
+    saveSucceeded: function(data) {
+        if (this.get('state') != 'updating') {
+            alert('erroneous state:', this.get('state'));
+        }
+
+        // flush the cache
+        this.xmlParts = {};
+    
+        this.set('revision', data.revision);
+        this.set('state', 'updated');
+    },
+
+    saveFailed: function() {
+        if (this.get('state') != 'updating') {
+            alert('erroneous state:', this.get('state'));
+        }        
+        this.set('state', 'dirty');
+    },
+
+    // For debbuging
+    set: function(property, value) {
+        if (property == 'state') {
+            console.log(this.description(), ':', property, '=', value);
+        }
+        return this._super(property, value);
+    }
+});
+
+
+Editor.ImageGalleryModel = Editor.Model.extend({
+    _className: 'Editor.ImageGalleryModel',
+    serverURL: null,
+    data: [],
+    state: 'empty',
+
+    init: function(serverURL) {
+        this._super();
+        this.set('state', 'empty');
+        this.serverURL = serverURL;
+        // olewać data
+        this.pages = [];
+    },
+
+    load: function(force) {
+        if (force || this.get('state') == 'empty') {
+            this.set('state', 'loading');
+            $.ajax({
+                url: this.serverURL,
+                dataType: 'json',
+                success: this.loadingSucceeded.bind(this)
+            });
+        }
+    },
+
+    loadingSucceeded: function(data) {
+        if (this.get('state') != 'loading') {
+            alert('erroneous state:', this.get('state'));
+        }
+
+        console.log('galleries:', data);
+
+        if (data.length === 0) {
+            this.set('data', []);
+        } else {            
+            this.set('data', data[0].pages);
+        }
+
+        this.set('state', 'synced');
+    },
+
+    set: function(property, value) {
+        if (property == 'state') {
+            console.log(this.description(), ':', property, '=', value);
+        }
+        return this._super(property, value);
+    }
+});
+
+
+Editor.DocumentModel = Editor.Model.extend({
+    _className: 'Editor.DocumentModel',
+    data: null, // name, text_url, user_revision, latest_shared_rev, parts_url, dc_url, size, merge_url
+    contentModels: {},
+    state: 'empty',
+    errors: '',
+    revision: '',
+    user: '',
+  
+    init: function() {
+        this._super();
+        this.set('state', 'empty');        
+    },
+  
+    load: function() {
+        if (this.get('state') == 'empty') {
+            this.set('state', 'loading');
+            messageCenter.addMessage('info', 'docload', 'Ładuję dane dokumentu...');
+            $.ajax({
+                cache: false,
+                url: documentInfo.docURL,
+                dataType: 'json',
+                success: this.successfulLoad.bind(this),
+                error: this.failedLoad.bind(this)
+            });
+        }
+    },
+  
+    successfulLoad: function(data) {
+        this.set('data', data);
+        this.set('state', 'synced');
+
+        this.set('revision', data.user_revision);
+        this.set('user', data.user);
+
+        this.contentModels = {
+            'xml': new Editor.XMLModel(this, data.text_url),
+            'html': new Editor.HTMLModel(this, data.text_url, data.html_url),
+            'gallery': new Editor.ImageGalleryModel(this, data.gallery_url)
+        };        
+
+        for (var key in this.contentModels) {
+            this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
+        }
+
+        this.error = '';
+
+        messageCenter.addMessage('success', 'docload', 'Dokument załadowany poprawnie :-)');
+    },
+
+    failedLoad: function(response) {
+        if (this.get('state') != 'loading') {
+            alert('erroneous state:', this.get('state'));
+        }
+        
+        var message = parseXHRError(response);        
+        this.set('error', '<h2>Nie udało się wczytać dokumentu</h2><p>'+message+"</p>");
+        this.set('state', 'error');
+    },
+  
+    contentModelStateChanged: function(property, value, contentModel) {
+        if (value == 'dirty') {
+            this.set('state', 'dirty');
+            for (var key in this.contentModels) {
+                if (this.contentModels[key].guid() != contentModel.guid()) {
+                    this.contentModels[key].set('state', 'unsynced');
+                }
+            }
+        } else if (value == 'updated') {
+            this.set('state', 'synced');
+            for (key in this.contentModels) {
+                if (this.contentModels[key].guid() == contentModel.guid()) {
+                    this.contentModels[key].set('state', 'synced');
+                    this.data.user_revision = this.contentModels[key].get('revision');
+                }
+            }
+            for (key in this.contentModels) {
+                if (this.contentModels[key].guid() != contentModel.guid()) {
+                    this.contentModels[key].set('revision', this.data.user_revision);
+                    this.contentModels[key].set('state', 'empty');
+                }
+            }
+        }
+    },
+  
+    saveDirtyContentModel: function(message) {
+        for (var key in this.contentModels) {
+            if (this.contentModels[key].get('state') == 'dirty') {
+                this.contentModels[key].save(message);
+                break;
+            }
+        }
+    },
+  
+    update: function() {
+        this.set('state', 'loading');
+        messageCenter.addMessage('info', 'Uaktualniam dokument...');
+        $.ajax({
+            url: this.data.merge_url,
+            dataType: 'json',
+            type: 'post',
+            data: {
+                type: 'update',
+                revision: this.revision,
+                user: this.user
+            },
+            complete: this.updateCompleted.bind(this),
+            success: function(data) {
+                this.set('updateData', data);
+            }.bind(this)
+        });
+    },
+  
+    updateCompleted: function(xhr, textStatus) {
+        console.log(xhr.status, textStatus);
+        if (xhr.status == 200) { // Sukces
+            this.data = this.get('updateData');
+            this.revision = this.data.user_revision;
+            this.user = this.data.user;
+            
+            messageCenter.addMessage('info', null, 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision,
+                'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision);
+            for (var key in this.contentModels) {
+                this.contentModels[key].set('revision', this.data.user_revision);
+                this.contentModels[key].set('state', 'empty');
+            }
+            messageCenter.addMessage('success', null, 'Uaktualniłem dokument do najnowszej wersji :-)');
+        } else if (xhr.status == 202) { // Wygenerowano PullRequest (tutaj?)
+        } else if (xhr.status == 204) { // Nic nie zmieniono
+            messageCenter.addMessage('info', null, 'Nic się nie zmieniło od ostatniej aktualizacji. Po co mam uaktualniać?');
+        } else if (xhr.status == 409) { // Konflikt podczas operacji
+            messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas aktualizacji. Pędź po programistów! :-(');
+        } else if (xhr.status == 500) {
+            messageCenter.addMessage('critical', null, 'Błąd serwera. Pędź po programistów! :-(');
+        }
+        this.set('state', 'synced');
+        this.set('updateData', null);
+    },
+  
+    merge: function(message) {
+        this.set('state', 'loading');
+        messageCenter.addMessage('info', null, 'Scalam dokument z głównym repozytorium...');
+        $.ajax({
+            url: this.data.merge_url,
+            type: 'post',
+            dataType: 'json',
+            data: {
+                type: 'share',
+                revision: this.revision,
+                user: this.user,
+                message: message
+            },
+            complete: this.mergeCompleted.bind(this),
+            success: function(data) {
+                this.set('mergeData', data);
+            }.bind(this)
+        });
+    },
+  
+    mergeCompleted: function(xhr, textStatus) {
+        console.log(xhr.status, textStatus);
+        if (xhr.status == 200) { // Sukces
+            this.data = this.get('updateData');
+            this.revision = this.data.user_revision;
+            this.user = this.data.user;
+            
+            for (var key in this.contentModels) {
+                this.contentModels[key].set('revision', this.revision);
+                this.contentModels[key].set('state', 'empty');
+            }
+
+            messageCenter.addMessage('success', null, 'Scaliłem dokument z głównym repozytorium :-)');
+        } else if (xhr.status == 202) { // Wygenerowano PullRequest
+            messageCenter.addMessage('success', null, 'Wysłałem prośbę o scalenie dokumentu z głównym repozytorium.');
+        } else if (xhr.status == 204) { // Nic nie zmieniono
+            messageCenter.addMessage('info', null, 'Nic się nie zmieniło od ostatniego scalenia. Po co mam scalać?');
+        } else if (xhr.status == 409) { // Konflikt podczas operacji
+            messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas scalania. Pędź po programistów! :-(');
+        } else if (xhr.status == 500) {
+            messageCenter.addMessage('critical', null, 'Błąd serwera. Pędź po programistów! :-(');
+        }
+        this.set('state', 'synced');
+        this.set('mergeData', null);
+    },
+  
+    // For debbuging
+    set: function(property, value) {
+        if (property == 'state') {
+            console.log(this.description(), ':', property, '=', value);
+        }
+        return this._super(property, value);
+    }
+});
+
+
+var leftPanelView, rightPanelContainer, doc;
+
+$(function()
+{
+    var flashView = new FlashView('#flashview', messageCenter);
+    
+    doc = new Editor.DocumentModel();
+
+    EditorView = new EditorView('#body-wrap', doc);
+    EditorView.freeze("<h1>Wczytuję dokument...</h1>");
+
+    leftPanelView = new PanelContainerView('#left-panel-container', doc);
+    rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
+
+    
+});
diff --git a/platforma/static/js/views/button_toolbar.js b/platforma/static/js/views/button_toolbar.js
new file mode 100644 (file)
index 0000000..7f03998
--- /dev/null
@@ -0,0 +1,78 @@
+/*globals View render_template scriptletCenter*/
+var ButtonToolbarView = View.extend({
+    _className: 'ButtonToolbarView',
+    template: null,
+    buttons: null,
+  
+    init: function(element, model, parent, template) {
+        this._super(element, model, null);
+        this.parent = parent;
+        this.template = 'button-toolbar-view-template';
+    
+        this.model.addObserver(this, 'buttons', this.modelButtonsChanged.bind(this));
+        this.buttons = this.model.get('buttons');
+        this.model.load();
+        this.render();
+    },
+  
+    modelButtonsChanged: function(property, value) {
+        this.set('buttons', value);
+        this.render();
+    },
+  
+    render: function() {
+        $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview');
+        $('.buttontoolbarview-button', this.element).unbind('click.buttontoolbarview');
+        var self = this;
+    
+        this.element.html(render_template(this.template, this));
+    
+        $('.buttontoolbarview-tab', this.element).bind('click.buttontoolbarview', function() {
+            var groupIndex = $(this).attr('ui:groupindex');
+            $('.buttontoolbarview-group', self.element).each(function() {
+                if ($(this).attr('ui:groupindex') == groupIndex) {
+                    $(this).show();
+                } else {
+                    $(this).hide();
+                }
+            });
+            $(self.element).trigger('resize');
+        });
+    
+        $('.buttontoolbarview-button', this.element).
+        bind('click.buttontoolbarview', this.buttonPressed.bind(this) );
+            
+        $(this.element).trigger('resize');
+    },
+
+    buttonPressed: function(event)
+    {
+        var self = this;
+        var target = event.target;
+        
+        var groupIndex = parseInt($(target).attr('ui:groupindex'), 10);
+        var buttonIndex = parseInt($(target).attr('ui:buttonindex'), 10);
+        var button = this.get('buttons')[groupIndex].buttons[buttonIndex];
+        var scriptletId = button.scriptlet_id;
+        var params = eval('(' + button.params + ')'); // To nie powinno być potrzebne
+
+        console.log('Executing', scriptletId, 'with params', params);
+        try {
+            self.parent.freeze('Wykonuję akcję...');
+            setTimeout(function() {
+                scriptletCenter.scriptlets[scriptletId](self.parent, params);
+                self.parent.unfreeze();
+            }, 10);
+        } catch(e) {
+            console.log("Scriptlet", scriptletId, "failed.", e);
+        }
+
+    },
+  
+    dispose: function() {
+        $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview');
+        $('.buttontoolbarview-button', this.element).unbind('click.buttontoolbarview');
+        this._super();
+    }
+});
+
diff --git a/platforma/static/js/views/editor.js b/platforma/static/js/views/editor.js
new file mode 100644 (file)
index 0000000..d43caaa
--- /dev/null
@@ -0,0 +1,151 @@
+/*global View render_template panels */
+var EditorView = View.extend({
+    _className: 'EditorView',
+    element: null,
+    model: null,
+    template: null,
+  
+    init: function(element, model, template) {
+        this._super(element, model, template);
+    
+        this.quickSaveButton = $('#action-quick-save', this.element).bind('click.editorview', this.quickSave.bind(this));
+        this.commitButton = $('#action-commit', this.element).bind('click.editorview', this.commit.bind(this));
+        this.updateButton = $('#action-update', this.element).bind('click.editorview', this.update.bind(this));
+        this.mergeButton = $('#action-merge', this.element).bind('click.editorview', this.merge.bind(this));
+    
+        this.model.addObserver(this, 'state', this.modelStateChanged.bind(this));
+        this.modelStateChanged('state', this.model.get('state'));        
+
+        this.splitView = new SplitView('#splitview', doc);
+        
+        // Inicjalizacja okien jQuery Modal
+        $('#commit-dialog', this.element).
+        jqm({
+            modal: true,
+            onShow: this.loadRelatedIssues.bind(this)
+        });
+    
+        $('#commit-dialog-cancel-button', this.element).click(function() {
+            $('#commit-dialog-error-empty-message').hide();
+            $('#commit-dialog').jqmHide();
+        });
+        
+    
+        // $('#split-dialog').jqm({
+        //      modal: true,
+        //      onShow: $.fbind(self, self.loadSplitDialog)
+        //  }).
+        //  jqmAddClose('button.dialog-close-button');
+    
+        this.model.load();
+    },
+  
+    quickSave: function(event) {
+        this.model.saveDirtyContentModel();
+    },
+  
+    commit: function(event) {
+        $('#commit-dialog', this.element).jqmShow({
+            callback: this.doCommit.bind(this)
+            });
+    },
+  
+    doCommit: function(message) {
+        this.model.saveDirtyContentModel(message);
+    },
+  
+    update: function(event) {
+        this.model.update();
+    },
+  
+    merge: function(event) {
+        $('#commit-dialog', this.element).jqmShow({
+            callback: this.doMerge.bind(this)
+            });
+    },
+  
+    doMerge: function(message) {
+        this.model.merge(message);
+    },
+  
+    loadRelatedIssues: function(hash) {
+        var self = this;
+        var c = $('#commit-dialog-related-issues');
+
+        $('#commit-dialog-save-button').click(function(event, data)
+        {
+            if ($('#commit-dialog-message').val().match(/^\s*$/)) {
+                $('#commit-dialog-error-empty-message').fadeIn();
+            } else {
+                $('#commit-dialog-error-empty-message').hide();
+                $('#commit-dialog').jqmHide();
+
+                var message = $('#commit-dialog-message').val();
+                $('#commit-dialog-related-issues input:checked')
+                .each(function() {
+                    message += ' refs #' + $(this).val();
+                });
+                console.log("COMMIT APROVED", hash.t);
+                hash.t.callback(message);
+            }
+            return false;
+        });
+
+        $("div.loading-box", c).show();
+        $("div.fatal-error-box", c).hide();
+        $("div.container-box", c).hide();
+    
+        $.getJSON(c.attr('ui:ajax-src') + '?callback=?',
+            function(data, status)
+            {
+                var fmt = '';
+                $(data).each( function() {
+                    fmt += '<label><input type="checkbox" checked="checked"';
+                    fmt += ' value="' + this.id + '" />' + this.subject +'</label>\n';
+                });
+                $("div.container-box", c).html(fmt);
+                $("div.loading-box", c).hide();
+                $("div.container-box", c).show();
+            });
+    
+        hash.w.show();
+    },
+  
+    modelStateChanged: function(property, value) {
+        // Uaktualnia stan przycisków
+        if (value == 'dirty') {
+            this.quickSaveButton.attr('disabled', null);
+            this.commitButton.attr('disabled', null);
+            this.updateButton.attr('disabled', 'disabled');
+            this.mergeButton.attr('disabled', 'disabled');
+        } else if (value == 'synced') {            
+            this.quickSaveButton.attr('disabled', 'disabled');
+            this.commitButton.attr('disabled', 'disabled');
+            this.updateButton.attr('disabled', null);
+            this.mergeButton.attr('disabled', null);
+            this.unfreeze();
+        } else if (value == 'empty') {
+            this.quickSaveButton.attr('disabled', 'disabled');
+            this.commitButton.attr('disabled', 'disabled');
+            this.updateButton.attr('disabled', 'disabled');
+            this.mergeButton.attr('disabled', 'disabled');
+        } else if (value == 'error') {
+            this.freeze(this.model.get('error'));
+            this.quickSaveButton.attr('disabled', 'disabled');
+            this.commitButton.attr('disabled', 'disabled');
+            this.updateButton.attr('disabled', 'disabled');
+            this.mergeButton.attr('disabled', 'disabled');
+            
+        }
+    },
+  
+    dispose: function() {
+        $('#action-quick-save', this.element).unbind('click.editorview');
+        $('#action-commit', this.element).unbind('click.editorview');
+        $('#action-update', this.element).unbind('click.editorview');
+        $('#action-merge', this.element).unbind('click.editorview');
+
+        this.model.removeObserver(this);
+        this._super();
+    }    
+});
diff --git a/platforma/static/js/views/flash.js b/platforma/static/js/views/flash.js
new file mode 100644 (file)
index 0000000..e67b046
--- /dev/null
@@ -0,0 +1,42 @@
+/*globals View render_template*/
+var FlashView = View.extend({
+  template: 'flash-view-template',
+
+  init: function(element, model, template) {
+    this.shownMessage = null;
+    this._super(element, model, template);
+    this.setModel(model);
+  },
+  
+  setModel: function(model) {
+    if (this.model) {
+      this.model.removeObserver(this);
+    }
+    this.model = model;
+    this.shownMessage = null;
+    if (this.model) {
+      this.shownMessage = this.model.get('firstFlashMessage');
+      this.model.addObserver(this, 'firstFlashMessage', this.modelFirstFlashMessageChanged.bind(this));
+    }
+    this.render();
+  },
+  
+  render: function() {
+    this.element.html(render_template(this.template, this));
+    setTimeout(function() {}, 0);
+  },
+  
+  modelFirstFlashMessageChanged: function(property, value) {    
+    this.element.fadeOut(200, (function() {
+    
+    this.element.css({'z-index': 0});
+    this.shownMessage = value;
+    this.render();
+
+    if(this.shownMessage) {
+        this.element.css({'z-index': 1000});
+        this.element.fadeIn();
+    };
+    }).bind(this));
+  }
+});
diff --git a/platforma/static/js/views/gallery.js b/platforma/static/js/views/gallery.js
new file mode 100644 (file)
index 0000000..7449344
--- /dev/null
@@ -0,0 +1,321 @@
+/*global View render_template panels */
+var ImageGalleryView = View.extend({
+  _className: 'ImageGalleryView',
+  element: null,
+  model: null,
+  currentPage: -1,
+  pageZoom: 1.0,
+  template: 'image-gallery-view-template',
+  
+  init: function(element, model, parent, template) 
+  {    
+    console.log("init for gallery");
+    this._super(element, model, template);
+    this.parent = parent;
+
+    console.log("galley model", this.model);
+       
+    this.model
+      .addObserver(this, 'data', this.modelDataChanged.bind(this))
+      .addObserver(this, 'state', this.modelStateChanged.bind(this));
+   
+    //$('.image-gallery-view', this.element).html(this.model.get('data'));
+    this.modelStateChanged('state', this.model.get('state'));
+    this.model.load();    
+  },
+  
+  modelDataChanged: function(property, value) 
+  {    
+    if( property == 'data')
+    {
+        this.render();
+        this.gotoPage(this.currentPage);        
+    }   
+  },
+
+  gotoPage: function(index) 
+  {
+     if (index < 0) 
+         index = 0;
+    
+     var n = this.$pages.length;
+     if (index >= n) index = n-1;
+
+     if( (this.currentPage == index) )
+         return;
+
+     var cpage = this.$currentPage();
+
+     if(cpage) {
+         var offset = this.pageViewOffset(cpage);
+         this.cleanPage(cpage);
+     }
+     
+     this.currentPage = index;
+
+     cpage = this.$currentPage()
+     this.renderImage(cpage);
+
+     if(offset) {
+         cpage.css({top: offset.y, left: offset.x});
+     }
+
+     var self = this;
+     $('img', cpage).bind('load', function() {
+        if(offset)
+             self.setPageViewOffset(cpage, offset);
+     });
+     
+     cpage.show();
+
+     if(this.currentPage == n-1)
+          this.$nextButton.attr('disabled', 'disabled');
+     else
+          this.$nextButton.removeAttr('disabled');
+
+      if(this.currentPage == 0)
+          this.$prevButton.attr('disabled', 'disabled');
+      else
+          this.$prevButton.removeAttr('disabled');
+
+      this.$pageInput.val( (this.currentPage+1) );
+  },
+  
+  reload: function() {},
+  
+  modelStateChanged: function(property, value) {   
+    if (value == 'loading') {
+      this.parent.freeze('Ładowanie...');
+    } else {
+      this.parent.unfreeze();
+    }
+  },
+
+  $currentPage: function() {
+      if(this.currentPage >= 0 && this.currentPage < this.$pages.length)
+          return $(this.$pages[this.currentPage]);
+      else
+          return undefined;
+  },    
+
+  cleanPage: function($page) {
+    $page.hide();
+    $('img', $page).unbind();
+    
+    $page.empty();
+    
+    this.setPageViewOffset($page, {x:0, y:0});
+  },
+
+  pageDragStart: function(event)
+  {      
+      this.dragStart = {x: event.clientX, y: event.clientY};
+      $(window).bind('mousemove.imagedrag', this.pageDrag.bind(this));
+      $(window).bind('mouseup.imagedrag', this.pageDragStop.bind(this));
+      
+      this.$currentPage().css('cursor', 'move');
+
+      return false;
+  },
+
+  pageDrag: function(event)
+  {
+      if(!this.dragStart) return;
+
+      var delta = {
+           x: this.dragStart.x - event.clientX,
+           y: this.dragStart.y - event.clientY };     
+
+      var offset = this.pageViewOffset( $(this.$pages[this.currentPage]) );
+      offset.x -= delta.x;
+      offset.y -= delta.y;
+      this.setPageViewOffset( $(this.$pages[this.currentPage]), offset);
+      
+      this.dragStart = {x: event.clientX, y: event.clientY };     
+      return false;
+  },
+
+  pageDragStop: function(event) {
+      this.$currentPage().css('cursor', 'auto');
+
+      this.dragStart = undefined;
+      $(window).unbind('mousemove.imagedrag');
+      $(window).unbind('mouseup.imagedrag');
+
+      return false;
+  },
+
+  pageViewOffset: function($page) {
+      var left = parseInt($page.css('left'));
+      var top = parseInt($page.css('top'));
+
+      return {x: left, y: top};
+  },
+
+  setPageViewOffset: function($page, offset) {
+      // check if the image will be actually visible
+      // and correct
+      var MARGIN = 30;
+
+
+      var vp_width = this.$pageListRoot.width();
+      var vp_height = this.$pageListRoot.height();
+      
+      var width = $page.outerWidth();
+      var height = $page.outerHeight();
+
+      // console.log(offset, vp_width, vp_height, width, height);
+      if( offset.x+width-MARGIN < 0 ) {
+        // console.log('too much on the left', offset.x, -width)
+        offset.x = -width+MARGIN;
+      }
+      
+      // too much on the right
+      if( offset.x > vp_width-MARGIN) {
+          offset.x = vp_width-MARGIN;
+          // console.log('too much on the right', offset.x, vp_width, width)
+      }
+      
+      if( offset.y+height-MARGIN < 0)
+        offset.y = -height+MARGIN;      
+
+      if( offset.y > vp_height-MARGIN)
+          offset.y = vp_height-MARGIN;               
+      
+      $page.css({left: offset.x, top: offset.y});           
+  }, 
+  
+  renderImage: function(target) 
+  {
+      var source = target.attr('ui:model');
+      var orig_width = parseInt(target.attr('ui:width'));
+      var orig_height = parseInt(target.attr('ui:height'));
+
+      target.html('<img src="' + source
+           + '" width="' + Math.floor(orig_width * this.pageZoom)
+           + '" height="' + Math.floor(orig_height * this.pageZoom)
+           + '" />');
+       
+      $('img', target).
+        css({
+            'user-select': 'none',
+            '-webkit-user-select': 'none',
+            '-khtml-user-select': 'none',
+            '-moz-user-select': 'none'
+        }).
+        attr('unselectable', 'on').
+        mousedown(this.pageDragStart.bind(this));    
+  },
+
+  render: function() 
+  {
+      if(!this.model) return;            
+      
+      /* first unbind all */    
+      if(this.$nextButton) this.$nextButton.unbind();
+      if(this.$prevButton) this.$prevButton.unbind();
+      if(this.$jumpButton) this.$jumpButton.unbind();
+      if(this.$pageInput) this.$pageInput.unbind();
+
+      if(this.$zoomInButton) this.$zoomInButton.unbind();
+      if(this.$zoomOutButton) this.$zoomOutButton.unbind();
+      if(this.$zoomResetButton) this.$zoomResetButton.unbind();
+
+      /* render */
+      this._super();
+
+      /* fetch important parts */
+      this.$pageListRoot = $('.image-gallery-page-list', this.element);
+      this.$pages = $('.image-gallery-page-container', this.$pageListRoot);
+
+      this.$nextButton = $('.image-gallery-next-button', this.element);
+      this.$prevButton = $('.image-gallery-prev-button', this.element);
+      this.$pageInput = $('.image-gallery-current-page', this.element);
+
+      // this.$zoomSelect = $('.image-gallery-current-zoom', this.element);
+      this.$zoomInButton = $('.image-gallery-zoom-in', this.element);
+      this.$zoomOutButton = $('.image-gallery-zoom-out', this.element);
+      this.$zoomResetButton = $('.image-gallery-zoom-reset', this.element);
+
+      /* re-bind events */
+      this.$nextButton.click( this.nextPage.bind(this) );
+      this.$prevButton.click( this.prevPage.bind(this) );
+      this.$pageInput.change( this.jumpToPage.bind(this) );
+
+      // this.$zoomSelect.change( this.zoomChanged.bind(this) );
+      this.$zoomInButton.click( this.zoomInOneStep.bind(this) );
+      this.$zoomOutButton.click( this.zoomOutOneStep.bind(this) );
+      this.$zoomResetButton.click( this.zoomReset.bind(this) );
+
+      this.gotoPage(this.currentPage);
+      this.changePageZoom(this.pageZoom);
+  },
+
+  jumpToPage: function() {     
+        this.gotoPage(parseInt(this.$pageInput.val())-1);
+  },
+  
+  nextPage: function() {
+      this.gotoPage(this.currentPage + 1);    
+  },
+
+  prevPage: function() {
+      this.gotoPage(this.currentPage - 1);
+  },
+
+  zoomReset: function() {
+      this.changePageZoom(1.0);
+  },
+
+  zoomInOneStep: function() {
+      var zoom = this.pageZoom + 0.1;
+      if(zoom > 3.0) zoom = 3.0;
+      this.changePageZoom(zoom);
+  },
+
+  zoomOutOneStep: function() {
+      var zoom = this.pageZoom - 0.1;
+      if(zoom < 0.3) zoom = 0.3;
+      this.changePageZoom(zoom);
+  },
+
+  changePageZoom: function(value) {
+      var current = this.$currentPage();
+
+      if(!current) return;
+
+      var alpha = value/this.pageZoom;
+      this.pageZoom = value;
+
+      var nwidth = current.attr('ui:width') * this.pageZoom;
+      var nheight = current.attr('ui:height') * this.pageZoom;
+      var off_top = parseInt(current.css('top'));
+      var off_left = parseInt(current.css('left'));
+      
+      var vpx = this.$pageListRoot.width() * 0.5;
+      var vpy = this.$pageListRoot.height() * 0.5;
+      
+      var new_off_left = vpx - alpha*(vpx-off_left);
+      var new_off_top = vpy - alpha*(vpy-off_top);
+                 
+      $('img', current).attr('width', nwidth);
+      $('img', current).attr('height', nheight);
+      
+      this.setPageViewOffset(current, {
+          y: new_off_top, x: new_off_left
+      });
+
+      // this.$zoomSelect.val(this.pageZoom);
+      // console.log('Zoom is now', this.pageZoom);
+  },
+  
+  dispose: function()
+  {
+      console.log("Disposing gallery.");
+      this.model.removeObserver(this);
+      this._super();
+  }
+});
+
+// Register view
+panels['gallery'] = ImageGalleryView;
\ No newline at end of file
diff --git a/platforma/static/js/views/html.js b/platforma/static/js/views/html.js
new file mode 100644 (file)
index 0000000..fa52bd0
--- /dev/null
@@ -0,0 +1,153 @@
+/*global View render_template panels */
+var HTMLView = View.extend({
+    _className: 'HTMLView',
+    element: null,
+    model: null,
+    template: 'html-view-template',
+  
+    init: function(element, model, parent, template) {
+        this._super(element, model, template);
+        this.parent = parent;
+    
+        this.model
+        .addObserver(this, 'data', this.modelDataChanged.bind(this))        
+        .addObserver(this, 'state', this.modelStateChanged.bind(this));
+      
+        $('.htmlview', this.element).html(this.model.get('data'));
+        this.modelStateChanged('state', this.model.get('state'));
+        this.model.load();
+    },
+
+    modelDataChanged: function(property, value) {
+        $('.htmlview', this.element).html(value);
+        this.updatePrintLink();
+    },
+
+    updatePrintLink: function() {
+        var base = this.$printLink.attr('ui:baseref');
+        this.$printLink.attr('href', base + "?user="+this.model.document.get('user')+"&revision=" + this.model.get('revision'));
+    },
+  
+    modelStateChanged: function(property, value) 
+    {
+        var self = $(this);
+
+        if (value == 'synced' || value == 'dirty') {
+            this.unfreeze();
+        } else if (value == 'unsynced') {
+            this.freeze('Niezsynchronizowany...');
+        } else if (value == 'loading') {
+            this.freeze('Ładowanie...');
+        } else if (value == 'saving') {
+            this.freeze('Zapisywanie...');
+        } else if (value == 'error') {
+            this.freeze(this.model.get('error'));
+            $('.xml-editor-ref', this.overlay).click(
+            function(event) {
+                console.log("Sending scroll rq.", this);
+                try {
+                    var href = $(this).attr('href').split('-');
+                    var line = parseInt(href[1]);
+                    var column = parseInt(href[2]);
+                    
+                    $(document).trigger('xml-scroll-request', {line:line, column:column});
+                } catch(e) {
+                    console.log(e);
+                }
+                
+                return false;
+            });
+        }
+    },
+
+    render: function() {
+        this.element.unbind('click');
+
+        if(this.$printLink) this.$printLink.unbind();
+        this._super();
+        this.$printLink = $('.html-print-link', this.element);
+        this.updatePrintLink();
+
+        this.element.bind('click', this.itemClicked.bind(this));
+    },
+  
+    reload: function() {
+        this.model.load(true);
+    },
+  
+    dispose: function() {
+        this.model.removeObserver(this);
+        this._super();
+    },
+
+    itemClicked: function(event) 
+    {
+        var self = this;
+        
+        console.log('click:', event, event.ctrlKey, event.target);
+        var editableContent = null;
+        var $e = $(event.target);
+
+        var n = 0;
+
+        while( ($e[0] != this.element[0]) && !($e.attr('wl2o:editable'))
+            && n < 50)
+        {
+            // console.log($e, $e.parent(), this.element);
+            $e = $e.parent();
+            n += 1;
+        }
+      
+        if(!$e.attr('wl2o:editable'))
+            return true;
+    
+        // start edition on this node
+        
+
+        var $overlay = $(
+        '<div class="html-editarea">\n\
+            <p class="html-editarea-toolbar">\n\
+                <button class="html-editarea-save-button" type="button">Zapisz</button>\n\
+                <button class="html-editarea-cancel-button" type="button">Anuluj</button>\n\
+            </p>\n\
+            <textarea></textarea>\n\
+        </div>');
+
+        var x = $e[0].offsetLeft;
+        var y = $e[0].offsetTop;
+        var w = $e.outerWidth();
+        var h = $e.innerHeight();
+        $overlay.css({position: 'absolute', height: h, left: "5%", top: y, width: "90%"});
+        $e.offsetParent().append($overlay);
+
+        // load the original XML content
+        console.log($e, $e.offsetParent(), $overlay);
+                        
+        $('.html-editarea-cancel-button', $overlay).click(function() {
+            $overlay.remove();
+        });
+
+        $('.html-editarea-save-button', $overlay).click(function() {
+            $overlay.remove();
+
+            // put the part back to the model
+            self.model.putXMLPart($e, $('textarea', $overlay).val());
+        });
+
+        $('textarea', $overlay).focus(function() {
+            $overlay.css('z-index', 3000);
+        }).blur(function() {
+            $overlay.css('z-index', 2000);
+        });
+
+        this.model.getXMLPart($e, function(path, data) {
+            $('textarea', $overlay).val(data);
+        });
+        
+        return false;
+    }
+  
+});
+
+// Register view
+panels['html'] = HTMLView;
\ No newline at end of file
diff --git a/platforma/static/js/views/panel_container.js b/platforma/static/js/views/panel_container.js
new file mode 100644 (file)
index 0000000..6dbddd9
--- /dev/null
@@ -0,0 +1,48 @@
+/*globals View render_template panels*/
+
+var PanelContainerView = View.extend({
+  _className: 'PanelContainerView',
+  element: null,
+  model: null,
+  template: 'panel-container-view-template',
+  contentView: null,
+  
+  init: function(element, model, template) {
+    this._super(element, model, template);
+
+    $('.panel-main-toolbar select', this.element.get(0)).bind('change.panel-container-view', this.selectChanged.bind(this));
+    $('.panel-main-toolbar .refresh', this.element.get(0))
+      .bind('click.panel-container-view', this.refreshButtonClicked.bind(this))
+      .attr('disabled', 'disabled');
+  },
+  
+  selectChanged: function(event) {
+    var value = $('select', this.element.get(0)).val();
+    var klass = panels[value];
+
+    if (this.contentView) {
+      this.contentView.dispose();
+      this.contentView = null;
+    }
+
+    if( value != 'empty') {
+    this.contentView = new klass($('.content-view', 
+      this.element.get(0)), this.model.contentModels[value], this);
+    $('.panel-main-toolbar .refresh', this.element.get(0)).attr('disabled', null);    
+    }
+  },
+  
+  refreshButtonClicked: function(event) {
+    if (this.contentView) {
+      console.log('refreshButtonClicked');
+      this.contentView.reload();
+    }
+  },
+  
+  dispose: function() {
+    $('.panel-main-toolbar .refresh', this.element.get(0)).unbind('click.panel-container-view');
+    $('.panel-main-toolbar select', this.element.get(0)).unbind('change.panel-container-view');
+    this._super();
+  }
+});
+
diff --git a/platforma/static/js/views/split.js b/platforma/static/js/views/split.js
new file mode 100644 (file)
index 0000000..30eda4a
--- /dev/null
@@ -0,0 +1,118 @@
+/*globals View*/
+
+// Split view inspired by jQuery Splitter Plugin http://methvin.com/splitter/
+var SplitView = View.extend({
+  _className: 'SplitView',
+  splitbarClass: 'splitview-splitbar',
+  activeClass: 'splitview-active',
+  overlayClass: 'splitview-overlay',
+  element: null,
+  model: null,
+  zombie: null,
+  leftViewOffset: 0,
+  
+  // Cache
+  _splitbarWidth: 0,
+  
+  init: function(element, model) {
+    this._super(element, model, null);
+    this.element.css('position', 'relative');
+    this._resizingSubviews = false;    
+    
+    this.views = $(">*", this.element[0]).css({
+       position: 'absolute',                     // positioned inside splitter container
+       'z-index': 1,                                           // splitbar is positioned above
+       '-moz-outline-style': 'none',   // don't show dotted outline
+      overflow: 'auto'
+    });
+    
+    this.leftView = $(this.views[0]);
+    this.rightView = $(this.views[1]);
+    
+    this.splitbar = $(this.views[2] || '<div></div>')
+      .insertAfter(this.leftView)
+      .css({
+        position: 'absolute',
+        'user-select': 'none',
+        '-webkit-user-select': 'none',
+        '-khtml-user-select': 'none',
+        '-moz-user-select': 'none',
+        'z-index': 100
+      })
+      .attr('unselectable', 'on')
+      .addClass(this.splitbarClass)
+      .bind('mousedown.splitview', this.beginResize.bind(this));
+    
+    this._splitbarWidth = this.splitbar.outerWidth();
+    
+    // Solomon's algorithm ;-)
+    this.resplit(this.element.width() / 2);
+  },
+    
+  beginResize: function(event) {
+    this.zombie = this.zombie || this.splitbar.clone(false).insertAfter(this.leftView);
+    this.overlay = this.overlay || $('<div></div>').addClass(this.overlayClass).css({
+        position: 'absolute',
+        width: this.element.width(),
+        height: this.element.height(),
+        top: this.element.position().top,
+        left: this.element.position().left
+      }).appendTo(this.element);
+    this.views.css("-webkit-user-select", "none"); // Safari selects A/B text on a move
+    this.splitbar.addClass(this.activeClass);
+    this.leftViewOffset = this.leftView[0].offsetWidth - event.pageX;
+    
+    $(document)
+      .bind('mousemove.splitview', this.resizeChanged.bind(this))
+      .bind('mouseup.splitview', this.endResize.bind(this));
+  },
+  
+  resizeChanged: function(event) {
+    var newPosition = event.pageX + this.leftViewOffset;
+    newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
+    this.splitbar.css('left', newPosition);
+  },
+
+  endResize: function(event) {
+    var newPosition = event.pageX + this.leftViewOffset;
+    this.zombie.remove();
+    this.zombie = null;
+    this.overlay.remove();
+    this.overlay = null;
+    this.resplit(newPosition);
+
+    $(document)
+      .unbind('mousemove.splitview')
+      .unbind('mouseup.splitview');
+  },
+
+  resized: function(event) {
+    if (!this._resizingSubviews) {
+      this.resplit(Math.min(this.leftView.width(), this.element.width() - this._splitbarWidth));
+    }
+  },
+  
+  resplit: function(newPosition) {
+    newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
+    this.splitbar.css('left', newPosition);
+    this.leftView.css({
+      left: 0,
+      width: newPosition
+    });
+    this.rightView.css({
+      left: newPosition + this._splitbarWidth,
+      width: this.element.width() - newPosition - this._splitbarWidth
+    });
+    if (!$.browser.msie) {
+      this._resizingSubviews = true;
+                 $(window).trigger('resize');
+                 this._resizingSubviews = false;
+               }
+  },
+  
+  dispose: function() {
+    this.splitter.unbind('mousedown.splitview');
+    this._super();
+  }
+});
+
diff --git a/platforma/static/js/views/view.js b/platforma/static/js/views/view.js
new file mode 100644 (file)
index 0000000..d0c6d17
--- /dev/null
@@ -0,0 +1,82 @@
+/*globals Editor render_template*/
+var View = Editor.Object.extend({
+    _className: 'View',
+    element: null,
+    model: null,
+    template: null,
+    overlayClass: 'view-overlay',
+    overlay: null,
+  
+    init: function(element, model, template)
+    {
+        console.log("init for view");
+        this.element = $(element);
+        this.model = model;
+        this.template = template || this.template;
+    
+        if (this.template) this.render();
+    
+        this._resizeHandler = this.resized.bind(this);
+        $(window).bind('resize', this._resizeHandler);
+        $(this.element).bind('resize', this._resizeHandler);
+    },
+
+    render: function() {
+        console.log('rendering:', this._className);
+        this.element.html(render_template(this.template, this));
+    },
+  
+    frozen: function() {
+        return !!this.overlay;
+    },
+  
+    freeze: function(message) {
+        if (this.frozen()) {
+            this.unfreeze();
+        }
+        this.overlay = this.overlay || $('<div><div>' + message + '</div></div>');
+
+        this.overlay.addClass(this.overlayClass)
+        .css({
+            
+        }).attr('unselectable', 'on')
+
+        this.overlay.appendTo(this.element);
+
+        var ovc = this.overlay.children('div');        
+        var padV = (this.overlay.height() - ovc.outerHeight())/2;
+        var padH = (this.overlay.width() - ovc.outerWidth())/2;
+                   
+        this.overlay.children('div').css({
+            top: padV, left: padH
+        });    
+    },
+  
+    unfreeze: function() {
+        if (this.frozen()) {
+            this.overlay.remove();
+            this.overlay = null;
+        }
+    },
+
+    resized: function(event) {
+        if(this.overlay) {
+            var ovc = this.overlay.children('div');
+            var padV = (this.overlay.height() - ovc.outerHeight())/2;
+            var padH = (this.overlay.width() - ovc.outerWidth())/2;
+
+            this.overlay.children('div').css({
+                top: padV,
+                left: padH
+            });
+        }
+    },
+  
+    dispose: function() {
+        console.log('disposing:', this._className);
+        $(window).unbind('resize', this._resizeHandler);
+        $(this.element).unbind('resize', this._resizeHandler);
+        this.unfreeze();
+        this.element.html('');
+    }
+});
\ No newline at end of file
diff --git a/platforma/static/js/views/xml.js b/platforma/static/js/views/xml.js
new file mode 100644 (file)
index 0000000..5c18432
--- /dev/null
@@ -0,0 +1,186 @@
+/*global View CodeMirror ToolbarView render_template panels */
+var XMLView = View.extend({
+    _className: 'XMLView',
+    element: null,
+    model: null,
+    template: 'xml-view-template',
+    editor: null,
+    buttonToolbar: null,
+  
+    init: function(element, model, parent, template) {
+        this._super(element, model, template);
+        this.parent = parent;
+        this.buttonToolbar = new ButtonToolbarView(
+            $('.xmlview-toolbar', this.element),
+            this.model.toolbarButtonsModel, parent);
+
+        this.hotkeys = [];
+        var self = this;
+
+        $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this));
+
+        // scroll to the given position (if availble)
+        this.scrollCallback = this.scrollOnRequest.bind(this);
+        $(document).bind('xml-scroll-request', this.scrollCallback);
+       
+        this.parent.freeze('Ładowanie edytora...');
+
+        setTimeout((function(){
+
+        this.editor = new CodeMirror($('.xmlview', this.element).get(0), {
+            parserfile: 'parsexml.js',
+            path: "/static/js/lib/codemirror/",
+            stylesheet: "/static/css/xmlcolors.css",
+            parserConfig: {
+                useHTMLKludges: false
+            },
+            textWrapping: true,
+            tabMode: 'spaces',
+            indentUnit: 0,
+            onChange: this.editorDataChanged.bind(this),
+            initCallback: this.editorDidLoad.bind(this)
+        });
+
+        }).bind(this), 0);
+    },
+  
+    resized: function(event) {
+        var height = this.element.height() - $('.xmlview-toolbar', this.element).outerHeight();
+        $('.xmlview', this.element).height(height);
+    },
+  
+    reload: function() {
+        this.model.load(true);
+    },
+  
+    editorDidLoad: function(editor) {
+        $(editor.frame).css({
+            width: '100%',
+            height: '100%'
+        });
+        this.model
+        .addObserver(this, 'data', this.modelDataChanged.bind(this))
+        .addObserver(this, 'state', this.modelStateChanged.bind(this))
+        .load();           
+      
+        this.editor.setCode(this.model.get('data'));
+        this.modelStateChanged('state', this.model.get('state'));
+        
+        editor.grabKeys(
+            this.hotkeyPressed.bind(this),
+            this.isHotkey.bind(this)
+        );
+
+        this.parent.unfreeze();
+    },
+  
+    editorDataChanged: function() {
+        this.model.set('data', this.editor.getCode());
+    },
+  
+    modelDataChanged: function(property, value) {
+        if (this.editor.getCode() != value) {
+            this.editor.setCode(value);
+        }
+    },
+  
+    modelStateChanged: function(property, value) {
+        if (value == 'synced' || value == 'dirty') {
+            this.unfreeze();
+        } else if (value == 'unsynced') {
+            this.freeze('Niezsynchronizowany...');
+        } else if (value == 'loading') {
+            this.freeze('Ładowanie danych...');
+        } else if (value == 'saving') {
+            this.freeze('Zapisywanie...');
+        } else if (value == 'error') {
+            this.freeze(this.model.get('error'));
+        }
+    },
+    
+    dispose: function() {
+        $(document).unbind('xml-scroll-request', this.scrollCallback);
+        
+        this.model.removeObserver(this);
+        $(this.editor.frame).remove();
+        this._super();
+    },    
+
+    getHotkey: function(event) {
+        var code = event.keyCode;
+        if(!((code >= 97 && code <= 122)
+           || (code >= 65 && code <= 90)) ) return null;
+
+        var ch = String.fromCharCode(code & 0xff).toLowerCase();
+        /* # console.log(ch.charCodeAt(0), '#', buttons); */
+
+        var buttons = $('.buttontoolbarview-button[hotkey='+ch+']', this.element);
+        var mod = 0;
+            
+        if(event.altKey) mod |= 0x01;
+        if(event.ctrlKey) mod |= 0x02;
+        if(event.shiftKey) mod |= 0x04;
+
+        if(buttons.length) {
+            var match = null;
+
+            buttons.each(function() {
+                if( parseInt($(this).attr('ui:hotkey_mod')) == mod ) {
+                    match = this;
+                    return;
+                }
+            })
+
+            return match;
+        }
+        else {
+            return null;
+        }
+    },
+
+    isHotkey: function() {
+        /* console.log(arguments); */
+        if(this.getHotkey.apply(this, arguments))
+            return true;
+        else
+            return false;
+    },
+
+    hotkeyPressed: function() {
+        var button = this.getHotkey.apply(this, arguments);
+        this.buttonToolbar.buttonPressed({
+            target: button
+        });
+    },
+
+    scrollOnRequest: function(event, data) 
+    {
+        try {
+            var line = this.editor.nthLine(data.line);
+            this.editor.selectLines(line, (data.column-1));
+        } catch(e) {
+            console.log('Exception in scrollOnRequest:', e);
+        }
+    }
+
+});
+
+function Hotkey(code) {
+    this.code = code;
+    this.has_alt = ((code & 0x01 << 8) !== 0);
+    this.has_ctrl = ((code & 0x01 << 9) !== 0);
+    this.has_shift = ((code & 0x01 << 10) !== 0);
+    this.character = String.fromCharCode(code & 0xff);
+}
+
+Hotkey.prototype.toString = function() {
+    var mods = [];
+    if(this.has_alt) mods.push('Alt');
+    if(this.has_ctrl) mods.push('Ctrl');
+    if(this.has_shift) mods.push('Shift');
+    mods.push('"'+this.character+'"');
+    return mods.join('+');
+};
+
+// Register view
+panels['xml'] = XMLView;
diff --git a/platforma/templates/503.html b/platforma/templates/503.html
new file mode 100644 (file)
index 0000000..1c2e4d2
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://platforma.wolnelektury.pl/">
+    <head>
+        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+        <title>Platforma Redakcyjna</title>
+
+    </head>
+    <body id="base">
+        <h2>Przepraszamy,</h2>
+        <p>Platfroma Redakcyjna jest tymczasowo niedostępna
+            z powodu prac administracyjnych.</p>
+
+        <p>Prosimy o wyrozumiałość i ponowne odwiedziny :)</p>
+    </body>
+</html>
diff --git a/platforma/templates/base.html b/platforma/templates/base.html
new file mode 100644 (file)
index 0000000..c00dd21
--- /dev/null
@@ -0,0 +1,29 @@
+{% load django_version_tags %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://platforma.wolnelektury.pl/">
+    <head>
+        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+        <title>{% block title %}Platforma Redakcyjna - {% version %}{% block subtitle %}{% endblock subtitle %}{% endblock title%}</title>
+        <link rel="stylesheet" href="{{ STATIC_URL }}css/master.css" type="text/css" />
+        <script src="{{ STATIC_URL }}js/lib/jquery.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{ STATIC_URL }}js/lib/jquery.logging.js" type="text/javascript" charset="utf-8"></script>
+        {% block extrahead %}
+        {% endblock %}
+    </head>
+    <body id="{% block bodyid %}base{% endblock %}">
+       <div id="body-wrap">
+       <div id="header">
+               <span id="breadcrumbs">{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a>{% endblock breadcrumbs %}</span>
+               <span id="header-right-toolbar">
+                       {% block header-toolbar %}{% endblock %}
+                       <span id="login_info">{% include "registration/head_login.html" %}</span>
+               </span>
+        <div id="message-box">{% block message-box %} {% endblock %}</div>
+       </div>
+    <div id="content">{% block maincontent %} {% endblock %}</div>
+
+    {% block extrabody %}{% endblock %}
+       </div>
+    </body>
+</html>
diff --git a/platforma/templates/explorer/editor.html b/platforma/templates/explorer/editor.html
new file mode 100644 (file)
index 0000000..576a793
--- /dev/null
@@ -0,0 +1,202 @@
+{% extends "base.html" %}
+
+{% block extrahead %}
+       <link rel="stylesheet" href="{{STATIC_URL}}css/jquery.modal.css" type="text/css" media="screen" charset="utf-8">
+       <link rel="stylesheet" href="{{STATIC_URL}}css/html.css" type="text/css" charset="utf-8">
+       <link rel="stylesheet" href="{{STATIC_URL}}css/toolbar.css" type="text/css" charset="utf-8">
+        <link rel="stylesheet" href="{{STATIC_URL}}css/autumn.css" type="text/css" media="screen" title="Autumn colors" charset="utf-8">
+       
+       <script type="text/javascript" charset="utf-8">
+            var documentInfo = {
+                docID: '{{ fileid }}',
+                userID: '{{ euser }}',
+                docURL: '{% url document_view fileid %}{% if euser %}?user={{ euser|urlencode }}{% endif %}',
+                toolbarURL: '{% url toolbar_buttons %}',
+                renderURL: '{% url api.views.render %}'
+            }          
+                
+       </script>
+       
+       {# Libraries #}
+        <script src="{{STATIC_URL}}js/lib/codemirror/codemirror.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/lib/jquery.modal.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{STATIC_URL}}js/lib/jquery.json.js" type="text/javascript" charset="utf-8"></script>
+        
+       {# Scriptlets #}
+       <script src="{{STATIC_URL}}js/button_scripts.js" type="text/javascript" charset="utf-8"></script>
+       
+       {# App and views #}
+       <script src="{{STATIC_URL}}js/app.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/messages.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/view.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/flash.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/editor.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/button_toolbar.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/split.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/xml.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/html.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{STATIC_URL}}js/views/gallery.js" type="text/javascript" charset="utf-8"></script>
+       <script src="{{STATIC_URL}}js/views/panel_container.js" type="text/javascript" charset="utf-8"></script>
+       
+       <script src="{{STATIC_URL}}js/models.js" type="text/javascript" charset="utf-8"></script>
+       
+       {# JavaScript templates #}
+       <script type="text/html" charset="utf-8" id="panel-container-view-template">
+               <div class="panel-main-toolbar">
+                <p><select>
+                        <option value="empty" selected="selected"> ---- </option>
+                       <% for (panel in panels) { %>
+                       <option value="<%= panel %>"><%= panel %></option>
+                       <% }; %>
+               </select> <button class="refresh">Odśwież panel</button></p>
+                </div>
+               <div class="content-view"></div>
+       </script>
+       
+       <script type="text/html" charset="utf-8" id="xml-view-template">
+               <div class="xmlview-toolbar"></div>
+               <div class="xmlview">
+                       
+               </div>
+       </script>
+       
+       <script type="text/html" charset="utf-8" id="html-view-template">
+                <div class="htmlview-toolbar">
+                    <a class="html-print-link" href="print" ui:baseref="{% url file_print fileid %}" target="_new">Wersja do druku</a>
+                </div>
+                
+               <div class="htmlview">
+               </div>
+       </script>       
+
+       <script type="text/html" charset="utf-8" id="flash-view-template">
+               <div class="flashview">
+               <% if (shownMessage) { %>
+                       <p style="margin: 0; padding: 0.2em 0.5em; line-height: 1.8em" class="<%= shownMessage.type %>"><%= shownMessage.text %></p>
+               <% } %>
+               </div>
+       </script>
+       
+    <script type="text/html" charset="utf-8" id="image-gallery-view-template">
+       <div class="image-gallery-view-template">
+
+        <div class="image-gallery-header">
+        <p>
+        <button type="button" class="image-gallery-prev-button">
+        <img alt="PPrevious" src="{{STATIC_URL}}/icons/go-previous.png" width="16" height="16" />
+        </button>
+
+        <input type="input" class="image-gallery-current-page"
+            size="5" value="<%= (currentPage + 1) %>" />
+
+
+        <button type="button" class="image-gallery-next-button">
+        <img alt="Next" src="{{STATIC_URL}}/icons/go-next.png" width="16" height="16" />
+        </button>
+
+        <button type="button" class="image-gallery-zoom-in">
+            <img alt="Zoom in" src="{{STATIC_URL}}/icons/zoom_in.png" width="16" height="16" />
+        </button>
+        <button type="button" class="image-gallery-zoom-out">
+            <img alt="Zoom out" src="{{STATIC_URL}}/icons/zoom_out.png" width="16" height="16" />
+        </button>
+        <button type="button" class="image-gallery-zoom-reset">
+            <img alt="Zoom reset" src="{{STATIC_URL}}/icons/zoom.png" width="16" height="16" />
+        </button>
+        </p>
+        </div>
+
+        <div class="image-gallery-page-list">
+            <% for(var i=0; i < model.data.length; i++) { %>
+                <div class="image-gallery-page-container"
+                    ui:model="<%= model.data[i] %>"
+                    ui:width="480"
+                    ui:height="752"
+                ></div>
+            <% }; %>
+       </div>
+        
+        </div>
+       </script>
+       
+       <script type="text/html" charset="utf-8" id="button-toolbar-view-template">
+               <div class="buttontoolbarview panel-toolbar">
+                       <div class="buttontoolbarview-tabs toolbar-tabs-container toolbar-buttons-container">
+                       <% for (var i=0; i < buttons.length; i++) { %>
+                               <button type="button" class="buttontoolbarview-tab" ui:groupindex="<%= i %>"><%= buttons[i].name %></button>
+                       <% }; %>
+                       </div>
+                       <div class="buttontoolbarview-groups ">
+                       <% for (var i=0; i < buttons.length; i++) { %>
+                               <div class="buttontoolbarview-group toolbar-buttons-container" ui:groupIndex="<%= i %>" style="display: none">
+                                       <% for (var j=0; j < buttons[i].buttons.length; j++) { %>
+                                               <% if (buttons[i].buttons[j].scriptlet_id) { %>
+                                               <button type="button" class="buttontoolbarview-button"
+                                                    title="<%= buttons[i].buttons[j].tooltip %>"
+                                                    hotkey="<%= buttons[i].buttons[j].key %>"
+                                                    ui:hotkey_mod="<%= buttons[i].buttons[j].key_mod %>"
+                                                    ui:groupindex="<%= i %>" ui:buttonindex="<%= j %>">
+                                                       <%= buttons[i].buttons[j].label %>
+                                               </button>
+                                               <% } %>
+                                       <% } %>
+                               </div>
+                       <% }; %>
+                       </div>
+               </div>
+       </script>
+{% endblock extrahead %}
+
+{% block breadcrumbs %}<a href="{% url file_list %}">Platforma</a> &gt; {{euser}} &gt; {{ fileid }}{% endblock breadcrumbs %}
+
+{% block header-toolbar %}
+    <a href="http://stigma.nowoczesnapolska.org.pl/platforma-hg/ksiazki/log/tip/{{ fileid }}.xml" target="_new" >Historia</a>
+       <button id="action-merge">Merge</button>
+        <button id="action-update">Update</button>
+        <button id="action-commit">Commit</button>
+        <button id="action-quick-save">Quick Save</button>
+{% endblock %}
+
+{% block maincontent %}    
+    
+
+    <div id="splitview">
+        <div id="left-panel-container" class='panel-container'></div>
+       <div id="right-panel-container" class='panel-container'></div>
+    </div>
+
+    <div id="commit-dialog" class="jqmWindow" style="display:none">
+        <form action="" method="POST">
+            <label for="message">Commit message:</label>
+            <textarea cols="60" rows="10" name="message" id="commit-dialog-message"></textarea>
+            <p id="commit-dialog-error-empty-message">Wiadomość nie może być pusta.</p>                
+            <fieldset id="commit-dialog-related-issues" ui:ajax-src="{{REDMINE_URL}}/publications/issues/{{fileid}}">
+                <legend>Related issues</legend>
+                <div class="loading-box" style="display: none;">
+                    <p>Loading related issues...</p>
+                </div>
+                <div class="container-box">No related issues.</div>
+            </fieldset>
+            <p>
+               <input type="button" value="Save" id="commit-dialog-save-button" />
+               <input type="reset" value="Cancel" id="commit-dialog-cancel-button" />
+            </p>
+        </form>
+    </div>
+    
+    <div id="split-dialog" class="jqmWindow" style="display:none">
+        <div class="container-box"> </div>
+        <div class="loading-box" style="display: none;">
+            <p>Loading dialog contents...</p>
+            <!-- <p><button type="button" class="dialog-close-button">Close</button></p> -->
+        </div>
+        <div class="fatal-error-box" style="display: none;">
+            <p>Server error, while loading dialog :(</p>
+            <p><button type="button" class="dialog-close-button">Close</button></p>
+        </div>
+    </div>
+{% endblock maincontent %}
+
+{% block extrabody %}
+       <div style="position: absolute; left: 35%; right: 35%; top: 0; height: 20px; z-index: 0" id="flashview"></div>
+{% endblock %}
\ No newline at end of file
diff --git a/platforma/templates/explorer/file_list.html b/platforma/templates/explorer/file_list.html
new file mode 100644 (file)
index 0000000..f44a105
--- /dev/null
@@ -0,0 +1,94 @@
+{% extends "base.html" %}
+
+{% block extrahead %}
+<link rel="stylesheet" href="{{ STATIC_URL }}css/filelist.css" type="text/css" />
+    <script src="{{STATIC_URL}}js/jquery.json.js" type="text/javascript" charset="utf-8"></script>
+    <script src="{{STATIC_URL}}js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
+<script type="text/javascript" charset="utf-8">
+$(function() {
+       function search(event) {
+        var expr = new RegExp($('#file-list-filter').val(), 'i');
+       
+        $('#file-list p').hide().filter(function(index) {
+            return expr.test($(this).attr('title'));
+        }).show();
+    
+        event.preventDefault();
+    }
+       
+    $('#file-list-find-button').click(search);
+       $('#file-list-filter').bind('keyup change DOMAttrModified', search);
+               
+    var defaultOptions = {
+        panels: [
+        {
+            name: 'htmleditor',
+            ratio: 0.5
+        },
+
+        {
+            name: 'gallery',
+            ratio: 0.5
+        }
+        ],
+        recentFiles: [],
+        lastUpdate: 0
+    };
+    
+       var options = null;
+    try {
+        var cookie = $.cookie('options');
+        options = $.secureEvalJSON(cookie);
+        if (!options) {
+            options = defaultOptions;
+        }
+    } catch (e) {    
+        options = defaultOptions;
+    };
+       
+       $.each(options.recentFiles, function(index) {
+               var fileId = options.recentFiles[index].fileId;
+               $('#recent-file-list ul').append('<li><a href="{% url editor_base %}'+fileId+'">' + fileId + '</a></li>');
+       });
+});
+</script>
+{% endblock extrahead %}
+
+{% block maincontent %}
+<div id="main-page-widgets">
+
+<div class="file-list-widget">
+    <form action="#" method="GET">
+    <p><input name="filter" id="file-list-filter" type="text" size="60" />
+        <input type="submit" value="Znajdź" id="file-list-find-button"/>
+        <input type="reset" value="Wyczyść" id="file-list-reset-button"/>
+    </p>
+    </form>
+    {% load explorer_tags %}
+    <ul class="file-tree-part file-tree-top">
+    {% for file in filetree %}    
+        {% tree_part file %}            
+    {% endfor %}
+    </ul>
+</div>
+
+<div id="recent-file-list">
+       <h2>Ostatnio oglądane pliki:</h2>
+       <ul>
+       </ul>
+</div>
+
+{% if perms.explorer.can_add_files %}
+<div class="upload-file-widget">
+<h2>Dodaj nowy utwór</h2>
+
+<form action="{% url file_upload %}" method="POST" enctype="multipart/form-data">
+    {{ bookform }}
+    <p><button type="submit">Dodaj książkę</button></p>
+</form>
+
+</div>
+{% endif %}
+
+</div>
+{% endblock maincontent %}
diff --git a/platforma/templates/explorer/file_tree_part.html b/platforma/templates/explorer/file_tree_part.html
new file mode 100644 (file)
index 0000000..f65cf28
--- /dev/null
@@ -0,0 +1,13 @@
+{% load explorer_tags %}
+
+<li class="file-tree-entry" title="{{document.name|bookname}}">
+    <a href="{% url editor_view document.name %}">{{ document.name|bookname }}</a>
+    {% if document.parts %}
+    <ul class="file-tree-part">
+        {% for part in document.parts %}
+        {% tree_part part %}
+        {% endfor %}
+    </ul>
+    {% endif %}
+</li>
+
diff --git a/platforma/templates/explorer/file_upload.html b/platforma/templates/explorer/file_upload.html
new file mode 100755 (executable)
index 0000000..1deedac
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block maincontent %}
+<h2>Dodawanie nowego utworu:</h2>
+
+{% if errors %}
+<div class="errorlist">
+{% for error in errors %}
+<p>{{ error }}: {{ error.errors }}</p>
+{% endfor %}
+</div>
+{% endif %}
+<form action="{% url file_upload %}" method="POST" enctype="multipart/form-data">
+    {{ bookform.as_p }}
+    <p><button type="submit">Dodaj książkę</button></p>
+</form>
+{% endblock maincontent %}
diff --git a/platforma/templates/explorer/folder_images.html b/platforma/templates/explorer/folder_images.html
new file mode 100644 (file)
index 0000000..8f5f600
--- /dev/null
@@ -0,0 +1,3 @@
+{% for image in images %}
+    <div class="image-box" src="{{ image }}" style="width: 480px; height: 640px;">{{ image }}</div>
+{% endfor %}
\ No newline at end of file
diff --git a/platforma/templates/explorer/split.html b/platforma/templates/explorer/split.html
new file mode 100755 (executable)
index 0000000..8d3b67b
--- /dev/null
@@ -0,0 +1,26 @@
+<form action="{% url explorer.views.split_text fileid %}" method="POST">\r
+    <fieldset>\r
+        <legend>Split options</legend>\r
+        {% for field in splitform %}\r
+        {% if field.is_hidden %}\r
+        {{ field}}\r
+        {% else %}\r
+        {{ field.errors }}\r
+        {% ifequal field.html_name 'splitform-autoxml' %}\r
+        <p><label>{{ field }} {{ field.label }}</label></p>\r
+        {% else %}\r
+        <p><label>{{ field.label }}: {{ field }}</label></p>\r
+        {% endifequal %}\r
+        {% endif %}\r
+        {% endfor %}        \r
+    </fieldset>\r
+\r
+    <fieldset id="split-form-dc-subform" style="display: none;">\r
+    <legend>Dublin Core</legend>\r
+    {{ dcform.as_p }}\r
+    </fieldset>\r
+    <p>\r
+        <button type="submit" id="split-dialog-button-accept">Split</button>\r
+        <button type="button" id="split-dialog-button-close">Close</button>\r
+    </p>\r
+</form>\r
diff --git a/platforma/templates/explorer/split_success.html b/platforma/templates/explorer/split_success.html
new file mode 100755 (executable)
index 0000000..4f3a57b
--- /dev/null
@@ -0,0 +1,3 @@
+<p>Split successful. You can edit the new part here:\r
+<a href="{% url editor_view cfileid %}" target="_new">{% url editor_view cfileid %}</a></p>\r
+<p><button type="button" id="split-dialog-button-dismiss">Close</button></p>
\ No newline at end of file
diff --git a/platforma/templates/html4print.html b/platforma/templates/html4print.html
new file mode 100644 (file)
index 0000000..1b4ef31
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>{{docid}}</title>
+    <link rel="stylesheet" href="{{STATIC_URL}}css/html_print.css" type="text/css" charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+  </head>
+  <body>
+      <div class="htmlview">
+        {{ output|safe }}
+      </div>
+  </body>
+</html>
diff --git a/platforma/templates/manager/pull_request.html b/platforma/templates/manager/pull_request.html
new file mode 100644 (file)
index 0000000..3b19c3b
--- /dev/null
@@ -0,0 +1,79 @@
+{% extends 'base.html' %}
+
+{% block extrahead %}
+<link rel="stylesheet" href="{{ STATIC_URL }}css/managment.css" type="text/css" />
+<script type="text/javascript">
+
+    
+    $(function() {
+
+        function refreshRow(id) {
+            var row = $('#request-' +id);
+
+            $.ajax({
+                url: '{% url pullrequest_list %}/'+id,
+                dataType: 'json',
+                type: 'GET',
+                success: function(data) {
+                    row.removeClass('status-N');
+                    row.removeClass('status-R');
+                    row.removeClass('status-A');
+                    row.addClass('status-'+ data.status);
+
+                    $('.column-doc', row).html(data.document);
+                    $('.column-status', row).html(data.status);
+
+                    alert('Merge accepted.');
+                }
+            });            
+            
+        }
+        
+        $('.accept-button').click(function()
+        {
+            var id = parseInt($(this).attr('title'));
+            
+
+            $.ajax({
+                url: '{% url pullrequest_list %}/'+id,
+                data: {action: 'accept'},
+                dataType: 'json',
+                type: 'PUT',
+                success: function(data) {
+                    refreshRow(id);
+                }
+            });
+            
+        });
+        
+    });
+</script>
+{% endblock %}
+
+{% block maincontent %}
+<table class="request-report" cellspacing="0">
+    <tr>
+        <th>Utwór</th><th>Użytkownik</th><th>Komentarz</th><th>Stan</th>
+        <th>Zgłoszono</th><th>Akcje</th>
+    </tr>
+{% if objects %}
+    {% for pullreq in objects %}
+    <tr class="status-{{pullreq.status}}" id="request-{{pullreq.id}}">
+        <td class="column-doc">{{ pullreq.document }}</td>
+        <td class="column-user">{{ pullreq.comitter }}</td>
+        <td class="column-comment">{{ pullreq.comment }}</td>
+        <td class="column-status"> {{ pullreq.status }}</td>
+        <td class="column-data">{{ pullreq.timestamp }}</td>
+        <td>
+            <button type="button" class="accept-button" title="{{pullreq.id}}">Akceptuj</button>
+            <a href="{% url editor_view pullreq.document %}?user=$prq-{{pullreq.id}}">Zobacz</a>
+        </td>
+    </tr>
+    
+    {% endfor %}
+{% else %}
+    <tr><td colspan="6">Brak żądań</td></tr>
+{% endif %}
+</table>
+
+{% endblock %}
diff --git a/platforma/templates/registration/head_login.html b/platforma/templates/registration/head_login.html
new file mode 100644 (file)
index 0000000..5eb353d
--- /dev/null
@@ -0,0 +1,10 @@
+{% if user.is_authenticated %}
+<span class="user_name">{{ user.username }}</span> | 
+<a href='{% url logout %}?next={{request.get_full_path}}'>Wyloguj</a>
+{% else %}
+{% url login as login_url %}
+{% ifnotequal login_url request.path %}
+    <a href='{{ login_url }}?next={{request.get_full_path}}'>Logowanie</a>
+{% endifnotequal %}
+
+{% endif %}
diff --git a/platforma/templates/registration/login.html b/platforma/templates/registration/login.html
new file mode 100644 (file)
index 0000000..810bd4e
--- /dev/null
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% block subtitle %} - Logowanie {% endblock subtitle %}
+
+{% block maincontent %}
+
+<div class="isection">
+<form method="POST" action="{% url django.contrib.auth.views.login %}">
+{{ form.as_p }}
+<p><input type="submit" value="Login" /></p>
+<input type="hidden" name="next" value="{{ next }}" />
+</form>
+</div>
+
+{% endblock maincontent %}
diff --git a/platforma/templates/wysiwyg.html b/platforma/templates/wysiwyg.html
new file mode 100644 (file)
index 0000000..a592bf1
--- /dev/null
@@ -0,0 +1,30 @@
+{% extends 'base.html' %}
+
+{% block maincontent %}
+<h1>Wysiwyg editor</h1>
+<div>This part is not editable!</div>
+<div id="loremIpsum">
+<p>
+<p>Lorem <b>ipsum</b> dolor sit amet, consectetur adipiscing elit.
+    Suspendisse a urna eu enim rutrum elementum nec sed nibh. Quisque sed tortor
+    nulla, et euismod turpis. Morbi purus nulla, vulputate in vulputate id, gravida
+    id eros. In interdum est tempor est consequat imperdiet. Vivamus vitae ligula quam.
+    Proin nibh quam, tincidunt sit amet auctor eget, laoreet sit amet eros. Cras augue
+    lectus, euismod nec posuere ac, ultricies sed magna. Aliquam a lacinia sapien.
+    Cras imperdiet urna vel dui accumsan mollis. Suspendisse convallis tincidunt ornare.
+    Aenean convallis libero in lectus dictum vestibulum interdum ipsum suscipit.
+    Suspendisse sed justo sapien, eu egestas libero. Sed tincidunt sagittis sollicitudin.
+    Aliquam erat volutpat. Nullam egestas dolor id massa sagittis at sagittis ipsum
+    hendrerit.
+</p>
+
+<p>Phasellus sed purus non orci eleifend posuere ac eu elit. Etiam orci justo, porta vitae varius in, scelerisque sed metus. Phasellus faucibus lorem at metus scelerisque sit amet sollicitudin dolor dignissim. Aliquam eu justo in diam blandit posuere at a diam. Pellentesque tristique sem eu odio gravida eleifend. Phasellus cursus adipiscing metus, nec pharetra enim pharetra ac. Pellentesque faucibus volutpat lorem nec vulputate. Mauris in faucibus ipsum. Nulla ut urna nulla. Sed at tellus nec diam posuere porttitor. Duis faucibus, libero nec rhoncus facilisis, tortor ligula adipiscing massa, nec varius justo ante et magna. Donec orci mauris, ultrices nec blandit vel, lacinia in ante. Maecenas libero mi, pretium id ultricies eget, fringilla sit amet risus. Integer ut ante sem, et condimentum odio. Nam nec est erat. Etiam ut metus ligula. In vel condimentum orci.</p>
+
+<p>Suspendisse potenti. Proin in augue nibh. Curabitur in sollicitudin ipsum. In ut leo vel purus volutpat tempus. Proin ut neque at augue euismod ullamcorper nec ac dui. Vestibulum id quam nunc, eu porta augue. Pellentesque interdum neque eu nulla rhoncus vulputate. Sed viverra diam ac sem consectetur semper. Quisque consectetur fringilla quam, in feugiat nisl vulputate quis. Fusce vel ipsum lectus, eu interdum nunc. Donec luctus libero vitae mauris imperdiet at iaculis magna aliquet. Curabitur ullamcorper, diam nec pulvinar venenatis, nibh ante volutpat mauris, nec tristique lectus sem in urna. Aenean eu malesuada metus. Integer auctor nulla sit amet ligula sollicitudin ut accumsan velit ullamcorper. Donec nec auctor augue.</p>
+
+<p>Maecenas eget lacus vitae velit tincidunt bibendum quis et diam. In ullamcorper condimentum velit, et elementum felis vestibulum facilisis. Donec vitae cursus ipsum. Cras accumsan tincidunt aliquet. Nulla pellentesque mattis magna aliquet hendrerit. Pellentesque pellentesque odio enim. Duis viverra rhoncus tristique. In in risus ligula. Nullam dapibus lacinia facilisis. Ut eu neque neque, tristique laoreet nisl. Aliquam placerat dignissim leo, tristique tempor est vestibulum ut.</p>
+
+<p>Donec semper tempus ante, eget gravida erat varius et. Suspendisse aliquam rutrum nunc ac pulvinar. Aliquam erat volutpat. Nulla consectetur ultricies imperdiet. Nulla tincidunt est vitae augue porttitor a faucibus odio facilisis. In nec nisl odio. Aliquam et libero tortor, eu tincidunt mi. Vivamus suscipit erat sed mi hendrerit fringilla. Integer iaculis tempus nulla, at egestas velit faucibus ut. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum vel massa enim. Aliquam erat volutpat. In ligula tortor, fermentum eu suscipit at, posuere vel nunc. Nullam nibh magna, sollicitudin at semper et, mattis ut quam. Curabitur accumsan semper elit ac posuere. Sed sit amet lorem tortor, vel porttitor justo. Fusce odio metus, bibendum ut bibendum sit amet, luctus a ipsum. </p>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/platforma/urls.py b/platforma/urls.py
new file mode 100644 (file)
index 0000000..e107635
--- /dev/null
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+from django.conf.urls.defaults import *
+from django.contrib import admin
+from django.conf import settings
+
+admin.autodiscover()
+PATH_SEC = r"(?P<path>[^/]+)"
+PATH_END = PATH_SEC + "/$"
+
+urlpatterns = patterns('',
+    # Explorer:
+    url(r'^$', 'explorer.views.file_list', name='file_list'),        
+    url(r'^file/upload', 'explorer.views.file_upload', name='file_upload'),
+
+
+    url(r'^managment/pull-requests$', 'explorer.views.pull_requests'),
+    
+#    url(r'^images/(?P<folder>[^/]+)/$', 'explorer.views.folder_images', name='folder_image'),
+#    url(r'^images/$', 'explorer.views.folder_images', {'folder': '.'}, name='folder_image_ajax'),
+    
+    # Editor panels
+ #   url(r'^editor/'+PATH_SEC+'/panel/(?P<name>[a-z]+)/$', 'explorer.views.panel_view', name='panel_view'),
+    url(r'^editor/'+PATH_END, 'explorer.views.display_editor', name='editor_view'),
+    url(r'^editor/$', 'explorer.views.file_list', name='editor_base'),
+
+ #   url(r'^editor/'+PATH_SEC+'/split$', 'explorer.views.split_text'),
+ #   url(r'^editor/'+PATH_SEC+'/split-success',
+ #       'explorer.views.split_success', name='split-success'),
+
+ #   url(r'^editor/'+PATH_SEC+'/print/html$', 'explorer.views.print_html'),
+ #   url(r'^editor/'+PATH_SEC+'/print/xml$', 'explorer.views.print_xml'),
+
+    url(r'^file/(?P<docid>[^/]+)/print$', 'explorer.views.print_html', name="file_print"),
+    # Task managment
+    # url(r'^manager/pull-requests$', 'explorer.views.pull_requests'),
+
+    # Admin panel
+    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+    url(r'^admin/(.*)', admin.site.root),
+
+    # Prototypes
+#    url(r'^wysiwyg-proto/', include('wysiwyg.urls')),
+
+    # Our über-restful api
+    url(r'^api/', include('api.urls') ),
+    
+)
+
+if 'cas_consumer' in settings.INSTALLED_APPS:
+    urlpatterns += patterns('',
+        # django-cas-consumer
+        url(r'^accounts/login/$', 'cas_consumer.views.login', name='login'),
+        url(r'^accounts/logout/$', 'cas_consumer.views.logout', name='logout'),
+    )
+else:
+    urlpatterns += patterns('',
+        # Django auth
+        url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'redirect_field_name': 'next'}, name='login'),
+        url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),    
+    )
+
+# Static files
+if settings.DEBUG and not hasattr(settings, 'DONT_SERVE_STATIC'):
+    urlpatterns += patterns('',
+        url(r'^%s(?P<path>.+)$' % settings.MEDIA_URL[1:], 'django.views.static.serve',
+            {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
+        url(r'^%s(?P<path>.+)$' % settings.STATIC_URL[1:], 'django.views.static.serve',
+            {'document_root': settings.STATIC_ROOT, 'show_indexes': True}),
+    )
+# 
\ No newline at end of file
diff --git a/project/__init__.py b/project/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/project/dispatch.wsgi b/project/dispatch.wsgi
deleted file mode 100755 (executable)
index 5724b23..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-import os
-from os import path
-import sys
-
-PROJECT_ROOT = path.realpath(path.dirname(__file__))
-
-# Redirect sys.stdout to sys.stderr for bad libraries like geopy that use
-# print statements for optional import exceptions.
-sys.stdout = sys.stderr
-
-# Add apps and lib directories to PYTHONPATH
-sys.path.insert(0, path.join(PROJECT_ROOT, '../apps'))
-sys.path.insert(0, path.join(PROJECT_ROOT, '../lib'))
-
-# Emulate manage.py path hacking.
-sys.path.insert(0, path.join(PROJECT_ROOT, ".."))
-sys.path.insert(0, PROJECT_ROOT)
-
-# Run Django
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-
-from django.core.handlers.wsgi import WSGIHandler
-application = WSGIHandler()
-
diff --git a/project/localsettings.sample b/project/localsettings.sample
deleted file mode 100644 (file)
index 1bac955..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# 
-#  localsettings template for Platforma
-#
-#  Duplicate this file as localsettings.py and change it to your liking.
-#  Settings defined in localsettings.py will override settings from 
-#  settings.py file. localsettings.py should never be commited 
-#  to a version control system. Please make changes to settings.py 
-#  or localsettings.sample instead.
-#
-
-# Path to repository with managed documents
-REPOSITORY_PATH = '/home/user/repository'
-
-# Subdirectory of STATIC_ROOT containing images
-IMAGE_DIR = 'images'
-
-# Authentication via Central Authentication Server
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.admin',
-    'django.contrib.admindocs',
-    
-    'cas_consumer',
-    'explorer',
-    'toolbar',
-    'api',
-    'wysiwyg',
-)
-
-AUTHENTICATION_BACKENDS = (
-    'cas_consumer.backends.CASBackend',
-)
-
-CAS_BASE = 'http://localhost:7000/'
-CAS_SERVICE = 'http://localhost:8000/accounts/login/'
-
diff --git a/project/manage.py b/project/manage.py
deleted file mode 100755 (executable)
index 8e180f0..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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__":
-    # Append lib and apps directories to PYTHONPATH
-    from os import path
-    import sys
-    
-    PROJECT_ROOT = path.realpath(path.dirname(__file__))
-    sys.path += [PROJECT_ROOT + '/../apps', PROJECT_ROOT + '/../lib']
-    
-    execute_manager(settings)
diff --git a/project/settings.py b/project/settings.py
deleted file mode 100644 (file)
index 3a1f3ad..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-# -*- coding: utf-8 -*-
-from os import path
-
-PROJECT_ROOT = path.realpath(path.dirname(__file__))
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-    (u'Marek Stępniowski', 'marek@stepniowski.com'),
-    (u'Łukasz Rekucki', 'lrekucki@gmail.com'),
-)
-
-MANAGERS = ADMINS
-
-DATABASE_ENGINE = 'sqlite3'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-DATABASE_NAME = PROJECT_ROOT + '/dev.sqlite'             # Or path to database file if using sqlite3.
-DATABASE_USER = ''             # Not used with sqlite3.
-DATABASE_PASSWORD = ''         # Not used with sqlite3.
-DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'Europe/Warsaw Poland'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'pl'
-
-#import locale
-#locale.setlocale(locale.LC_ALL, '')
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = PROJECT_ROOT + '/media/'
-STATIC_ROOT = PROJECT_ROOT + '/static/'
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash if there is a path component (optional in other cases).
-# Examples: "http://media.lawrence.com", "http://example.com/media/"
-MEDIA_URL = '/media/'
-STATIC_URL = '/static/'
-
-# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
-# trailing slash.
-# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = '/admin-media/'
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = 'ife@x^_lak+x84=lxtr!-ur$5g$+s6xt85gbbm@e_fk6q3r8=+'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.load_template_source',
-    'django.template.loaders.app_directories.load_template_source',
-#     'django.template.loaders.eggs.load_template_source',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.core.context_processors.auth",
-    "django.core.context_processors.debug",
-    "django.core.context_processors.i18n",
-    "explorer.context_processors.settings",
-    "django.core.context_processors.request",
-)
-
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'explorer.middleware.EditorSettingsMiddleware',
-    'django.middleware.doc.XViewMiddleware',
-
-    'maintenancemode.middleware.MaintenanceModeMiddleware',
-)
-
-ROOT_URLCONF = 'urls'
-
-TEMPLATE_DIRS = (
-    PROJECT_ROOT + '/templates',
-)
-
-# CSS and JS files to compress
-# COMPRESS_CSS = {
-#     'all': {
-#         'source_filenames': ('css/master.css', 'css/jquery.date_input.css', 'css/jquery.countdown.css',),
-#         'output_filename': 'css/all.min.css',
-#     }
-# }
-# 
-# COMPRESS_JS = {
-#     'all': {
-#         'source_filenames': ('js/jquery.js', 'js/jquery.date_input.js', 'js/jquery.date_input-pl.js',
-#             'js/jquery.countdown.js', 'js/jquery.countdown-pl.js',),
-#         'output_filename': 'js/all.min.js',
-#     }
-# }
-# 
-# COMPRESS_CSS_FILTERS = None
-
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.admin',
-    'django.contrib.admindocs',
-
-    'piston',
-    'explorer',
-    'toolbar',
-    'api',
-    'wysiwyg',
-)
-
-
-# REPOSITORY_PATH = '/Users/zuber/Projekty/platforma/files/books'
-IMAGE_DIR = 'images'
-EDITOR_COOKIE_NAME = 'options'
-EDITOR_DEFAULT_SETTINGS = {
-    'panels': [
-        {'name': 'htmleditor', 'ratio': 0.5},
-        {'name': 'gallery', 'ratio': 0.5}
-    ],
-}
-
-# Python logging settings
-import logging
-
-log = logging.getLogger('platforma')
-log.setLevel(logging.DEBUG)
-ch = logging.StreamHandler()
-ch.setLevel(logging.DEBUG)
-formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-ch.setFormatter(formatter)
-log.addHandler(ch)
-
-
-# Import localsettings file, which may override settings defined here
-try:
-    from localsettings import *
-except ImportError:
-    pass
-
diff --git a/project/static/css/autumn.css b/project/static/css/autumn.css
deleted file mode 100644 (file)
index dddd899..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* 
-    Document   : autumn
-    Created on : 2009-10-01, 00:23:51
-    Author     : lreqc
-    Description:
-        Autumn colors for PR.
-*/
-
-body {
-    background-color: #C28800;
-}
-
-#header {
-    background-color: #E2CF62;
-    border-bottom-color: #860000;
-}
-
-.panel-main-toolbar {
-    background-color: #412F1D;    
-}
-
-.splitview-splitbar {
-    /* border-color: #412F1D; */
-    border: none;
-    background-color: #860000;
-}
-
-.image-gallery-header {
-    background-color: #E2CF62;
-}
-
-a:link, a:visited, a:active {
-    color: #860000;
-    text-decoration: none;
-    font-weight: bold;
-}
-
-a:hover {
-    text-decoration: underline;
-}
-
-.toolbar-buttons-container {
-    background-color: #E2CF62;
-}
-
-.toolbar-tabs-container {
-    background-color: #860000;
-}
-
diff --git a/project/static/css/filelist.css b/project/static/css/filelist.css
deleted file mode 100755 (executable)
index 91e9b8d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/* 
-    Document   : filelist
-    Created on : 2009-09-04, 20:44:44
-    Author     : lreqc
-    Description:
-        Dodatkowe style dla listy plików na stronie głównej.
-*/
-
-#main-page-widgets > div {
-    float: left;
-    border: 1px solid black;
-    padding: 0.5em 2em;
-    margin: 1em;    
-}
-
-.file-list-widget {
-    background: #DDF;
-    max-width: 60%;
-}
-
-.file-list-widget .page-nav-wrap button {
-    width: 2.5em;
-}
-
-.upload-file-widget {
-    min-width: 20%;
-    width: 35%;
-}
-
-ul.file-tree-part {    
-    margin: 0.5em 1em;
-    padding: 0em;
-}
-
-ul.file-tree-part li {
-    list-style: square;    
-    padding: 0em;    
-}
-
-ul.file-tree-part a {
-    padding: 0em;
-}
-
-
-
-
-
diff --git a/project/static/css/html.css b/project/static/css/html.css
deleted file mode 100644 (file)
index 336a365..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
-.htmlview {
-    font-size: 16px;
-    font: Georgia, "Times New Roman", serif;
-    line-height: 1.5em;
-    padding: 3em;    
-}
-
-.htmlview div {
-    max-width: 36em;
-}
-
-.htmlview #toc {
-    display: none;
-}
-
-.htmlview a {
-    color: blue;
-    text-decoration: none;
-}
-
-.htmlview h1 {
-    font-size: 3em;
-    margin: 1.5em 0;
-    text-align: center;
-    line-height: 1.5em;
-    font-weight: bold;
-}
-
-.htmlview h2 {
-    font-size: 2em;
-    margin: 1.5em 0 0;
-    font-weight: bold;
-    line-height: 1.5em;
-}
-
-.htmlview h3 {
-    font-size: 1.5em;
-    margin: 1.5em 0 0;
-    font-weight: normal;
-    line-height: 1.5em;
-}
-
-.htmlview h4 {
-    font-size: 1em;
-    margin: 1.5em 0 0;
-    line-height: 1.5em;
-}
-
-.htmlview p {
-    margin: 0;
-}
-
-/* ======================== */
-/* = Footnotes and themes = */
-/* ======================== */
-.htmlview .theme-begin {
-    border-left: 0.1em solid #DDDDDD;
-    color: #777;
-    padding: 0 0.5em;
-    width: 7.5em;
-    font-style: normal;
-    font-weight: normal;
-    font-size: 16px;
-    float: right;
-    margin-right: -9.5em;
-    clear: both;
-    left: 40em;
-    line-height: 1.5em;
-    text-align: left;
-}
-
-.htmlview .annotation {
-    font-style: normal;
-    font-weight: normal;
-    font-size: 12px;
-}
-
-.htmlview #footnotes .annotation {
-    display: block;
-    float: left;
-    width: 2.5em;
-    clear: both;
-}
-
-.htmlview #footnotes div {
-    margin: 1.5em 0 0 0;
-}
-
-.htmlview #footnotes p {
-    margin-left: 2.5em;
-    font-size: 0.875em;
-}
-
-.htmlview blockquote {
-    font-size: 0.875em;
-}
-
-/* ============= */
-/* = Numbering = */
-/* ============= */
-.htmlview .anchor {
-    position: relative;
-    margin: 0em;
-    left: -2.2em;
-    color: #777;
-    font-size: 12px;
-    width: 2em;
-    text-align: center;
-    padding: 0.25em 0.7em;
-    line-height: 1.5em;
-}
-
-.htmlview .anchor:hover, .htmlview .anchor:active {
-    color: #FFF;
-    background-color: #CCC;
-}
-
-/* =================== */
-/* = Custom elements = */
-/* =================== */
-.htmlview .autor_utwor {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: 0.25em;
-}
-
-.htmlview .dzielo_nadrzedne {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: -0.25em;
-}
-
-.htmlview .podtytul {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: -0.25em;
-}
-
-.htmlview .didaskalia {
-    font-style: italic;
-    margin: 0.5em 0 0 1.5em;
-}
-
-.htmlview .kwestia {
-    margin: 0.5em 0 0;
-}
-
-.htmlview .strofa {
-    margin: 1.5em 0 0;
-}
-
-.htmlview .kwestia .strofa {
-    margin: 0;
-}
-
-.htmlview .akap, .htmlview .akap_cd, .htmlview .akap_dialog {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-.htmlview p.motto {
-    text-align: justify;
-    font-style: italic;
-    margin: 1.5em 0 0;
-}
-
-.htmlview p.motto_podpis {
-    font-size: 0.875em;
-    text-align: right;
-}
-
-.htmlview div.fragment {
-    border-bottom: 0.1em solid #999;
-    padding-bottom: 1.5em;
-}
-
-.htmlview div.nota p,
-.htmlview div.dedykacja p {
-    text-align: right;
-    font-style: italic;
-}
-
-.htmlview br.sekcja_swiatlo {
-    height: 3em;
-    /* visibility: hidden; */
-}
-
-.htmlview hr.separator_linia {
-    margin: 1.5em 0;
-    border: none;
-    border-bottom: 0.1em solid #000;
-}
-
-.htmlview p.sekcja_asterysk {
-    padding: 0;
-    margin: 1.5em 0;
-    text-align: center;
-}
-
-.htmlview div.lista_osob ol {
-    list-style: none;
-    padding: 0 0 0 1.5em;
-}
-
-.htmlview p.miejsce_czas {
-    font-style: italic;
-}
-
-.htmlview .mat,
-.htmlview .slowo_obce,
-.htmlview .tytul_dziela,
-.htmlview .didaskalia {
-    font-style: italic;
-}
-
-.htmlview .wyroznienie {
-    letter-spacing: 0.1em;
-}
-
-.htmlview .osoba {
-    font-style: normal;
-    font-variant: small-caps;
-}
diff --git a/project/static/css/html_print.css b/project/static/css/html_print.css
deleted file mode 100644 (file)
index 38cb596..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */
-.htmlview {
-    font-size: 16px;
-    font: Georgia, "Times New Roman", serif;
-    line-height: 1.5em;
-    padding: 3em;    
-}
-
-.htmlview div {
-    max-width: 36em;
-}
-
-.htmlview #toc {
-    display: none;
-}
-
-.htmlview a {
-    color: blue;
-    text-decoration: none;
-}
-
-.htmlview h1 {
-    font-size: 3em;
-    margin: 1.5em 0;
-    text-align: center;
-    line-height: 1.5em;
-    font-weight: bold;
-}
-
-.htmlview h2 {
-    font-size: 2em;
-    margin: 1.5em 0 0;
-    font-weight: bold;
-    line-height: 1.5em;
-}
-
-.htmlview h3 {
-    font-size: 1.5em;
-    margin: 1.5em 0 0;
-    font-weight: normal;
-    line-height: 1.5em;
-}
-
-.htmlview h4 {
-    font-size: 1em;
-    margin: 1.5em 0 0;
-    line-height: 1.5em;
-}
-
-.htmlview p {
-    margin: 0;
-}
-
-/* ======================== */
-/* = Footnotes and themes = */
-/* ======================== */
-.htmlview .theme-begin {
-    border-left: 0.1em solid #DDDDDD;
-    color: #777;
-    padding: 0 0.5em;
-    width: 7.5em;
-    font-style: normal;
-    font-weight: normal;
-    font-size: 16px;
-    float: right;
-    margin-right: -9.5em;
-    clear: both;
-    left: 40em;
-    line-height: 1.5em;
-    text-align: left;
-}
-
-.htmlview .annotation {
-    font-style: normal;
-    font-weight: normal;
-    font-size: 12px;
-}
-
-.htmlview #footnotes .annotation {
-    display: block;
-    float: left;
-    width: 2.5em;
-    clear: both;
-}
-
-.htmlview #footnotes div {
-    margin: 1.5em 0 0 0;
-}
-
-.htmlview #footnotes p {
-    margin-left: 2.5em;
-    font-size: 0.875em;
-}
-
-.htmlview blockquote {
-    font-size: 0.875em;
-}
-
-/* ============= */
-/* = Numbering = */
-/* ============= */
-.htmlview p {
-    position: relative;
-}
-
-.htmlview .anchor {
-    position: absolute;
-    margin: 0em;
-    left: -3em;
-    color: #777;
-    font-size: 12px;
-    width: 2em;
-    text-align: center;
-    padding: 0.25em 0.5em;
-    line-height: 1.5em;
-}
-
-.htmlview .anchor:hover, .htmlview .anchor:active {
-    color: #FFF;
-    background-color: #CCC;
-}
-
-/* =================== */
-/* = Custom elements = */
-/* =================== */
-.htmlview span.author {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: 0.25em;
-}
-
-.htmlview span.collection {
-    font-size: 0.375em;
-    display: block;
-    line-height: 1.5em;
-    margin-bottom: -0.25em;
-}
-
-.htmlview span.subtitle {
-    font-size: 0.5em;
-    display: block;
-    line-height: 1.5em;
-    margin-top: -0.25em;
-}
-
-.htmlview div.didaskalia {
-    font-style: italic;
-    margin: 0.5em 0 0 1.5em;
-}
-
-.htmlview div.kwestia {
-    margin: 0.5em 0 0;
-}
-
-.htmlview div.stanza {
-    margin: 1.5em 0 0;
-}
-
-.htmlview div.kwestia div.stanza {
-    margin: 0;
-}
-
-.htmlview p.paragraph {
-    text-align: justify;
-    margin: 1.5em 0 0;
-}
-
-.htmlview p.motto {
-    text-align: justify;
-    font-style: italic;
-    margin: 1.5em 0 0;
-}
-
-.htmlview p.motto_podpis {
-    font-size: 0.875em;
-    text-align: right;
-}
-
-.htmlview div.fragment {
-    border-bottom: 0.1em solid #999;
-    padding-bottom: 1.5em;
-}
-
-.htmlview div.note p, .htmlview div.dedication p,
-.htmlview div.note p.paragraph, .htmlview div.dedication p.paragraph {
-    text-align: right;
-    font-style: italic;
-}
-
-.htmlview hr.spacer {
-    height: 3em;
-    visibility: hidden;
-}
-
-.htmlview hr.spacer-line {
-    margin: 1.5em 0;
-    border: none;
-    border-bottom: 0.1em solid #000;
-}
-
-.htmlview p.spacer-asterisk {
-    padding: 0;
-    margin: 1.5em 0;
-    text-align: center;
-}
-
-.htmlview div.person-list ol {
-    list-style: none;
-    padding: 0 0 0 1.5em;
-}
-
-.htmlview p.place-and-time {
-    font-style: italic;
-}
-
-.htmlview em.math, .htmlview em.foreign-word,
-.htmlview em.book-title, .htmlview em.didaskalia {
-    font-style: italic;
-}
-
-.htmlview em.author-emphasis {
-    letter-spacing: 0.1em;
-}
-
-.htmlview em.person {
-    font-style: normal;
-    font-variant: small-caps;
-}
diff --git a/project/static/css/jquery.modal.css b/project/static/css/jquery.modal.css
deleted file mode 100755 (executable)
index 4717ad4..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* jqModal base Styling courtesy of;\r
-       Brice Burgess <bhb@iceburg.net> */\r
-\r
-/* The Window's CSS z-index value is respected (takes priority). If none is supplied,\r
-       the Window's z-index value will be set to 3000 by default (via jqModal.js). */\r
-       \r
-.jqmWindow {\r
-    display: none;\r
-    \r
-    position: absolute;\r
-    top: 40px;\r
-    left: 25%;\r
-    bottom: auto;\r
-    height: auto;\r
-    width: auto;\r
-\r
-    max-width: 80%;\r
-    max-height: 80%;\r
-\r
-    overflow: auto;\r
-    \r
-    background-color: #EEE;\r
-    color: #333;\r
-    border: 1px solid black;\r
-    padding: 1em;\r
-}\r
-\r
-.jqmOverlay { background-color: #000; }\r
diff --git a/project/static/css/managment.css b/project/static/css/managment.css
deleted file mode 100644 (file)
index 3777bcf..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-table.request-report 
-{
-    border: 2px groove black;
-    font-size: 12pt;
-
-    margin-top: 3em;
-    margin-bottom: 2em;
-    margin-left: auto;
-    margin-right: auto;
-}
-
-.request-report td, .request-report th  {
-    vertical-align: top;
-    border-right: 1px solid black;
-    border-bottom: 1px solid black;
-
-    padding: 0.4em 1em;
-    margin: 0em;
-}
-
-.request-report th {
-    background-color: black;
-    color: white;
-}
-
-.request-report .status-N {
-    background-color: teal;
-}
-
-.request-report .status-R {
-    background-color: red;
-}
-
-.request-report .status-A {
-    background-color: gray;
-}
\ No newline at end of file
diff --git a/project/static/css/master.css b/project/static/css/master.css
deleted file mode 100644 (file)
index cebb0dc..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-body {
-    margin: 0;    
-
-    font: 12px Helvetica, Verdana, sans-serif;
-    overflow: hidden;
-    background: #AAA;
-}
-
-/*  fix icon buttons */
-
-button img {
-    vertical-align: middle;
-    margin: 0px;
-}
-
-/* default form style hacks */
-select {
-    border: none;
-    margin-left: 0.1em;f
-}
-
-#body-wrap {
-    margin: 0px;
-    padding: 0px;
-    position: fixed;
-    left: 0px;
-    right: 0px;
-    bottom: 0px;
-    top: 0px;
-}
-
-#header { 
-    position: absolute;
-    padding: 2px 0.5em;
-    background-color: #CDCDCD; /* !unused */
-    border-bottom: 2px solid black;
-
-    font-size: 14px;
-
-    line-height: 26px;
-    vertical-align: middle;
-
-    /* height: 30px; */
-    top: 0px; left: 0px; right: 0px; 
-    z-index: 300;
-}
-
-#content {
-    position: absolute;
-    top: 32px; left: 0px; right: 0px; bottom: 0px;
-    overflow: auto;
-    background-color: white;
-}
-
-#header #breadcrumbs {
-}
-
-#header-right-toolbar {
-    position: absolute;
-    right: 1em;
-}
-
-#header button {
-    vertical-align: middle;
-}
-
-/* Commit dialog */
-#commit-dialog-error-empty-message {
-    color: red;
-    display: none;
-    font-weight: bold;
-}
-
-text#commit-dialog-message {
-    width: 80%;
-    margin: auto;
-}
-
-#split-dialog .container-box form {
-    margin: 0.2em 1em;
-}
-
-#split-dialog .container-box fieldset {
-    margin: 0.5em;
-}
-
-
-
-
-/* ======= */
-/* = New = */
-/* ======= */
-#splitview {
-    width: 100%;
-    height: 100%;
-    padding: 0;
-    margin: 0;
-}
-
-.splitview-splitbar {
-    width: 5px;
-    border-left: 1px solid #999;
-    border-right: 1px solid #999;
-    height: 100%;
-    background-color: #CCC;
-    z-index: 100;
-}
-
-.splitview-overlay {
-    z-index: 90;
-    background: #FFF;
-    opacity: 0.5;
-}
-
-.panel-container {
-    height: 100%;
-    position: relative;
-}
-
-.content-view {
-    position: absolute;
-    top: 25px;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    overflow: hidden;
-}
-
-.panel-main-toolbar {
-    z-index: 1100;    
-    position: absolute;
-
-    top: 0px;
-    right: 0px;
-    left: 0px;
-    height: 24px;
-
-    border-bottom: 1px solid black;
-    background: gray;
-
-    padding: 0px;
-}
-
-.panel-main-toolbar p {
-    margin: 0px;
-    padding: 2px;
-    line-height: 20px;
-    font-size: 13px;    
-}
-
-.xmlview {
-    height: 100%;
-}
-
-.view-overlay {
-    z-index: 1000;
-    background: #FFF;
-    opacity: 0.8;
-    text-align: center;
-    vertical-align: middle;
-
-    position: absolute;
-    left: 0;
-    right: 0;
-    top: 0;
-    bottom: 0;
-
-    user-select: 'none';
-    -webkit-user-select: 'none';
-    -khtml-user-select: 'none';
-    -moz-user-select: 'none';
-    overflow: 'hidden';    
-}
-
-.view-overlay div {
-    position: absolute;
-}
-
-/* .buttontoolbarview {
-    display: block;
-    background-color: #CCC;
-}
-
-.buttontoolbarview a {
-    color: #000;
-    text-decoration: none;
-} */ /* Similar classes already exist in toolbar.css */
-
-
-/* ================= */
-/* = Gallery panel = */
-/* ================= */
-.image-gallery-view-template {
-    position: absolute;
-    top: 0px; left: 0px; right: 0px; bottom: 0px;
-    overflow: hidden;
-}
-
-.image-gallery-header {
-    position: absolute;
-    bottom: 0px;
-    left: 0px;
-    right: 0px;
-    height: 30px;
-
-    background: gray;
-    border-top: 1px solid #780000;
-    z-index: 100;
-}
-
-input.image-gallery-current-page {
-    text-align: center;
-}
-
-.image-gallery-header p {
-    margin: 0px;
-    padding: 3px 1em;
-    height: 30px;
-    line-height: 24px;
-    text-align: center;
-    white-space: nowrap;
-}
-
-.image-gallery-page-list {
-    position: absolute;
-    top: 0px;
-    left: 0px;
-    right: 0px;
-    bottom: 31px;
-    background: black;
-    z-index: 0;
-
-    overflow: hidden;
-}
-
-.image-gallery-page-container {
-    display: none;
-    border: none;
-
-    position: absolute;
-    top: 0px; left: 0px;
-
-    text-align: center;
-    padding: 0px;
-}
-
-.htmlview {
-    position: absolute;
-    top: 25px;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    overflow: auto;
-    margin: 0;
-}
-
-.image-gallery-page-container img {
-    /* border: 2px solid green; */
-    margin: 0px;
-}
-
-body#base button {
-    background-color: #DDD;
-    border-width: 1px;
-    padding: 0px 0.5em;    
-    font-family: Sans-Serif;
-    /* color: #000; */
-    margin: 2px 4px;
-}
-
-body#base button:hover {
-    background-color: #EEE;
-}
-
-/* HTML editor interactive elements */
-
-.html-editarea {
-    border: 2px solid black;
-    background-color: gray;
-    padding: 1px;
-
-    z-index: 2000;
-}
-
-.html-editarea textarea
-{   
-   
-    border: 0px;
-    margin: 0px;    
-    padding: 0px;
-    
-    width: 100%;
-    height: 100%;
-    
-    z-index: 0;
-    font-size: 10pt;
-    background-color: ivory;
-}
-
-.html-editarea p.html-editarea-toolbar {
-    position: absolute;
-    background: gray;
-
-    bottom: -26px;
-    height: 24px;
-    
-    left: 0px;
-    right: 0px;    
-        
-    border: 2px solid black;
-
-    margin: 0px;
-    padding: 0px;
-
-    z-index: 100;
-}
-
-/* ================= */
-/* = Message boxes = */
-/* ================= */
-.info {
-    background-color: gray;
-}
-
-.success {
-    background-color: green;
-}
-
-.error {
-    background-color: yellow;
-}
-
-.critical {
-    background-color: red;
-}
-
diff --git a/project/static/css/toolbar.css b/project/static/css/toolbar.css
deleted file mode 100644 (file)
index b5df66f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-.toolbar-buttons-container {    
-    padding-top: 2px;
-    padding-bottom: 2px;
-}
-
diff --git a/project/static/css/xmlcolors.css b/project/static/css/xmlcolors.css
deleted file mode 100644 (file)
index aa26579..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-.editbox {
-  margin: .4em;
-  padding: 0;
-  font-family: monospace;
-  font-size: 10pt;
-  color: black;
-}
-
-.editbox p {
-  margin: 0;
-}
-
-span.xml-tagname {
-  color: #A0B;
-}
-
-span.xml-attribute {
-  color: #281;
-}
-
-span.xml-punctuation {
-  color: black;
-}
-
-span.xml-attname {
-  color: #00F;
-}
-
-span.xml-comment {
-  color: #A70;
-}
-
-span.xml-cdata {
-  color: #48A;
-}
-
-span.xml-processing {
-  color: #999;
-}
-
-span.xml-entity {
-  color: #A22;
-}
-
-span.xml-error {
-  color: #F00;
-}
-
-span.xml-text {
-  color: black;
-}
diff --git a/project/static/icons/go-next.png b/project/static/icons/go-next.png
deleted file mode 100644 (file)
index 6ef8de7..0000000
Binary files a/project/static/icons/go-next.png and /dev/null differ
diff --git a/project/static/icons/go-previous.png b/project/static/icons/go-previous.png
deleted file mode 100644 (file)
index 659cd90..0000000
Binary files a/project/static/icons/go-previous.png and /dev/null differ
diff --git a/project/static/icons/zoom.png b/project/static/icons/zoom.png
deleted file mode 100644 (file)
index 6033b4d..0000000
Binary files a/project/static/icons/zoom.png and /dev/null differ
diff --git a/project/static/icons/zoom_in.png b/project/static/icons/zoom_in.png
deleted file mode 100644 (file)
index c7feedc..0000000
Binary files a/project/static/icons/zoom_in.png and /dev/null differ
diff --git a/project/static/icons/zoom_out.png b/project/static/icons/zoom_out.png
deleted file mode 100644 (file)
index fdd7124..0000000
Binary files a/project/static/icons/zoom_out.png and /dev/null differ
diff --git a/project/static/js/app.js b/project/static/js/app.js
deleted file mode 100644 (file)
index e8b439d..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*global Class*/
-var editor;
-var panel_hooks;
-
-
-// prevent a console.log from blowing things up if we are on a browser that
-// does not support it
-if (typeof console === 'undefined') {
-  window.console = {} ;
-  console.log = console.info = console.warn = console.error = function(){};
-}
-
-
-(function(){
-  // Classes
-  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
-  this.Class = function(){};
-  Class.extend = function(prop) {
-    var _super = this.prototype;
-    initializing = true;
-    var prototype = new this();
-    initializing = false;
-    for (var name in prop) {
-      prototype[name] = typeof prop[name] == "function" &&
-        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
-        (function(name, fn){
-          return function() {
-            var tmp = this._super;
-            this._super = _super[name];
-            var ret = fn.apply(this, arguments);       
-            this._super = tmp;           
-            return ret;
-          };
-        })(name, prop[name]) :
-        prop[name];
-    }   
-    function Class() {
-      if ( !initializing && this.init )
-        this.init.apply(this, arguments);
-    }
-    Class.prototype = prototype;
-    Class.constructor = Class;
-    Class.extend = arguments.callee;   
-    return Class;
-  };
-  
-  // Templates
-  var cache = {};
-
-  this.render_template = function render_template(str, data){
-    // Figure out if we're getting a template, or if we need to
-    // load the template - and be sure to cache the result.    
-    var fn = !/^[\d\s-_]/.test(str) ?
-      cache[str] = cache[str] ||
-        render_template(document.getElementById(str).innerHTML) :
-
-      // Generate a reusable function that will serve as a template
-      // generator (and which will be cached).
-      
-      new Function("obj",
-        "var p=[],print=function(){p.push.apply(p,arguments);};" +
-
-        // Introduce the data as local variables using with(){}
-        "with(obj){p.push('" +
-
-        // Convert the template into pure JavaScript       
-        str
-          .replace(/[\r\t\n]/g, " ")
-          .split("<%").join("\t")
-          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
-          .replace(/\t=(.*?)%>/g, "',$1,'")
-          .split("\t").join("');")
-          .split("%>").join("p.push('")
-          .split("\r").join("\\'")
-      + "');}return p.join('');");
-
-      // Provide some basic currying to the user
-    return data ? fn( data ) : fn;
-  };
-})();
-
-
-(function() {
-  var slice = Array.prototype.slice;
-  
-  function update(array, args) {
-    var arrayLength = array.length, length = args.length;
-    while (length--) array[arrayLength + length] = args[length];
-    return array;
-  };
-  
-  function merge(array, args) {
-    array = slice.call(array, 0);
-    return update(array, args);
-  };
-  
-  Function.prototype.bind = function(context) {
-    if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
-      return this;
-    } 
-    var __method = this;
-    var args = slice.call(arguments, 1);
-    return function() {
-      var a = merge(args, arguments);
-      return __method.apply(context, a);
-    }
-  }
-  
-})();
-
-
-var Editor = Editor || {};
-
-// Obiekt implementujący wzorzec KVC/KVO
-Editor.Object = Class.extend({
-  _className: 'Editor.Object',
-  _observers: {},
-  _guid: null,
-  
-  init: function() {
-    this._observers = {};
-  },
-  
-  description: function() {
-    return this._className + '(guid = ' + this.guid() + ')';
-  },
-  
-  addObserver: function(observer, property, callback) {
-    // console.log('Add observer', observer.description(), 'to', this.description(), '[', property, ']');
-    if (!this._observers[property]) {
-      this._observers[property] = {}
-    }
-    this._observers[property][observer.guid()] = callback;
-    return this;
-  },
-  
-  removeObserver: function(observer, property) {
-    if (!property) {
-      for (var property in this._observers) {
-        this.removeObserver(observer, property)
-      }
-    } else {
-      // console.log('Remove observer', observer.description(), 'from', this.description(), '[', property, ']');
-      delete this._observers[property][observer.guid()];
-    }
-    return this;
-  },
-  
-  notifyObservers: function(property) {
-    var currentValue = this[property];
-    for (var guid in this._observers[property]) {
-      // console.log(this._observers[property][guid]);
-      // console.log('Notifying', guid, 'of', this.description(), '[', property, ']');
-      this._observers[property][guid](property, currentValue, this);
-    }
-    return this;
-  },
-  
-  guid: function() {
-    if (!this._guid) {
-      this._guid = ('editor-' + Editor.Object._lastGuid++);
-    }
-    return this._guid;
-  },
-  
-  get: function(property) {
-    return this[property];
-  },
-  
-  set: function(property, value) {
-    if (this[property] != value) {
-      this[property] = value;
-      this.notifyObservers(property);
-    }
-    return this;
-  },
-  
-  dispose: function() {
-    delete this._observers;
-  }
-});
-
-// Handle JSON error responses in uniform way
-function parseXHRError(response)
-{
-    var message = ""
-    try {
-      var json = $.evalJSON(response.responseText);
-
-      if(json.reason == 'xml-parse-error') {
-          message = json_response.message.replace(/(line\s+)(\d+)(\s+)/i,
-            "<a class='xml-editor-ref' href='#xml-$2-1'>$1$2$3</a>");
-
-          message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i,
-            "<a class='xml-editor-ref' href='#xml-$2-$4'>$1$2$3$4</a>");
-      }
-      message = json_response.message || json_response.reason || "Nieznany błąd :((";
-    } catch(e) {
-        // not a valid JSON response
-        message = response.statusText;
-    }
-    return message;
-}
-
-Editor.Object._lastGuid = 0;
-
-var panels = [];
\ No newline at end of file
diff --git a/project/static/js/button_scripts.js b/project/static/js/button_scripts.js
deleted file mode 100644 (file)
index 40f45f6..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-function ScriptletCenter()
-{
-    this.scriptlets = {};
-
-    this.scriptlets['insert_tag'] = function(context, params)
-    {
-        var text = this.XMLEditorSelectedText(context);
-        var start_tag = '<'+params.tag;
-
-        for (var attr in params.attrs) {
-            start_tag += ' '+attr+'="' + params.attrs[attr] + '"';
-        }
-
-        start_tag += '>';
-        var end_tag = '</'+params.tag+'>';
-
-        if(text.length > 0) {
-            // tokenize
-            var output = '';
-            var token = '';
-            for(var index=0; index < text.length; index++)
-            {
-                if (text[index].match(/\s/)) { // whitespace
-                    token += text[index];
-                }
-                else { // character
-                    output += token;
-                    if(output == token) output += start_tag;
-                    token = '';
-                    output += text[index];
-                }
-            }
-
-            if( output[output.length-1] == '\\' ) {
-                output = output.substr(0, output.length-1) + end_tag + '\\';
-            } else {
-                output += end_tag;
-            }
-            output += token;
-        }
-        else {
-            output = start_tag + end_tag;
-        }
-
-        this.XMLEditorReplaceSelectedText(context, output);
-
-        if (text.length == 0) {
-            this.XMLEditorMoveCursorForward(context, -params.tag.length-3);
-        }        
-    }.bind(this);
-
-    this.scriptlets['lineregexp'] = function(context, params) {
-
-        var exprs = $.map(params.exprs, function(expr) {
-            var opts = "g";
-            if(expr.length > 2) {
-                opts = expr[2];
-            } return {
-                rx: new RegExp(expr[0], opts),
-                repl: expr[1]
-                };
-        });
-
-        var partial = true;
-        var text = this.XMLEditorSelectedText(context);
-        if(!text) return;
-
-        var changed = 0;
-        var lines = text.split('\n');
-        lines = $.map(lines, function(line) {
-            var old_line = line;
-            $(exprs).each(function() {
-                var expr = this;
-                line = line.replace(expr.rx, expr.repl);
-            });
-
-            if(old_line != line) changed += 1;
-            return line;
-        });
-
-        if(changed > 0) {
-            this.XMLEditorReplaceSelectedText(context, lines.join('\n') );
-        }
-    }.bind(this);
-
-    this.scriptlets['codemirror_fontsize'] = function(context, params) {
-        var frameBody = this.XMLEditorBody(context);
-
-        if(params.fontSize) {
-            frameBody.css('font-size', params.fontSize);
-        }
-        else {
-            var old_size = parseInt(frameBody.css('font-size'), 10);
-            frameBody.css('font-size', old_size + (params.change || 0) );
-        }
-        
-    }.bind(this);
-
-    this.scriptlets['fulltextregexp'] = function(context, params) {
-        var exprs = $.map(params.exprs, function(expr) {
-            var opts = "mg";
-            if(expr.length > 2) {
-                opts = expr[2];
-            }
-            return {
-                rx: new RegExp(expr[0], opts),
-                repl: expr[1]
-                };
-        });
-
-        var text = this.XMLEditorSelectedText(context);
-        if(!text) return;
-        var original = text;
-        $(exprs).each(function() {
-            text = text.replace(this.rx, this.repl);
-        });
-
-        if( original != text) {
-            this.XMLEditorReplaceSelectedText(context, text);
-        }
-    }.bind(this);
-
-    this.scriptlets['macro'] = function(context, params) {
-        var self = this;
-
-        $(params).each(function() {
-            $.log(this[0], this[1]);
-            self.scriptlets[this[0]](context, this[1]);
-        });
-    }.bind(this);
-
-    this.scriptlets['lowercase'] = function(context, params)
-    {
-        var text = this.XMLEditorSelectedText(context);
-
-        if(!text) return;
-
-        var repl = '';
-        var lcase = text.toLowerCase();
-        var ucase = text.toUpperCase();
-
-        if(lcase == text) repl = ucase; /* was lowercase */
-        else if(ucase != text) repl = lcase; /* neither lower- or upper-case */
-        else { /* upper case -> camel-case */
-            var words = $(lcase.split(/\s/)).map(function() {
-                if(this.length > 0) { 
-                    return this[0].toUpperCase() + this.slice(1);
-                } else {
-                    return '';
-                }
-            });
-            repl = words.join(' ');
-        }
-
-        if(repl != text) this.XMLEditorReplaceSelectedText(context, repl);
-    }.bind(this);
-
-
-    this.scriptlets["insert_stanza"] = function(context, params) {
-        var text = this.XMLEditorSelectedText(context);
-
-        if(text) {
-            var verses = text.split('\n');
-            text = ''; var buf = ''; var ebuf = '';
-            var first = true;
-
-            for(var i=0;  i < verses.length; i++) {
-                var verse = verses[i].replace(/^\s+/, "").replace(/\s+$/, "");
-                if(verse) {
-                    text += (buf ? buf + '/\n' : '') + ebuf;
-                    buf = (first ? '<strofa>\n' : '') + verses[i];
-                    ebuf = '';
-                    first = false;
-                } else {
-                    ebuf += '\n' + verses[i];
-                }
-            }
-            text = text + buf + '\n</strofa>' + ebuf;
-            this.XMLEditorReplaceSelectedText(context, text);
-        }
-
-        if (!text) {
-            this.XMLEditorMoveCursorForward(context, params.tag.length + 2);
-        }
-        
-    }.bind(this);
-
-}
-
-ScriptletCenter.prototype.XMLEditorSelectedText = function(panel) {
-    return panel.contentView.editor.selection();
-};
-
-ScriptletCenter.prototype.XMLEditorReplaceSelectedText = function(panel, replacement)
-{
-    panel.contentView.editor.replaceSelection(replacement);
-    // Tell XML view that it's data has changed
-    panel.contentView.editorDataChanged();
-};
-
-ScriptletCenter.prototype.XMLEditorMoveCursorForward = function(panel, n) {
-    var pos = panel.contentView.editor.cursorPosition();
-    panel.contentView.editor.selectLines(pos.line, pos.character + n);
-};
-
-var scriptletCenter;
-
-$(function() {
-    scriptletCenter = new ScriptletCenter();
-});
\ No newline at end of file
diff --git a/project/static/js/editor.js b/project/static/js/editor.js
deleted file mode 100644 (file)
index f52950f..0000000
+++ /dev/null
@@ -1,578 +0,0 @@
-var editor;
-var panel_hooks;
-
-function Hotkey(code) {
-    this.code = code;
-    this.has_alt = ((code & 0x01 << 8) !== 0);
-    this.has_ctrl = ((code & 0x01 << 9) !== 0);
-    this.has_shift = ((code & 0x01 << 10) !== 0);
-    this.character = String.fromCharCode(code & 0xff);
-}
-
-Hotkey.prototype.toString = function() {
-    var mods = [];
-    if(this.has_alt) mods.push('Alt');
-    if(this.has_ctrl) mods.push('Ctrl');
-    if(this.has_shift) mods.push('Shift');
-    mods.push('"'+this.character+'"');
-    return mods.join('+');
-};
-
-function Panel(panelWrap) {
-    var self = this;
-    self.hotkeys = [];
-    self.wrap = panelWrap;
-    self.contentDiv = $('.panel-content', panelWrap);
-    self.instanceId = Math.ceil(Math.random() * 1000000000);
-    // $.log('new panel - wrap: ', self.wrap);
-       
-    $(document).bind('panel:unload.' + self.instanceId,
-        function(event, data) {
-            self.unload(event, data);
-        });
-
-    $(document).bind('panel:contentChanged', function(event, data) {
-        $.log(self, ' got changed event from: ', data);
-        if(self != data) {
-            self.otherPanelChanged(event.target);
-        } else {
-            self.markChanged();
-        }
-        return false;
-    });
-}
-
-Panel.prototype.callHook = function() {
-    var args = $.makeArray(arguments);
-    var hookName = args.splice(0,1)[0];
-    var noHookAction = args.splice(0,1)[0];
-    var result = false;
-
-    $.log('calling hook: ', hookName, 'with args: ', args);
-    if(this.hooks && this.hooks[hookName]) {
-        result = this.hooks[hookName].apply(this, args);
-    } else if (noHookAction instanceof Function) {
-        result = noHookAction(args);
-    }
-    return result;
-};
-
-Panel.prototype._endload = function () {
-    // this needs to be here, so we
-    this.connectToolbar();
-    this.callHook('toolbarResized');
-};  
-
-Panel.prototype.load = function (url) {
-    // $.log('preparing xhr load: ', this.wrap);
-    $(document).trigger('panel:unload', this);
-    var self = this;
-    self.current_url = url;
-
-    $.ajax({
-        url: url,
-        dataType: 'html',
-        success: function(data, tstat) {
-            panel_hooks = null;
-            $(self.contentDiv).html(data);
-            self.hooks = panel_hooks;
-            panel_hooks = null;            
-            self.callHook('load');           
-        },
-        error: function(request, textStatus, errorThrown) {
-            $.log('ajax', url, this.target, 'error:', textStatus, errorThrown);
-            $(self.contentDiv).html("<p>Wystapił błąd podczas wczytywania panelu.</p>");
-        }
-    });
-};
-
-Panel.prototype.unload = function(event, data) {
-    // $.log('got unload signal', this, ' target: ', data);
-    if( data == this ) {        
-        $(this.contentDiv).html('');
-
-        // disconnect the toolbar
-        $('div.panel-toolbar span.panel-toolbar-extra', this.wrap).html(
-            '<span />');
-        
-        this.callHook('unload');
-        this.hooks = null; // flush the hooks
-        return false;
-    }
-};
-
-Panel.prototype.refresh = function(event, data) {
-    var self = this;
-    var reload = function() {
-        $.log('hard reload for panel ', self.current_url);
-        self.load(self.current_url);
-        return true;
-    };
-
-    if( this.callHook('refresh', reload) ) {
-        $('.change-notification', this.wrap).fadeOut();
-    }
-}; 
-
-Panel.prototype.otherPanelChanged = function(other) {
-    $.log('Panel ', this, ' is aware that ', other, ' changed.');
-    if(!this.callHook('dirty')) {
-        $('.change-notification', this.wrap).fadeIn();
-    }
-};     
-
-Panel.prototype.markChanged = function () {
-    this.wrap.addClass('changed');
-};
-
-Panel.prototype.changed = function () {
-    return this.wrap.hasClass('changed');
-};
-
-Panel.prototype.unmarkChanged = function () {
-    this.wrap.removeClass('changed');
-};
-
-Panel.prototype.saveInfo = function() {
-    var saveInfo = {};
-    this.callHook('saveInfo', null, saveInfo);
-    return saveInfo;
-};
-
-Panel.prototype.connectToolbar = function()
-{
-    var self = this;
-    self.hotkeys = [];
-    
-    // check if there is a one
-    var toolbar = $("div.toolbar", this.contentDiv);
-    // $.log('Connecting toolbar', toolbar);
-    if(toolbar.length === 0) return;
-
-    // move the extra
-    var extra_buttons = $('span.panel-toolbar-extra button', toolbar);
-    var placeholder = $('div.panel-toolbar span.panel-toolbar-extra > span', this.wrap);
-    placeholder.replaceWith(extra_buttons);       
-
-    // connect group-switch buttons
-    var group_buttons = $('*.toolbar-tabs-container button', toolbar);
-
-    // $.log('Found groups:', group_buttons);
-
-    group_buttons.each(function() {
-        var group = $(this);
-        var group_name = group.attr('ui:group');
-        // $.log('Connecting group: ' + group_name);
-
-        group.click(function() {
-            // change the active group
-            var active = $("*.toolbar-tabs-container button.active", toolbar);
-            if (active != group) {
-                active.removeClass('active');                
-                group.addClass('active');
-                $(".toolbar-button-groups-container p", toolbar).each(function() {
-                    if ( $(this).attr('ui:group') != group_name) {
-                        $(this).hide();
-                    } else {
-                        $(this).show();
-                    }
-                });
-                self.callHook('toolbarResized');
-            }
-        });        
-    });
-
-    // connect action buttons
-    var allbuttons = $.makeArray(extra_buttons);
-    $.merge(allbuttons,
-        $.makeArray($('*.toolbar-button-groups-container button', toolbar)) );
-        
-    $(allbuttons).each(function() {
-        var button = $(this);
-        var hk = button.attr('ui:hotkey');
-        if(hk) hk = new Hotkey( parseInt(hk) );
-
-        try {
-            var params = $.evalJSON(button.attr('ui:action-params'));
-        } catch(object) {
-            $.log('JSON exception in ', button, ': ', object);
-            button.attr('disabled', 'disabled');
-            return;
-        }
-
-        var callback = function() {
-            editor.callScriptlet(button.attr('ui:action'), self, params);
-        };
-
-        // connect button
-        button.click(callback);
-       
-        // connect hotkey
-        if(hk) {
-            self.hotkeys[hk.code] = callback;
-        // $.log('hotkey', hk);
-        }
-        
-        // tooltip
-        if (button.attr('ui:tooltip') )
-        {
-            var tooltip = button.attr('ui:tooltip');
-            if(hk) tooltip += ' ['+hk+']';
-
-            button.wTooltip({
-                delay: 1000,
-                style: {
-                    border: "1px solid #7F7D67",
-                    opacity: 0.9,
-                    background: "#FBFBC6",
-                    padding: "1px",
-                    fontSize: "12px"
-                },
-                content: tooltip
-            });
-        }
-    });
-};
-
-Panel.prototype.hotkeyPressed = function(event)
-{
-    var code = event.keyCode;
-    if(event.altKey) code = code | 0x100;
-    if(event.ctrlKey) code = code | 0x200;
-    if(event.shiftKey) code = code | 0x400;
-
-    var callback = this.hotkeys[code];
-    if(callback) callback();
-};
-
-Panel.prototype.isHotkey = function(event) {
-    var code = event.keyCode;
-    if(event.altKey) code = code | 0x100;
-    if(event.ctrlKey) code = code | 0x200;
-    if(event.shiftKey) code = code | 0x400;
-
-    $.log(event.character, this.hotkeys[code]);
-
-    if(this.hotkeys[code]) {
-        return true;
-    }
-    return false;
-};
-
-Panel.prototype.fireEvent = function(name) {
-    $(document).trigger('panel:'+name, this);
-};
-
-function Editor()
-{
-    this.rootDiv = $('#panels');
-    this.popupQueue = [];
-    this.autosaveTimer = null;
-    this.scriplets = {};
-}
-
-Editor.prototype.loadConfig = function() {
-    // Load options from cookie
-    var defaultOptions = {
-        panels: [
-        {
-            name: 'htmleditor',
-            ratio: 0.5
-        },
-
-        {
-            name: 'gallery',
-            ratio: 0.5
-        }
-        ],
-        recentFiles: [],
-        lastUpdate: 0
-    };
-    
-    try {
-        var cookie = $.cookie('options');
-        this.options = $.secureEvalJSON(cookie);
-        if (!this.options) {
-            this.options = defaultOptions;
-        }
-    } catch (e) {    
-        this.options = defaultOptions;
-    }
-    
-    this.fileOptions = this.options;
-    var self = this;
-
-    if(!this.options.recentFiles)
-        this.options.recentFiles = [];
-
-    $.each(this.options.recentFiles, function(index) {
-        if (fileId == self.options.recentFiles[index].fileId) {
-            $.log('Found options for', fileId);
-            self.fileOptions = self.options.recentFiles[index];
-        }
-    });
-    
-    $.log(this.options);
-    $.log('fileOptions', this.fileOptions);
-    
-    this.loadPanelOptions();
-    this.savePanelOptions();
-};
-
-Editor.prototype.loadPanelOptions = function() {
-    // var self = this;
-    // var totalWidth = 0;
-    // 
-    // $('.panel-wrap', self.rootDiv).each(function(index) {
-    //     var panelWidth = self.fileOptions.panels[index].ratio * self.rootDiv.width();
-    //     if ($(this).hasClass('last-panel')) {
-    //         $(this).css({
-    //             left: totalWidth,
-    //             right: 0
-    //         });
-    //     } else {
-    //         $(this).css({
-    //             left: totalWidth,
-    //             width: panelWidth
-    //         });
-    //         totalWidth += panelWidth;               
-    //     }
-    //     $.log('panel:', this, $(this).css('left'));
-    //     $('.panel-toolbar option', this).each(function() {
-    //         if ($(this).attr('p:panel-name') == self.fileOptions.panels[index].name) {
-    //             $(this).parent('select').val($(this).attr('value'));
-    //         }
-    //     });
-    // });   
-};
-
-Editor.prototype.savePanelOptions = function() {
-    var self = this;
-    var panels = [];
-    $('.panel-wrap', self.rootDiv).not('.panel-content-overlay').each(function() {
-        panels.push({
-            name: $('.panel-toolbar option:selected', this).attr('p:panel-name'),
-            ratio: $(this).width() / self.rootDiv.width()
-        });
-    });
-    self.options.panels = panels;
-
-    // Dodaj obecnie oglądany plik do listy recentFiles
-    var recentFiles = [{fileId: fileId, panels: panels}];
-    var count = 1;
-    $.each(self.options.recentFiles, function(index) {
-        if (count < 5 && fileId != self.options.recentFiles[index].fileId) {
-            recentFiles.push(self.options.recentFiles[index]);
-            count++;
-        }
-    });
-    self.options.recentFiles = recentFiles;
-    
-    self.options.lastUpdate = new Date().getTime() / 1000;
-    $.log($.toJSON(self.options));    
-    $.cookie('options', $.toJSON(self.options), {
-        expires: 7,
-        path: '/'
-    });
-};
-
-Editor.prototype.saveToBranch = function(msg) 
-{
-    var changed_panel = $('.panel-wrap.changed');
-    var self = this;
-    $.log('Saving to local branch - panel:', changed_panel);
-
-    if(!msg) msg = "Szybki zapis z edytora platformy.";
-
-    if( changed_panel.length === 0) {
-        $.log('Nothing to save.');
-        return true; /* no changes */
-    }
-
-    if( changed_panel.length > 1) {
-        alert('Błąd: więcej niż jeden panel został zmodyfikowany. Nie można zapisać.');
-        return false;
-    }
-
-    var saveInfo = changed_panel.data('ctrl').saveInfo();
-    var postData = '';
-    
-    if (saveInfo.postData instanceof Object) {
-        postData = $.param(saveInfo.postData);
-    } else {
-        postData = saveInfo.postData;
-    }
-    
-    postData += '&' + $.param({
-        'commit_message': msg
-    });
-
-    self.showPopup('save-waiting', '', -1);
-
-    $.ajax({
-        url: saveInfo.url,
-        dataType: 'json',
-        success: function(data, textStatus) {
-            if (data.result != 'ok') {
-                self.showPopup('save-error', (data.errors && data.errors[0]) || 'Nieznany błąd X_X.');
-            }
-            else {
-                self.refreshPanels();
-
-
-                if(self.autosaveTimer) {
-                    clearTimeout(self.autosaveTimer);
-                }
-                if (data.warnings === null || data.warning === undefined) {
-                    self.showPopup('save-successful');
-                } else {
-                    self.showPopup('save-warn', data.warnings[0]);
-                }
-            }
-            
-            self.advancePopupQueue();
-        },
-        error: function(rq, tstat, err) {
-            self.showPopup('save-error', '- bład wewnętrzny serwera.');
-            self.advancePopupQueue();
-        },
-        type: 'POST',
-        data: postData
-    });
-
-    return true;
-};
-
-Editor.prototype.autoSave = function() 
-{
-    this.autosaveTimer = null;
-    // first check if there is anything to save
-    $.log('Autosave');
-    this.saveToBranch("Automatyczny zapis z edytora platformy.");
-};
-
-Editor.prototype.onContentChanged = function(event, data) {
-    var self = this;
-
-    $('button.provides-save').removeAttr('disabled');
-    $('button.requires-save').attr('disabled', 'disabled');
-    
-    if(this.autosaveTimer) return;
-    this.autosaveTimer = setTimeout( function() {
-        self.autoSave();
-    }, 300000 );
-};
-
-Editor.prototype.updateUserBranch = function() {
-    if($('.panel-wrap.changed').length !== 0) {
-        alert("There are unsaved changes - can't update.");
-    }
-
-    var self = this;
-    $.ajax({
-        url: $('#toolbar-button-update').attr('ui:ajax-action'),
-        dataType: 'json',
-        success: function(data, textStatus) {
-            switch(data.result) {
-                case 'done':
-                    self.showPopup('generic-yes', 'Plik uaktualniony.');
-                    self.refreshPanels();
-                    break;
-                case 'nothing-to-do':
-                    self.showPopup('generic-info', 'Brak zmian do uaktualnienia.');
-                    break;
-                default:
-                    self.showPopup('generic-error', data.errors && data.errors[0]);
-            }
-        },
-        error: function(rq, tstat, err) {
-            self.showPopup('generic-error', 'Błąd serwera: ' + err);
-        },
-        type: 'POST',
-        data: {}
-    });
-};
-
-Editor.prototype.sendMergeRequest = function (message) {
-    if( $('.panel-wrap.changed').length !== 0) {
-        alert("There are unsaved changes - can't commit.");
-    }
-
-    var self =  this;    
-        
-    $.ajax({        
-        url: $('#commit-dialog form').attr('action'),
-        dataType: 'json',
-        success: function(data, textStatus) {
-            switch(data.result) {
-                case 'done':
-                    self.showPopup('generic-yes', 'Łączenie zmian powiodło się.');
-
-                    if(data.localmodified) {
-                        self.refreshPanels();
-                    }
-                        
-                    break;
-                case 'nothing-to-do':
-                    self.showPopup('generic-info', 'Brak zmian do połaczenia.');
-                    break;
-                default:
-                    self.showPopup('generic-error', data.errors && data.errors[0]);
-            }
-        },
-        error: function(rq, tstat, err) {
-            self.showPopup('generic-error', 'Błąd serwera: ' + err);
-        },
-        type: 'POST',
-        data: {
-            'message': message
-        }
-    }); 
-};
-
-Editor.prototype.postSplitRequest = function(s, f)
-{
-    $.ajax({
-        url: $('#split-dialog form').attr('action'),
-        dataType: 'html',
-        success: s,
-        error: f,
-        type: 'POST',
-        data: $('#split-dialog form').serialize()
-    });
-};
-
-
-Editor.prototype.allPanels = function() {
-    return $('#' + this.rootDiv.attr('id') +' > *.panel-wrap', this.rootDiv.parent());
-};
-
-Editor.prototype.registerScriptlet = function(scriptlet_id, scriptlet_func)
-{
-    // I briefly assume, that it's verified not to break the world on SS
-    if (!this[scriptlet_id]) {
-        this[scriptlet_id] = scriptlet_func;
-    }
-};
-
-Editor.prototype.callScriptlet = function(scriptlet_id, panel, params) {
-    var func = this[scriptlet_id];
-    if(!func) {
-        throw 'No scriptlet named "' + scriptlet_id + '" found.';
-    }
-    return func(this, panel, params);
-};
-
-$(function() {
-    $.fbind = function (self, func) {
-        return function() { 
-            return func.apply(self, arguments);
-        };
-    };
-    
-    editor = new Editor();
-
-    // do the layout
-    editor.loadConfig();
-   editor.setupUI();
-});
diff --git a/project/static/js/lib/codemirror/codemirror.js b/project/static/js/lib/codemirror/codemirror.js
deleted file mode 100644 (file)
index f63ed07..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/* CodeMirror main module
- *
- * Implements the CodeMirror constructor and prototype, which take care
- * of initializing the editor frame, and providing the outside interface.
- */
-
-// The CodeMirrorConfig object is used to specify a default
-// configuration. If you specify such an object before loading this
-// file, the values you put into it will override the defaults given
-// below. You can also assign to it after loading.
-var CodeMirrorConfig = window.CodeMirrorConfig || {};
-
-var CodeMirror = (function(){
-  function setDefaults(object, defaults) {
-    for (var option in defaults) {
-      if (!object.hasOwnProperty(option))
-        object[option] = defaults[option];
-    }
-  }
-  function forEach(array, action) {
-    for (var i = 0; i < array.length; i++)
-      action(array[i]);
-  }
-
-  // These default options can be overridden by passing a set of
-  // options to a specific CodeMirror constructor. See manual.html for
-  // their meaning.
-  setDefaults(CodeMirrorConfig, {
-    stylesheet: "",
-    path: "",
-    parserfile: [],
-    basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"],
-    iframeClass: null,
-    passDelay: 200,
-    passTime: 50,
-    continuousScanning: false,
-    saveFunction: null,
-    onChange: null,
-    undoDepth: 50,
-    undoDelay: 800,
-    disableSpellcheck: true,
-    textWrapping: true,
-    readOnly: false,
-    width: "100%",
-    height: "300px",
-    autoMatchParens: false,
-    parserConfig: null,
-    tabMode: "indent", // or "spaces", "default", "shift"
-    reindentOnLoad: false,
-    activeTokens: null,
-    cursorActivity: null,
-    lineNumbers: false,
-    indentUnit: 2
-  });
-
-  function wrapLineNumberDiv(place) {
-    return function(node) {
-      var container = document.createElement("DIV"),
-          nums = document.createElement("DIV"),
-          scroller = document.createElement("DIV");
-      container.style.position = "relative";
-      nums.style.position = "absolute";
-      nums.style.height = "100%";
-      if (nums.style.setExpression) {
-        try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
-        catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
-      }
-      nums.style.top = "0px";
-      nums.style.overflow = "hidden";
-      place(container);
-      container.appendChild(node);
-      container.appendChild(nums);
-      scroller.className = "CodeMirror-line-numbers";
-      nums.appendChild(scroller);
-    }
-  }
-
-  function applyLineNumbers(frame) {
-    var win = frame.contentWindow, doc = win.document,
-        nums = frame.nextSibling, scroller = nums.firstChild;
-
-    var nextNum = 1, barWidth = null;
-    function sizeBar() {
-      for (var root = frame; root.parentNode; root = root.parentNode);
-      if (root != document || !win.Editor) {
-        clearInterval(sizeInterval);
-        return;
-      }
-
-      if (nums.offsetWidth != barWidth) {
-        barWidth = nums.offsetWidth;
-        nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px");
-      }
-    }
-    function update() {
-      var diff = 20 + Math.max(doc.body.offsetHeight, frame.offsetHeight) - scroller.offsetHeight;
-      for (var n = Math.ceil(diff / 10); n > 0; n--) {
-        var div = document.createElement("DIV");
-        div.appendChild(document.createTextNode(nextNum++));
-        scroller.appendChild(div);
-      }
-      nums.scrollTop = doc.body.scrollTop || doc.documentElement.scrollTop || 0;
-    }
-    sizeBar();
-    update();
-    win.addEventHandler(win, "scroll", update);
-    win.addEventHandler(win, "resize", update);
-    var sizeInterval = setInterval(sizeBar, 500);
-  }
-
-  function CodeMirror(place, options) {
-    // Backward compatibility for deprecated options.
-    if (options.dumbTabs) options.tabMode = "spaces";
-    else if (options.normalTab) options.tabMode = "default";
-
-    // Use passed options, if any, to override defaults.
-    this.options = options = options || {};
-    setDefaults(options, CodeMirrorConfig);
-
-    var frame = this.frame = document.createElement("IFRAME");
-    if (options.iframeClass) frame.className = options.iframeClass;
-    frame.frameBorder = 0;
-    frame.src = "javascript:false;";
-    frame.style.border = "0";
-    frame.style.width = options.width;
-    frame.style.height = options.height;
-    // display: block occasionally suppresses some Firefox bugs, so we
-    // always add it, redundant as it sounds.
-    frame.style.display = "block";
-
-    if (place.appendChild) {
-      var node = place;
-      place = function(n){node.appendChild(n);};
-    }
-    
-    if (options.lineNumbers) place = wrapLineNumberDiv(place);
-    place(frame);
-
-    // Link back to this object, so that the editor can fetch options
-    // and add a reference to itself.
-    frame.CodeMirror = this;
-    this.win = frame.contentWindow;
-
-    if (typeof options.parserfile == "string")
-      options.parserfile = [options.parserfile];
-    if (typeof options.stylesheet == "string")
-      options.stylesheet = [options.stylesheet];
-
-    var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><head>"];
-    // Hack to work around a bunch of IE8-specific problems.
-    html.push("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/>");
-    forEach(options.stylesheet, function(file) {
-      html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
-    });
-    forEach(options.basefiles.concat(options.parserfile), function(file) {
-      html.push("<script type=\"text/javascript\" src=\"" + options.path + file + "\"></script>");
-    });
-    html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
-              (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
-
-    var doc = this.win.document;
-    doc.open();
-    doc.write(html.join(""));
-    doc.close();
-  }
-
-  CodeMirror.prototype = {
-    init: function() {
-      if (this.options.initCallback) this.options.initCallback(this);
-      if (this.options.lineNumbers) applyLineNumbers(this.frame);
-      if (this.options.reindentOnLoad) this.reindent();
-    },
-
-    getCode: function() {return this.editor.getCode();},
-    setCode: function(code) {this.editor.importCode(code);},
-    selection: function() {this.focusIfIE(); return this.editor.selectedText();},
-    reindent: function() {this.editor.reindent();},
-    reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);},
-
-    focusIfIE: function() {
-      // in IE, a lot of selection-related functionality only works when the frame is focused
-      if (this.win.select.ie_selection) this.focus();
-    },
-    focus: function() {
-      this.win.focus();
-      if (this.editor.selectionSnapshot) // IE hack
-        this.win.select.selectCoords(this.win, this.editor.selectionSnapshot);
-    },
-    replaceSelection: function(text) {
-      this.focus();
-      this.editor.replaceSelection(text);
-      return true;
-    },
-    replaceChars: function(text, start, end) {
-      this.editor.replaceChars(text, start, end);
-    },
-    getSearchCursor: function(string, fromCursor) {
-      return this.editor.getSearchCursor(string, fromCursor);
-    },
-
-    undo: function() {this.editor.history.undo();},
-    redo: function() {this.editor.history.redo();},
-    historySize: function() {return this.editor.history.historySize();},
-    clearHistory: function() {this.editor.history.clear();},
-
-    grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);},
-    ungrabKeys: function() {this.editor.ungrabKeys();},
-
-    setParser: function(name) {this.editor.setParser(name);},
-
-    cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);},
-    firstLine: function() {return this.editor.firstLine();},
-    lastLine: function() {return this.editor.lastLine();},
-    nextLine: function(line) {return this.editor.nextLine(line);},
-    prevLine: function(line) {return this.editor.prevLine(line);},
-    lineContent: function(line) {return this.editor.lineContent(line);},
-    setLineContent: function(line, content) {this.editor.setLineContent(line, content);},
-    insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);},
-    selectLines: function(startLine, startOffset, endLine, endOffset) {
-      this.win.focus();
-      this.editor.selectLines(startLine, startOffset, endLine, endOffset);
-    },
-    nthLine: function(n) {
-      var line = this.firstLine();
-      for (; n > 1 && line !== false; n--)
-        line = this.nextLine(line);
-      return line;
-    },
-    lineNumber: function(line) {
-      var num = 0;
-      while (line !== false) {
-        num++;
-        line = this.prevLine(line);
-      }
-      return num;
-    },
-
-    // Old number-based line interface
-    jumpToLine: function(n) {
-      this.selectLines(this.nthLine(n), 0);
-      this.win.focus();
-    },
-    currentLine: function() {
-      return this.lineNumber(this.cursorPosition().line);
-    }
-  };
-
-  CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}};
-
-  CodeMirror.replace = function(element) {
-    if (typeof element == "string")
-      element = document.getElementById(element);
-    return function(newElement) {
-      element.parentNode.replaceChild(newElement, element);
-    };
-  };
-
-  CodeMirror.fromTextArea = function(area, options) {
-    if (typeof area == "string")
-      area = document.getElementById(area);
-
-    options = options || {};
-    if (area.style.width && options.width == null)
-      options.width = area.style.width;
-    if (area.style.height && options.height == null)
-      options.height = area.style.height;
-    if (options.content == null) options.content = area.value;
-
-    if (area.form) {
-      function updateField() {
-        area.value = mirror.getCode();
-      }
-      if (typeof area.form.addEventListener == "function")
-        area.form.addEventListener("submit", updateField, false);
-      else
-        area.form.attachEvent("onsubmit", updateField);
-    }
-
-    function insert(frame) {
-      if (area.nextSibling)
-        area.parentNode.insertBefore(frame, area.nextSibling);
-      else
-        area.parentNode.appendChild(frame);
-    }
-
-    area.style.display = "none";
-    var mirror = new CodeMirror(insert, options);
-    return mirror;
-  };
-
-  CodeMirror.isProbablySupported = function() {
-    // This is rather awful, but can be useful.
-    var match;
-    if (window.opera)
-      return Number(window.opera.version()) >= 9.52;
-    else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
-      return Number(match[1]) >= 3;
-    else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/)))
-      return Number(match[1]) >= 6;
-    else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i))
-      return Number(match[1]) >= 20050901;
-    else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/))
-      return Number(match[1]) >= 525;
-    else
-      return null;
-  };
-
-  return CodeMirror;
-})();
diff --git a/project/static/js/lib/codemirror/editor.js b/project/static/js/lib/codemirror/editor.js
deleted file mode 100644 (file)
index 3d7a205..0000000
+++ /dev/null
@@ -1,1314 +0,0 @@
-/* The Editor object manages the content of the editable frame. It
- * catches events, colours nodes, and indents lines. This file also
- * holds some functions for transforming arbitrary DOM structures into
- * plain sequences of <span> and <br> elements
- */
-
-// Make sure a string does not contain two consecutive 'collapseable'
-// whitespace characters.
-function makeWhiteSpace(n) {
-  var buffer = [], nb = true;
-  for (; n > 0; n--) {
-    buffer.push((nb || n == 1) ? nbsp : " ");
-    nb = !nb;
-  }
-  return buffer.join("");
-}
-
-// Create a set of white-space characters that will not be collapsed
-// by the browser, but will not break text-wrapping either.
-function fixSpaces(string) {
-  if (string.charAt(0) == " ") string = nbsp + string.slice(1);
-  return string.replace(/\t/g, function(){return makeWhiteSpace(indentUnit);})
-    .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);});
-}
-
-function cleanText(text) {
-  return text.replace(/\u00a0/g, " ").replace(/\u200b/g, "");
-}
-
-// Create a SPAN node with the expected properties for document part
-// spans.
-function makePartSpan(value, doc) {
-  var text = value;
-  if (value.nodeType == 3) text = value.nodeValue;
-  else value = doc.createTextNode(text);
-
-  var span = doc.createElement("SPAN");
-  span.isPart = true;
-  span.appendChild(value);
-  span.currentText = text;
-  return span;
-}
-
-// On webkit, when the last BR of the document does not have text
-// behind it, the cursor can not be put on the line after it. This
-// makes pressing enter at the end of the document occasionally do
-// nothing (or at least seem to do nothing). To work around it, this
-// function makes sure the document ends with a span containing a
-// zero-width space character. The traverseDOM iterator filters such
-// character out again, so that the parsers won't see them. This
-// function is called from a few strategic places to make sure the
-// zwsp is restored after the highlighting process eats it.
-var webkitLastLineHack = webkit ?
-  function(container) {
-    var last = container.lastChild;
-    if (!last || !last.isPart || last.textContent != "\u200b")
-      container.appendChild(makePartSpan("\u200b", container.ownerDocument));
-  } : function() {};
-
-var Editor = (function(){
-  // The HTML elements whose content should be suffixed by a newline
-  // when converting them to flat text.
-  var newlineElements = {"P": true, "DIV": true, "LI": true};
-
-  function asEditorLines(string) {
-    var tab = makeWhiteSpace(indentUnit);
-    return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces);
-  }
-
-  // Helper function for traverseDOM. Flattens an arbitrary DOM node
-  // into an array of textnodes and <br> tags.
-  function simplifyDOM(root, atEnd) {
-    var doc = root.ownerDocument;
-    var result = [];
-    var leaving = true;
-
-    function simplifyNode(node, top) {
-      if (node.nodeType == 3) {
-        var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/[\r\u200b]/g, "").replace(/\n/g, " "));
-        if (text.length) leaving = false;
-        result.push(node);
-      }
-      else if (isBR(node) && node.childNodes.length == 0) {
-        leaving = true;
-        result.push(node);
-      }
-      else {
-        forEach(node.childNodes, simplifyNode);
-        if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
-          leaving = true;
-          if (!atEnd || !top)
-            result.push(doc.createElement("BR"));
-        }
-      }
-    }
-
-    simplifyNode(root, true);
-    return result;
-  }
-
-  // Creates a MochiKit-style iterator that goes over a series of DOM
-  // nodes. The values it yields are strings, the textual content of
-  // the nodes. It makes sure that all nodes up to and including the
-  // one whose text is being yielded have been 'normalized' to be just
-  // <span> and <br> elements.
-  // See the story.html file for some short remarks about the use of
-  // continuation-passing style in this iterator.
-  function traverseDOM(start){
-    function yield(value, c){cc = c; return value;}
-    function push(fun, arg, c){return function(){return fun(arg, c);};}
-    function stop(){cc = stop; throw StopIteration;};
-    var cc = push(scanNode, start, stop);
-    var owner = start.ownerDocument;
-    var nodeQueue = [];
-
-    // Create a function that can be used to insert nodes after the
-    // one given as argument.
-    function pointAt(node){
-      var parent = node.parentNode;
-      var next = node.nextSibling;
-      return function(newnode) {
-        parent.insertBefore(newnode, next);
-      };
-    }
-    var point = null;
-
-    // Insert a normalized node at the current point. If it is a text
-    // node, wrap it in a <span>, and give that span a currentText
-    // property -- this is used to cache the nodeValue, because
-    // directly accessing nodeValue is horribly slow on some browsers.
-    // The dirty property is used by the highlighter to determine
-    // which parts of the document have to be re-highlighted.
-    function insertPart(part){
-      var text = "\n";
-      if (part.nodeType == 3) {
-        select.snapshotChanged();
-        part = makePartSpan(part, owner);
-        text = part.currentText;
-      }
-      part.dirty = true;
-      nodeQueue.push(part);
-      point(part);
-      return text;
-    }
-
-    // Extract the text and newlines from a DOM node, insert them into
-    // the document, and yield the textual content. Used to replace
-    // non-normalized nodes.
-    function writeNode(node, c, end) {
-      var toYield = [];
-      forEach(simplifyDOM(node, end), function(part) {
-        toYield.push(insertPart(part));
-      });
-      return yield(toYield.join(""), c);
-    }
-
-    // Check whether a node is a normalized <span> element.
-    function partNode(node){
-      if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
-        node.currentText = node.firstChild.nodeValue;
-        return !/[\n\t\r]/.test(node.currentText);
-      }
-      return false;
-    }
-
-    // Handle a node. Add its successor to the continuation if there
-    // is one, find out whether the node is normalized. If it is,
-    // yield its content, otherwise, normalize it (writeNode will take
-    // care of yielding).
-    function scanNode(node, c){
-      if (node.nextSibling)
-        c = push(scanNode, node.nextSibling, c);
-
-      if (partNode(node)){
-        nodeQueue.push(node);
-        return yield(node.currentText, c);
-      }
-      else if (isBR(node)) {
-        nodeQueue.push(node);
-        return yield("\n", c);
-      }
-      else {
-        var end = !node.nextSibling;
-        point = pointAt(node);
-        removeElement(node);
-        return writeNode(node, c, end);
-      }
-    }
-
-    // MochiKit iterators are objects with a next function that
-    // returns the next value or throws StopIteration when there are
-    // no more values.
-    return {next: function(){return cc();}, nodes: nodeQueue};
-  }
-
-  // Determine the text size of a processed node.
-  function nodeSize(node) {
-    return isBR(node) ? 1 : node.currentText.length;
-  }
-
-  // Search backwards through the top-level nodes until the next BR or
-  // the start of the frame.
-  function startOfLine(node) {
-    while (node && !isBR(node)) node = node.previousSibling;
-    return node;
-  }
-  function endOfLine(node, container) {
-    if (!node) node = container.firstChild;
-    else if (isBR(node)) node = node.nextSibling;
-
-    while (node && !isBR(node)) node = node.nextSibling;
-    return node;
-  }
-
-  function time() {return new Date().getTime();}
-
-  // Client interface for searching the content of the editor. Create
-  // these by calling CodeMirror.getSearchCursor. To use, call
-  // findNext on the resulting object -- this returns a boolean
-  // indicating whether anything was found, and can be called again to
-  // skip to the next find. Use the select and replace methods to
-  // actually do something with the found locations.
-  function SearchCursor(editor, string, fromCursor) {
-    this.editor = editor;
-    this.history = editor.history;
-    this.history.commit();
-
-    // Are we currently at an occurrence of the search string?
-    this.atOccurrence = false;
-    // The object stores a set of nodes coming after its current
-    // position, so that when the current point is taken out of the
-    // DOM tree, we can still try to continue.
-    this.fallbackSize = 15;
-    var cursor;
-    // Start from the cursor when specified and a cursor can be found.
-    if (fromCursor && (cursor = select.cursorPos(this.editor.container))) {
-      this.line = cursor.node;
-      this.offset = cursor.offset;
-    }
-    else {
-      this.line = null;
-      this.offset = 0;
-    }
-    this.valid = !!string;
-
-    // Create a matcher function based on the kind of string we have.
-    var target = string.split("\n"), self = this;
-    this.matches = (target.length == 1) ?
-      // For one-line strings, searching can be done simply by calling
-      // indexOf on the current line.
-      function() {
-        var match = cleanText(self.history.textAfter(self.line).slice(self.offset)).indexOf(string);
-        if (match > -1)
-          return {from: {node: self.line, offset: self.offset + match},
-                  to: {node: self.line, offset: self.offset + match + string.length}};
-      } :
-      // Multi-line strings require internal iteration over lines, and
-      // some clunky checks to make sure the first match ends at the
-      // end of the line and the last match starts at the start.
-      function() {
-        var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset));
-        var match = firstLine.lastIndexOf(target[0]);
-        if (match == -1 || match != firstLine.length - target[0].length)
-          return false;
-        var startOffset = self.offset + match;
-
-        var line = self.history.nodeAfter(self.line);
-        for (var i = 1; i < target.length - 1; i++) {
-          if (cleanText(self.history.textAfter(line)) != target[i])
-            return false;
-          line = self.history.nodeAfter(line);
-        }
-
-        if (cleanText(self.history.textAfter(line)).indexOf(target[target.length - 1]) != 0)
-          return false;
-
-        return {from: {node: self.line, offset: startOffset},
-                to: {node: line, offset: target[target.length - 1].length}};
-      };
-  }
-
-  SearchCursor.prototype = {
-    findNext: function() {
-      if (!this.valid) return false;
-      this.atOccurrence = false;
-      var self = this;
-
-      // Go back to the start of the document if the current line is
-      // no longer in the DOM tree.
-      if (this.line && !this.line.parentNode) {
-        this.line = null;
-        this.offset = 0;
-      }
-
-      // Set the cursor's position one character after the given
-      // position.
-      function saveAfter(pos) {
-        if (self.history.textAfter(pos.node).length > pos.offset) {
-          self.line = pos.node;
-          self.offset = pos.offset + 1;
-        }
-        else {
-          self.line = self.history.nodeAfter(pos.node);
-          self.offset = 0;
-        }
-      }
-
-      while (true) {
-        var match = this.matches();
-        // Found the search string.
-        if (match) {
-          this.atOccurrence = match;
-          saveAfter(match.from);
-          return true;
-        }
-        this.line = this.history.nodeAfter(this.line);
-        this.offset = 0;
-        // End of document.
-        if (!this.line) {
-          this.valid = false;
-          return false;
-        }
-      }
-    },
-
-    select: function() {
-      if (this.atOccurrence) {
-        select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to);
-        select.scrollToCursor(this.editor.container);
-      }
-    },
-
-    replace: function(string) {
-      if (this.atOccurrence) {
-        var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string);
-        this.line = end.node;
-        this.offset = end.offset;
-        this.atOccurrence = false;
-      }
-    }
-  };
-
-  // The Editor object is the main inside-the-iframe interface.
-  function Editor(options) {
-    this.options = options;
-    window.indentUnit = options.indentUnit;
-    this.parent = parent;
-    this.doc = document;
-    var container = this.container = this.doc.body;
-    this.win = window;
-    this.history = new History(container, options.undoDepth, options.undoDelay,
-                               this, options.onChange);
-    var self = this;
-
-    if (!Editor.Parser)
-      throw "No parser loaded.";
-    if (options.parserConfig && Editor.Parser.configure)
-      Editor.Parser.configure(options.parserConfig);
-
-    if (!options.readOnly)
-      select.setCursorPos(container, {node: null, offset: 0});
-
-    this.dirty = [];
-    if (options.content)
-      this.importCode(options.content);
-
-    if (!options.readOnly) {
-      if (options.continuousScanning !== false) {
-        this.scanner = this.documentScanner(options.passTime);
-        this.delayScanning();
-      }
-
-      function setEditable() {
-        // In IE, designMode frames can not run any scripts, so we use
-        // contentEditable instead.
-        if (document.body.contentEditable != undefined && internetExplorer)
-          document.body.contentEditable = "true";
-        else
-          document.designMode = "on";
-
-        document.documentElement.style.borderWidth = "0";
-        if (!options.textWrapping)
-          container.style.whiteSpace = "nowrap";
-      }
-
-      // If setting the frame editable fails, try again when the user
-      // focus it (happens when the frame is not visible on
-      // initialisation, in Firefox).
-      try {
-        setEditable();
-      }
-      catch(e) {
-        var focusEvent = addEventHandler(document, "focus", function() {
-          focusEvent();
-          setEditable();
-        }, true);
-      }
-
-      addEventHandler(document, "keydown", method(this, "keyDown"));
-      addEventHandler(document, "keypress", method(this, "keyPress"));
-      addEventHandler(document, "keyup", method(this, "keyUp"));
-
-      function cursorActivity() {self.cursorActivity(false);}
-      addEventHandler(document.body, "mouseup", cursorActivity);
-      addEventHandler(document.body, "cut", cursorActivity);
-
-      addEventHandler(document.body, "paste", function(event) {
-        cursorActivity();
-        var text = null;
-        try {
-          var clipboardData = event.clipboardData || window.clipboardData;
-          if (clipboardData) text = clipboardData.getData('Text');
-        }
-        catch(e) {}
-        if (text !== null) {
-          self.replaceSelection(text);
-          event.stop();
-        }
-      });
-
-      addEventHandler(document.body, "beforepaste", method(this, "reroutePasteEvent"));
-
-      if (this.options.autoMatchParens)
-        addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
-    }
-    else if (!options.textWrapping) {
-      container.style.whiteSpace = "nowrap";
-    }
-  }
-
-  function isSafeKey(code) {
-    return (code >= 16 && code <= 18) || // shift, control, alt
-           (code >= 33 && code <= 40); // arrows, home, end
-  }
-
-  Editor.prototype = {
-    // Import a piece of code into the editor.
-    importCode: function(code) {
-      this.history.push(null, null, asEditorLines(code));
-      this.history.reset();
-    },
-
-    // Extract the code from the editor.
-    getCode: function() {
-      if (!this.container.firstChild)
-        return "";
-
-      var accum = [];
-      select.markSelection(this.win);
-      forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
-      webkitLastLineHack(this.container);
-      select.selectMarked();
-      return cleanText(accum.join(""));
-    },
-
-    checkLine: function(node) {
-      if (node === false || !(node == null || node.parentNode == this.container))
-        throw parent.CodeMirror.InvalidLineHandle;
-    },
-
-    cursorPosition: function(start) {
-      if (start == null) start = true;
-      var pos = select.cursorPos(this.container, start);
-      if (pos) return {line: pos.node, character: pos.offset};
-      else return {line: null, character: 0};
-    },
-
-    firstLine: function() {
-      return null;
-    },
-
-    lastLine: function() {
-      if (this.container.lastChild) return startOfLine(this.container.lastChild);
-      else return null;
-    },
-
-    nextLine: function(line) {
-      this.checkLine(line);
-      var end = endOfLine(line, this.container);
-      return end || false;
-    },
-
-    prevLine: function(line) {
-      this.checkLine(line);
-      if (line == null) return false;
-      return startOfLine(line.previousSibling);
-    },
-
-    selectLines: function(startLine, startOffset, endLine, endOffset) {
-      this.checkLine(startLine);
-      var start = {node: startLine, offset: startOffset}, end = null;
-      if (endOffset !== undefined) {
-        this.checkLine(endLine);
-        end = {node: endLine, offset: endOffset};
-      }
-      select.setCursorPos(this.container, start, end);
-      select.scrollToCursor(this.container);
-    },
-
-    lineContent: function(line) {
-      this.checkLine(line);
-      var accum = [];
-      for (line = line ? line.nextSibling : this.container.firstChild;
-           line && !isBR(line); line = line.nextSibling)
-        accum.push(nodeText(line));
-      return cleanText(accum.join(""));
-    },
-
-    setLineContent: function(line, content) {
-      this.history.commit();
-      this.replaceRange({node: line, offset: 0},
-                        {node: line, offset: this.history.textAfter(line).length},
-                        content);
-      this.addDirtyNode(line);
-      this.scheduleHighlight();
-    },
-
-    insertIntoLine: function(line, position, content) {
-      var before = null;
-      if (position == "end") {
-        before = endOfLine(line, this.container);
-      }
-      else {
-        for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) {
-          if (position == 0) {
-            before = cur;
-            break;
-          }
-          var text = nodeText(cur);
-          if (text.length > position) {
-            before = cur.nextSibling;
-            content = text.slice(0, position) + content + text.slice(position);
-            removeElement(cur);
-            break;
-          }
-          position -= text.length;
-        }
-      }
-
-      var lines = asEditorLines(content), doc = this.container.ownerDocument;
-      for (var i = 0; i < lines.length; i++) {
-        if (i > 0) this.container.insertBefore(doc.createElement("BR"), before);
-        this.container.insertBefore(makePartSpan(lines[i], doc), before);
-      }
-      this.addDirtyNode(line);
-      this.scheduleHighlight();
-    },
-
-    // Retrieve the selected text.
-    selectedText: function() {
-      var h = this.history;
-      h.commit();
-
-      var start = select.cursorPos(this.container, true),
-          end = select.cursorPos(this.container, false);
-      if (!start || !end) return "";
-
-      if (start.node == end.node)
-        return h.textAfter(start.node).slice(start.offset, end.offset);
-
-      var text = [h.textAfter(start.node).slice(start.offset)];
-      for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos))
-        text.push(h.textAfter(pos));
-      text.push(h.textAfter(end.node).slice(0, end.offset));
-      return cleanText(text.join("\n"));
-    },
-
-    // Replace the selection with another piece of text.
-    replaceSelection: function(text) {
-      this.history.commit();
-
-      var start = select.cursorPos(this.container, true),
-          end = select.cursorPos(this.container, false);
-      if (!start || !end) return;
-
-      end = this.replaceRange(start, end, text);
-      select.setCursorPos(this.container, end);
-      webkitLastLineHack(this.container);
-    },
-
-    reroutePasteEvent: function() {
-      if (this.capturingPaste || window.opera) return;
-      this.capturingPaste = true;
-      var te = parent.document.createElement("TEXTAREA");
-      te.style.position = "absolute";
-      te.style.left = "-500px";
-      te.style.width = "10px";
-      te.style.top = nodeTop(frameElement) + "px";
-      parent.document.body.appendChild(te);
-      parent.focus();
-      te.focus();
-
-      var self = this;
-      this.parent.setTimeout(function() {
-        self.capturingPaste = false;
-        self.win.focus();
-        if (self.selectionSnapshot) // IE hack
-          self.win.select.selectCoords(self.win, self.selectionSnapshot);
-        var text = te.value;
-        if (text) self.replaceSelection(text);
-        removeElement(te);
-      }, 10);
-    },
-
-    replaceRange: function(from, to, text) {
-      var lines = asEditorLines(text);
-      lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0];
-      var lastLine = lines[lines.length - 1];
-      lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset);
-      var end = this.history.nodeAfter(to.node);
-      this.history.push(from.node, end, lines);
-      return {node: this.history.nodeBefore(end),
-              offset: lastLine.length};
-    },
-
-    getSearchCursor: function(string, fromCursor) {
-      return new SearchCursor(this, string, fromCursor);
-    },
-
-    // Re-indent the whole buffer
-    reindent: function() {
-      if (this.container.firstChild)
-        this.indentRegion(null, this.container.lastChild);
-    },
-
-    reindentSelection: function(direction) {
-      if (!select.somethingSelected(this.win)) {
-        this.indentAtCursor(direction);
-      }
-      else {
-        var start = select.selectionTopNode(this.container, true),
-            end = select.selectionTopNode(this.container, false);
-        if (start === false || end === false) return;
-        this.indentRegion(start, end, direction);
-      }
-    },
-
-    grabKeys: function(eventHandler, filter) {
-      this.frozen = eventHandler;
-      this.keyFilter = filter;
-    },
-    ungrabKeys: function() {
-      this.frozen = "leave";
-      this.keyFilter = null;
-    },
-
-    setParser: function(name) {
-      Editor.Parser = window[name];
-      if (this.container.firstChild) {
-        forEach(this.container.childNodes, function(n) {
-          if (n.nodeType != 3) n.dirty = true;
-        });
-        this.addDirtyNode(this.firstChild);
-        this.scheduleHighlight();
-      }
-    },
-
-    // Intercept enter and tab, and assign their new functions.
-    keyDown: function(event) {
-      if (this.frozen == "leave") this.frozen = null;
-      if (this.frozen && (!this.keyFilter || this.keyFilter(event))) {
-        event.stop();
-        this.frozen(event);
-        return;
-      }
-
-      var code = event.keyCode;
-      // Don't scan when the user is typing.
-      this.delayScanning();
-      // Schedule a paren-highlight event, if configured.
-      if (this.options.autoMatchParens)
-        this.scheduleParenBlink();
-
-      // The various checks for !altKey are there because AltGr sets both
-      // ctrlKey and altKey to true, and should not be recognised as
-      // Control.
-      if (code == 13) { // enter
-        if (event.ctrlKey && !event.altKey) {
-          this.reparseBuffer();
-        }
-        else {
-          select.insertNewlineAtCursor(this.win);
-          this.indentAtCursor();
-          select.scrollToCursor(this.container);
-        }
-        event.stop();
-      }
-      else if (code == 9 && this.options.tabMode != "default") { // tab
-        this.handleTab(!event.ctrlKey && !event.shiftKey);
-        event.stop();
-      }
-      else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space
-        this.handleTab(true);
-        event.stop();
-      }
-      else if (code == 36 && !event.shiftKey && !event.ctrlKey) { // home
-        if (this.home())
-          event.stop();
-      }
-      else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ]
-        this.blinkParens(event.shiftKey);
-        event.stop();
-      }
-      else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right
-        var cursor = select.selectionTopNode(this.container);
-        if (cursor === false || !this.container.firstChild) return;
-
-        if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container);
-        else {
-          var end = endOfLine(cursor, this.container);
-          select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container);
-        }
-        event.stop();
-      }
-      else if ((event.ctrlKey || event.metaKey) && !event.altKey) {
-        if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y
-          select.scrollToNode(this.history.redo());
-          event.stop();
-        }
-        else if (code == 90 || (safari && code == 8)) { // Z, backspace
-          select.scrollToNode(this.history.undo());
-          event.stop();
-        }
-        else if (code == 83 && this.options.saveFunction) { // S
-          this.options.saveFunction();
-          event.stop();
-        }
-      }
-    },
-
-    // Check for characters that should re-indent the current line,
-    // and prevent Opera from handling enter and tab anyway.
-    keyPress: function(event) {
-      var electric = Editor.Parser.electricChars, self = this;
-      // Hack for Opera, and Firefox on OS X, in which stopping a
-      // keydown event does not prevent the associated keypress event
-      // from happening, so we have to cancel enter and tab again
-      // here.
-      if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) ||
-          event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
-          (event.keyCode == 32 && event.shiftKey && this.options.tabMode == "default"))
-        event.stop();
-      else if (electric && electric.indexOf(event.character) != -1)
-        this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
-      else if ((event.character == "v" || event.character == "V")
-               && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V
-        this.reroutePasteEvent();
-    },
-
-    // Mark the node at the cursor dirty when a non-safe key is
-    // released.
-    keyUp: function(event) {
-      this.cursorActivity(isSafeKey(event.keyCode));
-    },
-
-    // Indent the line following a given <br>, or null for the first
-    // line. If given a <br> element, this must have been highlighted
-    // so that it has an indentation method. Returns the whitespace
-    // element that has been modified or created (if any).
-    indentLineAfter: function(start, direction) {
-      // whiteSpace is the whitespace span at the start of the line,
-      // or null if there is no such node.
-      var whiteSpace = start ? start.nextSibling : this.container.firstChild;
-      if (whiteSpace && !hasClass(whiteSpace, "whitespace"))
-        whiteSpace = null;
-
-      // Sometimes the start of the line can influence the correct
-      // indentation, so we retrieve it.
-      var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
-      var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
-
-      // Ask the lexical context for the correct indentation, and
-      // compute how much this differs from the current indentation.
-      var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0;
-      if (direction != null && this.options.tabMode == "shift")
-        newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit)
-      else if (start)
-        newIndent = start.indentation(nextChars, curIndent, direction);
-      else if (Editor.Parser.firstIndentation)
-        newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction);
-      var indentDiff = newIndent - curIndent;
-
-      // If there is too much, this is just a matter of shrinking a span.
-      if (indentDiff < 0) {
-        if (newIndent == 0) {
-          if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0);
-          removeElement(whiteSpace);
-          whiteSpace = null;
-        }
-        else {
-          select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
-          whiteSpace.currentText = makeWhiteSpace(newIndent);
-          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
-        }
-      }
-      // Not enough...
-      else if (indentDiff > 0) {
-        // If there is whitespace, we grow it.
-        if (whiteSpace) {
-          whiteSpace.currentText = makeWhiteSpace(newIndent);
-          whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
-        }
-        // Otherwise, we have to add a new whitespace node.
-        else {
-          whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc);
-          whiteSpace.className = "whitespace";
-          if (start) insertAfter(whiteSpace, start);
-          else this.container.insertBefore(whiteSpace, this.container.firstChild);
-        }
-        if (firstText) select.snapshotMove(firstText.firstChild, whiteSpace.firstChild, curIndent, false, true);
-      }
-      if (indentDiff != 0) this.addDirtyNode(start);
-      return whiteSpace;
-    },
-
-    // Re-highlight the selected part of the document.
-    highlightAtCursor: function() {
-      var pos = select.selectionTopNode(this.container, true);
-      var to = select.selectionTopNode(this.container, false);
-      if (pos === false || to === false) return;
-
-      select.markSelection(this.win);
-      if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false)
-        return false;
-      select.selectMarked();
-      return true;
-    },
-
-    // When tab is pressed with text selected, the whole selection is
-    // re-indented, when nothing is selected, the line with the cursor
-    // is re-indented.
-    handleTab: function(direction) {
-      if (this.options.tabMode == "spaces")
-        select.insertTabAtCursor(this.win);
-      else
-        this.reindentSelection(direction);
-    },
-
-    home: function() {
-      var cur = select.selectionTopNode(this.container, true), start = cur;
-      if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild)
-        return false;
-
-      while (cur && !isBR(cur)) cur = cur.previousSibling;
-      var next = cur ? cur.nextSibling : this.container.firstChild;
-      if (next && next != start && next.isPart && hasClass(next, "whitespace"))
-        select.focusAfterNode(next, this.container);
-      else
-        select.focusAfterNode(cur, this.container);
-
-      select.scrollToCursor(this.container);
-      return true;
-    },
-
-    // Delay (or initiate) the next paren blink event.
-    scheduleParenBlink: function() {
-      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
-      var self = this;
-      this.parenEvent = this.parent.setTimeout(function(){self.blinkParens();}, 300);
-    },
-
-    // Take the token before the cursor. If it contains a character in
-    // '()[]{}', search for the matching paren/brace/bracket, and
-    // highlight them in green for a moment, or red if no proper match
-    // was found.
-    blinkParens: function(jump) {
-      if (!window.select) return;
-      // Clear the event property.
-      if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
-      this.parenEvent = null;
-
-      // Extract a 'paren' from a piece of text.
-      function paren(node) {
-        if (node.currentText) {
-          var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/);
-          return match && match[1];
-        }
-      }
-      // Determine the direction a paren is facing.
-      function forward(ch) {
-        return /[\(\[\{]/.test(ch);
-      }
-
-      var ch, self = this, cursor = select.selectionTopNode(this.container, true);
-      if (!cursor || !this.highlightAtCursor()) return;
-      cursor = select.selectionTopNode(this.container, true);
-      if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor)))))
-        return;
-      // We only look for tokens with the same className.
-      var className = cursor.className, dir = forward(ch), match = matching[ch];
-
-      // Since parts of the document might not have been properly
-      // highlighted, and it is hard to know in advance which part we
-      // have to scan, we just try, and when we find dirty nodes we
-      // abort, parse them, and re-try.
-      function tryFindMatch() {
-        var stack = [], ch, ok = true;;
-        for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
-          if (runner.className == className && isSpan(runner) && (ch = paren(runner))) {
-            if (forward(ch) == dir)
-              stack.push(ch);
-            else if (!stack.length)
-              ok = false;
-            else if (stack.pop() != matching[ch])
-              ok = false;
-            if (!stack.length) break;
-          }
-          else if (runner.dirty || !isSpan(runner) && !isBR(runner)) {
-            return {node: runner, status: "dirty"};
-          }
-        }
-        return {node: runner, status: runner && ok};
-      }
-      // Temporarily give the relevant nodes a colour.
-      function blink(node, ok) {
-        node.style.fontWeight = "bold";
-        node.style.color = ok ? "#8F8" : "#F88";
-        self.parent.setTimeout(function() {node.style.fontWeight = ""; node.style.color = "";}, 500);
-      }
-
-      while (true) {
-        var found = tryFindMatch();
-        if (found.status == "dirty") {
-          this.highlight(found.node, endOfLine(found.node));
-          // Needed because in some corner cases a highlight does not
-          // reach a node.
-          found.node.dirty = false;
-          continue;
-        }
-        else {
-          blink(cursor, found.status);
-          if (found.node) {
-            blink(found.node, found.status);
-            if (jump) select.focusAfterNode(found.node.previousSibling, this.container);
-          }
-          break;
-        }
-      }
-    },
-
-    // Adjust the amount of whitespace at the start of the line that
-    // the cursor is on so that it is indented properly.
-    indentAtCursor: function(direction) {
-      if (!this.container.firstChild) return;
-      // The line has to have up-to-date lexical information, so we
-      // highlight it first.
-      if (!this.highlightAtCursor()) return;
-      var cursor = select.selectionTopNode(this.container, false);
-      // If we couldn't determine the place of the cursor,
-      // there's nothing to indent.
-      if (cursor === false)
-        return;
-      var lineStart = startOfLine(cursor);
-      var whiteSpace = this.indentLineAfter(lineStart, direction);
-      if (cursor == lineStart && whiteSpace)
-          cursor = whiteSpace;
-      // This means the indentation has probably messed up the cursor.
-      if (cursor == whiteSpace)
-        select.focusAfterNode(cursor, this.container);
-    },
-
-    // Indent all lines whose start falls inside of the current
-    // selection.
-    indentRegion: function(start, end, direction) {
-      var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling);
-      if (!isBR(end)) end = endOfLine(end, this.container);
-
-      do {
-        var next = endOfLine(current, this.container);
-        if (current) this.highlight(before, next, true);
-        this.indentLineAfter(current, direction);
-        before = current;
-        current = next;
-      } while (current != end);
-      select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0});
-    },
-
-    // Find the node that the cursor is in, mark it as dirty, and make
-    // sure a highlight pass is scheduled.
-    cursorActivity: function(safe) {
-      if (internetExplorer) {
-        this.container.createTextRange().execCommand("unlink");
-        this.selectionSnapshot = select.selectionCoords(this.win);
-      }
-
-      var activity = this.options.cursorActivity;
-      if (!safe || activity) {
-        var cursor = select.selectionTopNode(this.container, false);
-        if (cursor === false || !this.container.firstChild) return;
-        cursor = cursor || this.container.firstChild;
-        if (activity) activity(cursor);
-        if (!safe) {
-          this.scheduleHighlight();
-          this.addDirtyNode(cursor);
-        }
-      }
-    },
-
-    reparseBuffer: function() {
-      forEach(this.container.childNodes, function(node) {node.dirty = true;});
-      if (this.container.firstChild)
-        this.addDirtyNode(this.container.firstChild);
-    },
-
-    // Add a node to the set of dirty nodes, if it isn't already in
-    // there.
-    addDirtyNode: function(node) {
-      node = node || this.container.firstChild;
-      if (!node) return;
-
-      for (var i = 0; i < this.dirty.length; i++)
-        if (this.dirty[i] == node) return;
-
-      if (node.nodeType != 3)
-        node.dirty = true;
-      this.dirty.push(node);
-    },
-
-    // Cause a highlight pass to happen in options.passDelay
-    // milliseconds. Clear the existing timeout, if one exists. This
-    // way, the passes do not happen while the user is typing, and
-    // should as unobtrusive as possible.
-    scheduleHighlight: function() {
-      // Timeouts are routed through the parent window, because on
-      // some browsers designMode windows do not fire timeouts.
-      var self = this;
-      this.parent.clearTimeout(this.highlightTimeout);
-      this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay);
-    },
-
-    // Fetch one dirty node, and remove it from the dirty set.
-    getDirtyNode: function() {
-      while (this.dirty.length > 0) {
-        var found = this.dirty.pop();
-        // IE8 sometimes throws an unexplainable 'invalid argument'
-        // exception for found.parentNode
-        try {
-          // If the node has been coloured in the meantime, or is no
-          // longer in the document, it should not be returned.
-          while (found && found.parentNode != this.container)
-            found = found.parentNode
-          if (found && (found.dirty || found.nodeType == 3))
-            return found;
-        } catch (e) {}
-      }
-      return null;
-    },
-
-    // Pick dirty nodes, and highlight them, until options.passTime
-    // milliseconds have gone by. The highlight method will continue
-    // to next lines as long as it finds dirty nodes. It returns
-    // information about the place where it stopped. If there are
-    // dirty nodes left after this function has spent all its lines,
-    // it shedules another highlight to finish the job.
-    highlightDirty: function(force) {
-      // Prevent FF from raising an error when it is firing timeouts
-      // on a page that's no longer loaded.
-      if (!window.select) return;
-
-      if (!this.options.readOnly) select.markSelection(this.win);
-      var start, endTime = force ? null : time() + this.options.passTime;
-      while ((time() < endTime || force) && (start = this.getDirtyNode())) {
-        var result = this.highlight(start, endTime);
-        if (result && result.node && result.dirty)
-          this.addDirtyNode(result.node);
-      }
-      if (!this.options.readOnly) select.selectMarked();
-      if (start) this.scheduleHighlight();
-      return this.dirty.length == 0;
-    },
-
-    // Creates a function that, when called through a timeout, will
-    // continuously re-parse the document.
-    documentScanner: function(passTime) {
-      var self = this, pos = null;
-      return function() {
-        // FF timeout weirdness workaround.
-        if (!window.select) return;
-        // If the current node is no longer in the document... oh
-        // well, we start over.
-        if (pos && pos.parentNode != self.container)
-          pos = null;
-        select.markSelection(self.win);
-        var result = self.highlight(pos, time() + passTime, true);
-        select.selectMarked();
-        var newPos = result ? (result.node && result.node.nextSibling) : null;
-        pos = (pos == newPos) ? null : newPos;
-        self.delayScanning();
-      };
-    },
-
-    // Starts the continuous scanning process for this document after
-    // a given interval.
-    delayScanning: function() {
-      if (this.scanner) {
-        this.parent.clearTimeout(this.documentScan);
-        this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning);
-      }
-    },
-
-    // The function that does the actual highlighting/colouring (with
-    // help from the parser and the DOM normalizer). Its interface is
-    // rather overcomplicated, because it is used in different
-    // situations: ensuring that a certain line is highlighted, or
-    // highlighting up to X milliseconds starting from a certain
-    // point. The 'from' argument gives the node at which it should
-    // start. If this is null, it will start at the beginning of the
-    // document. When a timestamp is given with the 'target' argument,
-    // it will stop highlighting at that time. If this argument holds
-    // a DOM node, it will highlight until it reaches that node. If at
-    // any time it comes across two 'clean' lines (no dirty nodes), it
-    // will stop, except when 'cleanLines' is true. maxBacktrack is
-    // the maximum number of lines to backtrack to find an existing
-    // parser instance. This is used to give up in situations where a
-    // highlight would take too long and freeze the browser interface.
-    highlight: function(from, target, cleanLines, maxBacktrack){
-      var container = this.container, self = this, active = this.options.activeTokens;
-      var endTime = (typeof target == "number" ? target : null);
-
-      if (!container.firstChild)
-        return;
-      // Backtrack to the first node before from that has a partial
-      // parse stored.
-      while (from && (!from.parserFromHere || from.dirty)) {
-        if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0)
-          return false;
-        from = from.previousSibling;
-      }
-      // If we are at the end of the document, do nothing.
-      if (from && !from.nextSibling)
-        return;
-
-      // Check whether a part (<span> node) and the corresponding token
-      // match.
-      function correctPart(token, part){
-        return !part.reduced && part.currentText == token.value && part.className == token.style;
-      }
-      // Shorten the text associated with a part by chopping off
-      // characters from the front. Note that only the currentText
-      // property gets changed. For efficiency reasons, we leave the
-      // nodeValue alone -- we set the reduced flag to indicate that
-      // this part must be replaced.
-      function shortenPart(part, minus){
-        part.currentText = part.currentText.substring(minus);
-        part.reduced = true;
-      }
-      // Create a part corresponding to a given token.
-      function tokenPart(token){
-        var part = makePartSpan(token.value, self.doc);     
-        part.className = token.style;
-        return part;
-      }
-
-      function maybeTouch(node) {
-        if (node) {
-          var old = node.oldNextSibling;
-          if (lineDirty || old === undefined || node.nextSibling != old)
-            self.history.touch(node);
-          node.oldNextSibling = node.nextSibling;
-        }
-        else {
-          var old = self.container.oldFirstChild;
-          if (lineDirty || old === undefined || self.container.firstChild != old)
-            self.history.touch(null);
-          self.container.oldFirstChild = self.container.firstChild;
-        }
-      }
-
-      // Get the token stream. If from is null, we start with a new
-      // parser from the start of the frame, otherwise a partial parse
-      // is resumed.
-      var traversal = traverseDOM(from ? from.nextSibling : container.firstChild),
-          stream = stringStream(traversal),
-          parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream);
-
-      // parts is an interface to make it possible to 'delay' fetching
-      // the next DOM node until we are completely done with the one
-      // before it. This is necessary because often the next node is
-      // not yet available when we want to proceed past the current
-      // one.
-      var parts = {
-        current: null,
-        // Fetch current node.
-        get: function(){
-          if (!this.current)
-            this.current = traversal.nodes.shift();
-          return this.current;
-        },
-        // Advance to the next part (do not fetch it yet).
-        next: function(){
-          this.current = null;
-        },
-        // Remove the current part from the DOM tree, and move to the
-        // next.
-        remove: function(){
-          container.removeChild(this.get());
-          this.current = null;
-        },
-        // Advance to the next part that is not empty, discarding empty
-        // parts.
-        getNonEmpty: function(){
-          var part = this.get();
-          // Allow empty nodes when they are alone on a line, needed
-          // for the FF cursor bug workaround (see select.js,
-          // insertNewlineAtCursor).
-          while (part && isSpan(part) && part.currentText == "") {
-            var old = part;
-            this.remove();
-            part = this.get();
-            // Adjust selection information, if any. See select.js for details.
-            select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0);
-          }
-          return part;
-        }
-      };
-
-      var lineDirty = false, prevLineDirty = true, lineNodes = 0;
-
-      // This forEach loops over the tokens from the parsed stream, and
-      // at the same time uses the parts object to proceed through the
-      // corresponding DOM nodes.
-      forEach(parsed, function(token){
-        var part = parts.getNonEmpty();
-
-        if (token.value == "\n"){
-          // The idea of the two streams actually staying synchronized
-          // is such a long shot that we explicitly check.
-          if (!isBR(part))
-            throw "Parser out of sync. Expected BR.";
-
-          if (part.dirty || !part.indentation) lineDirty = true;
-          maybeTouch(from);
-          from = part;
-
-          // Every <br> gets a copy of the parser state and a lexical
-          // context assigned to it. The first is used to be able to
-          // later resume parsing from this point, the second is used
-          // for indentation.
-          part.parserFromHere = parsed.copy();
-          part.indentation = token.indentation;
-          part.dirty = false;
-
-          // If the target argument wasn't an integer, go at least
-          // until that node.
-          if (endTime == null && part == target) throw StopIteration;
-
-          // A clean line with more than one node means we are done.
-          // Throwing a StopIteration is the way to break out of a
-          // MochiKit forEach loop.
-          if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines))
-            throw StopIteration;
-          prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0;
-          parts.next();
-        }
-        else {
-          if (!isSpan(part))
-            throw "Parser out of sync. Expected SPAN.";
-          if (part.dirty)
-            lineDirty = true;
-          lineNodes++;
-
-          // If the part matches the token, we can leave it alone.
-          if (correctPart(token, part)){
-            part.dirty = false;
-            parts.next();
-          }
-          // Otherwise, we have to fix it.
-          else {
-            lineDirty = true;
-            // Insert the correct part.
-            var newPart = tokenPart(token);
-            container.insertBefore(newPart, part);
-            if (active) active(newPart, token, self);
-            var tokensize = token.value.length;
-            var offset = 0;
-            // Eat up parts until the text for this token has been
-            // removed, adjusting the stored selection info (see
-            // select.js) in the process.
-            while (tokensize > 0) {
-              part = parts.get();
-              var partsize = part.currentText.length;
-              select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset);
-              if (partsize > tokensize){
-                shortenPart(part, tokensize);
-                tokensize = 0;
-              }
-              else {
-                tokensize -= partsize;
-                offset += partsize;
-                parts.remove();
-              }
-            }
-          }
-        }
-      });
-      maybeTouch(from);
-      webkitLastLineHack(this.container);
-
-      // The function returns some status information that is used by
-      // hightlightDirty to determine whether and where it has to
-      // continue.
-      return {node: parts.getNonEmpty(),
-              dirty: lineDirty};
-    }
-  };
-
-  return Editor;
-})();
-
-addEventHandler(window, "load", function() {
-  var CodeMirror = window.frameElement.CodeMirror;
-  CodeMirror.editor = new Editor(CodeMirror.options);
-  this.parent.setTimeout(method(CodeMirror, "init"), 0);
-});
diff --git a/project/static/js/lib/codemirror/parsexml.js b/project/static/js/lib/codemirror/parsexml.js
deleted file mode 100644 (file)
index 95a8099..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/* This file defines an XML parser, with a few kludges to make it
- * useable for HTML. autoSelfClosers defines a set of tag names that
- * are expected to not have a closing tag, and doNotIndent specifies
- * the tags inside of which no indentation should happen (see Config
- * object). These can be disabled by passing the editor an object like
- * {useHTMLKludges: false} as parserConfig option.
- */
-
-var XMLParser = Editor.Parser = (function() {
-  var Kludges = {
-    autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
-                      "meta": true, "col": true, "frame": true, "base": true, "area": true},
-    doNotIndent: {"pre": true, "!cdata": true}
-  };
-  var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}};
-  var UseKludges = Kludges;
-  var alignCDATA = false;
-
-  // Simple stateful tokenizer for XML documents. Returns a
-  // MochiKit-style iterator, with a state property that contains a
-  // function encapsulating the current state. See tokenize.js.
-  var tokenizeXML = (function() {
-    function inText(source, setState) {
-      var ch = source.next();
-      if (ch == "<") {
-        if (source.equals("!")) {
-          source.next();
-          if (source.equals("[")) {
-            if (source.lookAhead("[CDATA[", true)) {
-              setState(inBlock("xml-cdata", "]]>"));
-              return null;
-            }
-            else {
-              return "xml-text";
-            }
-          }
-          else if (source.lookAhead("--", true)) {
-            setState(inBlock("xml-comment", "-->"));
-            return null;
-          }
-          else {
-            return "xml-text";
-          }
-        }
-        else if (source.equals("?")) {
-          source.next();
-          source.nextWhileMatches(/[\w\._\-]/);
-          setState(inBlock("xml-processing", "?>"));
-          return "xml-processing";
-        }
-        else {
-          if (source.equals("/")) source.next();
-          setState(inTag);
-          return "xml-punctuation";
-        }
-      }
-      else if (ch == "&") {
-        while (!source.endOfLine()) {
-          if (source.next() == ";")
-            break;
-        }
-        return "xml-entity";
-      }
-      else {
-        source.nextWhileMatches(/[^&<\n]/);
-        return "xml-text";
-      }
-    }
-
-    function inTag(source, setState) {
-      var ch = source.next();
-      if (ch == ">") {
-        setState(inText);
-        return "xml-punctuation";
-      }
-      else if (/[?\/]/.test(ch) && source.equals(">")) {
-        source.next();
-        setState(inText);
-        return "xml-punctuation";
-      }
-      else if (ch == "=") {
-        return "xml-punctuation";
-      }
-      else if (/[\'\"]/.test(ch)) {
-        setState(inAttribute(ch));
-        return null;
-      }
-      else {
-        source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/);
-        return "xml-name";
-      }
-    }
-
-    function inAttribute(quote) {
-      return function(source, setState) {
-        while (!source.endOfLine()) {
-          if (source.next() == quote) {
-            setState(inTag);
-            break;
-          }
-        }
-        return "xml-attribute";
-      };
-    }
-
-    function inBlock(style, terminator) {
-      return function(source, setState) {
-        while (!source.endOfLine()) {
-          if (source.lookAhead(terminator, true)) {
-            setState(inText);
-            break;
-          }
-          source.next();
-        }
-        return style;
-      };
-    }
-
-    return function(source, startState) {
-      return tokenizer(source, startState || inText);
-    };
-  })();
-
-  // The parser. The structure of this function largely follows that of
-  // parseJavaScript in parsejavascript.js (there is actually a bit more
-  // shared code than I'd like), but it is quite a bit simpler.
-  function parseXML(source) {
-    var tokens = tokenizeXML(source);
-    var cc = [base];
-    var tokenNr = 0, indented = 0;
-    var currentTag = null, context = null;
-    var consume, marked;
-    
-    function push(fs) {
-      for (var i = fs.length - 1; i >= 0; i--)
-        cc.push(fs[i]);
-    }
-    function cont() {
-      push(arguments);
-      consume = true;
-    }
-    function pass() {
-      push(arguments);
-      consume = false;
-    }
-
-    function mark(style) {
-      marked = style;
-    }
-    function expect(text) {
-      return function(style, content) {
-        if (content == text) cont();
-        else mark("xml-error") || cont(arguments.callee);
-      };
-    }
-
-    function pushContext(tagname, startOfLine) {
-      var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent);
-      context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent};
-    }
-    function popContext() {
-      context = context.prev;
-    }
-    function computeIndentation(baseContext) {
-      return function(nextChars, current) {
-        var context = baseContext;
-        if (context && context.noIndent)
-          return current;
-        if (alignCDATA && /<!\[CDATA\[/.test(nextChars))
-          return 0;
-        if (context && /^<\//.test(nextChars))
-          context = context.prev;
-        while (context && !context.startOfLine)
-          context = context.prev;
-        if (context)
-          return context.indent + indentUnit;
-        else
-          return 0;
-      };
-    }
-
-    function base() {
-      return pass(element, base);
-    }
-    var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true};
-    function element(style, content) {
-      if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1));
-      else if (content == "</") cont(closetagname, expect(">"));
-      else if (style == "xml-cdata") {
-        if (!context || context.name != "!cdata") pushContext("!cdata");
-        if (/\]\]>$/.test(content)) popContext();
-        cont();
-      }
-      else if (harmlessTokens.hasOwnProperty(style)) cont();
-      else mark("xml-error") || cont();
-    }
-    function tagname(style, content) {
-      if (style == "xml-name") {
-        currentTag = content.toLowerCase();
-        mark("xml-tagname");
-        cont();
-      }
-      else {
-        currentTag = null;
-        pass();
-      }
-    }
-    function closetagname(style, content) {
-      if (style == "xml-name" && context && content.toLowerCase() == context.name) {
-        popContext();
-        mark("xml-tagname");
-      }
-      else {
-        mark("xml-error");
-      }
-      cont();
-    }
-    function endtag(startOfLine) {
-      return function(style, content) {
-        if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont();
-        else if (content == ">") pushContext(currentTag, startOfLine) || cont();
-        else mark("xml-error") || cont(arguments.callee);
-      };
-    }
-    function attributes(style) {
-      if (style == "xml-name") mark("xml-attname") || cont(attribute, attributes);
-      else pass();
-    }
-    function attribute(style, content) {
-      if (content == "=") cont(value);
-      else if (content == ">" || content == "/>") pass(endtag);
-      else pass();
-    }
-    function value(style) {
-      if (style == "xml-attribute") cont(value);
-      else pass();
-    }
-
-    return {
-      indentation: function() {return indented;},
-
-      next: function(){
-        var token = tokens.next();
-        if (token.style == "whitespace" && tokenNr == 0)
-          indented = token.value.length;
-        else
-          tokenNr++;
-        if (token.content == "\n") {
-          indented = tokenNr = 0;
-          token.indentation = computeIndentation(context);
-        }
-
-        if (token.style == "whitespace" || token.type == "xml-comment")
-          return token;
-
-        while(true){
-          consume = marked = false;
-          cc.pop()(token.style, token.content);
-          if (consume){
-            if (marked)
-              token.style = marked;
-            return token;
-          }
-        }
-      },
-
-      copy: function(){
-        var _cc = cc.concat([]), _tokenState = tokens.state, _context = context;
-        var parser = this;
-        
-        return function(input){
-          cc = _cc.concat([]);
-          tokenNr = indented = 0;
-          context = _context;
-          tokens = tokenizeXML(input, _tokenState);
-          return parser;
-        };
-      }
-    };
-  }
-
-  return {
-    make: parseXML,
-    electricChars: "/",
-    configure: function(config) {
-      if (config.useHTMLKludges != null)
-        UseKludges = config.useHTMLKludges ? Kludges : NoKludges;
-      if (config.alignCDATA)
-        alignCDATA = config.alignCDATA;
-    }
-  };
-})();
diff --git a/project/static/js/lib/codemirror/select.js b/project/static/js/lib/codemirror/select.js
deleted file mode 100644 (file)
index 7746240..0000000
+++ /dev/null
@@ -1,624 +0,0 @@
-/* Functionality for finding, storing, and restoring selections
- *
- * This does not provide a generic API, just the minimal functionality
- * required by the CodeMirror system.
- */
-
-// Namespace object.
-var select = {};
-
-(function() {
-  select.ie_selection = document.selection && document.selection.createRangeCollection;
-
-  // Find the 'top-level' (defined as 'a direct child of the node
-  // passed as the top argument') node that the given node is
-  // contained in. Return null if the given node is not inside the top
-  // node.
-  function topLevelNodeAt(node, top) {
-    while (node && node.parentNode != top)
-      node = node.parentNode;
-    return node;
-  }
-
-  // Find the top-level node that contains the node before this one.
-  function topLevelNodeBefore(node, top) {
-    while (!node.previousSibling && node.parentNode != top)
-      node = node.parentNode;
-    return topLevelNodeAt(node.previousSibling, top);
-  }
-
-  var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
-
-  select.scrollToNode = function(element) {
-    if (!element) return;
-    var doc = element.ownerDocument, body = doc.body,
-        win = (doc.defaultView || doc.parentWindow),
-        html = doc.documentElement,
-        atEnd = !element.nextSibling || !element.nextSibling.nextSibling
-                || !element.nextSibling.nextSibling.nextSibling;
-    // In Opera (and recent Webkit versions), BR elements *always*
-    // have a scrollTop property of zero.
-    var compensateHack = 0;
-    while (element && !element.offsetTop) {
-      compensateHack++;
-      element = element.previousSibling;
-    }
-    // atEnd is another kludge for these browsers -- if the cursor is
-    // at the end of the document, and the node doesn't have an
-    // offset, just scroll to the end.
-    if (compensateHack == 0) atEnd = false;
-
-    var y = compensateHack * (element ? element.offsetHeight : 0), x = 0, pos = element;
-    while (pos && pos.offsetParent) {
-      y += pos.offsetTop;
-      // Don't count X offset for <br> nodes
-      if (!isBR(pos))
-        x += pos.offsetLeft;
-      pos = pos.offsetParent;
-    }
-
-    var scroll_x = body.scrollLeft || html.scrollLeft || 0,
-        scroll_y = body.scrollTop || html.scrollTop || 0,
-        screen_x = x - scroll_x, screen_y = y - scroll_y, scroll = false;
-
-    if (screen_x < 0 || screen_x > (win.innerWidth || html.clientWidth || 0)) {
-      scroll_x = x;
-      scroll = true;
-    }
-    if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) {
-      scroll_y = atEnd ? 1e10 : y;
-      scroll = true;
-    }
-    if (scroll) win.scrollTo(scroll_x, scroll_y);
-  };
-
-  select.scrollToCursor = function(container) {
-    select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild);
-  };
-
-  // Used to prevent restoring a selection when we do not need to.
-  var currentSelection = null;
-
-  select.snapshotChanged = function() {
-    if (currentSelection) currentSelection.changed = true;
-  };
-
-  // This is called by the code in editor.js whenever it is replacing
-  // a text node. The function sees whether the given oldNode is part
-  // of the current selection, and updates this selection if it is.
-  // Because nodes are often only partially replaced, the length of
-  // the part that gets replaced has to be taken into account -- the
-  // selection might stay in the oldNode if the newNode is smaller
-  // than the selection's offset. The offset argument is needed in
-  // case the selection does move to the new object, and the given
-  // length is not the whole length of the new node (part of it might
-  // have been used to replace another node).
-  select.snapshotReplaceNode = function(from, to, length, offset) {
-    if (!currentSelection) return;
-
-    function replace(point) {
-      if (from == point.node) {
-        currentSelection.changed = true;
-        if (length && point.offset > length) {
-          point.offset -= length;
-        }
-        else {
-          point.node = to;
-          point.offset += (offset || 0);
-        }
-      }
-    }
-    replace(currentSelection.start);
-    replace(currentSelection.end);
-  };
-
-  select.snapshotMove = function(from, to, distance, relative, ifAtStart) {
-    if (!currentSelection) return;
-
-    function move(point) {
-      if (from == point.node && (!ifAtStart || point.offset == 0)) {
-        currentSelection.changed = true;
-        point.node = to;
-        if (relative) point.offset = Math.max(0, point.offset + distance);
-        else point.offset = distance;
-      }
-    }
-    move(currentSelection.start);
-    move(currentSelection.end);
-  };
-
-  // Most functions are defined in two ways, one for the IE selection
-  // model, one for the W3C one.
-  if (select.ie_selection) {
-    function selectionNode(win, start) {
-      var range = win.document.selection.createRange();
-      range.collapse(start);
-
-      function nodeAfter(node) {
-        var found = null;
-        while (!found && node) {
-          found = node.nextSibling;
-          node = node.parentNode;
-        }
-        return nodeAtStartOf(found);
-      }
-
-      function nodeAtStartOf(node) {
-        while (node && node.firstChild) node = node.firstChild;
-        return {node: node, offset: 0};
-      }
-
-      var containing = range.parentElement();
-      if (!isAncestor(win.document.body, containing)) return null;
-      if (!containing.firstChild) return nodeAtStartOf(containing);
-
-      var working = range.duplicate();
-      working.moveToElementText(containing);
-      working.collapse(true);
-      for (var cur = containing.firstChild; cur; cur = cur.nextSibling) {
-        if (cur.nodeType == 3) {
-          var size = cur.nodeValue.length;
-          working.move("character", size);
-        }
-        else {
-          working.moveToElementText(cur);
-          working.collapse(false);
-        }
-
-        var dir = range.compareEndPoints("StartToStart", working);
-        if (dir == 0) return nodeAfter(cur);
-        if (dir == 1) continue;
-        if (cur.nodeType != 3) return nodeAtStartOf(cur);
-
-        working.setEndPoint("StartToEnd", range);
-        return {node: cur, offset: size - working.text.length};
-      }
-      return nodeAfter(containing);
-    }
-
-    select.markSelection = function(win) {
-      currentSelection = null;
-      var sel = win.document.selection;
-      if (!sel) return;
-      var start = selectionNode(win, true),
-          end = selectionNode(win, false);
-      if (!start || !end) return;
-      currentSelection = {start: start, end: end, window: win, changed: false};
-    };
-
-    select.selectMarked = function() {
-      if (!currentSelection || !currentSelection.changed) return;
-      var win = currentSelection.window, doc = win.document;
-
-      function makeRange(point) {
-        var range = doc.body.createTextRange(),
-            node = point.node;
-        if (!node) {
-          range.moveToElementText(currentSelection.window.document.body);
-          range.collapse(false);
-        }
-        else if (node.nodeType == 3) {
-          range.moveToElementText(node.parentNode);
-          var offset = point.offset;
-          while (node.previousSibling) {
-            node = node.previousSibling;
-            offset += (node.innerText || "").length;
-          }
-          range.move("character", offset);
-        }
-        else {
-          range.moveToElementText(node);
-          range.collapse(true);
-        }
-        return range;
-      }
-
-      var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end);
-      start.setEndPoint("StartToEnd", end);
-      start.select();
-    };
-
-    // Get the top-level node that one end of the cursor is inside or
-    // after. Note that this returns false for 'no cursor', and null
-    // for 'start of document'.
-    select.selectionTopNode = function(container, start) {
-      var selection = container.ownerDocument.selection;
-      if (!selection) return false;
-
-      var range = selection.createRange(), range2 = range.duplicate();
-      range.collapse(start);
-      var around = range.parentElement();
-      if (around && isAncestor(container, around)) {
-        // Only use this node if the selection is not at its start.
-        range2.moveToElementText(around);
-        if (range.compareEndPoints("StartToStart", range2) == 1)
-          return topLevelNodeAt(around, container);
-      }
-
-      // Move the start of a range to the start of a node,
-      // compensating for the fact that you can't call
-      // moveToElementText with text nodes.
-      function moveToNodeStart(range, node) {
-        if (node.nodeType == 3) {
-          var count = 0, cur = node.previousSibling;
-          while (cur && cur.nodeType == 3) {
-            count += cur.nodeValue.length;
-            cur = cur.previousSibling;
-          }
-          if (cur) {
-            try{range.moveToElementText(cur);}
-            catch(e){return false;}
-            range.collapse(false);
-          }
-          else range.moveToElementText(node.parentNode);
-          if (count) range.move("character", count);
-        }
-        else {
-          try{range.moveToElementText(node);}
-          catch(e){return false;}
-        }
-        return true;
-      }
-
-      // Do a binary search through the container object, comparing
-      // the start of each node to the selection
-      var start = 0, end = container.childNodes.length - 1;
-      while (start < end) {
-        var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
-        if (!node) return false; // Don't ask. IE6 manages this sometimes.
-        if (!moveToNodeStart(range2, node)) return false;
-        if (range.compareEndPoints("StartToStart", range2) == 1)
-          start = middle;
-        else
-          end = middle - 1;
-      }
-      return container.childNodes[start] || null;
-    };
-
-    // Place the cursor after this.start. This is only useful when
-    // manually moving the cursor instead of restoring it to its old
-    // position.
-    select.focusAfterNode = function(node, container) {
-      var range = container.ownerDocument.body.createTextRange();
-      range.moveToElementText(node || container);
-      range.collapse(!node);
-      range.select();
-    };
-
-    select.somethingSelected = function(win) {
-      var sel = win.document.selection;
-      return sel && (sel.createRange().text != "");
-    };
-
-    function insertAtCursor(window, html) {
-      var selection = window.document.selection;
-      if (selection) {
-        var range = selection.createRange();
-        range.pasteHTML(html);
-        range.collapse(false);
-        range.select();
-      }
-    }
-
-    // Used to normalize the effect of the enter key, since browsers
-    // do widely different things when pressing enter in designMode.
-    select.insertNewlineAtCursor = function(window) {
-      insertAtCursor(window, "<br>");
-    };
-
-    select.insertTabAtCursor = function(window) {
-      insertAtCursor(window, fourSpaces);
-    };
-
-    // Get the BR node at the start of the line on which the cursor
-    // currently is, and the offset into the line. Returns null as
-    // node if cursor is on first line.
-    select.cursorPos = function(container, start) {
-      var selection = container.ownerDocument.selection;
-      if (!selection) return null;
-
-      var topNode = select.selectionTopNode(container, start);
-      while (topNode && !isBR(topNode))
-        topNode = topNode.previousSibling;
-
-      var range = selection.createRange(), range2 = range.duplicate();
-      range.collapse(start);
-      if (topNode) {
-        range2.moveToElementText(topNode);
-        range2.collapse(false);
-      }
-      else {
-        // When nothing is selected, we can get all kinds of funky errors here.
-        try { range2.moveToElementText(container); }
-        catch (e) { return null; }
-        range2.collapse(true);
-      }
-      range.setEndPoint("StartToStart", range2);
-
-      return {node: topNode, offset: range.text.length};
-    };
-
-    select.setCursorPos = function(container, from, to) {
-      function rangeAt(pos) {
-        var range = container.ownerDocument.body.createTextRange();
-        if (!pos.node) {
-          range.moveToElementText(container);
-          range.collapse(true);
-        }
-        else {
-          range.moveToElementText(pos.node);
-          range.collapse(false);
-        }
-        range.move("character", pos.offset);
-        return range;
-      }
-
-      var range = rangeAt(from);
-      if (to && to != from)
-        range.setEndPoint("EndToEnd", rangeAt(to));
-      range.select();
-    }
-
-    // Some hacks for storing and re-storing the selection when the editor loses and regains focus.
-    select.selectionCoords = function (win) {
-      var selection = win.document.selection;
-      if (!selection) return null;
-      var start = selection.createRange(), end = start.duplicate();
-      start.collapse(true);
-      end.collapse(false);
-
-      var body = win.document.body;
-      return {start: {x: start.boundingLeft + body.scrollLeft - 1,
-                      y: start.boundingTop + body.scrollTop},
-              end: {x: end.boundingLeft + body.scrollLeft - 1,
-                    y: end.boundingTop + body.scrollTop}};
-    };
-
-    // Restore a stored selection.
-    select.selectCoords = function(win, coords) {
-      if (!coords) return;
-
-      var range1 = win.document.body.createTextRange(), range2 = range1.duplicate();
-      // This can fail for various hard-to-handle reasons.
-      try {
-        range1.moveToPoint(coords.start.x, coords.start.y);
-        range2.moveToPoint(coords.end.x, coords.end.y);
-        range1.setEndPoint("EndToStart", range2);
-        range1.select();
-      } catch(e) {}
-    };
-  }
-  // W3C model
-  else {
-    // Store start and end nodes, and offsets within these, and refer
-    // back to the selection object from those nodes, so that this
-    // object can be updated when the nodes are replaced before the
-    // selection is restored.
-    select.markSelection = function (win) {
-      var selection = win.getSelection();
-      if (!selection || selection.rangeCount == 0)
-        return (currentSelection = null);
-      var range = selection.getRangeAt(0);
-
-      currentSelection = {
-        start: {node: range.startContainer, offset: range.startOffset},
-        end: {node: range.endContainer, offset: range.endOffset},
-        window: win,
-        changed: false
-      };
-
-      // We want the nodes right at the cursor, not one of their
-      // ancestors with a suitable offset. This goes down the DOM tree
-      // until a 'leaf' is reached (or is it *up* the DOM tree?).
-      function normalize(point){
-        while (point.node.nodeType != 3 && !isBR(point.node)) {
-          var newNode = point.node.childNodes[point.offset] || point.node.nextSibling;
-          point.offset = 0;
-          while (!newNode && point.node.parentNode) {
-            point.node = point.node.parentNode;
-            newNode = point.node.nextSibling;
-          }
-          point.node = newNode;
-          if (!newNode)
-            break;
-        }
-      }
-
-      normalize(currentSelection.start);
-      normalize(currentSelection.end);
-    };
-
-    select.selectMarked = function () {
-      var cs = currentSelection;
-      if (!(cs && (cs.changed || (webkit && cs.start.node == cs.end.node)))) return;
-      var win = cs.window, range = win.document.createRange();
-
-      function setPoint(point, which) {
-        if (point.node) {
-          // Some magic to generalize the setting of the start and end
-          // of a range.
-          if (point.offset == 0)
-            range["set" + which + "Before"](point.node);
-          else
-            range["set" + which](point.node, point.offset);
-        }
-        else {
-          range.setStartAfter(win.document.body.lastChild || win.document.body);
-        }
-      }
-
-      setPoint(cs.end, "End");
-      setPoint(cs.start, "Start");
-      selectRange(range, win);
-    };
-
-    // Helper for selecting a range object.
-    function selectRange(range, window) {
-      var selection = window.getSelection();
-      selection.removeAllRanges();
-      selection.addRange(range);
-    };
-    function selectionRange(window) {
-      var selection = window.getSelection();
-      if (!selection || selection.rangeCount == 0)
-        return false;
-      else
-        return selection.getRangeAt(0);
-    }
-
-    // Finding the top-level node at the cursor in the W3C is, as you
-    // can see, quite an involved process.
-    select.selectionTopNode = function(container, start) {
-      var range = selectionRange(container.ownerDocument.defaultView);
-      if (!range) return false;
-
-      var node = start ? range.startContainer : range.endContainer;
-      var offset = start ? range.startOffset : range.endOffset;
-      // Work around (yet another) bug in Opera's selection model.
-      if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
-          container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
-        offset--;
-
-      // For text nodes, we look at the node itself if the cursor is
-      // inside, or at the node before it if the cursor is at the
-      // start.
-      if (node.nodeType == 3){
-        if (offset > 0)
-          return topLevelNodeAt(node, container);
-        else
-          return topLevelNodeBefore(node, container);
-      }
-      // Occasionally, browsers will return the HTML node as
-      // selection. If the offset is 0, we take the start of the frame
-      // ('after null'), otherwise, we take the last node.
-      else if (node.nodeName.toUpperCase() == "HTML") {
-        return (offset == 1 ? null : container.lastChild);
-      }
-      // If the given node is our 'container', we just look up the
-      // correct node by using the offset.
-      else if (node == container) {
-        return (offset == 0) ? null : node.childNodes[offset - 1];
-      }
-      // In any other case, we have a regular node. If the cursor is
-      // at the end of the node, we use the node itself, if it is at
-      // the start, we use the node before it, and in any other
-      // case, we look up the child before the cursor and use that.
-      else {
-        if (offset == node.childNodes.length)
-          return topLevelNodeAt(node, container);
-        else if (offset == 0)
-          return topLevelNodeBefore(node, container);
-        else
-          return topLevelNodeAt(node.childNodes[offset - 1], container);
-      }
-    };
-
-    select.focusAfterNode = function(node, container) {
-      var win = container.ownerDocument.defaultView,
-          range = win.document.createRange();
-      range.setStartBefore(container.firstChild || container);
-      // In Opera, setting the end of a range at the end of a line
-      // (before a BR) will cause the cursor to appear on the next
-      // line, so we set the end inside of the start node when
-      // possible.
-      if (node && !node.firstChild)
-        range.setEndAfter(node);
-      else if (node)
-        range.setEnd(node, node.childNodes.length);
-      else
-        range.setEndBefore(container.firstChild || container);
-      range.collapse(false);
-      selectRange(range, win);
-    };
-
-    select.somethingSelected = function(win) {
-      var range = selectionRange(win);
-      return range && !range.collapsed;
-    };
-
-    function insertNodeAtCursor(window, node) {
-      var range = selectionRange(window);
-      if (!range) return;
-
-      range.deleteContents();
-      range.insertNode(node);
-      webkitLastLineHack(window.document.body);
-      range = window.document.createRange();
-      range.selectNode(node);
-      range.collapse(false);
-      selectRange(range, window);
-    }
-
-    select.insertNewlineAtCursor = function(window) {
-      insertNodeAtCursor(window, window.document.createElement("BR"));
-    };
-
-    select.insertTabAtCursor = function(window) {
-      insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
-    };
-
-    select.cursorPos = function(container, start) {
-      var range = selectionRange(window);
-      if (!range) return;
-
-      var topNode = select.selectionTopNode(container, start);
-      while (topNode && !isBR(topNode))
-        topNode = topNode.previousSibling;
-
-      range = range.cloneRange();
-      range.collapse(start);
-      if (topNode)
-        range.setStartAfter(topNode);
-      else
-        range.setStartBefore(container);
-      return {node: topNode, offset: range.toString().length};
-    };
-
-    select.setCursorPos = function(container, from, to) {
-      var win = container.ownerDocument.defaultView,
-          range = win.document.createRange();
-
-      function setPoint(node, offset, side) {
-        if (!node)
-          node = container.firstChild;
-        else
-          node = node.nextSibling;
-
-        if (!node)
-          return;
-
-        if (offset == 0) {
-          range["set" + side + "Before"](node);
-          return true;
-        }
-
-        var backlog = []
-        function decompose(node) {
-          if (node.nodeType == 3)
-            backlog.push(node);
-          else
-            forEach(node.childNodes, decompose);
-        }
-        while (true) {
-          while (node && !backlog.length) {
-            decompose(node);
-            node = node.nextSibling;
-          }
-          var cur = backlog.shift();
-          if (!cur) return false;
-
-          var length = cur.nodeValue.length;
-          if (length >= offset) {
-            range["set" + side](cur, offset);
-            return true;
-          }
-          offset -= length;
-        }
-      }
-
-      to = to || from;
-      if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
-        selectRange(range, win);
-    };
-  }
-})();
diff --git a/project/static/js/lib/codemirror/stringstream.js b/project/static/js/lib/codemirror/stringstream.js
deleted file mode 100644 (file)
index 8c1c042..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* String streams are the things fed to parsers (which can feed them
- * to a tokenizer if they want). They provide peek and next methods
- * for looking at the current character (next 'consumes' this
- * character, peek does not), and a get method for retrieving all the
- * text that was consumed since the last time get was called.
- *
- * An easy mistake to make is to let a StopIteration exception finish
- * the token stream while there are still characters pending in the
- * string stream (hitting the end of the buffer while parsing a
- * token). To make it easier to detect such errors, the stringstreams
- * throw an exception when this happens.
- */
-
-// Make a stringstream stream out of an iterator that returns strings.
-// This is applied to the result of traverseDOM (see codemirror.js),
-// and the resulting stream is fed to the parser.
-window.stringStream = function(source){
-  // String that's currently being iterated over.
-  var current = "";
-  // Position in that string.
-  var pos = 0;
-  // Accumulator for strings that have been iterated over but not
-  // get()-ed yet.
-  var accum = "";
-  // Make sure there are more characters ready, or throw
-  // StopIteration.
-  function ensureChars() {
-    while (pos == current.length) {
-      accum += current;
-      current = ""; // In case source.next() throws
-      pos = 0;
-      try {current = source.next();}
-      catch (e) {
-        if (e != StopIteration) throw e;
-        else return false;
-      }
-    }
-    return true;
-  }
-
-  return {
-    // Return the next character in the stream.
-    peek: function() {
-      if (!ensureChars()) return null;
-      return current.charAt(pos);
-    },
-    // Get the next character, throw StopIteration if at end, check
-    // for unused content.
-    next: function() {
-      if (!ensureChars()) {
-        if (accum.length > 0)
-          throw "End of stringstream reached without emptying buffer ('" + accum + "').";
-        else
-          throw StopIteration;
-      }
-      return current.charAt(pos++);
-    },
-    // Return the characters iterated over since the last call to
-    // .get().
-    get: function() {
-      var temp = accum;
-      accum = "";
-      if (pos > 0){
-        temp += current.slice(0, pos);
-        current = current.slice(pos);
-        pos = 0;
-      }
-      return temp;
-    },
-    // Push a string back into the stream.
-    push: function(str) {
-      current = current.slice(0, pos) + str + current.slice(pos);
-    },
-    lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
-      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
-      str = cased(str);
-      var found = false;
-
-      var _accum = accum, _pos = pos;
-      if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
-
-      while (true) {
-        var end = pos + str.length, left = current.length - pos;
-        if (end <= current.length) {
-          found = str == cased(current.slice(pos, end));
-          pos = end;
-          break;
-        }
-        else if (str.slice(0, left) == cased(current.slice(pos))) {
-          accum += current; current = "";
-          try {current = source.next();}
-          catch (e) {break;}
-          pos = 0;
-          str = str.slice(left);
-        }
-        else {
-          break;
-        }
-      }
-
-      if (!(found && consume)) {
-        current = accum.slice(_accum.length) + current;
-        pos = _pos;
-        accum = _accum;
-      }
-
-      return found;
-    },
-
-    // Utils built on top of the above
-    more: function() {
-      return this.peek() !== null;
-    },
-    applies: function(test) {
-      var next = this.peek();
-      return (next !== null && test(next));
-    },
-    nextWhile: function(test) {
-      var next;
-      while ((next = this.peek()) !== null && test(next))
-        this.next();
-    },
-    matches: function(re) {
-      var next = this.peek();
-      return (next !== null && re.test(next));
-    },
-    nextWhileMatches: function(re) {
-      var next;
-      while ((next = this.peek()) !== null && re.test(next))
-        this.next();
-    },
-    equals: function(ch) {
-      return ch === this.peek();
-    },
-    endOfLine: function() {
-      var next = this.peek();
-      return next == null || next == "\n";
-    }
-  };
-};
diff --git a/project/static/js/lib/codemirror/tokenize.js b/project/static/js/lib/codemirror/tokenize.js
deleted file mode 100644 (file)
index 071970c..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-// A framework for simple tokenizers. Takes care of newlines and
-// white-space, and of getting the text from the source stream into
-// the token object. A state is a function of two arguments -- a
-// string stream and a setState function. The second can be used to
-// change the tokenizer's state, and can be ignored for stateless
-// tokenizers. This function should advance the stream over a token
-// and return a string or object containing information about the next
-// token, or null to pass and have the (new) state be called to finish
-// the token. When a string is given, it is wrapped in a {style, type}
-// object. In the resulting object, the characters consumed are stored
-// under the content property. Any whitespace following them is also
-// automatically consumed, and added to the value property. (Thus,
-// content is the actual meaningful part of the token, while value
-// contains all the text it spans.)
-
-function tokenizer(source, state) {
-  // Newlines are always a separate token.
-  function isWhiteSpace(ch) {
-    // The messy regexp is because IE's regexp matcher is of the
-    // opinion that non-breaking spaces are no whitespace.
-    return ch != "\n" && /^[\s\u00a0]*$/.test(ch);
-  }
-
-  var tokenizer = {
-    state: state,
-
-    take: function(type) {
-      if (typeof(type) == "string")
-        type = {style: type, type: type};
-
-      type.content = (type.content || "") + source.get();
-      if (!/\n$/.test(type.content))
-        source.nextWhile(isWhiteSpace);
-      type.value = type.content + source.get();
-      return type;
-    },
-
-    next: function () {
-      if (!source.more()) throw StopIteration;
-
-      var type;
-      if (source.equals("\n")) {
-        source.next();
-        return this.take("whitespace");
-      }
-      
-      if (source.applies(isWhiteSpace))
-        type = "whitespace";
-      else
-        while (!type)
-          type = this.state(source, function(s) {tokenizer.state = s;});
-
-      return this.take(type);
-    }
-  };
-  return tokenizer;
-}
diff --git a/project/static/js/lib/codemirror/undo.js b/project/static/js/lib/codemirror/undo.js
deleted file mode 100644 (file)
index 97daf59..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-/**
- * Storage and control for undo information within a CodeMirror
- * editor. 'Why on earth is such a complicated mess required for
- * that?', I hear you ask. The goal, in implementing this, was to make
- * the complexity of storing and reverting undo information depend
- * only on the size of the edited or restored content, not on the size
- * of the whole document. This makes it necessary to use a kind of
- * 'diff' system, which, when applied to a DOM tree, causes some
- * complexity and hackery.
- *
- * In short, the editor 'touches' BR elements as it parses them, and
- * the History stores these. When nothing is touched in commitDelay
- * milliseconds, the changes are committed: It goes over all touched
- * nodes, throws out the ones that did not change since last commit or
- * are no longer in the document, and assembles the rest into zero or
- * more 'chains' -- arrays of adjacent lines. Links back to these
- * chains are added to the BR nodes, while the chain that previously
- * spanned these nodes is added to the undo history. Undoing a change
- * means taking such a chain off the undo history, restoring its
- * content (text is saved per line) and linking it back into the
- * document.
- */
-
-// A history object needs to know about the DOM container holding the
-// document, the maximum amount of undo levels it should store, the
-// delay (of no input) after which it commits a set of changes, and,
-// unfortunately, the 'parent' window -- a window that is not in
-// designMode, and on which setTimeout works in every browser.
-function History(container, maxDepth, commitDelay, editor, onChange) {
-  this.container = container;
-  this.maxDepth = maxDepth; this.commitDelay = commitDelay;
-  this.editor = editor; this.parent = editor.parent;
-  this.onChange = onChange;
-  // This line object represents the initial, empty editor.
-  var initial = {text: "", from: null, to: null};
-  // As the borders between lines are represented by BR elements, the
-  // start of the first line and the end of the last one are
-  // represented by null. Since you can not store any properties
-  // (links to line objects) in null, these properties are used in
-  // those cases.
-  this.first = initial; this.last = initial;
-  // Similarly, a 'historyTouched' property is added to the BR in
-  // front of lines that have already been touched, and 'firstTouched'
-  // is used for the first line.
-  this.firstTouched = false;
-  // History is the set of committed changes, touched is the set of
-  // nodes touched since the last commit.
-  this.history = []; this.redoHistory = []; this.touched = [];
-}
-
-History.prototype = {
-  // Schedule a commit (if no other touches come in for commitDelay
-  // milliseconds).
-  scheduleCommit: function() {
-    var self = this;
-    this.parent.clearTimeout(this.commitTimeout);
-    this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
-  },
-
-  // Mark a node as touched. Null is a valid argument.
-  touch: function(node) {
-    this.setTouched(node);
-    this.scheduleCommit();
-  },
-
-  // Undo the last change.
-  undo: function() {
-    // Make sure pending changes have been committed.
-    this.commit();
-
-    if (this.history.length) {
-      // Take the top diff from the history, apply it, and store its
-      // shadow in the redo history.
-      var item = this.history.pop();
-      this.redoHistory.push(this.updateTo(item, "applyChain"));
-      if (this.onChange) this.onChange();
-      return this.chainNode(item);
-    }
-  },
-
-  // Redo the last undone change.
-  redo: function() {
-    this.commit();
-    if (this.redoHistory.length) {
-      // The inverse of undo, basically.
-      var item = this.redoHistory.pop();
-      this.addUndoLevel(this.updateTo(item, "applyChain"));
-      if (this.onChange) this.onChange();
-      return this.chainNode(item);
-    }
-  },
-
-  clear: function() {
-    this.history = [];
-    this.redoHistory = [];
-  },
-
-  // Ask for the size of the un/redo histories.
-  historySize: function() {
-    return {undo: this.history.length, redo: this.redoHistory.length};
-  },
-
-  // Push a changeset into the document.
-  push: function(from, to, lines) {
-    var chain = [];
-    for (var i = 0; i < lines.length; i++) {
-      var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
-      chain.push({from: from, to: end, text: cleanText(lines[i])});
-      from = end;
-    }
-    this.pushChains([chain], from == null && to == null);
-  },
-
-  pushChains: function(chains, doNotHighlight) {
-    this.commit(doNotHighlight);
-    this.addUndoLevel(this.updateTo(chains, "applyChain"));
-    this.redoHistory = [];
-  },
-
-  // Retrieve a DOM node from a chain (for scrolling to it after undo/redo).
-  chainNode: function(chains) {
-    for (var i = 0; i < chains.length; i++) {
-      var start = chains[i][0], node = start && (start.from || start.to);
-      if (node) return node;
-    }
-  },
-
-  // Clear the undo history, make the current document the start
-  // position.
-  reset: function() {
-    this.history = []; this.redoHistory = [];
-  },
-
-  textAfter: function(br) {
-    return this.after(br).text;
-  },
-
-  nodeAfter: function(br) {
-    return this.after(br).to;
-  },
-
-  nodeBefore: function(br) {
-    return this.before(br).from;
-  },
-
-  // Commit unless there are pending dirty nodes.
-  tryCommit: function() {
-    if (!window.History) return; // Stop when frame has been unloaded
-    if (this.editor.highlightDirty()) this.commit(true);
-    else this.scheduleCommit();
-  },
-
-  // Check whether the touched nodes hold any changes, if so, commit
-  // them.
-  commit: function(doNotHighlight) {
-    this.parent.clearTimeout(this.commitTimeout);
-    // Make sure there are no pending dirty nodes.
-    if (!doNotHighlight) this.editor.highlightDirty(true);
-    // Build set of chains.
-    var chains = this.touchedChains(), self = this;
-
-    if (chains.length) {
-      this.addUndoLevel(this.updateTo(chains, "linkChain"));
-      this.redoHistory = [];
-      if (this.onChange) this.onChange();
-    }
-  },
-
-  // [ end of public interface ]
-
-  // Update the document with a given set of chains, return its
-  // shadow. updateFunc should be "applyChain" or "linkChain". In the
-  // second case, the chains are taken to correspond the the current
-  // document, and only the state of the line data is updated. In the
-  // first case, the content of the chains is also pushed iinto the
-  // document.
-  updateTo: function(chains, updateFunc) {
-    var shadows = [], dirty = [];
-    for (var i = 0; i < chains.length; i++) {
-      shadows.push(this.shadowChain(chains[i]));
-      dirty.push(this[updateFunc](chains[i]));
-    }
-    if (updateFunc == "applyChain")
-      this.notifyDirty(dirty);
-    return shadows;
-  },
-
-  // Notify the editor that some nodes have changed.
-  notifyDirty: function(nodes) {
-    forEach(nodes, method(this.editor, "addDirtyNode"))
-    this.editor.scheduleHighlight();
-  },
-
-  // Link a chain into the DOM nodes (or the first/last links for null
-  // nodes).
-  linkChain: function(chain) {
-    for (var i = 0; i < chain.length; i++) {
-      var line = chain[i];
-      if (line.from) line.from.historyAfter = line;
-      else this.first = line;
-      if (line.to) line.to.historyBefore = line;
-      else this.last = line;
-    }
-  },
-
-  // Get the line object after/before a given node.
-  after: function(node) {
-    return node ? node.historyAfter : this.first;
-  },
-  before: function(node) {
-    return node ? node.historyBefore : this.last;
-  },
-
-  // Mark a node as touched if it has not already been marked.
-  setTouched: function(node) {
-    if (node) {
-      if (!node.historyTouched) {
-        this.touched.push(node);
-        node.historyTouched = true;
-      }
-    }
-    else {
-      this.firstTouched = true;
-    }
-  },
-
-  // Store a new set of undo info, throw away info if there is more of
-  // it than allowed.
-  addUndoLevel: function(diffs) {
-    this.history.push(diffs);
-    if (this.history.length > this.maxDepth)
-      this.history.shift();
-  },
-
-  // Build chains from a set of touched nodes.
-  touchedChains: function() {
-    var self = this;
-
-    // The temp system is a crummy hack to speed up determining
-    // whether a (currently touched) node has a line object associated
-    // with it. nullTemp is used to store the object for the first
-    // line, other nodes get it stored in their historyTemp property.
-    var nullTemp = null;
-    function temp(node) {return node ? node.historyTemp : nullTemp;}
-    function setTemp(node, line) {
-      if (node) node.historyTemp = line;
-      else nullTemp = line;
-    }
-
-    function buildLine(node) {
-      var text = [];
-      for (var cur = node ? node.nextSibling : self.container.firstChild;
-           cur && !isBR(cur); cur = cur.nextSibling)
-        if (cur.currentText) text.push(cur.currentText);
-      return {from: node, to: cur, text: cleanText(text.join(""))};
-    }
-
-    // Filter out unchanged lines and nodes that are no longer in the
-    // document. Build up line objects for remaining nodes.
-    var lines = [];
-    if (self.firstTouched) self.touched.push(null);
-    forEach(self.touched, function(node) {
-      if (node && node.parentNode != self.container) return;
-
-      if (node) node.historyTouched = false;
-      else self.firstTouched = false;
-
-      var line = buildLine(node), shadow = self.after(node);
-      if (!shadow || shadow.text != line.text || shadow.to != line.to) {
-        lines.push(line);
-        setTemp(node, line);
-      }
-    });
-
-    // Get the BR element after/before the given node.
-    function nextBR(node, dir) {
-      var link = dir + "Sibling", search = node[link];
-      while (search && !isBR(search))
-        search = search[link];
-      return search;
-    }
-
-    // Assemble line objects into chains by scanning the DOM tree
-    // around them.
-    var chains = []; self.touched = [];
-    forEach(lines, function(line) {
-      // Note that this makes the loop skip line objects that have
-      // been pulled into chains by lines before them.
-      if (!temp(line.from)) return;
-
-      var chain = [], curNode = line.from, safe = true;
-      // Put any line objects (referred to by temp info) before this
-      // one on the front of the array.
-      while (true) {
-        var curLine = temp(curNode);
-        if (!curLine) {
-          if (safe) break;
-          else curLine = buildLine(curNode);
-        }
-        chain.unshift(curLine);
-        setTemp(curNode, null);
-        if (!curNode) break;
-        safe = self.after(curNode);
-        curNode = nextBR(curNode, "previous");
-      }
-      curNode = line.to; safe = self.before(line.from);
-      // Add lines after this one at end of array.
-      while (true) {
-        if (!curNode) break;
-        var curLine = temp(curNode);
-        if (!curLine) {
-          if (safe) break;
-          else curLine = buildLine(curNode);
-        }
-        chain.push(curLine);
-        setTemp(curNode, null);
-        safe = self.before(curNode);
-        curNode = nextBR(curNode, "next");
-      }
-      chains.push(chain);
-    });
-
-    return chains;
-  },
-
-  // Find the 'shadow' of a given chain by following the links in the
-  // DOM nodes at its start and end.
-  shadowChain: function(chain) {
-    var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
-    while (true) {
-      shadows.push(next);
-      var nextNode = next.to;
-      if (!nextNode || nextNode == end)
-        break;
-      else
-        next = nextNode.historyAfter || this.before(end);
-      // (The this.before(end) is a hack -- FF sometimes removes
-      // properties from BR nodes, in which case the best we can hope
-      // for is to not break.)
-    }
-    return shadows;
-  },
-
-  // Update the DOM tree to contain the lines specified in a given
-  // chain, link this chain into the DOM nodes.
-  applyChain: function(chain) {
-    // Some attempt is made to prevent the cursor from jumping
-    // randomly when an undo or redo happens. It still behaves a bit
-    // strange sometimes.
-    var cursor = select.cursorPos(this.container, false), self = this;
-
-    // Remove all nodes in the DOM tree between from and to (null for
-    // start/end of container).
-    function removeRange(from, to) {
-      var pos = from ? from.nextSibling : self.container.firstChild;
-      while (pos != to) {
-        var temp = pos.nextSibling;
-        removeElement(pos);
-        pos = temp;
-      }
-    }
-
-    var start = chain[0].from, end = chain[chain.length - 1].to;
-    // Clear the space where this change has to be made.
-    removeRange(start, end);
-
-    // Insert the content specified by the chain into the DOM tree.
-    for (var i = 0; i < chain.length; i++) {
-      var line = chain[i];
-      // The start and end of the space are already correct, but BR
-      // tags inside it have to be put back.
-      if (i > 0)
-        self.container.insertBefore(line.from, end);
-
-      // Add the text.
-      var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument);
-      self.container.insertBefore(node, end);
-      // See if the cursor was on this line. Put it back, adjusting
-      // for changed line length, if it was.
-      if (cursor && cursor.node == line.from) {
-        var cursordiff = 0;
-        var prev = this.after(line.from);
-        if (prev && i == chain.length - 1) {
-          // Only adjust if the cursor is after the unchanged part of
-          // the line.
-          for (var match = 0; match < cursor.offset &&
-               line.text.charAt(match) == prev.text.charAt(match); match++);
-          if (cursor.offset > match)
-            cursordiff = line.text.length - prev.text.length;
-        }
-        select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
-      }
-      // Cursor was in removed line, this is last new line.
-      else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
-        select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
-      }
-    }
-
-    // Anchor the chain in the DOM tree.
-    this.linkChain(chain);
-    return start;
-  }
-};
diff --git a/project/static/js/lib/codemirror/util.js b/project/static/js/lib/codemirror/util.js
deleted file mode 100644 (file)
index 0cd91d4..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/* A few useful utility functions. */
-
-var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
-var webkit = /AppleWebKit/.test(navigator.userAgent);
-var safari = /Apple Computers, Inc/.test(navigator.vendor);
-
-// Capture a method on an object.
-function method(obj, name) {
-  return function() {obj[name].apply(obj, arguments);};
-}
-
-// The value used to signal the end of a sequence in iterators.
-var StopIteration = {toString: function() {return "StopIteration"}};
-
-// Apply a function to each element in a sequence.
-function forEach(iter, f) {
-  if (iter.next) {
-    try {while (true) f(iter.next());}
-    catch (e) {if (e != StopIteration) throw e;}
-  }
-  else {
-    for (var i = 0; i < iter.length; i++)
-      f(iter[i]);
-  }
-}
-
-// Map a function over a sequence, producing an array of results.
-function map(iter, f) {
-  var accum = [];
-  forEach(iter, function(val) {accum.push(f(val));});
-  return accum;
-}
-
-// Create a predicate function that tests a string againsts a given
-// regular expression. No longer used but might be used by 3rd party
-// parsers.
-function matcher(regexp){
-  return function(value){return regexp.test(value);};
-}
-
-// Test whether a DOM node has a certain CSS class. Much faster than
-// the MochiKit equivalent, for some reason.
-function hasClass(element, className){
-  var classes = element.className;
-  return classes && new RegExp("(^| )" + className + "($| )").test(classes);
-}
-
-// Insert a DOM node after another node.
-function insertAfter(newNode, oldNode) {
-  var parent = oldNode.parentNode;
-  parent.insertBefore(newNode, oldNode.nextSibling);
-  return newNode;
-}
-
-function removeElement(node) {
-  if (node.parentNode)
-    node.parentNode.removeChild(node);
-}
-
-function clearElement(node) {
-  while (node.firstChild)
-    node.removeChild(node.firstChild);
-}
-
-// Check whether a node is contained in another one.
-function isAncestor(node, child) {
-  while (child = child.parentNode) {
-    if (node == child)
-      return true;
-  }
-  return false;
-}
-
-// The non-breaking space character.
-var nbsp = "\u00a0";
-var matching = {"{": "}", "[": "]", "(": ")",
-                "}": "{", "]": "[", ")": "("};
-
-// Standardize a few unportable event properties.
-function normalizeEvent(event) {
-  if (!event.stopPropagation) {
-    event.stopPropagation = function() {this.cancelBubble = true;};
-    event.preventDefault = function() {this.returnValue = false;};
-  }
-  if (!event.stop) {
-    event.stop = function() {
-      this.stopPropagation();
-      this.preventDefault();
-    };
-  }
-
-  if (event.type == "keypress") {
-    event.code = (event.charCode == null) ? event.keyCode : event.charCode;
-    event.character = String.fromCharCode(event.code);
-  }
-  return event;
-}
-
-// Portably register event handlers.
-function addEventHandler(node, type, handler, removeFunc) {
-  function wrapHandler(event) {
-    handler(normalizeEvent(event || window.event));
-  }
-  if (typeof node.addEventListener == "function") {
-    node.addEventListener(type, wrapHandler, false);
-    if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);};
-  }
-  else {
-    node.attachEvent("on" + type, wrapHandler);
-    if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);};
-  }
-}
-
-function nodeText(node) {
-  return node.textContent || node.innerText || node.nodeValue || "";
-}
-
-function nodeTop(node) {
-  var top = 0;
-  while (node.offsetParent) {
-    top += node.offsetTop;
-    node = node.offsetParent;
-  }
-  return top;
-}
-
-function isBR(node) {
-  var nn = node.nodeName;
-  return nn == "BR" || nn == "br";
-}
-function isSpan(node) {
-  var nn = node.nodeName;
-  return nn == "SPAN" || nn == "span";
-}
diff --git a/project/static/js/lib/jquery.cookie.js b/project/static/js/lib/jquery.cookie.js
deleted file mode 100644 (file)
index 6df1fac..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * Cookie plugin
- *
- * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- */
-
-/**
- * Create a cookie with the given name and value and other optional parameters.
- *
- * @example $.cookie('the_cookie', 'the_value');
- * @desc Set the value of a cookie.
- * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
- * @desc Create a cookie with all available options.
- * @example $.cookie('the_cookie', 'the_value');
- * @desc Create a session cookie.
- * @example $.cookie('the_cookie', null);
- * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
- *       used when the cookie was set.
- *
- * @param String name The name of the cookie.
- * @param String value The value of the cookie.
- * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
- * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
- *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
- *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
- *                             when the the browser exits.
- * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
- * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
- * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
- *                        require a secure protocol (like HTTPS).
- * @type undefined
- *
- * @name $.cookie
- * @cat Plugins/Cookie
- * @author Klaus Hartl/klaus.hartl@stilbuero.de
- */
-
-/**
- * Get the value of a cookie with the given name.
- *
- * @example $.cookie('the_cookie');
- * @desc Get the value of a cookie.
- *
- * @param String name The name of the cookie.
- * @return The value of the cookie.
- * @type String
- *
- * @name $.cookie
- * @cat Plugins/Cookie
- * @author Klaus Hartl/klaus.hartl@stilbuero.de
- */
-jQuery.cookie = function(name, value, options) {
-    if (typeof value != 'undefined') { // name and value given, set cookie
-        options = options || {};
-        if (value === null) {
-            value = '';
-            options.expires = -1;
-        }
-        var expires = '';
-        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
-            var date;
-            if (typeof options.expires == 'number') {
-                date = new Date();
-                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
-            } else {
-                date = options.expires;
-            }
-            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
-        }
-        // CAUTION: Needed to parenthesize options.path and options.domain
-        // in the following expressions, otherwise they evaluate to undefined
-        // in the packed version for some reason...
-        var path = options.path ? '; path=' + (options.path) : '';
-        var domain = options.domain ? '; domain=' + (options.domain) : '';
-        var secure = options.secure ? '; secure' : '';
-        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
-    } else { // only name given, get cookie
-        var cookieValue = null;
-        if (document.cookie && document.cookie != '') {
-            var cookies = document.cookie.split(';');
-            for (var i = 0; i < cookies.length; i++) {
-                var cookie = jQuery.trim(cookies[i]);
-                // Does this cookie string begin with the name we want?
-                if (cookie.substring(0, name.length + 1) == (name + '=')) {
-                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
-                    break;
-                }
-            }
-        }
-        return cookieValue;
-    }
-};
\ No newline at end of file
diff --git a/project/static/js/lib/jquery.hpanel.js b/project/static/js/lib/jquery.hpanel.js
deleted file mode 100644 (file)
index 1ad0d17..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-(function($){
-    
-       /* behaviour */
-       $.hpanel = {
-        settings: {},
-               current_data: {},
-        resize_start: function(event, mydata) {
-                       $(document).bind('mousemove', mydata, $.hpanel.resize_changed).
-                               bind('mouseup', mydata, $.hpanel.resize_stop); 
-
-                       $('.panel-overlay', mydata.root).css('display', 'block');
-                       return false;
-               },
-       resize_changed: function(event) {
-                       var old_width = parseInt(event.data.overlay.css('width'));
-                       var delta = event.pageX + event.data.hotspot_x - old_width;
-                       event.data.overlay.css({'width': old_width + delta});
-
-                       if(event.data.overlay.next) {
-                               var left = parseInt(event.data.overlay.next.css('left'));
-                               event.data.overlay.next.css('left', left+delta);
-                       }
-
-            return false; 
-        },
-        resize_stop: function(event) {
-            $(document).unbind('mousemove', $.hpanel.resize_changed).unbind('mouseup', $.hpanel.resize_stop);
-                       // $('.panel-content', event.data.root).css('display', 'block');
-                       var overlays = $('.panel-content-overlay', event.data.root);
-                       $('.panel-content-overlay', event.data.root).each(function(i) {
-                               if( $(this).data('panel').hasClass('last-panel') )
-                                       $(this).data('panel').css({
-                                               'left': $(this).css('left'), 'right': $(this).css('right')}); 
-                               else
-                                       $(this).data('panel').css({
-                                               'left': $(this).css('left'), 'width': $(this).css('width')}); 
-                       });
-                       $('.panel-overlay', event.data.root).css('display', 'none');
-            $(event.data.root).trigger('stopResize');
-        }
-    };
-    
-    $.fn.makeHorizPanel = function(options) 
-       {
-               var root = $(this)
-
-               /* create an overlay */
-               var overlay_root = $("<div class='panel-overlay'></div>");
-               root.append(overlay_root);
-
-               var prev = null;
-
-               $('*.panel-wrap', root).each( function() 
-        {
-                       var panel = $(this);
-                       var handle = $('.panel-slider', panel);
-                       var overlay = $("<div class='panel-content-overlay panel-wrap'>&nbsp;</div>");
-                       overlay_root.append(overlay);
-                       overlay.data('panel', panel);
-                       overlay.data('next', null);
-
-                       if (prev) prev.next = overlay;
-
-                       if( panel.hasClass('last-panel') )                              
-            {
-                               overlay.css({'left': panel.css('left'), 'right': panel.css('right')});
-            }
-                       else {
-                               overlay.css({'left': panel.css('left'), 'width': panel.css('width')});
-                               $.log('Has handle: ' + panel.attr('id'));
-                               overlay.append(handle.clone());
-                               /* attach the trigger */
-                               handle.mousedown(function(event) {
-                                       var touch_data = {
-                                               root: root, overlay: overlay,
-                                               hotspot_x: event.pageX - handle.position().left
-                                       };
-
-                                       $(this).trigger('hpanel:panel-resize-start', touch_data);
-                                       return false;
-                               });
-                               $('.panel-content', panel).css('right', 
-                                       (handle.outerWidth() || 10) + 'px');
-                               $('.panel-content-overlay', panel).css('right',
-                                       (handle.outerWidth() || 10) + 'px');
-                       };                              
-
-                       prev = overlay;
-        });
-
-       root.bind('hpanel:panel-resize-start', $.hpanel.resize_start);
-    };
-})(jQuery);
-
diff --git a/project/static/js/lib/jquery.js b/project/static/js/lib/jquery.js
deleted file mode 100644 (file)
index 9263574..0000000
+++ /dev/null
@@ -1,4376 +0,0 @@
-/*!
- * 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 
-       // Will speed up references to window, and allows munging its name.
-       window = this,
-       // Will speed up references to undefined, and allows munging its name.
-       undefined,
-       // Map over jQuery in case of overwrite
-       _jQuery = window.jQuery,
-       // Map over the $ in case of overwrite
-       _$ = window.$,
-
-       jQuery = window.jQuery = window.$ = function( selector, context ) {
-               // The jQuery object is actually just the init constructor 'enhanced'
-               return new jQuery.fn.init( selector, context );
-       },
-
-       // A simple way to check for HTML strings or ID strings
-       // (both of which we optimize for)
-       quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\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] );
-
-                                       // Handle the case where IE and Opera return items
-                                       // by name instead of ID
-                                       if ( elem && 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;
-                               }
-
-                       // 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.isArray( selector ) ?
-                       selector :
-                       jQuery.makeArray(selector));
-       },
-
-       // Start with an empty selector
-       selector: "",
-
-       // The current version of jQuery being used
-       jquery: "1.3.2",
-
-       // 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
-                       Array.prototype.slice.call( 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 method, not like a jQuery method.
-       push: [].push,
-       sort: [].sort,
-       splice: [].splice,
-
-       find: function( selector ) {
-               if ( this.length === 1 ) {
-                       var ret = this.pushStack( [], "find", selector );
-                       ret.length = 0;
-                       jQuery.find( selector, this[0], ret );
-                       return ret;
-               } else {
-                       return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
-                               return jQuery.find( selector, elem );
-                       })), "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 html = this.outerHTML;
-                               if ( !html ) {
-                                       var div = this.ownerDocument.createElement("div");
-                                       div.appendChild( this.cloneNode(true) );
-                                       html = div.innerHTML;
-                               }
-
-                               return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
-                       } else
-                               return this.cloneNode(true);
-               });
-
-               // Copy the events from the original to the clone
-               if ( events === true ) {
-                       var orig = this.find("*").andSelf(), i = 0;
-
-                       ret.find("*").andSelf().each(function(){
-                               if ( this.nodeName !== orig[i].nodeName )
-                                       return;
-
-                               var events = jQuery.data( orig[i], "events" );
-
-                               for ( var type in events ) {
-                                       for ( var handler in events[ type ] ) {
-                                               jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
-                                       }
-                               }
-
-                               i++;
-                       });
-               }
-
-               // 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,
-                       closer = 0;
-
-               return this.map(function(){
-                       var cur = this;
-                       while ( cur && cur.ownerDocument ) {
-                               if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
-                                       jQuery.data(cur, "closest", closer);
-                                       return cur;
-                               }
-                               cur = cur.parentNode;
-                               closer++;
-                       }
-               });
-       },
-
-       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.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
-                               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;
-
-                       if ( first )
-                               for ( var i = 0, l = this.length; i < l; i++ )
-                                       callback.call( root(this[i], first), this.length > 1 || i > 0 ?
-                                                       fragment.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.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
-                       !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
-       },
-
-       // Evalulates a script in a global context
-       globalEval: function( data ) {
-               if ( data && /\S/.test(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 elem && 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, extra ) {
-               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;
-
-                               if ( extra === "border" )
-                                       return;
-
-                               jQuery.each( which, function() {
-                                       if ( !extra )
-                                               val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
-                                       if ( extra === "margin" )
-                                               val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
-                                       else
-                                               val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
-                               });
-                       }
-
-                       if ( elem.offsetWidth !== 0 )
-                               getWH();
-                       else
-                               jQuery.swap( elem, props, getWH );
-
-                       return Math.max(0, Math.round(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 + "></" + tag + ">";
-                               });
-
-                               // Trim whitespace, otherwise indexOf won't work as expected
-                               var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
-
-                               var wrap =
-                                       // option or optgroup
-                                       !tags.indexOf("<opt") &&
-                                       [ 1, "<select multiple='multiple'>", "</select>" ] ||
-
-                                       !tags.indexOf("<leg") &&
-                                       [ 1, "<fieldset>", "</fieldset>" ] ||
-
-                                       tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
-                                       [ 1, "<table>", "</table>" ] ||
-
-                                       !tags.indexOf("<tr") &&
-                                       [ 2, "<table><tbody>", "</tbody></table>" ] ||
-
-                                       // <thead> matched above
-                                       (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
-                                       [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
-
-                                       !tags.indexOf("<col") &&
-                                       [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
-
-                                       // IE can't serialize <link> and <script> tags normally
-                                       !jQuery.support.htmlSerialize &&
-                                       [ 1, "div<div>", "</div>" ] ||
-
-                                       [ 0, "", "" ];
-
-                               // Go to html and back, then peel off extra wrappers
-                               div.innerHTML = wrap[1] + elem + wrap[2];
-
-                               // Move to the right depth
-                               while ( wrap[0]-- )
-                                       div = div.lastChild;
-
-                               // Remove IE's autoinserted <tbody> from table fragments
-                               if ( !jQuery.support.tbody ) {
-
-                                       // String was a <table>, *may* have spurious <tbody>
-                                       var hasBody = /<tbody/i.test(elem),
-                                               tbody = !tags.indexOf("<table") && !hasBody ?
-                                                       div.firstChild && div.firstChild.childNodes :
-
-                                               // String was a bare <thead> or <tfoot>
-                                               wrap[1] == "<table>" && !hasBody ?
-                                                       div.childNodes :
-                                                       [];
-
-                                       for ( var j = tbody.length - 1; j >= 0 ; --j )
-                                               if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
-                                                       tbody[ j ].parentNode.removeChild( tbody[ j ] );
-
-                                       }
-
-                               // IE completely kills leading whitespace when innerHTML is used
-                               if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
-                                       div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
-                               
-                               elem = jQuery.makeArray( div.childNodes );
-                       }
-
-                       if ( elem.nodeType )
-                               ret.push( elem );
-                       else
-                               ret = jQuery.merge( ret, elem );
-
-               });
-
-               if ( fragment ) {
-                       for ( var i = 0; ret[i]; i++ ) {
-                               if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
-                                       scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
-                               } else {
-                                       if ( ret[i].nodeType === 1 )
-                                               ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
-                                       fragment.appendChild( ret[i] );
-                               }
-                       }
-                       
-                       return scripts;
-               }
-
-               return ret;
-       },
-
-       attr: function( elem, name, value ) {
-               // don't set attributes on text and comment nodes
-               if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
-                       return undefined;
-
-               var notxml = !jQuery.isXMLDoc( elem ),
-                       // Whether we are setting (or getting)
-                       set = value !== undefined;
-
-               // Try to normalize/fix the name
-               name = notxml && jQuery.props[ name ] || name;
-
-               // Only do all the following if this is a node (faster for style)
-               // IE elem.getAttribute passes even for style
-               if ( elem.tagName ) {
-
-                       // These attributes require special treatment
-                       var special = /href|src|style/.test( name );
-
-                       // Safari mis-reports the default selected property of a hidden option
-                       // Accessing the parent's selectedIndex property fixes it
-                       if ( name == "selected" && elem.parentNode )
-                               elem.parentNode.selectedIndex;
-
-                       // If applicable, access the attribute via the DOM 0 way
-                       if ( name in elem && notxml && !special ) {
-                               if ( set ){
-                                       // We can't allow the type property to be changed (since it causes problems in IE)
-                                       if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
-                                               throw "type property can't be changed";
-
-                                       elem[ name ] = value;
-                               }
-
-                               // browsers index elements by id/name on forms, give priority to attributes.
-                               if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
-                                       return elem.getAttributeNode( name ).nodeValue;
-
-                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
-                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
-                               if ( name == "tabIndex" ) {
-                                       var attributeNode = elem.getAttributeNode( "tabIndex" );
-                                       return attributeNode && attributeNode.specified
-                                               ? attributeNode.value
-                                               : elem.nodeName.match(/(button|input|object|select|textarea)/i)
-                                                       ? 0
-                                                       : elem.nodeName.match(/^(a|area)$/i) && elem.href
-                                                               ? 0
-                                                               : undefined;
-                               }
-
-                               return elem[ name ];
-                       }
-
-                       if ( !jQuery.support.style && notxml &&  name == "style" )
-                               return jQuery.attr( elem.style, "cssText", value );
-
-                       if ( set )
-                               // convert the value to a string (all browsers do this but IE) see #1070
-                               elem.setAttribute( name, "" + value );
-
-                       var attr = !jQuery.support.hrefNormalized && notxml && special
-                                       // Some attributes require a special call on IE
-                                       ? elem.getAttribute( name, 2 )
-                                       : elem.getAttribute( name );
-
-                       // Non-existent attributes return null, we normalize to undefined
-                       return attr === null ? undefined : attr;
-               }
-
-               // elem is actually elem.style ... set the style
-
-               // IE uses filters for opacity
-               if ( !jQuery.support.opacity && name == "opacity" ) {
-                       if ( set ) {
-                               // IE has trouble with opacity if it does not have layout
-                               // Force it by setting the zoom level
-                               elem.zoom = 1;
-
-                               // Set the alpha filter to set the opacity
-                               elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
-                                       (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
-                       }
-
-                       return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
-                               (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
-                               "";
-               }
-
-               name = name.replace(/-([a-z])/ig, function(all, letter){
-                       return letter.toUpperCase();
-               });
-
-               if ( set )
-                       elem[ name ] = value;
-
-               return elem[ name ];
-       },
-
-       trim: function( text ) {
-               return (text || "").replace( /^\s+|\s+$/g, "" );
-       },
-
-       makeArray: function( array ) {
-               var ret = [];
-
-               if( array != null ){
-                       var i = array.length;
-                       // The window, strings (and functions) also have 'length'
-                       if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
-                               ret[0] = array;
-                       else
-                               while( i )
-                                       ret[--i] = array[i];
-               }
-
-               return ret;
-       },
-
-       inArray: function( elem, array ) {
-               for ( var i = 0, length = array.length; i < length; i++ )
-               // Use === because on IE, window == document
-                       if ( array[ i ] === elem )
-                               return i;
-
-               return -1;
-       },
-
-       merge: function( first, second ) {
-               // We have to loop this way because IE & Opera overwrite the length
-               // expando of getElementsByTagName
-               var i = 0, elem, pos = first.length;
-               // Also, we need to make sure that the correct elements are being returned
-               // (IE returns comment nodes in a '*' query)
-               if ( !jQuery.support.getAll ) {
-                       while ( (elem = second[ i++ ]) != null )
-                               if ( elem.nodeType != 8 )
-                                       first[ pos++ ] = elem;
-
-               } else
-                       while ( (elem = second[ i++ ]) != null )
-                               first[ pos++ ] = elem;
-
-               return first;
-       },
-
-       unique: function( array ) {
-               var ret = [], done = {};
-
-               try {
-
-                       for ( var i = 0, length = array.length; i < length; i++ ) {
-                               var id = jQuery.data( array[ i ] );
-
-                               if ( !done[ id ] ) {
-                                       done[ id ] = true;
-                                       ret.push( array[ i ] );
-                               }
-                       }
-
-               } catch( e ) {
-                       ret = array;
-               }
-
-               return ret;
-       },
-
-       grep: function( elems, callback, inv ) {
-               var ret = [];
-
-               // Go through the array, only saving the items
-               // that pass the validator function
-               for ( var i = 0, length = elems.length; i < length; i++ )
-                       if ( !inv != !callback( elems[ i ], i ) )
-                               ret.push( elems[ i ] );
-
-               return ret;
-       },
-
-       map: function( elems, callback ) {
-               var ret = [];
-
-               // Go through the array, translating each of the items to their
-               // new value (or values).
-               for ( var i = 0, length = elems.length; i < length; i++ ) {
-                       var value = callback( elems[ i ], i );
-
-                       if ( value != null )
-                               ret[ ret.length ] = value;
-               }
-
-               return ret.concat.apply( [], ret );
-       }
-});
-
-// Use of jQuery.browser is deprecated.
-// It's included for backwards compatibility and plugins,
-// although they should work to migrate away.
-
-var userAgent = navigator.userAgent.toLowerCase();
-
-// Figure out what browser is being used
-jQuery.browser = {
-       version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
-       safari: /webkit/.test( userAgent ),
-       opera: /opera/.test( userAgent ),
-       msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
-       mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
-};
-
-jQuery.each({
-       parent: function(elem){return elem.parentNode;},
-       parents: function(elem){return jQuery.dir(elem,"parentNode");},
-       next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
-       prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
-       nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
-       prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
-       siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
-       children: function(elem){return jQuery.sibling(elem.firstChild);},
-       contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
-}, function(name, fn){
-       jQuery.fn[ name ] = function( selector ) {
-               var ret = jQuery.map( this, fn );
-
-               if ( selector && typeof selector == "string" )
-                       ret = jQuery.multiFilter( selector, ret );
-
-               return this.pushStack( jQuery.unique( ret ), name, selector );
-       };
-});
-
-jQuery.each({
-       appendTo: "append",
-       prependTo: "prepend",
-       insertBefore: "before",
-       insertAfter: "after",
-       replaceAll: "replaceWith"
-}, function(name, original){
-       jQuery.fn[ name ] = function( selector ) {
-               var ret = [], insert = jQuery( selector );
-
-               for ( var i = 0, l = insert.length; i < l; i++ ) {
-                       var elems = (i > 0 ? this.clone(true) : this).get();
-                       jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
-                       ret = ret.concat( elems );
-               }
-
-               return this.pushStack( ret, name, selector );
-       };
-});
-
-jQuery.each({
-       removeAttr: function( name ) {
-               jQuery.attr( this, name, "" );
-               if (this.nodeType == 1)
-                       this.removeAttribute( name );
-       },
-
-       addClass: function( classNames ) {
-               jQuery.className.add( this, classNames );
-       },
-
-       removeClass: function( classNames ) {
-               jQuery.className.remove( this, classNames );
-       },
-
-       toggleClass: function( classNames, state ) {
-               if( typeof state !== "boolean" )
-                       state = !jQuery.className.has( this, classNames );
-               jQuery.className[ state ? "add" : "remove" ]( this, classNames );
-       },
-
-       remove: function( selector ) {
-               if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
-                       // Prevent memory leaks
-                       jQuery( "*", this ).add([this]).each(function(){
-                               jQuery.event.remove(this);
-                               jQuery.removeData(this);
-                       });
-                       if (this.parentNode)
-                               this.parentNode.removeChild( this );
-               }
-       },
-
-       empty: function() {
-               // Remove element nodes and prevent memory leaks
-               jQuery(this).children().remove();
-
-               // Remove any remaining nodes
-               while ( this.firstChild )
-                       this.removeChild( this.firstChild );
-       }
-}, function(name, fn){
-       jQuery.fn[ name ] = function(){
-               return this.each( fn, arguments );
-       };
-});
-
-// Helper function used by the dimensions and offset modules
-function num(elem, prop) {
-       return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
-}
-var expando = "jQuery" + now(), uuid = 0, windowData = {};\r
-\r
-jQuery.extend({\r
-       cache: {},\r
-\r
-       data: function( elem, name, data ) {\r
-               elem = elem == window ?\r
-                       windowData :\r
-                       elem;\r
-\r
-               var id = elem[ expando ];\r
-\r
-               // Compute a unique ID for the element\r
-               if ( !id )\r
-                       id = elem[ expando ] = ++uuid;\r
-\r
-               // Only generate the data cache if we're\r
-               // trying to access or manipulate it\r
-               if ( name && !jQuery.cache[ id ] )\r
-                       jQuery.cache[ id ] = {};\r
-\r
-               // Prevent overriding the named cache with undefined values\r
-               if ( data !== undefined )\r
-                       jQuery.cache[ id ][ name ] = data;\r
-\r
-               // Return the named cache data, or the ID for the element\r
-               return name ?\r
-                       jQuery.cache[ id ][ name ] :\r
-                       id;\r
-       },\r
-\r
-       removeData: function( elem, name ) {\r
-               elem = elem == window ?\r
-                       windowData :\r
-                       elem;\r
-\r
-               var id = elem[ expando ];\r
-\r
-               // If we want to remove a specific section of the element's data\r
-               if ( name ) {\r
-                       if ( jQuery.cache[ id ] ) {\r
-                               // Remove the section of cache data\r
-                               delete jQuery.cache[ id ][ name ];\r
-\r
-                               // If we've removed all the data, remove the element's cache\r
-                               name = "";\r
-\r
-                               for ( name in jQuery.cache[ id ] )\r
-                                       break;\r
-\r
-                               if ( !name )\r
-                                       jQuery.removeData( elem );\r
-                       }\r
-\r
-               // Otherwise, we want to remove all of the element's data\r
-               } else {\r
-                       // Clean up the element expando\r
-                       try {\r
-                               delete elem[ expando ];\r
-                       } catch(e){\r
-                               // IE has trouble directly removing the expando\r
-                               // but it's ok with using removeAttribute\r
-                               if ( elem.removeAttribute )\r
-                                       elem.removeAttribute( expando );\r
-                       }\r
-\r
-                       // Completely remove the data cache\r
-                       delete jQuery.cache[ id ];\r
-               }\r
-       },\r
-       queue: function( elem, type, data ) {\r
-               if ( elem ){\r
-       \r
-                       type = (type || "fx") + "queue";\r
-       \r
-                       var q = jQuery.data( elem, type );\r
-       \r
-                       if ( !q || jQuery.isArray(data) )\r
-                               q = jQuery.data( elem, type, jQuery.makeArray(data) );\r
-                       else if( data )\r
-                               q.push( data );\r
-       \r
-               }\r
-               return q;\r
-       },\r
-\r
-       dequeue: function( elem, type ){\r
-               var queue = jQuery.queue( elem, type ),\r
-                       fn = queue.shift();\r
-               \r
-               if( !type || type === "fx" )\r
-                       fn = queue[0];\r
-                       \r
-               if( fn !== undefined )\r
-                       fn.call(elem);\r
-       }\r
-});\r
-\r
-jQuery.fn.extend({\r
-       data: function( key, value ){\r
-               var parts = key.split(".");\r
-               parts[1] = parts[1] ? "." + parts[1] : "";\r
-\r
-               if ( value === undefined ) {\r
-                       var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);\r
-\r
-                       if ( data === undefined && this.length )\r
-                               data = jQuery.data( this[0], key );\r
-\r
-                       return data === undefined && parts[1] ?\r
-                               this.data( parts[0] ) :\r
-                               data;\r
-               } else\r
-                       return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){\r
-                               jQuery.data( this, key, value );\r
-                       });\r
-       },\r
-\r
-       removeData: function( key ){\r
-               return this.each(function(){\r
-                       jQuery.removeData( this, key );\r
-               });\r
-       },\r
-       queue: function(type, data){\r
-               if ( typeof type !== "string" ) {\r
-                       data = type;\r
-                       type = "fx";\r
-               }\r
-\r
-               if ( data === undefined )\r
-                       return jQuery.queue( this[0], type );\r
-\r
-               return this.each(function(){\r
-                       var queue = jQuery.queue( this, type, data );\r
-                       \r
-                        if( type == "fx" && queue.length == 1 )\r
-                               queue[0].call(this);\r
-               });\r
-       },\r
-       dequeue: function(type){\r
-               return this.each(function(){\r
-                       jQuery.dequeue( this, type );\r
-               });\r
-       }\r
-});/*!
- * 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 chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
-       done = 0,
-       toString = Object.prototype.toString;
-
-var Sizzle = function(selector, context, results, seed) {
-       results = results || [];
-       context = context || document;
-
-       if ( context.nodeType !== 1 && context.nodeType !== 9 )
-               return [];
-       
-       if ( !selector || typeof selector !== "string" ) {
-               return results;
-       }
-
-       var parts = [], m, set, checkSet, check, mode, extra, prune = true;
-       
-       // Reset the position of the chunker regexp (start from head)
-       chunker.lastIndex = 0;
-       
-       while ( (m = chunker.exec(selector)) !== null ) {
-               parts.push( m[1] );
-               
-               if ( m[2] ) {
-                       extra = RegExp.rightContext;
-                       break;
-               }
-       }
-
-       if ( parts.length > 1 && origPOS.exec( selector ) ) {
-               if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
-                       set = posProcess( parts[0] + parts[1], context );
-               } else {
-                       set = Expr.relative[ parts[0] ] ?
-                               [ context ] :
-                               Sizzle( parts.shift(), context );
-
-                       while ( parts.length ) {
-                               selector = parts.shift();
-
-                               if ( Expr.relative[ selector ] )
-                                       selector += parts.shift();
-
-                               set = posProcess( selector, set );
-                       }
-               }
-       } else {
-               var ret = seed ?
-                       { expr: parts.pop(), set: makeArray(seed) } :
-                       Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
-               set = Sizzle.filter( ret.expr, ret.set );
-
-               if ( parts.length > 0 ) {
-                       checkSet = makeArray(set);
-               } else {
-                       prune = false;
-               }
-
-               while ( parts.length ) {
-                       var cur = parts.pop(), pop = cur;
-
-                       if ( !Expr.relative[ cur ] ) {
-                               cur = "";
-                       } else {
-                               pop = parts.pop();
-                       }
-
-                       if ( pop == null ) {
-                               pop = context;
-                       }
-
-                       Expr.relative[ cur ]( checkSet, pop, isXML(context) );
-               }
-       }
-
-       if ( !checkSet ) {
-               checkSet = set;
-       }
-
-       if ( !checkSet ) {
-               throw "Syntax error, unrecognized expression: " + (cur || selector);
-       }
-
-       if ( toString.call(checkSet) === "[object Array]" ) {
-               if ( !prune ) {
-                       results.push.apply( results, checkSet );
-               } else if ( context.nodeType === 1 ) {
-                       for ( var i = 0; checkSet[i] != null; i++ ) {
-                               if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
-                                       results.push( set[i] );
-                               }
-                       }
-               } else {
-                       for ( var i = 0; checkSet[i] != null; i++ ) {
-                               if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
-                                       results.push( set[i] );
-                               }
-                       }
-               }
-       } else {
-               makeArray( checkSet, results );
-       }
-
-       if ( extra ) {
-               Sizzle( extra, context, results, seed );
-
-               if ( sortOrder ) {
-                       hasDuplicate = false;
-                       results.sort(sortOrder);
-
-                       if ( hasDuplicate ) {
-                               for ( var i = 1; i < results.length; i++ ) {
-                                       if ( results[i] === results[i-1] ) {
-                                               results.splice(i--, 1);
-                                       }
-                               }
-                       }
-               }
-       }
-
-       return results;
-};
-
-Sizzle.matches = function(expr, set){
-       return Sizzle(expr, null, null, set);
-};
-
-Sizzle.find = function(expr, context, isXML){
-       var set, match;
-
-       if ( !expr ) {
-               return [];
-       }
-
-       for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
-               var type = Expr.order[i], match;
-               
-               if ( (match = Expr.match[ type ].exec( expr )) ) {
-                       var left = RegExp.leftContext;
-
-                       if ( left.substr( left.length - 1 ) !== "\\" ) {
-                               match[1] = (match[1] || "").replace(/\\/g, "");
-                               set = Expr.find[ type ]( match, context, isXML );
-                               if ( set != null ) {
-                                       expr = expr.replace( Expr.match[ type ], "" );
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       if ( !set ) {
-               set = context.getElementsByTagName("*");
-       }
-
-       return {set: set, expr: expr};
-};
-
-Sizzle.filter = function(expr, set, inplace, not){
-       var old = expr, result = [], curLoop = set, match, anyFound,
-               isXMLFilter = set && set[0] && isXML(set[0]);
-
-       while ( expr && set.length ) {
-               for ( var type in Expr.filter ) {
-                       if ( (match = Expr.match[ type ].exec( expr )) != null ) {
-                               var filter = Expr.filter[ type ], found, item;
-                               anyFound = false;
-
-                               if ( curLoop == result ) {
-                                       result = [];
-                               }
-
-                               if ( Expr.preFilter[ type ] ) {
-                                       match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
-
-                                       if ( !match ) {
-                                               anyFound = found = true;
-                                       } else if ( match === true ) {
-                                               continue;
-                                       }
-                               }
-
-                               if ( match ) {
-                                       for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
-                                               if ( item ) {
-                                                       found = filter( item, match, i, curLoop );
-                                                       var pass = not ^ !!found;
-
-                                                       if ( inplace && found != null ) {
-                                                               if ( pass ) {
-                                                                       anyFound = true;
-                                                               } else {
-                                                                       curLoop[i] = false;
-                                                               }
-                                                       } else if ( pass ) {
-                                                               result.push( item );
-                                                               anyFound = true;
-                                                       }
-                                               }
-                                       }
-                               }
-
-                               if ( found !== undefined ) {
-                                       if ( !inplace ) {
-                                               curLoop = result;
-                                       }
-
-                                       expr = expr.replace( Expr.match[ type ], "" );
-
-                                       if ( !anyFound ) {
-                                               return [];
-                                       }
-
-                                       break;
-                               }
-                       }
-               }
-
-               // Improper expression
-               if ( expr == old ) {
-                       if ( anyFound == null ) {
-                               throw "Syntax error, unrecognized expression: " + expr;
-                       } else {
-                               break;
-                       }
-               }
-
-               old = expr;
-       }
-
-       return curLoop;
-};
-
-var Expr = Sizzle.selectors = {
-       order: [ "ID", "NAME", "TAG" ],
-       match: {
-               ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
-               CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
-               NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
-               ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
-               TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
-               CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
-               POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
-               PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
-       },
-       attrMap: {
-               "class": "className",
-               "for": "htmlFor"
-       },
-       attrHandle: {
-               href: function(elem){
-                       return elem.getAttribute("href");
-               }
-       },
-       relative: {
-               "+": function(checkSet, part, isXML){
-                       var isPartStr = typeof part === "string",
-                               isTag = isPartStr && !/\W/.test(part),
-                               isPartStrNotTag = isPartStr && !isTag;
-
-                       if ( isTag && !isXML ) {
-                               part = part.toUpperCase();
-                       }
-
-                       for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
-                               if ( (elem = checkSet[i]) ) {
-                                       while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
-
-                                       checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
-                                               elem || false :
-                                               elem === part;
-                               }
-                       }
-
-                       if ( isPartStrNotTag ) {
-                               Sizzle.filter( part, checkSet, true );
-                       }
-               },
-               ">": function(checkSet, part, isXML){
-                       var isPartStr = typeof part === "string";
-
-                       if ( isPartStr && !/\W/.test(part) ) {
-                               part = isXML ? part : part.toUpperCase();
-
-                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
-                                       var elem = checkSet[i];
-                                       if ( elem ) {
-                                               var parent = elem.parentNode;
-                                               checkSet[i] = parent.nodeName === part ? parent : false;
-                                       }
-                               }
-                       } else {
-                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
-                                       var elem = checkSet[i];
-                                       if ( elem ) {
-                                               checkSet[i] = isPartStr ?
-                                                       elem.parentNode :
-                                                       elem.parentNode === part;
-                                       }
-                               }
-
-                               if ( isPartStr ) {
-                                       Sizzle.filter( part, checkSet, true );
-                               }
-                       }
-               },
-               "": function(checkSet, part, isXML){
-                       var doneName = done++, checkFn = dirCheck;
-
-                       if ( !part.match(/\W/) ) {
-                               var nodeCheck = part = isXML ? part : part.toUpperCase();
-                               checkFn = dirNodeCheck;
-                       }
-
-                       checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
-               },
-               "~": function(checkSet, part, isXML){
-                       var doneName = done++, checkFn = dirCheck;
-
-                       if ( typeof part === "string" && !part.match(/\W/) ) {
-                               var nodeCheck = part = isXML ? part : part.toUpperCase();
-                               checkFn = dirNodeCheck;
-                       }
-
-                       checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
-               }
-       },
-       find: {
-               ID: function(match, context, isXML){
-                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
-                               var m = context.getElementById(match[1]);
-                               return m ? [m] : [];
-                       }
-               },
-               NAME: function(match, context, isXML){
-                       if ( typeof context.getElementsByName !== "undefined" ) {
-                               var ret = [], results = context.getElementsByName(match[1]);
-
-                               for ( var i = 0, l = results.length; i < l; i++ ) {
-                                       if ( results[i].getAttribute("name") === match[1] ) {
-                                               ret.push( results[i] );
-                                       }
-                               }
-
-                               return ret.length === 0 ? null : ret;
-                       }
-               },
-               TAG: function(match, context){
-                       return context.getElementsByTagName(match[1]);
-               }
-       },
-       preFilter: {
-               CLASS: function(match, curLoop, inplace, result, not, isXML){
-                       match = " " + match[1].replace(/\\/g, "") + " ";
-
-                       if ( isXML ) {
-                               return match;
-                       }
-
-                       for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
-                               if ( elem ) {
-                                       if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
-                                               if ( !inplace )
-                                                       result.push( elem );
-                                       } else if ( inplace ) {
-                                               curLoop[i] = false;
-                                       }
-                               }
-                       }
-
-                       return false;
-               },
-               ID: function(match){
-                       return match[1].replace(/\\/g, "");
-               },
-               TAG: function(match, curLoop){
-                       for ( var i = 0; curLoop[i] === false; i++ ){}
-                       return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
-               },
-               CHILD: function(match){
-                       if ( match[1] == "nth" ) {
-                               // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
-                               var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
-                                       match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
-                                       !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
-
-                               // calculate the numbers (first)n+(last) including if they are negative
-                               match[2] = (test[1] + (test[2] || 1)) - 0;
-                               match[3] = test[3] - 0;
-                       }
-
-                       // TODO: Move to normal caching system
-                       match[0] = done++;
-
-                       return match;
-               },
-               ATTR: function(match, curLoop, inplace, result, not, isXML){
-                       var name = match[1].replace(/\\/g, "");
-                       
-                       if ( !isXML && Expr.attrMap[name] ) {
-                               match[1] = Expr.attrMap[name];
-                       }
-
-                       if ( match[2] === "~=" ) {
-                               match[4] = " " + match[4] + " ";
-                       }
-
-                       return match;
-               },
-               PSEUDO: function(match, curLoop, inplace, result, not){
-                       if ( match[1] === "not" ) {
-                               // If we're dealing with a complex expression, or a simple one
-                               if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
-                                       match[3] = Sizzle(match[3], null, null, curLoop);
-                               } else {
-                                       var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
-                                       if ( !inplace ) {
-                                               result.push.apply( result, ret );
-                                       }
-                                       return false;
-                               }
-                       } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
-                               return true;
-                       }
-                       
-                       return match;
-               },
-               POS: function(match){
-                       match.unshift( true );
-                       return match;
-               }
-       },
-       filters: {
-               enabled: function(elem){
-                       return elem.disabled === false && elem.type !== "hidden";
-               },
-               disabled: function(elem){
-                       return elem.disabled === true;
-               },
-               checked: function(elem){
-                       return elem.checked === true;
-               },
-               selected: function(elem){
-                       // Accessing this property makes selected-by-default
-                       // options in Safari work properly
-                       elem.parentNode.selectedIndex;
-                       return elem.selected === true;
-               },
-               parent: function(elem){
-                       return !!elem.firstChild;
-               },
-               empty: function(elem){
-                       return !elem.firstChild;
-               },
-               has: function(elem, i, match){
-                       return !!Sizzle( match[3], elem ).length;
-               },
-               header: function(elem){
-                       return /h\d/i.test( elem.nodeName );
-               },
-               text: function(elem){
-                       return "text" === elem.type;
-               },
-               radio: function(elem){
-                       return "radio" === elem.type;
-               },
-               checkbox: function(elem){
-                       return "checkbox" === elem.type;
-               },
-               file: function(elem){
-                       return "file" === elem.type;
-               },
-               password: function(elem){
-                       return "password" === elem.type;
-               },
-               submit: function(elem){
-                       return "submit" === elem.type;
-               },
-               image: function(elem){
-                       return "image" === elem.type;
-               },
-               reset: function(elem){
-                       return "reset" === elem.type;
-               },
-               button: function(elem){
-                       return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
-               },
-               input: function(elem){
-                       return /input|select|textarea|button/i.test(elem.nodeName);
-               }
-       },
-       setFilters: {
-               first: function(elem, i){
-                       return i === 0;
-               },
-               last: function(elem, i, match, array){
-                       return i === array.length - 1;
-               },
-               even: function(elem, i){
-                       return i % 2 === 0;
-               },
-               odd: function(elem, i){
-                       return i % 2 === 1;
-               },
-               lt: function(elem, i, match){
-                       return i < match[3] - 0;
-               },
-               gt: function(elem, i, match){
-                       return i > match[3] - 0;
-               },
-               nth: function(elem, i, match){
-                       return match[3] - 0 == i;
-               },
-               eq: function(elem, i, match){
-                       return match[3] - 0 == i;
-               }
-       },
-       filter: {
-               PSEUDO: function(elem, match, i, array){
-                       var name = match[1], filter = Expr.filters[ name ];
-
-                       if ( filter ) {
-                               return filter( elem, i, match, array );
-                       } else if ( name === "contains" ) {
-                               return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
-                       } else if ( name === "not" ) {
-                               var not = match[3];
-
-                               for ( var i = 0, l = not.length; i < l; i++ ) {
-                                       if ( not[i] === elem ) {
-                                               return false;
-                                       }
-                               }
-
-                               return true;
-                       }
-               },
-               CHILD: function(elem, match){
-                       var type = match[1], node = elem;
-                       switch (type) {
-                               case 'only':
-                               case 'first':
-                                       while (node = node.previousSibling)  {
-                                               if ( node.nodeType === 1 ) return false;
-                                       }
-                                       if ( type == 'first') return true;
-                                       node = elem;
-                               case 'last':
-                                       while (node = node.nextSibling)  {
-                                               if ( node.nodeType === 1 ) return false;
-                                       }
-                                       return true;
-                               case 'nth':
-                                       var first = match[2], last = match[3];
-
-                                       if ( first == 1 && last == 0 ) {
-                                               return true;
-                                       }
-                                       
-                                       var doneName = match[0],
-                                               parent = elem.parentNode;
-       
-                                       if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
-                                               var count = 0;
-                                               for ( node = parent.firstChild; node; node = node.nextSibling ) {
-                                                       if ( node.nodeType === 1 ) {
-                                                               node.nodeIndex = ++count;
-                                                       }
-                                               } 
-                                               parent.sizcache = doneName;
-                                       }
-                                       
-                                       var diff = elem.nodeIndex - last;
-                                       if ( first == 0 ) {
-                                               return diff == 0;
-                                       } else {
-                                               return ( diff % first == 0 && diff / first >= 0 );
-                                       }
-                       }
-               },
-               ID: function(elem, match){
-                       return elem.nodeType === 1 && elem.getAttribute("id") === match;
-               },
-               TAG: function(elem, match){
-                       return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
-               },
-               CLASS: function(elem, match){
-                       return (" " + (elem.className || elem.getAttribute("class")) + " ")
-                               .indexOf( match ) > -1;
-               },
-               ATTR: function(elem, match){
-                       var name = match[1],
-                               result = Expr.attrHandle[ name ] ?
-                                       Expr.attrHandle[ name ]( elem ) :
-                                       elem[ name ] != null ?
-                                               elem[ name ] :
-                                               elem.getAttribute( name ),
-                               value = result + "",
-                               type = match[2],
-                               check = match[4];
-
-                       return result == null ?
-                               type === "!=" :
-                               type === "=" ?
-                               value === check :
-                               type === "*=" ?
-                               value.indexOf(check) >= 0 :
-                               type === "~=" ?
-                               (" " + value + " ").indexOf(check) >= 0 :
-                               !check ?
-                               value && result !== false :
-                               type === "!=" ?
-                               value != check :
-                               type === "^=" ?
-                               value.indexOf(check) === 0 :
-                               type === "$=" ?
-                               value.substr(value.length - check.length) === check :
-                               type === "|=" ?
-                               value === check || value.substr(0, check.length + 1) === check + "-" :
-                               false;
-               },
-               POS: function(elem, match, i, array){
-                       var name = match[2], filter = Expr.setFilters[ name ];
-
-                       if ( filter ) {
-                               return filter( elem, i, match, array );
-                       }
-               }
-       }
-};
-
-var origPOS = Expr.match.POS;
-
-for ( var type in Expr.match ) {
-       Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
-}
-
-var makeArray = function(array, results) {
-       array = Array.prototype.slice.call( array );
-
-       if ( results ) {
-               results.push.apply( results, array );
-               return results;
-       }
-       
-       return array;
-};
-
-// Perform a simple check to determine if the browser is capable of
-// converting a NodeList to an array using builtin methods.
-try {
-       Array.prototype.slice.call( document.documentElement.childNodes );
-
-// Provide a fallback method if it does not work
-} catch(e){
-       makeArray = function(array, results) {
-               var ret = results || [];
-
-               if ( toString.call(array) === "[object Array]" ) {
-                       Array.prototype.push.apply( ret, array );
-               } else {
-                       if ( typeof array.length === "number" ) {
-                               for ( var i = 0, l = array.length; i < l; i++ ) {
-                                       ret.push( array[i] );
-                               }
-                       } else {
-                               for ( var i = 0; array[i]; i++ ) {
-                                       ret.push( array[i] );
-                               }
-                       }
-               }
-
-               return ret;
-       };
-}
-
-var sortOrder;
-
-if ( document.documentElement.compareDocumentPosition ) {
-       sortOrder = function( a, b ) {
-               var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
-               if ( ret === 0 ) {
-                       hasDuplicate = true;
-               }
-               return ret;
-       };
-} else if ( "sourceIndex" in document.documentElement ) {
-       sortOrder = function( a, b ) {
-               var ret = a.sourceIndex - b.sourceIndex;
-               if ( ret === 0 ) {
-                       hasDuplicate = true;
-               }
-               return ret;
-       };
-} else if ( document.createRange ) {
-       sortOrder = function( a, b ) {
-               var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
-               aRange.selectNode(a);
-               aRange.collapse(true);
-               bRange.selectNode(b);
-               bRange.collapse(true);
-               var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
-               if ( ret === 0 ) {
-                       hasDuplicate = true;
-               }
-               return ret;
-       };
-}
-
-// Check to see if the browser returns elements by name when
-// querying by getElementById (and provide a workaround)
-(function(){
-       // We're going to inject a fake input element with a specified name
-       var form = document.createElement("form"),
-               id = "script" + (new Date).getTime();
-       form.innerHTML = "<input name='" + id + "'/>";
-
-       // Inject it into the root element, check its status, and remove it quickly
-       var root = document.documentElement;
-       root.insertBefore( form, root.firstChild );
-
-       // The workaround has to do additional checks after a getElementById
-       // Which slows things down for other browsers (hence the branching)
-       if ( !!document.getElementById( id ) ) {
-               Expr.find.ID = function(match, context, isXML){
-                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
-                               var m = context.getElementById(match[1]);
-                               return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
-                       }
-               };
-
-               Expr.filter.ID = function(elem, match){
-                       var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
-                       return elem.nodeType === 1 && node && node.nodeValue === match;
-               };
-       }
-
-       root.removeChild( form );
-})();
-
-(function(){
-       // Check to see if the browser returns only elements
-       // when doing getElementsByTagName("*")
-
-       // Create a fake element
-       var div = document.createElement("div");
-       div.appendChild( document.createComment("") );
-
-       // Make sure no comments are found
-       if ( div.getElementsByTagName("*").length > 0 ) {
-               Expr.find.TAG = function(match, context){
-                       var results = context.getElementsByTagName(match[1]);
-
-                       // Filter out possible comments
-                       if ( match[1] === "*" ) {
-                               var tmp = [];
-
-                               for ( var i = 0; results[i]; i++ ) {
-                                       if ( results[i].nodeType === 1 ) {
-                                               tmp.push( results[i] );
-                                       }
-                               }
-
-                               results = tmp;
-                       }
-
-                       return results;
-               };
-       }
-
-       // Check to see if an attribute returns normalized href attributes
-       div.innerHTML = "<a href='#'></a>";
-       if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
-                       div.firstChild.getAttribute("href") !== "#" ) {
-               Expr.attrHandle.href = function(elem){
-                       return elem.getAttribute("href", 2);
-               };
-       }
-})();
-
-if ( document.querySelectorAll ) (function(){
-       var oldSizzle = Sizzle, div = document.createElement("div");
-       div.innerHTML = "<p class='TEST'></p>";
-
-       // Safari can't handle uppercase or unicode characters when
-       // in quirks mode.
-       if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
-               return;
-       }
-       
-       Sizzle = function(query, context, extra, seed){
-               context = context || document;
-
-               // Only use querySelectorAll on non-XML documents
-               // (ID selectors don't work in non-HTML documents)
-               if ( !seed && context.nodeType === 9 && !isXML(context) ) {
-                       try {
-                               return makeArray( context.querySelectorAll(query), extra );
-                       } catch(e){}
-               }
-               
-               return oldSizzle(query, context, extra, seed);
-       };
-
-       Sizzle.find = oldSizzle.find;
-       Sizzle.filter = oldSizzle.filter;
-       Sizzle.selectors = oldSizzle.selectors;
-       Sizzle.matches = oldSizzle.matches;
-})();
-
-if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
-       var div = document.createElement("div");
-       div.innerHTML = "<div class='test e'></div><div class='test'></div>";
-
-       // Opera can't find a second classname (in 9.6)
-       if ( div.getElementsByClassName("e").length === 0 )
-               return;
-
-       // Safari caches class attributes, doesn't catch changes (in 3.2)
-       div.lastChild.className = "e";
-
-       if ( div.getElementsByClassName("e").length === 1 )
-               return;
-
-       Expr.order.splice(1, 0, "CLASS");
-       Expr.find.CLASS = function(match, context, isXML) {
-               if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
-                       return context.getElementsByClassName(match[1]);
-               }
-       };
-})();
-
-function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
-       var sibDir = dir == "previousSibling" && !isXML;
-       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
-               var elem = checkSet[i];
-               if ( elem ) {
-                       if ( sibDir && elem.nodeType === 1 ){
-                               elem.sizcache = doneName;
-                               elem.sizset = i;
-                       }
-                       elem = elem[dir];
-                       var match = false;
-
-                       while ( elem ) {
-                               if ( elem.sizcache === doneName ) {
-                                       match = checkSet[elem.sizset];
-                                       break;
-                               }
-
-                               if ( elem.nodeType === 1 && !isXML ){
-                                       elem.sizcache = doneName;
-                                       elem.sizset = i;
-                               }
-
-                               if ( elem.nodeName === cur ) {
-                                       match = elem;
-                                       break;
-                               }
-
-                               elem = elem[dir];
-                       }
-
-                       checkSet[i] = match;
-               }
-       }
-}
-
-function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
-       var sibDir = dir == "previousSibling" && !isXML;
-       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
-               var elem = checkSet[i];
-               if ( elem ) {
-                       if ( sibDir && elem.nodeType === 1 ) {
-                               elem.sizcache = doneName;
-                               elem.sizset = i;
-                       }
-                       elem = elem[dir];
-                       var match = false;
-
-                       while ( elem ) {
-                               if ( elem.sizcache === doneName ) {
-                                       match = checkSet[elem.sizset];
-                                       break;
-                               }
-
-                               if ( elem.nodeType === 1 ) {
-                                       if ( !isXML ) {
-                                               elem.sizcache = doneName;
-                                               elem.sizset = i;
-                                       }
-                                       if ( typeof cur !== "string" ) {
-                                               if ( elem === cur ) {
-                                                       match = true;
-                                                       break;
-                                               }
-
-                                       } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
-                                               match = elem;
-                                               break;
-                                       }
-                               }
-
-                               elem = elem[dir];
-                       }
-
-                       checkSet[i] = match;
-               }
-       }
-}
-
-var contains = document.compareDocumentPosition ?  function(a, b){
-       return a.compareDocumentPosition(b) & 16;
-} : function(a, b){
-       return a !== b && (a.contains ? a.contains(b) : true);
-};
-
-var isXML = function(elem){
-       return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
-               !!elem.ownerDocument && isXML( elem.ownerDocument );
-};
-
-var posProcess = function(selector, context){
-       var tmpSet = [], later = "", match,
-               root = context.nodeType ? [context] : context;
-
-       // Position selectors must be done after the filter
-       // And so must :not(positional) so we move all PSEUDOs to the end
-       while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
-               later += match[0];
-               selector = selector.replace( Expr.match.PSEUDO, "" );
-       }
-
-       selector = Expr.relative[selector] ? selector + "*" : selector;
-
-       for ( var i = 0, l = root.length; i < l; i++ ) {
-               Sizzle( selector, root[i], tmpSet );
-       }
-
-       return Sizzle.filter( later, tmpSet );
-};
-
-// EXPOSE
-jQuery.find = Sizzle;
-jQuery.filter = Sizzle.filter;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.filters;
-
-Sizzle.selectors.filters.hidden = function(elem){
-       return elem.offsetWidth === 0 || elem.offsetHeight === 0;
-};
-
-Sizzle.selectors.filters.visible = function(elem){
-       return elem.offsetWidth > 0 || elem.offsetHeight > 0;
-};
-
-Sizzle.selectors.filters.animated = function(elem){
-       return jQuery.grep(jQuery.timers, function(fn){
-               return elem === fn.elem;
-       }).length;
-};
-
-jQuery.multiFilter = function( expr, elems, not ) {
-       if ( not ) {
-               expr = ":not(" + expr + ")";
-       }
-
-       return Sizzle.matches(expr, elems);
-};
-
-jQuery.dir = function( elem, dir ){
-       var matched = [], cur = elem[dir];
-       while ( cur && cur != document ) {
-               if ( cur.nodeType == 1 )
-                       matched.push( cur );
-               cur = cur[dir];
-       }
-       return matched;
-};
-
-jQuery.nth = function(cur, result, dir, elem){
-       result = result || 1;
-       var num = 0;
-
-       for ( ; cur; cur = cur[dir] )
-               if ( cur.nodeType == 1 && ++num == result )
-                       break;
-
-       return cur;
-};
-
-jQuery.sibling = function(n, elem){
-       var r = [];
-
-       for ( ; n; n = n.nextSibling ) {
-               if ( n.nodeType == 1 && n != elem )
-                       r.push( n );
-       }
-
-       return r;
-};
-
-return;
-
-window.Sizzle = Sizzle;
-
-})();
-/*
- * A number of helper functions used for managing events.
- * Many of the ideas behind this code originated from
- * Dean Edwards' addEvent library.
- */
-jQuery.event = {
-
-       // Bind an event to an element
-       // Original by Dean Edwards
-       add: function(elem, types, handler, data) {
-               if ( elem.nodeType == 3 || elem.nodeType == 8 )
-                       return;
-
-               // For whatever reason, IE has trouble passing the window object
-               // around, causing it to be cloned in the process
-               if ( elem.setInterval && elem != window )
-                       elem = window;
-
-               // Make sure that the function being executed has a unique ID
-               if ( !handler.guid )
-                       handler.guid = this.guid++;
-
-               // if data is passed, bind to handler
-               if ( data !== undefined ) {
-                       // Create temporary function pointer to original handler
-                       var fn = handler;
-
-                       // Create unique handler function, wrapped around original handler
-                       handler = this.proxy( fn );
-
-                       // Store data in unique handler
-                       handler.data = data;
-               }
-
-               // Init the element's event structure
-               var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
-                       handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
-                               // Handle the second event of a trigger and when
-                               // an event is called after a page has unloaded
-                               return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
-                                       jQuery.event.handle.apply(arguments.callee.elem, arguments) :
-                                       undefined;
-                       });
-               // Add elem as a property of the handle function
-               // This is to prevent a memory leak with non-native
-               // event in IE.
-               handle.elem = elem;
-
-               // Handle multiple events separated by a space
-               // jQuery(...).bind("mouseover mouseout", fn);
-               jQuery.each(types.split(/\s+/), function(index, type) {
-                       // Namespaced event handlers
-                       var namespaces = type.split(".");
-                       type = namespaces.shift();
-                       handler.type = namespaces.slice().sort().join(".");
-
-                       // Get the current list of functions bound to this event
-                       var handlers = events[type];
-                       
-                       if ( jQuery.event.specialAll[type] )
-                               jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
-
-                       // Init the event handler queue
-                       if (!handlers) {
-                               handlers = events[type] = {};
-
-                               // Check for a special event handler
-                               // Only use addEventListener/attachEvent if the special
-                               // events handler returns false
-                               if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
-                                       // Bind the global event handler to the element
-                                       if (elem.addEventListener)
-                                               elem.addEventListener(type, handle, false);
-                                       else if (elem.attachEvent)
-                                               elem.attachEvent("on" + type, handle);
-                               }
-                       }
-
-                       // Add the function to the element's handler list
-                       handlers[handler.guid] = handler;
-
-                       // Keep track of which events have been used, for global triggering
-                       jQuery.event.global[type] = true;
-               });
-
-               // Nullify elem to prevent memory leaks in IE
-               elem = null;
-       },
-
-       guid: 1,
-       global: {},
-
-       // Detach an event or set of events from an element
-       remove: function(elem, types, handler) {
-               // don't do events on text and comment nodes
-               if ( elem.nodeType == 3 || elem.nodeType == 8 )
-                       return;
-
-               var events = jQuery.data(elem, "events"), ret, index;
-
-               if ( events ) {
-                       // Unbind all events for the element
-                       if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
-                               for ( var type in events )
-                                       this.remove( elem, type + (types || "") );
-                       else {
-                               // types is actually an event object here
-                               if ( types.type ) {
-                                       handler = types.handler;
-                                       types = types.type;
-                               }
-
-                               // Handle multiple events seperated by a space
-                               // jQuery(...).unbind("mouseover mouseout", fn);
-                               jQuery.each(types.split(/\s+/), function(index, type){
-                                       // Namespaced event handlers
-                                       var namespaces = type.split(".");
-                                       type = namespaces.shift();
-                                       var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
-
-                                       if ( events[type] ) {
-                                               // remove the given handler for the given type
-                                               if ( handler )
-                                                       delete events[type][handler.guid];
-
-                                               // remove all handlers for the given type
-                                               else
-                                                       for ( var handle in events[type] )
-                                                               // Handle the removal of namespaced events
-                                                               if ( namespace.test(events[type][handle].type) )
-                                                                       delete events[type][handle];
-                                                                       
-                                               if ( jQuery.event.specialAll[type] )
-                                                       jQuery.event.specialAll[type].teardown.call(elem, namespaces);
-
-                                               // remove generic event handler if no more handlers exist
-                                               for ( ret in events[type] ) break;
-                                               if ( !ret ) {
-                                                       if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
-                                                               if (elem.removeEventListener)
-                                                                       elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
-                                                               else if (elem.detachEvent)
-                                                                       elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
-                                                       }
-                                                       ret = null;
-                                                       delete events[type];
-                                               }
-                                       }
-                               });
-                       }
-
-                       // Remove the expando if it's no longer used
-                       for ( ret in events ) break;
-                       if ( !ret ) {
-                               var handle = jQuery.data( elem, "handle" );
-                               if ( handle ) handle.elem = null;
-                               jQuery.removeData( elem, "events" );
-                               jQuery.removeData( elem, "handle" );
-                       }
-               }
-       },
-
-       // bubbling is internal
-       trigger: function( event, data, elem, bubbling ) {
-               // Event object or event type
-               var type = event.type || event;
-
-               if( !bubbling ){
-                       event = typeof event === "object" ?
-                               // jQuery.Event object
-                               event[expando] ? event :
-                               // Object literal
-                               jQuery.extend( jQuery.Event(type), event ) :
-                               // Just the event type (string)
-                               jQuery.Event(type);
-
-                       if ( type.indexOf("!") >= 0 ) {
-                               event.type = type = type.slice(0, -1);
-                               event.exclusive = true;
-                       }
-
-                       // Handle a global trigger
-                       if ( !elem ) {
-                               // Don't bubble custom events when global (to avoid too much overhead)
-                               event.stopPropagation();
-                               // Only trigger if we've ever bound an event for it
-                               if ( this.global[type] )
-                                       jQuery.each( jQuery.cache, function(){
-                                               if ( this.events && this.events[type] )
-                                                       jQuery.event.trigger( event, data, this.handle.elem );
-                                       });
-                       }
-
-                       // Handle triggering a single element
-
-                       // don't do events on text and comment nodes
-                       if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
-                               return undefined;
-                       
-                       // Clean up in case it is reused
-                       event.result = undefined;
-                       event.target = elem;
-                       
-                       // Clone the incoming data, if any
-                       data = jQuery.makeArray(data);
-                       data.unshift( event );
-               }
-
-               event.currentTarget = elem;
-
-               // Trigger the event, it is assumed that "handle" is a function
-               var handle = jQuery.data(elem, "handle");
-               if ( handle )
-                       handle.apply( elem, data );
-
-               // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
-               if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
-                       event.result = false;
-
-               // Trigger the native events (except for clicks on links)
-               if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
-                       this.triggered = true;
-                       try {
-                               elem[ type ]();
-                       // prevent IE from throwing an error for some hidden elements
-                       } catch (e) {}
-               }
-
-               this.triggered = false;
-
-               if ( !event.isPropagationStopped() ) {
-                       var parent = elem.parentNode || elem.ownerDocument;
-                       if ( parent )
-                               jQuery.event.trigger(event, data, parent, true);
-               }
-       },
-
-       handle: function(event) {
-               // returned undefined or false
-               var all, handlers;
-
-               event = arguments[0] = jQuery.event.fix( event || window.event );
-               event.currentTarget = this;
-               
-               // Namespaced event handlers
-               var namespaces = event.type.split(".");
-               event.type = namespaces.shift();
-
-               // Cache this now, all = true means, any handler
-               all = !namespaces.length && !event.exclusive;
-               
-               var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
-
-               handlers = ( jQuery.data(this, "events") || {} )[event.type];
-
-               for ( var j in handlers ) {
-                       var handler = handlers[j];
-
-                       // Filter the functions by class
-                       if ( all || namespace.test(handler.type) ) {
-                               // Pass in a reference to the handler function itself
-                               // So that we can later remove it
-                               event.handler = handler;
-                               event.data = handler.data;
-
-                               var ret = handler.apply(this, arguments);
-
-                               if( ret !== undefined ){
-                                       event.result = ret;
-                                       if ( ret === false ) {
-                                               event.preventDefault();
-                                               event.stopPropagation();
-                                       }
-                               }
-
-                               if( event.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(event) {
-               if ( event[expando] )
-                       return event;
-
-               // store a copy of the original event object
-               // and "clone" to set read-only properties
-               var originalEvent = event;
-               event = jQuery.Event( originalEvent );
-
-               for ( var i = this.props.length, prop; i; ){
-                       prop = this.props[ --i ];
-                       event[ prop ] = originalEvent[ prop ];
-               }
-
-               // Fix target property, if necessary
-               if ( !event.target )
-                       event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
-
-               // check if target is a textnode (safari)
-               if ( event.target.nodeType == 3 )
-                       event.target = event.target.parentNode;
-
-               // Add relatedTarget, if necessary
-               if ( !event.relatedTarget && event.fromElement )
-                       event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
-
-               // Calculate pageX/Y if missing and clientX/Y available
-               if ( event.pageX == null && event.clientX != null ) {
-                       var doc = document.documentElement, body = document.body;
-                       event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
-                       event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
-               }
-
-               // Add which for key events
-               if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
-                       event.which = event.charCode || event.keyCode;
-
-               // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
-               if ( !event.metaKey && event.ctrlKey )
-                       event.metaKey = event.ctrlKey;
-
-               // Add which for click: 1 == left; 2 == middle; 3 == right
-               // Note: button is not normalized, so don't use it
-               if ( !event.which && event.button )
-                       event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
-
-               return event;
-       },
-
-       proxy: function( fn, proxy ){
-               proxy = proxy || function(){ return fn.apply(this, arguments); };
-               // Set the guid of unique handler to the same of original handler, so it can be removed
-               proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
-               // So proxy can be declared as an argument
-               return proxy;
-       },
-
-       special: {
-               ready: {
-                       // Make sure the ready event is setup
-                       setup: bindReady,
-                       teardown: function() {}
-               }
-       },
-       
-       specialAll: {
-               live: {
-                       setup: function( selector, namespaces ){
-                               jQuery.event.add( this, namespaces[0], liveHandler );
-                       },
-                       teardown:  function( namespaces ){
-                               if ( namespaces.length ) {
-                                       var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
-                                       
-                                       jQuery.each( (jQuery.data(this, "events").live || {}), function(){
-                                               if ( name.test(this.type) )
-                                                       remove++;
-                                       });
-                                       
-                                       if ( remove < 1 )
-                                               jQuery.event.remove( this, namespaces[0], liveHandler );
-                               }
-                       }
-               }
-       }
-};
-
-jQuery.Event = function( src ){
-       // Allow instantiation without the 'new' keyword
-       if( !this.preventDefault )
-               return new jQuery.Event(src);
-       
-       // Event object
-       if( src && src.type ){
-               this.originalEvent = src;
-               this.type = src.type;
-       // Event type
-       }else
-               this.type = src;
-
-       // timeStamp is buggy for some events on Firefox(#3843)
-       // So we won't rely on the native value
-       this.timeStamp = now();
-       
-       // Mark it as fixed
-       this[expando] = true;
-};
-
-function returnFalse(){
-       return false;
-}
-function returnTrue(){
-       return true;
-}
-
-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-jQuery.Event.prototype = {
-       preventDefault: function() {
-               this.isDefaultPrevented = returnTrue;
-
-               var e = this.originalEvent;
-               if( !e )
-                       return;
-               // if preventDefault exists run it on the original event
-               if (e.preventDefault)
-                       e.preventDefault();
-               // otherwise set the returnValue property of the original event to false (IE)
-               e.returnValue = false;
-       },
-       stopPropagation: function() {
-               this.isPropagationStopped = returnTrue;
-
-               var e = this.originalEvent;
-               if( !e )
-                       return;
-               // if stopPropagation exists run it on the original event
-               if (e.stopPropagation)
-                       e.stopPropagation();
-               // otherwise set the cancelBubble property of the original event to true (IE)
-               e.cancelBubble = true;
-       },
-       stopImmediatePropagation:function(){
-               this.isImmediatePropagationStopped = returnTrue;
-               this.stopPropagation();
-       },
-       isDefaultPrevented: returnFalse,
-       isPropagationStopped: returnFalse,
-       isImmediatePropagationStopped: returnFalse
-};
-// Checks if an event happened on an element within another element
-// Used in jQuery.event.special.mouseenter and mouseleave handlers
-var withinElement = function(event) {
-       // Check if mouse(over|out) are still within the same parent element
-       var parent = event.relatedTarget;
-       // Traverse up the tree
-       while ( parent && parent != this )
-               try { parent = parent.parentNode; }
-               catch(e) { parent = this; }
-       
-       if( parent != this ){
-               // set the correct event type
-               event.type = event.data;
-               // handle event if we actually just moused on to a non sub-element
-               jQuery.event.handle.apply( this, arguments );
-       }
-};
-       
-jQuery.each({ 
-       mouseover: 'mouseenter', 
-       mouseout: 'mouseleave'
-}, function( orig, fix ){
-       jQuery.event.special[ fix ] = {
-               setup: function(){
-                       jQuery.event.add( this, orig, withinElement, fix );
-               },
-               teardown: function(){
-                       jQuery.event.remove( this, orig, withinElement );
-               }
-       };                         
-});
-
-jQuery.fn.extend({
-       bind: function( type, data, fn ) {
-               return type == "unload" ? this.one(type, data, fn) : this.each(function(){
-                       jQuery.event.add( this, type, fn || data, fn && data );
-               });
-       },
-
-       one: function( type, data, fn ) {
-               var one = jQuery.event.proxy( fn || data, function(event) {
-                       jQuery(this).unbind(event, one);
-                       return (fn || data).apply( this, arguments );
-               });
-               return this.each(function(){
-                       jQuery.event.add( this, type, one, fn && data);
-               });
-       },
-
-       unbind: function( type, fn ) {
-               return this.each(function(){
-                       jQuery.event.remove( this, type, fn );
-               });
-       },
-
-       trigger: function( type, data ) {
-               return this.each(function(){
-                       jQuery.event.trigger( type, data, this );
-               });
-       },
-
-       triggerHandler: function( type, data ) {
-               if( this[0] ){
-                       var event = jQuery.Event(type);
-                       event.preventDefault();
-                       event.stopPropagation();
-                       jQuery.event.trigger( event, data, this[0] );
-                       return event.result;
-               }               
-       },
-
-       toggle: function( fn ) {
-               // Save reference to arguments for access in closure
-               var args = arguments, i = 1;
-
-               // link all the functions, so any of them can unbind this click handler
-               while( i < args.length )
-                       jQuery.event.proxy( fn, args[i++] );
-
-               return this.click( jQuery.event.proxy( fn, function(event) {
-                       // Figure out which function to execute
-                       this.lastToggle = ( this.lastToggle || 0 ) % i;
-
-                       // Make sure that clicks stop
-                       event.preventDefault();
-
-                       // and execute the function
-                       return args[ this.lastToggle++ ].apply( this, arguments ) || false;
-               }));
-       },
-
-       hover: function(fnOver, fnOut) {
-               return this.mouseenter(fnOver).mouseleave(fnOut);
-       },
-
-       ready: function(fn) {
-               // Attach the listeners
-               bindReady();
-
-               // If the DOM is already ready
-               if ( jQuery.isReady )
-                       // Execute the function immediately
-                       fn.call( document, jQuery );
-
-               // Otherwise, remember the function for later
-               else
-                       // Add the function to the wait list
-                       jQuery.readyList.push( fn );
-
-               return this;
-       },
-       
-       live: function( type, fn ){
-               var proxy = jQuery.event.proxy( fn );
-               proxy.guid += this.selector + type;
-
-               jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
-
-               return this;
-       },
-       
-       die: function( type, fn ){
-               jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
-               return this;
-       }
-});
-
-function liveHandler( event ){
-       var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
-               stop = true,
-               elems = [];
-
-       jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
-               if ( check.test(fn.type) ) {
-                       var elem = jQuery(event.target).closest(fn.data)[0];
-                       if ( elem )
-                               elems.push({ elem: elem, fn: fn });
-               }
-       });
-
-       elems.sort(function(a,b) {
-               return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
-       });
-       
-       jQuery.each(elems, function(){
-               if ( this.fn.call(this.elem, event, this.fn.data) === false )
-                       return (stop = false);
-       });
-
-       return stop;
-}
-
-function liveConvert(type, selector){
-       return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
-}
-
-jQuery.extend({
-       isReady: false,
-       readyList: [],
-       // Handle when the DOM is ready
-       ready: function() {
-               // Make sure that the DOM is not already loaded
-               if ( !jQuery.isReady ) {
-                       // Remember that the DOM is ready
-                       jQuery.isReady = true;
-
-                       // If there are functions bound, to execute
-                       if ( jQuery.readyList ) {
-                               // Execute all of them
-                               jQuery.each( jQuery.readyList, function(){
-                                       this.call( document, jQuery );
-                               });
-
-                               // Reset the list of functions
-                               jQuery.readyList = null;
-                       }
-
-                       // Trigger any bound ready events
-                       jQuery(document).triggerHandler("ready");
-               }
-       }
-});
-
-var readyBound = false;
-
-function bindReady(){
-       if ( readyBound ) return;
-       readyBound = true;
-
-       // Mozilla, Opera and webkit nightlies currently support this event
-       if ( document.addEventListener ) {
-               // Use the handy event callback
-               document.addEventListener( "DOMContentLoaded", function(){
-                       document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
-                       jQuery.ready();
-               }, false );
-
-       // If IE event model is used
-       } else if ( document.attachEvent ) {
-               // ensure firing before onload,
-               // maybe late but safe also for iframes
-               document.attachEvent("onreadystatechange", function(){
-                       if ( document.readyState === "complete" ) {
-                               document.detachEvent( "onreadystatechange", arguments.callee );
-                               jQuery.ready();
-                       }
-               });
-
-               // If IE and not an iframe
-               // continually check to see if the document is ready
-               if ( document.documentElement.doScroll && window == window.top ) (function(){
-                       if ( jQuery.isReady ) return;
-
-                       try {
-                               // If IE is used, use the trick by Diego Perini
-                               // http://javascript.nwbox.com/IEContentLoaded/
-                               document.documentElement.doScroll("left");
-                       } catch( error ) {
-                               setTimeout( arguments.callee, 0 );
-                               return;
-                       }
-
-                       // and execute any waiting functions
-                       jQuery.ready();
-               })();
-       }
-
-       // A fallback to window.onload, that will always work
-       jQuery.event.add( window, "load", jQuery.ready );
-}
-
-jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
-       "mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
-       "change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
-
-       // Handle event binding
-       jQuery.fn[name] = function(fn){
-               return fn ? this.bind(name, fn) : this.trigger(name);
-       };
-});
-
-// Prevent memory leaks in IE
-// And prevent errors on refresh with events like mouseover in other browsers
-// Window isn't included so as not to unbind existing unload events
-jQuery( window ).bind( 'unload', function(){ 
-       for ( var id in jQuery.cache )
-               // Skip the window
-               if ( id != 1 && jQuery.cache[ id ].handle )
-                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
-}); 
-(function(){
-
-       jQuery.support = {};
-
-       var root = document.documentElement,
-               script = document.createElement("script"),
-               div = document.createElement("div"),
-               id = "script" + (new Date).getTime();
-
-       div.style.display = "none";
-       div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
-
-       var all = div.getElementsByTagName("*"),
-               a = div.getElementsByTagName("a")[0];
-
-       // Can't get basic test support
-       if ( !all || !all.length || !a ) {
-               return;
-       }
-
-       jQuery.support = {
-               // IE strips leading whitespace when .innerHTML is used
-               leadingWhitespace: div.firstChild.nodeType == 3,
-               
-               // Make sure that tbody elements aren't automatically inserted
-               // IE will insert them into empty tables
-               tbody: !div.getElementsByTagName("tbody").length,
-               
-               // Make sure that you can get all elements in an <object> element
-               // IE 7 always returns no results
-               objectAll: !!div.getElementsByTagName("object")[0]
-                       .getElementsByTagName("*").length,
-               
-               // Make sure that link elements get serialized correctly by innerHTML
-               // This requires a wrapper element in IE
-               htmlSerialize: !!div.getElementsByTagName("link").length,
-               
-               // Get the style information from getAttribute
-               // (IE uses .cssText insted)
-               style: /red/.test( a.getAttribute("style") ),
-               
-               // Make sure that URLs aren't manipulated
-               // (IE normalizes it by default)
-               hrefNormalized: a.getAttribute("href") === "/a",
-               
-               // Make sure that element opacity exists
-               // (IE uses filter instead)
-               opacity: a.style.opacity === "0.5",
-               
-               // Verify style float existence
-               // (IE uses styleFloat instead of cssFloat)
-               cssFloat: !!a.style.cssFloat,
-
-               // Will be defined later
-               scriptEval: false,
-               noCloneEvent: true,
-               boxModel: null
-       };
-       
-       script.type = "text/javascript";
-       try {
-               script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
-       } catch(e){}
-
-       root.insertBefore( script, root.firstChild );
-       
-       // Make sure that the execution of code works by injecting a script
-       // tag with appendChild/createTextNode
-       // (IE doesn't support this, fails, and uses .text instead)
-       if ( window[ id ] ) {
-               jQuery.support.scriptEval = true;
-               delete window[ id ];
-       }
-
-       root.removeChild( script );
-
-       if ( div.attachEvent && div.fireEvent ) {
-               div.attachEvent("onclick", function(){
-                       // Cloning a node shouldn't copy over any
-                       // bound event handlers (IE does this)
-                       jQuery.support.noCloneEvent = false;
-                       div.detachEvent("onclick", arguments.callee);
-               });
-               div.cloneNode(true).fireEvent("onclick");
-       }
-
-       // Figure out if the W3C box model works as expected
-       // document.body must exist before we can do this
-       jQuery(function(){
-               var div = document.createElement("div");
-               div.style.width = div.style.paddingLeft = "1px";
-
-               document.body.appendChild( div );
-               jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
-               document.body.removeChild( div ).style.display = 'none';
-       });
-})();
-
-var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
-
-jQuery.props = {
-       "for": "htmlFor",
-       "class": "className",
-       "float": styleFloat,
-       cssFloat: styleFloat,
-       styleFloat: styleFloat,
-       readonly: "readOnly",
-       maxlength: "maxLength",
-       cellspacing: "cellSpacing",
-       rowspan: "rowSpan",
-       tabindex: "tabIndex"
-};
-jQuery.fn.extend({
-       // Keep a copy of the old load
-       _load: jQuery.fn.load,
-
-       load: function( url, params, callback ) {
-               if ( typeof url !== "string" )
-                       return this._load( url );
-
-               var off = url.indexOf(" ");
-               if ( off >= 0 ) {
-                       var selector = url.slice(off, url.length);
-                       url = url.slice(0, off);
-               }
-
-               // Default to a GET request
-               var type = "GET";
-
-               // If the second parameter was provided
-               if ( params )
-                       // If it's a function
-                       if ( jQuery.isFunction( params ) ) {
-                               // We assume that it's the callback
-                               callback = params;
-                               params = null;
-
-                       // Otherwise, build a param string
-                       } else if( typeof params === "object" ) {
-                               params = jQuery.param( params );
-                               type = "POST";
-                       }
-
-               var self = this;
-
-               // Request the remote document
-               jQuery.ajax({
-                       url: url,
-                       type: type,
-                       dataType: "html",
-                       data: params,
-                       complete: function(res, status){
-                               // If successful, inject the HTML into all the matched elements
-                               if ( status == "success" || status == "notmodified" )
-                                       // See if a selector was specified
-                                       self.html( selector ?
-                                               // Create a dummy div to hold the results
-                                               jQuery("<div/>")
-                                                       // inject the contents of the document in, removing the scripts
-                                                       // to avoid any 'Permission Denied' errors in IE
-                                                       .append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
-
-                                                       // Locate the specified elements
-                                                       .find(selector) :
-
-                                               // If not, just inject the full result
-                                               res.responseText );
-
-                               if( callback )
-                                       self.each( callback, [res.responseText, status, res] );
-                       }
-               });
-               return this;
-       },
-
-       serialize: function() {
-               return jQuery.param(this.serializeArray());
-       },
-       serializeArray: function() {
-               return this.map(function(){
-                       return this.elements ? jQuery.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(i, elem){
-                       var val = jQuery(this).val();
-                       return val == null ? null :
-                               jQuery.isArray(val) ?
-                                       jQuery.map( val, function(val, i){
-                                               return {name: elem.name, value: val};
-                                       }) :
-                                       {name: elem.name, value: val};
-               }).get();
-       }
-});
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
-       jQuery.fn[o] = function(f){
-               return this.bind(o, f);
-       };
-});
-
-var jsc = now();
-
-jQuery.extend({
-  
-       get: function( url, data, callback, type ) {
-               // shift arguments if data argument was ommited
-               if ( jQuery.isFunction( data ) ) {
-                       callback = data;
-                       data = null;
-               }
-
-               return jQuery.ajax({
-                       type: "GET",
-                       url: url,
-                       data: data,
-                       success: callback,
-                       dataType: type
-               });
-       },
-
-       getScript: function( url, callback ) {
-               return jQuery.get(url, null, callback, "script");
-       },
-
-       getJSON: function( url, data, callback ) {
-               return jQuery.get(url, data, callback, "json");
-       },
-
-       post: function( url, data, callback, type ) {
-               if ( jQuery.isFunction( data ) ) {
-                       callback = data;
-                       data = {};
-               }
-
-               return jQuery.ajax({
-                       type: "POST",
-                       url: url,
-                       data: data,
-                       success: callback,
-                       dataType: type
-               });
-       },
-
-       ajaxSetup: function( settings ) {
-               jQuery.extend( jQuery.ajaxSettings, settings );
-       },
-
-       ajaxSettings: {
-               url: location.href,
-               global: true,
-               type: "GET",
-               contentType: "application/x-www-form-urlencoded",
-               processData: true,
-               async: true,
-               /*
-               timeout: 0,
-               data: null,
-               username: null,
-               password: null,
-               */
-               // Create the request object; Microsoft failed to properly
-               // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
-               // This function can be overriden by calling jQuery.ajaxSetup
-               xhr:function(){
-                       return window.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: "*/*"
-               }
-       },
-
-       // Last-Modified header cache for next request
-       lastModified: {},
-
-       ajax: function( s ) {
-               // Extend the settings, but re-extend 's' so that it can be
-               // checked again later (in the test suite, specifically)
-               s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
-
-               var jsonp, jsre = /=\?(&|$)/g, status, data,
-                       type = s.type.toUpperCase();
-
-               // convert data if not already a string
-               if ( s.data && s.processData && typeof s.data !== "string" )
-                       s.data = jQuery.param(s.data);
-
-               // Handle JSONP Parameter Callbacks
-               if ( s.dataType == "jsonp" ) {
-                       if ( type == "GET" ) {
-                               if ( !s.url.match(jsre) )
-                                       s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
-                       } else if ( !s.data || !s.data.match(jsre) )
-                               s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
-                       s.dataType = "json";
-               }
-
-               // Build temporary JSONP function
-               if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
-                       jsonp = "jsonp" + jsc++;
-
-                       // Replace the =? sequence both in the query string and the data
-                       if ( s.data )
-                               s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
-                       s.url = s.url.replace(jsre, "=" + jsonp + "$1");
-
-                       // We need to make sure
-                       // that a JSONP style response is executed properly
-                       s.dataType = "script";
-
-                       // Handle JSONP-style loading
-                       window[ jsonp ] = function(tmp){
-                               data = tmp;
-                               success();
-                               complete();
-                               // Garbage collect
-                               window[ jsonp ] = undefined;
-                               try{ delete window[ jsonp ]; } catch(e){}
-                               if ( head )
-                                       head.removeChild( script );
-                       };
-               }
-
-               if ( s.dataType == "script" && s.cache == null )
-                       s.cache = false;
-
-               if ( s.cache === false && type == "GET" ) {
-                       var ts = now();
-                       // try replacing _= if it is there
-                       var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
-                       // if nothing was replaced, add timestamp to the end
-                       s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
-               }
-
-               // If data is available, append data to url for get requests
-               if ( s.data && type == "GET" ) {
-                       s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
-
-                       // IE likes to send both get and post data, prevent this
-                       s.data = null;
-               }
-
-               // Watch for a new set of requests
-               if ( s.global && ! jQuery.active++ )
-                       jQuery.event.trigger( "ajaxStart" );
-
-               // Matches an absolute URL, and saves the domain
-               var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
-
-               // If we're requesting a remote document
-               // and trying to load JSON or Script with a GET
-               if ( s.dataType == "script" && type == "GET" && parts
-                       && ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
-
-                       var head = document.getElementsByTagName("head")[0];
-                       var script = document.createElement("script");
-                       script.src = s.url;
-                       if (s.scriptCharset)
-                               script.charset = s.scriptCharset;
-
-                       // Handle Script loading
-                       if ( !jsonp ) {
-                               var done = false;
-
-                               // Attach handlers for all browsers
-                               script.onload = script.onreadystatechange = function(){
-                                       if ( !done && (!this.readyState ||
-                                                       this.readyState == "loaded" || this.readyState == "complete") ) {
-                                               done = true;
-                                               success();
-                                               complete();
-
-                                               // Handle memory leak in IE
-                                               script.onload = script.onreadystatechange = null;
-                                               head.removeChild( script );
-                                       }
-                               };
-                       }
-
-                       head.appendChild(script);
-
-                       // We handle everything using the script element injection
-                       return undefined;
-               }
-
-               var requestDone = false;
-
-               // Create the request object
-               var xhr = s.xhr();
-
-               // Open the socket
-               // Passing null username, generates a login popup on Opera (#2865)
-               if( s.username )
-                       xhr.open(type, s.url, s.async, s.username, s.password);
-               else
-                       xhr.open(type, s.url, s.async);
-
-               // Need an extra try/catch for cross domain requests in Firefox 3
-               try {
-                       // Set the correct header, if data is being sent
-                       if ( s.data )
-                               xhr.setRequestHeader("Content-Type", s.contentType);
-
-                       // Set the If-Modified-Since header, if ifModified mode.
-                       if ( s.ifModified )
-                               xhr.setRequestHeader("If-Modified-Since",
-                                       jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
-
-                       // Set header so the called script knows that it's an XMLHttpRequest
-                       xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
-
-                       // Set the Accepts header for the server, depending on the dataType
-                       xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
-                               s.accepts[ s.dataType ] + ", */*" :
-                               s.accepts._default );
-               } catch(e){}
-
-               // Allow custom headers/mimetypes and early abort
-               if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
-                       // Handle the global AJAX counter
-                       if ( s.global && ! --jQuery.active )
-                               jQuery.event.trigger( "ajaxStop" );
-                       // close opended socket
-                       xhr.abort();
-                       return false;
-               }
-
-               if ( s.global )
-                       jQuery.event.trigger("ajaxSend", [xhr, s]);
-
-               // Wait for a response to come back
-               var onreadystatechange = function(isTimeout){
-                       // The request was aborted, clear the interval and decrement jQuery.active
-                       if (xhr.readyState == 0) {
-                               if (ival) {
-                                       // clear poll interval
-                                       clearInterval(ival);
-                                       ival = null;
-                                       // Handle the global AJAX counter
-                                       if ( s.global && ! --jQuery.active )
-                                               jQuery.event.trigger( "ajaxStop" );
-                               }
-                       // The transfer is complete and the data is available, or the request timed out
-                       } else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
-                               requestDone = true;
-
-                               // clear poll interval
-                               if (ival) {
-                                       clearInterval(ival);
-                                       ival = null;
-                               }
-
-                               status = isTimeout == "timeout" ? "timeout" :
-                                       !jQuery.httpSuccess( xhr ) ? "error" :
-                                       s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
-                                       "success";
-
-                               if ( status == "success" ) {
-                                       // Watch for, and catch, XML document parse errors
-                                       try {
-                                               // process the data (runs the xml through httpData regardless of callback)
-                                               data = jQuery.httpData( xhr, s.dataType, s );
-                                       } catch(e) {
-                                               status = "parsererror";
-                                       }
-                               }
-
-                               // Make sure that the request was successful or notmodified
-                               if ( status == "success" ) {
-                                       // Cache Last-Modified header, if ifModified mode.
-                                       var modRes;
-                                       try {
-                                               modRes = xhr.getResponseHeader("Last-Modified");
-                                       } catch(e) {} // swallow exception thrown by FF if header is not available
-
-                                       if ( s.ifModified && modRes )
-                                               jQuery.lastModified[s.url] = modRes;
-
-                                       // JSONP handles its own success callback
-                                       if ( !jsonp )
-                                               success();
-                               } else
-                                       jQuery.handleError(s, xhr, status);
-
-                               // Fire the complete handlers
-                               complete();
-
-                               if ( isTimeout )
-                                       xhr.abort();
-
-                               // Stop memory leaks
-                               if ( s.async )
-                                       xhr = null;
-                       }
-               };
-
-               if ( s.async ) {
-                       // don't attach the handler to the request, just poll it instead
-                       var ival = setInterval(onreadystatechange, 13);
-
-                       // Timeout checker
-                       if ( s.timeout > 0 )
-                               setTimeout(function(){
-                                       // Check to see if the request is still happening
-                                       if ( xhr && !requestDone )
-                                               onreadystatechange( "timeout" );
-                               }, s.timeout);
-               }
-
-               // Send the data
-               try {
-                       xhr.send(s.data);
-               } catch(e) {
-                       jQuery.handleError(s, xhr, null, e);
-               }
-
-               // firefox 1.5 doesn't fire statechange for sync requests
-               if ( !s.async )
-                       onreadystatechange();
-
-               function success(){
-                       // If a local callback was specified, fire it and pass it the data
-                       if ( s.success )
-                               s.success( data, status );
-
-                       // Fire the global callback
-                       if ( s.global )
-                               jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
-               }
-
-               function complete(){
-                       // Process result
-                       if ( s.complete )
-                               s.complete(xhr, status);
-
-                       // The request was completed
-                       if ( s.global )
-                               jQuery.event.trigger( "ajaxComplete", [xhr, s] );
-
-                       // Handle the global AJAX counter
-                       if ( s.global && ! --jQuery.active )
-                               jQuery.event.trigger( "ajaxStop" );
-               }
-
-               // return XMLHttpRequest to allow aborting the request etc.
-               return xhr;
-       },
-
-       handleError: function( s, xhr, status, e ) {
-               // If a local callback was specified, fire it
-               if ( s.error ) s.error( xhr, status, e );
-
-               // Fire the global callback
-               if ( s.global )
-                       jQuery.event.trigger( "ajaxError", [xhr, s, e] );
-       },
-
-       // Counter for holding the number of active queries
-       active: 0,
-
-       // Determines if an XMLHttpRequest was successful or not
-       httpSuccess: function( xhr ) {
-               try {
-                       // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
-                       return !xhr.status && location.protocol == "file:" ||
-                               ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
-               } catch(e){}
-               return false;
-       },
-
-       // Determines if an XMLHttpRequest returns NotModified
-       httpNotModified: function( xhr, url ) {
-               try {
-                       var xhrRes = xhr.getResponseHeader("Last-Modified");
-
-                       // Firefox always returns 200. check Last-Modified date
-                       return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
-               } catch(e){}
-               return false;
-       },
-
-       httpData: function( xhr, type, s ) {
-               var ct = xhr.getResponseHeader("content-type"),
-                       xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
-                       data = xml ? xhr.responseXML : xhr.responseText;
-
-               if ( xml && data.documentElement.tagName == "parsererror" )
-                       throw "parsererror";
-                       
-               // Allow a pre-filtering function to sanitize the response
-               // s != null is checked to keep backwards compatibility
-               if( s && s.dataFilter )
-                       data = s.dataFilter( data, type );
-
-               // The filter can actually parse the response
-               if( typeof data === "string" ){
-
-                       // If the type is "script", eval it in global context
-                       if ( type == "script" )
-                               jQuery.globalEval( data );
-
-                       // Get the JavaScript object, if JSON is used.
-                       if ( type == "json" )
-                               data = window["eval"]("(" + data + ")");
-               }
-               
-               return data;
-       },
-
-       // Serialize an array of form elements or a set of
-       // key/values into a query string
-       param: function( a ) {
-               var s = [ ];
-
-               function add( key, value ){
-                       s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
-               };
-
-               // If an array was passed in, assume that it is an array
-               // of form elements
-               if ( jQuery.isArray(a) || a.jquery )
-                       // Serialize the form elements
-                       jQuery.each( a, function(){
-                               add( this.name, this.value );
-                       });
-
-               // Otherwise, assume that it's an object of key/value pairs
-               else
-                       // Serialize the key/values
-                       for ( var j in a )
-                               // If the value is an array then the key names need to be repeated
-                               if ( jQuery.isArray(a[j]) )
-                                       jQuery.each( a[j], function(){
-                                               add( j, this );
-                                       });
-                               else
-                                       add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
-
-               // Return the resulting serialization
-               return s.join("&").replace(/%20/g, "+");
-       }
-
-});
-var elemdisplay = {},
-       timerId,
-       fxAttrs = [
-               // height animations
-               [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
-               // width animations
-               [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
-               // opacity animations
-               [ "opacity" ]
-       ];
-
-function genFx( type, num ){
-       var obj = {};
-       jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
-               obj[ this ] = type;
-       });
-       return obj;
-}
-
-jQuery.fn.extend({
-       show: function(speed,callback){
-               if ( speed ) {
-                       return this.animate( genFx("show", 3), speed, callback);
-               } else {
-                       for ( var i = 0, l = this.length; i < l; i++ ){
-                               var old = jQuery.data(this[i], "olddisplay");
-                               
-                               this[i].style.display = old || "";
-                               
-                               if ( jQuery.css(this[i], "display") === "none" ) {
-                                       var tagName = this[i].tagName, display;
-                                       
-                                       if ( elemdisplay[ tagName ] ) {
-                                               display = elemdisplay[ tagName ];
-                                       } else {
-                                               var elem = jQuery("<" + tagName + " />").appendTo("body");
-                                               
-                                               display = elem.css("display");
-                                               if ( display === "none" )
-                                                       display = "block";
-                                               
-                                               elem.remove();
-                                               
-                                               elemdisplay[ tagName ] = display;
-                                       }
-                                       
-                                       jQuery.data(this[i], "olddisplay", display);
-                               }
-                       }
-
-                       // Set the display of the elements in a second loop
-                       // to avoid the constant reflow
-                       for ( var i = 0, l = this.length; i < l; i++ ){
-                               this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
-                       }
-                       
-                       return this;
-               }
-       },
-
-       hide: function(speed,callback){
-               if ( speed ) {
-                       return this.animate( genFx("hide", 3), speed, callback);
-               } else {
-                       for ( var i = 0, l = this.length; i < l; i++ ){
-                               var old = jQuery.data(this[i], "olddisplay");
-                               if ( !old && old !== "none" )
-                                       jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
-                       }
-
-                       // Set the display of the elements in a second loop
-                       // to avoid the constant reflow
-                       for ( var i = 0, l = this.length; i < l; i++ ){
-                               this[i].style.display = "none";
-                       }
-
-                       return this;
-               }
-       },
-
-       // Save the old toggle function
-       _toggle: jQuery.fn.toggle,
-
-       toggle: function( fn, fn2 ){
-               var bool = typeof fn === "boolean";
-
-               return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
-                       this._toggle.apply( this, arguments ) :
-                       fn == null || bool ?
-                               this.each(function(){
-                                       var state = bool ? fn : jQuery(this).is(":hidden");
-                                       jQuery(this)[ state ? "show" : "hide" ]();
-                               }) :
-                               this.animate(genFx("toggle", 3), fn, fn2);
-       },
-
-       fadeTo: function(speed,to,callback){
-               return this.animate({opacity: to}, speed, callback);
-       },
-
-       animate: function( prop, speed, easing, callback ) {
-               var optall = jQuery.speed(speed, easing, callback);
-
-               return this[ optall.queue === false ? "each" : "queue" ](function(){
-               
-                       var opt = jQuery.extend({}, optall), p,
-                               hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
-                               self = this;
-       
-                       for ( p in prop ) {
-                               if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
-                                       return opt.complete.call(this);
-
-                               if ( ( p == "height" || p == "width" ) && this.style ) {
-                                       // Store display property
-                                       opt.display = jQuery.css(this, "display");
-
-                                       // Make sure that nothing sneaks out
-                                       opt.overflow = this.style.overflow;
-                               }
-                       }
-
-                       if ( opt.overflow != null )
-                               this.style.overflow = "hidden";
-
-                       opt.curAnim = jQuery.extend({}, prop);
-
-                       jQuery.each( prop, function(name, val){
-                               var e = new jQuery.fx( self, opt, name );
-
-                               if ( /toggle|show|hide/.test(val) )
-                                       e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
-                               else {
-                                       var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
-                                               start = e.cur(true) || 0;
-
-                                       if ( parts ) {
-                                               var end = parseFloat(parts[2]),
-                                                       unit = parts[3] || "px";
-
-                                               // We need to compute starting value
-                                               if ( unit != "px" ) {
-                                                       self.style[ name ] = (end || 1) + unit;
-                                                       start = ((end || 1) / e.cur(true)) * start;
-                                                       self.style[ name ] = start + unit;
-                                               }
-
-                                               // If a +=/-= token was provided, we're doing a relative animation
-                                               if ( parts[1] )
-                                                       end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
-
-                                               e.custom( start, end, unit );
-                                       } else
-                                               e.custom( start, val, "" );
-                               }
-                       });
-
-                       // For JS strict compliance
-                       return true;
-               });
-       },
-
-       stop: function(clearQueue, gotoEnd){
-               var timers = jQuery.timers;
-
-               if (clearQueue)
-                       this.queue([]);
-
-               this.each(function(){
-                       // go in reverse order so anything added to the queue during the loop is ignored
-                       for ( var i = timers.length - 1; i >= 0; i-- )
-                               if ( timers[i].elem == this ) {
-                                       if (gotoEnd)
-                                               // force the next step to be the last
-                                               timers[i](true);
-                                       timers.splice(i, 1);
-                               }
-               });
-
-               // start the next in the queue if the last step wasn't forced
-               if (!gotoEnd)
-                       this.dequeue();
-
-               return this;
-       }
-
-});
-
-// Generate shortcuts for custom animations
-jQuery.each({
-       slideDown: genFx("show", 1),
-       slideUp: genFx("hide", 1),
-       slideToggle: genFx("toggle", 1),
-       fadeIn: { opacity: "show" },
-       fadeOut: { opacity: "hide" }
-}, function( name, props ){
-       jQuery.fn[ name ] = function( speed, callback ){
-               return this.animate( props, speed, callback );
-       };
-});
-
-jQuery.extend({
-
-       speed: function(speed, easing, fn) {
-               var opt = typeof speed === "object" ? speed : {
-                       complete: fn || !fn && easing ||
-                               jQuery.isFunction( speed ) && speed,
-                       duration: speed,
-                       easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
-               };
-
-               opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
-                       jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
-
-               // Queueing
-               opt.old = opt.complete;
-               opt.complete = function(){
-                       if ( opt.queue !== false )
-                               jQuery(this).dequeue();
-                       if ( jQuery.isFunction( opt.old ) )
-                               opt.old.call( this );
-               };
-
-               return opt;
-       },
-
-       easing: {
-               linear: function( p, n, firstNum, diff ) {
-                       return firstNum + diff * p;
-               },
-               swing: function( p, n, firstNum, diff ) {
-                       return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
-               }
-       },
-
-       timers: [],
-
-       fx: function( elem, options, prop ){
-               this.options = options;
-               this.elem = elem;
-               this.prop = prop;
-
-               if ( !options.orig )
-                       options.orig = {};
-       }
-
-});
-
-jQuery.fx.prototype = {
-
-       // Simple function for setting a style value
-       update: function(){
-               if ( this.options.step )
-                       this.options.step.call( this.elem, this.now, this );
-
-               (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
-
-               // Set display property to block for height/width animations
-               if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
-                       this.elem.style.display = "block";
-       },
-
-       // Get the current size
-       cur: function(force){
-               if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
-                       return this.elem[ this.prop ];
-
-               var r = parseFloat(jQuery.css(this.elem, this.prop, force));
-               return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
-       },
-
-       // Start an animation from one number to another
-       custom: function(from, to, unit){
-               this.startTime = now();
-               this.start = from;
-               this.end = to;
-               this.unit = unit || this.unit || "px";
-               this.now = this.start;
-               this.pos = this.state = 0;
-
-               var self = this;
-               function t(gotoEnd){
-                       return self.step(gotoEnd);
-               }
-
-               t.elem = this.elem;
-
-               if ( t() && jQuery.timers.push(t) && !timerId ) {
-                       timerId = setInterval(function(){
-                               var timers = jQuery.timers;
-
-                               for ( var i = 0; i < timers.length; i++ )
-                                       if ( !timers[i]() )
-                                               timers.splice(i--, 1);
-
-                               if ( !timers.length ) {
-                                       clearInterval( timerId );
-                                       timerId = undefined;
-                               }
-                       }, 13);
-               }
-       },
-
-       // Simple 'show' function
-       show: function(){
-               // Remember where we started, so that we can go back to it later
-               this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-               this.options.show = true;
-
-               // Begin the animation
-               // Make sure that we start at a small width/height to avoid any
-               // flash of content
-               this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
-
-               // Start by showing the element
-               jQuery(this.elem).show();
-       },
-
-       // Simple 'hide' function
-       hide: function(){
-               // Remember where we started, so that we can go back to it later
-               this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
-               this.options.hide = true;
-
-               // Begin the animation
-               this.custom(this.cur(), 0);
-       },
-
-       // Each step of an animation
-       step: function(gotoEnd){
-               var t = now();
-
-               if ( gotoEnd || t >= this.options.duration + this.startTime ) {
-                       this.now = this.end;
-                       this.pos = this.state = 1;
-                       this.update();
-
-                       this.options.curAnim[ this.prop ] = true;
-
-                       var done = true;
-                       for ( var i in this.options.curAnim )
-                               if ( this.options.curAnim[i] !== true )
-                                       done = false;
-
-                       if ( done ) {
-                               if ( this.options.display != null ) {
-                                       // Reset the overflow
-                                       this.elem.style.overflow = this.options.overflow;
-
-                                       // Reset the display
-                                       this.elem.style.display = this.options.display;
-                                       if ( jQuery.css(this.elem, "display") == "none" )
-                                               this.elem.style.display = "block";
-                               }
-
-                               // Hide the element if the "hide" operation was done
-                               if ( this.options.hide )
-                                       jQuery(this.elem).hide();
-
-                               // Reset the properties, if the item has been hidden or shown
-                               if ( this.options.hide || this.options.show )
-                                       for ( var p in this.options.curAnim )
-                                               jQuery.attr(this.elem.style, p, this.options.orig[p]);
-                                       
-                               // Execute the complete function
-                               this.options.complete.call( this.elem );
-                       }
-
-                       return false;
-               } else {
-                       var n = t - this.startTime;
-                       this.state = n / this.options.duration;
-
-                       // Perform the easing function, defaults to swing
-                       this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
-                       this.now = this.start + ((this.end - this.start) * this.pos);
-
-                       // Perform the next step of the animation
-                       this.update();
-               }
-
-               return true;
-       }
-
-};
-
-jQuery.extend( jQuery.fx, {
-       speeds:{
-               slow: 600,
-               fast: 200,
-               // Default speed
-               _default: 400
-       },
-       step: {
-
-               opacity: function(fx){
-                       jQuery.attr(fx.elem.style, "opacity", fx.now);
-               },
-
-               _default: function(fx){
-                       if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
-                               fx.elem.style[ fx.prop ] = fx.now + fx.unit;
-                       else
-                               fx.elem[ fx.prop ] = fx.now;
-               }
-       }
-});
-if ( document.documentElement["getBoundingClientRect"] )
-       jQuery.fn.offset = function() {
-               if ( !this[0] ) return { top: 0, left: 0 };
-               if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
-               var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
-                       clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
-                       top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
-                       left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
-               return { top: top, left: left };
-       };
-else 
-       jQuery.fn.offset = function() {
-               if ( !this[0] ) return { top: 0, left: 0 };
-               if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
-               jQuery.offset.initialized || jQuery.offset.initialize();
-
-               var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
-                       doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
-                       body = doc.body, defaultView = doc.defaultView,
-                       prevComputedStyle = defaultView.getComputedStyle(elem, null),
-                       top = elem.offsetTop, left = elem.offsetLeft;
-
-               while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
-                       computedStyle = defaultView.getComputedStyle(elem, null);
-                       top -= elem.scrollTop, left -= elem.scrollLeft;
-                       if ( elem === offsetParent ) {
-                               top += elem.offsetTop, left += elem.offsetLeft;
-                               if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
-                                       top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
-                                       left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
-                               prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
-                       }
-                       if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
-                               top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
-                               left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
-                       prevComputedStyle = computedStyle;
-               }
-
-               if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
-                       top  += body.offsetTop,
-                       left += body.offsetLeft;
-
-               if ( prevComputedStyle.position === "fixed" )
-                       top  += Math.max(docElem.scrollTop, body.scrollTop),
-                       left += Math.max(docElem.scrollLeft, body.scrollLeft);
-
-               return { top: top, left: left };
-       };
-
-jQuery.offset = {
-       initialize: function() {
-               if ( this.initialized ) return;
-               var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
-                       html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
-
-               rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
-               for ( prop in rules ) container.style[prop] = rules[prop];
-
-               container.innerHTML = html;
-               body.insertBefore(container, body.firstChild);
-               innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
-
-               this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
-               this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
-
-               innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
-               this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
-
-               body.style.marginTop = '1px';
-               this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
-               body.style.marginTop = bodyMarginTop;
-
-               body.removeChild(container);
-               this.initialized = true;
-       },
-
-       bodyOffset: function(body) {
-               jQuery.offset.initialized || jQuery.offset.initialize();
-               var top = body.offsetTop, left = body.offsetLeft;
-               if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
-                       top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
-                       left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
-               return { top: top, left: left };
-       }
-};
-
-
-jQuery.fn.extend({
-       position: function() {
-               var left = 0, top = 0, results;
-
-               if ( this[0] ) {
-                       // Get *real* offsetParent
-                       var offsetParent = this.offsetParent(),
-
-                       // Get correct offsets
-                       offset       = this.offset(),
-                       parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
-
-                       // Subtract element margins
-                       // note: when an element has margin: auto the offsetLeft and marginLeft 
-                       // are the same in Safari causing offset.left to incorrectly be 0
-                       offset.top  -= num( this, 'marginTop'  );
-                       offset.left -= num( this, 'marginLeft' );
-
-                       // Add offsetParent borders
-                       parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
-                       parentOffset.left += num( offsetParent, 'borderLeftWidth' );
-
-                       // Subtract the two offsets
-                       results = {
-                               top:  offset.top  - parentOffset.top,
-                               left: offset.left - parentOffset.left
-                       };
-               }
-
-               return results;
-       },
-
-       offsetParent: function() {
-               var offsetParent = this[0].offsetParent || document.body;
-               while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
-                       offsetParent = offsetParent.offsetParent;
-               return jQuery(offsetParent);
-       }
-});
-
-
-// Create scrollLeft and scrollTop methods
-jQuery.each( ['Left', 'Top'], function(i, name) {
-       var method = 'scroll' + name;
-       
-       jQuery.fn[ method ] = function(val) {
-               if (!this[0]) return null;
-
-               return val !== undefined ?
-
-                       // Set the scroll offset
-                       this.each(function() {
-                               this == window || this == document ?
-                                       window.scrollTo(
-                                               !i ? val : jQuery(window).scrollLeft(),
-                                                i ? val : jQuery(window).scrollTop()
-                                       ) :
-                                       this[ method ] = val;
-                       }) :
-
-                       // Return the scroll offset
-                       this[0] == window || this[0] == document ?
-                               self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
-                                       jQuery.boxModel && document.documentElement[ method ] ||
-                                       document.body[ method ] :
-                               this[0][ method ];
-       };
-});
-// Create innerHeight, innerWidth, outerHeight and outerWidth methods
-jQuery.each([ "Height", "Width" ], function(i, name){
-
-       var tl = i ? "Left"  : "Top",  // top or left
-               br = i ? "Right" : "Bottom", // bottom or right
-               lower = name.toLowerCase();
-
-       // innerHeight and innerWidth
-       jQuery.fn["inner" + name] = function(){
-               return this[0] ?
-                       jQuery.css( this[0], lower, false, "padding" ) :
-                       null;
-       };
-
-       // outerHeight and outerWidth
-       jQuery.fn["outer" + name] = function(margin) {
-               return this[0] ?
-                       jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
-                       null;
-       };
-       
-       var type = name.toLowerCase();
-
-       jQuery.fn[ type ] = function( size ) {
-               // Get window width or height
-               return this[0] == window ?
-                       // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
-                       document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
-                       document.body[ "client" + name ] :
-
-                       // Get document width or height
-                       this[0] == document ?
-                               // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
-                               Math.max(
-                                       document.documentElement["client" + name],
-                                       document.body["scroll" + name], document.documentElement["scroll" + name],
-                                       document.body["offset" + name], document.documentElement["offset" + name]
-                               ) :
-
-                               // Get or set width or height on the element
-                               size === undefined ?
-                                       // Get width or height on the element
-                                       (this.length ? jQuery.css( this[0], type ) : null) :
-
-                                       // Set the width or height on the element (default to pixels if value is unitless)
-                                       this.css( type, typeof size === "string" ? size : size + "px" );
-       };
-
-});
-})();
diff --git a/project/static/js/lib/jquery.json.js b/project/static/js/lib/jquery.json.js
deleted file mode 100644 (file)
index 87f5d01..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * jQuery JSON Plugin
- * version: 2.1 (2009-08-14)
- *
- * This document is licensed as free software under the terms of the
- * MIT License: http://www.opensource.org/licenses/mit-license.php
- *
- * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org 
- * website's http://www.json.org/json2.js, which proclaims:
- * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
- * I uphold.
- *
- * It is also influenced heavily by MochiKit's serializeJSON, which is 
- * copyrighted 2005 by Bob Ippolito.
- */
-(function($) {
-    /** jQuery.toJSON( json-serializble )
-        Converts the given argument into a JSON respresentation.
-
-        If an object has a "toJSON" function, that will be used to get the representation.
-        Non-integer/string keys are skipped in the object, as are keys that point to a function.
-
-        json-serializble:
-            The *thing* to be converted.
-     **/
-    $.toJSON = function(o)
-    {
-        if (typeof(JSON) == 'object' && JSON.stringify)
-            return JSON.stringify(o);
-        
-        var type = typeof(o);
-    
-        if (o === null)
-            return "null";
-    
-        if (type == "undefined")
-            return undefined;
-        
-        if (type == "number" || type == "boolean")
-            return o + "";
-    
-        if (type == "string")
-            return $.quoteString(o);
-    
-        if (type == 'object')
-        {
-            if (typeof o.toJSON == "function") 
-                return $.toJSON( o.toJSON() );
-            
-            if (o.constructor === Date)
-            {
-                var month = o.getUTCMonth() + 1;
-                if (month < 10) month = '0' + month;
-
-                var day = o.getUTCDate();
-                if (day < 10) day = '0' + day;
-
-                var year = o.getUTCFullYear();
-                
-                var hours = o.getUTCHours();
-                if (hours < 10) hours = '0' + hours;
-                
-                var minutes = o.getUTCMinutes();
-                if (minutes < 10) minutes = '0' + minutes;
-                
-                var seconds = o.getUTCSeconds();
-                if (seconds < 10) seconds = '0' + seconds;
-                
-                var milli = o.getUTCMilliseconds();
-                if (milli < 100) milli = '0' + milli;
-                if (milli < 10) milli = '0' + milli;
-
-                return '"' + year + '-' + month + '-' + day + 'T' +
-                             hours + ':' + minutes + ':' + seconds + 
-                             '.' + milli + 'Z"'; 
-            }
-
-            if (o.constructor === Array) 
-            {
-                var ret = [];
-                for (var i = 0; i < o.length; i++)
-                    ret.push( $.toJSON(o[i]) || "null" );
-
-                return "[" + ret.join(",") + "]";
-            }
-        
-            var pairs = [];
-            for (var k in o) {
-                var name;
-                var type = typeof k;
-
-                if (type == "number")
-                    name = '"' + k + '"';
-                else if (type == "string")
-                    name = $.quoteString(k);
-                else
-                    continue;  //skip non-string or number keys
-            
-                if (typeof o[k] == "function") 
-                    continue;  //skip pairs where the value is a function.
-            
-                var val = $.toJSON(o[k]);
-            
-                pairs.push(name + ":" + val);
-            }
-
-            return "{" + pairs.join(", ") + "}";
-        }
-    };
-
-    /** jQuery.evalJSON(src)
-        Evaluates a given piece of json source.
-     **/
-    $.evalJSON = function(src)
-    {
-        if (typeof(JSON) == 'object' && JSON.parse)
-            return JSON.parse(src);
-        return eval("(" + src + ")");
-    };
-    
-    /** jQuery.secureEvalJSON(src)
-        Evals JSON in a way that is *more* secure.
-    **/
-    $.secureEvalJSON = function(src)
-    {
-        if (typeof(JSON) == 'object' && JSON.parse)
-            return JSON.parse(src);
-        
-        var filtered = src;
-        filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
-        filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
-        filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
-        
-        if (/^[\],:{}\s]*$/.test(filtered))
-            return eval("(" + src + ")");
-        else
-            throw new SyntaxError("Error parsing JSON, source is not valid.");
-    };
-
-    /** jQuery.quoteString(string)
-        Returns a string-repr of a string, escaping quotes intelligently.  
-        Mostly a support function for toJSON.
-    
-        Examples:
-            >>> jQuery.quoteString("apple")
-            "apple"
-        
-            >>> jQuery.quoteString('"Where are we going?", she asked.')
-            "\"Where are we going?\", she asked."
-     **/
-    $.quoteString = function(string)
-    {
-        if (string.match(_escapeable))
-        {
-            return '"' + string.replace(_escapeable, function (a) 
-            {
-                var c = _meta[a];
-                if (typeof c === 'string') return c;
-                c = a.charCodeAt();
-                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
-            }) + '"';
-        }
-        return '"' + string + '"';
-    };
-    
-    var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
-    
-    var _meta = {
-        '\b': '\\b',
-        '\t': '\\t',
-        '\n': '\\n',
-        '\f': '\\f',
-        '\r': '\\r',
-        '"' : '\\"',
-        '\\': '\\\\'
-    };
-})(jQuery);
diff --git a/project/static/js/lib/jquery.lazyload.js b/project/static/js/lib/jquery.lazyload.js
deleted file mode 100755 (executable)
index c15167b..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-(function($) {
-    jQuery.fn.lazyload = function(pattern, options) {
-        var settings = {
-            threshold: 0,
-            scrollThreshold: 300,
-            placeholder: 'loading...',
-            checkInterval: 2000
-        };
-        
-        if (options) {
-            $.extend(settings, options);
-        }
-        
-        var container = this;
-        container.data('lazyload:lastCheckedScrollTop', -10000);
-        
-        function aboveViewport(container, element, threshold) {
-            return $(container).offset().top >= $(element).offset().top + $(element).height() + threshold;
-        }
-        
-        function belowViewport(container, element, threshold) {
-            return $(container).offset().top + $(container).height() + threshold <= $(element).offset().top;
-        }
-        
-        function checkScroll() {
-            if (container.data('lazyload:lastCheckedScrollTop') == undefined) {
-                return;
-            }
-            if (Math.abs(container.scrollTop() - container.data('lazyload:lastCheckedScrollTop')) > settings.scrollThreshold) {
-                container.data('lazyload:lastCheckedScrollTop', container.scrollTop());
-                
-                $(pattern, container).each(function() {
-                    if (aboveViewport(container, this, settings.threshold)
-                        || belowViewport(container, this, settings.threshold)) {
-                        $(this).html(settings.placeholder);
-                    } else {
-                        $(this).html('');
-                        var self = this;
-                        $('<img src="' + $(this).attr('src') + '" width="' + $(this).width() + '" />').load(function() {
-                            if ($(this).height() > $(self).height()) {
-                                $(self).height($(this).height());
-                            }
-                        }).appendTo(this);
-                    }
-                })
-            }
-            setTimeout(checkScroll, settings.checkInterval);
-        }
-        
-        checkScroll();
-    };
-})(jQuery);
diff --git a/project/static/js/lib/jquery.logging.js b/project/static/js/lib/jquery.logging.js
deleted file mode 100644 (file)
index d6cee14..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-(function($) {
-       var LEVEL_DEBUG = 1;
-       var LEVEL_INFO = 2;
-       var LEVEL_WARN = 3;
-       var LOG_LEVEL = LEVEL_DEBUG;
-       
-        var standardLog = function() {
-            if (window.console)
-                console.log.apply(console, arguments);
-        };
-    
-       var operaLog = function() {
-               opera.postError(arguments.join(' '));
-       };
-
-        var msieLog = function() {
-            var args = $.makeArray(arguments);
-            var vals = $.map(args, function(n) {
-                try {
-                    return JSON.stringify(n);
-                } catch(e) {
-                    return ('' + n);
-                }
-            });
-
-            if (window.console)
-                console.log(vals.join(" "));
-        };
-
-       $.log = function() {
-               return $.log.browserLog.apply(this, arguments);
-       };
-
-        if($.browser.opera)
-            $.log.browserLog = operaLog;
-        else if($.browser.msie)
-            $.log.browserLog = msieLog;
-        else
-            $.log.browserLog = standardLog;
-
-})(jQuery);
diff --git a/project/static/js/lib/jquery.modal.js b/project/static/js/lib/jquery.modal.js
deleted file mode 100755 (executable)
index 3aac816..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * jqModal - Minimalist Modaling with jQuery
- *   (http://dev.iceburg.net/jquery/jqModal/)
- *
- * Copyright (c) 2007,2008 Brice Burgess <bhb@iceburg.net>
- * Dual licensed under the MIT and GPL licenses:
- *   http://www.opensource.org/licenses/mit-license.php
- *   http://www.gnu.org/licenses/gpl.html
- * 
- * $Version: 03/01/2009 +r14
- */
-(function($) {
-$.fn.jqm=function(o){
-var p={
-overlay: 50,
-overlayClass: 'jqmOverlay',
-closeClass: 'jqmClose',
-trigger: '.jqModal',
-ajax: F,
-ajaxText: '',
-target: F,
-modal: F,
-toTop: F,
-onShow: F,
-onHide: F,
-onLoad: F
-};
-return this.each(function(){if(this._jqm)return H[this._jqm].c=$.extend({},H[this._jqm].c,o);s++;this._jqm=s;
-H[s]={c:$.extend(p,$.jqm.params,o),a:F,w:$(this).addClass('jqmID'+s),s:s};
-if(p.trigger)$(this).jqmAddTrigger(p.trigger);
-});};
-
-$.fn.jqmAddClose=function(e){return hs(this,e,'jqmHide');};
-$.fn.jqmAddTrigger=function(e){return hs(this,e,'jqmShow');};
-$.fn.jqmShow=function(t){return this.each(function(){t=t||window.event;$.jqm.open(this._jqm,t);});};
-$.fn.jqmHide=function(t){return this.each(function(){t=t||window.event;$.jqm.close(this._jqm,t)});};
-
-$.jqm = {
-hash:{},
-open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=(parseInt(h.w.css('z-index'))),z=(z>0)?z:3000,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});if(h.a)return F;h.t=t;h.a=true;h.w.css('z-index',z);
- if(c.modal) {if(!A[0])L('bind');A.push(s);}
- else if(c.overlay > 0)h.w.jqmAddClose(o);
- else o=F;
-
- h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):F;
- if(ie6){$('html,body').css({height:'100%',width:'100%'});if(o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}}
-
- if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
-  r.html(c.ajaxText).load(u,function(){if(c.onLoad)c.onLoad.call(this,h);if(cc)h.w.jqmAddClose($(cc,h.w));e(h);});}
- else if(cc)h.w.jqmAddClose($(cc,h.w));
-
- if(c.toTop&&h.o)h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);        
- (c.onShow)?c.onShow(h):h.w.show();e(h);return F;
-},
-close:function(s){var h=H[s];if(!h.a)return F;h.a=F;
- if(A[0]){A.pop();if(!A[0])L('unbind');}
- if(h.c.toTop&&h.o)$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
- if(h.c.onHide)h.c.onHide(h);else{h.w.hide();if(h.o)h.o.remove();} return F;
-},
-params:{}};
-var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),F=false,
-i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
-e=function(h){if(ie6)if(h.o)h.o.html('<p style="width:100%;height:100%"/>').prepend(i);else if(!$('iframe.jqm',h.w)[0])h.w.prepend(i); f(h);},
-f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(_){}},
-L=function(t){$()[t]("keypress",m)[t]("keydown",m)[t]("mousedown",m);},
-m=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);if(r)f(h);return !r;},
-hs=function(w,t,c){return w.each(function(){var s=this._jqm;$(t).each(function() {
- if(!this[c]){this[c]=[];$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return F;});}this[c].push(s);});});};
-})(jQuery);
\ No newline at end of file
diff --git a/project/static/js/lib/jquery.wtooltip.js b/project/static/js/lib/jquery.wtooltip.js
deleted file mode 100644 (file)
index 01233ba..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/* Wayfarer Tooltip\r
- * Version 1.0.4\r
- * Author Abel Mohler\r
- * URI: http://www.wayfarerweb.com/wtooltip.php\r
- * Released with the MIT License: http://www.wayfarerweb.com/mit.php\r
- */\r
-(function($){ //jQuery.noConflict()compliant\r
-    $.fn.wTooltip = function(o, callback){\r
-        o = $.extend({ //defaults, can be overidden\r
-            content: null, //string content for tooltip.\r
-            ajax: null, //path to content for tooltip\r
-            follow: true, //does tooltip follow the cursor?\r
-            auto: true, //If false, tooltip won't automatically transition, it must be manually shown/hidden\r
-            fadeIn: 0, //fade in, in milliseconds ("fast, "slow", etc may also be used)\r
-            fadeOut: 0, //fade out, in milliseconds ("fast, "slow", etc may also be used)\r
-            appendTip: document.body, //should probably not need to be overridden\r
-            degrade: false, //if true, in IE6 tooltip will degrade to a title attribute message\r
-            offsetY: 10, //offsetY and offsetX properties designate position from the cursor\r
-            offsetX: 1,\r
-            style: {},\r
-            className: null, //to style the tooltip externally, pass a className or id\r
-            id: null,\r
-            callBefore: function(tooltip, node, settings){\r
-            }, //called when mouse enters the area\r
-            callAfter: function(tooltip, node, settings){\r
-            }, //called when mouse leaves the area (same as "callback" option)\r
-            clickAction: function(tooltip, node){\r
-                $(tooltip).hide();\r
-            }, //called when the element is clicked, with access to tooltip\r
-            delay: 0, //delay (in milliseconds)before tooltip appears and callBefore executes\r
-            timeout: 0, //delay (in milliseconds)before tooltip transitions away, and callAfter executes\r
-            cloneable: false //UNWORKING.  Requires $().wClone plugin. If true, tooltip may be dragged and placed anywhere on the screen.\r
-        }, o ||\r
-        {});\r
-        \r
-        if (!o.style && typeof o.style != "object") {\r
-            o.style = {};\r
-            o.style.zIndex = "1000";\r
-        }\r
-        else {\r
-            o.style = $.extend({ //the default style rules of the tooltip\r
-                border: "1px solid gray",\r
-                background: "#edeef0",\r
-                color: "#000",\r
-                padding: "10px",\r
-                zIndex: "1000",\r
-                textAlign: "left"\r
-            }, o.style ||\r
-            {});\r
-        }\r
-        \r
-        if (typeof callback == "function") \r
-            o.callAfter = callback || o.callAfter;\r
-        \r
-        o.style.display = "none", o.style.position = "absolute"; //permanent defaults\r
-        //private settings\r
-        var title, timeout, timeout2, iId, over = {}, firstMove = true, hovered = false, maxed = false, tooltip = document.createElement('div'), ie6 = (typeof document.body.style.maxWidth == "undefined") ? true : false, talk = (typeof $.talk == "function" && typeof $.listen == "function") ? true : false;\r
-        \r
-        if (o.id) \r
-            tooltip.id = o.id;\r
-        if (o.className) \r
-            tooltip.className = o.className;\r
-        \r
-        o.degrade = (o.degrade && ie6) ? true : false; //only degrades if also IE6\r
-        for (var p in o.style)//apply styles to tooltip\r
-             tooltip.style[p] = o.style[p];\r
-        \r
-        function fillTooltip(condition){\r
-            if (condition) {\r
-                if (o.degrade)//replace html characters for proper degradation to title attribute\r
-                    $(tooltip).html(o.content.replace(/<\/?[^>]+>/gi, ''));\r
-                else //otherwise just fill the tooltip with content\r
-                     $(tooltip).html(o.content);\r
-            }\r
-        }\r
-        \r
-        if (o.ajax) { //if o.ajax is selected, this will fill and thus override o.content\r
-            $.get(o.ajax, function(data){\r
-                if (data) \r
-                    o.content = data;\r
-                fillTooltip(o.content);\r
-            });\r
-        }\r
-        \r
-        function offConditions(that){\r
-            function _offActions(that){\r
-                if (title && !o.content) {\r
-                    that.title = title;\r
-                    title = null;\r
-                }\r
-            }\r
-            function _execute(){\r
-                if (!hovered && o.auto) {\r
-                    clearInterval(iId);\r
-                    if (o.fadeOut) {\r
-                        $(tooltip).fadeOut(o.fadeOut, function(){\r
-                            _offActions(that);\r
-                        });\r
-                    }\r
-                    else {\r
-                        _offActions(that);\r
-                        tooltip.style.display = "none";\r
-                    }\r
-                }\r
-                if (typeof o.callAfter == "function") \r
-                    o.callAfter(tooltip, that, o);\r
-                if (talk) \r
-                    o = $.listen(o);\r
-            }\r
-            if (o.timeout > 0) {\r
-                timeout2 = setTimeout(function(){\r
-                    _execute();\r
-                }, o.timeout);\r
-            }\r
-            else {\r
-                _execute();\r
-            }\r
-        }\r
-        \r
-        $(tooltip).hover(function(){\r
-            hovered = true;\r
-        }, function(){\r
-            hovered = false;\r
-            offConditions(over);\r
-        });\r
-        \r
-        //initialize\r
-        if (talk) { //A "channel" for plugins to "talk" to each other, and callbacks to manipulate settings\r
-            o.key = tooltip;\r
-            o.plugin = "wTooltip";\r
-            o.channel = "wayfarer";\r
-            $.talk(o);\r
-        }\r
-        \r
-        fillTooltip(o.content && !o.ajax);\r
-        $(tooltip).appendTo(o.appendTip);\r
-        \r
-        return this.each(function(){ //returns the element chain\r
-            this.onmouseover = function(ev){\r
-                var that = this;\r
-                clearTimeout(timeout2);\r
-                if (this.title && !o.degrade && !o.content) {\r
-                    title = this.title;\r
-                    this.title = "";\r
-                }\r
-                if (o.content && o.degrade) \r
-                    this.title = tooltip.innerHTML;\r
-                \r
-                function _execute(){\r
-                    if (typeof o.callBefore == "function") \r
-                        o.callBefore(tooltip, that, o);\r
-                    if (talk) \r
-                        o = $.listen(o); //ping for new settings\r
-                    if (o.auto) {\r
-                        var display;\r
-                        if (o.content) {\r
-                            if (!o.degrade) \r
-                                display = "block";\r
-                        }\r
-                        else \r
-                            if (title && !o.degrade) {\r
-                                $(tooltip).html(unescape(title));\r
-                                display = "block";\r
-                            }\r
-                            else {\r
-                                display = "none";\r
-                            }\r
-                        if (display == "block" && o.fadeIn) \r
-                            $(tooltip).fadeIn(o.fadeIn);\r
-                        else \r
-                            tooltip.style.display = display;\r
-                    }\r
-                }\r
-                \r
-                if (o.delay > 0) {\r
-                    timeout = setTimeout(function(){\r
-                        _execute();\r
-                    }, o.delay);\r
-                }\r
-                else {\r
-                    _execute();\r
-                }\r
-            }\r
-            \r
-            this.onmousemove = function(ev){\r
-                var e = (ev) ? ev : window.event, that = this;\r
-                over = this; //tracks the event trigger in the plugin-global "over"\r
-                if (o.follow || firstMove) {\r
-                    var scrollY = $(window).scrollTop(), scrollX = $(window).scrollLeft(), top = e.clientY + scrollY + o.offsetY, left = e.clientX + scrollX + o.offsetX, outerH = $(o.appendTip).outerHeight(), innerH = $(o.appendTip).innerHeight(), maxLeft = $(window).width() + scrollX - $(tooltip).outerWidth(), maxTop = $(window).height() + scrollY - $(tooltip).outerHeight();\r
-                    \r
-                    top = (outerH > innerH) ? top - (outerH - innerH) : top; //if appended area (usually BODY) has a border on top, adjust\r
-                    maxed = (top > maxTop || left > maxLeft) ? true : false;\r
-                    \r
-                    if (left - scrollX <= 0 && o.offsetX < 0) \r
-                        left = scrollX;\r
-                    else \r
-                        if (left > maxLeft) \r
-                            left = maxLeft;\r
-                    if (top - scrollY <= 0 && o.offsetY < 0) \r
-                        top = scrollY;\r
-                    else \r
-                        if (top > maxTop) \r
-                            top = maxTop;\r
-                    \r
-                    tooltip.style.top = top + "px";\r
-                    tooltip.style.left = left + "px";\r
-                    firstMove = false;\r
-                }\r
-            }\r
-            \r
-            this.onmouseout = function(){\r
-                clearTimeout(timeout);\r
-                var that = this;\r
-                firstMove = true;\r
-                if (!o.follow || maxed || (o.offsetX < 0 && o.offsetY < 0)) {\r
-                    setTimeout(function(){\r
-                        iId = setInterval(function(){\r
-                            offConditions(that)\r
-                        }, 1)\r
-                    }, 1);\r
-                }\r
-                else {\r
-                    offConditions(this);\r
-                }\r
-            }\r
-            \r
-            if (typeof o.clickAction == "function") {\r
-                this.onclick = function(){\r
-                    o.clickAction(tooltip, this);\r
-                }\r
-            }\r
-        });\r
-    }\r
-})(jQuery);
\ No newline at end of file
diff --git a/project/static/js/messages.js b/project/static/js/messages.js
deleted file mode 100644 (file)
index eddb46e..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*global Editor*/
-Editor.MessageCenter = Editor.Object.extend({
-    init: function() {
-        this.messages = [];
-        this.flashMessages = [];
-        this.firstFlashMessage = null;
-        this.timeout = null;
-        console.log("MSC-init:", Date(), this);
-    },
-  
-    addMessage: function(type, tag, text, flash)
-    {
-        if (!tag) tag = '#default'
-        
-        if (!flash) {
-            flash = text;
-        }
-
-        this.messages.push({
-            type: type,
-            text: text
-        });
-
-        this.flashMessages.push({
-            type: type,
-            text: flash,
-            tag: tag
-        });
-
-        if(this.timeout) {
-            if(this.flashMessages[0] && (this.flashMessages[0].tag == tag))
-            {
-                clearTimeout(this.timeout);
-                this.timeout = null;
-                this.changeFlashMessage();
-            }
-        }       
-        
-        else {
-            /* queue was empty at the start */
-            if (this.flashMessages.length == 1) {
-                console.log("MSC-added-fisrt", Date(), this);
-                this.set('firstFlashMessage', this.flashMessages[0]);
-                this.timeout = setTimeout(this.changeFlashMessage.bind(this), 3000);
-            }
-
-        }
-        
-    },
-  
-    changeFlashMessage: function() 
-    {
-        console.log("MSC-change", Date(), this);
-        var previous = this.flashMessages.splice(0, 1);
-        
-        if (this.flashMessages.length > 0) 
-        {
-            console.log("MSC-chaning-first", Date(), this);
-            this.set('firstFlashMessage', this.flashMessages[0]);            
-            this.timeout = setTimeout(this.changeFlashMessage.bind(this), 3000);
-        } else {
-            console.log("MSC-emptying", Date(), this);
-            this.set('firstFlashMessage', null);
-        }
-    }
-  
-});
-
-
-var messageCenter = new Editor.MessageCenter();
-
diff --git a/project/static/js/models.js b/project/static/js/models.js
deleted file mode 100644 (file)
index 6399c03..0000000
+++ /dev/null
@@ -1,604 +0,0 @@
-/*globals Editor fileId SplitView PanelContainerView EditorView FlashView messageCenter*/
-Editor.Model = Editor.Object.extend({
-    synced: false,
-    data: null
-});
-
-Editor.ToolbarButtonsModel = Editor.Model.extend({
-    className: 'Editor.ToolbarButtonsModel',
-    buttons: {},
-  
-    init: function() {
-        this._super();
-    },
-  
-    load: function() {
-        if (!this.get('buttons').length) {
-            $.ajax({
-                url: documentInfo.toolbarURL,
-                dataType: 'json',
-                success: this.loadSucceeded.bind(this)
-            });
-        }
-    },
-  
-    loadSucceeded: function(data)
-    {
-        // do some escaping
-        $.each(data, function() {
-            $.each(this.buttons, function() {
-                //do some lame escapes
-                this.tooltip = this.tooltip.replace(/"/g, "&#34;");
-            });
-        });
-        this.set('buttons', data);
-    }
-});
-
-
-// Stany modelu:
-//
-//                  -> error -> loading
-//                 /
-// empty -> loading -> synced -> unsynced -> loading
-//                           \
-//                            -> dirty -> updating -> updated -> synced
-//
-Editor.XMLModel = Editor.Model.extend({
-    _className: 'Editor.XMLModel',
-    serverURL: null,
-    data: '',
-    state: 'empty',
-  
-    init: function(document, serverURL) {
-        this._super();
-        this.set('state', 'empty');
-        this.set('revision', document.get('revision'));
-        this.document = document;
-        this.serverURL = serverURL;
-        this.toolbarButtonsModel = new Editor.ToolbarButtonsModel();
-        this.addObserver(this, 'data', this.dataChanged.bind(this));
-    },
-  
-    load: function(force) {
-        if (force || this.get('state') == 'empty') {
-            this.set('state', 'loading');
-            messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...');
-            $.ajax({
-                url: this.serverURL,
-                dataType: 'text',
-                data: {
-                    revision: this.get('revision'),
-                    user: this.document.get('user')
-                    },
-                success: this.loadingSucceeded.bind(this),
-                error: this.loadingFailed.bind(this)
-            });
-            return true;
-        }
-        return false;
-    },
-  
-    loadingSucceeded: function(data) {
-        if (this.get('state') != 'loading') {
-            alert('erroneous state:', this.get('state'));
-        }
-        this.set('data', data);
-        this.set('state', 'synced');
-        messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)');
-    },
-  
-    loadingFailed: function() {
-        if (this.get('state') != 'loading') {
-            alert('erroneous state:', this.get('state'));
-        }
-        var message = parseXHRError(response);
-        
-        this.set('error', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
-        this.set('state', 'error');
-        messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-(');
-    },
-  
-    save: function(message) {
-        if (this.get('state') == 'dirty') {
-            this.set('state', 'updating');
-            messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...');
-      
-            var payload = {
-                contents: this.get('data'),
-                revision: this.get('revision'),
-                user: this.document.get('user')
-            };
-            if (message) {
-                payload.message = message;
-            }
-      
-            $.ajax({
-                url: this.serverURL,
-                type: 'post',
-                dataType: 'json',
-                data: payload,
-                success: this.saveSucceeded.bind(this),
-                error: this.saveFailed.bind(this)
-            });
-            return true;
-        }
-        return false;
-    },
-  
-    saveSucceeded: function(data) {
-        if (this.get('state') != 'updating') {
-            alert('erroneous state:', this.get('state'));
-        }
-        this.set('revision', data.revision);
-        this.set('state', 'updated');
-        messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)');
-    },
-  
-    saveFailed: function() {
-        if (this.get('state') != 'updating') {
-            alert('erroneous state:', this.get('state'));
-        }
-        messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-(');
-        this.set('state', 'dirty');
-    },
-  
-    // For debbuging
-    set: function(property, value) {
-        if (property == 'state') {
-            console.log(this.description(), ':', property, '=', value);
-        }
-        return this._super(property, value);
-    },
-  
-    dataChanged: function(property, value) {
-        if (this.get('state') == 'synced') {
-            this.set('state', 'dirty');
-        }
-    },
-  
-    dispose: function() {
-        this.removeObserver(this);
-        this._super();
-    }
-});
-
-
-Editor.HTMLModel = Editor.Model.extend({
-    _className: 'Editor.HTMLModel',
-    dataURL: null,
-    htmlURL: null,
-    renderURL: null,
-    displaData: '',
-    xmlParts: {},
-    state: 'empty',
-  
-    init: function(document, dataURL, htmlURL) {
-        this._super();
-        this.set('state', 'empty');
-        this.set('revision', document.get('revision'));        
-        
-        this.document = document;
-        this.htmlURL = htmlURL;
-        this.dataURL = dataURL;
-        this.renderURL = documentInfo.renderURL;
-        this.xmlParts = {};
-    },
-  
-    load: function(force) {
-        if (force || this.get('state') == 'empty') {
-            this.set('state', 'loading');
-
-            // load the transformed data
-            // messageCenter.addMessage('info', 'Wczytuję HTML...');
-
-            $.ajax({
-                url: this.htmlURL,
-                dataType: 'text',
-                data: {
-                    revision: this.get('revision'),
-                    user: this.document.get('user')
-                    },
-                success: this.loadingSucceeded.bind(this),
-                error: this.loadingFailed.bind(this)
-            });
-        }
-    },    
-  
-    loadingSucceeded: function(data) {
-        if (this.get('state') != 'loading') {
-            alert('erroneous state:', this.get('state'));
-        }
-        this.set('data', data);
-        this.set('state', 'synced');
-    },
-  
-    loadingFailed: function(response) {
-        if (this.get('state') != 'loading') {
-            alert('erroneous state:', this.get('state'));
-        }
-        
-        var message = parseXHRError(response);
-        
-        this.set('error', '<p>Nie udało się wczytać widoku HTML: </p>' + message);
-        this.set('state', 'error');        
-    },
-
-    getXMLPart: function(elem, callback)
-    {
-        var path = elem.attr('wl2o:path');
-        if(!this.xmlParts[path])
-            this.loadXMLPart(elem, callback);
-        else
-            callback(path, this.xmlParts[path]);
-    },
-
-    loadXMLPart: function(elem, callback)
-    {
-        var path = elem.attr('wl2o:path');
-        var self = this;
-
-        $.ajax({
-            url: this.dataURL,
-            dataType: 'text',
-            data: {
-                revision: this.get('revision'),
-                user: this.document.get('user'),
-                part: path
-            },
-            success: function(data) {
-                self.xmlParts[path] = data;
-                callback(path, data);
-            },
-            // TODO: error handling
-            error: function(data) {
-                console.log('Failed to load fragment');
-                callback(undefined, undefined);
-            }
-        });
-    },
-
-    putXMLPart: function(elem, data) {
-        var self = this;
-      
-        var path = elem.attr('wl2o:path');
-        this.xmlParts[path] = data;
-
-        this.set('state', 'unsynced');
-
-        /* re-render the changed fragment */
-        $.ajax({
-            url: this.renderURL,
-            type: "POST",
-            dataType: 'text; charset=utf-8',
-            data: {
-                fragment: data,
-                part: path
-            },
-            success: function(htmldata) {
-                elem.replaceWith(htmldata);
-                self.set('state', 'dirty');
-            }
-        });
-    },
-
-    save: function(message) {
-        if (this.get('state') == 'dirty') {
-            this.set('state', 'updating');
-
-            var payload = {
-                chunks: $.toJSON(this.xmlParts),
-                revision: this.get('revision'),
-                user: this.document.get('user')
-            };
-
-            if (message) {
-                payload.message = message;
-            }
-
-            console.log(payload)
-
-            $.ajax({
-                url: this.dataURL,
-                type: 'post',
-                dataType: 'json',
-                data: payload,
-                success: this.saveSucceeded.bind(this),
-                error: this.saveFailed.bind(this)
-            });
-            return true;
-        }
-        return false;
-      
-    },
-
-    saveSucceeded: function(data) {
-        if (this.get('state') != 'updating') {
-            alert('erroneous state:', this.get('state'));
-        }
-
-        // flush the cache
-        this.xmlParts = {};
-    
-        this.set('revision', data.revision);
-        this.set('state', 'updated');
-    },
-
-    saveFailed: function() {
-        if (this.get('state') != 'updating') {
-            alert('erroneous state:', this.get('state'));
-        }        
-        this.set('state', 'dirty');
-    },
-
-    // For debbuging
-    set: function(property, value) {
-        if (property == 'state') {
-            console.log(this.description(), ':', property, '=', value);
-        }
-        return this._super(property, value);
-    }
-});
-
-
-Editor.ImageGalleryModel = Editor.Model.extend({
-    _className: 'Editor.ImageGalleryModel',
-    serverURL: null,
-    data: [],
-    state: 'empty',
-
-    init: function(serverURL) {
-        this._super();
-        this.set('state', 'empty');
-        this.serverURL = serverURL;
-        // olewać data
-        this.pages = [];
-    },
-
-    load: function(force) {
-        if (force || this.get('state') == 'empty') {
-            this.set('state', 'loading');
-            $.ajax({
-                url: this.serverURL,
-                dataType: 'json',
-                success: this.loadingSucceeded.bind(this)
-            });
-        }
-    },
-
-    loadingSucceeded: function(data) {
-        if (this.get('state') != 'loading') {
-            alert('erroneous state:', this.get('state'));
-        }
-
-        console.log('galleries:', data);
-
-        if (data.length === 0) {
-            this.set('data', []);
-        } else {            
-            this.set('data', data[0].pages);
-        }
-
-        this.set('state', 'synced');
-    },
-
-    set: function(property, value) {
-        if (property == 'state') {
-            console.log(this.description(), ':', property, '=', value);
-        }
-        return this._super(property, value);
-    }
-});
-
-
-Editor.DocumentModel = Editor.Model.extend({
-    _className: 'Editor.DocumentModel',
-    data: null, // name, text_url, user_revision, latest_shared_rev, parts_url, dc_url, size, merge_url
-    contentModels: {},
-    state: 'empty',
-    errors: '',
-    revision: '',
-    user: '',
-  
-    init: function() {
-        this._super();
-        this.set('state', 'empty');        
-    },
-  
-    load: function() {
-        if (this.get('state') == 'empty') {
-            this.set('state', 'loading');
-            messageCenter.addMessage('info', 'docload', 'Ładuję dane dokumentu...');
-            $.ajax({
-                cache: false,
-                url: documentInfo.docURL,
-                dataType: 'json',
-                success: this.successfulLoad.bind(this),
-                error: this.failedLoad.bind(this)
-            });
-        }
-    },
-  
-    successfulLoad: function(data) {
-        this.set('data', data);
-        this.set('state', 'synced');
-
-        this.set('revision', data.user_revision);
-        this.set('user', data.user);
-
-        this.contentModels = {
-            'xml': new Editor.XMLModel(this, data.text_url),
-            'html': new Editor.HTMLModel(this, data.text_url, data.html_url),
-            'gallery': new Editor.ImageGalleryModel(this, data.gallery_url)
-        };        
-
-        for (var key in this.contentModels) {
-            this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
-        }
-
-        this.error = '';
-
-        messageCenter.addMessage('success', 'docload', 'Dokument załadowany poprawnie :-)');
-    },
-
-    failedLoad: function(response) {
-        if (this.get('state') != 'loading') {
-            alert('erroneous state:', this.get('state'));
-        }
-        
-        var message = parseXHRError(response);        
-        this.set('error', '<h2>Nie udało się wczytać dokumentu</h2><p>'+message+"</p>");
-        this.set('state', 'error');
-    },
-  
-    contentModelStateChanged: function(property, value, contentModel) {
-        if (value == 'dirty') {
-            this.set('state', 'dirty');
-            for (var key in this.contentModels) {
-                if (this.contentModels[key].guid() != contentModel.guid()) {
-                    this.contentModels[key].set('state', 'unsynced');
-                }
-            }
-        } else if (value == 'updated') {
-            this.set('state', 'synced');
-            for (key in this.contentModels) {
-                if (this.contentModels[key].guid() == contentModel.guid()) {
-                    this.contentModels[key].set('state', 'synced');
-                    this.data.user_revision = this.contentModels[key].get('revision');
-                }
-            }
-            for (key in this.contentModels) {
-                if (this.contentModels[key].guid() != contentModel.guid()) {
-                    this.contentModels[key].set('revision', this.data.user_revision);
-                    this.contentModels[key].set('state', 'empty');
-                }
-            }
-        }
-    },
-  
-    saveDirtyContentModel: function(message) {
-        for (var key in this.contentModels) {
-            if (this.contentModels[key].get('state') == 'dirty') {
-                this.contentModels[key].save(message);
-                break;
-            }
-        }
-    },
-  
-    update: function() {
-        this.set('state', 'loading');
-        messageCenter.addMessage('info', 'Uaktualniam dokument...');
-        $.ajax({
-            url: this.data.merge_url,
-            dataType: 'json',
-            type: 'post',
-            data: {
-                type: 'update',
-                revision: this.revision,
-                user: this.user
-            },
-            complete: this.updateCompleted.bind(this),
-            success: function(data) {
-                this.set('updateData', data);
-            }.bind(this)
-        });
-    },
-  
-    updateCompleted: function(xhr, textStatus) {
-        console.log(xhr.status, textStatus);
-        if (xhr.status == 200) { // Sukces
-            this.data = this.get('updateData');
-            this.revision = this.data.user_revision;
-            this.user = this.data.user;
-            
-            messageCenter.addMessage('info', null, 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision,
-                'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision);
-            for (var key in this.contentModels) {
-                this.contentModels[key].set('revision', this.data.user_revision);
-                this.contentModels[key].set('state', 'empty');
-            }
-            messageCenter.addMessage('success', null, 'Uaktualniłem dokument do najnowszej wersji :-)');
-        } else if (xhr.status == 202) { // Wygenerowano PullRequest (tutaj?)
-        } else if (xhr.status == 204) { // Nic nie zmieniono
-            messageCenter.addMessage('info', null, 'Nic się nie zmieniło od ostatniej aktualizacji. Po co mam uaktualniać?');
-        } else if (xhr.status == 409) { // Konflikt podczas operacji
-            messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas aktualizacji. Pędź po programistów! :-(');
-        } else if (xhr.status == 500) {
-            messageCenter.addMessage('critical', null, 'Błąd serwera. Pędź po programistów! :-(');
-        }
-        this.set('state', 'synced');
-        this.set('updateData', null);
-    },
-  
-    merge: function(message) {
-        this.set('state', 'loading');
-        messageCenter.addMessage('info', null, 'Scalam dokument z głównym repozytorium...');
-        $.ajax({
-            url: this.data.merge_url,
-            type: 'post',
-            dataType: 'json',
-            data: {
-                type: 'share',
-                revision: this.revision,
-                user: this.user,
-                message: message
-            },
-            complete: this.mergeCompleted.bind(this),
-            success: function(data) {
-                this.set('mergeData', data);
-            }.bind(this)
-        });
-    },
-  
-    mergeCompleted: function(xhr, textStatus) {
-        console.log(xhr.status, textStatus);
-        if (xhr.status == 200) { // Sukces
-            this.data = this.get('updateData');
-            this.revision = this.data.user_revision;
-            this.user = this.data.user;
-            
-            for (var key in this.contentModels) {
-                this.contentModels[key].set('revision', this.revision);
-                this.contentModels[key].set('state', 'empty');
-            }
-
-            messageCenter.addMessage('success', null, 'Scaliłem dokument z głównym repozytorium :-)');
-        } else if (xhr.status == 202) { // Wygenerowano PullRequest
-            messageCenter.addMessage('success', null, 'Wysłałem prośbę o scalenie dokumentu z głównym repozytorium.');
-        } else if (xhr.status == 204) { // Nic nie zmieniono
-            messageCenter.addMessage('info', null, 'Nic się nie zmieniło od ostatniego scalenia. Po co mam scalać?');
-        } else if (xhr.status == 409) { // Konflikt podczas operacji
-            messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas scalania. Pędź po programistów! :-(');
-        } else if (xhr.status == 500) {
-            messageCenter.addMessage('critical', null, 'Błąd serwera. Pędź po programistów! :-(');
-        }
-        this.set('state', 'synced');
-        this.set('mergeData', null);
-    },
-  
-    // For debbuging
-    set: function(property, value) {
-        if (property == 'state') {
-            console.log(this.description(), ':', property, '=', value);
-        }
-        return this._super(property, value);
-    }
-});
-
-
-var leftPanelView, rightPanelContainer, doc;
-
-$(function()
-{
-    var flashView = new FlashView('#flashview', messageCenter);
-    
-    doc = new Editor.DocumentModel();
-
-    EditorView = new EditorView('#body-wrap', doc);
-    EditorView.freeze("<h1>Wczytuję dokument...</h1>");
-
-    leftPanelView = new PanelContainerView('#left-panel-container', doc);
-    rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
-
-    
-});
diff --git a/project/static/js/views/button_toolbar.js b/project/static/js/views/button_toolbar.js
deleted file mode 100644 (file)
index 7f03998..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*globals View render_template scriptletCenter*/
-var ButtonToolbarView = View.extend({
-    _className: 'ButtonToolbarView',
-    template: null,
-    buttons: null,
-  
-    init: function(element, model, parent, template) {
-        this._super(element, model, null);
-        this.parent = parent;
-        this.template = 'button-toolbar-view-template';
-    
-        this.model.addObserver(this, 'buttons', this.modelButtonsChanged.bind(this));
-        this.buttons = this.model.get('buttons');
-        this.model.load();
-        this.render();
-    },
-  
-    modelButtonsChanged: function(property, value) {
-        this.set('buttons', value);
-        this.render();
-    },
-  
-    render: function() {
-        $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview');
-        $('.buttontoolbarview-button', this.element).unbind('click.buttontoolbarview');
-        var self = this;
-    
-        this.element.html(render_template(this.template, this));
-    
-        $('.buttontoolbarview-tab', this.element).bind('click.buttontoolbarview', function() {
-            var groupIndex = $(this).attr('ui:groupindex');
-            $('.buttontoolbarview-group', self.element).each(function() {
-                if ($(this).attr('ui:groupindex') == groupIndex) {
-                    $(this).show();
-                } else {
-                    $(this).hide();
-                }
-            });
-            $(self.element).trigger('resize');
-        });
-    
-        $('.buttontoolbarview-button', this.element).
-        bind('click.buttontoolbarview', this.buttonPressed.bind(this) );
-            
-        $(this.element).trigger('resize');
-    },
-
-    buttonPressed: function(event)
-    {
-        var self = this;
-        var target = event.target;
-        
-        var groupIndex = parseInt($(target).attr('ui:groupindex'), 10);
-        var buttonIndex = parseInt($(target).attr('ui:buttonindex'), 10);
-        var button = this.get('buttons')[groupIndex].buttons[buttonIndex];
-        var scriptletId = button.scriptlet_id;
-        var params = eval('(' + button.params + ')'); // To nie powinno być potrzebne
-
-        console.log('Executing', scriptletId, 'with params', params);
-        try {
-            self.parent.freeze('Wykonuję akcję...');
-            setTimeout(function() {
-                scriptletCenter.scriptlets[scriptletId](self.parent, params);
-                self.parent.unfreeze();
-            }, 10);
-        } catch(e) {
-            console.log("Scriptlet", scriptletId, "failed.", e);
-        }
-
-    },
-  
-    dispose: function() {
-        $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview');
-        $('.buttontoolbarview-button', this.element).unbind('click.buttontoolbarview');
-        this._super();
-    }
-});
-
diff --git a/project/static/js/views/editor.js b/project/static/js/views/editor.js
deleted file mode 100644 (file)
index d43caaa..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*global View render_template panels */
-var EditorView = View.extend({
-    _className: 'EditorView',
-    element: null,
-    model: null,
-    template: null,
-  
-    init: function(element, model, template) {
-        this._super(element, model, template);
-    
-        this.quickSaveButton = $('#action-quick-save', this.element).bind('click.editorview', this.quickSave.bind(this));
-        this.commitButton = $('#action-commit', this.element).bind('click.editorview', this.commit.bind(this));
-        this.updateButton = $('#action-update', this.element).bind('click.editorview', this.update.bind(this));
-        this.mergeButton = $('#action-merge', this.element).bind('click.editorview', this.merge.bind(this));
-    
-        this.model.addObserver(this, 'state', this.modelStateChanged.bind(this));
-        this.modelStateChanged('state', this.model.get('state'));        
-
-        this.splitView = new SplitView('#splitview', doc);
-        
-        // Inicjalizacja okien jQuery Modal
-        $('#commit-dialog', this.element).
-        jqm({
-            modal: true,
-            onShow: this.loadRelatedIssues.bind(this)
-        });
-    
-        $('#commit-dialog-cancel-button', this.element).click(function() {
-            $('#commit-dialog-error-empty-message').hide();
-            $('#commit-dialog').jqmHide();
-        });
-        
-    
-        // $('#split-dialog').jqm({
-        //      modal: true,
-        //      onShow: $.fbind(self, self.loadSplitDialog)
-        //  }).
-        //  jqmAddClose('button.dialog-close-button');
-    
-        this.model.load();
-    },
-  
-    quickSave: function(event) {
-        this.model.saveDirtyContentModel();
-    },
-  
-    commit: function(event) {
-        $('#commit-dialog', this.element).jqmShow({
-            callback: this.doCommit.bind(this)
-            });
-    },
-  
-    doCommit: function(message) {
-        this.model.saveDirtyContentModel(message);
-    },
-  
-    update: function(event) {
-        this.model.update();
-    },
-  
-    merge: function(event) {
-        $('#commit-dialog', this.element).jqmShow({
-            callback: this.doMerge.bind(this)
-            });
-    },
-  
-    doMerge: function(message) {
-        this.model.merge(message);
-    },
-  
-    loadRelatedIssues: function(hash) {
-        var self = this;
-        var c = $('#commit-dialog-related-issues');
-
-        $('#commit-dialog-save-button').click(function(event, data)
-        {
-            if ($('#commit-dialog-message').val().match(/^\s*$/)) {
-                $('#commit-dialog-error-empty-message').fadeIn();
-            } else {
-                $('#commit-dialog-error-empty-message').hide();
-                $('#commit-dialog').jqmHide();
-
-                var message = $('#commit-dialog-message').val();
-                $('#commit-dialog-related-issues input:checked')
-                .each(function() {
-                    message += ' refs #' + $(this).val();
-                });
-                console.log("COMMIT APROVED", hash.t);
-                hash.t.callback(message);
-            }
-            return false;
-        });
-
-        $("div.loading-box", c).show();
-        $("div.fatal-error-box", c).hide();
-        $("div.container-box", c).hide();
-    
-        $.getJSON(c.attr('ui:ajax-src') + '?callback=?',
-            function(data, status)
-            {
-                var fmt = '';
-                $(data).each( function() {
-                    fmt += '<label><input type="checkbox" checked="checked"';
-                    fmt += ' value="' + this.id + '" />' + this.subject +'</label>\n';
-                });
-                $("div.container-box", c).html(fmt);
-                $("div.loading-box", c).hide();
-                $("div.container-box", c).show();
-            });
-    
-        hash.w.show();
-    },
-  
-    modelStateChanged: function(property, value) {
-        // Uaktualnia stan przycisków
-        if (value == 'dirty') {
-            this.quickSaveButton.attr('disabled', null);
-            this.commitButton.attr('disabled', null);
-            this.updateButton.attr('disabled', 'disabled');
-            this.mergeButton.attr('disabled', 'disabled');
-        } else if (value == 'synced') {            
-            this.quickSaveButton.attr('disabled', 'disabled');
-            this.commitButton.attr('disabled', 'disabled');
-            this.updateButton.attr('disabled', null);
-            this.mergeButton.attr('disabled', null);
-            this.unfreeze();
-        } else if (value == 'empty') {
-            this.quickSaveButton.attr('disabled', 'disabled');
-            this.commitButton.attr('disabled', 'disabled');
-            this.updateButton.attr('disabled', 'disabled');
-            this.mergeButton.attr('disabled', 'disabled');
-        } else if (value == 'error') {
-            this.freeze(this.model.get('error'));
-            this.quickSaveButton.attr('disabled', 'disabled');
-            this.commitButton.attr('disabled', 'disabled');
-            this.updateButton.attr('disabled', 'disabled');
-            this.mergeButton.attr('disabled', 'disabled');
-            
-        }
-    },
-  
-    dispose: function() {
-        $('#action-quick-save', this.element).unbind('click.editorview');
-        $('#action-commit', this.element).unbind('click.editorview');
-        $('#action-update', this.element).unbind('click.editorview');
-        $('#action-merge', this.element).unbind('click.editorview');
-
-        this.model.removeObserver(this);
-        this._super();
-    }    
-});
diff --git a/project/static/js/views/flash.js b/project/static/js/views/flash.js
deleted file mode 100644 (file)
index e67b046..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*globals View render_template*/
-var FlashView = View.extend({
-  template: 'flash-view-template',
-
-  init: function(element, model, template) {
-    this.shownMessage = null;
-    this._super(element, model, template);
-    this.setModel(model);
-  },
-  
-  setModel: function(model) {
-    if (this.model) {
-      this.model.removeObserver(this);
-    }
-    this.model = model;
-    this.shownMessage = null;
-    if (this.model) {
-      this.shownMessage = this.model.get('firstFlashMessage');
-      this.model.addObserver(this, 'firstFlashMessage', this.modelFirstFlashMessageChanged.bind(this));
-    }
-    this.render();
-  },
-  
-  render: function() {
-    this.element.html(render_template(this.template, this));
-    setTimeout(function() {}, 0);
-  },
-  
-  modelFirstFlashMessageChanged: function(property, value) {    
-    this.element.fadeOut(200, (function() {
-    
-    this.element.css({'z-index': 0});
-    this.shownMessage = value;
-    this.render();
-
-    if(this.shownMessage) {
-        this.element.css({'z-index': 1000});
-        this.element.fadeIn();
-    };
-    }).bind(this));
-  }
-});
diff --git a/project/static/js/views/gallery.js b/project/static/js/views/gallery.js
deleted file mode 100644 (file)
index 7449344..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-/*global View render_template panels */
-var ImageGalleryView = View.extend({
-  _className: 'ImageGalleryView',
-  element: null,
-  model: null,
-  currentPage: -1,
-  pageZoom: 1.0,
-  template: 'image-gallery-view-template',
-  
-  init: function(element, model, parent, template) 
-  {    
-    console.log("init for gallery");
-    this._super(element, model, template);
-    this.parent = parent;
-
-    console.log("galley model", this.model);
-       
-    this.model
-      .addObserver(this, 'data', this.modelDataChanged.bind(this))
-      .addObserver(this, 'state', this.modelStateChanged.bind(this));
-   
-    //$('.image-gallery-view', this.element).html(this.model.get('data'));
-    this.modelStateChanged('state', this.model.get('state'));
-    this.model.load();    
-  },
-  
-  modelDataChanged: function(property, value) 
-  {    
-    if( property == 'data')
-    {
-        this.render();
-        this.gotoPage(this.currentPage);        
-    }   
-  },
-
-  gotoPage: function(index) 
-  {
-     if (index < 0) 
-         index = 0;
-    
-     var n = this.$pages.length;
-     if (index >= n) index = n-1;
-
-     if( (this.currentPage == index) )
-         return;
-
-     var cpage = this.$currentPage();
-
-     if(cpage) {
-         var offset = this.pageViewOffset(cpage);
-         this.cleanPage(cpage);
-     }
-     
-     this.currentPage = index;
-
-     cpage = this.$currentPage()
-     this.renderImage(cpage);
-
-     if(offset) {
-         cpage.css({top: offset.y, left: offset.x});
-     }
-
-     var self = this;
-     $('img', cpage).bind('load', function() {
-        if(offset)
-             self.setPageViewOffset(cpage, offset);
-     });
-     
-     cpage.show();
-
-     if(this.currentPage == n-1)
-          this.$nextButton.attr('disabled', 'disabled');
-     else
-          this.$nextButton.removeAttr('disabled');
-
-      if(this.currentPage == 0)
-          this.$prevButton.attr('disabled', 'disabled');
-      else
-          this.$prevButton.removeAttr('disabled');
-
-      this.$pageInput.val( (this.currentPage+1) );
-  },
-  
-  reload: function() {},
-  
-  modelStateChanged: function(property, value) {   
-    if (value == 'loading') {
-      this.parent.freeze('Ładowanie...');
-    } else {
-      this.parent.unfreeze();
-    }
-  },
-
-  $currentPage: function() {
-      if(this.currentPage >= 0 && this.currentPage < this.$pages.length)
-          return $(this.$pages[this.currentPage]);
-      else
-          return undefined;
-  },    
-
-  cleanPage: function($page) {
-    $page.hide();
-    $('img', $page).unbind();
-    
-    $page.empty();
-    
-    this.setPageViewOffset($page, {x:0, y:0});
-  },
-
-  pageDragStart: function(event)
-  {      
-      this.dragStart = {x: event.clientX, y: event.clientY};
-      $(window).bind('mousemove.imagedrag', this.pageDrag.bind(this));
-      $(window).bind('mouseup.imagedrag', this.pageDragStop.bind(this));
-      
-      this.$currentPage().css('cursor', 'move');
-
-      return false;
-  },
-
-  pageDrag: function(event)
-  {
-      if(!this.dragStart) return;
-
-      var delta = {
-           x: this.dragStart.x - event.clientX,
-           y: this.dragStart.y - event.clientY };     
-
-      var offset = this.pageViewOffset( $(this.$pages[this.currentPage]) );
-      offset.x -= delta.x;
-      offset.y -= delta.y;
-      this.setPageViewOffset( $(this.$pages[this.currentPage]), offset);
-      
-      this.dragStart = {x: event.clientX, y: event.clientY };     
-      return false;
-  },
-
-  pageDragStop: function(event) {
-      this.$currentPage().css('cursor', 'auto');
-
-      this.dragStart = undefined;
-      $(window).unbind('mousemove.imagedrag');
-      $(window).unbind('mouseup.imagedrag');
-
-      return false;
-  },
-
-  pageViewOffset: function($page) {
-      var left = parseInt($page.css('left'));
-      var top = parseInt($page.css('top'));
-
-      return {x: left, y: top};
-  },
-
-  setPageViewOffset: function($page, offset) {
-      // check if the image will be actually visible
-      // and correct
-      var MARGIN = 30;
-
-
-      var vp_width = this.$pageListRoot.width();
-      var vp_height = this.$pageListRoot.height();
-      
-      var width = $page.outerWidth();
-      var height = $page.outerHeight();
-
-      // console.log(offset, vp_width, vp_height, width, height);
-      if( offset.x+width-MARGIN < 0 ) {
-        // console.log('too much on the left', offset.x, -width)
-        offset.x = -width+MARGIN;
-      }
-      
-      // too much on the right
-      if( offset.x > vp_width-MARGIN) {
-          offset.x = vp_width-MARGIN;
-          // console.log('too much on the right', offset.x, vp_width, width)
-      }
-      
-      if( offset.y+height-MARGIN < 0)
-        offset.y = -height+MARGIN;      
-
-      if( offset.y > vp_height-MARGIN)
-          offset.y = vp_height-MARGIN;               
-      
-      $page.css({left: offset.x, top: offset.y});           
-  }, 
-  
-  renderImage: function(target) 
-  {
-      var source = target.attr('ui:model');
-      var orig_width = parseInt(target.attr('ui:width'));
-      var orig_height = parseInt(target.attr('ui:height'));
-
-      target.html('<img src="' + source
-           + '" width="' + Math.floor(orig_width * this.pageZoom)
-           + '" height="' + Math.floor(orig_height * this.pageZoom)
-           + '" />');
-       
-      $('img', target).
-        css({
-            'user-select': 'none',
-            '-webkit-user-select': 'none',
-            '-khtml-user-select': 'none',
-            '-moz-user-select': 'none'
-        }).
-        attr('unselectable', 'on').
-        mousedown(this.pageDragStart.bind(this));    
-  },
-
-  render: function() 
-  {
-      if(!this.model) return;            
-      
-      /* first unbind all */    
-      if(this.$nextButton) this.$nextButton.unbind();
-      if(this.$prevButton) this.$prevButton.unbind();
-      if(this.$jumpButton) this.$jumpButton.unbind();
-      if(this.$pageInput) this.$pageInput.unbind();
-
-      if(this.$zoomInButton) this.$zoomInButton.unbind();
-      if(this.$zoomOutButton) this.$zoomOutButton.unbind();
-      if(this.$zoomResetButton) this.$zoomResetButton.unbind();
-
-      /* render */
-      this._super();
-
-      /* fetch important parts */
-      this.$pageListRoot = $('.image-gallery-page-list', this.element);
-      this.$pages = $('.image-gallery-page-container', this.$pageListRoot);
-
-      this.$nextButton = $('.image-gallery-next-button', this.element);
-      this.$prevButton = $('.image-gallery-prev-button', this.element);
-      this.$pageInput = $('.image-gallery-current-page', this.element);
-
-      // this.$zoomSelect = $('.image-gallery-current-zoom', this.element);
-      this.$zoomInButton = $('.image-gallery-zoom-in', this.element);
-      this.$zoomOutButton = $('.image-gallery-zoom-out', this.element);
-      this.$zoomResetButton = $('.image-gallery-zoom-reset', this.element);
-
-      /* re-bind events */
-      this.$nextButton.click( this.nextPage.bind(this) );
-      this.$prevButton.click( this.prevPage.bind(this) );
-      this.$pageInput.change( this.jumpToPage.bind(this) );
-
-      // this.$zoomSelect.change( this.zoomChanged.bind(this) );
-      this.$zoomInButton.click( this.zoomInOneStep.bind(this) );
-      this.$zoomOutButton.click( this.zoomOutOneStep.bind(this) );
-      this.$zoomResetButton.click( this.zoomReset.bind(this) );
-
-      this.gotoPage(this.currentPage);
-      this.changePageZoom(this.pageZoom);
-  },
-
-  jumpToPage: function() {     
-        this.gotoPage(parseInt(this.$pageInput.val())-1);
-  },
-  
-  nextPage: function() {
-      this.gotoPage(this.currentPage + 1);    
-  },
-
-  prevPage: function() {
-      this.gotoPage(this.currentPage - 1);
-  },
-
-  zoomReset: function() {
-      this.changePageZoom(1.0);
-  },
-
-  zoomInOneStep: function() {
-      var zoom = this.pageZoom + 0.1;
-      if(zoom > 3.0) zoom = 3.0;
-      this.changePageZoom(zoom);
-  },
-
-  zoomOutOneStep: function() {
-      var zoom = this.pageZoom - 0.1;
-      if(zoom < 0.3) zoom = 0.3;
-      this.changePageZoom(zoom);
-  },
-
-  changePageZoom: function(value) {
-      var current = this.$currentPage();
-
-      if(!current) return;
-
-      var alpha = value/this.pageZoom;
-      this.pageZoom = value;
-
-      var nwidth = current.attr('ui:width') * this.pageZoom;
-      var nheight = current.attr('ui:height') * this.pageZoom;
-      var off_top = parseInt(current.css('top'));
-      var off_left = parseInt(current.css('left'));
-      
-      var vpx = this.$pageListRoot.width() * 0.5;
-      var vpy = this.$pageListRoot.height() * 0.5;
-      
-      var new_off_left = vpx - alpha*(vpx-off_left);
-      var new_off_top = vpy - alpha*(vpy-off_top);
-                 
-      $('img', current).attr('width', nwidth);
-      $('img', current).attr('height', nheight);
-      
-      this.setPageViewOffset(current, {
-          y: new_off_top, x: new_off_left
-      });
-
-      // this.$zoomSelect.val(this.pageZoom);
-      // console.log('Zoom is now', this.pageZoom);
-  },
-  
-  dispose: function()
-  {
-      console.log("Disposing gallery.");
-      this.model.removeObserver(this);
-      this._super();
-  }
-});
-
-// Register view
-panels['gallery'] = ImageGalleryView;
\ No newline at end of file
diff --git a/project/static/js/views/html.js b/project/static/js/views/html.js
deleted file mode 100644 (file)
index fa52bd0..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*global View render_template panels */
-var HTMLView = View.extend({
-    _className: 'HTMLView',
-    element: null,
-    model: null,
-    template: 'html-view-template',
-  
-    init: function(element, model, parent, template) {
-        this._super(element, model, template);
-        this.parent = parent;
-    
-        this.model
-        .addObserver(this, 'data', this.modelDataChanged.bind(this))        
-        .addObserver(this, 'state', this.modelStateChanged.bind(this));
-      
-        $('.htmlview', this.element).html(this.model.get('data'));
-        this.modelStateChanged('state', this.model.get('state'));
-        this.model.load();
-    },
-
-    modelDataChanged: function(property, value) {
-        $('.htmlview', this.element).html(value);
-        this.updatePrintLink();
-    },
-
-    updatePrintLink: function() {
-        var base = this.$printLink.attr('ui:baseref');
-        this.$printLink.attr('href', base + "?user="+this.model.document.get('user')+"&revision=" + this.model.get('revision'));
-    },
-  
-    modelStateChanged: function(property, value) 
-    {
-        var self = $(this);
-
-        if (value == 'synced' || value == 'dirty') {
-            this.unfreeze();
-        } else if (value == 'unsynced') {
-            this.freeze('Niezsynchronizowany...');
-        } else if (value == 'loading') {
-            this.freeze('Ładowanie...');
-        } else if (value == 'saving') {
-            this.freeze('Zapisywanie...');
-        } else if (value == 'error') {
-            this.freeze(this.model.get('error'));
-            $('.xml-editor-ref', this.overlay).click(
-            function(event) {
-                console.log("Sending scroll rq.", this);
-                try {
-                    var href = $(this).attr('href').split('-');
-                    var line = parseInt(href[1]);
-                    var column = parseInt(href[2]);
-                    
-                    $(document).trigger('xml-scroll-request', {line:line, column:column});
-                } catch(e) {
-                    console.log(e);
-                }
-                
-                return false;
-            });
-        }
-    },
-
-    render: function() {
-        this.element.unbind('click');
-
-        if(this.$printLink) this.$printLink.unbind();
-        this._super();
-        this.$printLink = $('.html-print-link', this.element);
-        this.updatePrintLink();
-
-        this.element.bind('click', this.itemClicked.bind(this));
-    },
-  
-    reload: function() {
-        this.model.load(true);
-    },
-  
-    dispose: function() {
-        this.model.removeObserver(this);
-        this._super();
-    },
-
-    itemClicked: function(event) 
-    {
-        var self = this;
-        
-        console.log('click:', event, event.ctrlKey, event.target);
-        var editableContent = null;
-        var $e = $(event.target);
-
-        var n = 0;
-
-        while( ($e[0] != this.element[0]) && !($e.attr('wl2o:editable'))
-            && n < 50)
-        {
-            // console.log($e, $e.parent(), this.element);
-            $e = $e.parent();
-            n += 1;
-        }
-      
-        if(!$e.attr('wl2o:editable'))
-            return true;
-    
-        // start edition on this node
-        
-
-        var $overlay = $(
-        '<div class="html-editarea">\n\
-            <p class="html-editarea-toolbar">\n\
-                <button class="html-editarea-save-button" type="button">Zapisz</button>\n\
-                <button class="html-editarea-cancel-button" type="button">Anuluj</button>\n\
-            </p>\n\
-            <textarea></textarea>\n\
-        </div>');
-
-        var x = $e[0].offsetLeft;
-        var y = $e[0].offsetTop;
-        var w = $e.outerWidth();
-        var h = $e.innerHeight();
-        $overlay.css({position: 'absolute', height: h, left: "5%", top: y, width: "90%"});
-        $e.offsetParent().append($overlay);
-
-        // load the original XML content
-        console.log($e, $e.offsetParent(), $overlay);
-                        
-        $('.html-editarea-cancel-button', $overlay).click(function() {
-            $overlay.remove();
-        });
-
-        $('.html-editarea-save-button', $overlay).click(function() {
-            $overlay.remove();
-
-            // put the part back to the model
-            self.model.putXMLPart($e, $('textarea', $overlay).val());
-        });
-
-        $('textarea', $overlay).focus(function() {
-            $overlay.css('z-index', 3000);
-        }).blur(function() {
-            $overlay.css('z-index', 2000);
-        });
-
-        this.model.getXMLPart($e, function(path, data) {
-            $('textarea', $overlay).val(data);
-        });
-        
-        return false;
-    }
-  
-});
-
-// Register view
-panels['html'] = HTMLView;
\ No newline at end of file
diff --git a/project/static/js/views/panel_container.js b/project/static/js/views/panel_container.js
deleted file mode 100644 (file)
index 6dbddd9..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*globals View render_template panels*/
-
-var PanelContainerView = View.extend({
-  _className: 'PanelContainerView',
-  element: null,
-  model: null,
-  template: 'panel-container-view-template',
-  contentView: null,
-  
-  init: function(element, model, template) {
-    this._super(element, model, template);
-
-    $('.panel-main-toolbar select', this.element.get(0)).bind('change.panel-container-view', this.selectChanged.bind(this));
-    $('.panel-main-toolbar .refresh', this.element.get(0))
-      .bind('click.panel-container-view', this.refreshButtonClicked.bind(this))
-      .attr('disabled', 'disabled');
-  },
-  
-  selectChanged: function(event) {
-    var value = $('select', this.element.get(0)).val();
-    var klass = panels[value];
-
-    if (this.contentView) {
-      this.contentView.dispose();
-      this.contentView = null;
-    }
-
-    if( value != 'empty') {
-    this.contentView = new klass($('.content-view', 
-      this.element.get(0)), this.model.contentModels[value], this);
-    $('.panel-main-toolbar .refresh', this.element.get(0)).attr('disabled', null);    
-    }
-  },
-  
-  refreshButtonClicked: function(event) {
-    if (this.contentView) {
-      console.log('refreshButtonClicked');
-      this.contentView.reload();
-    }
-  },
-  
-  dispose: function() {
-    $('.panel-main-toolbar .refresh', this.element.get(0)).unbind('click.panel-container-view');
-    $('.panel-main-toolbar select', this.element.get(0)).unbind('change.panel-container-view');
-    this._super();
-  }
-});
-
diff --git a/project/static/js/views/split.js b/project/static/js/views/split.js
deleted file mode 100644 (file)
index 30eda4a..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*globals View*/
-
-// Split view inspired by jQuery Splitter Plugin http://methvin.com/splitter/
-var SplitView = View.extend({
-  _className: 'SplitView',
-  splitbarClass: 'splitview-splitbar',
-  activeClass: 'splitview-active',
-  overlayClass: 'splitview-overlay',
-  element: null,
-  model: null,
-  zombie: null,
-  leftViewOffset: 0,
-  
-  // Cache
-  _splitbarWidth: 0,
-  
-  init: function(element, model) {
-    this._super(element, model, null);
-    this.element.css('position', 'relative');
-    this._resizingSubviews = false;    
-    
-    this.views = $(">*", this.element[0]).css({
-       position: 'absolute',                     // positioned inside splitter container
-       'z-index': 1,                                           // splitbar is positioned above
-       '-moz-outline-style': 'none',   // don't show dotted outline
-      overflow: 'auto'
-    });
-    
-    this.leftView = $(this.views[0]);
-    this.rightView = $(this.views[1]);
-    
-    this.splitbar = $(this.views[2] || '<div></div>')
-      .insertAfter(this.leftView)
-      .css({
-        position: 'absolute',
-        'user-select': 'none',
-        '-webkit-user-select': 'none',
-        '-khtml-user-select': 'none',
-        '-moz-user-select': 'none',
-        'z-index': 100
-      })
-      .attr('unselectable', 'on')
-      .addClass(this.splitbarClass)
-      .bind('mousedown.splitview', this.beginResize.bind(this));
-    
-    this._splitbarWidth = this.splitbar.outerWidth();
-    
-    // Solomon's algorithm ;-)
-    this.resplit(this.element.width() / 2);
-  },
-    
-  beginResize: function(event) {
-    this.zombie = this.zombie || this.splitbar.clone(false).insertAfter(this.leftView);
-    this.overlay = this.overlay || $('<div></div>').addClass(this.overlayClass).css({
-        position: 'absolute',
-        width: this.element.width(),
-        height: this.element.height(),
-        top: this.element.position().top,
-        left: this.element.position().left
-      }).appendTo(this.element);
-    this.views.css("-webkit-user-select", "none"); // Safari selects A/B text on a move
-    this.splitbar.addClass(this.activeClass);
-    this.leftViewOffset = this.leftView[0].offsetWidth - event.pageX;
-    
-    $(document)
-      .bind('mousemove.splitview', this.resizeChanged.bind(this))
-      .bind('mouseup.splitview', this.endResize.bind(this));
-  },
-  
-  resizeChanged: function(event) {
-    var newPosition = event.pageX + this.leftViewOffset;
-    newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
-    this.splitbar.css('left', newPosition);
-  },
-
-  endResize: function(event) {
-    var newPosition = event.pageX + this.leftViewOffset;
-    this.zombie.remove();
-    this.zombie = null;
-    this.overlay.remove();
-    this.overlay = null;
-    this.resplit(newPosition);
-
-    $(document)
-      .unbind('mousemove.splitview')
-      .unbind('mouseup.splitview');
-  },
-
-  resized: function(event) {
-    if (!this._resizingSubviews) {
-      this.resplit(Math.min(this.leftView.width(), this.element.width() - this._splitbarWidth));
-    }
-  },
-  
-  resplit: function(newPosition) {
-    newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
-    this.splitbar.css('left', newPosition);
-    this.leftView.css({
-      left: 0,
-      width: newPosition
-    });
-    this.rightView.css({
-      left: newPosition + this._splitbarWidth,
-      width: this.element.width() - newPosition - this._splitbarWidth
-    });
-    if (!$.browser.msie) {
-      this._resizingSubviews = true;
-                 $(window).trigger('resize');
-                 this._resizingSubviews = false;
-               }
-  },
-  
-  dispose: function() {
-    this.splitter.unbind('mousedown.splitview');
-    this._super();
-  }
-});
-
diff --git a/project/static/js/views/view.js b/project/static/js/views/view.js
deleted file mode 100644 (file)
index d0c6d17..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*globals Editor render_template*/
-var View = Editor.Object.extend({
-    _className: 'View',
-    element: null,
-    model: null,
-    template: null,
-    overlayClass: 'view-overlay',
-    overlay: null,
-  
-    init: function(element, model, template)
-    {
-        console.log("init for view");
-        this.element = $(element);
-        this.model = model;
-        this.template = template || this.template;
-    
-        if (this.template) this.render();
-    
-        this._resizeHandler = this.resized.bind(this);
-        $(window).bind('resize', this._resizeHandler);
-        $(this.element).bind('resize', this._resizeHandler);
-    },
-
-    render: function() {
-        console.log('rendering:', this._className);
-        this.element.html(render_template(this.template, this));
-    },
-  
-    frozen: function() {
-        return !!this.overlay;
-    },
-  
-    freeze: function(message) {
-        if (this.frozen()) {
-            this.unfreeze();
-        }
-        this.overlay = this.overlay || $('<div><div>' + message + '</div></div>');
-
-        this.overlay.addClass(this.overlayClass)
-        .css({
-            
-        }).attr('unselectable', 'on')
-
-        this.overlay.appendTo(this.element);
-
-        var ovc = this.overlay.children('div');        
-        var padV = (this.overlay.height() - ovc.outerHeight())/2;
-        var padH = (this.overlay.width() - ovc.outerWidth())/2;
-                   
-        this.overlay.children('div').css({
-            top: padV, left: padH
-        });    
-    },
-  
-    unfreeze: function() {
-        if (this.frozen()) {
-            this.overlay.remove();
-            this.overlay = null;
-        }
-    },
-
-    resized: function(event) {
-        if(this.overlay) {
-            var ovc = this.overlay.children('div');
-            var padV = (this.overlay.height() - ovc.outerHeight())/2;
-            var padH = (this.overlay.width() - ovc.outerWidth())/2;
-
-            this.overlay.children('div').css({
-                top: padV,
-                left: padH
-            });
-        }
-    },
-  
-    dispose: function() {
-        console.log('disposing:', this._className);
-        $(window).unbind('resize', this._resizeHandler);
-        $(this.element).unbind('resize', this._resizeHandler);
-        this.unfreeze();
-        this.element.html('');
-    }
-});
\ No newline at end of file
diff --git a/project/static/js/views/xml.js b/project/static/js/views/xml.js
deleted file mode 100644 (file)
index 5c18432..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*global View CodeMirror ToolbarView render_template panels */
-var XMLView = View.extend({
-    _className: 'XMLView',
-    element: null,
-    model: null,
-    template: 'xml-view-template',
-    editor: null,
-    buttonToolbar: null,
-  
-    init: function(element, model, parent, template) {
-        this._super(element, model, template);
-        this.parent = parent;
-        this.buttonToolbar = new ButtonToolbarView(
-            $('.xmlview-toolbar', this.element),
-            this.model.toolbarButtonsModel, parent);
-
-        this.hotkeys = [];
-        var self = this;
-
-        $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this));
-
-        // scroll to the given position (if availble)
-        this.scrollCallback = this.scrollOnRequest.bind(this);
-        $(document).bind('xml-scroll-request', this.scrollCallback);
-       
-        this.parent.freeze('Ładowanie edytora...');
-
-        setTimeout((function(){
-
-        this.editor = new CodeMirror($('.xmlview', this.element).get(0), {
-            parserfile: 'parsexml.js',
-            path: "/static/js/lib/codemirror/",
-            stylesheet: "/static/css/xmlcolors.css",
-            parserConfig: {
-                useHTMLKludges: false
-            },
-            textWrapping: true,
-            tabMode: 'spaces',
-            indentUnit: 0,
-            onChange: this.editorDataChanged.bind(this),
-            initCallback: this.editorDidLoad.bind(this)
-        });
-
-        }).bind(this), 0);
-    },
-  
-    resized: function(event) {
-        var height = this.element.height() - $('.xmlview-toolbar', this.element).outerHeight();
-        $('.xmlview', this.element).height(height);
-    },
-  
-    reload: function() {
-        this.model.load(true);
-    },
-  
-    editorDidLoad: function(editor) {
-        $(editor.frame).css({
-            width: '100%',
-            height: '100%'
-        });
-        this.model
-        .addObserver(this, 'data', this.modelDataChanged.bind(this))
-        .addObserver(this, 'state', this.modelStateChanged.bind(this))
-        .load();           
-      
-        this.editor.setCode(this.model.get('data'));
-        this.modelStateChanged('state', this.model.get('state'));
-        
-        editor.grabKeys(
-            this.hotkeyPressed.bind(this),
-            this.isHotkey.bind(this)
-        );
-
-        this.parent.unfreeze();
-    },
-  
-    editorDataChanged: function() {
-        this.model.set('data', this.editor.getCode());
-    },
-  
-    modelDataChanged: function(property, value) {
-        if (this.editor.getCode() != value) {
-            this.editor.setCode(value);
-        }
-    },
-  
-    modelStateChanged: function(property, value) {
-        if (value == 'synced' || value == 'dirty') {
-            this.unfreeze();
-        } else if (value == 'unsynced') {
-            this.freeze('Niezsynchronizowany...');
-        } else if (value == 'loading') {
-            this.freeze('Ładowanie danych...');
-        } else if (value == 'saving') {
-            this.freeze('Zapisywanie...');
-        } else if (value == 'error') {
-            this.freeze(this.model.get('error'));
-        }
-    },
-    
-    dispose: function() {
-        $(document).unbind('xml-scroll-request', this.scrollCallback);
-        
-        this.model.removeObserver(this);
-        $(this.editor.frame).remove();
-        this._super();
-    },    
-
-    getHotkey: function(event) {
-        var code = event.keyCode;
-        if(!((code >= 97 && code <= 122)
-           || (code >= 65 && code <= 90)) ) return null;
-
-        var ch = String.fromCharCode(code & 0xff).toLowerCase();
-        /* # console.log(ch.charCodeAt(0), '#', buttons); */
-
-        var buttons = $('.buttontoolbarview-button[hotkey='+ch+']', this.element);
-        var mod = 0;
-            
-        if(event.altKey) mod |= 0x01;
-        if(event.ctrlKey) mod |= 0x02;
-        if(event.shiftKey) mod |= 0x04;
-
-        if(buttons.length) {
-            var match = null;
-
-            buttons.each(function() {
-                if( parseInt($(this).attr('ui:hotkey_mod')) == mod ) {
-                    match = this;
-                    return;
-                }
-            })
-
-            return match;
-        }
-        else {
-            return null;
-        }
-    },
-
-    isHotkey: function() {
-        /* console.log(arguments); */
-        if(this.getHotkey.apply(this, arguments))
-            return true;
-        else
-            return false;
-    },
-
-    hotkeyPressed: function() {
-        var button = this.getHotkey.apply(this, arguments);
-        this.buttonToolbar.buttonPressed({
-            target: button
-        });
-    },
-
-    scrollOnRequest: function(event, data) 
-    {
-        try {
-            var line = this.editor.nthLine(data.line);
-            this.editor.selectLines(line, (data.column-1));
-        } catch(e) {
-            console.log('Exception in scrollOnRequest:', e);
-        }
-    }
-
-});
-
-function Hotkey(code) {
-    this.code = code;
-    this.has_alt = ((code & 0x01 << 8) !== 0);
-    this.has_ctrl = ((code & 0x01 << 9) !== 0);
-    this.has_shift = ((code & 0x01 << 10) !== 0);
-    this.character = String.fromCharCode(code & 0xff);
-}
-
-Hotkey.prototype.toString = function() {
-    var mods = [];
-    if(this.has_alt) mods.push('Alt');
-    if(this.has_ctrl) mods.push('Ctrl');
-    if(this.has_shift) mods.push('Shift');
-    mods.push('"'+this.character+'"');
-    return mods.join('+');
-};
-
-// Register view
-panels['xml'] = XMLView;
diff --git a/project/templates/503.html b/project/templates/503.html
deleted file mode 100644 (file)
index 1c2e4d2..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://platforma.wolnelektury.pl/">
-    <head>
-        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-        <title>Platforma Redakcyjna</title>
-
-    </head>
-    <body id="base">
-        <h2>Przepraszamy,</h2>
-        <p>Platfroma Redakcyjna jest tymczasowo niedostępna
-            z powodu prac administracyjnych.</p>
-
-        <p>Prosimy o wyrozumiałość i ponowne odwiedziny :)</p>
-    </body>
-</html>
diff --git a/project/templates/base.html b/project/templates/base.html
deleted file mode 100644 (file)
index 06fbc62..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://platforma.wolnelektury.pl/">
-    <head>
-        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-        <title>{% block title %}Platforma Redakcyjna{% block subtitle %}{% endblock subtitle %}{% endblock title%}</title>
-        <link rel="stylesheet" href="{{ STATIC_URL }}css/master.css" type="text/css" />
-        <script src="{{ STATIC_URL }}js/lib/jquery.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{ STATIC_URL }}js/lib/jquery.logging.js" type="text/javascript" charset="utf-8"></script>
-        {% block extrahead %}
-        {% endblock %}
-    </head>
-    <body id="{% block bodyid %}base{% endblock %}">
-       <div id="body-wrap">
-       <div id="header">
-               <span id="breadcrumbs">{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a>{% endblock breadcrumbs %}</span>
-               <span id="header-right-toolbar">
-                       {% block header-toolbar %}{% endblock %}
-                       <span id="login_info">{% include "registration/head_login.html" %}</span>
-               </span>
-        <div id="message-box">{% block message-box %} {% endblock %}</div>
-       </div>
-    <div id="content">{% block maincontent %} {% endblock %}</div>
-
-    {% block extrabody %}{% endblock %}
-       </div>
-    </body>
-</html>
diff --git a/project/templates/explorer/editor.html b/project/templates/explorer/editor.html
deleted file mode 100644 (file)
index 576a793..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-{% extends "base.html" %}
-
-{% block extrahead %}
-       <link rel="stylesheet" href="{{STATIC_URL}}css/jquery.modal.css" type="text/css" media="screen" charset="utf-8">
-       <link rel="stylesheet" href="{{STATIC_URL}}css/html.css" type="text/css" charset="utf-8">
-       <link rel="stylesheet" href="{{STATIC_URL}}css/toolbar.css" type="text/css" charset="utf-8">
-        <link rel="stylesheet" href="{{STATIC_URL}}css/autumn.css" type="text/css" media="screen" title="Autumn colors" charset="utf-8">
-       
-       <script type="text/javascript" charset="utf-8">
-            var documentInfo = {
-                docID: '{{ fileid }}',
-                userID: '{{ euser }}',
-                docURL: '{% url document_view fileid %}{% if euser %}?user={{ euser|urlencode }}{% endif %}',
-                toolbarURL: '{% url toolbar_buttons %}',
-                renderURL: '{% url api.views.render %}'
-            }          
-                
-       </script>
-       
-       {# Libraries #}
-        <script src="{{STATIC_URL}}js/lib/codemirror/codemirror.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/lib/jquery.modal.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{STATIC_URL}}js/lib/jquery.json.js" type="text/javascript" charset="utf-8"></script>
-        
-       {# Scriptlets #}
-       <script src="{{STATIC_URL}}js/button_scripts.js" type="text/javascript" charset="utf-8"></script>
-       
-       {# App and views #}
-       <script src="{{STATIC_URL}}js/app.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/messages.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/view.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/flash.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/editor.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/button_toolbar.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/split.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/xml.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/html.js" type="text/javascript" charset="utf-8"></script>
-        <script src="{{STATIC_URL}}js/views/gallery.js" type="text/javascript" charset="utf-8"></script>
-       <script src="{{STATIC_URL}}js/views/panel_container.js" type="text/javascript" charset="utf-8"></script>
-       
-       <script src="{{STATIC_URL}}js/models.js" type="text/javascript" charset="utf-8"></script>
-       
-       {# JavaScript templates #}
-       <script type="text/html" charset="utf-8" id="panel-container-view-template">
-               <div class="panel-main-toolbar">
-                <p><select>
-                        <option value="empty" selected="selected"> ---- </option>
-                       <% for (panel in panels) { %>
-                       <option value="<%= panel %>"><%= panel %></option>
-                       <% }; %>
-               </select> <button class="refresh">Odśwież panel</button></p>
-                </div>
-               <div class="content-view"></div>
-       </script>
-       
-       <script type="text/html" charset="utf-8" id="xml-view-template">
-               <div class="xmlview-toolbar"></div>
-               <div class="xmlview">
-                       
-               </div>
-       </script>
-       
-       <script type="text/html" charset="utf-8" id="html-view-template">
-                <div class="htmlview-toolbar">
-                    <a class="html-print-link" href="print" ui:baseref="{% url file_print fileid %}" target="_new">Wersja do druku</a>
-                </div>
-                
-               <div class="htmlview">
-               </div>
-       </script>       
-
-       <script type="text/html" charset="utf-8" id="flash-view-template">
-               <div class="flashview">
-               <% if (shownMessage) { %>
-                       <p style="margin: 0; padding: 0.2em 0.5em; line-height: 1.8em" class="<%= shownMessage.type %>"><%= shownMessage.text %></p>
-               <% } %>
-               </div>
-       </script>
-       
-    <script type="text/html" charset="utf-8" id="image-gallery-view-template">
-       <div class="image-gallery-view-template">
-
-        <div class="image-gallery-header">
-        <p>
-        <button type="button" class="image-gallery-prev-button">
-        <img alt="PPrevious" src="{{STATIC_URL}}/icons/go-previous.png" width="16" height="16" />
-        </button>
-
-        <input type="input" class="image-gallery-current-page"
-            size="5" value="<%= (currentPage + 1) %>" />
-
-
-        <button type="button" class="image-gallery-next-button">
-        <img alt="Next" src="{{STATIC_URL}}/icons/go-next.png" width="16" height="16" />
-        </button>
-
-        <button type="button" class="image-gallery-zoom-in">
-            <img alt="Zoom in" src="{{STATIC_URL}}/icons/zoom_in.png" width="16" height="16" />
-        </button>
-        <button type="button" class="image-gallery-zoom-out">
-            <img alt="Zoom out" src="{{STATIC_URL}}/icons/zoom_out.png" width="16" height="16" />
-        </button>
-        <button type="button" class="image-gallery-zoom-reset">
-            <img alt="Zoom reset" src="{{STATIC_URL}}/icons/zoom.png" width="16" height="16" />
-        </button>
-        </p>
-        </div>
-
-        <div class="image-gallery-page-list">
-            <% for(var i=0; i < model.data.length; i++) { %>
-                <div class="image-gallery-page-container"
-                    ui:model="<%= model.data[i] %>"
-                    ui:width="480"
-                    ui:height="752"
-                ></div>
-            <% }; %>
-       </div>
-        
-        </div>
-       </script>
-       
-       <script type="text/html" charset="utf-8" id="button-toolbar-view-template">
-               <div class="buttontoolbarview panel-toolbar">
-                       <div class="buttontoolbarview-tabs toolbar-tabs-container toolbar-buttons-container">
-                       <% for (var i=0; i < buttons.length; i++) { %>
-                               <button type="button" class="buttontoolbarview-tab" ui:groupindex="<%= i %>"><%= buttons[i].name %></button>
-                       <% }; %>
-                       </div>
-                       <div class="buttontoolbarview-groups ">
-                       <% for (var i=0; i < buttons.length; i++) { %>
-                               <div class="buttontoolbarview-group toolbar-buttons-container" ui:groupIndex="<%= i %>" style="display: none">
-                                       <% for (var j=0; j < buttons[i].buttons.length; j++) { %>
-                                               <% if (buttons[i].buttons[j].scriptlet_id) { %>
-                                               <button type="button" class="buttontoolbarview-button"
-                                                    title="<%= buttons[i].buttons[j].tooltip %>"
-                                                    hotkey="<%= buttons[i].buttons[j].key %>"
-                                                    ui:hotkey_mod="<%= buttons[i].buttons[j].key_mod %>"
-                                                    ui:groupindex="<%= i %>" ui:buttonindex="<%= j %>">
-                                                       <%= buttons[i].buttons[j].label %>
-                                               </button>
-                                               <% } %>
-                                       <% } %>
-                               </div>
-                       <% }; %>
-                       </div>
-               </div>
-       </script>
-{% endblock extrahead %}
-
-{% block breadcrumbs %}<a href="{% url file_list %}">Platforma</a> &gt; {{euser}} &gt; {{ fileid }}{% endblock breadcrumbs %}
-
-{% block header-toolbar %}
-    <a href="http://stigma.nowoczesnapolska.org.pl/platforma-hg/ksiazki/log/tip/{{ fileid }}.xml" target="_new" >Historia</a>
-       <button id="action-merge">Merge</button>
-        <button id="action-update">Update</button>
-        <button id="action-commit">Commit</button>
-        <button id="action-quick-save">Quick Save</button>
-{% endblock %}
-
-{% block maincontent %}    
-    
-
-    <div id="splitview">
-        <div id="left-panel-container" class='panel-container'></div>
-       <div id="right-panel-container" class='panel-container'></div>
-    </div>
-
-    <div id="commit-dialog" class="jqmWindow" style="display:none">
-        <form action="" method="POST">
-            <label for="message">Commit message:</label>
-            <textarea cols="60" rows="10" name="message" id="commit-dialog-message"></textarea>
-            <p id="commit-dialog-error-empty-message">Wiadomość nie może być pusta.</p>                
-            <fieldset id="commit-dialog-related-issues" ui:ajax-src="{{REDMINE_URL}}/publications/issues/{{fileid}}">
-                <legend>Related issues</legend>
-                <div class="loading-box" style="display: none;">
-                    <p>Loading related issues...</p>
-                </div>
-                <div class="container-box">No related issues.</div>
-            </fieldset>
-            <p>
-               <input type="button" value="Save" id="commit-dialog-save-button" />
-               <input type="reset" value="Cancel" id="commit-dialog-cancel-button" />
-            </p>
-        </form>
-    </div>
-    
-    <div id="split-dialog" class="jqmWindow" style="display:none">
-        <div class="container-box"> </div>
-        <div class="loading-box" style="display: none;">
-            <p>Loading dialog contents...</p>
-            <!-- <p><button type="button" class="dialog-close-button">Close</button></p> -->
-        </div>
-        <div class="fatal-error-box" style="display: none;">
-            <p>Server error, while loading dialog :(</p>
-            <p><button type="button" class="dialog-close-button">Close</button></p>
-        </div>
-    </div>
-{% endblock maincontent %}
-
-{% block extrabody %}
-       <div style="position: absolute; left: 35%; right: 35%; top: 0; height: 20px; z-index: 0" id="flashview"></div>
-{% endblock %}
\ No newline at end of file
diff --git a/project/templates/explorer/file_list.html b/project/templates/explorer/file_list.html
deleted file mode 100644 (file)
index f44a105..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-{% extends "base.html" %}
-
-{% block extrahead %}
-<link rel="stylesheet" href="{{ STATIC_URL }}css/filelist.css" type="text/css" />
-    <script src="{{STATIC_URL}}js/jquery.json.js" type="text/javascript" charset="utf-8"></script>
-    <script src="{{STATIC_URL}}js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
-<script type="text/javascript" charset="utf-8">
-$(function() {
-       function search(event) {
-        var expr = new RegExp($('#file-list-filter').val(), 'i');
-       
-        $('#file-list p').hide().filter(function(index) {
-            return expr.test($(this).attr('title'));
-        }).show();
-    
-        event.preventDefault();
-    }
-       
-    $('#file-list-find-button').click(search);
-       $('#file-list-filter').bind('keyup change DOMAttrModified', search);
-               
-    var defaultOptions = {
-        panels: [
-        {
-            name: 'htmleditor',
-            ratio: 0.5
-        },
-
-        {
-            name: 'gallery',
-            ratio: 0.5
-        }
-        ],
-        recentFiles: [],
-        lastUpdate: 0
-    };
-    
-       var options = null;
-    try {
-        var cookie = $.cookie('options');
-        options = $.secureEvalJSON(cookie);
-        if (!options) {
-            options = defaultOptions;
-        }
-    } catch (e) {    
-        options = defaultOptions;
-    };
-       
-       $.each(options.recentFiles, function(index) {
-               var fileId = options.recentFiles[index].fileId;
-               $('#recent-file-list ul').append('<li><a href="{% url editor_base %}'+fileId+'">' + fileId + '</a></li>');
-       });
-});
-</script>
-{% endblock extrahead %}
-
-{% block maincontent %}
-<div id="main-page-widgets">
-
-<div class="file-list-widget">
-    <form action="#" method="GET">
-    <p><input name="filter" id="file-list-filter" type="text" size="60" />
-        <input type="submit" value="Znajdź" id="file-list-find-button"/>
-        <input type="reset" value="Wyczyść" id="file-list-reset-button"/>
-    </p>
-    </form>
-    {% load explorer_tags %}
-    <ul class="file-tree-part file-tree-top">
-    {% for file in filetree %}    
-        {% tree_part file %}            
-    {% endfor %}
-    </ul>
-</div>
-
-<div id="recent-file-list">
-       <h2>Ostatnio oglądane pliki:</h2>
-       <ul>
-       </ul>
-</div>
-
-{% if perms.explorer.can_add_files %}
-<div class="upload-file-widget">
-<h2>Dodaj nowy utwór</h2>
-
-<form action="{% url file_upload %}" method="POST" enctype="multipart/form-data">
-    {{ bookform }}
-    <p><button type="submit">Dodaj książkę</button></p>
-</form>
-
-</div>
-{% endif %}
-
-</div>
-{% endblock maincontent %}
diff --git a/project/templates/explorer/file_tree_part.html b/project/templates/explorer/file_tree_part.html
deleted file mode 100644 (file)
index f65cf28..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-{% load explorer_tags %}
-
-<li class="file-tree-entry" title="{{document.name|bookname}}">
-    <a href="{% url editor_view document.name %}">{{ document.name|bookname }}</a>
-    {% if document.parts %}
-    <ul class="file-tree-part">
-        {% for part in document.parts %}
-        {% tree_part part %}
-        {% endfor %}
-    </ul>
-    {% endif %}
-</li>
-
diff --git a/project/templates/explorer/file_upload.html b/project/templates/explorer/file_upload.html
deleted file mode 100755 (executable)
index 1deedac..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "base.html" %}
-
-{% block maincontent %}
-<h2>Dodawanie nowego utworu:</h2>
-
-{% if errors %}
-<div class="errorlist">
-{% for error in errors %}
-<p>{{ error }}: {{ error.errors }}</p>
-{% endfor %}
-</div>
-{% endif %}
-<form action="{% url file_upload %}" method="POST" enctype="multipart/form-data">
-    {{ bookform.as_p }}
-    <p><button type="submit">Dodaj książkę</button></p>
-</form>
-{% endblock maincontent %}
diff --git a/project/templates/explorer/folder_images.html b/project/templates/explorer/folder_images.html
deleted file mode 100644 (file)
index 8f5f600..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-{% for image in images %}
-    <div class="image-box" src="{{ image }}" style="width: 480px; height: 640px;">{{ image }}</div>
-{% endfor %}
\ No newline at end of file
diff --git a/project/templates/html4print.html b/project/templates/html4print.html
deleted file mode 100644 (file)
index 1b4ef31..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-  <head>
-    <title>{{docid}}</title>
-    <link rel="stylesheet" href="{{STATIC_URL}}css/html_print.css" type="text/css" charset="utf-8">
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-  </head>
-  <body>
-      <div class="htmlview">
-        {{ output|safe }}
-      </div>
-  </body>
-</html>
diff --git a/project/templates/manager/pull_request.html b/project/templates/manager/pull_request.html
deleted file mode 100644 (file)
index 3b19c3b..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-{% extends 'base.html' %}
-
-{% block extrahead %}
-<link rel="stylesheet" href="{{ STATIC_URL }}css/managment.css" type="text/css" />
-<script type="text/javascript">
-
-    
-    $(function() {
-
-        function refreshRow(id) {
-            var row = $('#request-' +id);
-
-            $.ajax({
-                url: '{% url pullrequest_list %}/'+id,
-                dataType: 'json',
-                type: 'GET',
-                success: function(data) {
-                    row.removeClass('status-N');
-                    row.removeClass('status-R');
-                    row.removeClass('status-A');
-                    row.addClass('status-'+ data.status);
-
-                    $('.column-doc', row).html(data.document);
-                    $('.column-status', row).html(data.status);
-
-                    alert('Merge accepted.');
-                }
-            });            
-            
-        }
-        
-        $('.accept-button').click(function()
-        {
-            var id = parseInt($(this).attr('title'));
-            
-
-            $.ajax({
-                url: '{% url pullrequest_list %}/'+id,
-                data: {action: 'accept'},
-                dataType: 'json',
-                type: 'PUT',
-                success: function(data) {
-                    refreshRow(id);
-                }
-            });
-            
-        });
-        
-    });
-</script>
-{% endblock %}
-
-{% block maincontent %}
-<table class="request-report" cellspacing="0">
-    <tr>
-        <th>Utwór</th><th>Użytkownik</th><th>Komentarz</th><th>Stan</th>
-        <th>Zgłoszono</th><th>Akcje</th>
-    </tr>
-{% if objects %}
-    {% for pullreq in objects %}
-    <tr class="status-{{pullreq.status}}" id="request-{{pullreq.id}}">
-        <td class="column-doc">{{ pullreq.document }}</td>
-        <td class="column-user">{{ pullreq.comitter }}</td>
-        <td class="column-comment">{{ pullreq.comment }}</td>
-        <td class="column-status"> {{ pullreq.status }}</td>
-        <td class="column-data">{{ pullreq.timestamp }}</td>
-        <td>
-            <button type="button" class="accept-button" title="{{pullreq.id}}">Akceptuj</button>
-            <a href="{% url editor_view pullreq.document %}?user=$prq-{{pullreq.id}}">Zobacz</a>
-        </td>
-    </tr>
-    
-    {% endfor %}
-{% else %}
-    <tr><td colspan="6">Brak żądań</td></tr>
-{% endif %}
-</table>
-
-{% endblock %}
diff --git a/project/templates/registration/head_login.html b/project/templates/registration/head_login.html
deleted file mode 100644 (file)
index 5eb353d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-{% if user.is_authenticated %}
-<span class="user_name">{{ user.username }}</span> | 
-<a href='{% url logout %}?next={{request.get_full_path}}'>Wyloguj</a>
-{% else %}
-{% url login as login_url %}
-{% ifnotequal login_url request.path %}
-    <a href='{{ login_url }}?next={{request.get_full_path}}'>Logowanie</a>
-{% endifnotequal %}
-
-{% endif %}
diff --git a/project/templates/registration/login.html b/project/templates/registration/login.html
deleted file mode 100644 (file)
index 810bd4e..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "base.html" %}
-
-{% block subtitle %} - Logowanie {% endblock subtitle %}
-
-{% block maincontent %}
-
-<div class="isection">
-<form method="POST" action="{% url django.contrib.auth.views.login %}">
-{{ form.as_p }}
-<p><input type="submit" value="Login" /></p>
-<input type="hidden" name="next" value="{{ next }}" />
-</form>
-</div>
-
-{% endblock maincontent %}
diff --git a/project/templates/wysiwyg.html b/project/templates/wysiwyg.html
deleted file mode 100644 (file)
index a592bf1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{% extends 'base.html' %}
-
-{% block maincontent %}
-<h1>Wysiwyg editor</h1>
-<div>This part is not editable!</div>
-<div id="loremIpsum">
-<p>
-<p>Lorem <b>ipsum</b> dolor sit amet, consectetur adipiscing elit.
-    Suspendisse a urna eu enim rutrum elementum nec sed nibh. Quisque sed tortor
-    nulla, et euismod turpis. Morbi purus nulla, vulputate in vulputate id, gravida
-    id eros. In interdum est tempor est consequat imperdiet. Vivamus vitae ligula quam.
-    Proin nibh quam, tincidunt sit amet auctor eget, laoreet sit amet eros. Cras augue
-    lectus, euismod nec posuere ac, ultricies sed magna. Aliquam a lacinia sapien.
-    Cras imperdiet urna vel dui accumsan mollis. Suspendisse convallis tincidunt ornare.
-    Aenean convallis libero in lectus dictum vestibulum interdum ipsum suscipit.
-    Suspendisse sed justo sapien, eu egestas libero. Sed tincidunt sagittis sollicitudin.
-    Aliquam erat volutpat. Nullam egestas dolor id massa sagittis at sagittis ipsum
-    hendrerit.
-</p>
-
-<p>Phasellus sed purus non orci eleifend posuere ac eu elit. Etiam orci justo, porta vitae varius in, scelerisque sed metus. Phasellus faucibus lorem at metus scelerisque sit amet sollicitudin dolor dignissim. Aliquam eu justo in diam blandit posuere at a diam. Pellentesque tristique sem eu odio gravida eleifend. Phasellus cursus adipiscing metus, nec pharetra enim pharetra ac. Pellentesque faucibus volutpat lorem nec vulputate. Mauris in faucibus ipsum. Nulla ut urna nulla. Sed at tellus nec diam posuere porttitor. Duis faucibus, libero nec rhoncus facilisis, tortor ligula adipiscing massa, nec varius justo ante et magna. Donec orci mauris, ultrices nec blandit vel, lacinia in ante. Maecenas libero mi, pretium id ultricies eget, fringilla sit amet risus. Integer ut ante sem, et condimentum odio. Nam nec est erat. Etiam ut metus ligula. In vel condimentum orci.</p>
-
-<p>Suspendisse potenti. Proin in augue nibh. Curabitur in sollicitudin ipsum. In ut leo vel purus volutpat tempus. Proin ut neque at augue euismod ullamcorper nec ac dui. Vestibulum id quam nunc, eu porta augue. Pellentesque interdum neque eu nulla rhoncus vulputate. Sed viverra diam ac sem consectetur semper. Quisque consectetur fringilla quam, in feugiat nisl vulputate quis. Fusce vel ipsum lectus, eu interdum nunc. Donec luctus libero vitae mauris imperdiet at iaculis magna aliquet. Curabitur ullamcorper, diam nec pulvinar venenatis, nibh ante volutpat mauris, nec tristique lectus sem in urna. Aenean eu malesuada metus. Integer auctor nulla sit amet ligula sollicitudin ut accumsan velit ullamcorper. Donec nec auctor augue.</p>
-
-<p>Maecenas eget lacus vitae velit tincidunt bibendum quis et diam. In ullamcorper condimentum velit, et elementum felis vestibulum facilisis. Donec vitae cursus ipsum. Cras accumsan tincidunt aliquet. Nulla pellentesque mattis magna aliquet hendrerit. Pellentesque pellentesque odio enim. Duis viverra rhoncus tristique. In in risus ligula. Nullam dapibus lacinia facilisis. Ut eu neque neque, tristique laoreet nisl. Aliquam placerat dignissim leo, tristique tempor est vestibulum ut.</p>
-
-<p>Donec semper tempus ante, eget gravida erat varius et. Suspendisse aliquam rutrum nunc ac pulvinar. Aliquam erat volutpat. Nulla consectetur ultricies imperdiet. Nulla tincidunt est vitae augue porttitor a faucibus odio facilisis. In nec nisl odio. Aliquam et libero tortor, eu tincidunt mi. Vivamus suscipit erat sed mi hendrerit fringilla. Integer iaculis tempus nulla, at egestas velit faucibus ut. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum vel massa enim. Aliquam erat volutpat. In ligula tortor, fermentum eu suscipit at, posuere vel nunc. Nullam nibh magna, sollicitudin at semper et, mattis ut quam. Curabitur accumsan semper elit ac posuere. Sed sit amet lorem tortor, vel porttitor justo. Fusce odio metus, bibendum ut bibendum sit amet, luctus a ipsum. </p>
-</div>
-{% endblock %}
\ No newline at end of file
diff --git a/project/urls.py b/project/urls.py
deleted file mode 100644 (file)
index e107635..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from django.conf.urls.defaults import *
-from django.contrib import admin
-from django.conf import settings
-
-admin.autodiscover()
-PATH_SEC = r"(?P<path>[^/]+)"
-PATH_END = PATH_SEC + "/$"
-
-urlpatterns = patterns('',
-    # Explorer:
-    url(r'^$', 'explorer.views.file_list', name='file_list'),        
-    url(r'^file/upload', 'explorer.views.file_upload', name='file_upload'),
-
-
-    url(r'^managment/pull-requests$', 'explorer.views.pull_requests'),
-    
-#    url(r'^images/(?P<folder>[^/]+)/$', 'explorer.views.folder_images', name='folder_image'),
-#    url(r'^images/$', 'explorer.views.folder_images', {'folder': '.'}, name='folder_image_ajax'),
-    
-    # Editor panels
- #   url(r'^editor/'+PATH_SEC+'/panel/(?P<name>[a-z]+)/$', 'explorer.views.panel_view', name='panel_view'),
-    url(r'^editor/'+PATH_END, 'explorer.views.display_editor', name='editor_view'),
-    url(r'^editor/$', 'explorer.views.file_list', name='editor_base'),
-
- #   url(r'^editor/'+PATH_SEC+'/split$', 'explorer.views.split_text'),
- #   url(r'^editor/'+PATH_SEC+'/split-success',
- #       'explorer.views.split_success', name='split-success'),
-
- #   url(r'^editor/'+PATH_SEC+'/print/html$', 'explorer.views.print_html'),
- #   url(r'^editor/'+PATH_SEC+'/print/xml$', 'explorer.views.print_xml'),
-
-    url(r'^file/(?P<docid>[^/]+)/print$', 'explorer.views.print_html', name="file_print"),
-    # Task managment
-    # url(r'^manager/pull-requests$', 'explorer.views.pull_requests'),
-
-    # Admin panel
-    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-    url(r'^admin/(.*)', admin.site.root),
-
-    # Prototypes
-#    url(r'^wysiwyg-proto/', include('wysiwyg.urls')),
-
-    # Our über-restful api
-    url(r'^api/', include('api.urls') ),
-    
-)
-
-if 'cas_consumer' in settings.INSTALLED_APPS:
-    urlpatterns += patterns('',
-        # django-cas-consumer
-        url(r'^accounts/login/$', 'cas_consumer.views.login', name='login'),
-        url(r'^accounts/logout/$', 'cas_consumer.views.logout', name='logout'),
-    )
-else:
-    urlpatterns += patterns('',
-        # Django auth
-        url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'redirect_field_name': 'next'}, name='login'),
-        url(r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),    
-    )
-
-# Static files
-if settings.DEBUG and not hasattr(settings, 'DONT_SERVE_STATIC'):
-    urlpatterns += patterns('',
-        url(r'^%s(?P<path>.+)$' % settings.MEDIA_URL[1:], 'django.views.static.serve',
-            {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
-        url(r'^%s(?P<path>.+)$' % settings.STATIC_URL[1:], 'django.views.static.serve',
-            {'document_root': settings.STATIC_ROOT, 'show_indexes': True}),
-    )
-# 
\ No newline at end of file
index 972644b..227f79c 100644 (file)
@@ -1,6 +1,8 @@
+--find-links=http://stigma.nowoczesnapolska.org.pl/pypi/
+
 Django==1.1.1
--e hg+ssh://hg@bitbucket.org/jespern/django-piston/#egg=django-piston
-librarian==1.2.6
 lxml==2.2.2
 mercurial==1.3.1
--e git://github.com/zuber/django-cas-consumer.git#egg=django-cas-consumer
\ No newline at end of file
+librarian==1.2.6
+django-piston==0.2.3rc1
+django-cas-consumer==0.1dev