linkdrop
This commit is contained in:
Коммит
ce578f69c5
|
@ -0,0 +1,10 @@
|
|||
syntax:regexp
|
||||
\.egg
|
||||
\.egg-info
|
||||
\.pyc
|
||||
\.pyo
|
||||
\.swp
|
||||
linkdrop.kpf
|
||||
renv
|
||||
\.DS_Store
|
||||
development.db
|
|
@ -0,0 +1,3 @@
|
|||
include linkdrop/config/deployment.ini_tmpl
|
||||
recursive-include linkdrop/public *
|
||||
recursive-include linkdrop/templates *
|
|
@ -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.
|
|
@ -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
|
|
@ -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/
|
|
@ -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,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,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))
|
|
@ -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()
|
|
@ -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)
|
|
@ -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']
|
|
@ -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
|
|
@ -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())
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 339 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.8 KiB |
|
@ -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>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.3 KiB |
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
""",
|
||||
)
|
|
@ -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.
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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 <{/}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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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);
|
|
@ -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);
|
||||
};
|
||||
});
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -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);
|
|
@ -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("<").split(">").join(">").split('"').join(""").split("'").join("'");
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
|
@ -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");}})();
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -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>
|
Загрузка…
Ссылка в новой задаче