This commit is contained in:
Mark Hammond 2010-09-01 17:41:39 +10:00
Коммит ce578f69c5
62 изменённых файлов: 6696 добавлений и 0 удалений

10
.hgignore Normal file
Просмотреть файл

@ -0,0 +1,10 @@
syntax:regexp
\.egg
\.egg-info
\.pyc
\.pyo
\.swp
linkdrop.kpf
renv
\.DS_Store
development.db

3
MANIFEST.in Normal file
Просмотреть файл

@ -0,0 +1,3 @@
include linkdrop/config/deployment.ini_tmpl
recursive-include linkdrop/public *
recursive-include linkdrop/templates *

19
README.txt Normal file
Просмотреть файл

@ -0,0 +1,19 @@
This file is for you to describe the linkdrop application. Typically
you would include information such as the information below:
Installation and Setup
======================
Install ``linkdrop`` using easy_install::
easy_install linkdrop
Make a config file as follows::
paster make-config linkdrop config.ini
Tweak the config file as appropriate and then setup the application::
paster setup-app config.ini
Then you are ready to go.

104
development.ini Normal file
Просмотреть файл

@ -0,0 +1,104 @@
#
# linkdrop - Pylons development environment configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = true
# Uncomment and replace with the address which should receive any error reports
#email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
oauth.twitter.com.consumer_key = 2r1qbed58DAaNMe142msTg
oauth.twitter.com.consumer_secret = prh6A961516mJ3XEjd7eERsGxuVZqycrBB6lV7LQ
# This is a 'raindrop' app currently owned by markh. By necessity it is
# configured to use a redirect URL back to the default host and port specified
# below for this server.
oauth.facebook.com.app_id = 158102624846
oauth.facebook.com.app_secret = 4203f7f23803f405e06509ec4d4b9729
[composite:main]
use = egg:Paste#urlmap
/ = home
/api = api
[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 5000
[app:home]
use = egg:Paste#static
document_root = %(here)s/web
[app:api]
#use: config:api.ini
use = egg:linkdrop
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = linkdrop
beaker.session.secret = somesecret
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# SQLAlchemy database URL
sqlalchemy.url = sqlite:///%(here)s/development.db
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
#set debug = false
# Logging configuration
[loggers]
keys = root, routes, linkdrop, sqlalchemy
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_routes]
level = INFO
handlers =
qualname = routes.middleware
# "level = DEBUG" logs the route matched and routing variables.
[logger_linkdrop]
level = DEBUG
handlers =
qualname = linkdrop
[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] [%(threadName)s] %(message)s
datefmt = %H:%M:%S

19
docs/index.txt Normal file
Просмотреть файл

@ -0,0 +1,19 @@
linkdrop
++++++++
This is the main index page of your documentation. It should be written in
`reStructuredText format <http://docutils.sourceforge.net/rst.html>`_.
You can generate your documentation in HTML format by running this command::
setup.py pudge
For this to work you will need to download and install `buildutils`_,
`pudge`_, and `pygments`_. The ``pudge`` command is disabled by
default; to ativate it in your project, run::
setup.py addcommand -p buildutils.pudge_command
.. _buildutils: http://pypi.python.org/pypi/buildutils
.. _pudge: http://pudge.lesscode.org/
.. _pygments: http://pygments.org/

276
ez_setup.py Normal file
Просмотреть файл

@ -0,0 +1,276 @@
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c9"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
}
import sys, os
try: from hashlib import md5
except ImportError: from md5 import md5
def _validate_md5(egg_name, data):
if egg_name in md5_data:
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
def do_download():
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
try:
import pkg_resources
except ImportError:
return do_download()
try:
pkg_resources.require("setuptools>="+version); return
except pkg_resources.VersionConflict, e:
if was_imported:
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first, using 'easy_install -U setuptools'."
"\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return do_download()
except pkg_resources.DistributionNotFound:
return do_download()
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

0
linkdrop/__init__.py Normal file
Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,63 @@
#
# linkdrop - Pylons configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = true
email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 5000
[app:main]
use = egg:linkdrop
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = linkdrop
beaker.session.secret = ${app_instance_secret}
app_instance_uuid = ${app_instance_uuid}
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# SQLAlchemy database URL
sqlalchemy.url = sqlite:///production.db
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
set debug = false
# Logging configuration
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s] [%(threadName)s] %(message)s

Просмотреть файл

@ -0,0 +1,43 @@
"""Pylons environment configuration"""
import os
from pylons.configuration import PylonsConfig
from sqlalchemy import engine_from_config
import linkdrop.lib.app_globals as app_globals
import linkdrop.lib.helpers
from linkdrop.config.routing import make_map
from linkdrop.model import init_model
def load_environment(global_conf, app_conf):
"""Configure the Pylons environment via the ``pylons.config``
object
"""
config = PylonsConfig()
# Pylons paths
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
paths = dict(root=root,
controllers=os.path.join(root, 'controllers'),
static_files=os.path.join(root, 'public'),
templates=[os.path.join(root, 'templates')])
# Initialize config with the basic options
config.init_app(global_conf, app_conf, package='linkdrop', paths=paths)
config['routes.map'] = make_map(config)
config['pylons.app_globals'] = app_globals.Globals(config)
config['pylons.h'] = linkdrop.lib.helpers
# Setup cache object as early as possible
import pylons
pylons.cache._push_object(config['pylons.app_globals'].cache)
# Setup the SQLAlchemy database engine
engine = engine_from_config(config, 'sqlalchemy.')
init_model(engine)
# CONFIGURATION OPTIONS HERE (note: all config options will override
# any Pylons config options)
return config

Просмотреть файл

@ -0,0 +1,67 @@
"""Pylons middleware initialization"""
from beaker.middleware import SessionMiddleware
from paste.cascade import Cascade
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from pylons.middleware import ErrorHandler, StatusCodeRedirect
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware
from linkdrop.config.environment import load_environment
def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
"""Create a Pylons WSGI application and return it
``global_conf``
The inherited configuration for this application. Normally from
the [DEFAULT] section of the Paste ini file.
``full_stack``
Whether this application provides a full WSGI stack (by default,
meaning it handles its own exceptions and errors). Disable
full_stack when this application is "managed" by another WSGI
middleware.
``static_files``
Whether this application serves its own static files; disable
when another web server is responsible for serving them.
``app_conf``
The application's local configuration. Normally specified in
the [app:<name>] section of the Paste ini file (where <name>
defaults to main).
"""
# Configure the Pylons environment
config = load_environment(global_conf, app_conf)
# The Pylons WSGI app
app = PylonsApp(config=config)
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'], singleton=False)
app = SessionMiddleware(app, config)
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
if asbool(full_stack):
# Handle Python exceptions
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
if asbool(config['debug']):
app = StatusCodeRedirect(app)
else:
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
# Establish the Registry for this application
app = RegistryManager(app)
if asbool(static_files):
# Serve static files
static_app = StaticURLParser(config['pylons.paths']['static_files'])
app = Cascade([static_app, app])
app.config = config
return app

Просмотреть файл

@ -0,0 +1,28 @@
"""Routes configuration
The more specific and detailed routes should be defined first so they
may take precedent over the more generic routes. For more information
refer to the routes manual at http://routes.groovie.org/docs/
"""
from routes import Mapper
def make_map(config):
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])
map.minimization = False
map.explicit = False
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect('/error/{action}', controller='error')
map.connect('/error/{action}/{id}', controller='error')
# CUSTOM ROUTES HERE
map.connect('/account/oauth_facebook/{redirect_info}', controller='account', action="oauth_facebook")
map.connect('/{controller}/{action}')
map.connect('/{controller}/{action}/{id}')
return map

Просмотреть файл

Просмотреть файл

@ -0,0 +1,246 @@
import logging
import urllib, cgi, json
from pylons import config, request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from pylons.decorators import jsonify
from pylons.decorators.util import get_pylons
from linkdrop import simple_oauth
from linkdrop.lib.base import BaseController, render
from linkdrop.lib.helpers import json_exception_response, api_response, api_entry, api_arg
from linkdrop.model.meta import Session
from linkdrop.model.account import Account
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy import and_
log = logging.getLogger(__name__)
def get_oauth_config(provider):
key = 'oauth.'+provider+'.'
keylen = len(key)
d = {}
for k,v in config.items():
if k.startswith(key):
d[k[keylen:]] = v
return d
def get_oauth_consumer(oauth_config):
return simple_oauth.OAuthEntity(oauth_config['consumer_key'], oauth_config['consumer_secret'])
class AccountController(BaseController):
"""
Accounts
========
The 'account' namespace is used to access information regarding the current
user's account. This does not retrieve the users contact, for that see
the contacts API that uses @me/@self.
"""
__api_controller__ = True # for docs
# for testing...
@api_response
@json_exception_response
def get(self, id=None):
if id is None:
accts = Session.query(Account).all()
else:
accts = [Session.query(Account).get(id)]
return [a.to_dict() for a in accts]
@json_exception_response
def oauth_start(self, *args, **kw):
pylons = get_pylons(args)
try:
domain = request.params.get('domain')
return_path = request.params['return_to']
except KeyError, what:
raise ValueError("'%s' request param is not optional" % (what,))
scope = request.params.get('scope', domain)
oauth_config = get_oauth_config(domain)
url_gen = simple_oauth.getOAuthUrlGenerator(domain, '')
consumer = get_oauth_consumer(oauth_config)
callback_url = url(controller='account', action="oauth_done",
qualified=True, domain=domain,
return_to=return_path)
csrf_token = request.environ.get('CSRF_TOKEN')
if csrf_token:
callback_url += '&rd-token=%s' % (csrf_token)
# Note the xoauth module automatically generates nonces and timestamps to prevent replays.)
request_entity = simple_oauth.GenerateRequestToken(consumer, scope, None, None,
callback_url, url_gen)
# Save the request secret into the session.
session = request.environ['beaker.session']
session["oauth_request_key"] = request_entity.key
session["oauth_request_secret"] = request_entity.secret
session.save()
# And arrange for the client to redirect to the service to continue
# the process...
loc = '%s?oauth_token=%s' % (url_gen.GetAuthorizeTokenUrl(),
simple_oauth.UrlEscape(request_entity.key))
log.info("redirecting to %r and requesting to land back on %r",
loc, callback_url)
pylons.response.headers['Location'] = loc
pylons.response.status_int = 302
@json_exception_response
def oauth_done(self, *args, **kw):
pylons = get_pylons(args)
try:
domain = request.params['domain']
return_path = request.params['return_to']
except KeyError, what:
raise ValueError("'%s' request param is not optional" % (what,))
oauth_config = get_oauth_config(domain)
url_gen = simple_oauth.getOAuthUrlGenerator(domain, '')
oauth_token = request.params['oauth_token']
oauth_verifier = request.params['oauth_verifier']
# Get the request secret from the session.
# Save the request secret into the session.
session = request.environ['beaker.session']
request_key = session.pop("oauth_request_key")
request_secret = session.pop("oauth_request_secret")
session.save()
if request_secret and request_key == oauth_token:
request_token = simple_oauth.OAuthEntity(oauth_token, request_secret)
consumer = get_oauth_consumer(oauth_config)
# Make the oauth call to get the final verified token
verified_token = simple_oauth.GetAccessToken(consumer, request_token,
oauth_verifier, url_gen)
if domain == "twitter.com":
userid = verified_token.user_id
username = verified_token.screen_name
else:
raise ValueError(domain) # can't obtain user information for this provider...
# Find or create an account
try:
acct = Session.query(Account).filter(and_(Account.domain==domain, Account.userid==userid)).one()
except NoResultFound:
acct = Account()
acct.domain = domain
acct.userid = userid
acct.username = username
Session.add(acct)
# Update the account with the final tokens and delete the transient ones.
acct.oauth_token = verified_token.key
acct.oauth_token_secret = verified_token.secret
Session.commit()
fragment = "oauth_success_" + domain
else:
fragment = "oauth_failure_" + domain
# and finally redirect back to the signup page.
loc = request.host_url + return_path + "#" + fragment.replace(".", "_")
log.info("Final redirect back to %r", loc)
pylons.response.headers['Location'] = loc
pylons.response.status_int = 302
@json_exception_response
def oauth_facebook(self, redirect_info=None, *args, **kw):
pylons = get_pylons(args)
# NOTE: facebook insists the redirect URLS are identical for each
# leg of the auth (ie, no 'oauth_start/oauth_done') and that the
# redirect URL contains no request params (ie, no '?return_to=xxx' in
# the URL.) We worm around the second problem by encoding the params
# as base64 and appending it to the URL itself (in which case it comes
# to us via redirect_info)
if redirect_info is None:
# this is the initial request from linkdrop.
return_to = request.params.get('return_to', None)
else:
# this is a redirected leg.
return_to = redirect_info.decode("base64")
domain = "facebook.com"
# experimentation shows callback_url can not have request params!
callback_url = url(controller='account', action="oauth_facebook",
redirect_info=return_to.encode("base64")[:-1],
qualified=True)
csrf_token = request.environ.get('CSRF_TOKEN')
if csrf_token:
if '?' in callback_url:
callback_url += '&rd-token=%s' % (csrf_token)
else:
callback_url += '?rd-token=%s' % (csrf_token)
oauth_config = get_oauth_config(domain)
args = dict(client_id=oauth_config['app_id'], redirect_uri=callback_url)
verification_code = request.params.get("code")
if not verification_code:
# make the auth request to get the code.
args['scope'] = request.params.get('scope', '')
loc = "https://graph.facebook.com/oauth/authorize?" + urllib.urlencode(args)
log.info("facebook auth redirecting to %r and requesting to land back on %r",
loc, callback_url)
else:
args["client_secret"] = oauth_config['app_secret']
args["code"] = verification_code
# must we really use urlopen here, or can we do it via redirects?
resp = urllib.urlopen(
"https://graph.facebook.com/oauth/access_token?" +
urllib.urlencode(args))
redirect_query = ""
if resp.headers.get("content-type", "").startswith("text/javascript"):
# almost certainly an error response.
resp = json.load(resp)
log.error("facebook auth request failed with %r", resp)
fragment = "oauth_failure_" + domain
else:
response = cgi.parse_qs(resp.read())
access_token = response["access_token"][-1]
# Download the user profile and until we know what to do with
# it, just log it!
profile = json.load(urllib.urlopen(
"https://graph.facebook.com/me?" +
urllib.urlencode(dict(access_token=access_token))))
from pprint import pformat
log.info("facebook profile: %s", pformat(profile))
if 'error' in profile:
log.error("facebook profile request failed with %r", profile)
fragment = "oauth_failure_" + domain
else:
# Setup the linkdrop account.
facebookid = profile['id']
acct_proto = "facebook"
# Try and find an account to use or create a new one.
try:
acct = Session.query(Account).filter(and_(Account.domain=="facebook.com", Account.userid==facebookid)).one()
except NoResultFound:
acct = Account()
acct.domain = "facebook.com"
acct.userid = facebookid
acct.username = ""
Session.add(acct)
acct.oauth_token = access_token
Session.commit()
fragment = "oauth_success_" + domain
redirect_query = "?" + urllib.urlencode(dict(id=acct.id, name=profile['name']))
loc = request.host_url + return_to + redirect_query + "#" + fragment.replace(".", "_")
log.info("Final redirect back to %r", loc)
pylons.response.headers['Location'] = loc
pylons.response.status_int = 302

Просмотреть файл

@ -0,0 +1,44 @@
import cgi
from paste.urlparser import PkgResourcesParser
from pylons.middleware import error_document_template
from webhelpers.html.builder import literal
from linkdrop.lib.base import BaseController
class ErrorController(BaseController):
"""Generates error documents as and when they are required.
The ErrorDocuments middleware forwards to ErrorController when error
related status codes are returned from the application.
This behaviour can be altered by changing the parameters to the
ErrorDocuments middleware in your config/middleware.py file.
"""
def document(self):
"""Render the error document"""
request = self._py_object.request
resp = request.environ.get('pylons.original_response')
content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
page = error_document_template % \
dict(prefix=request.environ.get('SCRIPT_NAME', ''),
code=cgi.escape(request.GET.get('code', str(resp.status_int))),
message=content)
return page
def img(self, id):
"""Serve Pylons' stock images"""
return self._serve_file('/'.join(['media/img', id]))
def style(self, id):
"""Serve Pylons' stock stylesheets"""
return self._serve_file('/'.join(['media/style', id]))
def _serve_file(self, path):
"""Call Paste's FileApp (a WSGI application) to serve the file
at the specified path
"""
request = self._py_object.request
request.environ['PATH_INFO'] = '/%s' % path
return PkgResourcesParser('pylons', 'pylons')(request.environ, self.start_response)

0
linkdrop/lib/__init__.py Normal file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,18 @@
"""The application's Globals object"""
from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options
class Globals(object):
"""Globals acts as a container for objects available throughout the
life of the application
"""
def __init__(self, config):
"""One instance of Globals is created during application
initialization and is available during requests via the
'app_globals' variable
"""
self.cache = CacheManager(**parse_cache_config_options(config))

20
linkdrop/lib/base.py Normal file
Просмотреть файл

@ -0,0 +1,20 @@
"""The base Controller API
Provides the BaseController class for subclassing.
"""
from pylons.controllers import WSGIController
from pylons.templating import render_mako as render
from linkdrop.model.meta import Session
class BaseController(WSGIController):
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
try:
return WSGIController.__call__(self, environ, start_response)
finally:
Session.remove()

163
linkdrop/lib/helpers.py Normal file
Просмотреть файл

@ -0,0 +1,163 @@
"""Helper functions
Consists of functions to typically be used within templates, but also
available to Controllers. This module is available to templates as 'h'.
"""
from pylons.decorators.util import get_pylons
from pylons.controllers.core import HTTPException
from decorator import decorator
import pprint
from xml.sax.saxutils import escape
import json
import logging
from raindrop.model.meta import Session
logger=logging.getLogger(__name__)
@decorator
def exception_rollback(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except Exception, e:
Session.rollback()
raise
@decorator
def json_exception_response(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except HTTPException:
raise
except Exception, e:
logger.exception("%s(%s, %s) failed", func, args, kwargs)
#pylons = get_pylons(args)
#pylons.response.status_int = 500
return {
'result': None,
'error': {
'name': e.__class__.__name__,
'message': str(e)
}
}
@decorator
def api_response(func, *args, **kwargs):
pylons = get_pylons(args)
data = func(*args, **kwargs)
format = pylons.request.params.get('format', 'json')
if format == 'test':
pylons.response.headers['Content-Type'] = 'text/plain'
return pprint.pformat(data)
elif format == 'xml':
# a quick-dirty dict serializer
def ser(d):
r = ""
for k,v in d.items():
if isinstance(v, dict):
r += "<%s>%s</%s>" % (k, ser(v), k)
elif isinstance(v, list):
for i in v:
#print k,i
r += ser({k:i})
else:
r += "<%s>%s</%s>" % (k, escape("%s"%v), k)
return r
pylons.response.headers['Content-Type'] = 'text/xml'
return '<?xml version="1.0" encoding="UTF-8"?>' + ser({'response': data}).encode('utf-8')
pylons.response.headers['Content-Type'] = 'application/json'
return json.dumps(data)
def api_entry(**kw):
"""Decorator to add tags to functions.
"""
def decorate(f):
if not hasattr(f, "__api"):
f.__api = kw
if not getattr(f, "__doc__") and 'doc' in kw:
doc = kw['doc']
if 'name' in kw:
doc = kw['name'] + "\n" + "="*len(kw['name']) +"\n\n" + doc
args = []
for m in kw.get('queryargs', []):
line = " %(name)-20s %(type)-10s %(doc)s" % m
opts = []
if m['required']: opts.append("required")
if m['default']: opts.append("default=%s" % m['default'])
if m['allowed']: opts.append("options=%r" % m['allowed'])
if opts:
line = "%s (%s)" % (line, ','.join(opts),)
args.append(line)
d = "Request Arguments\n-----------------\n\n%s\n\n" % '\n'.join(args)
if 'bodyargs' in kw:
assert 'body' not in kw, "can't specify body and bodyargs"
for m in kw['bodyargs']:
line = " %(name)-20s %(type)-10s %(doc)s" % m
opts = []
if m['required']: opts.append("required")
if m['default']: opts.append("default=%s" % m['default'])
if m['allowed']: opts.append("options=%r" % m['allowed'])
if opts:
line = "%s (%s)" % (line, ','.join(opts),)
args.append(line)
d = d+ "**Request Body**: A JSON object with the following fields:"
d = d+ "\n".join(args)
elif 'body' in kw:
d = d+ "**Request Body**: %(type)-10s %(doc)s\n\n" % kw['body']
if 'response' in kw:
d = d+ "**Response Body**: %(type)-10s %(doc)s\n\n" % kw['response']
f.__doc__ = doc + d
return f
return decorate
def api_arg(name, type=None, required=False, default=None, allowed=None, doc=None):
return {
'name': name,
'type': type,
'required': required,
'default': default,
'allowed': allowed,
'doc': doc or ''
}
if __name__ == '__main__':
@api_entry(
name="contacts",
body=("json", "A json object"),
doc="""
See Portable Contacts for api for detailed documentation.
http://portablecontacts.net/draft-spec.html
**Examples**::
/contacts returns all contacts
/contacts/@{user}/@{group} returns all contacts (user=me, group=all)
/contacts/@{user}/@{group}/{id} returns a specific contact
""",
urlargs=[
api_arg('user', 'string', True, None, ['me'], 'User to query'),
api_arg('group', 'string', True, None, ['all', 'self'], 'Group to query'),
api_arg('id', 'integer', False, None, None, 'Contact ID to return'),
],
queryargs=[
# name, type, required, default, allowed, doc
api_arg('filterBy', 'string', False, None, None, 'Field name to query'),
api_arg('filterOp', 'string', False, None, ['equals', 'contains', 'startswith', 'present'], 'Filter operation'),
api_arg('filterValue', 'string', False, None, None, 'A value to compare using filterOp (not used with present)'),
api_arg('startIndex', 'int', False, 0, None, 'The start index of the query, used for paging'),
api_arg('count', 'int', False, 20, None, 'The number of results to return, used with paging'),
api_arg('sortBy', 'string', False, 'ascending', ['ascending','descending'], 'A list of conversation ids'),
api_arg('sortOrder', 'string', False, 'ascending', ['ascending','descending'], 'A list of conversation ids'),
api_arg('fields', 'list', False, None, None, 'A list of fields to return'),
],
response=('object', 'A POCO result object')
)
def foo():
pass
print foo.__doc__

Просмотреть файл

@ -0,0 +1,9 @@
"""The application's model objects"""
from linkdrop.model.meta import Session, Base
from linkdrop.model.account import Account
def init_model(engine):
"""Call me before using any of the tables or classes in the model"""
Session.configure(bind=engine)
Base.metadata.create_all(bind=Session.bind)

17
linkdrop/model/account.py Normal file
Просмотреть файл

@ -0,0 +1,17 @@
# Account definitions
from sqlalchemy import Column, Integer, String, Boolean, UniqueConstraint
from linkdrop.model.meta import Base, Session, make_table_args
from linkdrop.model.types import RDUnicode
from linkdrop.model.expando_mixin import JsonExpandoMixin
from linkdrop.model.serializer_mixin import SerializerMixin
class Account(JsonExpandoMixin, SerializerMixin, Base):
__tablename__ = 'accounts'
__table_args__ = make_table_args(UniqueConstraint('domain', 'username', 'userid'))
id = Column(Integer, primary_key=True)
# The external account identity information, modelled from poco
domain = Column(RDUnicode(128), nullable=False)
username = Column(RDUnicode(128), nullable=False)
userid = Column(RDUnicode(128), nullable=False)

Просмотреть файл

@ -0,0 +1,81 @@
# A mixin for all objects which want 'expando' functionality;
# ie, the ability to have arbitrary content stored in a json column, but
# have the object seamlessly provide access to the items in the json as though
# they were real properties.
import json
from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE
from sqlalchemy import Column, Text
# A mapper extension to help us with 'expandos' magic - ensures that expando
# attributes set via normal 'object.expando=value' syntax is reflected
# back into the json_attributes column.
class _ExpandoFlushingExtension(MapperExtension):
def before_insert(self, mapper, connection, instance):
instance._flush_expandos()
return EXT_CONTINUE
before_update = before_insert
# The actual mixin class
class JsonExpandoMixin(object):
__mapper_args__ = {'extension': _ExpandoFlushingExtension()}
json_attributes = Column(Text)
# Methods for providing 'expandos' via the json_attributes field.
def _get_expando_namespace(self):
if '_expando_namespace' not in self.__dict__:
assert '_orig_json' not in self.__dict__
attrs = self.json_attributes
self.__dict__['_orig_json'] = attrs
if not attrs:
_expando_namespace = {}
else:
_expando_namespace = json.loads(attrs)
self.__dict__['_expando_namespace'] = _expando_namespace
return self.__dict__['_expando_namespace']
def __getattr__(self, name):
if name.startswith('_'):
raise AttributeError(name)
# is it in the namespace?
try:
return self._get_expando_namespace()[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
if name.startswith("_") or name in self.__dict__ or hasattr(self.__class__, name):
object.__setattr__(self, name, value)
return
# assume it is an 'expando' object
# Set json attributes to itself simply so the object is marked as
# 'dirty' for subsequent updates.
self.json_attributes = self.json_attributes
self._get_expando_namespace()[name] = value
def __delattr__(self, name):
try:
del self._get_expando_namespace()[name]
self.json_attributes = self.json_attributes # to mark as dirty
except KeyError:
raise AttributeError("'%s' is not an 'expando' property" % (name,))
# Note that you should never need to call this function manually - a
# mapper extension is defined above which calls this function before
# the object is saved.
def _flush_expandos(self):
try:
en = self.__dict__['_expando_namespace']
except KeyError:
# no property accesses at all
return
if self._orig_json != self.json_attributes:
# This means someone used 'expandos' *and* explicitly set
# json_attributes on the same object.
raise ValueError("object's json_attributes have changed externally")
self.json_attributes = None if not en else json.dumps(en)
# and reset the world back to as if expandos have never been set.
del self.__dict__['_orig_json']
del self.__dict__['_expando_namespace']

22
linkdrop/model/meta.py Normal file
Просмотреть файл

@ -0,0 +1,22 @@
"""SQLAlchemy Metadata and Session object"""
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
__all__ = ['Base', 'Session']
# SQLAlchemy session manager. Updated by model.init_model()
Session = scoped_session(sessionmaker())
# The declarative Base
Base = declarative_base()
# Return a value suitable for __table_args__ which includes common table
# arguments which should be used by all tables.
def make_table_args(*args, **kw):
kwuse = kw.copy()
if 'mysql_charset' not in kwuse:
kwuse['mysql_charset'] = 'utf8'
if not args:
return kwuse
return args + (kwuse,)

Просмотреть файл

@ -0,0 +1,40 @@
# A serializer for items.
from datetime import datetime
import json
from sqlalchemy.orm.util import object_mapper
from sqlalchemy.orm.properties import ColumnProperty, RelationProperty
class SerializerMixin(object):
# deal with the special plural fields
def _rd_collection_to_dict(self, name, fields):
if fields and name not in fields:
return
if hasattr(self, name):
for entry in getattr(self, name, []):
val = entry.to_dict()
if val:
#import sys;print >> sys.stderr, val
yield val
#propdict.setdefault(name, []).append(val)
def to_dict(self, fields=None):
propdict = {}
for prop in object_mapper(self).iterate_properties:
if isinstance(prop, (ColumnProperty)):# or isinstance(prop, RelationProperty) and prop.secondary:
if fields and prop.key not in fields: continue
val = getattr(self, prop.key)
if val:
if isinstance(val, datetime):
val = val.isoformat().split('.')[0].replace('+00:00','Z')
if prop.key == 'json_attributes':
propdict.update(json.loads(val))
else:
propdict[prop.key] = val
elif prop.key != 'json_attributes':
propdict[prop.key] = val
for val in self._rd_collection_to_dict('tags', fields):
propdict.setdefault('tags', []).append(val)
return propdict

94
linkdrop/model/types.py Normal file
Просмотреть файл

@ -0,0 +1,94 @@
import time
import datetime
import dateutil.parser
from dateutil.tz import tzutc, tzlocal
from email.utils import formatdate as email_format_date
from email.utils import mktime_tz, parsedate_tz
from sqlalchemy.types import TypeDecorator, DateTime, Unicode
import codecs
# SqlAlchemy takes a very anal approach to Unicode - if a column is unicode,
# then the Python object must also be unicode and not a string. This is very
# painful in py2k, so we loosen this a little - string objects are fine so long
# as they don't include extended chars.
class RDUnicode(Unicode):
def __init__(self, length=None, **kwargs):
kwargs.setdefault('_warn_on_bytestring', False)
super(RDUnicode, self).__init__(length=length, **kwargs)
def bind_processor(self, dialect):
encoder = codecs.getencoder(dialect.encoding)
def process(value):
if isinstance(value, unicode):
return encoder.encode(value)
elif isinstance(value, str):
# Force an error should someone pass a non-ascii string
assert value.decode('ascii')==value
return value
elif value is None:
return None
raise ValueError("invalid value for unicode column: %r" % (value,))
# from http://stackoverflow.com/questions/2528189/can-sqlalchemy-datetime-objects-only-be-naive
# to force all dates going to and coming back from the DB to be in UTC.
# All raindrop DateTime fields should be declared using this type.
class UTCDateTime(TypeDecorator):
impl = DateTime
def process_bind_param(self, value, engine):
if value is not None:
try:
return value.astimezone(tzutc())
except ValueError:
print "FAILED", value
def process_result_value(self, value, engine):
if value is not None:
return datetime.datetime(value.year, value.month, value.day,
value.hour, value.minute, value.second,
value.microsecond, tzinfo=tzutc())
# Helpers for creating, formatting and parsing datetime values.
@classmethod
def from_string(cls, strval, deftz=None):
try:
ret = dateutil.parser.parse(strval)
except ValueError:
# Sadly, some (but not many) dates which appear in emails can't be
# parsed by dateutil, but can by the email package. I've no idea
# if such dates are rfc compliant, but they do exist in the wild -
# eg:
# "Sat, 11 Oct 2008 13:29:43 -0400 (Eastern Daylight Time)"
try:
utctimestamp = mktime_tz(parsedate_tz(strval))
ret = datetime.datetime.fromtimestamp(utctimestamp, tzutc())
except TypeError, exc:
raise ValueError(exc.args[0])
else:
# dateutil parsed it - now turn it into a UTC value.
# If there is no tzinfo in the string we assume utc.
if ret.tzinfo is None:
if deftz is None: deftz = tzutc()
ret = ret.replace(tzinfo=deftz)
return ret
@classmethod
def as_string(cls, datetimeval):
return datetimeval.isoformat().split('.')[0].replace('+00:00','Z')
@classmethod
def as_rfc2822_string(cls, datetimeval):
# Need to pass localtime as that is what the email package expects.
lt = datetimeval.astimezone(tzlocal())
timestamp = time.mktime(lt.timetuple())
return email_format_date(timestamp)
@classmethod
def from_timestamp(cls, tsval, tz=None):
if tz is None: tz = tzutc()
return datetime.datetime.fromtimestamp(tsval, tz)
@classmethod
def now(cls):
return datetime.datetime.now(tzutc())

Двоичные данные
linkdrop/public/bg.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 339 B

Двоичные данные
linkdrop/public/favicon.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

137
linkdrop/public/index.html Normal file
Просмотреть файл

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<!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" xml:lang="en" lang="en">
<head>
<title>Welcome to Pylons!</title>
<style type="text/css">
body {
font-family: arial, helvetica, sans-serif;
background-color: #ffc900;
background-image: url(bg.png);
background-repeat: repeat-x;
width:100%;
height:100%;
margin:0;
max-height: 100%;
padding:0;
border:none;
line-height:1.4;
}
#container {
color:white;
background-color:#111;
position: absolute;
left: 50%;
width: 500px;
margin-left: -300px;
padding:50px;
height:100%;
}
#footer {
margin: 120px 0 0 0;
padding: 3px;
text-align:center;
font-size:small;
background-color:#222;
letter-spacing: 1px;
}
h1 {
text-align:center;
font-size:xx-large;
font-weight:normal;
margin: 0 0 20px 0;
border:none;
padding:0;
letter-spacing: 5px;
}
h2 {
font-size:xx-large;
font-weight:normal;
margin: 0 0 20px 0;
border:none;
padding:0;
}
hr {
margin-bottom:30px;
border: 1px solid #222;
background-color: #222;
padding: 2px;
}
#logo {
background-image: url(signum8b_spk.png);
background-repeat: no-repeat;
height: 0;
overflow: hidden;
padding-top: 99px;
width: 239px;
}
#left {
float:left;
width:250px;
margin:0 50px 0 0;
border:none;
padding:0 0 0 10px;
}
#right {
margin:0 0 0 330px;
border:none;
padding:0;
}
ul {
list-style:none;
margin:0;
border:none;
padding:0;
}
a:visited {
color:white;
text-decoration:none;
}
a:link {
color:white;
text-decoration:none;
}</style>
</head>
<body>
<div id="container">
<h1>Welcome to <img src="pylons-logo.gif" alt="Logo displaying the word Pylons"
style="vertical-align:-15px; width: 250px;"/>
</h1>
<hr/>
<div id="left">
<h2>Let's begin!</h2>
<p>If you haven't used Pylons before, start with the <a href="http://pylonshq.com/docs/en/0.9.7/gettingstarted/"
style="text-decoration:underline;">beginners' tutorial</a>.</p>
</div>
<div id="right">
<h2>Help</h2>
<ul>
<li>
<a href="http://pylonshq.com/docs/en/0.9.7/">Official documentation</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/display/pylonsfaq/Home">FAQ</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/dashboard.action">Wiki</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/display/pylonscommunity/Home#Home-JointheMailingLists">Mailing list</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/display/pylonscommunity/Home#Home-IRC">IRC</a>
</li>
<li>
<a href="http://pylonshq.com/project/pylonshq/roadmap">Bug tracker</a>
</li>
</ul>
</div>
<div id="footer">
<a href="http://www.pylonshq.com" style="color: #ccc; text-decoration:none;"
>www.pylonshq.com</a>
</div>
</div>
</body>
</html>

Двоичные данные
linkdrop/public/pylons-logo.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.3 KiB

401
linkdrop/simple_oauth.py Normal file
Просмотреть файл

@ -0,0 +1,401 @@
# Copyright 2010 Google Inc.
# Portions copyright Mozilla Messaging 2010.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Code lifted from google's XOAUTH sample; almost identical to that sample
# but with print statements removed or replaced with logging.
# Also added some raindrop-specific helper functions...
"""Utilities for XOAUTH authentication.
This script has the following modes of operation:
--generate_oauth_token
--generate_xoauth_string
--test_imap_authentication
--test_smtp_authentication
The --generate_oauth_token mode will generate and authorize an OAuth token for
testing.
xoauth --generate_oauth_token --user=xxx@googlemail.com
The script will converse with Google Accounts and generate an oauth request
token, then present you with a URL you should visit in your browser to authorize
the token. Once you get the verification code from the website, enter it into
the script to get your OAuth access token. The output from this command will be
two values: an OAuth token and an OAuth token secret. These values are reusable,
so if you save them somewhere you won't have to keep repeating this first step.
The --generate_xoauth_string option generates an XOauth auth string that can
be fed directly to IMAP or SMTP.
(3-legged OAuth)
xoauth --generate_xoauth_string --user=xxx@googlemail.com
--oauth_token=k99hfs9dh --oauth_token_secret=sd9fhidfskfj
(2-legged OAuth)
xoauth --generate_xoauth_string --user=xxx@googlemail.com
--consumer_key=foo.com --consumer_secret=sd9fhidfskfj
--xoauth_requestor_id=xxx@googlemail.com
The output of this mode will be a base64-encoded string. To use it, connect to
imap.googlemail.com:993 and pass it as the second argument to the AUTHENTICATE
command.
a AUTHENTICATE XOAUTH a9sha9sfs[...]9dfja929dk==
The --test_imap_authentication and --test_smtp_authentication comands generate
an XOAUTH string and use them to authenticate to a live IMAP or SMTP server.
You can use the --imap_hostname and --smtp_hostname options to specify the
server to connect to.
xoauth --test_imap_authentication --user=xxx@googlemail.com
--oauth_token=k99hfs9dh --oauth_token_secret=sd9fhidfskfj
xoauth --test_smtp_authentication --user=xxx@googlemail.com
--oauth_token=k99hfs9dh --oauth_token_secret=sd9fhidfskfj
"""
import base64
import hmac
import imaplib
import random
import hashlib
import smtplib
import sys
import time
import urllib
import logging
logger = logging.getLogger(__name__)
def UrlEscape(text):
# See OAUTH 5.1 for a definition of which characters need to be escaped.
return urllib.quote(text, safe='~-._')
def UrlUnescape(text):
# See OAUTH 5.1 for a definition of which characters need to be escaped.
return urllib.unquote(text)
def FormatUrlParams(params):
"""Formats parameters into a URL query string.
Args:
params: A key-value map.
Returns:
A URL query string version of the given parameters.
"""
param_fragments = []
for param in sorted(params.iteritems(), key=lambda x: x[0]):
param_fragments.append('%s=%s' % (param[0], UrlEscape(param[1])))
return '&'.join(param_fragments)
def EscapeAndJoin(elems):
return '&'.join([UrlEscape(x) for x in elems])
def GenerateSignatureBaseString(method, request_url_base, params):
"""Generates an OAuth signature base string.
Args:
method: The HTTP request method, e.g. "GET".
request_url_base: The base of the requested URL. For example, if the
requested URL is
"https://mail.google.com/mail/b/xxx@googlemail.com/imap/?" +
"xoauth_requestor_id=xxx@googlemail.com", the request_url_base would be
"https://mail.google.com/mail/b/xxx@googlemail.com/imap/".
params: Key-value map of OAuth parameters, plus any parameters from the
request URL.
Returns:
A signature base string prepared according to the OAuth Spec.
"""
return EscapeAndJoin([method, request_url_base, FormatUrlParams(params)])
def GenerateHmacSha1Signature(text, key):
digest = hmac.new(key, text, hashlib.sha1)
return base64.b64encode(digest.digest())
def GenerateOauthSignature(base_string, consumer_secret, token_secret):
key = EscapeAndJoin([consumer_secret, token_secret])
return GenerateHmacSha1Signature(base_string, key)
def ParseUrlParamString(param_string):
"""Parses a URL parameter string into a key-value map.
Args:
param_string: A URL parameter string, e.g. "foo=bar&oof=baz".
Returns:
A key-value dict.
"""
kv_pairs = param_string.split('&')
params = {}
for kv in kv_pairs:
k, v = kv.split('=')
params[k] = UrlUnescape(v)
return params
class OAuthEntity(object):
"""Represents consumers and tokens in OAuth."""
def __init__(self, key, secret, **rest):
self.key = key
self.secret = secret
for n, v in rest.iteritems():
setattr(self, n, v)
def __repr__(self):
return "<OAuthEntity(%r)>" % self.__dict__
def FillInCommonOauthParams(params, consumer, nonce=None, timestamp=None):
"""Fills in parameters that are common to all oauth requests.
Args:
params: Parameter map, which will be added to.
consumer: An OAuthEntity representing the OAuth consumer.
nonce: optional supplied nonce
timestamp: optional supplied timestamp
"""
params['oauth_consumer_key'] = consumer.key
if nonce:
params['oauth_nonce'] = nonce
else:
params['oauth_nonce'] = str(random.randrange(2**64 - 1))
params['oauth_signature_method'] = 'HMAC-SHA1'
params['oauth_version'] = '1.0'
if timestamp:
params['oauth_timestamp'] = timestamp
else:
params['oauth_timestamp'] = str(int(time.time()))
def GenerateRequestToken(consumer, scope, nonce, timestamp, callback_url,
google_accounts_url_generator):
"""Generates an OAuth request token by talking to Google Accounts.
Args:
consumer: An OAuthEntity representing the OAuth consumer.
scope: Scope for the OAuth access token.
nonce: The nonce to use in the signature. If None is passed, a random nonce
will be generated.
timestamp: Timestamp to use in the signature. If None is passed, the current
time will be used.
google_accounts_url_generator: function that creates a Google Accounts URL
for the given URL fragment.
Returns:
An OAuthEntity representing the request token.
"""
params = {}
FillInCommonOauthParams(params, consumer, nonce, timestamp)
#params['oauth_callback'] = 'oob'
params['oauth_callback'] = callback_url
params['scope'] = scope
request_url = google_accounts_url_generator.GetRequestTokenUrl()
token = OAuthEntity(None, '')
base_string = GenerateSignatureBaseString('GET', request_url, params)
signature = GenerateOauthSignature(base_string, consumer.secret,
token.secret)
params['oauth_signature'] = signature
url = '%s?%s' % (request_url, FormatUrlParams(params))
response = urllib.urlopen(url).read()
response_params = ParseUrlParamString(response)
# for param in response_params.items():
# print '%s: %s' % param
key = response_params.pop('oauth_token')
secret = response_params.pop('oauth_token_secret')
token = OAuthEntity(key, secret, **response_params)
#print ('To authorize token, visit this url and follow the directions '
# 'to generate a verification code:')
#print ' %s?oauth_token=%s' % (
# google_accounts_url_generator.GetAuthorizeTokenUrl(),
# UrlEscape(response_params['oauth_token']))
return token
def GetAccessToken(consumer, request_token, oauth_verifier,
google_accounts_url_generator):
"""Obtains an OAuth access token from Google Accounts.
Args:
consumer: An OAuth entity representing the OAuth consumer.
request_token: An OAuthEntity representing the request token (e.g. as
returned by GenerateRequestToken.
oauth_verifier: The verification string displayed to the user after
completing Google Accounts authorization.
google_accounts_url_generator: function that creates a Google Accounts URL
for the given URL fragment.
Returns:
An OAuthEntity representing the OAuth access token.
"""
params = {}
FillInCommonOauthParams(params, consumer)
params['oauth_token'] = request_token.key
params['oauth_verifier'] = oauth_verifier
request_url = google_accounts_url_generator.GetAccessTokenUrl()
base_string = GenerateSignatureBaseString('GET', request_url, params)
signature = GenerateOauthSignature(base_string, consumer.secret,
request_token.secret)
params['oauth_signature'] = signature
url = '%s?%s' % (request_url, FormatUrlParams(params))
response = urllib.urlopen(url).read()
response_params = ParseUrlParamString(response)
#for param in ('oauth_token', 'oauth_token_secret'):
# print '%s: %s' % (param, response_params[param])
key = response_params.pop('oauth_token')
secret = response_params.pop('oauth_token_secret')
return OAuthEntity(key, secret, **response_params)
def GenerateXOauthString(consumer, access_token, user, proto,
xoauth_requestor_id, nonce, timestamp):
"""Generates an IMAP XOAUTH authentication string.
Args:
consumer: An OAuthEntity representing the consumer.
access_token: An OAuthEntity representing the access token.
user: The Google Mail username (full email address)
proto: "imap" or "smtp", for example.
xoauth_requestor_id: xoauth_requestor_id URL parameter for 2-legged OAuth
nonce: optional supplied nonce
timestamp: optional supplied timestamp
Returns:
A string that can be passed as the argument to an IMAP
"AUTHENTICATE XOAUTH" command after being base64-encoded.
"""
method = 'GET'
url_params = {}
if xoauth_requestor_id:
url_params['xoauth_requestor_id'] = xoauth_requestor_id
oauth_params = {}
FillInCommonOauthParams(oauth_params, consumer, nonce, timestamp)
if access_token.key:
oauth_params['oauth_token'] = access_token.key
signed_params = oauth_params.copy()
signed_params.update(url_params)
request_url_base = (
'https://mail.google.com/mail/b/%s/%s/' % (user, proto))
base_string = GenerateSignatureBaseString(
method,
request_url_base,
signed_params)
logger.debug('signature base string: %s', base_string)
signature = GenerateOauthSignature(base_string, consumer.secret,
access_token.secret)
oauth_params['oauth_signature'] = signature
formatted_params = []
for k, v in sorted(oauth_params.iteritems()):
formatted_params.append('%s="%s"' % (k, UrlEscape(v)))
param_list = ','.join(formatted_params)
if url_params:
request_url = '%s?%s' % (request_url_base,
FormatUrlParams(url_params))
else:
request_url = request_url_base
preencoded = '%s %s %s' % (method, request_url, param_list)
logger.debug('xoauth string: %s' + preencoded)
return preencoded
class GoogleAccountsUrlGenerator:
def __init__(self, user):
self.__apps_domain = None
at_index = user.find('@')
if at_index != -1 and (at_index + 1) < len(user):
domain = user[(at_index + 1):].lower()
if domain != 'gmail.com' and domain != 'googlemail.com':
self.__apps_domain = domain
def GetRequestTokenUrl(self):
return 'https://www.google.com/accounts/OAuthGetRequestToken'
def GetAuthorizeTokenUrl(self):
if self.__apps_domain:
return ('https://www.google.com/a/%s/OAuthAuthorizeToken' %
self.__apps_domain)
else:
return 'https://www.google.com/accounts/OAuthAuthorizeToken'
def GetAccessTokenUrl(self):
return 'https://www.google.com/accounts/OAuthGetAccessToken'
# Some raindrop specific stuff...
class TwitterAccountsUrlGenerator:
def GetRequestTokenUrl(self):
return 'http://twitter.com/oauth/request_token'
def GetAuthorizeTokenUrl(self):
return 'http://twitter.com/oauth/authorize'
def GetAccessTokenUrl(self):
return 'http://twitter.com/oauth/access_token'
def getOAuthUrlGenerator(provider, user):
if provider == 'twitter.com':
return TwitterAccountsUrlGenerator()
elif provider in ['gmail.com', 'google.com']:
return GoogleAccountsUrlGenerator(user)
else:
raise Exception('OAuth provider %s not supported' % provider)
def GenerateXOauthStringFromAcctInfo(protocol, acct_info):
"""Generates an IMAP XOAUTH authentication string from a raindrop
'account info' dictionary.
"""
username = acct_info.username
oauth_token = getattr(acct_info, 'oauth_token', None)
oauth_token_secret = getattr(acct_info, 'oauth_token_secret', None)
consumer_key = getattr(acct_info, 'oauth_consumer_key', 'anonymous')
consumer_secret = getattr(acct_info, 'oauth_consumer_secret', 'anonymous')
consumer = OAuthEntity(consumer_key, consumer_secret)
google_accounts_url_generator = GoogleAccountsUrlGenerator(username)
access_token = OAuthEntity(oauth_token, oauth_token_secret)
xoauth_requestor_id = None
# the utility functions above will generate nonce and timestamps for us
nonce = None
timestamp = None
xoauth_string = GenerateXOauthString(
consumer, access_token, username, protocol,
xoauth_requestor_id, nonce, timestamp)
return xoauth_string
def AcctInfoSupportsOAuth(acct_info):
# A reflection on the need to have a URL generator per provider is that
# we only support gmail for now...
is_google = getattr(acct_info, 'kind', None) == 'gmail' or \
getattr(acct_info, 'host', '').endswith('gmail.com')
return is_google and getattr(acct_info, 'oauth_token', None) and getattr(acct_info, 'oauth_token_secret', None)

Просмотреть файл

@ -0,0 +1,34 @@
"""Pylons application test package
This package assumes the Pylons environment is already loaded, such as
when this script is imported from the `nosetests --with-pylons=test.ini`
command.
This module initializes the application via ``websetup`` (`paster
setup-app`) and provides the base testing objects.
"""
from unittest import TestCase
from paste.deploy import loadapp
from paste.script.appinstall import SetupCommand
from pylons import url
from routes.util import URLGenerator
from webtest import TestApp
import pylons.test
__all__ = ['environ', 'url', 'TestController']
# Invoke websetup with the current config file
SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']])
environ = {}
class TestController(TestCase):
def __init__(self, *args, **kwargs):
wsgiapp = pylons.test.pylonsapp
config = wsgiapp.config
self.app = TestApp(wsgiapp)
url._push_object(URLGenerator(config['routes.map'], environ))
TestCase.__init__(self, *args, **kwargs)

Просмотреть файл

Просмотреть файл

18
linkdrop/websetup.py Normal file
Просмотреть файл

@ -0,0 +1,18 @@
"""Setup the linkdrop application"""
import logging
import pylons.test
from linkdrop.config.environment import load_environment
from linkdrop.model.meta import Session, Base
log = logging.getLogger(__name__)
def setup_app(command, conf, vars):
"""Place any commands to setup linkdrop here"""
# Don't reload the app if it was loaded under the testing environment
if not pylons.test.pylonsapp:
load_environment(conf.global_conf, conf.local_conf)
# Create the tables if they don't already exist
Base.metadata.create_all(bind=Session.bind)

31
setup.cfg Normal file
Просмотреть файл

@ -0,0 +1,31 @@
[egg_info]
tag_build = dev
tag_svn_revision = true
[easy_install]
find_links = http://www.pylonshq.com/download/
[nosetests]
with-pylons = test.ini
# Babel configuration
[compile_catalog]
domain = linkdrop
directory = linkdrop/i18n
statistics = true
[extract_messages]
add_comments = TRANSLATORS:
output_file = linkdrop/i18n/linkdrop.pot
width = 80
[init_catalog]
domain = linkdrop
input_file = linkdrop/i18n/linkdrop.pot
output_dir = linkdrop/i18n
[update_catalog]
domain = linkdrop
input_file = linkdrop/i18n/linkdrop.pot
output_dir = linkdrop/i18n
previous = true

37
setup.py Normal file
Просмотреть файл

@ -0,0 +1,37 @@
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='linkdrop',
version='0.1',
description='',
author='',
author_email='',
url='',
install_requires=[
"Pylons>=1.0",
"SQLAlchemy>=0.5",
],
setup_requires=["PasteScript>=1.6.3"],
packages=find_packages(exclude=['ez_setup']),
include_package_data=True,
test_suite='nose.collector',
package_data={'linkdrop': ['i18n/*/LC_MESSAGES/*.mo']},
#message_extractors={'linkdrop': [
# ('**.py', 'python', None),
# ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
# ('public/**', 'ignore', None)]},
zip_safe=False,
paster_plugins=['PasteScript', 'Pylons'],
entry_points="""
[paste.app_factory]
main = linkdrop.config.middleware:make_app
[paste.app_install]
main = pylons.util:PylonsInstaller
""",
)

21
test.ini Normal file
Просмотреть файл

@ -0,0 +1,21 @@
#
# linkdrop - Pylons testing environment configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = true
# Uncomment and replace with the address which should receive any error reports
#email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 5000
[app:main]
use = config:development.ini
# Add additional test specific configuration options as necessary.

6
web/scratch/README.txt Normal file
Просмотреть файл

@ -0,0 +1,6 @@
This is a "scratch pad" folder - it is used for items to aid with development
before the "real" application has matured. As a result, they tend to be very
light in terms of styling and design elements - they are just bare-bones.
Items here should be short-lived and removed from here once they are replaced
with real UI.

Просмотреть файл

@ -0,0 +1,168 @@
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Raindrop.
-
- The Initial Developer of the Original Code is
- Mozilla Messaging, Inc..
- Portions created by the Initial Developer are Copyright (C) 2009
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- -->
<!DOCTYPE html>
<html>
<head>
<title>Links keep dropping on my head</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script src="../../scripts/requireplugins-jquery-1.4.2.js" charset="utf-8"></script>
<script>require(["index.js"]);</script>
<style>
.hidden {
display: none;
}
.invisible {
visibility: hidden;
}
</style>
</head>
<body class="settings">
<div class="row">
<div class="c4 logo">
</div>
</div>
<!-- default section -->
<div class="section welcome hidden">
<div class="row">
<div class="c1">
<p>Welcome.</p>
<p><a href="#twitter">Create an account</a></p>
</div>
</div>
</div>
<!-- twitter section -->
<div class="section twitter hidden">
<form id="oauthForm" action="../../api/account/oauth_start" method="GET">
<div class="row">
<div class="c1 twitterHeader">
<strong>step 4:</strong> Add twitter account *
</div>
<div class="c1">
<input type="hidden" name="domain" value="twitter.com">
<input type="hidden" name="return_to" value="/scratch/oauth/">
<div class="twitterActions">
<a class="skip" href="#facebook">skip this step</a>
<button>submit</button>
</div>
<div class="finePrint grey">
*You may delete your account at any time
</div>
</div>
</div>
<div class="c1">
<div class="usernameError error invisible">Please enter your Twitter name</div>
</div>
</div>
</form>
</div>
<!-- oauth_success_twitter section -->
<div class="section oauth_success_twitter_com hidden">
<div class="row">
<div class="c1">
<h4 class="success">Success!</h4>
<p>Twitter properly configured</p>
<button class="notification" onclick="location='#facebook'">continue</button>
</div>
</div>
</div>
<!-- oauth_failure_twitter section -->
<div class="section oauth_failure_twitter_com hidden">
<div class="row">
<div class="c1">
<h4 class="error">Error</h4>
<p>Twitter not properly configured</p>
<button class="notification" onclick="location='#twitter'">try again</button>
<button class="notification" onclick="location='#facebook'">continue</button>
</div>
</div>
</div>
<!-- facebook section -->
<div class="section facebook hidden">
<form id="oauthForm" action="../../api/account/oauth_facebook" method="GET">
<div class="row">
<div class="c1 facebookHeader">
<strong>step 4:</strong> Add facebook account *
</div>
<div class="c1">
<input type="hidden" name="domain" value="facebook.com">
<input type="hidden" name="scope" value="email,offline_access,publish_stream">
<input type="hidden" name="return_to" value="/scratch/oauth/">
<div class="facebookActions">
<a class="skip" href="#done">skip this step</a>
<button>submit</button>
</div>
<div class="finePrint grey">
*You may delete your account at any time
</div>
</div>
</div>
</div>
</form>
</div>
<!-- oauth_success_facebook section -->
<div class="section oauth_success_facebook_com hidden">
<div class="row">
<div class="c1">
<h4 class="success">Success!</h4>
<p>Facebook properly configured</p>
<button class="notification" onclick="location='#done'">continue</button>
</div>
</div>
</div>
<!-- oauth_failure_facebook section -->
<div class="section oauth_failure_facebook_com hidden">
<div class="row">
<div class="c1">
<h4 class="error">Error</h4>
<p>Facebook not properly configured</p>
<button class="notification" onclick="location='#facebook'">try again</button>
<button class="notification" onclick="location='#done'">continue</button>
</div>
</div>
</div>
<!-- done section -->
<div class="section done hidden">
<div class="row">
<div class="c1">
<p>Thanks - we are done!</p>
</div>
</div>
</div>
</body>
</html>

121
web/scratch/oauth/index.js Normal file
Просмотреть файл

@ -0,0 +1,121 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Raindrop.
*
* The Initial Developer of the Original Code is
* Mozilla Messaging, Inc..
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* */
/*jslint plusplus: false */
/*global require: false, location: true, window: false, alert: false */
"use strict";
require.def("signup",
["require", "jquery", "blade/fn", "rdapi", "placeholder", "blade/url"],
function (require, $, fn, rdapi, placeholder, url) {
//Given a new rd-token, save it to local storage
var search = window.location.href.split('?')[1];
if (search) {
search = search.split('#')[0];
var args = url.queryToObject(search);
if (args['rd-token']) {
localStorage['X-Raindrop-Token'] = args['rd-token'];
}
}
var rdtoken = localStorage['X-Raindrop-Token'];
if (rdtoken) {
$('form').each(function(i, node) {
node = $(node);
node.append('<input type="hidden" name="rd-token" value="'+localStorage['X-Raindrop-Token']+'">')
})
}
var validHashRegExp = /^\w+$/;
function onHashChange() {
var value = location.hash.split("#")[1],
start, end;
value = value || "welcome";
if (validHashRegExp.test(value)) {
$(".section").each(function (i, node) {
node = $(node);
if (node.hasClass(value)) {
end = node;
} else if (!node.hasClass("hidden")) {
start = node;
}
});
}
//Animate!
if (start) {
//Start node
start.fadeOut(600, function () {
start.addClass("hidden");
});
}
if (end) {
//End node
end.removeClass("hidden")
.fadeIn(600);
}
}
//Set up hashchange listener
window.addEventListener("hashchange", onHashChange, false);
$(function () {
$("#oauthForm")
.submit(function (evt) {
//First clear old errors
$(".error").addClass("invisible");
var form = evt.target,
isError = false;
//Make sure all form elements are trimmed and username exists.
$.each(form.elements, function (i, node) {
var trimmed = node.value.trim();
if (node.getAttribute("placeholder") === trimmed) {
trimmed = "";
}
node.value = trimmed;
});
if (isError) {
$(".usernameError", form).removeClass("invisible");
placeholder(form);
evt.stopPropagation();
evt.preventDefault();
}
})
.each(function (i, node) {
placeholder(node);
});
//Make sure we set up initial state
onHashChange();
});
});

Просмотреть файл

@ -0,0 +1,99 @@
/**
* @license blade/defer Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
* Available via the MIT, GPL or new BSD license.
* see: http://github.com/jrburke/blade for details
*/
/*jslint nomen: false, plusplus: false */
/*global require: false */
'use strict';
require.def('blade/defer', ['blade/fn', 'blade/dispatch'], function (fn, bladeDispatch) {
/**
* Creates an object representing a deferred action.
* @param {Function} [onCancel] optional function to call if the deferred
* action is canceled
* @param {Array} otherEventNames an array of event names to also allow
* sending and notifying on this type of deferred action. This allows you
* to express more complex interactions besides something that just indicates
* "ok", "error" or "cancel".
* @returns {Object} object representing the deferred action. It contains
* two properties:
* send: a function to send events. It takes a string name for the event,
* "ok", "error" or "cancel", and a value.
* listener: an object that only exposes an "ok", "error" and "cancel"
* functions that allow listening to those respective events. If otherEventNames
* specified other events, then there are listener registration functions
* for those event names too.
*/
function defer(onCancel, otherEventNames) {
var dfd = {},
sentName, i, evtName,
dispatch = bladeDispatch.make(),
makeCb = function (name) {
return function (obj, f) {
var cb = fn.bind(obj, f);
dispatch.onAfter(name, function (evt) {
return cb(evt.returnValue);
}, true);
return dfd.listener;
};
};
//Set up the cancellation action if desired.
if (onCancel) {
dispatch.onAfter('cancel', function (evt) {
return onCancel();
});
}
dfd.send = function (name, value) {
//Do not allow sending more than one message for the deferred.
if (sentName) {
throw new Error('blade/defer object already sent event: ' + sentName);
}
sentName = name;
dispatch.send({
name: name,
args: [value],
persist: true
});
//If no error handlers on this deferred, be sure to at least
//log it to allow some sort of debugging.
if (name === 'error' &&
(!dispatch._dispatchAfterQ || ! dispatch._dispatchAfterQ.error) &&
defer.onErrorDefault) {
defer.onErrorDefault(value);
}
return dfd;
};
dfd.listener = {
ok: makeCb('ok'),
error: makeCb('error'),
cancel: makeCb('cancel')
};
//Allow wiring up other event names
if (otherEventNames) {
for (var i = 0; (evtName = otherEventNames[i]); i++) {
dfd.listener[name] = makeCb[name];
}
}
return dfd;
}
defer.onErrorDefault = function (err) {
if (typeof console !== 'undefined') {
console.error(err);
}
}
return defer;
});

Просмотреть файл

@ -0,0 +1,227 @@
/**
* @license blade/dispatch Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
* Available via the MIT, GPL or new BSD license.
* see: http://github.com/jrburke/blade for details
*/
/*jslint nomen: false, plusplus: false */
/*global require: false */
'use strict';
require.def('blade/dispatch', ['blade/object', 'blade/fn'], function (object, fn) {
var emptyFunc = function () {},
mainDispatch,
slice = Array.prototype.slice,
needBind = function (f) {
return f !== undefined && (typeof f === 'string' || fn.is(f));
},
register = function (type) {
return function (name, obj, f) {
//Adjust args to allow for a bind call
if (needBind(f)) {
f = fn.bind(obj, f);
} else {
f = obj;
}
var qName = type,
typeQ = this[qName] || (this[qName] = {}),
q = typeQ[name] || (typeQ[name] = []), index;
index = q.push(f) - 1;
q.count = q.count ? q.count + 1 : 1;
//Return an unregister function to allow removing
//a listener. Notice that it can make the q array sparsely
//populated. This should be a sparsely populated array
//to allow a callback to unregister itself without affecting
//other callbacks in the array.
return function () {
q[index] = null;
q.count -= 1;
if (q.count === 0) {
delete typeQ[name];
}
//Clean up closure references for good measure/avoid leaks.
qName = typeQ = q = null;
};
};
},
onAfter = register('_dispatchAfterQ'),
/**
* Defines the dispatch object. You can call its methods for a general
* publish/subscribe mechanism, or mixin its prototype properties
* to another object to give that object dispatch capabilities.
*/
dispatch = {
on: register('_dispatchBeforeQ'),
onAfter: function (name, obj, f, wantValue) {
var doBind = needBind(f), result, value, callback, evt;
//Adjust args if needing a bind
if (doBind) {
callback = f = fn.bind(obj, f);
} else {
wantValue = f;
callback = obj;
}
result = doBind ? onAfter.call(this, name, f, wantValue) : onAfter.call(this, name, obj, f);
if (wantValue) {
//value is the property on the object, unless it is something
//that should be immutable or does not exist, then only get a value from _dispatchPersisted
value = name in this ? this[name] :
(this._dispatchPersisted && name in this._dispatchPersisted ? this._dispatchPersisted[name] : undefined);
evt = {
preventDefault: emptyFunc,
stopPropagation: emptyFunc,
returnValue: value
};
if (value !== undefined) {
callback(evt);
}
}
return result;
},
/**
* Sends an event. An event can have its values modified by "before"
* listeners before the default action happens. A "before" listener
* can also prevent the default action from occurring. "after" listeners
* only get to be notified of the return value from the event.
*
* @param {Object||String} message the message can either be an object
* with the following properties:
* @param {String} message.name the name of the message
* @param {Array} message.args the array of arguments for the message
* @param {Boolean}message.persist the result of the send should be
* remembered, so that any subsequent listeners that listen after
* the result is rememberd can opt to get the last good value.
* @param {Function} [message.defaultAction] a default action to take
* if any of the "before" listeners do not call preventDefault()
* on the event object they receive.
*
* If message is a string, then that is like the "name" property mentioned
* above, and any additional function arguments are treated as the
* args array.
*
* If defaultAction is not passed, then the default action will be to
* either set the property value on this object that matches the name
* to the first arg value, or if the name maps to function property
* on the object, it will call that function with the args.
*
* @returns {Object} the returnValue from any
*/
send: function (message) {
if (typeof message === 'string') {
//Normalize message to object arg form.
message = {
name: message,
args: slice.call(arguments, 1)
};
}
var name = message.name,
beforeQ = this._dispatchBeforeQ && this._dispatchBeforeQ[name],
afterQ = this._dispatchAfterQ && this._dispatchAfterQ[name],
preventDefault = false, stopImmediatePropagation,
evt = {
preventDefault: function () {
preventDefault = true;
},
stopImmediatePropagation: function () {
stopImmediatePropagation = true;
},
args: message.args
},
i, result, value, args, isFunc, persisted;
//Trigger before listeners
if (beforeQ) {
for (i = 0; i < beforeQ.length; i++) {
//array can be sparse because of unregister functions
if (beforeQ[i]) {
beforeQ[i](evt);
if (stopImmediatePropagation) {
break;
}
}
}
}
//If a before handler prevents the default action, exit
//early, using any return value found in the event that may
//have been set by a before handler.
if (preventDefault) {
return evt.returnValue;
}
//Do the default action.
if (message.defaultAction) {
result = message.defaultAction.apply(this, evt.args);
} else {
//Only bother if the property already exists on the object,
//otherwise it is a catch all or just an event router
args = evt.args;
if (name in this) {
isFunc = fn.is(this[name]);
value = this[name];
if (args && args.length) {
//A set operation
result = isFunc ? value.apply(this, args) : this[name] = args[0];
} else {
//A get operation
result = isFunc ? this[name]() : value;
}
} else if (this._dispatchCatchAll) {
//Allow the catch all to get it.
result = this._dispatchCatchAll(name, args);
} else {
result = args && args[0];
}
}
//Trigger mutable after listeners first, before the immutable ones
//to allow the mutable ones to modify the result.
if (afterQ) {
stopImmediatePropagation = false;
evt.returnValue = result;
for (i = 0; i < afterQ.length; i++) {
//array can be sparse because of unregister functions
if (afterQ[i]) {
afterQ[i](evt);
if (stopImmediatePropagation) {
break;
}
}
}
result = evt.returnValue;
}
//Hold on to the result if need be. Useful for the deferred/promise
//cases where listeners can be added after the deferred completes.
if (message.persist) {
persisted = this._dispatchPersisted || (this._dispatchPersisted = {});
persisted[message.name] = result;
}
return result;
}
};
//Create a top level dispatch that can be used for "global" event routing,
//and which can make new dispatch objects that have all the methods above,
//but without the instance variables.
mainDispatch = object.create(dispatch);
mainDispatch.make = function () {
return object.create(dispatch);
};
return mainDispatch;
});

58
web/scripts/blade/fn.js Normal file
Просмотреть файл

@ -0,0 +1,58 @@
/**
* @license blade/func Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
* Available via the MIT, GPL or new BSD license.
* see: http://github.com/jrburke/blade for details
*/
/*jslint nomen: false, plusplus: false */
/*global require: false */
'use strict';
require.def('blade/fn', function () {
var slice = Array.prototype.slice,
ostring = Object.prototype.toString,
fn = {
/**
* Determines if the input a function.
* @param {Object} it whatever you want to test to see if it is a function.
* @returns Boolean
*/
is: function (it) {
return ostring.call(it) === '[object Function]';
},
/**
* Different from Function.prototype.bind in ES5 --
* it has the "this" argument listed first. This is generally
* more readable, since the "this" object is visible before
* the function body, reducing chances for error by missing it.
* If only obj has a real value then obj will be returned,
* allowing this method to be called even if you are not aware
* of the format of the obj and f types.
* It also allows the function to be a string name, in which case,
* obj[f] is used to find the function.
* @param {Object||Function} obj the "this" object, or a function.
* @param {Function||String} f the function of function name that
* should be called with obj set as the "this" value.
* @returns {Function}
*/
bind: function (obj, f) {
//Do not bother if
if (!f) {
return obj;
}
//Make sure we have a function
if (typeof f === 'string') {
f = obj[f];
}
var args = slice.call(arguments, 2);
return function () {
return f.apply(obj, args.concat(slice.call(arguments, 0)));
};
}
};
return fn;
});

863
web/scripts/blade/jig.js Normal file
Просмотреть файл

@ -0,0 +1,863 @@
/**
* @license blade/jig Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
* Available via the MIT, GPL or new BSD license.
* see: http://github.com/jrburke/blade for details
*/
/*jslint nomen: false, plusplus: false */
/*global require: false, document: false, console: false, jQuery: false */
'use strict';
require.def('blade/jig',
['require', 'blade/object'],
function (require, object) {
//Fix unit test: something is wrong with it, says it passes, but
//with attachData change, the string is actually different now.
//TODO: for attachData, only generate a new ID when the data value changes,
//and similarly, only attach the data one time per data value.
//If have <img class="contactPhoto" src="{foo}"> browser tries to fetch
//{foo} if that is in markup. Doing a <{/}img, then FF browser treats that
//as &lt;{/}img. Using <img{/} ends up with <img{ }="" in text.
var jig, commands,
ostring = Object.prototype.toString,
decode = typeof decodeURIComponent === 'undefined' ? function () {} : decodeURIComponent,
startToken = '{',
endToken = '}',
rawHtmlToken = '^',
templateRefToken = '#',
argSeparator = ' ',
//First character in an action cannot be something that
//could be the start of a regular JS property name,
//or an array indice indicator, [, or the HTML raw output
//indicator, ^.
propertyRegExp = /[_\[\^\w]/,
defaultArg = '_',
startTagRegExp = /<\s*\w+/,
wordRegExp = /^\d+$/,
badCommentRegExp = /\/(\/)?\s*\]/,
templateCache = {},
defaultFuncs = {
openCurly: function () {
return '{';
},
closeCurly: function () {
return '}';
},
eq: function (a, b) {
return a === b;
},
gt: function (a, b) {
return a > b;
},
gte: function (a, b) {
return a >= b;
},
lt: function (a, b) {
return a < b;
},
lte: function (a, b) {
return a <= b;
},
or: function (a, b) {
return !!(a || b);
},
and: function (a, b) {
return !!(a && b);
},
is: function (a) {
return !!a;
},
eachProp: function (obj) {
//Converts object properties into an array
//of objects that have 'prop' and 'value' properties.
var prop, ret = [];
for (prop in obj) {
if (obj.hasOwnProperty(prop)) {
ret.push({
prop: prop,
value: obj[prop]
});
}
}
//Sort the names to be roughly alphabetic
return ret.sort(function (a, b) {
return a.prop > b.prop ? 1 : -1;
});
}
},
attachData = false,
dataIdCounter = 1,
controlIdCounter = 1,
dataRegistry = {},
tempNode = typeof document !== 'undefined' && document.createElement ?
document.createElement('div') : null,
templateClassRegExp = /(\s*)(template)(\s*)/;
function isArray(it) {
return ostring.call(it) === '[object Array]';
}
/**
* Gets a property from a context object. Allows for an alternative topContext
* object that can be used for the first part property lookup if it is not
* found in context first.
* @param {Array} parts the list of nested properties to look up on a context.
* @param {Object} context the context to start the property lookup
* @param {Object} [topContext] an object to use as an alternate context
* for the very first part property to look up if it is not found in context.
* @returns {Object}
*/
function getProp(parts, context, topContext) {
var obj = context, i, p;
for (i = 0; obj && (p = parts[i]); i++) {
obj = (typeof obj === 'object' && p in obj ? obj[p] : (topContext && i === 0 && p in topContext ? topContext[p] : undefined));
}
return obj; // mixed
}
function strToInt(value) {
return value ? parseInt(value, 10) : 0;
}
function getObject(name, data, options) {
var brackRegExp = /\[([\w0-9\.'":]+)\]/,
part = name,
parent = data,
isTop = true,
match, pre, prop, obj, startIndex, endIndex, indices, result,
parenStart, parenEnd, func, funcName, arg, args, i, firstChar;
//If asking for the default arg it means giving back the current data.
if (name === defaultArg) {
return data;
}
//If name is just an integer, just return it.
if (wordRegExp.test(name)) {
return strToInt(name);
}
//An empty string is just returned.
if (name === '') {
return '';
}
//If the name looks like a string, just return that.
firstChar = name.charAt(0);
if (firstChar === "'" || firstChar === "'") {
return name.substring(1, name.length - 1);
}
//First check for function call. Function must be globally visible.
if ((parenStart = name.indexOf('(')) !== -1) {
parenEnd = name.lastIndexOf(')');
funcName = name.substring(0, parenStart);
func = options.fn[funcName];
if (!func) {
jig.error('Cannot find function named: ' + funcName + ' for ' + name);
return '';
}
arg = name.substring(parenStart + 1, parenEnd);
if (arg.indexOf(',') !== -1) {
args = arg.split(',');
for (i = args.length - 1; i >= 0; i--) {
args[i] = getObject(args[i], data, options);
}
result = func.apply(null, args);
} else {
result = func(getObject(arg, data, options));
}
if (parenEnd < name.length - 1) {
//More data properties after the function call, fetch them
//If the part after the paren is a dot, then skip over that part
if (name.charAt(parenEnd + 1) === '.') {
parenEnd += 1;
}
return getObject(name.substring(parenEnd + 1, name.length), result, options);
} else {
return result;
}
}
//Now handle regular object references, which could have [] notation.
while ((match = brackRegExp.exec(part))) {
prop = match[1].replace(/['"]/g, '');
pre = part.substring(0, match.index);
part = part.substring(match.index + match[0].length, part.length);
if (part.indexOf('.') === 0) {
part = part.substring(1, part.length);
}
obj = getProp(pre.split('.'), parent, isTop ? options.context : null);
isTop = false;
if (!obj && prop) {
jig.error('blade/jig: No property "' + prop + '" on ' + obj);
return '';
}
if (prop.indexOf(':') !== -1) {
//An array slice action
indices = prop.split(':');
startIndex = strToInt(indices[0]);
endIndex = strToInt(indices[1]);
if (!endIndex) {
obj = obj.slice(startIndex);
} else {
obj = obj.slice(startIndex, endIndex);
}
} else {
if (options.strict && !(prop in obj)) {
jig.error('blade/jig: no property "' + prop + '"');
}
obj = obj[prop];
}
parent = obj;
}
if (!part) {
result = parent;
} else {
result = getProp(part.split('.'), parent, isTop ? options.context : null);
}
if (options.strict && result === undefined) {
jig.error('blade/jig: undefined value for property "' + name + '"');
}
return result;
}
/**
* Gets a compiled template based on the template ID. Will look in the
* DOM for an element with that ID if a template is not found already in
* the compiled cache.
* @param {String} id the ID of the template/DOM node
* @param {Object} [options]
*
* @returns {Array} the compiled template.
*/
function compiledById(id, options) {
options = options || {};
var compiled = jig.cache(id, options), node;
//Did not find the text template. Maybe it is a DOM element.
if (compiled === undefined && typeof document !== 'undefined') {
node = document.getElementById(id);
if (node) {
jig.parse([node], options);
}
compiled = jig.cache(id, options);
}
if (compiled === undefined) {
throw new Error('blade/jig: no template or node with ID: ' + id);
}
return compiled;
}
commands = {
'_default_': {
doc: 'Property reference',
action: function (args, data, options, children, render) {
var value = args[0] ? getObject(args[0], data, options) : data,
comparison = args[1] ? getObject(args[1], data, options) : undefined,
i, text = '';
//If comparing to some other value, then the value is the data,
//and need to compute if the values compare.
if (args[1]) {
comparison = value === comparison;
value = data;
} else {
//Just use the value, so the value is used in the comparison.
comparison = value;
}
//Want to allow returning 0 for values, so this next check is
//a bit verbose.
if (comparison === false || comparison === null ||
comparison === undefined || (isArray(comparison) && !comparison.length)) {
return '';
} else if (children) {
if (isArray(value)) {
for (i = 0; i < value.length; i++) {
text += render(children, value[i], options);
}
} else {
//If the value is true or false, then just use parent data.
//for the child rendering.
if (typeof value === 'boolean') {
value = data;
}
text = render(children, value, options);
}
} else {
text = value;
}
return text;
}
},
'!': {
doc: 'Not',
action: function (args, data, options, children, render) {
var value = getObject(args[0], data, options),
comparison = args[1] ? getObject(args[1], data, options) : undefined;
//If comparing to some other value, then the value is the data,
//and need to compute if the values compare.
if (args[1]) {
comparison = value === comparison;
value = data;
} else {
//Just use the value, so the value is used in the comparison.
comparison = value;
}
if (children && !comparison) {
return render(children, data, options);
}
return '';
}
},
'#': {
doc: 'Template reference',
action: function (args, data, options, children, render) {
var compiled = compiledById(args[0], options);
data = getObject(args.length > 1 ? args[1] : defaultArg, data, options);
return render(compiled, data, options);
}
},
'.': {
doc: 'Variable declaration',
action: function (args, data, options, children, render) {
options.context[args[0]] = getObject(args[1], data, options);
//TODO: allow definining a variable then doing a block with
//that variable.
return '';
}
},
'>': {
doc: 'Else',
action: function (args, data, options, children, render) {
if (children) {
return render(children, data, options);
}
return '';
}
}
};
jig = function (text, data, options) {
var id;
if (typeof text === 'string') {
if (text.charAt(0) === '#') {
//a lookup by template ID
id = text.substring(1, text.length);
text = compiledById(id, options);
} else {
text = jig.compile(text, options);
}
}
return jig.render(text, data, options);
};
jig.htmlEscape = function (text) {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
};
function compile(text, options) {
var compiled = [],
start = 0,
useRawHtml = false,
controlId = 0,
segment, index, match, tag, command, args, lastArg, lastChar,
children, i, tempTag;
while ((index = text.indexOf(options.startToken, start)) !== -1) {
//Output any string that is before the template tag start
if (index !== start) {
compiled.push(text.substring(start, index));
}
//Find the end of the token
segment = text.substring(index);
match = options.endRegExp.exec(segment);
if (!match) {
//Just a loose start thing could be a regular punctuation.
compiled.push(segment);
return compiled;
} else {
//Command Match!
//Increment start past the match.
start = index + match[0].length;
//Pull out the command
tag = text.substring(index + options.startToken.length, index + match[0].length - options.endToken.length).trim();
//decode in case the value was in an URL field, like an href or an img src attribute
tag = decode(tag);
//if the command is commented out end block call, that messes with stuff,
//just throw to let the user know, otherwise browser can lock up.
if (badCommentRegExp.test(tag)) {
throw new Error('blade/jig: end block tags should not be commented: ' + tag);
}
command = tag.charAt(0);
if (command === ']' && controlId) {
//In a control block, previous block was a related control block,
//so parse it without the starting ] character.
tempTag = tag.substring(1).trim();
if (tempTag === '[') {
command = '>';
} else {
command = tempTag.charAt(0);
//Remove the starting ] so it is seen as a regular tag
tag = tempTag;
}
}
if (command && !options.propertyRegExp.test(command)) {
//Have a template command
tag = tag.substring(1).trim();
} else {
command = '_default_';
//Command could contain just the raw HTML indicator.
useRawHtml = (command === options.rawHtmlToken);
}
//Allow for raw HTML output, but it is not the default.
//template references use raw by default though.
if ((useRawHtml = tag.indexOf(options.rawHtmlToken) === 0)) {
tag = tag.substring(options.rawHtmlToken.length, tag.length);
}
//However, template references use raw always
if (command === templateRefToken) {
useRawHtml = true;
}
args = tag.split(options.argSeparator);
lastArg = args[args.length - 1];
lastChar = lastArg.charAt(lastArg.length - 1);
children = null;
if (command === ']') {
//If there are no other args, this is an end tag, to close
//out a block and possibly a set of control blocks.
if (lastChar !== '[') {
//End of a block. End the recursion, let the parent know
//the place where parsing stopped.
compiled.templateEnd = start;
//Also end of a control section, indicate it as such.
compiled.endControl = true;
} else {
//End of a block. End the recursion, let the parent know
//the place where parsing stopped, before this end tag,
//so it can process it and match it to a control flow
//from previous control tag.
compiled.templateEnd = start - match[0].length;
}
return compiled;
} else if (lastChar === '[') {
//If last arg ends with a [ it means a block element.
//Assign a new control section ID if one is not in play already
if (!controlId) {
controlId = controlIdCounter++;
}
//Adjust the last arg to not have the block character.
args[args.length - 1] = lastArg.substring(0, lastArg.length - 1);
//Process the block
children = compile(text.substring(start), options);
//Skip the part of the string that is part of the child compile.
start += children.templateEnd;
}
//If this defines a template, save it off,
//if a comment (starts with /), then ignore it.
if (command === '+') {
options.templates[args[0]] = children;
} else if (command !== '/') {
//Adjust args if some end in commas, it means they are function
//args.
if (args.length > 1) {
for (i = args.length - 1; i >= 0; i--) {
if (args[i].charAt(args[i].length - 1) === ',') {
args[i] = args[i] + args[i + 1];
args.splice(i + 1, 1);
}
}
}
compiled.push({
action: options.commands[command].action,
useRawHtml: useRawHtml,
args: args,
controlId: controlId,
children: children
});
}
//If the end of a block, clear the control ID
if (children && children.endControl) {
controlId = 0;
}
}
}
if (start !== text.length - 1) {
compiled.push(text.substring(start, text.length));
}
return compiled;
}
jig.compile = function (text, options) {
//Mix in defaults
options = options || {};
object.mixin(options, {
startToken: startToken,
endToken: endToken,
rawHtmlToken: rawHtmlToken,
propertyRegExp: propertyRegExp,
commands: commands,
argSeparator: argSeparator,
templates: templateCache
});
options.endRegExp = new RegExp('[^\\r\\n]*?' + endToken);
//Do some reset to avoid a number from getting too big.
controlIdCounter = 1;
return compile(text, options);
};
/**
* Converts a node to a compiled template, and will store it in the cache. If already
* in the cache, it will give back the cached value.
*/
function nodeToCompiled(node, options) {
var text, compiled, clss,
id = node.id,
cache = options.templates || templateCache;
//If the nodes has already been cached, then just get the cached value.
if (cache[id]) {
return cache[id];
}
//Call listener to allow processing of the node before
//template complication happens.
if (options.onBeforeParse) {
options.onBeforeParse(node);
}
if (node.nodeName.toUpperCase() === 'SCRIPT') {
text = node.text.trim();
if (node.parentNode) {
node.parentNode.removeChild(node);
}
} else {
//Put node in temp node to get the innerHTML so node's element
//html is in the output.
tempNode.appendChild(node);
//Remove the id node and the template class, since this
//template text could be duplicated many times, and a
//template class is no longer useful.
node.removeAttribute('id');
clss = (node.getAttribute('class') || '').trim();
if (clss) {
node.setAttribute('class', clss.replace(templateClassRegExp, '$1$3'));
}
//Decode braces when may get URL encoded as part of hyperlinks
text = tempNode.innerHTML.replace(/%7B/g, '{').replace(/%7D/g, '}');
//Clear out the temp node for the next use.
tempNode.removeChild(node);
}
compiled = jig.compile(text, options);
jig.cache(id, compiled, options);
return compiled;
}
/**
* Parses an HTML document for templates, compiles them, and stores them
* in a cache of templates to use on the page. Only useful in browser environments.
* Script tags with type="text/template" are parsed, as well as DOM elements
* that have a class of "template" on them. The found nodes will be removed
* from the DOM as part of the parse operation.
*
* @param {Array-Like} [nodes] An array-like list of nodes. Could be a NodeList.
* @param {Object} [options] A collection of options to use for compilation.
*/
jig.parse = function (nodes, options) {
//Allow nodes to not be passed in, but still have options.
if (nodes && !nodes.length) {
options = nodes;
nodes = null;
}
options = options || {};
nodes = nodes || document.querySelectorAll('.template, script[type="text/template"]');
var node, i;
for (i = nodes.length - 1; i > -1 && (node = nodes[i]); i--) {
nodeToCompiled(node, options);
}
};
function render(compiled, data, options) {
var text = '', i, dataId, controlId, currentControlId, currentValue, lastValue;
if (typeof compiled === 'string') {
text = compiled;
} else if (isArray(compiled)) {
for (i = 0; i < compiled.length; i++) {
//Account for control blocks (if/elseif/else)
//control blocks all have the same control ID, so only call the next
//control block if the first one did not return a value.
currentControlId = compiled[i].controlId;
if (!currentControlId || currentControlId !== controlId || !lastValue) {
currentValue = render(compiled[i], data, options);
text += currentValue;
if (currentControlId) {
controlId = currentControlId;
lastValue = currentValue;
}
}
}
} else {
//A template command to run.
text = compiled.action(compiled.args, data, options, compiled.children, render);
if (!text) {
text = '';
} else if (!compiled.useRawHtml && !compiled.children) {
//Only html escape commands that are not block actions.
text = jig.htmlEscape(text.toString());
}
}
if (options.attachData) {
if (startTagRegExp.test(text)) {
dataId = 'id' + (dataIdCounter++);
text = text.replace(startTagRegExp, '$& data-blade-jig="' + dataId + '" ');
dataRegistry[dataId] = data;
}
}
return text;
}
/**
* Render a compiled template.
*
* @param {Array} compiled a compiled template
* @param {Object} data the data to use in the template
* @param {Object} options options for rendering. They include:
* @param {Object} templates a cache of compiled templates that might be
* referenced by the primary template
* @param {Object} options.fn a set of functions that might be used
* by the template(s). Each property on this object is a name of a function
* that may show up in the templates, and the value should be the function
* definition.
* @returns {String} the rendered template.
*/
jig.render = function (compiled, data, options) {
var i, result = '';
//Normalize options, filling in defaults.
options = options || {};
object.mixin(options, {
templates: templateCache,
attachData: attachData,
strict: jig.strict
});
//Mix in default functions
if (options.fn) {
object.mixin(options.fn, defaultFuncs);
} else {
options.fn = defaultFuncs;
}
//Mix in top level context object
options.context = options.context || object.create(data);
//If data is an array, then render should be called for each item
//in the array.
if (isArray(data)) {
for (i = 0; i < data.length; i++) {
result += render(compiled, data[i], options);
}
return result;
}
//Default case, just render
return render(compiled, data, options);
};
/**
* Enable strict template rendering checks. If a property does not exist on a
* data object, then an error will be logged.
*/
jig.strict = false;
/**
* Track errors by logging to console if available.
*/
jig.error = typeof console !== 'undefined' && console.error ?
function (msg) {
console.error(msg);
}
: function () {};
/**
* Adds functions to the default set of functions that can be used inside
* a template. Newer definitions of a function will take precedence
* over the previously registered function.
* @param {Object} an object whose properties are names of functions
* and values are the functions that correspond to the names.
*/
jig.addFn = function (obj) {
object.mixin(defaultFuncs, obj, true);
};
/**
* Gets and sets the data bound to a particular rendered template. Setting
* the data does not change the already rendered template.
*
* @param {String||DOMNode} dataId the data ID, or a DOM node with a
* data-blade-jig attribute that was generated from a rendered template.
* @returns {Object} the bound data. Can return undefined if there is
* no data stored with that ID.
*/
jig.data = function (dataId, value) {
if (typeof dataId !== 'string') {
//Should be a DOM node or node list if it is not already a string.
if (!dataId.nodeType) {
dataId = dataId[0];
}
dataId = dataId.getAttribute('data-blade-jig');
}
if (value !== undefined) {
return (dataRegistry[dataId] = value);
} else {
return dataRegistry[dataId];
}
};
/**
* Removes some data that was bound to a rendered template.
* @param {String} dataId the data ID. It can be fetched from the
* data-blade-jig attribute on a rendered template.
*/
jig.removeData = function (dataId) {
delete dataRegistry[dataId];
};
/**
* Gets an object given a string representation. For example,
* jig.getObject('foo.bar', baz) will return the baz.foo.bar value.
*
* @param {String} name the string value to fetch. The following formats
* are allowed: 'foo.bar', 'foo['bar']', 'foo[0]', 'foo[2:6]'. The last one
* will return an array subset. Functions are also supported: 'doSomething(foo.bar)'
* but the doSomething function needs to be defined in the options.fn
* property, as options.fn.doSomething = function (){}
*
* @param {Object} data the object to use as the basis for the object lookup.
*
* @param {Object} options. Options to the lookup. The only supported option
* at this time is options.func, and object defining functions can could be
* called.
*
* @returns {Object} it could return null if the name is not found off the data
*/
jig.getObject = getObject;
/**
* Gets or sets a compiled template from a template cache.
* @param {String} id the template ID
* @param {String} [value] A string to compile to a template, or
* the compiled template value.
* @param {Object} [options] optional options object with a 'templates'
* property that contains some cached templates. If provided, a matching
* cache value for the ID will be used from options.templates, otherwise,
* the ID will be used to look up in the global blade/jig template cache.
* @returns {Object} a compiled template. It could return undefined if
* not match is found.
*/
jig.cache = function (id, value, options) {
//Convert the value to a compiled templated if necessary.
if (typeof value === 'string') {
value = jig.compile(value, options);
}
//If value is not an array, then a get operation, likely an options.
if (!isArray(value)) {
options = value;
value = undefined;
}
var cache = (options && options.templates) || templateCache;
if (value !== undefined) {
cache[id] = value;
}
//Return the value. For get use, the template may not be in
//the local options.templates, but in the global cache, so
//be sure to check both.
return cache[id] || templateCache[id];
};
function addToJQuery(jQuery) {
//Only handles queries where it is by a node ID, '#something'.
jQuery.fn.jig = function (data, options) {
//Convert this, which is a DOM node into a string of data
options = options || {};
var id = this.selector,
compiled;
if (id.charAt(0) !== '#') {
throw new Error('blade/jig: only ID selectors, like "#something" are allowed with jig()');
}
id = id.substring(1, id.length);
//See if the template is already compiled.
compiled = (options.templates || templateCache)[id];
if (!compiled) {
compiled = nodeToCompiled(this[0]);
}
return jQuery(jig.render(compiled, data, options));
};
}
//Set up the plugin with a RequireJS-aware jQuery module but also
//if there is a global jQuery.
//require.modify('jquery', 'jquery-jig', ['jquery'], addToJQuery);
if (typeof jQuery !== 'undefined') {
addToJQuery(jQuery);
}
return jig;
});

128
web/scripts/blade/object.js Normal file
Просмотреть файл

@ -0,0 +1,128 @@
/**
* @license blade/object Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
* Available via the MIT, GPL or new BSD license.
* see: http://github.com/jrburke/blade for details
*/
/*jslint plusplus: false */
/*global require: false */
'use strict';
require.def('blade/object', function () {
var empty = {},
/**
* Creates a new constructor function for generating objects of a certain type.
*
* @param {Object} base the base object to inherit from in the
* prototype chain. Pass null if no parent desired.
*
* @param {Array} mixins an array of objects to use to mix in their
* properties into the new object. Pass null if no mixins desired.
*
* @param {Function} objPropertyFunc, a function that returns an object
* whose properties should be part of this new object's prototype.
* The function will be passed the function used to call methods
* on the parent prototype used for this object. The function expects
* three arguments:
* - obj: pass the this object for this arg
* - funcName: the function name to call on the prototype object (a string)
* - args: an array of arguments. Normally just pass the arguments object.
* The parent prototype will be a combination of the base object
* with all mixins applied.
*
* @returns {Function} a constructor function.
*/
object = function (base, mixins, objPropertyFunc) {
var constructor,
//Create the parent and its parentFunc calling wrapper.
//The parent function just makes it easier to call the parent
parent = object.create(base.prototype, mixins),
parentFunc = function (obj, funcName, args) {
return parent[funcName].apply(obj, args);
},
//Create a different object for the prototype instead of using
//parent, so that parent can still refer to parent object
//without the curren object's properties mixed in
//(via the objPropertyFunc) with the mixed in properties taking
//priority over the parent's properties.
proto = object.create(parent);
object.mixin(proto, objPropertyFunc(parentFunc), true);
//Create the constructor function. Calls init if it is defined
//on the prototype (proto)
constructor = function () {
//Protect against a missing new
if (!(this instanceof constructor)) {
throw new Error('blade/object: constructor function called without "new" in front');
}
//Call initializer if present.
if (this.init) {
this.init.apply(this, arguments);
}
};
//Set the prototype for this constructor
constructor.prototype = proto;
return constructor;
};
/**
* Similar to ES5 create, but instead of setting property attributes
* for the second arg, allow an array of mixins to mix in properties
* to the newly created object.
* A copy of dojo.delegate
* @param {Object} parent the parent object to use as the prototype.
* @param {Array} [mixins] array of mixin objects to mix in to the new object.
*/
function Temp() {}
object.create = function (obj, mixins) {
Temp.prototype = obj;
var temp = new Temp(), i, mixin;
//Avoid any extra memory hanging around
Temp.prototype = null;
if (mixins) {
for (i = 0; (mixin = mixins[i]); i++) {
object.mixin(temp, mixin);
}
}
return temp; // Object
};
/**
* Simple function to mix in properties from source into target,
* but only if target does not already have a property of the same name,
* unless override is set to true. Borrowed from Dojo.
*
* To extend a prototype on a given object, pass in the prototype property
* to mixin. For example: object.mixin(func.prototype, {a: 'b'});
*
* @param {Object} target the object receiving the mixed in properties.
*
* @param {Object} source the object that contains the properties to mix in.
*
* @param {Boolean} [override] if set to true, then the source's properties
* will be mixed in even if a property of the same name already exists on
* the target.
*/
object.mixin = function (target, source, override) {
//TODO: consider ES5 getters and setters in here.
for (var prop in source) {
if (!(prop in empty) && (!(prop in target) || override)) {
target[prop] = source[prop];
}
}
return require;
};
return object;
});

59
web/scripts/blade/url.js Normal file
Просмотреть файл

@ -0,0 +1,59 @@
/**
* @license blade/url Copyright (c) 2010, The Dojo Foundation All Rights Reserved.
* Available via the MIT, GPL or new BSD license.
* see: http://github.com/jrburke/blade for details
*/
/*jslint nomen: false, plusplus: false */
/*global require: false */
'use strict';
require.def('blade/url', function () {
var ostring = Object.prototype.toString;
return {
queryToObject: function (/*String*/ str) {
// summary:
// Create an object representing a de-serialized query section of a
// URL. Query keys with multiple values are returned in an array.
//
// example:
// This string:
//
// | "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
//
// results in this object structure:
//
// | {
// | foo: [ "bar", "baz" ],
// | thinger: " spaces =blah",
// | zonk: "blarg"
// | }
//
// Note that spaces and other urlencoded entities are correctly
// handled.
var ret = {},
qp = str.split('&'),
dec = decodeURIComponent,
parts, name, val;
qp.forEach(function (item) {
if (item.length) {
parts = item.split('=');
name = dec(parts.shift());
val = dec(parts.join('='));
if (typeof ret[name] === 'string') {
ret[name] = [ret[name]];
}
if (ostring.call(ret[name]) === '[object Array]') {
ret[name].push(val);
} else {
ret[name] = val;
}
}
});
return ret;
}
};
});

149
web/scripts/cards.js Normal file
Просмотреть файл

@ -0,0 +1,149 @@
/*jslint */
/*global require: false, window: false, document: false, cards: true */
'use strict';
require.def('cards', ['jquery', 'text!templates/cardsHeader.html'], function ($, headerTemplate) {
var header, display, back, nlCards,
cardPosition = 0,
headerText = '',
cardTitles = [];
function adjustCardSizes() {
var cardWidth = display.outerWidth(),
cardList = $('.card'),
totalWidth = cardWidth * cardList.length,
height = window.innerHeight - header.outerHeight();
//Set height
display.css('height', height + 'px');
//Set widths and heights of cards. Need to set the heights
//explicitly so any card using iscroll will get updated correctly.
nlCards.css({
width: totalWidth + 'px',
height: height + 'px'
});
cardList.css({
width: cardWidth + 'px',
height: height + 'px'
});
//Reset the scroll correctly.
cards.scroll();
}
function cards(nl, options) {
nl = nl.jquery ? nl : $(nl);
$(function () {
//Insert the header before the cards
header = $(headerTemplate).insertBefore(nl);
headerText = $('#headerText');
back = $('#back');
back.css('display', 'none');
back.click((options && options.onBack) || cards.back);
display = nl;
nlCards = display.find('#cards');
adjustCardSizes();
cards.setTitle(options && options.title);
//Detect orientation changes and size the card container size accordingly.
if ('onorientationchange' in window) {
window.addEventListener('orientationchange', adjustCardSizes, false);
}
window.addEventListener('resize', adjustCardSizes, false);
});
}
cards.adjustCardSizes = adjustCardSizes;
/**
* Adds a new card to the list of cards, at the end of the cards.
* Only adds the card, does not navigate to it. Only adds the card
* if a DOM element with the info.id does not already exist in the page.
*
* @param {Object} info the info about the card. It must have the following
* properties:
* @param {String} info.id the ID to use for the new card's DOM element.
* @param {String} info.title the text title to use for the card.
* @param {String} info.content a string of HTML to use for the content.
*/
cards.add = function (info) {
var existing = $('#' + info.id),
title = info.title;
if (!title) {
title = info.content.match(/<h1>([^<]+)<\/h1>/);
title = (title && title[1]) || '';
}
if (!existing.length) {
existing = $('<div id="' + info.id + '" class="card" title="' + title + '">' + info.content + '</div>')
.appendTo('#cards');
cards.adjustCardSizes();
}
return existing[0];
};
cards.back = function () {
cardPosition -= 1;
if (cardPosition < 0) {
cardPosition = 0;
}
cards.scroll();
};
cards.moveTo = function (id) {
cardPosition = $('.card').index(document.getElementById(id));
if (cardPosition < 0) {
cardPosition = 0;
}
cards.scroll();
};
cards.forward = function (title) {
cardPosition += 1;
cards.scroll(title);
};
cards.scroll = function (title) {
if (title) {
cardTitles[cardPosition] = title;
}
cards.setTitle(title);
var left = display.outerWidth() * cardPosition;
nlCards.animate(
{
left: '-' + left + 'px'
}, {
duration: 300,
easing: 'linear'
}
);
/*
Was used for CSS -webkit-transition
nlCards.css({
left: '-' + left + 'px'
});
*/
//Hide/Show back button as appropriate
back.css('display', !cardPosition ? 'none' : '');
};
cards.setTitle = function (title) {
title = title || cardTitles[cardPosition] || nlCards.find('.card').eq(cardPosition).attr('title') || '';
headerText.html(title);
};
return cards;
});

174
web/scripts/fancyzoom.js Normal file
Просмотреть файл

@ -0,0 +1,174 @@
(function($){
$.fn.fancyZoom = function(options){
var options = options || {};
var directory = options && options.directory ? options.directory : 'images';
var zooming = false;
if ($('#zoom').length == 0) {
var ext = $.browser.msie ? 'gif' : 'png';
var html = '<div id="zoom" style="display:none;"> \
<table id="zoom_table" style="border-collapse:collapse; width:100%; height:100%;"> \
<tbody> \
<tr> \
<td class="tl" style="background:url(' + directory + '/tl.' + ext + ') 0 0 no-repeat; width:20px; height:20px; overflow:hidden;" /> \
<td class="tm" style="background:url(' + directory + '/tm.' + ext + ') 0 0 repeat-x; height:20px; overflow:hidden;" /> \
<td class="tr" style="background:url(' + directory + '/tr.' + ext + ') 100% 0 no-repeat; width:20px; height:20px; overflow:hidden;" /> \
</tr> \
<tr> \
<td class="ml" style="background:url(' + directory + '/ml.' + ext + ') 0 0 repeat-y; width:20px; overflow:hidden;" /> \
<td class="mm" style="background:#fff; vertical-align:top; padding:10px;"> \
<div id="zoom_content"> \
</div> \
</td> \
<td class="mr" style="background:url(' + directory + '/mr.' + ext + ') 100% 0 repeat-y; width:20px; overflow:hidden;" /> \
</tr> \
<tr> \
<td class="bl" style="background:url(' + directory + '/bl.' + ext + ') 0 100% no-repeat; width:20px; height:20px; overflow:hidden;" /> \
<td class="bm" style="background:url(' + directory + '/bm.' + ext + ') 0 100% repeat-x; height:20px; overflow:hidden;" /> \
<td class="br" style="background:url(' + directory + '/br.' + ext + ') 100% 100% no-repeat; width:20px; height:20px; overflow:hidden;" /> \
</tr> \
</tbody> \
</table> \
<a href="#" title="Close" id="zoom_close" style="position:absolute;"> \
<img src="' + directory + '/closebox.' + ext + '" alt="Close" style="border:none; margin:0; padding:0;" /> \
</a> \
</div>';
$('body').append(html);
$('html').click(function(e){if($(e.target).parents('#zoom:visible').length == 0) hide();});
$(document).keyup(function(event){
if (event.keyCode == 27 && $('#zoom:visible').length > 0) hide();
});
$('#zoom_close').click(hide);
}
var zoom = $('#zoom');
var zoom_table = $('#zoom_table');
var zoom_close = $('#zoom_close');
var zoom_content = $('#zoom_content');
var middle_row = $('td.ml,td.mm,td.mr');
this.each(function(i) {
$($(this).attr('href')).hide();
$(this).click(show);
});
return this;
function show(e) {
if (zooming) return false;
zooming = true;
var content_div = $($(this).attr('href'));
var zoom_width = options.width;
var zoom_height = options.height;
var width = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth);
var height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight);
var x = window.pageXOffset || (window.document.documentElement.scrollLeft || window.document.body.scrollLeft);
var y = window.pageYOffset || (window.document.documentElement.scrollTop || window.document.body.scrollTop);
var window_size = {'width':width, 'height':height, 'x':x, 'y':y}
var width = (zoom_width || content_div.width()) + 60;
var height = (zoom_height || content_div.height()) + 60;
var d = window_size;
// ensure that newTop is at least 0 so it doesn't hide close button
var newTop = Math.max((d.height/2) - (height/2) + y, 0);
var newLeft = (d.width/2) - (width/2);
var curTop = e.pageY;
var curLeft = e.pageX;
zoom_close.attr('curTop', curTop);
zoom_close.attr('curLeft', curLeft);
zoom_close.attr('scaleImg', options.scaleImg ? 'true' : 'false');
$('#zoom').hide().css({
position : 'absolute',
top : curTop + 'px',
left : curLeft + 'px',
width : '1px',
height : '1px'
});
fixBackgroundsForIE();
zoom_close.hide();
if (options.closeOnClick) {
$('#zoom').click(hide);
}
if (options.scaleImg) {
zoom_content.html(content_div.html());
$('#zoom_content img').css('width', '100%');
} else {
zoom_content.html('');
}
$('#zoom').animate({
top : newTop + 'px',
left : newLeft + 'px',
opacity : "show",
width : width,
height : height
}, 100, null, function() {
if (options.scaleImg != true) {
zoom_content.html(content_div.html());
}
unfixBackgroundsForIE();
zoom_close.show();
zooming = false;
})
return false;
}
function hide() {
if (zooming) return false;
zooming = true;
$('#zoom').unbind('click');
fixBackgroundsForIE();
if (zoom_close.attr('scaleImg') != 'true') {
zoom_content.html('');
}
zoom_close.hide();
$('#zoom').animate({
top : zoom_close.attr('curTop') + 'px',
left : zoom_close.attr('curLeft') + 'px',
opacity : "hide",
width : '1px',
height : '1px'
}, 100, null, function() {
if (zoom_close.attr('scaleImg') == 'true') {
zoom_content.html('');
}
unfixBackgroundsForIE();
zooming = false;
});
return false;
}
function switchBackgroundImagesTo(to) {
$('#zoom_table td').each(function(i) {
var bg = $(this).css('background-image').replace(/\.(png|gif|none)\"\)$/, '.' + to + '")');
$(this).css('background-image', bg);
});
var close_img = zoom_close.children('img');
var new_img = close_img.attr('src').replace(/\.(png|gif|none)$/, '.' + to);
close_img.attr('src', new_img);
}
function fixBackgroundsForIE() {
if ($.browser.msie && parseFloat($.browser.version) >= 7) {
switchBackgroundImagesTo('gif');
}
}
function unfixBackgroundsForIE() {
if ($.browser.msie && $.browser.version >= 7) {
switchBackgroundImagesTo('png');
}
}
}
})(jQuery);

129
web/scripts/friendly.js Normal file
Просмотреть файл

@ -0,0 +1,129 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Raindrop.
*
* The Initial Developer of the Original Code is
* Mozilla Messaging, Inc..
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* */
/*jslint plusplus: false, nomen: false */
/*global require: false */
"use strict";
require.def("friendly", function () {
var friendly = {
timestamp: function (timestamp) {
return friendly.date(new Date(timestamp * 1000));
},
date: function (date) {
var diff = (((new Date()).getTime() - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400),
dObj = { "friendly" : date.toLocaleDateString(),
"additional" : date.toLocaleTimeString(),
"utc" : date.toUTCString(),
"locale" : date.toLocaleString() };
/* some kind of error */
if (day_diff < 0) {
dObj.friendly = "in the future";
return dObj;
} else if (isNaN(day_diff)) {
dObj.friendly = dObj.additional = "unknown";
return dObj;
}
if (day_diff === 0) {
if (diff < 60) {
dObj.friendly = "just now";
return dObj;
}
if (diff < 120 + 30) { /* 1 minute plus some fuzz */
dObj.friendly = "a minute ago";
return dObj;
}
if (diff < 3600) {
dObj.friendly = Math.floor(diff / 60) + " minutes ago";
return dObj;
}
if (diff < (60 * 60) * 2) {
dObj.friendly = "1 hour ago";
return dObj;
}
if (diff < 24 * 60 * 60) {
dObj.friendly = Math.floor(diff / 3600) + " hours ago";
return dObj;
}
}
if (day_diff === 1) {
dObj.friendly = "yesterday";
return dObj;
}
if (day_diff < 7) {
dObj.friendly = day_diff + " days ago";
return dObj;
}
if (day_diff < 8) {
dObj.friendly = "last week";
return dObj;
}
/* for this scope: we want day of week and the date
plus the month (if different) */
if (day_diff < 31) {
dObj.friendly = Math.ceil(day_diff / 7) + " weeks ago";
return dObj;
}
/* for this scope: we want month + date */
if (day_diff < 62) {
dObj.friendly = "a month ago";
return dObj;
}
if (day_diff < 365) {
dObj.friendly = Math.ceil(day_diff / 31) + " months ago";
return dObj;
}
/* for this scope: we want month + year */
if (day_diff >= 365 && day_diff < 730) {
dObj.additional = date.toLocaleDateString();
dObj.friendly = "a year ago";
return dObj;
}
if (day_diff >= 365) {
dObj.additional = date.toLocaleDateString();
dObj.friendly = Math.ceil(day_diff / 365) + " years ago";
return dObj;
}
return dObj;
},
name: function (name) {
var firstName = name.split(' ')[0];
if (firstName.indexOf('@') !== -1) {
firstName = firstName.split('@')[0];
}
firstName = firstName.replace(" ", "");
firstName = firstName.replace("'", "");
firstName = firstName.replace('"', "");
return firstName;
}
};
return friendly;
});

Просмотреть файл

@ -0,0 +1,42 @@
'use strict';
/*jslint */
/*global require: false, location: true, window: false */
require.def('hashDispatch', function (object) {
/**
* Registers an object to receive hash changes. Expects the object
* to have property names that match the hash values.
* It will call the actions object immediately if the current hash
* matches a method on the object.
*
* @param {Object} actions the object that has property names that
* map to hash values and values for the properties are functions
* to be called. The name '_default' is used for any default action
* (one that corresponds to no hash value). A name of '_catchAll' is
* used to catch hash changes that do not map to a specific named
* action on the actions object.
*/
return function hashDispatch(actions) {
function hashUpdated() {
var hash = (location.href.split('#')[1] || '_default'),
arg, index;
//Only use the part of the hash before a colon to find the action
index = hash.indexOf(':');
if (index !== -1) {
arg = hash.substring(index + 1, hash.length);
hash = hash.substring(0, index);
}
if (hash in actions) {
actions[hash](arg);
} else if (actions._catchAll) {
actions._catchAll(hash, arg);
}
}
hashUpdated();
window.addEventListener('hashchange', hashUpdated, false);
};
});

1
web/scripts/iscroll-min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

149
web/scripts/isoDate.js Normal file
Просмотреть файл

@ -0,0 +1,149 @@
//Ported directly from dojo.date.stamp
'use strict';
/*jslint nomen: false, regexp: false, plusplus: false */
/*global require: false */
require('isoDate', function () {
// Methods to convert dates to or from a wire (string) format using well-known conventions
var _isoRegExp = /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+\-](\d{2}):(\d{2}))|Z)?)?$/,
isoDate = function (/*String*/formattedString, /*Number?*/defaultTime) {
// summary:
// Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
//
// description:
// Accepts a string formatted according to a profile of ISO8601 as defined by
// [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
// Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
// The following combinations are valid:
//
// * dates only
// | * yyyy
// | * yyyy-MM
// | * yyyy-MM-dd
// * times only, with an optional time zone appended
// | * THH:mm
// | * THH:mm:ss
// | * THH:mm:ss.SSS
// * and "datetimes" which could be any combination of the above
//
// timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
// Assumes the local time zone if not specified. Does not validate. Improperly formatted
// input may return null. Arguments which are out of bounds will be handled
// by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
// Only years between 100 and 9999 are supported.
//
// formattedString:
// A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
//
// defaultTime:
// Used for defaults for fields omitted in the formattedString.
// Uses 1970-01-01T00:00:00.0Z by default.
var match = _isoRegExp.exec(formattedString),
result = null, offset, zoneSign;
if (match) {
match.shift();
if (match[1]) {
match[1]--; // Javascript Date months are 0-based
}
if (match[6]) {
match[6] *= 1000; // Javascript Date expects fractional seconds as milliseconds
}
if (defaultTime) {
// mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
defaultTime = new Date(defaultTime);
["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"].map(function (prop) {
return defaultTime["get" + prop]();
}).forEach(function (value, index) {
match[index] = match[index] || value;
});
}
result = new Date(match[0] || 1970, match[1] || 0, match[2] || 1, match[3] || 0, match[4] || 0, match[5] || 0, match[6] || 0); //TODO: UTC defaults
if (match[0] < 100) {
result.setFullYear(match[0] || 1970);
}
offset = 0;
zoneSign = match[7] && match[7].charAt(0);
if (zoneSign !== 'Z') {
offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
if (zoneSign !== '-') {
offset *= -1;
}
}
if (zoneSign) {
offset -= result.getTimezoneOffset();
}
if (offset) {
result.setTime(result.getTime() + offset * 60000);
}
}
return result; // Date or null
};
/*=====
__Options = function(){
// selector: String
// "date" or "time" for partial formatting of the Date object.
// Both date and time will be formatted by default.
// zulu: Boolean
// if true, UTC/GMT is used for a timezone
// milliseconds: Boolean
// if true, output milliseconds
this.selector = selector;
this.zulu = zulu;
this.milliseconds = milliseconds;
}
=====*/
isoDate.toIsoString = function (/*Date*/dateObject, /*__Options?*/options) {
// summary:
// Format a Date object as a string according a subset of the ISO-8601 standard
//
// description:
// When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
// The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
// Does not check bounds. Only years between 100 and 9999 are supported.
//
// dateObject:
// A Date object
var _ = function (n) {
return (n < 10) ? "0" + n : n;
},
formattedDate, getter, date, year, time, millis, timezoneOffset, absOffset;
options = options || {};
formattedDate = [];
getter = options.zulu ? "getUTC" : "get";
date = "";
if (options.selector !== "time") {
year = dateObject[getter + "FullYear"]();
date = ["0000".substr((year + "").length) + year, _(dateObject[getter + "Month"]() + 1), _(dateObject[getter + "Date"]())].join('-');
}
formattedDate.push(date);
if (options.selector !== "date") {
time = [_(dateObject[getter + "Hours"]()), _(dateObject[getter + "Minutes"]()), _(dateObject[getter + "Seconds"]())].join(':');
millis = dateObject[getter + "Milliseconds"]();
if (options.milliseconds) {
time += "." + (millis < 100 ? "0" : "") + _(millis);
}
if (options.zulu) {
time += "Z";
} else if (options.selector !== "time") {
timezoneOffset = dateObject.getTimezoneOffset();
absOffset = Math.abs(timezoneOffset);
time += (timezoneOffset > 0 ? "-" : "+") +
_(Math.floor(absOffset / 60)) + ":" + _(absOffset % 60);
}
formattedDate.push(time);
}
return formattedDate.join('T'); // String
};
return isoDate;
});

Просмотреть файл

@ -0,0 +1,205 @@
/*
* jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
*
* Uses the built in easing capabilities added In jQuery 1.1
* to offer multiple easing options
*
* TERMS OF USE - jQuery Easing
*
* Open source under the BSD License.
*
* Copyright © 2008 George McGinley Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];
jQuery.extend( jQuery.easing,
{
def: 'easeOutQuad',
swing: function (x, t, b, c, d) {
//alert(jQuery.easing.default);
return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
},
easeInQuad: function (x, t, b, c, d) {
return c*(t/=d)*t + b;
},
easeOutQuad: function (x, t, b, c, d) {
return -c *(t/=d)*(t-2) + b;
},
easeInOutQuad: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInCubic: function (x, t, b, c, d) {
return c*(t/=d)*t*t + b;
},
easeOutCubic: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOutCubic: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
},
easeInQuart: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t + b;
},
easeOutQuart: function (x, t, b, c, d) {
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOutQuart: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
},
easeInQuint: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t*t + b;
},
easeOutQuint: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOutQuint: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
},
easeInSine: function (x, t, b, c, d) {
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
},
easeOutSine: function (x, t, b, c, d) {
return c * Math.sin(t/d * (Math.PI/2)) + b;
},
easeInOutSine: function (x, t, b, c, d) {
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
},
easeInExpo: function (x, t, b, c, d) {
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOutExpo: function (x, t, b, c, d) {
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOutExpo: function (x, t, b, c, d) {
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
},
easeInCirc: function (x, t, b, c, d) {
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOutCirc: function (x, t, b, c, d) {
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOutCirc: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
},
easeInElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
easeOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
},
easeInOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
},
easeInBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
easeInBounce: function (x, t, b, c, d) {
return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
},
easeOutBounce: function (x, t, b, c, d) {
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOutBounce: function (x, t, b, c, d) {
if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
}
});
/*
*
* TERMS OF USE - EASING EQUATIONS
*
* Open source under the BSD License.
*
* Copyright © 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

Просмотреть файл

@ -0,0 +1,308 @@
/*************************************************
** jQuery Masonry version 1.2.0
** copyright David DeSandro, licensed GPL & MIT
** http://desandro.com/resources/jquery-masonry
**************************************************/
;(function($){
/*!
* smartresize: debounced resize event for jQuery
* http://github.com/lrbabe/jquery-smartresize
*
* Copyright (c) 2009 Louis-Remi Babe
* Licensed under the GPL license.
* http://docs.jquery.com/License
*
*/
var event = $.event,
resizeTimeout;
event.special[ "smartresize" ] = {
setup: function() {
$( this ).bind( "resize", event.special.smartresize.handler );
},
teardown: function() {
$( this ).unbind( "resize", event.special.smartresize.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments;
// set correct event type
event.type = "smartresize";
if(resizeTimeout)
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
jQuery.event.handle.apply( context, args );
}, execAsap === "execAsap"? 0 : 100);
}
};
$.fn.smartresize = function( fn ) {
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
};
// masonry code begin
$.fn.masonry = function(options, callback) {
function getBricks(props, opts) {
props.$bricks = opts.itemSelector == undefined ?
opts.$brickParent.children() :
opts.$brickParent.find(opts.itemSelector);
}
function placeBrick($brick, setCount, setY, setSpan, props, opts) {
var shortCol = 0;
for ( i=0; i < setCount; i++ ) {
if ( setY[i] < setY[ shortCol ] ) shortCol = i;
}
var position = {
left: props.colW * shortCol + props.posLeft,
top: setY[ shortCol ]
};
if( props.masoned && opts.animate ) {
$brick.animate( position, {
duration: opts.animationOptions.duration,
easing: opts.animationOptions.easing,
complete: opts.animationOptions.complete,
step: opts.animationOptions.step,
queue: opts.animationOptions.queue,
specialEasing: opts.animationOptions.specialEasing
});
} else {
$brick.css(position);
}
for ( i=0; i < setSpan; i++ ) {
props.colY[ shortCol + i ] = setY[ shortCol ] + $brick.outerHeight(true) ;
}
};
function masonrySetup($wall, opts, props) {
getBricks(props, opts);
if ( opts.columnWidth == undefined) {
props.colW = props.masoned ?
$wall.data('masonry').colW :
props.$bricks.outerWidth(true);
} else {
props.colW = opts.columnWidth;
}
props.colCount = Math.floor( $wall.width() / props.colW ) ;
props.colCount = Math.max( props.colCount, 1 );
};
function masonryArrange($wall, opts, props) {
// if masonry hasn't been called before
if( !props.masoned ) $wall.css( 'position', 'relative' );
if ( !props.masoned || opts.appendedContent != undefined ) {
// just the new bricks
props.$bricks.css( 'position', 'absolute' );
}
// get top left position of where the bricks should be
var cursor = $('<div />');
$wall.prepend( cursor );
props.posTop = Math.round( cursor.position().top );
props.posLeft = Math.round( cursor.position().left );
cursor.remove();
// set up column Y array
if ( props.masoned && opts.appendedContent != undefined ) {
// if appendedContent is set, use colY from last call
props.colY = $wall.data('masonry').colY;
/*
* in the case that the wall is not resizeable,
* but the colCount has changed from the previous time
* masonry has been called
*/
for (i= $wall.data('masonry').colCount; i < props.colCount; i++) {
props.colY[i] = props.posTop;
};
} else {
props.colY = [];
for ( i=0; i < props.colCount; i++) {
props.colY[i] = props.posTop;
}
}
// layout logic
if ( opts.singleMode ) {
props.$bricks.each(function(){
var $brick = $(this);
placeBrick($brick, props.colCount, props.colY, 1, props, opts);
});
} else {
props.$bricks.each(function() {
var $brick = $(this);
//how many columns does this brick span
var colSpan = Math.ceil( $brick.outerWidth(true) / props.colW);
colSpan = Math.min( colSpan, props.colCount );
if ( colSpan == 1 ) {
// if brick spans only one column, just like singleMode
placeBrick($brick, props.colCount, props.colY, 1, props, opts);
} else {
// brick spans more than one column
//how many different places could this brick fit horizontally
var groupCount = props.colCount + 1 - colSpan;
var groupY = [0];
// for each group potential horizontal position
for ( i=0; i < groupCount; i++ ) {
groupY[i] = 0;
// for each column in that group
for ( j=0; j < colSpan; j++ ) {
// get the maximum column height in that group
groupY[i] = Math.max( groupY[i], props.colY[i+j] );
}
}
placeBrick($brick, groupCount, groupY, colSpan, props, opts);
}
}); // /props.bricks.each(function() {
} // /layout logic
// set the height of the wall to the tallest column
props.wallH = 0;
for ( i=0; i < props.colCount; i++ ) {
props.wallH = Math.max( props.wallH, props.colY[i] );
}
var wallCSS = { height: props.wallH - props.posTop };
// $wall.height( props.wallH - props.posTop );
if ( props.masoned && opts.animate ) {
$wall.animate(wallCSS, {
duration: opts.animationOptions.duration,
easing: opts.animationOptions.easing,
complete: opts.animationOptions.complete,
step: opts.animationOptions.step,
queue: opts.animationOptions.queue,
specialEasing: opts.animationOptions.specialEasing
});
} else {
$wall.css(wallCSS);
}
// add masoned class first time around
if ( !props.masoned ) $wall.addClass('masoned');
// provide props.bricks as context for the callback
callback.call( props.$bricks );
// set all data so we can retrieve it for appended appendedContent
// or anyone else's crazy jquery fun
$wall.data('masonry', props );
}; // /masonryArrange function
function masonryResize($wall, opts, props) {
props.masoned = $wall.data('masonry') != undefined;
var prevColCount = $wall.data('masonry').colCount;
masonrySetup($wall, opts, props);
if ( props.colCount != prevColCount ) masonryArrange($wall, opts, props);
};
/*
* let's begin
* IN A WORLD...
*/
return this.each(function() {
var $wall = $(this);
var props = $.extend( {}, $.masonry );
// checks if masonry has been called before on this object
props.masoned = $wall.data('masonry') != undefined;
var previousOptions = props.masoned ? $wall.data('masonry').options : {};
var opts = $.extend(
{},
props.defaults,
previousOptions,
options
);
// should we save these options for next time?
props.options = opts.saveOptions ? opts : previousOptions;
//picked up from Paul Irish
callback = callback || function(){};
if ( props.masoned && opts.appendedContent != undefined ) {
// if we're dealing with appendedContent
opts.$brickParent = opts.appendedContent;
} else {
opts.$brickParent = $wall;
}
getBricks(props, opts);
if ( props.$bricks.length ) {
// call masonry layout
masonrySetup($wall, opts, props);
masonryArrange($wall, opts, props);
// binding window resizing
var resizeOn = previousOptions.resizeable;
if ( !resizeOn && opts.resizeable ) {
$(window).bind('smartresize.masonry', function() { masonryResize($wall, opts, props); } );
}
if ( resizeOn && !opts.resizeable ) $(window).unbind('smartresize.masonry');
} else {
// brickParent is empty, do nothing, go back home and eat chips
return this;
}
}); // /return this.each(function()
}; // /$.fn.masonry = function(options)
$.masonry = {
defaults : {
singleMode: false,
columnWidth: undefined,
itemSelector: undefined,
appendedContent: undefined,
saveOptions: true,
resizeable: true,
animate: false,
animationOptions: {}
},
colW: undefined,
colCount: undefined,
colY: undefined,
wallH: undefined,
masoned: undefined,
posTop: 0,
posLeft: 0,
options: undefined,
$bricks: undefined,
$brickParent: undefined
};
})(jQuery);

486
web/scripts/jquery.tmpl.js Normal file
Просмотреть файл

@ -0,0 +1,486 @@
/*
* jQuery Templating Plugin
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
*/
(function( jQuery, undefined ){
var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$/,
newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
function newTmplItem( options, parentItem, fn, data ) {
// Returns a template item data structure for a new rendered instance of a template (a 'template item').
// The content field is a hierarchical array of strings and nested items (to be
// removed and replaced by nodes field of dom elements, once inserted in DOM).
var newItem = {
data: data || (parentItem ? parentItem.data : {}),
_wrap: parentItem ? parentItem._wrap : null,
tmpl: null,
parent: parentItem || null,
nodes: [],
calls: tiCalls,
nest: tiNest,
wrap: tiWrap,
html: tiHtml,
update: tiUpdate
};
if ( options ) {
jQuery.extend( newItem, options, { nodes: [], parent: parentItem } );
}
if ( fn ) {
// Build the hierarchical content to be used during insertion into DOM
newItem.tmpl = fn;
newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
newItem.key = ++itemKey;
// Keep track of new template item, until it is stored as jQuery Data on DOM element
(stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
}
return newItem;
}
// Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
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 ),
parent = this.length === 1 && this[0].parentNode;
appendToTmplItems = newTmplItems || {};
if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
insert[ original ]( this[0] );
ret = this;
} else {
for ( var i = 0, l = insert.length; i < l; i++ ) {
cloneIndex = i;
var elems = (i > 0 ? this.clone(true) : this).get();
jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
ret = ret.concat( elems );
}
cloneIndex = 0;
ret = this.pushStack( ret, name, insert.selector );
}
var tmplItems = appendToTmplItems;
appendToTmplItems = null;
jQuery.tmpl.complete( tmplItems );
return ret;
};
});
jQuery.fn.extend({
// Use first wrapped element as template markup.
// Return wrapped set of template items, obtained by rendering template against data.
tmpl: function( data, options, parentItem ) {
return jQuery.tmpl( this[0], data, options, parentItem );
},
// Find which rendered template item the first wrapped DOM element belongs to
tmplItem: function() {
return jQuery.tmplItem( this[0] );
},
// Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
templates: function( name ) {
return jQuery.templates( name, this[0] );
},
domManip: function( args, table, callback, options ) {
// This appears to be a bug in the appendTo, etc. implementation
// it should be doing .call() instead of .apply(). See #6227
if ( args[0].nodeType ) {
var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem;
while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {};
if ( argsLength > 1 ) {
dmArgs[0] = [jQuery.makeArray( args )];
}
if ( tmplItem && cloneIndex ) {
dmArgs[2] = function( fragClone ) {
// Handler called by oldManip when rendered template has been inserted into DOM.
jQuery.tmpl.afterManip( this, fragClone, callback );
}
}
oldManip.apply( this, dmArgs );
} else {
oldManip.apply( this, arguments );
}
cloneIndex = 0;
if ( !appendToTmplItems ) {
jQuery.tmpl.complete( newTmplItems );
}
return this;
}
});
jQuery.extend({
// Return wrapped set of template items, obtained by rendering template against data.
tmpl: function( tmpl, data, options, parentItem ) {
var ret, topLevel = !parentItem;
if ( topLevel ) {
// This is a top-level tmpl call (not from a nested template using {{tmpl}})
parentItem = topTmplItem;
tmpl = jQuery.templates[tmpl] || jQuery.templates( null, tmpl );
} else if ( !tmpl ) {
// The template item is already associated with DOM - this is a refresh.
// Re-evaluate rendered template for the parentItem
tmpl = parentItem.tmpl;
newTmplItems[parentItem.key] = parentItem;
parentItem.nodes = [];
updateWrapped( parentItem );
// Rebuild, without creating a new template item
return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
}
if ( !tmpl ) {
return []; // Could throw...
}
if ( typeof data === "function" ) {
data = data.call( parentItem || {} );
}
if ( options && options.wrapped ) {
// Create template item for wrapped content, without rendering template
parentItem = newTmplItem( options, parentItem, null, data );
parentItem.key = ++itemKey;
wrappedItems[itemKey] = parentItem;
parentItem.tmpl = tmpl;
updateWrapped( parentItem );
}
ret = jQuery.isArray( data ) ?
jQuery.map( data, function( dataItem ) {
return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
}) :
[ newTmplItem( options, parentItem, tmpl, data ) ];
return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
},
// Return rendered template item for an element.
tmplItem: function( elem ) {
var tmplItem;
if ( elem instanceof jQuery ) {
elem = tmpl[0];
}
while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
return tmplItem || topTmplItem;
},
// Set:
// Use $.templates( name, tmpl ) to cache a named template,
// where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
// Use $( "selector" ).templates( name ) to provide access by name to a script block template declaration.
// Get:
// Use $.templates( name ) to access a cached template.
// Also $( selectorToScriptBlock ).templates(), or $.templates( null, templateString )
// will return the compiled template, without adding a name reference.
// If templateString includes at least one HTML tag, $.templates( templateString ) is equivalent
// to $.templates( null, templateString )
templates: function( name, tmpl ) {
if (tmpl) {
// Compile template and associate with name
if ( typeof tmpl === "string" ) {
// This is an HTML string being passed directly in.
tmpl = buildTmplFn( tmpl )
} else if ( tmpl instanceof jQuery ) {
tmpl = tmpl[0] || {};
}
if ( tmpl.nodeType ) {
// If this is a template block, use cached copy, or generate tmpl function and cache.
tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
}
return typeof name === "string" ? (jQuery.templates[name] = tmpl) : tmpl;
}
// Return named compiled template
return typeof name !== "string" ? jQuery.templates( null, name ):
(jQuery.templates[name] ||
// If not in map, treat as a selector. (If integrated with core, use quickExpr.exec)
jQuery.templates( null, htmlExpr.test( name ) ? name : jQuery( name )));
},
encode: function( text ) {
// Do HTML encoding replacing < > & and ' and " by corresponding entities.
return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
}
});
jQuery.extend( jQuery.tmpl, {
tags: {
"tmpl": {
_default: { $2: "null" },
open: "if($notnull_1){_=_.concat($item.nest($1,$2));}"
// tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
// This means that {{tmpl foo}} treats foo as a template (which IS a function).
// Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
},
"wrap": {
_default: { $2: "null" },
open: "$item.calls(_,$1,$2);_=[];",
close: "call=$item.calls();_=call._.concat($item.wrap(call,_));"
},
"each": {
_default: { $2: "$index, $value" },
open: "if($notnull_1){$.each($1a,function($2){with(this){",
close: "}});}"
},
"if": {
open: "if(($notnull_1) && $1a){",
close: "}"
},
"else": {
open: "}else{"
},
"html": {
open: "if($notnull_1){_.push($1a);}"
},
"=": {
_default: { $1: "$data" },
open: "if($notnull_1){_.push($.encode($1a));}"
}
},
// This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
complete: function( items ) {
newTmplItems = {};
},
// Call this from code which overrides domManip, or equivalent
// Manage cloning/storing template items etc.
afterManip: function afterManip( elem, fragClone, callback ) {
// Provides cloned fragment ready for fixup prior to and after insertion into DOM
var content = fragClone.nodeType === 11 ?
jQuery.makeArray(fragClone.childNodes) :
fragClone.nodeType === 1 ? [fragClone] : [];
// Return fragment to original caller (e.g. append) for DOM insertion
callback.call( elem, fragClone );
// Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
storeTmplItems( content );
cloneIndex++;
}
});
//========================== Private helper functions, used by code above ==========================
function build( tmplItem, nested, content ) {
// Convert hierarchical content into flat string array
// and finally return array of fragments ready for DOM insertion
var frag, ret = jQuery.map( content, function( item ) {
return (typeof item === "string") ?
// Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) :
// This is a child template item. Build nested template.
build( item, tmplItem, item._ctnt );
});
if ( nested ) {
return ret;
}
// top-level template
ret = ret.join("");
// Support templates which have initial or final text nodes, or consist only of text
// Also support HTML entities within the HTML markup.
ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
frag = jQuery( middle ).get();
storeTmplItems( frag );
if ( before ) {
frag = unencode( before ).concat(frag);
}
if ( after ) {
frag = frag.concat(unencode( after ));
}
});
return frag ? frag : unencode( ret );
}
function unencode( text ) {
// Use createElement, since createTextNode will not render HTML entities correctly
var el = document.createElement( "div" );
el.innerHTML = text;
return jQuery.makeArray(el.childNodes);
}
// Generate a reusable function that will serve to render a template against data
function buildTmplFn( markup ) {
return new Function("jQuery","$item",
"var $=jQuery,_=[],$data=$item.data;" +
// Introduce the data as local variables using with(){}
"with($data){_.push('" +
// Convert the template into pure JavaScript
$.trim(markup)
.replace( /([\\'])/g, "\\$1" )
.replace( /[\r\t\n]/g, " " )
.replace( /\${([^}]*)}/g, "{{= $1}}" )
.replace( /{{(\/?)(\w+|.)(?:\(((?:.(?!}}))*?)?\))?(?:\s+(.*?)?)?(\((.*?)\))?\s*}}/g,
function( all, slash, type, fnargs, target, parens, args ) {
var cmd = jQuery.tmpl.tags[ type ], def, expr, exprAutoFnDetect;
if ( !cmd ) {
throw "Template command not found: " + type;
}
def = cmd._default || [];
if ( target ) {
target = unescape( target );
args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
if ( parens && target.indexOf(".") > -1 ) {
// Support for target being things like a.toLowerCase();
// In that case don't call with template item as 'this' pointer. Just evaluate...
target += parens;
args = "";
}
expr = args ? ("(" + target + ").call($item" + args) : target;
exprAutoFnDetect = args ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
} else {
expr = def["$1"] || "null";
}
fnargs = unescape( fnargs );
return "');" +
cmd[ slash ? "close" : "open" ]
.split( "$notnull_1" ).join( "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" )
.split( "$1a" ).join( exprAutoFnDetect )
.split( "$1" ).join( expr )
.split( "$2" ).join( fnargs ?
fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) {
params = params ? ("," + params + ")") : (parens ? ")" : "");
return params ? ("(" + name + ").call($item" + params) : all;
})
: (def["$2"]||"")
) +
"_.push('";
}) +
"');}return _;"
);
}
function updateWrapped( tmplItem ) {
if ( tmplItem.wrapped ) {
var wrapped = tmplItem.wrapped;
// Build the wrapped content
tmplItem._wrap = build( tmplItem, true,
jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
).join("");
}
}
function unescape( args ) {
return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
}
function outerHtml( elem ) {
var div = document.createElement("div");
div.appendChild( elem.cloneNode(true) );
return div.innerHTML;
}
// Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
function storeTmplItems( content ) {
var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {};
for ( var i = 0, l = content.length; i < l; i++ ) {
if ( (elem = content[i]).nodeType !== 1 ) {
continue;
}
elems = elem.getElementsByTagName("*");
for ( var m = elems.length - 1; m >= 0; m-- ) {
processItemKey( elems[m] );
}
processItemKey( elem );
}
// Cannot remove temporary wrappedItem objects, since needed during updating of nested items. //wrappedItems = {};
// TODO - ensure no memory leaks
function processItemKey( el ) {
var pntKey, pntNode = el, pntItem, tmplItem, key;
// Ensure that each rendered template inserted into the DOM has its own template item,
if ( key = el.getAttribute( tmplItmAtt )) {
while ((pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
if ( pntKey !== key ) {
// The next ancestor with a _tmplitem expando is on a different key than this one.
// So this is a top-level element within this template item
pntNode = pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0);
if ( !(tmplItem = newTmplItems[key]) ) {
// The item is for wrapped content, and was copied from the temporary parent wrappedItem.
tmplItem = wrappedItems[key];
tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true );
tmplItem.key = ++itemKey;
// Note that there is a remaining issue on parenting of wrappedItems.
// ...Currently there may be additional newTmplItems items wrapped contexts, leading to duplicate rendered events.
newTmplItems[itemKey] = tmplItem;
}
if ( cloneIndex ) {
cloneTmplItem( key );
}
}
el.removeAttribute( tmplItmAtt );
} else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
// This was a rendered element, cloned during append or appendTo etc.
// TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
cloneTmplItem( tmplItem.key );
newTmplItems[tmplItem.key] = tmplItem;
pntNode = jQuery.data( el.parentNode, "tmplItem" );
pntNode = pntNode ? pntNode.key : 0;
}
if ( tmplItem ) {
pntItem = tmplItem;
// Find the template item of the parent element
while ( pntItem && pntItem.key != pntNode ) {
// Add this element as a top-level node for this rendered template item, as well as for any
// ancestor items between this item and the item of its parent element
pntItem.nodes.push( el );
pntItem = pntItem.parent;
}
// Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
delete tmplItem._ctnt;
delete tmplItem._wrap;
// Store template item as jQuery data on the element
jQuery.data( el, "tmplItem", tmplItem );
}
function cloneTmplItem( key ) {
key = key + keySuffix;
tmplItem = newClonedItems[key]
= (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true ));
}
}
}
//---- Helper functions for template item ----
function tiCalls( content, tmpl, data, options ) {
if ( !content ) {
return stack.pop();
}
var l = stack.length;
stack.push({ _: content, tmpl: tmpl, parent: l ? stack[l - 1].item : this, item:this, data: data, options: options });
}
function tiNest( tmpl, data, options ) {
// nested template, using {{tmpl}} tag
return jQuery.tmpl( jQuery.templates( tmpl ), data, options, this );
}
function tiWrap( call, wrapped ) {
// nested template, using {{wrap}} tag
var options = call.options;
options.wrapped = wrapped;
// Apply the template, which may incorporate wrapped content,
return jQuery.tmpl( jQuery.templates( call.tmpl ), call.data, options, call.parent );
}
function tiHtml( filter, textOnly ) {
var wrapped = this._wrap;
return jQuery.map(
jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
function(e) {
return textOnly ?
e.innerText || e.textContent :
e.outerHTML || outerHtml(e);
});
}
function tiUpdate() {
var coll = this.nodes;
jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
jQuery( coll ).remove();
}
})(jQuery);

Просмотреть файл

@ -0,0 +1,334 @@
/**
* jQuery VGrid v0.1.5 - variable grid layout plugin
*
* Terms of Use - jQuery VGrid
* under the MIT (http://www.opensource.org/licenses/mit-license.php) License.
*
* Copyright 2009-2010 xlune.com All rights reserved.
* (http://blog.xlune.com/2009/09/jqueryvgrid.html)
*/
(function($)
{
function makePos(self)
{
var _childs = self.data("_vgchild");
var _width = self.width();
var _matrix = [[0,_width,0]];
var _hmax=0, _c, _size, _point;
_childs.each(function(i)
{
_c = $(this);
_size = getSize(_c);
_point = getAttachPoint(_matrix, _size[0]);
_matrix = updateAttachArea(_matrix, _point, _size);
_hmax = Math.max(_hmax, _point[1] + _size[1]);
_c.data("_vgleft", _point[0]);
_c.data("_vgtop", _point[1]);
});
self.data("_vgwrapheight", _hmax);
heightTo(self);
};
function getAttachPoint(mtx, width)
{
var _mtx = mtx.concat().sort(matrixSortDepth);
var _max = _mtx[_mtx.length-1][2];
for(var i=0,imax=_mtx.length; i<imax; i++)
{
if(_mtx[i][2] >= _max) break;
if(_mtx[i][1]-_mtx[i][0] >= width)
{
return [_mtx[i][0], _mtx[i][2]];
}
}
return [0, _max];
};
function updateAttachArea(mtx, point, size)
{
var _mtx = mtx.concat().sort(matrixSortDepth);
var _cell = [point[0], point[0]+size[0], point[1]+size[1]];
for(var i=0,imax=_mtx.length; i<imax; i++)
{
if(_cell[0] <= _mtx[i][0] && _mtx[i][1] <= _cell[1])
{
delete _mtx[i];
}
else
{
_mtx[i] = matrixTrimWidth(_mtx[i], _cell);
}
}
return matrixJoin(_mtx, _cell);
};
function matrixSortDepth(a, b)
{
if(!a || !b) return 0;
return ((a[2] == b[2] && a[0] > b[0]) || a[2] > b[2]) ? 1 : -1;
};
function matrixSortX(a, b)
{
if(!a || !b) return 0;
return (a[0] > b[0]) ? 1 : -1;
};
function matrixJoin(mtx, cell)
{
var _mtx = mtx.concat([cell]).sort(matrixSortX);
var _mtx_join = [];
for(var i=0,imax=_mtx.length; i<imax; i++)
{
if(!_mtx[i]) continue;
if(_mtx_join.length > 0
&& _mtx_join[_mtx_join.length-1][1] == _mtx[i][0]
&& _mtx_join[_mtx_join.length-1][2] == _mtx[i][2])
{
_mtx_join[_mtx_join.length-1][1] = _mtx[i][1];
}
else
{
_mtx_join.push(_mtx[i]);
}
}
return _mtx_join;
};
function matrixTrimWidth(a, b)
{
if(a[0] >= b[0] && a[0] < b[1] || a[1] >= b[0] && a[1] < b[1])
{
if(a[0] >= b[0] && a[0] < b[1])
{
a[0] = b[1];
}
else
{
a[1] = b[0];
}
}
return a;
};
function getSize(child)
{
var _w = child.width();
var _h = child.height();
_w += Number(child.css("margin-left").replace('px', ''))
+Number(child.css("padding-left").replace('px', ''))
+Number(child.get(0).style.borderLeftWidth.replace('px', ''))
+Number(child.css("margin-right").replace('px', ''))
+Number(child.css("padding-right").replace('px', ''))
+Number(child.get(0).style.borderRightWidth.replace('px', ''));
_h += Number(child.css("margin-top").replace('px', ''))
+Number(child.css("padding-top").replace('px', ''))
+Number(child.get(0).style.borderTopWidth.replace('px', ''))
+Number(child.css("margin-bottom").replace('px', ''))
+Number(child.css("padding-bottom").replace('px', ''))
+Number(child.get(0).style.borderBottomWidth.replace('px', ''));
return [_w, _h];
};
function heightTo(self)
{
var _self = self;
var _delay = _self.data("_vgchild").length
* (_self.data("_vgopt").delay || 0)
+ _self.data("_vgopt").time || 500;
_self.stop();
if(_self.height() < _self.data("_vgwrapheight"))
{
if($.browser.msie)
{
_self.height(_self.data("_vgwrapheight"));
}
else
{
_self.animate(
{
height: _self.data("_vgwrapheight")+"px"
},
(_self.data("_vgopt").time || 500),
"easeOutQuart"
);
}
}
else
{
clearTimeout(_self.data("_vgwraptimeout"));
_self.data("_vgwraptimeout", setTimeout(function(){
if($.browser.msie)
{
_self.height(_self.data("_vgwrapheight"));
}
else
{
_self.animate(
{
height: _self.data("_vgwrapheight")+"px"
},
(_self.data("_vgopt").time || 500),
"easeOutQuart"
);
}
}, _delay));
}
};
function moveTo(childs)
{
var _c;
childs.each(function(i)
{
_c = $(this);
_c.css("left", ~~_c.data("_vgleft")+"px");
_c.css("top", ~~_c.data("_vgtop")+"px");
});
};
function animateTo(childs, easing, time, delay)
{
var _self = $(childs).parent();
var isMove = false;
var imax = childs.length;
var i,_c,_pos;
for(i=0; i<imax; i++)
{
_c = $(childs[i]);
_pos = _c.position();
if(_pos.left != _c.data("_vgleft") && _pos.top != _c.data("_vgtop"))
{
isMove = true;
}
}
if(isMove)
{
if(typeof(_self.data("_vgopt").onStart) == "function") _self.data("_vgopt").onStart();
childs.each(function(i)
{
var _c = $(this);
var _opt = {
duration: time,
easing: easing
};
if(childs.size()-1 == i)
{
_opt.complete = _self.data("_vgopt").onFinish || null;
}
clearTimeout(_c.data("_vgtimeout"));
_c.data("_vgtimeout", setTimeout(function(){
_c.animate(
{
left: _c.data("_vgleft")+"px",
top: _c.data("_vgtop")+"px"
},
_opt
);
}, i*delay));
});
}
};
function refleshHandler(tg)
{
var _self = tg;
clearTimeout(_self.data("_vgtimeout"));
makePos(_self);
_self.data("_vgtimeout", setTimeout(function(){
animateTo(
_self.data("_vgchild"),
_self.data("_vgopt").easeing || "linear",
_self.data("_vgopt").time || 500,
_self.data("_vgopt").delay || 0
);
}, 500));
};
function setFontSizeListener()
{
var s = $("<span />")
.text(" ")
.attr("id", "_vgridspan")
.hide()
.appendTo("body");
s.data("size", s.css("font-size"));
s.data("timer", setInterval(function(){
if(s.css("font-size") != s.data("size"))
{
s.data("size", s.css("font-size"));
$(window).resize();
}
}, 1000));
};
$.fn.extend({
vgrid: function(option)
{
var _self = $(this);
var _opt = option || {};
_self.data("_vgopt", _opt);
_self.data("_vgchild", _self.find("> *"));
_self.data("_vgdefchild", _self.data("_vgchild"));
_self.css({
"position": "relative",
"width": "auto"
});
_self.data("_vgchild").css("position", "absolute");
makePos(_self);
moveTo(_self.data("_vgchild"));
if(_self.data("_vgopt").fadeIn)
{
var _prop = (typeof(_self.data("_vgopt").fadeIn)=='object')
? _self.data("_vgopt").fadeIn
: {time: _self.data("_vgopt").fadeIn} ;
_self.data("_vgchild").each(function(i)
{
var _c = $(this);
_c.css('display', 'none');
setTimeout(function(){
_c.fadeIn(_prop.time || 250);
}, i * (_prop.delay || 0));
});
}
$(window).resize(function(e)
{
refleshHandler(_self);
});
setFontSizeListener();
return _self;
},
vgrefresh: function(easeing, time, delay, func)
{
var _obj = $(this);
if(_obj.data("_vgchild"))
{
_obj.data("_vgchild", _obj.find("> *"));
_obj.data("_vgchild").css("position", "absolute");
makePos(_obj);
time = typeof(time)=="number" ? time : _obj.data("_vgopt").time || 500;
delay = typeof(delay)=="number" ? delay : _obj.data("_vgopt").delay || 0;
animateTo(
_obj.data("_vgchild"),
easeing || _obj.data("_vgopt").easeing || "linear",
time,
delay
);
if(typeof(func)=='function')
{
setTimeout(
func,
_obj.data("_vgchild").length * delay + time
);
}
}
return _obj;
},
vgsort: function(func, easeing, time, delay)
{
var _obj = $(this);
if(_obj.data("_vgchild"))
{
_obj.data("_vgchild", _obj.data("_vgchild").sort(func));
_obj.data("_vgchild").each(function(num){
$(this).appendTo(_obj);
});
makePos(_obj);
animateTo(
_obj.data("_vgchild"),
easeing || _obj.data("_vgopt").easeing || "linear",
typeof(time)=="number" ? time : _obj.data("_vgopt").time || 500,
typeof(delay)=="number" ? delay : _obj.data("_vgopt").delay || 0
);
}
return _obj;
}
});
})(jQuery);

7
web/scripts/json2.js Normal file
Просмотреть файл

@ -0,0 +1,7 @@
if(!this.JSON)this.JSON={};
(function(){function k(a){return a<10?"0"+a:a}function n(a){o.lastIndex=0;return o.test(a)?'"'+a.replace(o,function(c){var d=q[c];return typeof d==="string"?d:"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function l(a,c){var d,f,i=g,e,b=c[a];if(b&&typeof b==="object"&&typeof b.toJSON==="function")b=b.toJSON(a);if(typeof j==="function")b=j.call(c,a,b);switch(typeof b){case "string":return n(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);
case "object":if(!b)return"null";g+=m;e=[];if(Object.prototype.toString.apply(b)==="[object Array]"){f=b.length;for(a=0;a<f;a+=1)e[a]=l(a,b)||"null";c=e.length===0?"[]":g?"[\n"+g+e.join(",\n"+g)+"\n"+i+"]":"["+e.join(",")+"]";g=i;return c}if(j&&typeof j==="object"){f=j.length;for(a=0;a<f;a+=1){d=j[a];if(typeof d==="string")if(c=l(d,b))e.push(n(d)+(g?": ":":")+c)}}else for(d in b)if(Object.hasOwnProperty.call(b,d))if(c=l(d,b))e.push(n(d)+(g?": ":":")+c);c=e.length===0?"{}":g?"{\n"+g+e.join(",\n"+g)+
"\n"+i+"}":"{"+e.join(",")+"}";g=i;return c}}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+k(this.getUTCMonth()+1)+"-"+k(this.getUTCDate())+"T"+k(this.getUTCHours())+":"+k(this.getUTCMinutes())+":"+k(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var p=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,g,m,q={"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},j;if(typeof JSON.stringify!=="function")JSON.stringify=function(a,c,d){var f;m=g="";if(typeof d==="number")for(f=0;f<d;f+=1)m+=" ";else if(typeof d==="string")m=d;if((j=c)&&typeof c!=="function"&&(typeof c!=="object"||typeof c.length!=="number"))throw new Error("JSON.stringify");return l("",
{"":a})};if(typeof JSON.parse!=="function")JSON.parse=function(a,c){function d(f,i){var e,b,h=f[i];if(h&&typeof h==="object")for(e in h)if(Object.hasOwnProperty.call(h,e)){b=d(h,e);if(b!==undefined)h[e]=b;else delete h[e]}return c.call(f,i,h)}a=String(a);p.lastIndex=0;if(p.test(a))a=a.replace(p,function(f){return"\\u"+("0000"+f.charCodeAt(0).toString(16)).slice(-4)});if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){a=eval("("+a+")");return typeof c==="function"?d({"":a},""):a}throw new SyntaxError("JSON.parse");}})();

381
web/scripts/md5.js Normal file
Просмотреть файл

@ -0,0 +1,381 @@
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
const EXPORTED_SYMBOLS = ["hex_md5"];
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
/*
* Perform a simple self-test to see if the VM is working
*/
function md5_vm_test()
{
return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s)
{
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data)
{
var bkey = rstr2binl(key);
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input)
{
try { hexcase } catch(e) { hexcase=0; }
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var output = "";
var x;
for(var i = 0; i < input.length; i++)
{
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F)
+ hex_tab.charAt( x & 0x0F);
}
return output;
}
/*
* Convert a raw string to a base-64 string
*/
function rstr2b64(input)
{
try { b64pad } catch(e) { b64pad=''; }
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var output = "";
var len = input.length;
for(var i = 0; i < len; i += 3)
{
var triplet = (input.charCodeAt(i) << 16)
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
}
}
return output;
}
/*
* Convert a raw string to an arbitrary string encoding
*/
function rstr2any(input, encoding)
{
var divisor = encoding.length;
var i, j, q, x, quotient;
/* Convert to an array of 16-bit big-endian values, forming the dividend */
var dividend = Array(Math.ceil(input.length / 2));
for(i = 0; i < dividend.length; i++)
{
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
}
/*
* Repeatedly perform a long division. The binary array forms the dividend,
* the length of the encoding is the divisor. Once computed, the quotient
* forms the dividend for the next step. All remainders are stored for later
* use.
*/
var full_length = Math.ceil(input.length * 8 /
(Math.log(encoding.length) / Math.log(2)));
var remainders = Array(full_length);
for(j = 0; j < full_length; j++)
{
quotient = Array();
x = 0;
for(i = 0; i < dividend.length; i++)
{
x = (x << 16) + dividend[i];
q = Math.floor(x / divisor);
x -= q * divisor;
if(quotient.length > 0 || q > 0)
quotient[quotient.length] = q;
}
remainders[j] = x;
dividend = quotient;
}
/* Convert the remainders to the output string */
var output = "";
for(i = remainders.length - 1; i >= 0; i--)
output += encoding.charAt(remainders[i]);
return output;
}
/*
* Encode a string as utf-8.
* For efficiency, this assumes the input is valid utf-16.
*/
function str2rstr_utf8(input)
{
var output = "";
var i = -1;
var x, y;
while(++i < input.length)
{
/* Decode utf-16 surrogate pairs */
x = input.charCodeAt(i);
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
{
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
i++;
}
/* Encode output as utf-8 */
if(x <= 0x7F)
output += String.fromCharCode(x);
else if(x <= 0x7FF)
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F));
else if(x <= 0xFFFF)
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
else if(x <= 0x1FFFFF)
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
}
return output;
}
/*
* Encode a string as utf-16
*/
function str2rstr_utf16le(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
(input.charCodeAt(i) >>> 8) & 0xFF);
return output;
}
function str2rstr_utf16be(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
input.charCodeAt(i) & 0xFF);
return output;
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input)
{
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
return output;
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input)
{
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
return output;
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}

104
web/scripts/placeholder.js Normal file
Просмотреть файл

@ -0,0 +1,104 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Raindrop.
*
* The Initial Developer of the Original Code is
* Mozilla Messaging, Inc..
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* */
/*jslint nomen: false, plusplus: false */
/*global require: false, clearTimeout: false, setTimeout: false */
"use strict";
require.def("placeholder", ["jquery"], function ($) {
/**
* Set the input value to use placeholder value if HTML5 placeholder
* attribute is not supported.
* @param {DOMNode} input an input element.
*/
function setPlaceholder(input) {
//If no native support for placeholder then JS to the rescue!
var missingNative = !("placeholder" in input),
placeholder = input.getAttribute("placeholder"),
trimmed = input.value.trim();
if (!trimmed || trimmed === placeholder) {
$(input).addClass("placeholder");
if (missingNative) {
input.value = placeholder;
if (placeholder === "password" && input.type === "password") {
input.type = "text";
}
}
} else {
$(input).removeClass("placeholder");
}
}
/**
* Handles focus events on the node to see if placehoder needs to be removed.
* @param {Event} evt
*/
function onfocus(evt) {
//Clear out placeholder, change the style.
var input = evt.target,
placeholder = input.getAttribute("placeholder");
if (input.value === placeholder) {
if (!("placeholder" in input)) {
input.value = "";
if (placeholder === "password" && input.type === "text") {
input.type = "password";
}
}
$(input).removeClass("placeholder");
}
}
/** Handles blur events on the node to see if placeholder needs to be reinstated.
* @param {Event} evt
*/
function onblur(evt) {
//Reset placeholder text if necessary.
setPlaceholder(evt.target);
}
/**
* Scans domNode and its children for text input/textarea elements that have a placeholder
* attribute, and attach placeholder behavior to it.
* Allow for the existence of browsers that already have placeholder support
* built in.
*
* @param {DOMNode} domNode
* @param {Widget} [widget] an optional widget that will track the connect handles.
*
*/
return function (domNode, widget) {
$('input[type="text"], input[type="password"], textarea', domNode).each(function (i, node) {
//Skip nodes that have already been bound
if (node.getAttribute("data-rdwPlaceholder") !== "set") {
$(node).focus(onfocus).blur(onblur);
node.setAttribute("data-rdwPlaceholder", "set");
}
//Set up initial state.
setPlaceholder(node);
});
};
});

221
web/scripts/rdapi.js Normal file
Просмотреть файл

@ -0,0 +1,221 @@
/*jslint plusplus: false, nomen: false*/
/*global require: false, document: false, hex_md5: false, localStorage: false, console: false */
"use strict";
require.def('rdapi',
['require', 'jquery', 'blade/object', 'blade/jig', 'friendly', 'isoDate', 'md5'],
function (require, $, object, jig, friendly, isoDate) {
var rdapi,
userTokenHeader = 'X-Raindrop-Token',
contacts = {},
jigFunctions = {
contact: function (identity) {
return identity.iid && identity.domain ? contacts[identity.iid] || {} : identity;
},
contactPhotoUrl: function (contact) {
var url = 'i/face2.png', photos, mailaddr;
contact = jigFunctions.contact(contact);
photos = contact.photos;
if (photos && photos.length) {
url = photos[0].value;
photos.forEach(function (photo) {
if (photo.primary) {
url = photo.value;
}
});
} else
if (contact.emails && contact.emails.length) {
// gravatar as a default
mailaddr = contact.emails[0].value;
contact.emails.forEach(function (email) {
if (email.primary) {
mailaddr = email.value;
}
});
url = 'http://www.gravatar.com/avatar/' + hex_md5(mailaddr) + '?s=24 &d=identicon';
}
return url;
},
allMessages: function (conversation) {
return [conversation.topic].concat(conversation.messages || []);
},
friendlyDate: function (isoString) {
return friendly.date(isoDate(isoString)).friendly;
},
htmlBody: function (text) {
return jig.htmlEscape(text).replace(/\n/g, '<br>');
}
},
config = {
baseUrl: '/',
apiPath: 'api/',
saveTemplateData: true
};
//Register functions with jig
jig.addFn(jigFunctions);
function normalize(options) {
if (typeof options === 'string') {
options = {
template: options
};
} else if (options.templateId) {
options.template = jig.cache(options.templateId);
}
if (!('attachData' in options)) {
options.attachData = rdapi.attachTemplateData;
}
if (options.emptyTemplateId) {
options.emptyTemplate = jig.cache(options.emptyTemplateId);
}
return options;
}
function ajax(url, options) {
options.url = config.baseUrl + config.apiPath + url;
object.mixin(options, {
limit: 30,
message_limit: 3,
dataType: 'json',
error: function (xhr, textStatus, errorThrown) {
console.log(errorThrown);
}
});
var oldSuccess = options.success,
userToken = localStorage[userTokenHeader];
//Intercept any success calls to get a hold of contacts from
//any API call that returns them. Also be sure to remember any
//user token
options.success = function (json, textStatus, xhr) {
//If user token passed back, hold on to it.
var headerToken = xhr.getResponseHeader(userTokenHeader) || '';
if (headerToken)
localStorage[userTokenHeader] = headerToken;
if (json.contacts) {
object.mixin(contacts, json.contacts, true);
}
if (oldSuccess) {
return oldSuccess.apply(null, arguments);
} else {
return json;
}
};
if (userToken) {
options.beforeSend = function (xhr) {
xhr.setRequestHeader(userTokenHeader, userToken);
};
}
$.ajax(options);
}
function finishApiTemplating(html, options) {
var parentNode = options.forId ?
document.getElementById(options.forId) : null;
if (parentNode) {
parentNode.innerHTML = html;
}
if (options.onTemplateDone) {
options.onTemplateDone(html);
}
$(document).trigger('rdapi-done', parentNode);
}
rdapi = function (url, options) {
options = normalize(options);
object.mixin(options, {
success: function (json) {
var template = options.template,
emptyTemplate = options.emptyTemplate,
html = '';
if (options.forId && template) {
if (options.prop) {
json = jig.getObject(options.prop, json, options);
}
if (require.isArray(json)) {
if (!json.length) {
html += jig(emptyTemplate, json, options);
} else {
json.forEach(function (item) {
html += jig(template, item, options);
});
}
} else {
html += jig(!json ? emptyTemplate : template, json, options);
}
finishApiTemplating(html, options);
}
},
error: function (xhr, textStatus, errorThrown) {
if (options.emptyTemplate) {
var html = jig(options.emptyTemplate, errorThrown, options);
finishApiTemplating(html, options);
} else {
throw errorThrown;
}
}
});
ajax(url, options);
};
rdapi.contactPhotoUrl = jigFunctions.contactPhotoUrl;
rdapi.attachTemplateData = false;
require.ready(function () {
var apiOptions = [];
//Build up lists of templates to use.
jig.parse({
//rdapi adds some additional semantics to some nodes,
//to allow automatic API calls, so pull off those attributes
//to use later for each template parsed.
onBeforeParse: function (node) {
var id = node.id,
api = node.getAttribute('data-rdapi'),
forId = node.getAttribute('data-rdfor'),
prop = node.getAttribute('data-rdprop');
if (api) {
apiOptions.push({
templateId: id,
api: api,
forId: forId,
prop: prop
});
}
//Remove the data attributes specific to rdapi
['data-rdapi', 'data-rdprop', 'data-rdfor'].forEach(function (attr) {
node.removeAttribute(attr);
});
}
});
//Finally, do all the API calls. This is a separate loop because
//all templates need to be strings before the api calls execute in
//case subtemplates are needed.
apiOptions.forEach(function (apiOption) {
rdapi(apiOption.api, apiOption);
});
});
return rdapi;
});

Просмотреть файл

@ -0,0 +1,204 @@
/*
RequireJS Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
Available via the MIT, GPL or new BSD license.
see: http://github.com/jrburke/requirejs for details
RequireJS i18n Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
Available via the MIT, GPL or new BSD license.
see: http://github.com/jrburke/requirejs for details
RequireJS text Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
Available via the MIT, GPL or new BSD license.
see: http://github.com/jrburke/requirejs for details
RequireJS jsonp Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
Available via the MIT, GPL or new BSD license.
see: http://github.com/jrburke/requirejs for details
RequireJS order Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
Available via the MIT, GPL or new BSD license.
see: http://github.com/jrburke/requirejs for details
jQuery JavaScript Library v1.4.2
http://jquery.com/
Copyright 2010, John Resig
Dual licensed under the MIT or GPL Version 2 licenses.
http://jquery.org/license
Includes Sizzle.js
http://sizzlejs.com/
Copyright 2010, The Dojo Foundation
Released under the MIT, BSD, and GPL Licenses.
Date: Sat Feb 13 22:33:48 2010 -0500
*/
var require;
(function(){function J(l){return ka.call(l)==="[object Function]"}function y(l,o,v){var z=k.plugins.defined[l];if(z)z[v.name].apply(null,v.args);else{z=k.plugins.waiting[l]||(k.plugins.waiting[l]=[]);z.push(v);o.defined.require(["require/"+l])}}function Y(l,o){var v=k.plugins.callbacks[l]=[];k.plugins[l]=function(){for(var z=0,A;A=v[z];z++)if(A.apply(null,arguments)===true&&o)return true;return false}}var B={},k,s,H=[],D,E,R,P,S,Q,U,N=/^(complete|loaded)$/,Z=!!(typeof window!=="undefined"&&navigator&&
document),c=!Z&&typeof importScripts!=="undefined",ka=Object.prototype.toString,da,w,fa;if(typeof require!=="undefined")if(J(require))return;else Q=require;w=require=function(l,o){if(typeof l==="string"&&!J(o))return require.get(l,o);return require.def.apply(require,arguments)};require.def=function(l,o,v,z){var A=null,C,W,O,X;if(typeof l==="string"){C=l.indexOf("!");if(C!==-1){O=l.substring(0,C);l=l.substring(C+1,l.length)}if(!require.isArray(o)){z=v;v=o;o=[]}z=z||k.ctxName;if((C=k.contexts[z])&&
(C.defined[l]||C.waiting[l]))return require}else if(require.isArray(l)){z=v;v=o;o=l;l=null}else if(require.isFunction(l)){v=l;z=o;l=null;o=[]}else{A=l;l=null;if(require.isFunction(o)){z=v;v=o;o=[]}z=z||A.context}z=z||k.ctxName;C=k.contexts[z];if(!C){C={contextName:z,config:{waitSeconds:7,baseUrl:k.baseUrl||"./",paths:{}},waiting:[],specified:{require:true,exports:true,module:true},loaded:{require:true},urlFetched:{},defined:{},modifiers:{}};C.defined.require=require;k.plugins.newContext&&k.plugins.newContext(C);
C=k.contexts[z]=C}if(A){if(A.baseUrl)if(A.baseUrl.charAt(A.baseUrl.length-1)!=="/")A.baseUrl+="/";X=C.config.paths;require.mixin(C.config,A,true);if(A.paths){for(W in A.paths)W in B||(X[W]=A.paths[W]);C.config.paths=X}if(A.priority){w(A.priority);C.config.priorityWait=A.priority}if(A.deps||A.callback)w(A.deps||[],A.callback);A.ready&&require.ready(A.ready);if(!o)return require}if(o){A=o;o=[];for(W=0;W<A.length;W++)o[W]=require.splitPrefix(A[W],l)}A=C.waiting.push({name:l,deps:o,callback:v});if(l){C.waiting[l]=
A-1;C.specified[l]=true}if(l&&v&&!require.isFunction(v))C.defined[l]=v;O&&y(O,C,{name:"require",args:[l,o,v,C]});if(k.paused||C.config.priorityWait)(k.paused||(k.paused=[])).push([O,l,o,C]);else{require.checkDeps(O,l,o,C);require.checkLoaded(z)}if(l)C.loaded[l]=true;return require};require.mixin=function(l,o,v){for(var z in o)if(!(z in B)&&(!(z in l)||v))l[z]=o[z];return require};require.version="0.12.0+";k=require.s={ctxName:"_",contexts:{},plugins:{defined:{},callbacks:{},waiting:{}},skipAsync:{},
isBrowser:Z,isPageLoaded:!Z,readyCalls:[],doc:Z?document:null};require.isBrowser=k.isBrowser;if(Z){k.head=document.getElementsByTagName("head")[0];if(fa=document.getElementsByTagName("base")[0])k.head=fa.parentNode}require.plugin=function(l){var o,v,z,A=l.prefix,C=k.plugins.callbacks,W=k.plugins.waiting[A],O;o=k.plugins.defined;z=k.contexts;if(o[A])return require;o[A]=l;O=["newContext","isWaiting","orderDeps"];for(o=0;v=O[o];o++){k.plugins[v]||Y(v,v==="isWaiting");C[v].push(l[v])}if(l.newContext)for(v in z)if(!(v in
B)){o=z[v];l.newContext(o)}if(W){for(o=0;z=W[o];o++)l[z.name]&&l[z.name].apply(null,z.args);delete k.plugins.waiting[A]}return require};require.pause=function(){if(!k.paused)k.paused=[]};require.resume=function(){var l,o,v;if(!k.contexts[k.ctxName].config.priorityWait){if(k.paused){v=k.paused;delete k.paused;for(l=0;o=v[l];l++)require.checkDeps.apply(require,o)}require.checkLoaded(k.ctxName)}};require.checkDeps=function(l,o,v,z){if(l)y(l,z,{name:"checkDeps",args:[o,v,z]});else for(l=0;o=v[l];l++)if(!z.specified[o.fullName]){z.specified[o.fullName]=
true;z.startTime=(new Date).getTime();o.prefix?y(o.prefix,z,{name:"load",args:[o.name,z.contextName]}):require.load(o.name,z.contextName)}};require.isArray=function(l){return ka.call(l)==="[object Array]"};require.isFunction=J;require.get=function(l,o){if(l==="exports"||l==="module")throw new Error("require of "+l+" is not allowed.");o=o||k.ctxName;var v=k.contexts[o].defined[l];if(v===undefined)throw new Error("require: module name '"+l+"' has not been loaded yet for context: "+o);return v};require.load=
function(l,o){var v=k.contexts[o],z=v.urlFetched;v=v.loaded;k.isDone=false;v[l]||(v[l]=false);v=require.nameToUrl(l,null,o);if(!z[v]){require.attach(v,o,l);z[v]=true}};require.jsExtRegExp=/\.js$/;require.normalizeName=function(l,o){if(l.charAt(0)==="."){o=o.split("/");o=o.slice(0,o.length-1);l=o.concat(l.split("/"));for(s=0;o=l[s];s++)if(o==="."){l.splice(s,1);s-=1}else if(o===".."){l.splice(s-1,2);s-=2}l=l.join("/")}return l};require.splitPrefix=function(l,o){var v=l.indexOf("!"),z=null;if(v!==-1){z=
l.substring(0,v);l=l.substring(v+1,l.length)}if(o)l=require.normalizeName(l,o);return{prefix:z,name:l,fullName:z?z+"!"+l:l}};require.nameToUrl=function(l,o,v){var z,A,C;v=k.contexts[v].config;if(l.indexOf(":")!==-1||l.charAt(0)==="/"||require.jsExtRegExp.test(l))return l;else if(l.charAt(0)===".")throw new Error("require.nameToUrl does not handle relative module names (ones that start with '.' or '..')");else{z=v.paths;l=l.split("/");for(A=l.length;A>0;A--){C=l.slice(0,A).join("/");if(z[C]){l.splice(0,
A,z[C]);break}}o=l.join("/")+(o||".js");return(o.charAt(0)==="/"||o.match(/^\w+:/)?"":v.baseUrl)+o}};require.checkLoaded=function(l){var o=k.contexts[l||k.ctxName],v=o.config.waitSeconds*1E3,z=v&&o.startTime+v<(new Date).getTime(),A,C;v="";var W=false,O=false,X,$,ea=k.plugins.isWaiting,ga=k.plugins.orderDeps,na={};if(!o.isCheckLoaded){if(o.config.priorityWait){C=true;for(A=0;$=o.config.priorityWait[A];A++)if(!o.loaded[$]){C=false;break}if(C){delete o.config.priorityWait;require.resume()}else return}o.isCheckLoaded=
true;C=o.waiting;A=o.loaded;for(X in A)if(!(X in B)){W=true;if(!A[X])if(z)v+=X+" ";else{O=true;break}}if(!W&&!C.length&&(!ea||!ea(o)))o.isCheckLoaded=false;else{if(z&&v){o=new Error("require.js load timeout for modules: "+v);o.requireType="timeout";o.requireModules=v;throw o;}if(O){o.isCheckLoaded=false;if(Z||c)setTimeout(function(){require.checkLoaded(l)},50)}else{o.waiting=[];o.loaded={};ga&&ga(o);for(A=0;v=C[A];A++)require.exec(v,na,C,o);o.isCheckLoaded=false;if(o.waiting.length||ea&&ea(o))require.checkLoaded(l);
else if(!H.length){k.ctxName="_";k.isDone=true;require.callReady&&require.callReady()}}}}};require.exec=function(l,o,v,z){if(l){var A=l.name,C=l.callback;C=l.deps;var W,O,X=z.defined,$,ea=[],ga=false;if(A){if(o[A]||A in X)return X[A];o[A]=true}if(C)for(W=0;O=C[W];W++){O=O.name;if(O==="exports"){O=X[A]={};ga=true}else O=O==="module"?{id:A,uri:A?require.nameToUrl(A,null,z.contextName):undefined}:O in X?X[O]:o[O]?undefined:require.exec(v[v[O]],o,v,z);ea.push(O)}if((C=l.callback)&&require.isFunction(C)){$=
require.execCb(A,C,ea);if(A)if(ga)$=X[A];else if(A in X)throw new Error(A+" has already been defined");else X[A]=$}return $}};require.execCb=function(l,o,v){return o.apply(null,v)};require.onScriptLoad=function(l){var o=l.currentTarget||l.srcElement,v;if(l.type==="load"||N.test(o.readyState)){l=o.getAttribute("data-requirecontext");v=o.getAttribute("data-requiremodule");k.contexts[l].loaded[v]=true;require.checkLoaded(l);o.removeEventListener?o.removeEventListener("load",require.onScriptLoad,false):
o.detachEvent("onreadystatechange",require.onScriptLoad)}};require.attach=function(l,o,v,z,A){var C;if(Z){z=z||require.onScriptLoad;C=document.createElement("script");C.type=A||"text/javascript";C.charset="utf-8";k.skipAsync[l]||C.setAttribute("async","async");C.setAttribute("data-requirecontext",o);C.setAttribute("data-requiremodule",v);C.addEventListener?C.addEventListener("load",z,false):C.attachEvent("onreadystatechange",z);C.src=l;return fa?k.head.insertBefore(C,fa):k.head.appendChild(C)}else if(c){o=
k.contexts[o].loaded;o[v]=false;importScripts(l);o[v]=true}return null};k.baseUrl=Q&&Q.baseUrl;if(Z&&(!k.baseUrl||!k.head)){D=document.getElementsByTagName("script");R=Q&&Q.baseUrlMatch?Q.baseUrlMatch:/(requireplugins-|require-)?jquery[\-\d\.]*(min)?\.js(\W|$)/i;for(s=D.length-1;s>-1&&(E=D[s]);s--){if(!k.head)k.head=E.parentNode;if(P=E.src)if(S=P.match(R)){k.baseUrl=P.substring(0,S.index);break}}}require.pageLoaded=function(){if(!k.isPageLoaded){k.isPageLoaded=true;da&&clearInterval(da);if(U)document.readyState=
"complete";require.callReady()}};require.callReady=function(){var l=k.readyCalls,o,v;if(k.isPageLoaded&&k.isDone&&l.length){k.readyCalls=[];for(o=0;v=l[o];o++)v()}};require.ready=function(l){k.isPageLoaded&&k.isDone?l():k.readyCalls.push(l);return require};if(Z){if(document.addEventListener){document.addEventListener("DOMContentLoaded",require.pageLoaded,false);window.addEventListener("load",require.pageLoaded,false);if(!document.readyState){U=true;document.readyState="loading"}}else if(window.attachEvent){window.attachEvent("onload",
require.pageLoaded);if(self===self.top)da=setInterval(function(){try{if(document.body){document.documentElement.doScroll("left");require.pageLoaded()}}catch(l){}},30)}document.readyState==="complete"&&require.pageLoaded()}Q&&w(Q)})();
(function(){function J(k,s){s=s.nlsWaiting;return s[k]||(s[k]=s[s.push({_name:k})-1])}function y(k,s,H,D){var E,R,P,S,Q,U,N="root";R=H.split("-");P=[];S=J(k,D);for(E=R.length;E>-1;E--){Q=E?R.slice(0,E).join("-"):"root";if(U=s[Q]){if(H===D.config.locale&&!S._match)S._match=Q;if(N==="root")N=Q;S[Q]=Q;if(U===true){U=k.split("/");U.splice(-1,0,Q);U=U.join("/");!D.specified[U]&&!(U in D.loaded)&&!D.defined[U]&&P.push(U)}}}if(N!==H)if(D.defined[N])D.defined[H]=D.defined[N];else S[H]=N;P.length&&D.defined.require(P)}
var Y=/(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/,B={};require.plugin({prefix:"i18n",require:function(k,s,H,D){var E,R=D.defined[k];E=Y.exec(k);if(E[5]){k=E[1]+E[5];s=J(k,D);s[E[4]]=E[4];s=D.nls[k];if(!s){D.defined.require([k]);s=D.nls[k]={}}s[E[4]]=H}else{if(s=D.nls[k])require.mixin(s,R);else s=D.nls[k]=R;D.nlsRootLoaded[k]=true;if(E=D.nlsToLoad[k]){delete D.nlsToLoad[k];for(H=0;H<E.length;H++)y(k,s,E[H],D)}y(k,s,D.config.locale,D)}},newContext:function(k){require.mixin(k,{nlsWaiting:[],nls:{},nlsRootLoaded:{},
nlsToLoad:{}});if(!k.config.locale)k.config.locale=typeof navigator==="undefined"?"root":(navigator.language||navigator.userLanguage||"root").toLowerCase()},load:function(k,s){var H=require.s.contexts[s],D=Y.exec(k),E=D[4];if(D[5]){k=D[1]+D[5];s=H.nls[k];if(H.nlsRootLoaded[k]&&s)y(k,s,E,H);else{(H.nlsToLoad[k]||(H.nlsToLoad[k]=[])).push(E);H.defined.require([k])}}else H.nlsRootLoaded[k]||require.load(k,s)},checkDeps:function(){},isWaiting:function(k){return!!k.nlsWaiting.length},orderDeps:function(k){var s,
H,D,E,R,P,S,Q,U,N,Z,c,ka=k.nlsWaiting,da;k.nlsWaiting=[];k.nlsToLoad={};for(s=0;E=ka[s];s++){D=E._name;R=k.nls[D];Z=null;P=D.split("/");U=P.slice(0,P.length-1).join("/");S=P[P.length-1];for(N in E)if(N!=="_name"&&!(N in B))if(N==="_match")Z=E[N];else if(E[N]!==N)(da||(da={}))[N]=E[N];else{Q={};P=N.split("-");for(H=P.length;H>0;H--){c=P.slice(0,H).join("-");c!=="root"&&R[c]&&require.mixin(Q,R[c])}R.root&&require.mixin(Q,R.root);k.defined[U+"/"+N+"/"+S]=Q}k.defined[D]=k.defined[U+"/"+Z+"/"+S];if(da)for(N in da)N in
B||(k.defined[U+"/"+N+"/"+S]=k.defined[U+"/"+da[N]+"/"+S])}}})})();
(function(){var J=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"],y=/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,Y=/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im;if(!require.textStrip)require.textStrip=function(B){if(B){B=B.replace(y,"");var k=B.match(Y);if(k)B=k[1]}else B="";return B};if(!require.getXhr)require.getXhr=function(){var B,k,s;if(typeof XMLHttpRequest!=="undefined")return new XMLHttpRequest;else for(k=0;k<3;k++){s=J[k];try{B=new ActiveXObject(s)}catch(H){}if(B){J=[s];
break}}if(!B)throw new Error("require.getXhr(): XMLHttpRequest not available");return B};if(!require.fetchText)require.fetchText=function(B,k){var s=require.getXhr();s.open("GET",B,true);s.onreadystatechange=function(){s.readyState===4&&k(s.responseText)};s.send(null)};require.plugin({prefix:"text",require:function(){},newContext:function(B){require.mixin(B,{text:{},textWaiting:[]})},load:function(B,k){var s=false,H=null,D,E=B.indexOf("."),R=B.substring(0,E),P=B.substring(E+1,B.length),S=require.s.contexts[k],
Q=S.textWaiting;E=P.indexOf("!");if(E!==-1){s=P.substring(E+1,P.length);P=P.substring(0,E);E=s.indexOf("!");if(E!==-1&&s.substring(0,E)==="strip"){H=s.substring(E+1,s.length);s="strip"}else if(s!=="strip"){H=s;s=null}}D=R+"!"+P;E=s?D+"!"+s:D;if(H!==null&&!S.text[D])S.defined[B]=S.text[D]=H;else if(!S.text[D]&&!S.textWaiting[D]&&!S.textWaiting[E]){Q[E]||(Q[E]=Q[Q.push({name:B,key:D,fullKey:E,strip:!!s})-1]);s=require.nameToUrl(R,"."+P,k);S.loaded[B]=false;require.fetchText(s,function(U){S.text[D]=
U;S.loaded[B]=true;require.checkLoaded(k)})}},checkDeps:function(){},isWaiting:function(B){return!!B.textWaiting.length},orderDeps:function(B){var k,s,H,D=B.textWaiting;B.textWaiting=[];for(k=0;s=D[k];k++){H=B.text[s.key];B.defined[s.name]=s.strip?require.textStrip(H):H}}})})();
(function(){var J=0;require._jsonp={};require.plugin({prefix:"jsonp",require:function(){},newContext:function(y){require.mixin(y,{jsonpWaiting:[]})},load:function(y,Y){var B=y.indexOf("?"),k=y.substring(0,B);B=y.substring(B+1,y.length);var s=require.s.contexts[Y],H={name:y},D="f"+J++,E=require.s.head,R=E.ownerDocument.createElement("script");require._jsonp[D]=function(P){H.value=P;s.loaded[y]=true;require.checkLoaded(Y);setTimeout(function(){E.removeChild(R);delete require._jsonp[D]},15)};s.jsonpWaiting.push(H);
k=require.nameToUrl(k,"?",Y);k+=(k.indexOf("?")===-1?"?":"")+B.replace("?","require._jsonp."+D);s.loaded[y]=false;R.type="text/javascript";R.charset="utf-8";R.src=k;R.setAttribute("async","async");E.appendChild(R)},checkDeps:function(){},isWaiting:function(y){return!!y.jsonpWaiting.length},orderDeps:function(y){var Y,B,k=y.jsonpWaiting;y.jsonpWaiting=[];for(Y=0;B=k[Y];Y++)y.defined[B.name]=B.value}})})();
(function(){function J(B){var k=B.currentTarget||B.srcElement,s,H,D,E;if(B.type==="load"||Y.test(k.readyState)){H=k.getAttribute("data-requirecontext");s=k.getAttribute("data-requiremodule");B=require.s.contexts[H];D=B.orderWaiting;E=B.orderCached;E[s]=true;for(s=0;E[D[s]];s++);s>0&&require(D.splice(0,s),H);if(!D.length)B.orderCached={};setTimeout(function(){k.parentNode.removeChild(k)},15)}}var y=window.opera&&Object.prototype.toString.call(window.opera)==="[object Opera]"||"MozAppearance"in document.documentElement.style,
Y=/^(complete|loaded)$/;require.plugin({prefix:"order",require:function(){},newContext:function(B){require.mixin(B,{orderWaiting:[],orderCached:{}})},load:function(B,k){var s=require.s.contexts[k],H=require.nameToUrl(B,null,k);require.s.skipAsync[H]=true;if(y)require([B],k);else{s.orderWaiting.push(B);s.loaded[B]=false;require.attach(H,k,B,J,"script/cache")}},checkDeps:function(){},isWaiting:function(B){return!!B.orderWaiting.length},orderDeps:function(){}})})();
(function(J,y){function Y(){if(!c.isReady){try{w.documentElement.doScroll("left")}catch(a){setTimeout(Y,1);return}c.ready()}}function B(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function k(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var r in b)k(a,r,b[r],f,e,d);return a}if(d!==y){f=!i&&f&&c.isFunction(d);for(r=0;r<j;r++)e(a[r],b,f?d.call(a[r],r,e(a[r],b)):d,i);return a}return j?
e(a[0],b):y}function s(){return(new Date).getTime()}function H(){return false}function D(){return true}function E(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function R(a){var b,d=[],f=[],e=arguments,i,j,r,m,q,x;j=c.data(this,"events");if(!(a.liveFired===this||!j||!j.live||a.button&&a.type==="click")){a.liveFired=this;var G=j.live.slice(0);for(m=0;m<G.length;m++){j=G[m];j.origType.replace(oa,"")===a.type?f.push(j.selector):G.splice(m--,1)}i=c(a.target).closest(f,a.currentTarget);q=0;for(x=
i.length;q<x;q++)for(m=0;m<G.length;m++){j=G[m];if(i[q].selector===j.selector){r=i[q].elem;f=null;if(j.preType==="mouseenter"||j.preType==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==r)d.push({elem:r,handleObj:j})}}q=0;for(x=d.length;q<x;q++){i=d[q];a.currentTarget=i.elem;a.data=i.handleObj.data;a.handleObj=i.handleObj;if(i.handleObj.origHandler.apply(i.elem,e)===false){b=false;break}}return b}}function P(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function Q(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function U(a,b,d){var f,e,i;b=b&&b[0]?b[0].ownerDocument||b[0]:w;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===w&&!Ea.test(a[0])&&(c.support.checkClone||!Fa.test(a[0]))){e=
true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=i?f:1;return{fragment:f,cacheable:e}}function N(a,b){var d={};c.each(Ga.concat.apply([],Ga.slice(0,b)),function(){d[this]=a});return d}function Z(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},ka=J.jQuery,da=J.$,w=J.document,fa,l=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,o=/^.[^:#\[\.,]*$/,v=/\S/,z=
/^(\s|\u00A0)+|(\s|\u00A0)+$/g,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,C=navigator.userAgent,W=false,O=[],X=!!(typeof require!=="undefined"&&require.def),$,ea=Object.prototype.toString,ga=Object.prototype.hasOwnProperty,na=Array.prototype.push,pa=Array.prototype.slice,Ha=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=w;this[0]=w.body;this.selector="body";this.length=1;return this}if(typeof a===
"string")if((d=l.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:w;if(a=A.exec(a))if(c.isPlainObject(b)){a=[w.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=U([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=w.getElementById(d[2])){if(b.id!==d[2])return fa.find(a);this.length=1;this[0]=b}this.context=w;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=w;a=w.getElementsByTagName(a);
return c.merge(this,a)}else return!b||b.jquery?(b||fa).find(a):c(b).find(a);else if(c.isFunction(a))return fa.ready(a);if(a.selector!==y){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return pa.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?na.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=
this.context;if(b==="find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady&&(!X||require.s.isDone))a.call(w,c);else{O||(O=[]);O.push(a)}return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(pa.apply(this,arguments),"slice",
pa.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:na,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,r;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];
r=e[i];if(a!==r)if(f&&r&&(c.isPlainObject(r)||c.isArray(r))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(r)?[]:{};a[i]=c.extend(f,j,r)}else if(r!==y)a[i]=r}return a};c.extend({noConflict:function(a){J.$=da;if(a)J.jQuery=ka;return c},isReady:false,ready:function(){if(!c.isReady){if(!w.body)return setTimeout(c.ready,13);c.isReady=true;c.callReady()}},callReady:function(){if(c.isReady&&(!X||require.s.isDone)){if(O){for(var a,b=0;a=O[b++];)a.call(w,c);O=null}c.fn.triggerHandler&&c(w).triggerHandler("ready")}},
bindReady:function(){if(!W){W=true;if(w.readyState==="complete")return c.ready();if(w.addEventListener){w.addEventListener("DOMContentLoaded",$,false);J.addEventListener("load",c.ready,false)}else if(w.attachEvent){w.attachEvent("onreadystatechange",$);J.attachEvent("onload",c.ready);var a=false;try{a=J.frameElement==null}catch(b){}w.documentElement.doScroll&&a&&Y()}}},isFunction:function(a){return ea.call(a)==="[object Function]"},isArray:function(a){return ea.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||
ea.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!ga.call(a,"constructor")&&!ga.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===y||ga.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return J.JSON&&J.JSON.parse?J.JSON.parse(a):(new Function("return "+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&v.test(a)){var b=w.getElementsByTagName("head")[0]||w.documentElement,d=w.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(w.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===
b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===y||c.isFunction(a);if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(z,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?na.call(b,
a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==y;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],
f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=y}else if(b&&!c.isFunction(b)){d=b;b=y}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},
browser:{}});C=c.uaMatch(C);if(C.browser){c.browser[C.browser]=true;c.browser.version=C.version}if(c.browser.webkit)c.browser.safari=true;if(Ha)c.inArray=function(a,b){return Ha.call(b,a)};fa=c(w);if(w.addEventListener)$=function(){w.removeEventListener("DOMContentLoaded",$,false);c.ready()};else if(w.attachEvent)$=function(){if(w.readyState==="complete"){w.detachEvent("onreadystatechange",$);c.ready()}};(function(){c.support={};var a=w.documentElement,b=w.createElement("script"),d=w.createElement("div"),
f="script"+s();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),
cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:w.createElement("select").appendChild(w.createElement("option")).selected,parentNode:d.removeChild(d.appendChild(w.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(w.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(J[f]){c.support.scriptEval=true;delete J[f]}try{delete b.test}catch(r){c.support.deleteExpando=
false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function m(){c.support.noCloneEvent=false;d.detachEvent("onclick",m)});d.cloneNode(true).fireEvent("onclick")}d=w.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=w.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var m=w.createElement("div");m.style.width=m.style.paddingLeft="1px";w.body.appendChild(m);
c.boxModel=c.support.boxModel=m.offsetWidth===2;w.body.removeChild(m).style.display="none"});a=function(m){var q=w.createElement("div");m="on"+m;var x=m in q;if(!x){q.setAttribute(m,"return;");x=typeof q[m]==="function"}return x};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",
frameborder:"frameBorder"};var ha="jQuery"+s(),$a=0,Ia={};c.extend({cache:{},expando:ha,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==J?Ia:a;var f=a[ha],e=c.cache;if(!f&&typeof b==="string"&&d===y)return null;f||(f=++$a);if(typeof b==="object"){a[ha]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[ha]=f;e[f]={}}a=e[f];if(d!==y)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=
a==J?Ia:a;var d=a[ha],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===y){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===y&&
this.length)f=c.data(this[0],a);return f===y&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();
if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===y)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||
"fx",[])}});var Ja=/[\n\t]/g,ua=/\s+/,ab=/\r/g,bb=/href|src|style/,cb=/(button|input)/i,db=/(button|input|object|select|textarea)/i,eb=/^(a|area)$/i,Ka=/radio|checkbox/;c.fn.extend({attr:function(a,b){return k(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(q){var x=c(this);x.addClass(a.call(this,q,x.attr("class")))});if(a&&typeof a==="string")for(var b=
(a||"").split(ua),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var i=" "+e.className+" ",j=e.className,r=0,m=b.length;r<m;r++)if(i.indexOf(" "+b[r]+" ")<0)j+=" "+b[r];e.className=c.trim(j)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(m){var q=c(this);q.removeClass(a.call(this,m,q.attr("class")))});if(a&&typeof a==="string"||a===y)for(var b=(a||"").split(ua),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===
1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(Ja," "),j=0,r=b.length;j<r;j++)i=i.replace(" "+b[j]+" "," ");e.className=c.trim(i)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),r=b,m=a.split(ua);e=m[i++];){r=f?r:!j.hasClass(e);j[r?"addClass":"removeClass"](e)}else if(d===
"undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Ja," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===y){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=
[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(Ka.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(ab,"")}return y}var r=c.isFunction(a);return this.each(function(m){var q=c(this),x=a;if(this.nodeType===1){if(r)x=a.call(this,m,q.val());if(typeof x==="number")x+="";if(c.isArray(x)&&Ka.test(this.type))this.checked=
c.inArray(q.val(),x)>=0;else if(c.nodeName(this,"select")){var G=c.makeArray(x);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),G)>=0});if(!G.length)this.selectedIndex=-1}else this.value=x}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return y;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==y;b=f&&c.props[b]||b;if(a.nodeType===
1){var i=bb.test(b);if(b in a&&f&&!i){if(e){b==="type"&&cb.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:db.test(a.nodeName)||eb.test(a.nodeName)&&a.href?0:y;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&
f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?y:a}return c.style(a,b,d)}});var oa=/\.(.*)$/,fb=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==J&&!a.frameElement)a=J;var e,i;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(i=c.data(a)){var j=i.events=i.events||{},r=i.handle;if(!r)i.handle=r=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(r.elem,
arguments):y};r.elem=a;b=b.split(" ");for(var m,q=0,x;m=b[q++];){i=e?c.extend({},e):{handler:d,data:f};if(m.indexOf(".")>-1){x=m.split(".");m=x.shift();i.namespace=x.slice(0).sort().join(".")}else{x=[];i.namespace=""}i.type=m;i.guid=d.guid;var G=j[m],M=c.event.special[m]||{};if(!G){G=j[m]=[];if(!M.setup||M.setup.call(a,f,x,r)===false)if(a.addEventListener)a.addEventListener(m,r,false);else a.attachEvent&&a.attachEvent("on"+m,r)}if(M.add){M.add.call(a,i);if(!i.handler.guid)i.handler.guid=d.guid}G.push(i);
c.event.global[m]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,i=0,j,r,m,q,x,G,M=c.data(a),V=M&&M.events;if(M&&V){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in V)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[i++];){q=e;j=e.indexOf(".")<0;r=[];if(!j){r=e.split(".");e=r.shift();m=new RegExp("(^|\\.)"+c.map(r.slice(0).sort(),fb).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(x=V[e])if(d){q=c.event.special[e]||
{};for(T=f||0;T<x.length;T++){G=x[T];if(d.guid===G.guid){if(j||m.test(G.namespace)){f==null&&x.splice(T--,1);q.remove&&q.remove.call(a,G)}if(f!=null)break}}if(x.length===0||f!=null&&x.length===1){if(!q.teardown||q.teardown.call(a,r)===false)La(a,e,M.handle);delete V[e]}}else for(var T=0;T<x.length;T++){G=x[T];if(j||m.test(G.namespace)){c.event.remove(a,q,G.handler,T);x.splice(T--,1)}}}if(c.isEmptyObject(V)){if(b=M.handle)b.elem=null;delete M.events;delete M.handle;c.isEmptyObject(M)&&c.removeData(a)}}}}},
trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[ha]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return y;a.result=y;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;
try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var j,r=c.nodeName(f,"a")&&e==="click",m=c.event.special[e]||{};if((!m._default||m._default.call(d,a)===false)&&!r&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(j=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(q){}if(j)f["on"+e]=
j;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||J.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var i=d.length;e<i;e++){var j=d[e];if(b||f.test(j.namespace)){a.handler=j.handler;a.data=j.data;a.handleObj=j;j=j.handler.apply(this,arguments);if(j!==y){a.result=
j;if(j===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[ha])return a;
var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||w;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=w.documentElement;d=w.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||d&&d.scrollTop||0)-(b&&b.clientTop||
d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==y)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:R}))},remove:function(a){var b=true,d=a.origType.replace(oa,"");c.each(c.data(this,"events").live||[],function(){if(d===
this.origType.replace(oa,""))return b=false});b&&c.event.remove(this,a.origType,R)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var La=w.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=
a;this.timeStamp=s();this[ha]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=D;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=D;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=D;this.stopPropagation()},isDefaultPrevented:H,isPropagationStopped:H,isImmediatePropagationStopped:H};
var Ma=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Na=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Na:Ma,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Na:Ma)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return E("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return E("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var va=/textarea|input|select/i,
wa,Oa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},xa=function(a,b){var d=a.target,f,e;if(!(!va.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Oa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",e);if(!(f===y||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,
b,d)}}};c.event.special.change={filters:{focusout:xa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return xa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return xa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",Oa(a))}},setup:function(){if(this.type==="file")return false;
for(var a in wa)c.event.add(this,a+".specialChange",wa[a]);return va.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return va.test(this.nodeName)}};wa=c.event.special.change.filters}w.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind",
"one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=y}var j=b==="one"?c.proxy(e,function(m){c(this).unbind(m,j);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{i=0;for(var r=this.length;i<r;i++)c.event.add(this[i],d,j,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<
f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,
function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Pa={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,i){var j,r=0,m,q,x=i||this.selector,G=i?this:c(this.context);if(c.isFunction(f)){e=f;f=y}for(d=(d||"").split(" ");(j=d[r++])!=null;){i=
oa.exec(j);m="";if(i){m=i[0];j=j.replace(oa,"")}if(j==="hover")d.push("mouseenter"+m,"mouseleave"+m);else{q=j;if(j==="focus"||j==="blur"){d.push(Pa[j]+m);j+=m}else j=(Pa[j]||j)+m;b==="live"?G.each(function(){c.event.add(this,P(j,x),{data:f,selector:x,handler:e,origType:j,origHandler:e,preType:q})}):G.unbind(P(j,x),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});J.attachEvent&&!J.addEventListener&&J.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",n,p=0;g[p];p++){n=g[p];if(n.nodeType===3||n.nodeType===4)h+=n.nodeValue;else if(n.nodeType!==8)h+=a(n.childNodes)}return h}function b(g,h,n,p,u,t){u=0;for(var I=p.length;u<I;u++){var F=p[u];
if(F){F=F[g];for(var L=false;F;){if(F.sizcache===n){L=p[F.sizset];break}if(F.nodeType===1&&!t){F.sizcache=n;F.sizset=u}if(F.nodeName.toLowerCase()===h){L=F;break}F=F[g]}p[u]=L}}}function d(g,h,n,p,u,t){u=0;for(var I=p.length;u<I;u++){var F=p[u];if(F){F=F[g];for(var L=false;F;){if(F.sizcache===n){L=p[F.sizset];break}if(F.nodeType===1){if(!t){F.sizcache=n;F.sizset=u}if(typeof h!=="string"){if(F===h){L=true;break}}else if(m.filter(h,[F]).length>0){L=F;break}}F=F[g]}p[u]=L}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,i=Object.prototype.toString,j=false,r=true;[0,0].sort(function(){r=false;return 0});var m=function(g,h,n,p){n=n||[];var u=h=h||w;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return n;for(var t=[],I,F,L,qa,ia=true,la=K(h),ja=g;(f.exec(""),I=f.exec(ja))!==null;){ja=I[3];t.push(I[1]);if(I[2]){qa=I[3];break}}if(t.length>1&&x.exec(g))if(t.length===2&&q.relative[t[0]])F=ya(t[0]+t[1],h);else for(F=q.relative[t[0]]?[h]:m(t.shift(),h);t.length;){g=t.shift();if(q.relative[g])g+=
t.shift();F=ya(g,F)}else{if(!p&&t.length>1&&h.nodeType===9&&!la&&q.match.ID.test(t[0])&&!q.match.ID.test(t[t.length-1])){I=m.find(t.shift(),h,la);h=I.expr?m.filter(I.expr,I.set)[0]:I.set[0]}if(h){I=p?{expr:t.pop(),set:M(p)}:m.find(t.pop(),t.length===1&&(t[0]==="~"||t[0]==="+")&&h.parentNode?h.parentNode:h,la);F=I.expr?m.filter(I.expr,I.set):I.set;if(t.length>0)L=M(F);else ia=false;for(;t.length;){var aa=t.pop();I=aa;if(q.relative[aa])I=t.pop();else aa="";if(I==null)I=h;q.relative[aa](L,I,la)}}else L=
[]}L||(L=F);L||m.error(aa||g);if(i.call(L)==="[object Array]")if(ia)if(h&&h.nodeType===1)for(g=0;L[g]!=null;g++){if(L[g]&&(L[g]===true||L[g].nodeType===1&&ba(h,L[g])))n.push(F[g])}else for(g=0;L[g]!=null;g++)L[g]&&L[g].nodeType===1&&n.push(F[g]);else n.push.apply(n,L);else M(L,n);if(qa){m(qa,u,n,p);m.uniqueSort(n)}return n};m.uniqueSort=function(g){if(T){j=r;g.sort(T);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};m.matches=function(g,h){return m(g,null,null,h)};m.find=function(g,
h,n){var p,u;if(!g)return[];for(var t=0,I=q.order.length;t<I;t++){var F=q.order[t];if(u=q.leftMatch[F].exec(g)){var L=u[1];u.splice(1,1);if(L.substr(L.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");p=q.find[F](u,h,n);if(p!=null){g=g.replace(q.match[F],"");break}}}}p||(p=h.getElementsByTagName("*"));return{set:p,expr:g}};m.filter=function(g,h,n,p){for(var u=g,t=[],I=h,F,L,qa=h&&h[0]&&K(h[0]);g&&h.length;){for(var ia in q.filter)if((F=q.leftMatch[ia].exec(g))!=null&&F[2]){var la=q.filter[ia],ja,
aa;aa=F[1];L=false;F.splice(1,1);if(aa.substr(aa.length-1)!=="\\"){if(I===t)t=[];if(q.preFilter[ia])if(F=q.preFilter[ia](F,I,n,t,p,qa)){if(F===true)continue}else L=ja=true;if(F)for(var ra=0;(aa=I[ra])!=null;ra++)if(aa){ja=la(aa,F,ra,I);var Qa=p^!!ja;if(n&&ja!=null)if(Qa)L=true;else I[ra]=false;else if(Qa){t.push(aa);L=true}}if(ja!==y){n||(I=t);g=g.replace(q.match[ia],"");if(!L)return[];break}}}if(g===u)if(L==null)m.error(g);else break;u=g}return I};m.error=function(g){throw"Syntax error, unrecognized expression: "+
g;};var q=m.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\))?/},
leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,h){var n=typeof h==="string",p=n&&!/\W/.test(h);n=n&&!p;if(p)h=h.toLowerCase();p=0;for(var u=g.length,t;p<u;p++)if(t=g[p]){for(;(t=t.previousSibling)&&t.nodeType!==1;);g[p]=n||t&&t.nodeName.toLowerCase()===h?t||false:t===h}n&&m.filter(h,g,true)},">":function(g,h){var n=typeof h==="string";if(n&&!/\W/.test(h)){h=h.toLowerCase();for(var p=0,u=g.length;p<u;p++){var t=
g[p];if(t){n=t.parentNode;g[p]=n.nodeName.toLowerCase()===h?n:false}}}else{p=0;for(u=g.length;p<u;p++)if(t=g[p])g[p]=n?t.parentNode:t.parentNode===h;n&&m.filter(h,g,true)}},"":function(g,h,n){var p=e++,u=d;if(typeof h==="string"&&!/\W/.test(h)){var t=h=h.toLowerCase();u=b}u("parentNode",h,p,g,t,n)},"~":function(g,h,n){var p=e++,u=d;if(typeof h==="string"&&!/\W/.test(h)){var t=h=h.toLowerCase();u=b}u("previousSibling",h,p,g,t,n)}},find:{ID:function(g,h,n){if(typeof h.getElementById!=="undefined"&&
!n)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var n=[];h=h.getElementsByName(g[1]);for(var p=0,u=h.length;p<u;p++)h[p].getAttribute("name")===g[1]&&n.push(h[p]);return n.length===0?null:n}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,n,p,u,t){g=" "+g[1].replace(/\\/g,"")+" ";if(t)return g;t=0;for(var I;(I=h[t])!=null;t++)if(I)if(u^(I.className&&(" "+I.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=
0))n||p.push(I);else if(n)h[t]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,n,p,u,t){h=g[1].replace(/\\/g,"");if(!t&&q.attrMap[h])g[1]=q.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,n,p,
u){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=m(g[3],null,null,h);else{g=m.filter(g[3],h,n,true^u);n||p.push.apply(p,g);return false}else if(q.match.POS.test(g[0])||q.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},
empty:function(g){return!g.firstChild},has:function(g,h,n){return!!m(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===
g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,n,p){return h===p.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,n){return h<n[3]-0},gt:function(g,h,n){return h>n[3]-0},nth:function(g,h,n){return n[3]-0===h},eq:function(g,h,n){return n[3]-0===h}},filter:{PSEUDO:function(g,h,n,p){var u=h[1],t=q.filters[u];if(t)return t(g,
n,h,p);else if(u==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(u==="not"){h=h[3];n=0;for(p=h.length;n<p;n++)if(h[n]===g)return false;return true}else m.error("Syntax error, unrecognized expression: "+u)},CHILD:function(g,h){var n=h[1],p=g;switch(n){case "only":case "first":for(;p=p.previousSibling;)if(p.nodeType===1)return false;if(n==="first")return true;p=g;case "last":for(;p=p.nextSibling;)if(p.nodeType===1)return false;return true;case "nth":n=h[2];var u=
h[3];if(n===1&&u===0)return true;h=h[0];var t=g.parentNode;if(t&&(t.sizcache!==h||!g.nodeIndex)){var I=0;for(p=t.firstChild;p;p=p.nextSibling)if(p.nodeType===1)p.nodeIndex=++I;t.sizcache=h}g=g.nodeIndex-u;return n===0?g===0:g%n===0&&g/n>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var n=
h[1];g=q.attrHandle[n]?q.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);n=g+"";var p=h[2];h=h[4];return g==null?p==="!=":p==="="?n===h:p==="*="?n.indexOf(h)>=0:p==="~="?(" "+n+" ").indexOf(h)>=0:!h?n&&g!==false:p==="!="?n!==h:p==="^="?n.indexOf(h)===0:p==="$="?n.substr(n.length-h.length)===h:p==="|="?n===h||n.substr(0,h.length+1)===h+"-":false},POS:function(g,h,n,p){var u=q.setFilters[h[2]];if(u)return u(g,n,h,p)}}},x=q.match.POS;for(var G in q.match){q.match[G]=new RegExp(q.match[G].source+/(?![^\[]*\])(?![^\(]*\))/.source);
q.leftMatch[G]=new RegExp(/(^(?:.|\r|\n)*?)/.source+q.match[G].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var M=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(w.documentElement.childNodes,0)}catch(V){M=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var n=0,p=g.length;n<p;n++)h.push(g[n]);else for(n=0;g[n];n++)h.push(g[n]);return h}}var T;
if(w.documentElement.compareDocumentPosition)T=function(g,h){if(!g.compareDocumentPosition||!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in w.documentElement)T=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(w.createRange)T=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==
h)j=true;return g.ownerDocument?-1:1}var n=g.ownerDocument.createRange(),p=h.ownerDocument.createRange();n.setStart(g,0);n.setEnd(g,0);p.setStart(h,0);p.setEnd(h,0);g=n.compareBoundaryPoints(Range.START_TO_END,p);if(g===0)j=true;return g};(function(){var g=w.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var n=w.documentElement;n.insertBefore(g,n.firstChild);if(w.getElementById(h)){q.find.ID=function(p,u,t){if(typeof u.getElementById!=="undefined"&&!t)return(u=
u.getElementById(p[1]))?u.id===p[1]||typeof u.getAttributeNode!=="undefined"&&u.getAttributeNode("id").nodeValue===p[1]?[u]:y:[]};q.filter.ID=function(p,u){var t=typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id");return p.nodeType===1&&t&&t.nodeValue===u}}n.removeChild(g);n=g=null})();(function(){var g=w.createElement("div");g.appendChild(w.createComment(""));if(g.getElementsByTagName("*").length>0)q.find.TAG=function(h,n){n=n.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var p=
0;n[p];p++)n[p].nodeType===1&&h.push(n[p]);n=h}return n};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")q.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();w.querySelectorAll&&function(){var g=m,h=w.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){m=function(p,u,t,I){u=u||w;if(!I&&u.nodeType===9&&!K(u))try{return M(u.querySelectorAll(p),
t)}catch(F){}return g(p,u,t,I)};for(var n in g)m[n]=g[n];h=null}}();(function(){var g=w.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){q.order.splice(1,0,"CLASS");q.find.CLASS=function(h,n,p){if(typeof n.getElementsByClassName!=="undefined"&&!p)return n.getElementsByClassName(h[1])};g=null}}})();var ba=w.compareDocumentPosition?
function(g,h){return!!(g.compareDocumentPosition(h)&16)}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},K=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ya=function(g,h){var n=[],p="",u;for(h=h.nodeType?[h]:h;u=q.match.PSEUDO.exec(g);){p+=u[0];g=g.replace(q.match.PSEUDO,"")}g=q.relative[g]?g+"*":g;u=0;for(var t=h.length;u<t;u++)m(g,h[u],n);return m.filter(p,n)};c.find=m;c.expr=m.selectors;c.expr[":"]=c.expr.filters;c.unique=m.uniqueSort;c.text=
a;c.isXMLDoc=K;c.contains=ba})();var gb=/Until$/,hb=/^(?:parents|prevUntil|prevAll)/,ib=/,/;pa=Array.prototype.slice;var Ra=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(o.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=
this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ra(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ra(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>
0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var r=a.length;e<r;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var m=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(q,x){for(;x&&x.ownerDocument&&x!==b;){if(m?m.index(x)>-1:c(x).is(a))return x;
x=x.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(S(a[0])||S(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},
parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},
contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);gb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||ib.test(f))&&hb.test(a))e=e.reverse();return this.pushStack(e,a,pa.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=
a[b];a&&a.nodeType!==9&&(d===y||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Sa=/ jQuery\d+="(?:\d+|null)"/g,sa=/^\s+/,Ta=/(<([\w:]+)[^>]*?)\/>/g,jb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ua=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,Ea=/<script|<object|<embed|<option|<style/i,
Fa=/checked\s*(?:[^=]|=\s*.checked.)/i,Va=function(a,b,d){return jb.test(d)?a:b+"></"+d+">"},ca={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};ca.optgroup=ca.option;ca.tbody=ca.tfoot=ca.colgroup=ca.caption=ca.thead;ca.th=
ca.td;if(!c.support.htmlSerialize)ca._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==y)return this.empty().append((this[0]&&this[0].ownerDocument||w).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&
b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},
append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&
this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=
this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Sa,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(sa,"")],f)[0]}else return this.cloneNode(true)});if(a===true){Q(this,b);
Q(this.find("*"),b.find("*"))}return b},html:function(a){if(a===y)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Sa,""):null;else if(typeof a==="string"&&!Ea.test(a)&&(c.support.leadingWhitespace||!sa.test(a))&&!ca[(Ua.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ta,Va);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var i=
c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},
detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(G){return c.nodeName(G,"table")?G.getElementsByTagName("tbody")[0]||G.appendChild(G.ownerDocument.createElement("tbody")):G}var e,i,j=a[0],r=[],m;if(!c.support.checkClone&&arguments.length===3&&typeof j==="string"&&Fa.test(j))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(j))return this.each(function(G){var M=c(this);a[0]=j.call(this,G,b?M.html():y);M.domManip(a,b,d)});if(this[0]){e=j&&j.parentNode;
e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:U(a,this,r);m=e.fragment;if(i=m.childNodes.length===1?(m=m.firstChild):m.firstChild){b=b&&c.nodeName(i,"tr");for(var q=0,x=this.length;q<x;q++)d.call(b?f(this[q],i):this[q],q>0||e.cacheable||this.length>1?m.cloneNode(true):m)}r.length&&c.each(r,B)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=
function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{e=0;for(var i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||w;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||w;for(var e=[],i=0,j;(j=a[i])!=null;i++){if(typeof j==="number")j+=
"";if(j){if(typeof j==="string"&&!lb.test(j))j=b.createTextNode(j);else if(typeof j==="string"){j=j.replace(Ta,Va);var r=(Ua.exec(j)||["",""])[1].toLowerCase(),m=ca[r]||ca._default,q=m[0],x=b.createElement("div");for(x.innerHTML=m[1]+j+m[2];q--;)x=x.lastChild;if(!c.support.tbody){q=kb.test(j);r=r==="table"&&!q?x.firstChild&&x.firstChild.childNodes:m[1]==="<table>"&&!q?x.childNodes:[];for(m=r.length-1;m>=0;--m)c.nodeName(r[m],"tbody")&&!r[m].childNodes.length&&r[m].parentNode.removeChild(r[m])}!c.support.leadingWhitespace&&
sa.test(j)&&x.insertBefore(b.createTextNode(sa.exec(j)[0]),x.firstChild);j=x.childNodes}if(j.nodeType)e.push(j);else e=c.merge(e,j)}}if(d)for(i=0;e[i];i++)if(f&&c.nodeName(e[i],"script")&&(!e[i].type||e[i].type.toLowerCase()==="text/javascript"))f.push(e[i].parentNode?e[i].parentNode.removeChild(e[i]):e[i]);else{e[i].nodeType===1&&e.splice.apply(e,[i+1,0].concat(c.makeArray(e[i].getElementsByTagName("script"))));d.appendChild(e[i])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,
i=c.support.deleteExpando,j=0,r;(r=a[j])!=null;j++)if(d=r[c.expando]){b=f[d];if(b.events)for(var m in b.events)e[m]?c.event.remove(r,m):La(r,m,b.handle);if(i)delete r[c.expando];else r.removeAttribute&&r.removeAttribute(c.expando);delete f[d]}}});var mb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Wa=/alpha\([^)]*\)/,Xa=/opacity=([^)]*)/,za=/float/i,Aa=/-([a-z])/ig,nb=/([A-Z])/g,ob=/^-?\d+(?:px)?$/i,pb=/^-?\d/,qb={position:"absolute",visibility:"hidden",display:"block"},rb=["Left","Right"],
sb=["Top","Bottom"],tb=w.defaultView&&w.defaultView.getComputedStyle,Ya=c.support.cssFloat?"cssFloat":"styleFloat",Ba=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return k(this,a,b,true,function(d,f,e){if(e===y)return c.curCSS(d,f);if(typeof e==="number"&&!mb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return y;if((b==="width"||b==="height")&&parseFloat(d)<0)d=y;var f=a.style||a,e=d!==y;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=
1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Wa.test(a)?a.replace(Wa,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Xa.exec(f.filter)[1])/100+"":""}if(za.test(b))b=Ya;b=b.replace(Aa,Ba);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?rb:sb;d=function(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,
true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})};a.offsetWidth!==0?d():c.swap(a,qb,d);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Xa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(za.test(b))b=Ya;if(!d&&e&&e[b])f=e[b];else if(tb){if(za.test(b))b="float";b=b.replace(nb,
"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(Aa,Ba);f=a.currentStyle[b]||a.currentStyle[d];if(!ob.test(f)&&pb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=
b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var ub=s(),vb=/<script(.|\s)*?\/script>/gi,wb=/select|textarea/i,xb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,ma=/=\?(&|$)/,
Ca=/\?/,yb=/(\?|&)_=.*?(&|$)/,zb=/^(\w+:)?\/\/([^\/?#]+)/,Ab=/%20/g,Bb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string")return Bb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var i=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(j,r){if(r==="success"||r==="notmodified")i.html(e?
c("<div />").append(j.responseText.replace(vb,"")).find(e):j.responseText);d&&i.each(d,[j.responseText,r,j])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||wb.test(this.nodeName)||xb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,
value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",
url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:J.XMLHttpRequest&&(J.location.protocol!=="file:"||!J.ActiveXObject)?function(){return new J.XMLHttpRequest}:function(){try{return new J.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",
json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(m,r,j,K);e.global&&f("ajaxSuccess",[K,e])}function d(){e.complete&&e.complete.call(m,K,j);e.global&&f("ajaxComplete",[K,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(u,t){(e.context?c(e.context):c.event).trigger(u,t)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,r,m=a&&a.context||e,q=e.type.toUpperCase();if(e.data&&e.processData&&
typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(q==="GET")ma.test(e.url)||(e.url+=(Ca.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!ma.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&ma.test(e.data)||ma.test(e.url))){i=e.jsonpCallback||"jsonp"+ub++;if(e.data)e.data=(e.data+"").replace(ma,"="+i+"$1");e.url=e.url.replace(ma,"="+i+"$1");e.dataType="script";J[i]=J[i]||
function(u){r=u;b();d();J[i]=y;try{delete J[i]}catch(t){}M&&M.removeChild(V)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&q==="GET"){var x=s(),G=e.url.replace(yb,"$1_="+x+"$2");e.url=G+(G===e.url?(Ca.test(e.url)?"&":"?")+"_="+x:"")}if(e.data&&q==="GET")e.url+=(Ca.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");x=(x=zb.exec(e.url))&&(x[1]&&x[1]!==location.protocol||x[2]!==location.host);if(e.dataType==="script"&&q==="GET"&&x){var M=w.getElementsByTagName("head")[0]||
w.documentElement,V=w.createElement("script");V.src=e.url;if(e.scriptCharset)V.charset=e.scriptCharset;if(!i){var T=false;V.onload=V.onreadystatechange=function(){if(!T&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){T=true;b();d();V.onload=V.onreadystatechange=null;M&&V.parentNode&&M.removeChild(V)}}}M.insertBefore(V,M.firstChild);return y}var ba=false,K=e.xhr();if(K){e.username?K.open(q,e.url,e.async,e.username,e.password):K.open(q,e.url,e.async);try{if(e.data||a&&
a.contentType)K.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&K.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&K.setRequestHeader("If-None-Match",c.etag[e.url])}x||K.setRequestHeader("X-Requested-With","XMLHttpRequest");K.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ya){}if(e.beforeSend&&e.beforeSend.call(m,K,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");
K.abort();return false}e.global&&f("ajaxSend",[K,e]);var g=K.onreadystatechange=function(u){if(!K||K.readyState===0||u==="abort"){ba||d();ba=true;if(K)K.onreadystatechange=c.noop}else if(!ba&&K&&(K.readyState===4||u==="timeout")){ba=true;K.onreadystatechange=c.noop;j=u==="timeout"?"timeout":!c.httpSuccess(K)?"error":e.ifModified&&c.httpNotModified(K,e.url)?"notmodified":"success";var t;if(j==="success")try{r=c.httpData(K,e.dataType,e)}catch(I){j="parsererror";t=I}if(j==="success"||j==="notmodified")i||
b();else c.handleError(e,K,j,t);d();u==="timeout"&&K.abort();if(e.async)K=null}};try{var h=K.abort;K.abort=function(){K&&h.call(K);g("abort")}}catch(n){}e.async&&e.timeout>0&&setTimeout(function(){K&&!ba&&g("timeout")},e.timeout);try{K.send(q==="POST"||q==="PUT"||q==="DELETE"?e.data:null)}catch(p){c.handleError(e,K,null,p);d()}e.async||g();return K}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},
active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;
e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(j,r){if(c.isArray(r))c.each(r,function(m,q){b||/\[\]$/.test(j)?f(j,q):d(j+"["+(typeof q==="object"||c.isArray(q)?m:"")+"]",q)});else!b&&r!=null&&typeof r==="object"?c.each(r,function(m,q){d(j+"["+m+"]",
q)}):f(j,r)}function f(j,r){r=c.isFunction(r)?r():r;e[e.length]=encodeURIComponent(j)+"="+encodeURIComponent(r)}var e=[];if(b===y)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var i in a)d(i,a[i]);return e.join("&").replace(Ab,"+")}});var Da={},Cb=/toggle|show|hide/,Db=/^([+-]=)?([\d+-.]+)(.*)$/,ta,Ga=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],
["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(N("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(Da[d])f=Da[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();Da[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},
hide:function(a,b){if(a||a===0)return this.animate(N("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":
"hide"]()}):this.animate(N("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,r=this.nodeType===1&&c(this).is(":hidden"),m=this;for(j in a){var q=j.replace(Aa,Ba);if(j!==q){a[q]=a[j];delete a[j];j=q}if(a[j]==="hide"&&r||a[j]==="show"&&!r)return i.complete.call(this);
if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(x,G){var M=new c.fx(m,i,x);if(Cb.test(G))M[G==="toggle"?r?"show":"hide":G](a);else{var V=Db.exec(G),T=M.cur(true)||0;if(V){G=parseFloat(V[2]);var ba=V[3]||"px";if(ba!=="px"){m.style[x]=(G||1)+ba;T=(G||1)/M.cur(true)*T;m.style[x]=
T+ba}if(V[1])G=(V[1]==="-="?-1:1)*G+T;M.custom(T,G,ba)}else M.custom(T,G,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:N("show",1),slideUp:N("hide",1),slideToggle:N("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,
b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=
b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,
this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=s();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!ta)ta=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=
c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=s(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,
"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);
this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(ta);ta=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=
a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in w.documentElement?function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&
f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,i,j=e.documentElement,r=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;
for(var m=b.offsetTop,q=b.offsetLeft;(b=b.parentNode)&&b!==r&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;m-=b.scrollTop;q-=b.scrollLeft;if(b===d){m+=b.offsetTop;q+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){m+=parseFloat(i.borderTopWidth)||0;q+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!==
"visible"){m+=parseFloat(i.borderTopWidth)||0;q+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){m+=r.offsetTop;q+=r.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){m+=Math.max(j.scrollTop,r.scrollTop);q+=Math.max(j.scrollLeft,r.scrollLeft)}return{top:m,left:q}};c.offset={initialize:function(){var a=w.body,b=w.createElement("div"),d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,
border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<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>";a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=
e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,
"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left-e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),
d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||w.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=
a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==y)return this.each(function(){if(i=Z(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=Z(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?
c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(i){var j=c(this);j[d](f.call(this,i,j[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+
b],e.body["offset"+b],e.documentElement["offset"+b]):f===y?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});J.jQuery=J.$=c;if(X){var Za=require.callReady;require.callReady=function(){Za&&Za();c.callReady()};require.def("jquery",function(){return c});require.def("jQuery",function(){return c})}})(window);

Просмотреть файл

@ -0,0 +1,8 @@
<div id="header">
<div id="back">
<div class="mblArrowButtonHead"></div>
<div class="mblArrowButtonBody mblArrowButtonText">Back</div>
<div class="mblArrowButtonNeck"></div>
</div>
<span id="headerText"></span>
</div>