diff options
Diffstat (limited to 'module')
| -rw-r--r-- | module/lib/bottle.py | 1117 | ||||
| -rw-r--r-- | module/web/static/js/default.js | 65 | ||||
| -rw-r--r-- | module/web/static/js/libs/backbone-0.9.9.js (renamed from module/web/static/js/libs/backbone-0.9.2.js) | 986 | ||||
| -rw-r--r-- | module/web/static/js/libs/jquery-1.8.3.js (renamed from module/web/static/js/libs/jquery-1.8.0.js) | 3437 | ||||
| -rw-r--r-- | module/web/static/js/libs/jquery.peity-0.6.js | 184 | ||||
| -rw-r--r-- | module/web/static/js/libs/jquery.peity-1.0.js | 246 | ||||
| -rw-r--r-- | module/web/static/js/libs/jquery.transit-0.9.9.js (renamed from module/web/static/js/libs/jquery.transit-0.1.3.js) | 142 | ||||
| -rw-r--r-- | module/web/static/js/libs/lodash-1.0.rc3.js (renamed from module/web/static/js/libs/lodash-0.7.0.js) | 2674 | ||||
| -rw-r--r-- | module/web/static/js/libs/require-2.1.2.js (renamed from module/web/static/js/libs/require-2.0.6.js) | 788 | ||||
| -rw-r--r-- | module/web/static/js/views/packageView.js | 1 | ||||
| -rw-r--r-- | module/web/templates/default/base.html | 2 | ||||
| -rw-r--r-- | module/web/templates/mobile/base.html | 2 | 
12 files changed, 5320 insertions, 4324 deletions
| diff --git a/module/lib/bottle.py b/module/lib/bottle.py index 2c243278e..b00bda1c9 100644 --- a/module/lib/bottle.py +++ b/module/lib/bottle.py @@ -9,14 +9,14 @@ Python Standard Library.  Homepage and documentation: http://bottlepy.org/ -Copyright (c) 2011, Marcel Hellkamp. -License: MIT (see LICENSE.txt for details) +Copyright (c) 2012, Marcel Hellkamp. +License: MIT (see LICENSE for details)  """  from __future__ import with_statement  __author__ = 'Marcel Hellkamp' -__version__ = '0.10.2' +__version__ = '0.11.4'  __license__ = 'MIT'  # The gevent server adapter needs to patch some modules before they are imported @@ -35,102 +35,111 @@ if __name__ == '__main__':      if _cmd_options.server and _cmd_options.server.startswith('gevent'):          import gevent.monkey; gevent.monkey.patch_all() -import sys -import base64 -import cgi -import email.utils -import functools -import hmac -import httplib -import imp -import itertools -import mimetypes -import os -import re -import subprocess -import tempfile -import thread -import threading -import time -import warnings - -from Cookie import SimpleCookie +import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\ +        os, re, subprocess, sys, tempfile, threading, time, urllib, warnings +  from datetime import date as datedate, datetime, timedelta  from tempfile import TemporaryFile  from traceback import format_exc, print_exc -from urlparse import urljoin, SplitResult as UrlSplitResult - -# Workaround for a bug in some versions of lib2to3 (fixed on CPython 2.7 and 3.2) -import urllib -urlencode = urllib.urlencode -urlquote = urllib.quote -urlunquote = urllib.unquote - -try: from collections import MutableMapping as DictMixin -except ImportError: # pragma: no cover -    from UserDict import DictMixin - -try: from urlparse import parse_qs -except ImportError: # pragma: no cover -    from cgi import parse_qs - -try: import cPickle as pickle -except ImportError: # pragma: no cover -    import pickle  try: from json import dumps as json_dumps, loads as json_lds  except ImportError: # pragma: no cover      try: from simplejson import dumps as json_dumps, loads as json_lds -    except ImportError: # pragma: no cover +    except ImportError:          try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds -        except ImportError: # pragma: no cover +        except ImportError:              def json_dumps(data):                  raise ImportError("JSON support requires Python 2.6 or simplejson.")              json_lds = json_dumps -py3k = sys.version_info >= (3,0,0) -NCTextIOWrapper = None -if py3k: # pragma: no cover -    json_loads = lambda s: json_lds(touni(s)) -    # See Request.POST + +# We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. +# It ain't pretty but it works... Sorry for the mess. + +py   = sys.version_info +py3k = py >= (3,0,0) +py25 = py <  (2,6,0) +py31 = (3,1,0) <= py < (3,2,0) + +# Workaround for the missing "as" keyword in py3k. +def _e(): return sys.exc_info()[1] + +# Workaround for the "print is a keyword/function" Python 2/3 dilemma +# and a fallback for mod_wsgi (resticts stdout/err attribute access) +try: +    _stdout, _stderr = sys.stdout.write, sys.stderr.write +except IOError: +    _stdout = lambda x: sys.stdout.write(x) +    _stderr = lambda x: sys.stderr.write(x) + +# Lots of stdlib and builtin differences. +if py3k: +    import http.client as httplib +    import _thread as thread +    from urllib.parse import urljoin, SplitResult as UrlSplitResult +    from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote +    urlunquote = functools.partial(urlunquote, encoding='latin1') +    from http.cookies import SimpleCookie +    from collections import MutableMapping as DictMixin +    import pickle      from io import BytesIO -    def touni(x, enc='utf8', err='strict'): -        """ Convert anything to unicode """ -        return str(x, enc, err) if isinstance(x, bytes) else str(x) -    if sys.version_info < (3,2,0): -        from io import TextIOWrapper -        class NCTextIOWrapper(TextIOWrapper): -            ''' Garbage collecting an io.TextIOWrapper(buffer) instance closes -                the wrapped buffer. This subclass keeps it open. ''' -            def close(self): pass -else: -    json_loads = json_lds +    basestring = str +    unicode = str +    json_loads = lambda s: json_lds(touni(s)) +    callable = lambda x: hasattr(x, '__call__') +    imap = map +else: # 2.x +    import httplib +    import thread +    from urlparse import urljoin, SplitResult as UrlSplitResult +    from urllib import urlencode, quote as urlquote, unquote as urlunquote +    from Cookie import SimpleCookie +    from itertools import imap +    import cPickle as pickle      from StringIO import StringIO as BytesIO -    bytes = str -    def touni(x, enc='utf8', err='strict'): -        """ Convert anything to unicode """ -        return x if isinstance(x, unicode) else unicode(str(x), enc, err) - -def tob(data, enc='utf8'): -    """ Convert anything to bytes """ -    return data.encode(enc) if isinstance(data, unicode) else bytes(data) +    if py25: +        from UserDict import DictMixin +        def next(it): return it.next() +        bytes = str +    else: # 2.6, 2.7 +        from collections import MutableMapping as DictMixin +    json_loads = json_lds +# Some helpers for string/byte handling +def tob(s, enc='utf8'): +    return s.encode(enc) if isinstance(s, unicode) else bytes(s) +def touni(s, enc='utf8', err='strict'): +    return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)  tonat = touni if py3k else tob -tonat.__doc__ = """ Convert anything to native strings """ -def try_update_wrapper(wrapper, wrapped, *a, **ka): -    try: # Bug: functools breaks if wrapper is an instane method -        functools.update_wrapper(wrapper, wrapped, *a, **ka) +# 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense). +# 3.1 needs a workaround. +if py31: +    from io import TextIOWrapper +    class NCTextIOWrapper(TextIOWrapper): +        def close(self): pass # Keep wrapped buffer open. + +# File uploads (which are implemented as empty FiledStorage instances...) +# have a negative truth value. That makes no sense, here is a fix. +class FieldStorage(cgi.FieldStorage): +    def __nonzero__(self): return bool(self.list or self.file) +    if py3k: __bool__ = __nonzero__ + +# A bug in functools causes it to break if the wrapper is an instance method +def update_wrapper(wrapper, wrapped, *a, **ka): +    try: functools.update_wrapper(wrapper, wrapped, *a, **ka)      except AttributeError: pass -# Backward compatibility + + +# These helpers are used at module level and need to be defined first. +# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. +  def depr(message):      warnings.warn(message, DeprecationWarning, stacklevel=3) - -# Small helpers -def makelist(data): +def makelist(data): # This is just to handy      if isinstance(data, (tuple, list, set, dict)): return list(data)      elif data: return [data]      else: return [] @@ -161,7 +170,7 @@ class DictProperty(object):          del getattr(obj, self.attr)[self.key] -class CachedProperty(object): +class cached_property(object):      ''' A property that is only computed once per instance and then replaces          itself with an ordinary attribute. Deleting the attribute resets the          property. ''' @@ -174,10 +183,8 @@ class CachedProperty(object):          value = obj.__dict__[self.func.__name__] = self.func(obj)          return value -cached_property = CachedProperty - -class lazy_attribute(object): # Does not need configuration -> lower-case name +class lazy_attribute(object):      ''' A property that caches itself to the class object. '''      def __init__(self, func):          functools.update_wrapper(self, func, updated=[]) @@ -203,35 +210,6 @@ class BottleException(Exception):      pass -#TODO: These should subclass BaseRequest - -class HTTPResponse(BottleException): -    """ Used to break execution and immediately finish the response """ -    def __init__(self, output='', status=200, header=None): -        super(BottleException, self).__init__("HTTP Response %d" % status) -        self.status = int(status) -        self.output = output -        self.headers = HeaderDict(header) if header else None - -    def apply(self, response): -        if self.headers: -            for key, value in self.headers.iterallitems(): -                response.headers[key] = value -        response.status = self.status - - -class HTTPError(HTTPResponse): -    """ Used to generate an error page """ -    def __init__(self, code=500, output='Unknown Error', exception=None, -                 traceback=None, header=None): -        super(HTTPError, self).__init__(output, code, header) -        self.exception = exception -        self.traceback = traceback - -    def __repr__(self): -        return template(ERROR_PAGE_TEMPLATE, e=self) - - @@ -251,12 +229,15 @@ class RouteReset(BottleException):  class RouterUnknownModeError(RouteError): pass +  class RouteSyntaxError(RouteError):      """ The route parser found something not supported by this router """ +  class RouteBuildError(RouteError):      """ The route could not been built """ +  class Router(object):      ''' A Router is an ordered collection of route->target pairs. It is used to          efficiently match WSGI requests against a number of routes and return @@ -285,7 +266,7 @@ class Router(object):          #: If true, static routes are no longer checked first.          self.strict_order = strict          self.filters = {'re': self.re_filter, 'int': self.int_filter, -                        'float': self.re_filter, 'path': self.path_filter} +                        'float': self.float_filter, 'path': self.path_filter}      def re_filter(self, conf):          return conf or self.default_pattern, None, None @@ -294,17 +275,17 @@ class Router(object):          return r'-?\d+', int, lambda x: str(int(x))      def float_filter(self, conf): -        return r'-?\d*\.\d+', float, lambda x: str(float(x)) +        return r'-?[\d.]+', float, lambda x: str(float(x))      def path_filter(self, conf): -        return r'.*?', None, None -     +        return r'.+?', None, None +      def add_filter(self, name, func):          ''' Add a filter. The provided function is called with the configuration          string as parameter and must return a (regexp, to_python, to_url) tuple.          The first element is a string, the last two are callables or None. '''          self.filters[name] = func -     +      def parse_rule(self, rule):          ''' Parses a rule into a (name, filter, conf) token stream. If mode is              None, name contains a static rule part. ''' @@ -366,8 +347,8 @@ class Router(object):          try:              re_match = re.compile('^(%s)$' % pattern).match -        except re.error, e: -            raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e)) +        except re.error: +            raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e()))          def match(path):              """ Return an url-argument dictionary. """ @@ -383,7 +364,7 @@ class Router(object):              combined = '%s|(^%s$)' % (self.dynamic[-1][0].pattern, flat_pattern)              self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])              self.dynamic[-1][1].append((match, target)) -        except (AssertionError, IndexError), e: # AssertionError: Too many groups +        except (AssertionError, IndexError): # AssertionError: Too many groups              self.dynamic.append((re.compile('(^%s$)' % flat_pattern),                                  [(match, target)]))          return match @@ -396,8 +377,8 @@ class Router(object):              for i, value in enumerate(anons): query['anon%d'%i] = value              url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder])              return url if not query else url+'?'+urlencode(query) -        except KeyError, e: -            raise RouteBuildError('Missing URL argument: %r' % e.args[0]) +        except KeyError: +            raise RouteBuildError('Missing URL argument: %r' % _e().args[0])      def match(self, environ):          ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). ''' @@ -424,9 +405,7 @@ class Router(object):          allowed = [verb for verb in targets if verb != 'ANY']          if 'GET' in allowed and 'HEAD' not in allowed:              allowed.append('HEAD') -        raise HTTPError(405, "Method not allowed.", -                        header=[('Allow',",".join(allowed))]) - +        raise HTTPError(405, "Method not allowed.", Allow=",".join(allowed))  class Route(object): @@ -435,7 +414,6 @@ class Route(object):          turing an URL path rule into a regular expression usable by the Router.      ''' -      def __init__(self, app, rule, method, callback, name=None,                   plugins=None, skiplist=None, **config):          #: The application this route is installed to. @@ -509,9 +487,12 @@ class Route(object):              except RouteReset: # Try again with changed configuration.                  return self._make_callback()              if not callback is self.callback: -                try_update_wrapper(callback, self.callback) +                update_wrapper(callback, self.callback)          return callback +    def __repr__(self): +        return '<%s %r %r>' % (self.method, self.rule, self.callback) + @@ -523,27 +504,38 @@ class Route(object):  class Bottle(object): -    """ WSGI application """ +    """ Each Bottle object represents a single, distinct web application and +        consists of routes, callbacks, plugins, resources and configuration. +        Instances are callable WSGI applications. + +        :param catchall: If true (default), handle all exceptions. Turn off to +                         let debugging middleware handle exceptions. +    """ + +    def __init__(self, catchall=True, autojson=True): +        #: If true, most exceptions are caught and returned as :exc:`HTTPError` +        self.catchall = catchall + +        #: A :class:`ResourceManager` for application files +        self.resources = ResourceManager() + +        #: A :class:`ConfigDict` for app specific configuration. +        self.config = ConfigDict() +        self.config.autojson = autojson -    def __init__(self, catchall=True, autojson=True, config=None): -        """ Create a new bottle instance. -            You usually don't do that. Use `bottle.app.push()` instead. -        """          self.routes = [] # List of installed :class:`Route` instances.          self.router = Router() # Maps requests to :class:`Route` instances. -        self.plugins = [] # List of installed plugins. -          self.error_handler = {} -        #: If true, most exceptions are catched and returned as :exc:`HTTPError` -        self.config = ConfigDict(config or {}) -        self.catchall = catchall -        #: An instance of :class:`HooksPlugin`. Empty by default. + +        # Core plugins +        self.plugins = [] # List of installed plugins.          self.hooks = HooksPlugin()          self.install(self.hooks) -        if autojson: +        if self.config.autojson:              self.install(JSONPlugin())          self.install(TemplatePlugin()) +      def mount(self, prefix, app, **options):          ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific              URL prefix. Example:: @@ -560,14 +552,11 @@ class Bottle(object):              prefix, app = app, prefix              depr('Parameter order of Bottle.mount() changed.') # 0.10 -        parts = filter(None, prefix.split('/')) -        if not parts: raise ValueError('Empty path prefix.') -        path_depth = len(parts) -        options.setdefault('skip', True) -        options.setdefault('method', 'ANY') +        segments = [p for p in prefix.split('/') if p] +        if not segments: raise ValueError('Empty path prefix.') +        path_depth = len(segments) -        @self.route('/%s/:#.*#' % '/'.join(parts), **options) -        def mountpoint(): +        def mountpoint_wrapper():              try:                  request.path_shift(path_depth)                  rs = BaseResponse([], 200) @@ -575,13 +564,30 @@ class Bottle(object):                      rs.status = status                      for name, value in header: rs.add_header(name, value)                      return rs.body.append -                rs.body = itertools.chain(rs.body, app(request.environ, start_response)) -                return HTTPResponse(rs.body, rs.status, rs.headers) +                body = app(request.environ, start_response) +                body = itertools.chain(rs.body, body) +                return HTTPResponse(body, rs.status_code, **rs.headers)              finally:                  request.path_shift(-path_depth) +        options.setdefault('skip', True) +        options.setdefault('method', 'ANY') +        options.setdefault('mountpoint', {'prefix': prefix, 'target': app}) +        options['callback'] = mountpoint_wrapper + +        self.route('/%s/<:re:.*>' % '/'.join(segments), **options)          if not prefix.endswith('/'): -            self.route('/' + '/'.join(parts), callback=mountpoint, **options) +            self.route('/' + '/'.join(segments), **options) + +    def merge(self, routes): +        ''' Merge the routes of another :class:`Bottle` application or a list of +            :class:`Route` objects into this application. The routes keep their +            'owner', meaning that the :data:`Route.app` attribute is not +            changed. ''' +        if isinstance(routes, Bottle): +            routes = routes.routes +        for route in routes: +            self.add_route(route)      def install(self, plugin):          ''' Add a plugin to the list of plugins and prepare it for being @@ -610,6 +616,10 @@ class Bottle(object):          if removed: self.reset()          return removed +    def run(self, **kwargs): +        ''' Calls :func:`run` with the same parameters. ''' +        run(self, **kwargs) +      def reset(self, route=None):          ''' Reset all routes (force plugins to be re-applied) and clear all              caches. If an ID or route object is given, only that specific route @@ -640,6 +650,13 @@ class Bottle(object):          location = self.router.build(routename, **kargs).lstrip('/')          return urljoin(urljoin('/', scriptname), location) +    def add_route(self, route): +        ''' Add a route object, but do not change the :data:`Route.app` +            attribute.''' +        self.routes.append(route) +        self.router.add(route.rule, route.method, route, name=route.name) +        if DEBUG: route.prepare() +      def route(self, path=None, method='GET', callback=None, name=None,                apply=None, skip=None, **config):          """ A decorator to bind a function to a request URL. Example:: @@ -678,9 +695,7 @@ class Bottle(object):                      verb = verb.upper()                      route = Route(self, rule, verb, callback, name=name,                                    plugins=plugins, skiplist=skiplist, **config) -                    self.routes.append(route) -                    self.router.add(rule, verb, route, name=name) -                    if DEBUG: route.prepare() +                    self.add_route(route)              return callback          return decorator(callback) if callback else decorator @@ -708,7 +723,13 @@ class Bottle(object):          return wrapper      def hook(self, name): -        """ Return a decorator that attaches a callback to a hook. """ +        """ Return a decorator that attaches a callback to a hook. Three hooks +            are currently implemented: + +            - before_request: Executed once before each request +            - after_request: Executed once after each request +            - app_reset: Called whenever :meth:`reset` is called. +        """          def wrapper(func):              self.hooks.add(name, func)              return func @@ -716,8 +737,8 @@ class Bottle(object):      def handle(self, path, method='GET'):          """ (deprecated) Execute the first matching route callback and return -            the result. :exc:`HTTPResponse` exceptions are catched and returned. -            If :attr:`Bottle.catchall` is true, other exceptions are catched as +            the result. :exc:`HTTPResponse` exceptions are caught and returned. +            If :attr:`Bottle.catchall` is true, other exceptions are caught as              well and returned as :exc:`HTTPError` instances (500).          """          depr("This method will change semantics in 0.10. Try to avoid it.") @@ -725,26 +746,33 @@ class Bottle(object):              return self._handle(path)          return self._handle({'PATH_INFO': path, 'REQUEST_METHOD': method.upper()}) +    def default_error_handler(self, res): +        return tob(template(ERROR_PAGE_TEMPLATE, e=res)) +      def _handle(self, environ):          try: +            environ['bottle.app'] = self +            request.bind(environ) +            response.bind()              route, args = self.router.match(environ) -            environ['route.handle'] = environ['bottle.route'] = route +            environ['route.handle'] = route +            environ['bottle.route'] = route              environ['route.url_args'] = args              return route.call(**args) -        except HTTPResponse, r: -            return r +        except HTTPResponse: +            return _e()          except RouteReset:              route.reset()              return self._handle(environ)          except (KeyboardInterrupt, SystemExit, MemoryError):              raise -        except Exception, e: +        except Exception:              if not self.catchall: raise -            stacktrace = format_exc(10) +            stacktrace = format_exc()              environ['wsgi.errors'].write(stacktrace) -            return HTTPError(500, "Internal Server Error", e, stacktrace) +            return HTTPError(500, "Internal Server Error", _e(), stacktrace) -    def _cast(self, out, request, response, peek=None): +    def _cast(self, out, peek=None):          """ Try to convert the parameter into something WSGI compatible and set          correct HTTP headers when possible.          Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, @@ -753,7 +781,8 @@ class Bottle(object):          # Empty output is done here          if not out: -            response['Content-Length'] = 0 +            if 'Content-Length' not in response: +                response['Content-Length'] = 0              return []          # Join lists of byte or unicode strings. Mixed lists are NOT supported          if isinstance(out, (tuple, list))\ @@ -764,19 +793,18 @@ class Bottle(object):              out = out.encode(response.charset)          # Byte Strings are just returned          if isinstance(out, bytes): -            response['Content-Length'] = len(out) +            if 'Content-Length' not in response: +                response['Content-Length'] = len(out)              return [out]          # HTTPError or HTTPException (recursive, because they may wrap anything)          # TODO: Handle these explicitly in handle() or make them iterable.          if isinstance(out, HTTPError):              out.apply(response) -            out = self.error_handler.get(out.status, repr)(out) -            if isinstance(out, HTTPResponse): -                depr('Error handlers must not return :exc:`HTTPResponse`.') #0.9 -            return self._cast(out, request, response) +            out = self.error_handler.get(out.status_code, self.default_error_handler)(out) +            return self._cast(out)          if isinstance(out, HTTPResponse):              out.apply(response) -            return self._cast(out.output, request, response) +            return self._cast(out.body)          # File-like objects.          if hasattr(out, 'read'): @@ -788,54 +816,54 @@ class Bottle(object):          # Handle Iterables. We peek into them to detect their inner type.          try:              out = iter(out) -            first = out.next() +            first = next(out)              while not first: -                first = out.next() +                first = next(out)          except StopIteration: -            return self._cast('', request, response) -        except HTTPResponse, e: -            first = e -        except Exception, e: -            first = HTTPError(500, 'Unhandled exception', e, format_exc(10)) -            if isinstance(e, (KeyboardInterrupt, SystemExit, MemoryError))\ -            or not self.catchall: -                raise +            return self._cast('') +        except HTTPResponse: +            first = _e() +        except (KeyboardInterrupt, SystemExit, MemoryError): +            raise +        except Exception: +            if not self.catchall: raise +            first = HTTPError(500, 'Unhandled exception', _e(), format_exc()) +          # These are the inner types allowed in iterator or generator objects.          if isinstance(first, HTTPResponse): -            return self._cast(first, request, response) +            return self._cast(first)          if isinstance(first, bytes):              return itertools.chain([first], out)          if isinstance(first, unicode): -            return itertools.imap(lambda x: x.encode(response.charset), +            return imap(lambda x: x.encode(response.charset),                                    itertools.chain([first], out))          return self._cast(HTTPError(500, 'Unsupported response type: %s'\ -                                         % type(first)), request, response) +                                         % type(first)))      def wsgi(self, environ, start_response):          """ The bottle WSGI-interface. """          try: -            environ['bottle.app'] = self -            request.bind(environ) -            response.bind() -            out = self._cast(self._handle(environ), request, response) +            out = self._cast(self._handle(environ))              # rfc2616 section 4.3              if response._status_code in (100, 101, 204, 304)\ -            or request.method == 'HEAD': +            or environ['REQUEST_METHOD'] == 'HEAD':                  if hasattr(out, 'close'): out.close()                  out = [] -            start_response(response._status_line, list(response.iter_headers())) +            start_response(response._status_line, response.headerlist)              return out          except (KeyboardInterrupt, SystemExit, MemoryError):              raise -        except Exception, e: +        except Exception:              if not self.catchall: raise              err = '<h1>Critical error while processing request: %s</h1>' \ -                  % environ.get('PATH_INFO', '/') +                  % html_escape(environ.get('PATH_INFO', '/'))              if DEBUG: -                err += '<h2>Error:</h2>\n<pre>%s</pre>\n' % repr(e) -                err += '<h2>Traceback:</h2>\n<pre>%s</pre>\n' % format_exc(10) -            environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html -            start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')]) +                err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \ +                       '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \ +                       % (html_escape(repr(_e())), html_escape(format_exc())) +            environ['wsgi.errors'].write(err) +            headers = [('Content-Type', 'text/html; charset=UTF-8')] +            start_response('500 INTERNAL SERVER ERROR', headers)              return [tob(err)]      def __call__(self, environ, start_response): @@ -852,19 +880,33 @@ class Bottle(object):  ############################################################################### -class BaseRequest(DictMixin): +class BaseRequest(object):      """ A wrapper for WSGI environment dictionaries that adds a lot of -        convenient access methods and properties. Most of them are read-only.""" +        convenient access methods and properties. Most of them are read-only. + +        Adding new attributes to a request actually adds them to the environ +        dictionary (as 'bottle.request.ext.<name>'). This is the recommended +        way to store and access request-specific data. +    """ + +    __slots__ = ('environ')      #: Maximum size of memory buffer for :attr:`body` in bytes.      MEMFILE_MAX = 102400 +    #: Maximum number pr GET or POST parameters per request +    MAX_PARAMS  = 100 -    def __init__(self, environ): +    def __init__(self, environ=None):          """ Wrap a WSGI environ dictionary. """          #: The wrapped WSGI environ dictionary. This is the only real attribute.          #: All other attributes actually are read-only properties. -        self.environ = environ -        environ['bottle.request'] = self +        self.environ = {} if environ is None else environ +        self.environ['bottle.request'] = self + +    @DictProperty('environ', 'bottle.app', read_only=True) +    def app(self): +        ''' Bottle application handling this request. ''' +        raise RuntimeError('This request is not connected to an application.')      @property      def path(self): @@ -892,7 +934,8 @@ class BaseRequest(DictMixin):          """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT              decoded. Use :meth:`get_cookie` if you expect signed cookies. """          cookies = SimpleCookie(self.environ.get('HTTP_COOKIE','')) -        return FormsDict((c.key, c.value) for c in cookies.itervalues()) +        cookies = list(cookies.values())[:self.MAX_PARAMS] +        return FormsDict((c.key, c.value) for c in cookies)      def get_cookie(self, key, default=None, secret=None):          """ Return the content of a cookie. To read a `Signed Cookie`, the @@ -911,11 +954,10 @@ class BaseRequest(DictMixin):              values are sometimes called "URL arguments" or "GET parameters", but              not to be confused with "URL wildcards" as they are provided by the              :class:`Router`. ''' -        data = parse_qs(self.query_string, keep_blank_values=True)          get = self.environ['bottle.get'] = FormsDict() -        for key, values in data.iteritems(): -            for value in values: -                get[key] = value +        pairs = _parse_qsl(self.environ.get('QUERY_STRING', '')) +        for key, value in pairs[:self.MAX_PARAMS]: +            get[key] = value          return get      @DictProperty('environ', 'bottle.request.forms', read_only=True) @@ -925,7 +967,7 @@ class BaseRequest(DictMixin):              :class:`FormsDict`. All keys and values are strings. File uploads              are stored separately in :attr:`files`. """          forms = FormsDict() -        for name, item in self.POST.iterallitems(): +        for name, item in self.POST.allitems():              if not hasattr(item, 'filename'):                  forms[name] = item          return forms @@ -935,9 +977,9 @@ class BaseRequest(DictMixin):          """ A :class:`FormsDict` with the combined values of :attr:`query` and              :attr:`forms`. File uploads are stored in :attr:`files`. """          params = FormsDict() -        for key, value in self.query.iterallitems(): +        for key, value in self.query.allitems():              params[key] = value -        for key, value in self.forms.iterallitems(): +        for key, value in self.forms.allitems():              params[key] = value          return params @@ -959,7 +1001,7 @@ class BaseRequest(DictMixin):                  on big files.          """          files = FormsDict() -        for name, item in self.POST.iterallitems(): +        for name, item in self.POST.allitems():              if hasattr(item, 'filename'):                  files[name] = item          return files @@ -1009,15 +1051,26 @@ class BaseRequest(DictMixin):              instances of :class:`cgi.FieldStorage` (file uploads).          """          post = FormsDict() +        # We default to application/x-www-form-urlencoded for everything that +        # is not multipart and take the fast path (also: 3.1 workaround) +        if not self.content_type.startswith('multipart/'): +            maxlen = max(0, min(self.content_length, self.MEMFILE_MAX)) +            pairs = _parse_qsl(tonat(self.body.read(maxlen), 'latin1')) +            for key, value in pairs[:self.MAX_PARAMS]: +                post[key] = value +            return post +          safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi          for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):              if key in self.environ: safe_env[key] = self.environ[key] -        if NCTextIOWrapper: -            fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n') -        else: -            fb = self.body -        data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True) -        for item in data.list or []: +        args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) +        if py31: +            args['fp'] = NCTextIOWrapper(args['fp'], encoding='ISO-8859-1', +                                         newline='\n') +        elif py3k: +            args['encoding'] = 'ISO-8859-1' +        data = FieldStorage(**args) +        for item in (data.list or [])[:self.MAX_PARAMS]:              post[item.name] = item if item.filename else item.value          return post @@ -1042,7 +1095,7 @@ class BaseRequest(DictMixin):              but the fragment is always empty because it is not visible to the              server. '''          env = self.environ -        http = env.get('wsgi.url_scheme', 'http') +        http = env.get('HTTP_X_FORWARDED_PROTO') or env.get('wsgi.url_scheme', 'http')          host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')          if not host:              # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. @@ -1091,6 +1144,11 @@ class BaseRequest(DictMixin):          return int(self.environ.get('CONTENT_LENGTH') or -1)      @property +    def content_type(self): +        ''' The Content-Type header as a lowercase-string (default: empty). ''' +        return self.environ.get('CONTENT_TYPE', '').lower() + +    @property      def is_xhr(self):          ''' True if the request was triggered by a XMLHttpRequest. This only              works with JavaScript libraries that support the `X-Requested-With` @@ -1139,6 +1197,7 @@ class BaseRequest(DictMixin):          """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """          return Request(self.environ.copy()) +    def get(self, value, default=None): return self.environ.get(value, default)      def __getitem__(self, key): return self.environ[key]      def __delitem__(self, key): self[key] = ""; del(self.environ[key])      def __iter__(self): return iter(self.environ) @@ -1166,27 +1225,41 @@ class BaseRequest(DictMixin):      def __repr__(self):          return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) +    def __getattr__(self, name): +        ''' Search in self.environ for additional user defined attributes. ''' +        try: +            var = self.environ['bottle.request.ext.%s'%name] +            return var.__get__(self) if hasattr(var, '__get__') else var +        except KeyError: +            raise AttributeError('Attribute %r not defined.' % name) + +    def __setattr__(self, name, value): +        if name == 'environ': return object.__setattr__(self, name, value) +        self.environ['bottle.request.ext.%s'%name] = value + + + +  def _hkey(s):      return s.title().replace('_','-')  class HeaderProperty(object):      def __init__(self, name, reader=None, writer=str, default=''): -        self.name, self.reader, self.writer, self.default = name, reader, writer, default +        self.name, self.default = name, default +        self.reader, self.writer = reader, writer          self.__doc__ = 'Current value of the %r header.' % name.title()      def __get__(self, obj, cls):          if obj is None: return self -        value = obj.headers.get(self.name) -        return self.reader(value) if (value and self.reader) else (value or self.default) +        value = obj.headers.get(self.name, self.default) +        return self.reader(value) if self.reader else value      def __set__(self, obj, value): -        if self.writer: value = self.writer(value) -        obj.headers[self.name] = value +        obj.headers[self.name] = self.writer(value)      def __delete__(self, obj): -        if self.name in obj.headers: -            del obj.headers[self.name] +        del obj.headers[self.name]  class BaseResponse(object): @@ -1209,11 +1282,9 @@ class BaseResponse(object):                    'Content-Md5', 'Last-Modified'))}      def __init__(self, body='', status=None, **headers): -        self._status_line = None -        self._status_code = None -        self.body = body          self._cookies = None          self._headers = {'Content-Type': [self.default_content_type]} +        self.body = body          self.status = status or self.default_status          if headers:              for name, value in headers.items(): @@ -1253,26 +1324,24 @@ class BaseResponse(object):              raise ValueError('String status line without a reason phrase.')          if not 100 <= code <= 999: raise ValueError('Status code out of range.')          self._status_code = code -        self._status_line = status or ('%d Unknown' % code) +        self._status_line = str(status or ('%d Unknown' % code))      def _get_status(self): -        depr('BaseReuqest.status will change to return a string in 0.11. Use'\ -             ' status_line and status_code to make sure.') #0.10 -        return self._status_code +        return self._status_line      status = property(_get_status, _set_status, None,          ''' A writeable property to change the HTTP response status. It accepts              either a numeric code (100-999) or a string with a custom reason              phrase (e.g. "404 Brain not found"). Both :data:`status_line` and -            :data:`status_code` are updates accordingly. The return value is -            always a numeric code. ''') +            :data:`status_code` are updated accordingly. The return value is +            always a status string. ''')      del _get_status, _set_status      @property      def headers(self):          ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like              view on the response headers. ''' -        self.__dict__['headers'] = hdict = HeaderDict() +        hdict = HeaderDict()          hdict.dict = self._headers          return hdict @@ -1286,13 +1355,10 @@ class BaseResponse(object):              header with that name, return a default value. '''          return self._headers.get(_hkey(name), [default])[-1] -    def set_header(self, name, value, append=False): +    def set_header(self, name, value):          ''' Create a new response header, replacing any previously defined              headers with the same name. ''' -        if append: -            self.add_header(name, value) -        else: -            self._headers[_hkey(name)] = [str(value)] +        self._headers[_hkey(name)] = [str(value)]      def add_header(self, name, value):          ''' Add an additional response header, not removing duplicates. ''' @@ -1301,16 +1367,7 @@ class BaseResponse(object):      def iter_headers(self):          ''' Yield (header, value) tuples, skipping headers that are not              allowed with the current response status code. ''' -        headers = self._headers.iteritems() -        bad_headers = self.bad_headers.get(self.status_code) -        if bad_headers: -            headers = [h for h in headers if h[0] not in bad_headers] -        for name, values in headers: -            for value in values: -                yield name, value -        if self._cookies: -            for c in self._cookies.values(): -                yield 'Set-Cookie', c.OutputString() +        return self.headerlist      def wsgiheader(self):          depr('The wsgiheader method is deprecated. See headerlist.') #0.10 @@ -1319,7 +1376,16 @@ class BaseResponse(object):      @property      def headerlist(self):          ''' WSGI conform list of (header, value) tuples. ''' -        return list(self.iter_headers()) +        out = [] +        headers = self._headers.items() +        if self._status_code in self.bad_headers: +            bad_headers = self.bad_headers[self._status_code] +            headers = [h for h in headers if h[0] not in bad_headers] +        out += [(name, val) for name, vals in headers for val in vals] +        if self._cookies: +            for c in self._cookies.values(): +                out.append(('Set-Cookie', c.OutputString())) +        return out      content_type = HeaderProperty('Content-Type')      content_length = HeaderProperty('Content-Length', reader=int) @@ -1384,7 +1450,7 @@ class BaseResponse(object):          if len(value) > 4096: raise ValueError('Cookie value to long.')          self._cookies[name] = value -        for key, value in options.iteritems(): +        for key, value in options.items():              if key == 'max_age':                  if isinstance(value, timedelta):                      value = value.seconds + value.days * 24 * 3600 @@ -1409,20 +1475,76 @@ class BaseResponse(object):              out += '%s: %s\n' % (name.title(), value.strip())          return out +#: Thread-local storage for :class:`LocalRequest` and :class:`LocalResponse` +#: attributes. +_lctx = threading.local() -class LocalRequest(BaseRequest, threading.local): -    ''' A thread-local subclass of :class:`BaseRequest`. ''' -    def __init__(self): pass +def local_property(name): +    def fget(self): +        try: +            return getattr(_lctx, name) +        except AttributeError: +            raise RuntimeError("Request context not initialized.") +    def fset(self, value): setattr(_lctx, name, value) +    def fdel(self): delattr(_lctx, name) +    return property(fget, fset, fdel, +        'Thread-local property stored in :data:`_lctx.%s`' % name) + + +class LocalRequest(BaseRequest): +    ''' A thread-local subclass of :class:`BaseRequest` with a different +        set of attribues for each thread. There is usually only one global +        instance of this class (:data:`request`). If accessed during a +        request/response cycle, this instance always refers to the *current* +        request (even on a multithreaded server). '''      bind = BaseRequest.__init__ +    environ = local_property('request_environ') -class LocalResponse(BaseResponse, threading.local): -    ''' A thread-local subclass of :class:`BaseResponse`. ''' +class LocalResponse(BaseResponse): +    ''' A thread-local subclass of :class:`BaseResponse` with a different +        set of attribues for each thread. There is usually only one global +        instance of this class (:data:`response`). Its attributes are used +        to build the HTTP response at the end of the request/response cycle. +    '''      bind = BaseResponse.__init__ +    _status_line = local_property('response_status_line') +    _status_code = local_property('response_status_code') +    _cookies     = local_property('response_cookies') +    _headers     = local_property('response_headers') +    body         = local_property('response_body') + +Request = BaseRequest +Response = BaseResponse + +class HTTPResponse(Response, BottleException): +    def __init__(self, body='', status=None, header=None, **headers): +        if header or 'output' in headers: +            depr('Call signature changed (for the better)') +            if header: headers.update(header) +            if 'output' in headers: body = headers.pop('output') +        super(HTTPResponse, self).__init__(body, status, **headers) + +    def apply(self, response): +        response._status_code = self._status_code +        response._status_line = self._status_line +        response._headers = self._headers +        response._cookies = self._cookies +        response.body = self.body -Response = LocalResponse # BC 0.9 -Request  = LocalRequest  # BC 0.9 +    def _output(self, value=None): +        depr('Use HTTPResponse.body instead of HTTPResponse.output') +        if value is None: return self.body +        self.body = value +    output = property(_output, _output, doc='Alias for .body') + +class HTTPError(HTTPResponse): +    default_status = 500 +    def __init__(self, status=None, body=None, exception=None, traceback=None, header=None, **headers): +        self.exception = exception +        self.traceback = traceback +        super(HTTPError, self).__init__(body, status, header, **headers) @@ -1441,7 +1563,7 @@ class JSONPlugin(object):      def __init__(self, json_dumps=json_dumps):          self.json_dumps = json_dumps -    def apply(self, callback, context): +    def apply(self, callback, route):          dumps = self.json_dumps          if not dumps: return callback          def wrapper(*a, **ka): @@ -1491,7 +1613,7 @@ class HooksPlugin(object):          if ka.pop('reversed', False): hooks = hooks[::-1]          return [hook(*a, **ka) for hook in hooks] -    def apply(self, callback, context): +    def apply(self, callback, route):          if self._empty(): return callback          def wrapper(*a, **ka):              self.trigger('before_request') @@ -1566,26 +1688,37 @@ class MultiDict(DictMixin):      """      def __init__(self, *a, **k): -        self.dict = dict((k, [v]) for k, v in dict(*a, **k).iteritems()) +        self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) +      def __len__(self): return len(self.dict)      def __iter__(self): return iter(self.dict)      def __contains__(self, key): return key in self.dict      def __delitem__(self, key): del self.dict[key]      def __getitem__(self, key): return self.dict[key][-1]      def __setitem__(self, key, value): self.append(key, value) -    def iterkeys(self): return self.dict.iterkeys() -    def itervalues(self): return (v[-1] for v in self.dict.itervalues()) -    def iteritems(self): return ((k, v[-1]) for (k, v) in self.dict.iteritems()) -    def iterallitems(self): -        for key, values in self.dict.iteritems(): -            for value in values: -                yield key, value - -    # 2to3 is not able to fix these automatically. -    keys     = iterkeys     if py3k else lambda self: list(self.iterkeys()) -    values   = itervalues   if py3k else lambda self: list(self.itervalues()) -    items    = iteritems    if py3k else lambda self: list(self.iteritems()) -    allitems = iterallitems if py3k else lambda self: list(self.iterallitems()) +    def keys(self): return self.dict.keys() + +    if py3k: +        def values(self): return (v[-1] for v in self.dict.values()) +        def items(self): return ((k, v[-1]) for k, v in self.dict.items()) +        def allitems(self): +            return ((k, v) for k, vl in self.dict.items() for v in vl) +        iterkeys = keys +        itervalues = values +        iteritems = items +        iterallitems = allitems + +    else: +        def values(self): return [v[-1] for v in self.dict.values()] +        def items(self): return [(k, v[-1]) for k, v in self.dict.items()] +        def iterkeys(self): return self.dict.iterkeys() +        def itervalues(self): return (v[-1] for v in self.dict.itervalues()) +        def iteritems(self): +            return ((k, v[-1]) for k, v in self.dict.iteritems()) +        def iterallitems(self): +            return ((k, v) for k, vl in self.dict.iteritems() for v in vl) +        def allitems(self): +            return [(k, v) for k, vl in self.dict.iteritems() for v in vl]      def get(self, key, default=None, index=-1, type=None):          ''' Return the most recent value for a key. @@ -1600,7 +1733,7 @@ class MultiDict(DictMixin):          try:              val = self.dict[key][index]              return type(val) if type else val -        except Exception, e: +        except Exception:              pass          return default @@ -1626,25 +1759,45 @@ class FormsDict(MultiDict):      ''' This :class:`MultiDict` subclass is used to store request form data.          Additionally to the normal dict-like item access methods (which return          unmodified data as native strings), this container also supports -        attribute-like access to its values. Attribues are automatiically de- or -        recoded to match :attr:`input_encoding` (default: 'utf8'). Missing +        attribute-like access to its values. Attributes are automatically de- +        or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing          attributes default to an empty string. '''      #: Encoding used for attribute values.      input_encoding = 'utf8' +    #: If true (default), unicode strings are first encoded with `latin1` +    #: and then decoded to match :attr:`input_encoding`. +    recode_unicode = True + +    def _fix(self, s, encoding=None): +        if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI +            s = s.encode('latin1') +        if isinstance(s, bytes): # Python 2 WSGI +            return s.decode(encoding or self.input_encoding) +        return s + +    def decode(self, encoding=None): +        ''' Returns a copy with all keys and values de- or recoded to match +            :attr:`input_encoding`. Some libraries (e.g. WTForms) want a +            unicode dictionary. ''' +        copy = FormsDict() +        enc = copy.input_encoding = encoding or self.input_encoding +        copy.recode_unicode = False +        for key, value in self.allitems(): +            copy.append(self._fix(key, enc), self._fix(value, enc)) +        return copy      def getunicode(self, name, default=None, encoding=None): -        value, enc = self.get(name, default), encoding or self.input_encoding          try: -            if isinstance(value, bytes): # Python 2 WSGI -                return value.decode(enc) -            elif isinstance(value, unicode): # Python 3 WSGI -                return value.encode('latin1').decode(enc) -            return value -        except UnicodeError, e: +            return self._fix(self[name], encoding) +        except (UnicodeError, KeyError):              return default -    def __getattr__(self, name): return self.getunicode(name, default=u'') +    def __getattr__(self, name, default=unicode()): +        # Without this guard, pickle generates a cryptic TypeError: +        if name.startswith('__') and name.endswith('__'): +            return super(FormsDict, self).__getattr__(name) +        return self.getunicode(name, default=default)  class HeaderDict(MultiDict): @@ -1666,7 +1819,7 @@ class HeaderDict(MultiDict):      def get(self, key, default=None, index=-1):          return MultiDict.get(self, _hkey(key), default, index)      def filter(self, names): -        for name in map(_hkey, names): +        for name in [_hkey(n) for n in names]:              if name in self.dict:                  del self.dict[name] @@ -1682,7 +1835,7 @@ class WSGIHeaderDict(DictMixin):          Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one          that uses non-native strings.)      ''' -    #: List of keys that do not have a 'HTTP_' prefix. +    #: List of keys that do not have a ``HTTP_`` prefix.      cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH')      def __init__(self, environ): @@ -1749,7 +1902,7 @@ class ConfigDict(dict):          if key in self: del self[key]      def __call__(self, *a, **ka): -        for key, value in dict(*a, **ka).iteritems(): setattr(self, key, value) +        for key, value in dict(*a, **ka).items(): setattr(self, key, value)          return self @@ -1770,17 +1923,103 @@ class AppStack(list):  class WSGIFileWrapper(object): -   def __init__(self, fp, buffer_size=1024*64): -       self.fp, self.buffer_size = fp, buffer_size -       for attr in ('fileno', 'close', 'read', 'readlines'): -           if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) +    def __init__(self, fp, buffer_size=1024*64): +        self.fp, self.buffer_size = fp, buffer_size +        for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'): +            if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) + +    def __iter__(self): +        buff, read = self.buffer_size, self.read +        while True: +            part = read(buff) +            if not part: return +            yield part + + +class ResourceManager(object): +    ''' This class manages a list of search paths and helps to find and open +        application-bound resources (files). + +        :param base: default value for :meth:`add_path` calls. +        :param opener: callable used to open resources. +        :param cachemode: controls which lookups are cached. One of 'all', +                         'found' or 'none'. +    ''' + +    def __init__(self, base='./', opener=open, cachemode='all'): +        self.opener = open +        self.base = base +        self.cachemode = cachemode -   def __iter__(self): -       read, buff = self.fp.read, self.buffer_size -       while True: -           part = read(buff) -           if not part: break -           yield part +        #: A list of search paths. See :meth:`add_path` for details. +        self.path = [] +        #: A cache for resolved paths. ``res.cache.clear()`` clears the cache. +        self.cache = {} + +    def add_path(self, path, base=None, index=None, create=False): +        ''' Add a new path to the list of search paths. Return False if the +            path does not exist. + +            :param path: The new search path. Relative paths are turned into +                an absolute and normalized form. If the path looks like a file +                (not ending in `/`), the filename is stripped off. +            :param base: Path used to absolutize relative search paths. +                Defaults to :attr:`base` which defaults to ``os.getcwd()``. +            :param index: Position within the list of search paths. Defaults +                to last index (appends to the list). + +            The `base` parameter makes it easy to reference files installed +            along with a python module or package:: + +                res.add_path('./resources/', __file__) +        ''' +        base = os.path.abspath(os.path.dirname(base or self.base)) +        path = os.path.abspath(os.path.join(base, os.path.dirname(path))) +        path += os.sep +        if path in self.path: +            self.path.remove(path) +        if create and not os.path.isdir(path): +            os.makedirs(path) +        if index is None: +            self.path.append(path) +        else: +            self.path.insert(index, path) +        self.cache.clear() +        return os.path.exists(path) + +    def __iter__(self): +        ''' Iterate over all existing files in all registered paths. ''' +        search = self.path[:] +        while search: +            path = search.pop() +            if not os.path.isdir(path): continue +            for name in os.listdir(path): +                full = os.path.join(path, name) +                if os.path.isdir(full): search.append(full) +                else: yield full + +    def lookup(self, name): +        ''' Search for a resource and return an absolute file path, or `None`. + +            The :attr:`path` list is searched in order. The first match is +            returend. Symlinks are followed. The result is cached to speed up +            future lookups. ''' +        if name not in self.cache or DEBUG: +            for path in self.path: +                fpath = os.path.join(path, name) +                if os.path.isfile(fpath): +                    if self.cachemode in ('all', 'found'): +                        self.cache[name] = fpath +                    return fpath +            if self.cachemode == 'all': +                self.cache[name] = None +        return self.cache[name] + +    def open(self, name, mode='r', *args, **kwargs): +        ''' Find a resource and return a file object, or raise IOError. ''' +        fname = self.lookup(name) +        if not fname: raise IOError("Resource %r not found." % name) +        return self.opener(name, mode=mode, *args, **kwargs) @@ -1803,7 +2042,20 @@ def redirect(url, code=None):      if code is None:          code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302      location = urljoin(request.url, url) -    raise HTTPResponse("", status=code, header=dict(Location=location)) +    res = HTTPResponse("", status=code, Location=location) +    if response._cookies: +        res._cookies = response._cookies +    raise res + + +def _file_iter_range(fp, offset, bytes, maxread=1024*1024): +    ''' Yield chunks from a range in a file. No chunk is bigger than maxread.''' +    fp.seek(offset) +    while bytes > 0: +        part = fp.read(min(bytes, maxread)) +        if not part: break +        bytes -= len(part) +        yield part  def static_file(filename, root, mimetype='auto', download=False): @@ -1814,7 +2066,7 @@ def static_file(filename, root, mimetype='auto', download=False):      """      root = os.path.abspath(root) + os.sep      filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) -    header = dict() +    headers = dict()      if not filename.startswith(root):          return HTTPError(403, "Access denied.") @@ -1825,29 +2077,41 @@ def static_file(filename, root, mimetype='auto', download=False):      if mimetype == 'auto':          mimetype, encoding = mimetypes.guess_type(filename) -        if mimetype: header['Content-Type'] = mimetype -        if encoding: header['Content-Encoding'] = encoding +        if mimetype: headers['Content-Type'] = mimetype +        if encoding: headers['Content-Encoding'] = encoding      elif mimetype: -        header['Content-Type'] = mimetype +        headers['Content-Type'] = mimetype      if download:          download = os.path.basename(filename if download == True else download) -        header['Content-Disposition'] = 'attachment; filename="%s"' % download +        headers['Content-Disposition'] = 'attachment; filename="%s"' % download      stats = os.stat(filename) -    header['Content-Length'] = stats.st_size +    headers['Content-Length'] = clen = stats.st_size      lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) -    header['Last-Modified'] = lm +    headers['Last-Modified'] = lm      ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')      if ims:          ims = parse_date(ims.split(";")[0].strip())      if ims is not None and ims >= int(stats.st_mtime): -        header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) -        return HTTPResponse(status=304, header=header) +        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) +        return HTTPResponse(status=304, **headers)      body = '' if request.method == 'HEAD' else open(filename, 'rb') -    return HTTPResponse(body, header=header) + +    headers["Accept-Ranges"] = "bytes" +    ranges = request.environ.get('HTTP_RANGE') +    if 'HTTP_RANGE' in request.environ: +        ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen)) +        if not ranges: +            return HTTPError(416, "Requested Range Not Satisfiable") +        offset, end = ranges[0] +        headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen) +        headers["Content-Length"] = str(end-offset) +        if body: body = _file_iter_range(body, offset, end-offset) +        return HTTPResponse(body, status=206, **headers) +    return HTTPResponse(body, **headers) @@ -1880,15 +2144,42 @@ def parse_auth(header):      try:          method, data = header.split(None, 1)          if method.lower() == 'basic': -            #TODO: Add 2to3 save base64[encode/decode] functions.              user, pwd = touni(base64.b64decode(tob(data))).split(':',1)              return user, pwd      except (KeyError, ValueError):          return None +def parse_range_header(header, maxlen=0): +    ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip +        unsatisfiable ranges. The end index is non-inclusive.''' +    if not header or header[:6] != 'bytes=': return +    ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] +    for start, end in ranges: +        try: +            if not start:  # bytes=-100    -> last 100 bytes +                start, end = max(0, maxlen-int(end)), maxlen +            elif not end:  # bytes=100-    -> all but the first 99 bytes +                start, end = int(start), maxlen +            else:          # bytes=100-200 -> bytes 100-200 (inclusive) +                start, end = int(start), min(int(end)+1, maxlen) +            if 0 <= start < end <= maxlen: +                yield start, end +        except ValueError: +            pass + +def _parse_qsl(qs): +    r = [] +    for pair in qs.replace(';','&').split('&'): +        if not pair: continue +        nv = pair.split('=', 1) +        if len(nv) != 2: nv.append('') +        key = urlunquote(nv[0].replace('+', ' ')) +        value = urlunquote(nv[1].replace('+', ' ')) +        r.append((key, value)) +    return r  def _lscmp(a, b): -    ''' Compares two strings in a cryptographically save way: +    ''' Compares two strings in a cryptographically safe way:          Runtime is not affected by length of common prefix. '''      return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b) @@ -1988,7 +2279,7 @@ def validate(**vkargs):      def decorator(func):          @functools.wraps(func)          def wrapper(*args, **kargs): -            for key, value in vkargs.iteritems(): +            for key, value in vkargs.items():                  if key not in kargs:                      abort(403, 'Missing parameter: %s' % key)                  try: @@ -2014,6 +2305,9 @@ def auth_basic(check, realm="private", text="Access denied"):      return decorator +# Shortcuts for common Bottle methods. +# They all refer to the current default application. +  def make_default_app_wrapper(name):      ''' Return a callable that relays calls to the current default app. '''      @functools.wraps(getattr(Bottle, name)) @@ -2021,12 +2315,18 @@ def make_default_app_wrapper(name):          return getattr(app(), name)(*a, **ka)      return wrapper +route     = make_default_app_wrapper('route') +get       = make_default_app_wrapper('get') +post      = make_default_app_wrapper('post') +put       = make_default_app_wrapper('put') +delete    = make_default_app_wrapper('delete') +error     = make_default_app_wrapper('error') +mount     = make_default_app_wrapper('mount') +hook      = make_default_app_wrapper('hook') +install   = make_default_app_wrapper('install') +uninstall = make_default_app_wrapper('uninstall') +url       = make_default_app_wrapper('get_url') -for name in '''route get post put delete error mount -               hook install uninstall'''.split(): -    globals()[name] = make_default_app_wrapper(name) -url = make_default_app_wrapper('get_url') -del name @@ -2091,6 +2391,12 @@ class CherryPyServer(ServerAdapter):              server.stop() +class WaitressServer(ServerAdapter): +    def run(self, handler): +        from waitress import serve +        serve(handler, host=self.host, port=self.port) + +  class PasteServer(ServerAdapter):      def run(self, handler): # pragma: no cover          from paste import httpserver @@ -2120,8 +2426,8 @@ class FapwsServer(ServerAdapter):          evwsgi.start(self.host, port)          # fapws3 never releases the GIL. Complain upstream. I tried. No luck.          if 'BOTTLE_CHILD' in os.environ and not self.quiet: -            print "WARNING: Auto-reloading does not work with Fapws3." -            print "         (Fapws3 breaks python thread support)" +            _stderr("WARNING: Auto-reloading does not work with Fapws3.\n") +            _stderr("         (Fapws3 breaks python thread support)\n")          evwsgi.set_base_module(base)          def app(environ, start_response):              environ['wsgi.multiprocess'] = False @@ -2178,16 +2484,17 @@ class DieselServer(ServerAdapter):  class GeventServer(ServerAdapter):      """ Untested. Options: -        * `monkey` (default: True) fixes the stdlib to use greenthreads.          * `fast` (default: False) uses libevent's http server, but has some            issues: No streaming, no pipelining, no SSL.      """      def run(self, handler): -        from gevent import wsgi as wsgi_fast, pywsgi, monkey, local -        if self.options.get('monkey', True): -            if not threading.local is local.local: monkey.patch_all() -        wsgi = wsgi_fast if self.options.get('fast') else pywsgi -        wsgi.WSGIServer((self.host, self.port), handler).serve_forever() +        from gevent import wsgi, pywsgi, local +        if not isinstance(_lctx, local.local): +            msg = "Bottle requires gevent.monkey.patch_all() (before import)" +            raise RuntimeError(msg) +        if not self.options.get('fast'): wsgi = pywsgi +        log = None if self.quiet else 'default' +        wsgi.WSGIServer((self.host, self.port), handler, log=log).serve_forever()  class GunicornServer(ServerAdapter): @@ -2212,7 +2519,12 @@ class EventletServer(ServerAdapter):      """ Untested """      def run(self, handler):          from eventlet import wsgi, listen -        wsgi.server(listen((self.host, self.port)), handler) +        try: +            wsgi.server(listen((self.host, self.port)), handler, +                        log_output=(not self.quiet)) +        except TypeError: +            # Fallback, if we have old version of eventlet +            wsgi.server(listen((self.host, self.port)), handler)  class RocketServer(ServerAdapter): @@ -2232,7 +2544,7 @@ class BjoernServer(ServerAdapter):  class AutoServer(ServerAdapter):      """ Untested. """ -    adapters = [PasteServer, CherryPyServer, TwistedServer, WSGIRefServer] +    adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer]      def run(self, handler):          for sa in self.adapters:              try: @@ -2244,6 +2556,7 @@ server_names = {      'cgi': CGIServer,      'flup': FlupFCGIServer,      'wsgiref': WSGIRefServer, +    'waitress': WaitressServer,      'cherrypy': CherryPyServer,      'paste': PasteServer,      'fapws3': FapwsServer, @@ -2303,8 +2616,10 @@ def load_app(target):          default_app.remove(tmp) # Remove the temporary added default application          NORUN = nr_old +_debug = debug  def run(app=None, server='wsgiref', host='127.0.0.1', port=8080, -        interval=1, reloader=False, quiet=False, plugins=None, **kargs): +        interval=1, reloader=False, quiet=False, plugins=None, +        debug=False, **kargs):      """ Start a server instance. This method blocks until the server terminates.          :param app: WSGI application or target string supported by @@ -2324,6 +2639,7 @@ def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,      if NORUN: return      if reloader and not os.environ.get('BOTTLE_CHILD'):          try: +            lockfile = None              fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')              os.close(fd) # We only need this file to exist. We never write to it              while os.path.exists(lockfile): @@ -2345,9 +2661,8 @@ def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,                  os.unlink(lockfile)          return -    stderr = sys.stderr.write -      try: +        _debug(debug)          app = app or default_app()          if isinstance(app, basestring):              app = load_app(app) @@ -2368,9 +2683,9 @@ def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,          server.quiet = server.quiet or quiet          if not server.quiet: -            stderr("Bottle server starting up (using %s)...\n" % repr(server)) -            stderr("Listening on http://%s:%d/\n" % (server.host, server.port)) -            stderr("Hit Ctrl-C to quit.\n\n") +            _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server))) +            _stderr("Listening on http://%s:%d/\n" % (server.host, server.port)) +            _stderr("Hit Ctrl-C to quit.\n\n")          if reloader:              lockfile = os.environ.get('BOTTLE_LOCKFILE') @@ -2383,12 +2698,15 @@ def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,              server.run(app)      except KeyboardInterrupt:          pass -    except (SyntaxError, ImportError): +    except (SystemExit, MemoryError): +        raise +    except:          if not reloader: raise -        if not getattr(server, 'quiet', False): print_exc() +        if not getattr(server, 'quiet', quiet): +            print_exc() +        time.sleep(interval)          sys.exit(3) -    finally: -        if not getattr(server, 'quiet', False): stderr('Shutdown...\n') +  class FileCheckerThread(threading.Thread): @@ -2406,7 +2724,7 @@ class FileCheckerThread(threading.Thread):          mtime = lambda path: os.stat(path).st_mtime          files = dict() -        for module in sys.modules.values(): +        for module in list(sys.modules.values()):              path = getattr(module, '__file__', '')              if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]              if path and exists(path): files[path] = mtime(path) @@ -2416,20 +2734,20 @@ class FileCheckerThread(threading.Thread):              or mtime(self.lockfile) < time.time() - self.interval - 5:                  self.status = 'error'                  thread.interrupt_main() -            for path, lmtime in files.iteritems(): +            for path, lmtime in list(files.items()):                  if not exists(path) or mtime(path) > lmtime:                      self.status = 'reload'                      thread.interrupt_main()                      break              time.sleep(self.interval) -     +      def __enter__(self):          self.start() -     +      def __exit__(self, exc_type, exc_val, exc_tb):          if not self.status: self.status = 'exit' # silent exit          self.join() -        return issubclass(exc_type, KeyboardInterrupt) +        return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) @@ -2465,7 +2783,7 @@ class BaseTemplate(object):          self.name = name          self.source = source.read() if hasattr(source, 'read') else source          self.filename = source.filename if hasattr(source, 'filename') else None -        self.lookup = map(os.path.abspath, lookup) +        self.lookup = [os.path.abspath(x) for x in lookup]          self.encoding = encoding          self.settings = self.settings.copy() # Copy from class variable          self.settings.update(settings) # Apply @@ -2481,11 +2799,19 @@ class BaseTemplate(object):      def search(cls, name, lookup=[]):          """ Search name in all directories specified in lookup.          First without, then with common extensions. Return first hit. """ -        if os.path.isfile(name): return name +        if not lookup: +            depr('The template lookup path list should not be empty.') +            lookup = ['.'] + +        if os.path.isabs(name) and os.path.isfile(name): +            depr('Absolute template path names are deprecated.') +            return os.path.abspath(name) +          for spath in lookup: -            fname = os.path.join(spath, name) -            if os.path.isfile(fname): -                return fname +            spath = os.path.abspath(spath) + os.sep +            fname = os.path.abspath(os.path.join(spath, name)) +            if not fname.startswith(spath): continue +            if os.path.isfile(fname): return fname              for ext in cls.extensions:                  if os.path.isfile('%s.%s' % (fname, ext)):                      return '%s.%s' % (fname, ext) @@ -2577,16 +2903,17 @@ class Jinja2Template(BaseTemplate):      def loader(self, name):          fname = self.search(name, self.lookup) -        if fname: -            with open(fname, "rb") as f: -                return f.read().decode(self.encoding) +        if not fname: return +        with open(fname, "rb") as f: +            return f.read().decode(self.encoding)  class SimpleTALTemplate(BaseTemplate): -    ''' Untested! ''' +    ''' Deprecated, do not use. '''      def prepare(self, **options): +        depr('The SimpleTAL template handler is deprecated'\ +             ' and will be removed in 0.12')          from simpletal import simpleTAL -        # TODO: add option to load METAL files during render          if self.source:              self.tpl = simpleTAL.compileHTMLTemplate(self.source)          else: @@ -2596,7 +2923,6 @@ class SimpleTALTemplate(BaseTemplate):      def render(self, *args, **kwargs):          from simpletal import simpleTALES          for dictarg in args: kwargs.update(dictarg) -        # TODO: maybe reuse a context instead of always creating one          context = simpleTALES.Context()          for k,v in self.defaults.items():              context.addGlobal(k, v) @@ -2684,13 +3010,13 @@ class SimpleTemplate(BaseTemplate):          for line in template.splitlines(True):              lineno += 1 -            line = line if isinstance(line, unicode)\ -                        else unicode(line, encoding=self.encoding) +            line = touni(line, self.encoding) +            sline = line.lstrip()              if lineno <= 2: -                m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line) +                m = re.match(r"%\s*#.*coding[:=]\s*([-\w.]+)", sline)                  if m: self.encoding = m.group(1)                  if m: line = line.replace('coding','coding (removed)') -            if line.strip()[:2].count('%') == 1: +            if sline and sline[0] == '%' and sline[:2] != '%%':                  line = line.split('%',1)[1].lstrip() # Full line following the %                  cline = self.split_comment(line).strip()                  cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0] @@ -2768,21 +3094,22 @@ def template(*args, **kwargs):      or directly (as keyword arguments).      '''      tpl = args[0] if args else None -    template_adapter = kwargs.pop('template_adapter', SimpleTemplate) -    if tpl not in TEMPLATES or DEBUG: +    adapter = kwargs.pop('template_adapter', SimpleTemplate) +    lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) +    tplid = (id(lookup), tpl) +    if tplid not in TEMPLATES or DEBUG:          settings = kwargs.pop('template_settings', {}) -        lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) -        if isinstance(tpl, template_adapter): -            TEMPLATES[tpl] = tpl -            if settings: TEMPLATES[tpl].prepare(**settings) +        if isinstance(tpl, adapter): +            TEMPLATES[tplid] = tpl +            if settings: TEMPLATES[tplid].prepare(**settings)          elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: -            TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings) +            TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)          else: -            TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings) -    if not TEMPLATES[tpl]: +            TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) +    if not TEMPLATES[tplid]:          abort(500, 'Template (%s) not found' % tpl)      for dictarg in args[1:]: kwargs.update(dictarg) -    return TEMPLATES[tpl].render(kwargs) +    return TEMPLATES[tplid].render(kwargs)  mako_template = functools.partial(template, template_adapter=MakoTemplate)  cheetah_template = functools.partial(template, template_adapter=CheetahTemplate) @@ -2839,17 +3166,16 @@ HTTP_CODES[428] = "Precondition Required"  HTTP_CODES[429] = "Too Many Requests"  HTTP_CODES[431] = "Request Header Fields Too Large"  HTTP_CODES[511] = "Network Authentication Required" -_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.iteritems()) +_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items())  #: The default template used for error pages. Override with @error()  ERROR_PAGE_TEMPLATE = """ -%try: -    %from bottle import DEBUG, HTTP_CODES, request, touni -    %status_name = HTTP_CODES.get(e.status, 'Unknown').title() +%%try: +    %%from %s import DEBUG, HTTP_CODES, request, touni      <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">      <html>          <head> -            <title>Error {{e.status}}: {{status_name}}</title> +            <title>Error: {{e.status}}</title>              <style type="text/css">                html {background-color: #eee; font-family: sans;}                body {background-color: #fff; border: 1px solid #ddd; @@ -2858,31 +3184,34 @@ ERROR_PAGE_TEMPLATE = """              </style>          </head>          <body> -            <h1>Error {{e.status}}: {{status_name}}</h1> +            <h1>Error: {{e.status}}</h1>              <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>                 caused an error:</p> -            <pre>{{e.output}}</pre> -            %if DEBUG and e.exception: +            <pre>{{e.body}}</pre> +            %%if DEBUG and e.exception:                <h2>Exception:</h2>                <pre>{{repr(e.exception)}}</pre> -            %end -            %if DEBUG and e.traceback: +            %%end +            %%if DEBUG and e.traceback:                <h2>Traceback:</h2>                <pre>{{e.traceback}}</pre> -            %end +            %%end          </body>      </html> -%except ImportError: +%%except ImportError:      <b>ImportError:</b> Could not generate the error page. Please add bottle to      the import path. -%end -""" +%%end +""" % __name__ -#: A thread-safe instance of :class:`Request` representing the `current` request. -request = Request() +#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a +#: request callback, this instance always refers to the *current* request +#: (even on a multithreaded server). +request = LocalRequest() -#: A thread-safe instance of :class:`Response` used to build the HTTP response. -response = Response() +#: A thread-safe instance of :class:`LocalResponse`. It is used to change the +#: HTTP response for the *current* request. +response = LocalResponse()  #: A thread-safe namespace. Not used by Bottle.  local = threading.local() @@ -2894,29 +3223,29 @@ app.push()  #: A virtual package that redirects import statements.  #: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. -ext = _ImportRedirect(__name__+'.ext', 'bottle_%s').module +ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else __name__+".ext", 'bottle_%s').module  if __name__ == '__main__':      opt, args, parser = _cmd_options, _cmd_args, _cmd_parser      if opt.version: -        print 'Bottle', __version__; sys.exit(0) +        _stdout('Bottle %s\n'%__version__) +        sys.exit(0)      if not args:          parser.print_help() -        print '\nError: No application specified.\n' +        _stderr('\nError: No application specified.\n')          sys.exit(1) -    try: -        sys.path.insert(0, '.') -        sys.modules.setdefault('bottle', sys.modules['__main__']) -    except (AttributeError, ImportError), e: -        parser.error(e.args[0]) +    sys.path.insert(0, '.') +    sys.modules.setdefault('bottle', sys.modules['__main__']) + +    host, port = (opt.bind or 'localhost'), 8080 +    if ':' in host: +        host, port = host.rsplit(':', 1) + +    run(args[0], host=host, port=port, server=opt.server, +        reloader=opt.reload, plugins=opt.plugin, debug=opt.debug) + -    if opt.bind and ':' in opt.bind: -        host, port = opt.bind.rsplit(':', 1) -    else: -        host, port = (opt.bind or 'localhost'), 8080 -    debug(opt.debug) -    run(args[0], host=host, port=port, server=opt.server, reloader=opt.reload, plugins=opt.plugin)  # THE END diff --git a/module/web/static/js/default.js b/module/web/static/js/default.js index 3d02ba49e..8bd54cb25 100644 --- a/module/web/static/js/default.js +++ b/module/web/static/js/default.js @@ -3,41 +3,41 @@  require.config({      // XXX: To many dots in file breaks dependencies -    paths:{ +    paths: { -        jquery:"libs/jquery-1.8.0", -        jqueryui:"libs/jqueryui", -        flot:"libs/jquery.flot-1.1", +        jquery: "libs/jquery-1.8.3", +        jqueryui: "libs/jqueryui", +        flot: "libs/jquery.flot-1.1",          flotpie: "libs/jquery.flot.pie", -        peity: "libs/jquery.peity-0.6", -        transit:"libs/jquery.transit-0.1.3", +        peity: "libs/jquery.peity-1.0", +        transit: "libs/jquery.transit-0.9.9",          omniwindow: "libs/jquery.omniwindow",          bootstrap: "libs/bootstrap-2.1.1", -        underscore:"libs/lodash-0.7.0", -        backbone:"libs/backbone-0.9.2", +        underscore: "libs/lodash-1.0.rc3", +        backbone: "libs/backbone-0.9.9",  //        handlebars: "libs/Handlebars-1.0rc1",          // Plugins  //        hbs: "plugins/hbs-2.0.1", -        text:"plugins/text-2.0.3", +        text: "plugins/text-2.0.3",          tpl: "../../templates"      },      // Sets the configuration for your third party scripts that are not AMD compatible -    shim:{ +    shim: { -        "backbone":{ -            deps:["underscore", "jquery"], -            exports:"Backbone"  //attaches "Backbone" to the window object +        "backbone": { +            deps: ["underscore", "jquery"], +            exports: "Backbone"  //attaches "Backbone" to the window object          }, -        "flot" : ["jquery"], -        "peity" : ["jquery"], -        "flotpie" : ["flot"], -        "transit" : ["jquery"], -        "omniwindow" : ["jquery"], -        "bootstrap" : ["jquery"] +        "flot": ["jquery"], +        "peity": ["jquery"], +        "flotpie": ["flot"], +        "transit": ["jquery"], +        "omniwindow": ["jquery"], +        "bootstrap": ["jquery"]      } // end Shim Configuration      // Handlebar Configuration @@ -49,20 +49,19 @@ require.config({  define('default', ['jquery', 'backbone', 'routers/defaultRouter', 'views/headerView', 'views/packageTreeView',      'utils/animations', 'bootstrap'], -    function ($, Backbone, DefaultRouter, HeaderView, TreeView) { +    function($, Backbone, DefaultRouter, HeaderView, TreeView) { +        var init = function() { +            var view = new HeaderView(); +            view.render(); +        }; -    var init = function(){ -        var view = new HeaderView(); -        view.render(); -    }; +        var initPackageTree = function() { +            $(function() { +                var view = new TreeView(); +                view.init(); +            }); +        }; -    var initPackageTree = function() { -        $(function() { -            var view = new TreeView(); -            view.init(); -        }); -    }; - -   return {"init":init, "initPackageTree": initPackageTree}; -});
\ No newline at end of file +        return {"init": init, "initPackageTree": initPackageTree}; +    });
\ No newline at end of file diff --git a/module/web/static/js/libs/backbone-0.9.2.js b/module/web/static/js/libs/backbone-0.9.9.js index d0410b5c8..90e18d4ac 100644 --- a/module/web/static/js/libs/backbone-0.9.2.js +++ b/module/web/static/js/libs/backbone-0.9.9.js @@ -1,4 +1,4 @@ -//     Backbone.js 0.9.2 +//     Backbone.js 0.9.9  //     (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.  //     Backbone may be freely distributed under the MIT license. @@ -10,7 +10,7 @@    // Initial Setup    // ------------- -  // Save a reference to the global object (`window` in the browser, `global` +  // Save a reference to the global object (`window` in the browser, `exports`    // on the server).    var root = this; @@ -18,9 +18,11 @@    // restored later on, if `noConflict` is used.    var previousBackbone = root.Backbone; -  // Create a local reference to slice/splice. -  var slice = Array.prototype.slice; -  var splice = Array.prototype.splice; +  // Create a local reference to array methods. +  var array = []; +  var push = array.push; +  var slice = array.slice; +  var splice = array.splice;    // The top-level namespace. All public Backbone classes and modules will    // be attached to this. Exported for both CommonJS and the browser. @@ -32,23 +34,14 @@    }    // Current version of the library. Keep in sync with `package.json`. -  Backbone.VERSION = '0.9.2'; +  Backbone.VERSION = '0.9.9';    // Require Underscore, if we're on the server, and it's not already present.    var _ = root._;    if (!_ && (typeof require !== 'undefined')) _ = require('underscore');    // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. -  var $ = root.jQuery || root.Zepto || root.ender; - -  // Set the JavaScript library that will be used for DOM manipulation and -  // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery, -  // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an -  // alternate JavaScript library (or a mock library for testing your views -  // outside of a browser). -  Backbone.setDomLibrary = function(lib) { -    $ = lib; -  }; +  Backbone.$ = root.jQuery || root.Zepto || root.ender;    // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable    // to its previous owner. Returns a reference to this Backbone object. @@ -69,14 +62,51 @@    Backbone.emulateJSON = false;    // Backbone.Events -  // ----------------- +  // --------------- -  // Regular expression used to split event strings +  // Regular expression used to split event strings.    var eventSplitter = /\s+/; +  // Implement fancy features of the Events API such as multiple event +  // names `"change blur"` and jQuery-style event maps `{change: action}` +  // in terms of the existing API. +  var eventsApi = function(obj, action, name, rest) { +    if (!name) return true; +    if (typeof name === 'object') { +      for (var key in name) { +        obj[action].apply(obj, [key, name[key]].concat(rest)); +      } +    } else if (eventSplitter.test(name)) { +      var names = name.split(eventSplitter); +      for (var i = 0, l = names.length; i < l; i++) { +        obj[action].apply(obj, [names[i]].concat(rest)); +      } +    } else { +      return true; +    } +  }; + +  // Optimized internal dispatch function for triggering events. Tries to +  // keep the usual cases speedy (most Backbone events have 3 arguments). +  var triggerEvents = function(obj, events, args) { +    var ev, i = -1, l = events.length; +    switch (args.length) { +    case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); +    return; +    case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]); +    return; +    case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]); +    return; +    case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]); +    return; +    default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); +    } +  }; +    // A module that can be mixed in to *any object* in order to provide it with -  // custom events. You may bind with `on` or remove with `off` callback functions -  // to an event; trigger`-ing an event fires all callbacks in succession. +  // custom events. You may bind with `on` or remove with `off` callback +  // functions to an event; `trigger`-ing an event fires all callbacks in +  // succession.    //    //     var object = {};    //     _.extend(object, Backbone.Events); @@ -85,58 +115,58 @@    //    var Events = Backbone.Events = { -    // Bind one or more space separated events, `events`, to a `callback` -    // function. Passing `"all"` will bind the callback to all events fired. -    on: function(events, callback, context) { - -      var calls, event, node, tail, list; -      if (!callback) return this; -      events = events.split(eventSplitter); -      calls = this._callbacks || (this._callbacks = {}); - -      // Create an immutable callback list, allowing traversal during -      // modification.  The tail is an empty object that will always be used -      // as the next node. -      while (event = events.shift()) { -        list = calls[event]; -        node = list ? list.tail : {}; -        node.next = tail = {}; -        node.context = context; -        node.callback = callback; -        calls[event] = {tail: tail, next: list ? list.next : node}; -      } - +    // Bind one or more space separated events, or an events map, +    // to a `callback` function. Passing `"all"` will bind the callback to +    // all events fired. +    on: function(name, callback, context) { +      if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this; +      this._events || (this._events = {}); +      var list = this._events[name] || (this._events[name] = []); +      list.push({callback: callback, context: context, ctx: context || this});        return this;      }, -    // Remove one or many callbacks. If `context` is null, removes all callbacks -    // with that function. If `callback` is null, removes all callbacks for the -    // event. If `events` is null, removes all bound callbacks for all events. -    off: function(events, callback, context) { -      var event, calls, node, tail, cb, ctx; +    // Bind events to only be triggered a single time. After the first time +    // the callback is invoked, it will be removed. +    once: function(name, callback, context) { +      if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this; +      var self = this; +      var once = _.once(function() { +        self.off(name, once); +        callback.apply(this, arguments); +      }); +      once._callback = callback; +      this.on(name, once, context); +      return this; +    }, -      // No events, or removing *all* events. -      if (!(calls = this._callbacks)) return; -      if (!(events || callback || context)) { -        delete this._callbacks; +    // Remove one or many callbacks. If `context` is null, removes all +    // callbacks with that function. If `callback` is null, removes all +    // callbacks for the event. If `events` is null, removes all bound +    // callbacks for all events. +    off: function(name, callback, context) { +      var list, ev, events, names, i, l, j, k; +      if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; +      if (!name && !callback && !context) { +        this._events = {};          return this;        } -      // Loop through the listed events and contexts, splicing them out of the -      // linked list of callbacks if appropriate. -      events = events ? events.split(eventSplitter) : _.keys(calls); -      while (event = events.shift()) { -        node = calls[event]; -        delete calls[event]; -        if (!node || !(callback || context)) continue; -        // Create a new list, omitting the indicated callbacks. -        tail = node.tail; -        while ((node = node.next) !== tail) { -          cb = node.callback; -          ctx = node.context; -          if ((callback && cb !== callback) || (context && ctx !== context)) { -            this.on(event, cb, ctx); +      names = name ? [name] : _.keys(this._events); +      for (i = 0, l = names.length; i < l; i++) { +        name = names[i]; +        if (list = this._events[name]) { +          events = []; +          if (callback || context) { +            for (j = 0, k = list.length; j < k; j++) { +              ev = list[j]; +              if ((callback && callback !== (ev.callback._callback || ev.callback)) || +                  (context && context !== ev.context)) { +                events.push(ev); +              } +            }            } +          this._events[name] = events;          }        } @@ -147,40 +177,53 @@      // passed the same arguments as `trigger` is, apart from the event name      // (unless you're listening on `"all"`, which will cause your callback to      // receive the true name of the event as the first argument). -    trigger: function(events) { -      var event, node, calls, tail, args, all, rest; -      if (!(calls = this._callbacks)) return this; -      all = calls.all; -      events = events.split(eventSplitter); -      rest = slice.call(arguments, 1); - -      // For each event, walk through the linked list of callbacks twice, -      // first to trigger the event, then to trigger any `"all"` callbacks. -      while (event = events.shift()) { -        if (node = calls[event]) { -          tail = node.tail; -          while ((node = node.next) !== tail) { -            node.callback.apply(node.context || this, rest); -          } -        } -        if (node = all) { -          tail = node.tail; -          args = [event].concat(rest); -          while ((node = node.next) !== tail) { -            node.callback.apply(node.context || this, args); -          } +    trigger: function(name) { +      if (!this._events) return this; +      var args = slice.call(arguments, 1); +      if (!eventsApi(this, 'trigger', name, args)) return this; +      var events = this._events[name]; +      var allEvents = this._events.all; +      if (events) triggerEvents(this, events, args); +      if (allEvents) triggerEvents(this, allEvents, arguments); +      return this; +    }, + +    // An inversion-of-control version of `on`. Tell *this* object to listen to +    // an event in another object ... keeping track of what it's listening to. +    listenTo: function(object, events, callback) { +      var listeners = this._listeners || (this._listeners = {}); +      var id = object._listenerId || (object._listenerId = _.uniqueId('l')); +      listeners[id] = object; +      object.on(events, callback || this, this); +      return this; +    }, + +    // Tell this object to stop listening to either specific events ... or +    // to every object it's currently listening to. +    stopListening: function(object, events, callback) { +      var listeners = this._listeners; +      if (!listeners) return; +      if (object) { +        object.off(events, callback, this); +        if (!events && !callback) delete listeners[object._listenerId]; +      } else { +        for (var id in listeners) { +          listeners[id].off(null, null, this);          } +        this._listeners = {};        } -        return this;      } -    };    // Aliases for backwards compatibility.    Events.bind   = Events.on;    Events.unbind = Events.off; +  // Allow the `Backbone` object to serve as a global event bus, for folks who +  // want global "pubsub" in a convenient place. +  _.extend(Backbone, Events); +    // Backbone.Model    // -------------- @@ -188,23 +231,16 @@    // is automatically generated and assigned for you.    var Model = Backbone.Model = function(attributes, options) {      var defaults; -    attributes || (attributes = {}); -    if (options && options.parse) attributes = this.parse(attributes); -    if (defaults = getValue(this, 'defaults')) { -      attributes = _.extend({}, defaults, attributes); -    } -    if (options && options.collection) this.collection = options.collection; -    this.attributes = {}; -    this._escapedAttributes = {}; +    var attrs = attributes || {};      this.cid = _.uniqueId('c');      this.changed = {}; -    this._silent = {}; -    this._pending = {}; -    this.set(attributes, {silent: true}); -    // Reset change tracking. -    this.changed = {}; -    this._silent = {}; -    this._pending = {}; +    this.attributes = {}; +    this._changes = []; +    if (options && options.collection) this.collection = options.collection; +    if (options && options.parse) attrs = this.parse(attrs); +    if (defaults = _.result(this, 'defaults')) _.defaults(attrs, defaults); +    this.set(attrs, {silent: true}); +    this._currentAttributes = _.clone(this.attributes);      this._previousAttributes = _.clone(this.attributes);      this.initialize.apply(this, arguments);    }; @@ -215,14 +251,6 @@      // A hash of attributes whose current and previous value differ.      changed: null, -    // A hash of attributes that have silently changed since the last time -    // `change` was called.  Will become pending attributes on the next call. -    _silent: null, - -    // A hash of attributes that have changed since the last `'change'` event -    // began. -    _pending: null, -      // The default name for the JSON `id` attribute is `"id"`. MongoDB and      // CouchDB users may want to set this to `"_id"`.      idAttribute: 'id', @@ -236,6 +264,11 @@        return _.clone(this.attributes);      }, +    // Proxy `Backbone.sync` by default. +    sync: function() { +      return Backbone.sync.apply(this, arguments); +    }, +      // Get the value of an attribute.      get: function(attr) {        return this.attributes[attr]; @@ -243,10 +276,7 @@      // Get the HTML-escaped value of an attribute.      escape: function(attr) { -      var html; -      if (html = this._escapedAttributes[attr]) return html; -      var val = this.get(attr); -      return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val); +      return _.escape(this.get(attr));      },      // Returns `true` if the attribute contains a value that is not null @@ -257,23 +287,21 @@      // Set a hash of model attributes on the object, firing `"change"` unless      // you choose to silence it. -    set: function(key, value, options) { -      var attrs, attr, val; +    set: function(key, val, options) { +      var attr, attrs; +      if (key == null) return this;        // Handle both `"key", value` and `{key: value}` -style arguments. -      if (_.isObject(key) || key == null) { +      if (_.isObject(key)) {          attrs = key; -        options = value; +        options = val;        } else { -        attrs = {}; -        attrs[key] = value; +        (attrs = {})[key] = val;        }        // Extract attributes and options. -      options || (options = {}); -      if (!attrs) return this; -      if (attrs instanceof Model) attrs = attrs.attributes; -      if (options.unset) for (attr in attrs) attrs[attr] = void 0; +      var silent = options && options.silent; +      var unset = options && options.unset;        // Run validation.        if (!this._validate(attrs, options)) return false; @@ -281,52 +309,38 @@        // Check for changes of `id`.        if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; -      var changes = options.changes = {};        var now = this.attributes; -      var escaped = this._escapedAttributes; -      var prev = this._previousAttributes || {};        // For each `set` attribute...        for (attr in attrs) {          val = attrs[attr]; -        // If the new and current value differ, record the change. -        if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) { -          delete escaped[attr]; -          (options.silent ? this._silent : changes)[attr] = true; -        } - -        // Update or delete the current value. -        options.unset ? delete now[attr] : now[attr] = val; - -        // If the new and previous value differ, record the change.  If not, -        // then remove changes for this attribute. -        if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) { -          this.changed[attr] = val; -          if (!options.silent) this._pending[attr] = true; -        } else { -          delete this.changed[attr]; -          delete this._pending[attr]; -        } +        // Update or delete the current value, and track the change. +        unset ? delete now[attr] : now[attr] = val; +        this._changes.push(attr, val);        } +      // Signal that the model's state has potentially changed, and we need +      // to recompute the actual changes. +      this._hasComputed = false; +        // Fire the `"change"` events. -      if (!options.silent) this.change(options); +      if (!silent) this.change(options);        return this;      },      // Remove an attribute from the model, firing `"change"` unless you choose      // to silence it. `unset` is a noop if the attribute doesn't exist.      unset: function(attr, options) { -      (options || (options = {})).unset = true; -      return this.set(attr, null, options); +      return this.set(attr, void 0, _.extend({}, options, {unset: true}));      },      // Clear all attributes on the model, firing `"change"` unless you choose      // to silence it.      clear: function(options) { -      (options || (options = {})).unset = true; -      return this.set(_.clone(this.attributes), options); +      var attrs = {}; +      for (var key in this.attributes) attrs[key] = void 0; +      return this.set(attrs, _.extend({}, options, {unset: true}));      },      // Fetch the model from the server. If the server's representation of the @@ -334,35 +348,34 @@      // triggering a `"change"` event.      fetch: function(options) {        options = options ? _.clone(options) : {}; +      if (options.parse === void 0) options.parse = true;        var model = this;        var success = options.success;        options.success = function(resp, status, xhr) { -        if (!model.set(model.parse(resp, xhr), options)) return false; -        if (success) success(model, resp); +        if (!model.set(model.parse(resp), options)) return false; +        if (success) success(model, resp, options);        }; -      options.error = Backbone.wrapError(options.error, model, options); -      return (this.sync || Backbone.sync).call(this, 'read', this, options); +      return this.sync('read', this, options);      },      // Set a hash of model attributes, and sync the model to the server.      // If the server returns an attributes hash that differs, the model's      // state will be `set` again. -    save: function(key, value, options) { -      var attrs, current; +    save: function(key, val, options) { +      var attrs, current, done; -      // Handle both `("key", value)` and `({key: value})` -style calls. -      if (_.isObject(key) || key == null) { +      // Handle both `"key", value` and `{key: value}` -style arguments. +      if (key == null || _.isObject(key)) {          attrs = key; -        options = value; -      } else { -        attrs = {}; -        attrs[key] = value; +        options = val; +      } else if (key != null) { +        (attrs = {})[key] = val;        }        options = options ? _.clone(options) : {};        // If we're "wait"-ing to set changed attributes, validate early.        if (options.wait) { -        if (!this._validate(attrs, options)) return false; +        if (attrs && !this._validate(attrs, options)) return false;          current = _.clone(this.attributes);        } @@ -372,29 +385,33 @@          return false;        } +      // Do not persist invalid models. +      if (!attrs && !this._validate(null, options)) return false; +        // After a successful server-side save, the client is (optionally)        // updated with the server-side state.        var model = this;        var success = options.success;        options.success = function(resp, status, xhr) { -        var serverAttrs = model.parse(resp, xhr); -        if (options.wait) { -          delete options.wait; -          serverAttrs = _.extend(attrs || {}, serverAttrs); -        } +        done = true; +        var serverAttrs = model.parse(resp); +        if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);          if (!model.set(serverAttrs, options)) return false; -        if (success) { -          success(model, resp); -        } else { -          model.trigger('sync', model, resp, options); -        } +        if (success) success(model, resp, options);        };        // Finish configuring and sending the Ajax request. -      options.error = Backbone.wrapError(options.error, model, options); -      var method = this.isNew() ? 'create' : 'update'; -      var xhr = (this.sync || Backbone.sync).call(this, method, this, options); -      if (options.wait) this.set(current, silentOptions); +      var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); +      if (method == 'patch') options.attrs = attrs; +      var xhr = this.sync(method, this, options); + +      // When using `wait`, reset attributes to original values unless +      // `success` has been called already. +      if (!done && options.wait) { +        this.clear(silentOptions); +        this.set(current, silentOptions); +      } +        return xhr;      }, @@ -406,27 +423,22 @@        var model = this;        var success = options.success; -      var triggerDestroy = function() { +      var destroy = function() {          model.trigger('destroy', model, model.collection, options);        }; +      options.success = function(resp) { +        if (options.wait || model.isNew()) destroy(); +        if (success) success(model, resp, options); +      }; +        if (this.isNew()) { -        triggerDestroy(); +        options.success();          return false;        } -      options.success = function(resp) { -        if (options.wait) triggerDestroy(); -        if (success) { -          success(model, resp); -        } else { -          model.trigger('sync', model, resp, options); -        } -      }; - -      options.error = Backbone.wrapError(options.error, model, options); -      var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options); -      if (!options.wait) triggerDestroy(); +      var xhr = this.sync('delete', this, options); +      if (!options.wait) destroy();        return xhr;      }, @@ -434,14 +446,14 @@      // using Backbone's restful methods, override this to change the endpoint      // that will be called.      url: function() { -      var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); +      var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();        if (this.isNew()) return base; -      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); +      return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);      },      // **parse** converts a response into the hash of attributes to be `set` on      // the model. The default implementation is just to pass the response along. -    parse: function(resp, xhr) { +    parse: function(resp) {        return resp;      }, @@ -459,30 +471,24 @@      // a `"change:attribute"` event for each changed attribute.      // Calling this will cause all objects observing the model to update.      change: function(options) { -      options || (options = {});        var changing = this._changing;        this._changing = true; -      // Silent changes become pending changes. -      for (var attr in this._silent) this._pending[attr] = true; +      // Generate the changes to be triggered on the model. +      var triggers = this._computeChanges(true); -      // Silent changes are triggered. -      var changes = _.extend({}, options.changes, this._silent); -      this._silent = {}; -      for (var attr in changes) { -        this.trigger('change:' + attr, this, this.get(attr), options); +      this._pending = !!triggers.length; + +      for (var i = triggers.length - 2; i >= 0; i -= 2) { +        this.trigger('change:' + triggers[i], this, triggers[i + 1], options);        } +        if (changing) return this; -      // Continue firing `"change"` events while there are pending changes. -      while (!_.isEmpty(this._pending)) { -        this._pending = {}; +      // Trigger a `change` while there have been changes. +      while (this._pending) { +        this._pending = false;          this.trigger('change', this, options); -        // Pending and silent changes still remain. -        for (var attr in this.changed) { -          if (this._pending[attr] || this._silent[attr]) continue; -          delete this.changed[attr]; -        }          this._previousAttributes = _.clone(this.attributes);        } @@ -493,7 +499,8 @@      // Determine if the model has changed since the last `"change"` event.      // If you specify an attribute name, determine if that attribute has changed.      hasChanged: function(attr) { -      if (!arguments.length) return !_.isEmpty(this.changed); +      if (!this._hasComputed) this._computeChanges(); +      if (attr == null) return !_.isEmpty(this.changed);        return _.has(this.changed, attr);      }, @@ -513,10 +520,43 @@        return changed;      }, +    // Looking at the built up list of `set` attribute changes, compute how +    // many of the attributes have actually changed. If `loud`, return a +    // boiled-down list of only the real changes. +    _computeChanges: function(loud) { +      this.changed = {}; +      var already = {}; +      var triggers = []; +      var current = this._currentAttributes; +      var changes = this._changes; + +      // Loop through the current queue of potential model changes. +      for (var i = changes.length - 2; i >= 0; i -= 2) { +        var key = changes[i], val = changes[i + 1]; +        if (already[key]) continue; +        already[key] = true; + +        // Check if the attribute has been modified since the last change, +        // and update `this.changed` accordingly. If we're inside of a `change` +        // call, also add a trigger to the list. +        if (current[key] !== val) { +          this.changed[key] = val; +          if (!loud) continue; +          triggers.push(key, val); +          current[key] = val; +        } +      } +      if (loud) this._changes = []; + +      // Signals `this.changed` is current to prevent duplicate calls from `this.hasChanged`. +      this._hasComputed = true; +      return triggers; +    }, +      // Get the previous value of an attribute, recorded at the time the last      // `"change"` event was fired.      previous: function(attr) { -      if (!arguments.length || !this._previousAttributes) return null; +      if (attr == null || !this._previousAttributes) return null;        return this._previousAttributes[attr];      }, @@ -526,25 +566,16 @@        return _.clone(this._previousAttributes);      }, -    // Check if the model is currently in a valid state. It's only possible to -    // get into an *invalid* state if you're using silent changes. -    isValid: function() { -      return !this.validate(this.attributes); -    }, -      // Run validation against the next complete set of model attributes,      // returning `true` if all is well. If a specific `error` callback has      // been passed, call that instead of firing the general `"error"` event.      _validate: function(attrs, options) { -      if (options.silent || !this.validate) return true; +      if (!this.validate) return true;        attrs = _.extend({}, this.attributes, attrs);        var error = this.validate(attrs, options);        if (!error) return true; -      if (options && options.error) { -        options.error(this, error, options); -      } else { -        this.trigger('error', this, error, options); -      } +      if (options && options.error) options.error(this, error, options); +      this.trigger('error', this, error, options);        return false;      } @@ -559,10 +590,10 @@    var Collection = Backbone.Collection = function(models, options) {      options || (options = {});      if (options.model) this.model = options.model; -    if (options.comparator) this.comparator = options.comparator; +    if (options.comparator !== void 0) this.comparator = options.comparator;      this._reset();      this.initialize.apply(this, arguments); -    if (models) this.reset(models, {silent: true, parse: options.parse}); +    if (models) this.reset(models, _.extend({silent: true}, options));    };    // Define the Collection's inheritable methods. @@ -582,54 +613,65 @@        return this.map(function(model){ return model.toJSON(options); });      }, +    // Proxy `Backbone.sync` by default. +    sync: function() { +      return Backbone.sync.apply(this, arguments); +    }, +      // Add a model, or list of models to the set. Pass **silent** to avoid      // firing the `add` event for every new model.      add: function(models, options) { -      var i, index, length, model, cid, id, cids = {}, ids = {}, dups = []; -      options || (options = {}); +      var i, args, length, model, existing, needsSort; +      var at = options && options.at; +      var sort = ((options && options.sort) == null ? true : options.sort);        models = _.isArray(models) ? models.slice() : [models]; -      // Begin by turning bare objects into model references, and preventing -      // invalid models or duplicate models from being added. -      for (i = 0, length = models.length; i < length; i++) { -        if (!(model = models[i] = this._prepareModel(models[i], options))) { -          throw new Error("Can't add an invalid model to a collection"); +      // Turn bare objects into model references, and prevent invalid models +      // from being added. +      for (i = models.length - 1; i >= 0; i--) { +        if(!(model = this._prepareModel(models[i], options))) { +          this.trigger("error", this, models[i], options); +          models.splice(i, 1); +          continue;          } -        cid = model.cid; -        id = model.id; -        if (cids[cid] || this._byCid[cid] || ((id != null) && (ids[id] || this._byId[id]))) { -          dups.push(i); +        models[i] = model; + +        existing = model.id != null && this._byId[model.id]; +        // If a duplicate is found, prevent it from being added and +        // optionally merge it into the existing model. +        if (existing || this._byCid[model.cid]) { +          if (options && options.merge && existing) { +            existing.set(model.attributes, options); +            needsSort = sort; +          } +          models.splice(i, 1);            continue;          } -        cids[cid] = ids[id] = model; -      } -      // Remove duplicates. -      i = dups.length; -      while (i--) { -        models.splice(dups[i], 1); -      } - -      // Listen to added models' events, and index models for lookup by -      // `id` and by `cid`. -      for (i = 0, length = models.length; i < length; i++) { -        (model = models[i]).on('all', this._onModelEvent, this); +        // Listen to added models' events, and index models for lookup by +        // `id` and by `cid`. +        model.on('all', this._onModelEvent, this);          this._byCid[model.cid] = model;          if (model.id != null) this._byId[model.id] = model;        } -      // Insert models into the collection, re-sorting if needed, and triggering -      // `add` events unless silenced. -      this.length += length; -      index = options.at != null ? options.at : this.models.length; -      splice.apply(this.models, [index, 0].concat(models)); -      if (this.comparator) this.sort({silent: true}); -      if (options.silent) return this; -      for (i = 0, length = this.models.length; i < length; i++) { -        if (!cids[(model = this.models[i]).cid]) continue; -        options.index = i; +      // See if sorting is needed, update `length` and splice in new models. +      if (models.length) needsSort = sort; +      this.length += models.length; +      args = [at != null ? at : this.models.length, 0]; +      push.apply(args, models); +      splice.apply(this.models, args); + +      // Sort the collection if appropriate. +      if (needsSort && this.comparator && at == null) this.sort({silent: true}); + +      if (options && options.silent) return this; + +      // Trigger `add` events. +      while (model = models.shift()) {          model.trigger('add', model, this, options);        } +        return this;      }, @@ -640,7 +682,7 @@        options || (options = {});        models = _.isArray(models) ? models.slice() : [models];        for (i = 0, l = models.length; i < l; i++) { -        model = this.getByCid(models[i]) || this.get(models[i]); +        model = this.get(models[i]);          if (!model) continue;          delete this._byId[model.id];          delete this._byCid[model.cid]; @@ -659,7 +701,7 @@      // Add a model to the end of the collection.      push: function(model, options) {        model = this._prepareModel(model, options); -      this.add(model, options); +      this.add(model, _.extend({at: this.length}, options));        return model;      }, @@ -684,15 +726,15 @@        return model;      }, -    // Get a model from the set by id. -    get: function(id) { -      if (id == null) return void 0; -      return this._byId[id.id != null ? id.id : id]; +    // Slice out a sub-array of models from the collection. +    slice: function(begin, end) { +      return this.models.slice(begin, end);      }, -    // Get a model from the set by client id. -    getByCid: function(cid) { -      return cid && this._byCid[cid.cid || cid]; +    // Get a model from the set by id. +    get: function(obj) { +      if (obj == null) return void 0; +      return this._byId[obj.id != null ? obj.id : obj] || this._byCid[obj.cid || obj];      },      // Get the model at the given index. @@ -715,34 +757,74 @@      // normal circumstances, as the set will maintain sort order as each item      // is added.      sort: function(options) { -      options || (options = {}); -      if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); -      var boundComparator = _.bind(this.comparator, this); -      if (this.comparator.length == 1) { -        this.models = this.sortBy(boundComparator); +      if (!this.comparator) { +        throw new Error('Cannot sort a set without a comparator'); +      } + +      if (_.isString(this.comparator) || this.comparator.length === 1) { +        this.models = this.sortBy(this.comparator, this);        } else { -        this.models.sort(boundComparator); +        this.models.sort(_.bind(this.comparator, this));        } -      if (!options.silent) this.trigger('reset', this, options); + +      if (!options || !options.silent) this.trigger('sort', this, options);        return this;      },      // Pluck an attribute from each model in the collection.      pluck: function(attr) { -      return _.map(this.models, function(model){ return model.get(attr); }); +      return _.invoke(this.models, 'get', attr); +    }, + +    // Smartly update a collection with a change set of models, adding, +    // removing, and merging as necessary. +    update: function(models, options) { +      var model, i, l, existing; +      var add = [], remove = [], modelMap = {}; +      var idAttr = this.model.prototype.idAttribute; +      options = _.extend({add: true, merge: true, remove: true}, options); +      if (options.parse) models = this.parse(models); + +      // Allow a single model (or no argument) to be passed. +      if (!_.isArray(models)) models = models ? [models] : []; + +      // Proxy to `add` for this case, no need to iterate... +      if (options.add && !options.remove) return this.add(models, options); + +      // Determine which models to add and merge, and which to remove. +      for (i = 0, l = models.length; i < l; i++) { +        model = models[i]; +        existing = this.get(model.id || model.cid || model[idAttr]); +        if (options.remove && existing) modelMap[existing.cid] = true; +        if ((options.add && !existing) || (options.merge && existing)) { +          add.push(model); +        } +      } +      if (options.remove) { +        for (i = 0, l = this.models.length; i < l; i++) { +          model = this.models[i]; +          if (!modelMap[model.cid]) remove.push(model); +        } +      } + +      // Remove models (if applicable) before we add and merge the rest. +      if (remove.length) this.remove(remove, options); +      if (add.length) this.add(add, options); +      return this;      },      // When you have more items than you want to add or remove individually,      // you can reset the entire set with a new list of models, without firing      // any `add` or `remove` events. Fires `reset` when finished.      reset: function(models, options) { -      models  || (models = []);        options || (options = {}); +      if (options.parse) models = this.parse(models);        for (var i = 0, l = this.models.length; i < l; i++) {          this._removeReference(this.models[i]);        } +      options.previousModels = this.models;        this._reset(); -      this.add(models, _.extend({silent: true}, options)); +      if (models) this.add(models, _.extend({silent: true}, options));        if (!options.silent) this.trigger('reset', this, options);        return this;      }, @@ -752,34 +834,30 @@      // models to the collection instead of resetting.      fetch: function(options) {        options = options ? _.clone(options) : {}; -      if (options.parse === undefined) options.parse = true; +      if (options.parse === void 0) options.parse = true;        var collection = this;        var success = options.success;        options.success = function(resp, status, xhr) { -        collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); -        if (success) success(collection, resp); +        var method = options.update ? 'update' : 'reset'; +        collection[method](resp, options); +        if (success) success(collection, resp, options);        }; -      options.error = Backbone.wrapError(options.error, collection, options); -      return (this.sync || Backbone.sync).call(this, 'read', this, options); +      return this.sync('read', this, options);      },      // Create a new instance of a model in this collection. Add the model to the      // collection immediately, unless `wait: true` is passed, in which case we      // wait for the server to agree.      create: function(model, options) { -      var coll = this; +      var collection = this;        options = options ? _.clone(options) : {};        model = this._prepareModel(model, options);        if (!model) return false; -      if (!options.wait) coll.add(model, options); +      if (!options.wait) collection.add(model, options);        var success = options.success; -      options.success = function(nextModel, resp, xhr) { -        if (options.wait) coll.add(nextModel, options); -        if (success) { -          success(nextModel, resp); -        } else { -          nextModel.trigger('sync', model, resp, options); -        } +      options.success = function(model, resp, options) { +        if (options.wait) collection.add(model, options); +        if (success) success(model, resp, options);        };        model.save(null, options);        return model; @@ -787,19 +865,24 @@      // **parse** converts a response into a list of models to be added to the      // collection. The default implementation is just to pass it through. -    parse: function(resp, xhr) { +    parse: function(resp) {        return resp;      }, +    // Create a new collection with an identical list of models as this one. +    clone: function() { +      return new this.constructor(this.models); +    }, +      // Proxy to _'s chain. Can't be proxied the same way the rest of the      // underscore methods are proxied because it relies on the underscore      // constructor. -    chain: function () { +    chain: function() {        return _(this.models).chain();      },      // Reset all internal state. Called when the collection is reset. -    _reset: function(options) { +    _reset: function() {        this.length = 0;        this.models = [];        this._byId  = {}; @@ -807,24 +890,21 @@      },      // Prepare a model or hash of attributes to be added to this collection. -    _prepareModel: function(model, options) { -      options || (options = {}); -      if (!(model instanceof Model)) { -        var attrs = model; -        options.collection = this; -        model = new this.model(attrs, options); -        if (!model._validate(model.attributes, options)) model = false; -      } else if (!model.collection) { -        model.collection = this; +    _prepareModel: function(attrs, options) { +      if (attrs instanceof Model) { +        if (!attrs.collection) attrs.collection = this; +        return attrs;        } +      options || (options = {}); +      options.collection = this; +      var model = new this.model(attrs, options); +      if (!model._validate(attrs, options)) return false;        return model;      },      // Internal method to remove a model's ties to a collection.      _removeReference: function(model) { -      if (this == model.collection) { -        delete model.collection; -      } +      if (this === model.collection) delete model.collection;        model.off('all', this._onModelEvent, this);      }, @@ -833,13 +913,11 @@      // events simply proxy through. "add" and "remove" events that originate      // in other collections are ignored.      _onModelEvent: function(event, model, collection, options) { -      if ((event == 'add' || event == 'remove') && collection != this) return; -      if (event == 'destroy') { -        this.remove(model, options); -      } +      if ((event === 'add' || event === 'remove') && collection !== this) return; +      if (event === 'destroy') this.remove(model, options);        if (model && event === 'change:' + model.idAttribute) {          delete this._byId[model.previous(model.idAttribute)]; -        this._byId[model.id] = model; +        if (model.id != null) this._byId[model.id] = model;        }        this.trigger.apply(this, arguments);      } @@ -847,21 +925,37 @@    });    // Underscore methods that we want to implement on the Collection. -  var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', -    'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', -    'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', -    'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', -    'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; +  var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', +    'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', +    'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', +    'max', 'min', 'sortedIndex', 'toArray', 'size', 'first', 'head', 'take', +    'initial', 'rest', 'tail', 'last', 'without', 'indexOf', 'shuffle', +    'lastIndexOf', 'isEmpty'];    // Mix in each Underscore method as a proxy to `Collection#models`.    _.each(methods, function(method) {      Collection.prototype[method] = function() { -      return _[method].apply(_, [this.models].concat(_.toArray(arguments))); +      var args = slice.call(arguments); +      args.unshift(this.models); +      return _[method].apply(_, args); +    }; +  }); + +  // Underscore methods that take a property name as an argument. +  var attributeMethods = ['groupBy', 'countBy', 'sortBy']; + +  // Use attributes instead of properties. +  _.each(attributeMethods, function(method) { +    Collection.prototype[method] = function(value, context) { +      var iterator = _.isFunction(value) ? value : function(model) { +        return model.get(value); +      }; +      return _[method](this.models, iterator, context);      };    });    // Backbone.Router -  // ------------------- +  // ---------------    // Routers map faux-URLs to actions, and fire events when routes are    // matched. Creating a new one sets its `routes` hash, if not set statically. @@ -874,9 +968,10 @@    // Cached regular expressions for matching named param parts and splatted    // parts of route strings. +  var optionalParam = /\((.*?)\)/g;    var namedParam    = /:\w+/g;    var splatParam    = /\*\w+/g; -  var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g; +  var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;    // Set up all inheritable **Backbone.Router** properties and methods.    _.extend(Router.prototype, Events, { @@ -892,7 +987,6 @@      //     });      //      route: function(route, name, callback) { -      Backbone.history || (Backbone.history = new History);        if (!_.isRegExp(route)) route = this._routeToRegExp(route);        if (!callback) callback = this[name];        Backbone.history.route(route, _.bind(function(fragment) { @@ -907,6 +1001,7 @@      // Simple proxy to `Backbone.history` to save a fragment into the history.      navigate: function(fragment, options) {        Backbone.history.navigate(fragment, options); +      return this;      },      // Bind all defined routes to `Backbone.history`. We have to reverse the @@ -914,12 +1009,9 @@      // routes can be defined at the bottom of the route map.      _bindRoutes: function() {        if (!this.routes) return; -      var routes = []; -      for (var route in this.routes) { -        routes.unshift([route, this.routes[route]]); -      } -      for (var i = 0, l = routes.length; i < l; i++) { -        this.route(routes[i][0], routes[i][1], this[routes[i][1]]); +      var route, routes = _.keys(this.routes); +      while ((route = routes.pop()) != null) { +        this.route(route, this.routes[route]);        }      }, @@ -927,6 +1019,7 @@      // against the current location hash.      _routeToRegExp: function(route) {        route = route.replace(escapeRegExp, '\\$&') +                   .replace(optionalParam, '(?:$1)?')                     .replace(namedParam, '([^\/]+)')                     .replace(splatParam, '(.*?)');        return new RegExp('^' + route + '$'); @@ -948,14 +1041,26 @@    var History = Backbone.History = function() {      this.handlers = [];      _.bindAll(this, 'checkUrl'); + +    // Ensure that `History` can be used outside of the browser. +    if (typeof window !== 'undefined') { +      this.location = window.location; +      this.history = window.history; +    }    }; -  // Cached regex for cleaning leading hashes and slashes . -  var routeStripper = /^[#\/]/; +  // Cached regex for stripping a leading hash/slash and trailing space. +  var routeStripper = /^[#\/]|\s+$/g; + +  // Cached regex for stripping leading and trailing slashes. +  var rootStripper = /^\/+|\/+$/g;    // Cached regex for detecting MSIE.    var isExplorer = /msie [\w.]+/; +  // Cached regex for removing a trailing slash. +  var trailingSlash = /\/$/; +    // Has the history handling already been started?    History.started = false; @@ -968,9 +1073,8 @@      // Gets the true hash value. Cannot use location.hash directly due to bug      // in Firefox where location.hash will always be decoded. -    getHash: function(windowOverride) { -      var loc = windowOverride ? windowOverride.location : window.location; -      var match = loc.href.match(/#(.*)$/); +    getHash: function(window) { +      var match = (window || this).location.href.match(/#(.*)$/);        return match ? match[1] : '';      }, @@ -978,15 +1082,14 @@      // the hash, or the override.      getFragment: function(fragment, forcePushState) {        if (fragment == null) { -        if (this._hasPushState || forcePushState) { -          fragment = window.location.pathname; -          var search = window.location.search; -          if (search) fragment += search; +        if (this._hasPushState || !this._wantsHashChange || forcePushState) { +          fragment = this.location.pathname; +          var root = this.root.replace(trailingSlash, ''); +          if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);          } else {            fragment = this.getHash();          }        } -      if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length);        return fragment.replace(routeStripper, '');      }, @@ -999,24 +1102,28 @@        // Figure out the initial configuration. Do we need an iframe?        // Is pushState desired ... is it available?        this.options          = _.extend({}, {root: '/'}, this.options, options); +      this.root             = this.options.root;        this._wantsHashChange = this.options.hashChange !== false;        this._wantsPushState  = !!this.options.pushState; -      this._hasPushState    = !!(this.options.pushState && window.history && window.history.pushState); +      this._hasPushState    = !!(this.options.pushState && this.history && this.history.pushState);        var fragment          = this.getFragment();        var docMode           = document.documentMode;        var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); -      if (oldIE) { -        this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow; +      // Normalize root to always include a leading and trailing slash. +      this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + +      if (oldIE && this._wantsHashChange) { +        this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;          this.navigate(fragment);        }        // Depending on whether we're using pushState or hashes, and whether        // 'onhashchange' is supported, determine how we check the URL state.        if (this._hasPushState) { -        $(window).bind('popstate', this.checkUrl); +        Backbone.$(window).bind('popstate', this.checkUrl);        } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) { -        $(window).bind('hashchange', this.checkUrl); +        Backbone.$(window).bind('hashchange', this.checkUrl);        } else if (this._wantsHashChange) {          this._checkUrlInterval = setInterval(this.checkUrl, this.interval);        } @@ -1024,14 +1131,14 @@        // Determine if we need to change the base url, for a pushState link        // opened by a non-pushState browser.        this.fragment = fragment; -      var loc = window.location; -      var atRoot  = loc.pathname == this.options.root; +      var loc = this.location; +      var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;        // If we've started off with a route from a `pushState`-enabled browser,        // but we're currently in a browser that doesn't support it...        if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {          this.fragment = this.getFragment(null, true); -        window.location.replace(this.options.root + '#' + this.fragment); +        this.location.replace(this.root + this.location.search + '#' + this.fragment);          // Return immediately as browser will do redirect to new url          return true; @@ -1039,18 +1146,16 @@        // in a browser where it could be `pushState`-based instead...        } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {          this.fragment = this.getHash().replace(routeStripper, ''); -        window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment); +        this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);        } -      if (!this.options.silent) { -        return this.loadUrl(); -      } +      if (!this.options.silent) return this.loadUrl();      },      // Disable Backbone.history, perhaps temporarily. Not useful in a real app,      // but possibly useful for unit testing Routers.      stop: function() { -      $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl); +      Backbone.$(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl);        clearInterval(this._checkUrlInterval);        History.started = false;      }, @@ -1065,8 +1170,10 @@      // calls `loadUrl`, normalizing across the hidden iframe.      checkUrl: function(e) {        var current = this.getFragment(); -      if (current == this.fragment && this.iframe) current = this.getFragment(this.getHash(this.iframe)); -      if (current == this.fragment) return false; +      if (current === this.fragment && this.iframe) { +        current = this.getFragment(this.getHash(this.iframe)); +      } +      if (current === this.fragment) return false;        if (this.iframe) this.navigate(current);        this.loadUrl() || this.loadUrl(this.getHash());      }, @@ -1095,31 +1202,31 @@      navigate: function(fragment, options) {        if (!History.started) return false;        if (!options || options === true) options = {trigger: options}; -      var frag = (fragment || '').replace(routeStripper, ''); -      if (this.fragment == frag) return; +      fragment = this.getFragment(fragment || ''); +      if (this.fragment === fragment) return; +      this.fragment = fragment; +      var url = this.root + fragment;        // If pushState is available, we use it to set the fragment as a real URL.        if (this._hasPushState) { -        if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag; -        this.fragment = frag; -        window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag); +        this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);        // If hash changes haven't been explicitly disabled, update the hash        // fragment to store history.        } else if (this._wantsHashChange) { -        this.fragment = frag; -        this._updateHash(window.location, frag, options.replace); -        if (this.iframe && (frag != this.getFragment(this.getHash(this.iframe)))) { -          // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change. -          // When replace is true, we don't want this. +        this._updateHash(this.location, fragment, options.replace); +        if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) { +          // Opening and closing the iframe tricks IE7 and earlier to push a +          // history entry on hash-tag change.  When replace is true, we don't +          // want this.            if(!options.replace) this.iframe.document.open().close(); -          this._updateHash(this.iframe.location, frag, options.replace); +          this._updateHash(this.iframe.location, fragment, options.replace);          }        // If you've told us that you explicitly don't want fallback hashchange-        // based history, then `navigate` becomes a page refresh.        } else { -        window.location.assign(this.options.root + fragment); +        return this.location.assign(url);        }        if (options.trigger) this.loadUrl(fragment);      }, @@ -1128,13 +1235,19 @@      // a new one to the browser history.      _updateHash: function(location, fragment, replace) {        if (replace) { -        location.replace(location.toString().replace(/(javascript:|#).*$/, '') + '#' + fragment); +        var href = location.href.replace(/(javascript:|#).*$/, ''); +        location.replace(href + '#' + fragment);        } else { -        location.hash = fragment; +        // Some browsers require that `hash` contains a leading #. +        location.hash = '#' + fragment;        }      } +    }); +  // Create the default Backbone.history. +  Backbone.history = new History; +    // Backbone.View    // ------------- @@ -1152,7 +1265,7 @@    var delegateEventSplitter = /^(\S+)\s*(.*)$/;    // List of view options to be merged as properties. -  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName']; +  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];    // Set up all inheritable **Backbone.View** properties and methods.    _.extend(View.prototype, Events, { @@ -1177,10 +1290,11 @@        return this;      }, -    // Remove this view from the DOM. Note that the view isn't present in the -    // DOM by default, so calling this method may be a no-op. +    // Remove this view by taking the element out of the DOM, and removing any +    // applicable Backbone.Events listeners.      remove: function() {        this.$el.remove(); +      this.stopListening();        return this;      }, @@ -1191,8 +1305,8 @@      //      make: function(tagName, attributes, content) {        var el = document.createElement(tagName); -      if (attributes) $(el).attr(attributes); -      if (content) $(el).html(content); +      if (attributes) Backbone.$(el).attr(attributes); +      if (content != null) Backbone.$(el).html(content);        return el;      }, @@ -1200,7 +1314,7 @@      // re-delegation.      setElement: function(element, delegate) {        if (this.$el) this.undelegateEvents(); -      this.$el = (element instanceof $) ? element : $(element); +      this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);        this.el = this.$el[0];        if (delegate !== false) this.delegateEvents();        return this; @@ -1222,7 +1336,7 @@      // This only works for delegate-able events: not `focus`, `blur`, and      // not `change`, `submit`, and `reset` in Internet Explorer.      delegateEvents: function(events) { -      if (!(events || (events = getValue(this, 'events')))) return; +      if (!(events || (events = _.result(this, 'events')))) return;        this.undelegateEvents();        for (var key in events) {          var method = events[key]; @@ -1251,11 +1365,8 @@      // Keys with special meaning *(model, collection, id, className)*, are      // attached directly to the view.      _configure: function(options) { -      if (this.options) options = _.extend({}, this.options, options); -      for (var i = 0, l = viewOptions.length; i < l; i++) { -        var attr = viewOptions[i]; -        if (options[attr]) this[attr] = options[attr]; -      } +      if (this.options) options = _.extend({}, _.result(this, 'options'), options); +      _.extend(this, _.pick(options, viewOptions));        this.options = options;      }, @@ -1265,27 +1376,17 @@      // an element from the `id`, `className` and `tagName` properties.      _ensureElement: function() {        if (!this.el) { -        var attrs = getValue(this, 'attributes') || {}; -        if (this.id) attrs.id = this.id; -        if (this.className) attrs['class'] = this.className; -        this.setElement(this.make(this.tagName, attrs), false); +        var attrs = _.extend({}, _.result(this, 'attributes')); +        if (this.id) attrs.id = _.result(this, 'id'); +        if (this.className) attrs['class'] = _.result(this, 'className'); +        this.setElement(this.make(_.result(this, 'tagName'), attrs), false);        } else { -        this.setElement(this.el, false); +        this.setElement(_.result(this, 'el'), false);        }      }    }); -  // The self-propagating extend function that Backbone classes use. -  var extend = function (protoProps, classProps) { -    var child = inherits(this, protoProps, classProps); -    child.extend = this.extend; -    return child; -  }; - -  // Set up inheritance for the model, collection, and view. -  Model.extend = Collection.extend = Router.extend = View.extend = extend; -    // Backbone.sync    // ------------- @@ -1293,6 +1394,7 @@    var methodMap = {      'create': 'POST',      'update': 'PUT', +    'patch':  'PATCH',      'delete': 'DELETE',      'read':   'GET'    }; @@ -1316,116 +1418,116 @@      var type = methodMap[method];      // Default options, unless specified. -    options || (options = {}); +    _.defaults(options || (options = {}), { +      emulateHTTP: Backbone.emulateHTTP, +      emulateJSON: Backbone.emulateJSON +    });      // Default JSON-request options.      var params = {type: type, dataType: 'json'};      // Ensure that we have a URL.      if (!options.url) { -      params.url = getValue(model, 'url') || urlError(); +      params.url = _.result(model, 'url') || urlError();      }      // Ensure that we have the appropriate request data. -    if (!options.data && model && (method == 'create' || method == 'update')) { +    if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {        params.contentType = 'application/json'; -      params.data = JSON.stringify(model.toJSON()); +      params.data = JSON.stringify(options.attrs || model.toJSON(options));      }      // For older servers, emulate JSON by encoding the request into an HTML-form. -    if (Backbone.emulateJSON) { +    if (options.emulateJSON) {        params.contentType = 'application/x-www-form-urlencoded';        params.data = params.data ? {model: params.data} : {};      }      // For older servers, emulate HTTP by mimicking the HTTP method with `_method`      // And an `X-HTTP-Method-Override` header. -    if (Backbone.emulateHTTP) { -      if (type === 'PUT' || type === 'DELETE') { -        if (Backbone.emulateJSON) params.data._method = type; -        params.type = 'POST'; -        params.beforeSend = function(xhr) { -          xhr.setRequestHeader('X-HTTP-Method-Override', type); -        }; -      } +    if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { +      params.type = 'POST'; +      if (options.emulateJSON) params.data._method = type; +      var beforeSend = options.beforeSend; +      options.beforeSend = function(xhr) { +        xhr.setRequestHeader('X-HTTP-Method-Override', type); +        if (beforeSend) return beforeSend.apply(this, arguments); +      };      }      // Don't process data on a non-GET request. -    if (params.type !== 'GET' && !Backbone.emulateJSON) { +    if (params.type !== 'GET' && !options.emulateJSON) {        params.processData = false;      } +    var success = options.success; +    options.success = function(resp, status, xhr) { +      if (success) success(resp, status, xhr); +      model.trigger('sync', model, resp, options); +    }; + +    var error = options.error; +    options.error = function(xhr, status, thrown) { +      if (error) error(model, xhr, options); +      model.trigger('error', model, xhr, options); +    }; +      // Make the request, allowing the user to override any Ajax options. -    return $.ajax(_.extend(params, options)); +    var xhr = Backbone.ajax(_.extend(params, options)); +    model.trigger('request', model, xhr, options); +    return xhr;    }; -  // Wrap an optional error callback with a fallback error event. -  Backbone.wrapError = function(onError, originalModel, options) { -    return function(model, resp) { -      resp = model === originalModel ? resp : model; -      if (onError) { -        onError(originalModel, resp, options); -      } else { -        originalModel.trigger('error', originalModel, resp, options); -      } -    }; +  // Set the default implementation of `Backbone.ajax` to proxy through to `$`. +  Backbone.ajax = function() { +    return Backbone.$.ajax.apply(Backbone.$, arguments);    };    // Helpers    // ------- -  // Shared empty constructor function to aid in prototype-chain creation. -  var ctor = function(){}; -    // Helper function to correctly set up the prototype chain, for subclasses.    // Similar to `goog.inherits`, but uses a hash of prototype properties and    // class properties to be extended. -  var inherits = function(parent, protoProps, staticProps) { +  var extend = function(protoProps, staticProps) { +    var parent = this;      var child;      // The constructor function for the new subclass is either defined by you      // (the "constructor" property in your `extend` definition), or defaulted      // by us to simply call the parent's constructor. -    if (protoProps && protoProps.hasOwnProperty('constructor')) { +    if (protoProps && _.has(protoProps, 'constructor')) {        child = protoProps.constructor;      } else {        child = function(){ parent.apply(this, arguments); };      } -    // Inherit class (static) properties from parent. -    _.extend(child, parent); +    // Add static properties to the constructor function, if supplied. +    _.extend(child, parent, staticProps);      // Set the prototype chain to inherit from `parent`, without calling      // `parent`'s constructor function. -    ctor.prototype = parent.prototype; -    child.prototype = new ctor(); +    var Surrogate = function(){ this.constructor = child; }; +    Surrogate.prototype = parent.prototype; +    child.prototype = new Surrogate;      // Add prototype properties (instance properties) to the subclass,      // if supplied.      if (protoProps) _.extend(child.prototype, protoProps); -    // Add static properties to the constructor function, if supplied. -    if (staticProps) _.extend(child, staticProps); - -    // Correctly set child's `prototype.constructor`. -    child.prototype.constructor = child; - -    // Set a convenience property in case the parent's prototype is needed later. +    // Set a convenience property in case the parent's prototype is needed +    // later.      child.__super__ = parent.prototype;      return child;    }; -  // Helper function to get a value from a Backbone object as a property -  // or as a function. -  var getValue = function(object, prop) { -    if (!(object && object[prop])) return null; -    return _.isFunction(object[prop]) ? object[prop]() : object[prop]; -  }; +  // Set up inheritance for the model, collection, router, view and history. +  Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;    // Throw an error when a URL is needed, and none is supplied.    var urlError = function() {      throw new Error('A "url" property or function must be specified');    }; -}).call(this);
\ No newline at end of file +}).call(this); diff --git a/module/web/static/js/libs/jquery-1.8.0.js b/module/web/static/js/libs/jquery-1.8.3.js index 43991b385..a86bf797a 100644 --- a/module/web/static/js/libs/jquery-1.8.0.js +++ b/module/web/static/js/libs/jquery-1.8.3.js @@ -1,5 +1,5 @@  /*! - * jQuery JavaScript Library v1.8.0 + * jQuery JavaScript Library v1.8.3   * http://jquery.com/   *   * Includes Sizzle.js @@ -9,7 +9,7 @@   * Released under the MIT license   * http://jquery.org/license   * - * Date: Thu Aug 09 2012 16:24:48 GMT-0400 (Eastern Daylight Time) + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)   */  (function( window, undefined ) {  var @@ -51,8 +51,8 @@ var  	core_rnotwhite = /\S/,  	core_rspace = /\s+/, -	// IE doesn't match non-breaking spaces with \s -	rtrim = core_rnotwhite.test("\xA0") ? (/^[\s\xA0]+|[\s\xA0]+$/g) : /^\s+|\s+$/g, +	// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) +	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,  	// A simple way to check for HTML strings  	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521) @@ -186,7 +186,7 @@ jQuery.fn = jQuery.prototype = {  	selector: "",  	// The current version of jQuery being used -	jquery: "1.8.0", +	jquery: "1.8.3",  	// The default length of a jQuery object is 0  	length: 0, @@ -573,7 +573,7 @@ jQuery.extend({  	},  	nodeName: function( elem, name ) { -		return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); +		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();  	},  	// args is for internal usage only @@ -619,7 +619,7 @@ jQuery.extend({  	},  	// Use native String.trim function wherever possible -	trim: core_trim ? +	trim: core_trim && !core_trim.call("\uFEFF\xA0") ?  		function( text ) {  			return text == null ?  				"" : @@ -630,7 +630,7 @@ jQuery.extend({  		function( text ) {  			return text == null ?  				"" : -				text.toString().replace( rtrim, "" ); +				( text + "" ).replace( rtrim, "" );  		},  	// results is for internal usage only @@ -776,7 +776,7 @@ jQuery.extend({  		};  		// Set the guid of unique handler to the same of original handler, so it can be removed -		proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; +		proxy.guid = fn.guid = fn.guid || jQuery.guid++;  		return proxy;  	}, @@ -844,9 +844,10 @@ jQuery.ready.promise = function( obj ) {  		readyList = jQuery.Deferred(); -		// Catch cases where $(document).ready() is called after the -		// browser event has already occurred. -		if ( document.readyState === "complete" || ( document.readyState !== "loading" && document.addEventListener ) ) { +		// Catch cases where $(document).ready() is called after the browser event has already occurred. +		// we once tried to use readyState "interactive" here, but it caused issues like the one +		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 +		if ( document.readyState === "complete" ) {  			// Handle it asynchronously to allow scripts the opportunity to delay ready  			setTimeout( jQuery.ready, 1 ); @@ -997,9 +998,12 @@ jQuery.Callbacks = function( options ) {  					var start = list.length;  					(function add( args ) {  						jQuery.each( args, function( _, arg ) { -							if ( jQuery.isFunction( arg ) && ( !options.unique || !self.has( arg ) ) ) { -								list.push( arg ); -							} else if ( arg && arg.length ) { +							var type = jQuery.type( arg ); +							if ( type === "function" ) { +								if ( !options.unique || !self.has( arg ) ) { +									list.push( arg ); +								} +							} else if ( arg && arg.length && type !== "string" ) {  								// Inspect recursively  								add( arg );  							} @@ -1141,7 +1145,7 @@ jQuery.extend({  				// Get a promise for this deferred  				// If obj is provided, the promise aspect is added to the object  				promise: function( obj ) { -					return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise; +					return obj != null ? jQuery.extend( obj, promise ) : promise;  				}  			},  			deferred = {}; @@ -1251,24 +1255,23 @@ jQuery.support = (function() {  		clickFn,  		div = document.createElement("div"); -	// Preliminary tests +	// Setup  	div.setAttribute( "className", "t" );  	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>"; +	// Support tests won't run in some limited or non-browser environments  	all = div.getElementsByTagName("*");  	a = div.getElementsByTagName("a")[ 0 ]; -	a.style.cssText = "top:1px;float:left;opacity:.5"; - -	// Can't get basic test support -	if ( !all || !all.length || !a ) { +	if ( !all || !a || !all.length ) {  		return {};  	} -	// First batch of supports tests +	// First batch of tests  	select = document.createElement("select");  	opt = select.appendChild( document.createElement("option") );  	input = div.getElementsByTagName("input")[ 0 ]; +	a.style.cssText = "top:1px;float:left;opacity:.5";  	support = {  		// IE strips leading whitespace when .innerHTML is used  		leadingWhitespace: ( div.firstChild.nodeType === 3 ), @@ -1310,7 +1313,7 @@ jQuery.support = (function() {  		// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)  		getSetAttribute: div.className !== "t", -		// Tests for enctype support on a form(#6743) +		// Tests for enctype support on a form (#6743)  		enctype: !!document.createElement("form").enctype,  		// Makes sure cloning an html5 element does not cause problems @@ -1452,10 +1455,8 @@ jQuery.support = (function() {  		support.boxSizing = ( div.offsetWidth === 4 );  		support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); -		// NOTE: To any future maintainer, window.getComputedStyle was used here -		// instead of getComputedStyle because it gave a better gzip size. -		// The difference between window.getComputedStyle and getComputedStyle is -		// 7 bytes +		// NOTE: To any future maintainer, we've window.getComputedStyle +		// because jsdom on node.js will break without it.  		if ( window.getComputedStyle ) {  			support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";  			support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; @@ -1505,7 +1506,7 @@ jQuery.support = (function() {  	return support;  })(); -var rbrace = /^(?:\{.*\}|\[.*\])$/, +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,  	rmultiDash = /([A-Z])/g;  jQuery.extend({ @@ -1513,7 +1514,7 @@ jQuery.extend({  	deletedIds: [], -	// Please use with caution +	// Remove at next major release (1.9/2.0)  	uuid: 0,  	// Unique for each copy of jQuery on the page @@ -1565,7 +1566,7 @@ jQuery.extend({  			// Only DOM nodes need a new unique ID for each element since their data  			// ends up in the global cache  			if ( isNode ) { -				elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid; +				elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;  			} else {  				id = internalKey;  			} @@ -1739,7 +1740,7 @@ jQuery.fn.extend({  					for ( l = attr.length; i < l; i++ ) {  						name = attr[i].name; -						if ( name.indexOf( "data-" ) === 0 ) { +						if ( !name.indexOf( "data-" ) ) {  							name = jQuery.camelCase( name.substring(5) );  							dataAttr( elem, name, data[ name ] ); @@ -1868,6 +1869,7 @@ jQuery.extend({  		type = type || "fx";  		var queue = jQuery.queue( elem, type ), +			startLength = queue.length,  			fn = queue.shift(),  			hooks = jQuery._queueHooks( elem, type ),  			next = function() { @@ -1877,6 +1879,7 @@ jQuery.extend({  		// If the fx queue is dequeued, always remove the progress sentinel  		if ( fn === "inprogress" ) {  			fn = queue.shift(); +			startLength--;  		}  		if ( fn ) { @@ -1891,7 +1894,8 @@ jQuery.extend({  			delete hooks.stop;  			fn.call( elem, next, hooks );  		} -		if ( !queue.length && hooks ) { + +		if ( !startLength && hooks ) {  			hooks.empty.fire();  		}  	}, @@ -1977,7 +1981,8 @@ jQuery.fn.extend({  		type = type || "fx";  		while( i-- ) { -			if ( (tmp = jQuery._data( elements[ i ], type + "queueHooks" )) && tmp.empty ) { +			tmp = jQuery._data( elements[ i ], type + "queueHooks" ); +			if ( tmp && tmp.empty ) {  				count++;  				tmp.empty.add( resolve );  			} @@ -2045,7 +2050,7 @@ jQuery.fn.extend({  						setClass = " " + elem.className + " ";  						for ( c = 0, cl = classNames.length; c < cl; c++ ) { -							if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { +							if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) {  								setClass += classNames[ c ] + " ";  							}  						} @@ -2078,7 +2083,7 @@ jQuery.fn.extend({  					// loop over each item in the removal list  					for ( c = 0, cl = removes.length; c < cl; c++ ) {  						// Remove until there is nothing to remove, -						while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) { +						while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) {  							className = className.replace( " " + removes[ c ] + " " , " " );  						}  					} @@ -2132,7 +2137,7 @@ jQuery.fn.extend({  			i = 0,  			l = this.length;  		for ( ; i < l; i++ ) { -			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { +			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {  				return true;  			}  		} @@ -2213,26 +2218,25 @@ jQuery.extend({  		},  		select: {  			get: function( elem ) { -				var value, i, max, option, -					index = elem.selectedIndex, -					values = [], +				var value, option,  					options = elem.options, -					one = elem.type === "select-one"; - -				// Nothing was selected -				if ( index < 0 ) { -					return null; -				} +					index = elem.selectedIndex, +					one = elem.type === "select-one" || index < 0, +					values = one ? null : [], +					max = one ? index + 1 : options.length, +					i = index < 0 ? +						max : +						one ? index : 0;  				// Loop through all the selected options -				i = one ? index : 0; -				max = one ? index + 1 : options.length;  				for ( ; i < max; i++ ) {  					option = options[ i ]; -					// Don't return options that are disabled or in a disabled optgroup -					if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && -							(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { +					// oldIE doesn't update selected after form reset (#2551) +					if ( ( option.selected || i === index ) && +							// Don't return options that are disabled or in a disabled optgroup +							( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && +							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {  						// Get the specific value for the option  						value = jQuery( option ).val(); @@ -2247,11 +2251,6 @@ jQuery.extend({  					}  				} -				// Fixes Bug #2551 -- select.val() broken in IE after form.reset() -				if ( one && !values.length && options.length ) { -					return jQuery( options[ index ] ).val(); -				} -  				return values;  			}, @@ -2310,7 +2309,7 @@ jQuery.extend({  				return ret;  			} else { -				elem.setAttribute( name, "" + value ); +				elem.setAttribute( name, value + "" );  				return value;  			} @@ -2574,7 +2573,7 @@ if ( !jQuery.support.style ) {  			return elem.style.cssText.toLowerCase() || undefined;  		},  		set: function( elem, value ) { -			return ( elem.style.cssText = "" + value ); +			return ( elem.style.cssText = value + "" );  		}  	};  } @@ -2707,6 +2706,7 @@ jQuery.event = {  				handler: handler,  				guid: handler.guid,  				selector: selector, +				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),  				namespace: namespaces.join(".")  			}, handleObjIn ); @@ -2942,7 +2942,7 @@ jQuery.event = {  			}  			// Note that this is a bare JS function and not a jQuery handler  			handle = ontype && cur[ ontype ]; -			if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { +			if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {  				event.preventDefault();  			}  		} @@ -2987,10 +2987,10 @@ jQuery.event = {  		// Make a writable jQuery.Event from the native event object  		event = jQuery.event.fix( event || window.event ); -		var i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related, +		var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,  			handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),  			delegateCount = handlers.delegateCount, -			args = [].slice.call( arguments ), +			args = core_slice.call( arguments ),  			run_all = !event.exclusive && !event.namespace,  			special = jQuery.event.special[ event.type ] || {},  			handlerQueue = []; @@ -3008,23 +3008,20 @@ jQuery.event = {  		// Avoid non-left-click bubbling in Firefox (#3861)  		if ( delegateCount && !(event.button && event.type === "click") ) { -			// Pregenerate a single jQuery object for reuse with .is() -			jqcur = jQuery(this); -			jqcur.context = this; -  			for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { -				// Don't process clicks (ONLY) on disabled elements (#6911, #8165, #xxxx) +				// Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)  				if ( cur.disabled !== true || event.type !== "click" ) {  					selMatch = {};  					matches = []; -					jqcur[0] = cur;  					for ( i = 0; i < delegateCount; i++ ) {  						handleObj = handlers[ i ];  						sel = handleObj.selector;  						if ( selMatch[ sel ] === undefined ) { -							selMatch[ sel ] = jqcur.is( sel ); +							selMatch[ sel ] = handleObj.needsContext ? +								jQuery( sel, this ).index( cur ) >= 0 : +								jQuery.find( sel, this, null, [ cur ] ).length;  						}  						if ( selMatch[ sel ] ) {  							matches.push( handleObj ); @@ -3165,11 +3162,6 @@ jQuery.event = {  	},  	special: { -		ready: { -			// Make sure the ready event is setup -			setup: jQuery.bindReady -		}, -  		load: {  			// Prevent triggered image.load events from bubbling to window.load  			noBubble: true @@ -3236,7 +3228,7 @@ jQuery.removeEvent = document.removeEventListener ?  		if ( elem.detachEvent ) { -			// #8545, #7054, preventing memory leaks for custom events in IE6-8 ââ¬â +			// #8545, #7054, preventing memory leaks for custom events in IE6-8  			// detachEvent needed property on element, by name of that event, to properly expose it to GC  			if ( typeof elem[ name ] === "undefined" ) {  				elem[ name ] = null; @@ -3458,7 +3450,7 @@ if ( !jQuery.support.changeBubbles ) {  		teardown: function() {  			jQuery.event.remove( this, "._change" ); -			return rformElems.test( this.nodeName ); +			return !rformElems.test( this.nodeName );  		}  	};  } @@ -3599,7 +3591,7 @@ jQuery.fn.extend({  	},  	undelegate: function( selector, types, fn ) {  		// ( namespace ) or ( selector, types [, fn] ) -		return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); +		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );  	},  	trigger: function( type, data ) { @@ -3668,1463 +3660,1685 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl  		jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;  	}  }); -/*! - * Sizzle CSS Selector Engine - *  Copyright 2012 jQuery Foundation and other contributors - *  Released under the MIT license - *  http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var cachedruns, -	dirruns, -	sortOrder, -	siblingCheck, -	assertGetIdNotName, - -	document = window.document, -	docElem = document.documentElement, - -	strundefined = "undefined", -	hasDuplicate = false, -	baseHasDuplicate = true, -	done = 0, -	slice = [].slice, -	push = [].push, - -	expando = ( "sizcache" + Math.random() ).replace( ".", "" ), - -	// Regex - -	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace -	whitespace = "[\\x20\\t\\r\\n\\f]", -	// http://www.w3.org/TR/css3-syntax/#characters -	characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", - -	// Loosely modeled on CSS identifier characters -	// An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) -	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier -	identifier = characterEncoding.replace( "w", "w#" ), - -	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors -	operators = "([*^$|!~]?=)", -	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + -		"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", -	pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)", -	pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)", -	combinators = whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*", -	groups = "(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|" + attributes + "|" + pseudos.replace( 2, 7 ) + "|[^\\\\(),])+", - -	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter -	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - -	rcombinators = new RegExp( "^" + combinators ), - -	// All simple (non-comma) selectors, excluding insignifant trailing whitespace -	rgroups = new RegExp( groups + "?(?=" + whitespace + "*,|$)", "g" ), - -	// A selector, or everything after leading whitespace -	// Optionally followed in either case by a ")" for terminating sub-selectors -	rselector = new RegExp( "^(?:(?!,)(?:(?:^|,)" + whitespace + "*" + groups + ")*?|" + whitespace + "*(.*?))(\\)|$)" ), - -	// All combinators and selector components (attribute test, tag, pseudo, etc.), the latter appearing together when consecutive -	rtokens = new RegExp( groups.slice( 19, -6 ) + "\\x20\\t\\r\\n\\f>+~])+|" + combinators, "g" ), - -	// Easily-parseable/retrievable ID or TAG or CLASS selectors -	rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, - -	rsibling = /[\x20\t\r\n\f]*[+~]/, -	rendsWithNot = /:not\($/, - -	rheader = /h\d/i, -	rinputs = /input|select|textarea|button/i, - -	rbackslash = /\\(?!\\)/g, - -	matchExpr = { -		"ID": new RegExp( "^#(" + characterEncoding + ")" ), -		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), -		"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), -		"TAG": new RegExp( "^(" + characterEncoding.replace( "[-", "[-\\*" ) + ")" ), -		"ATTR": new RegExp( "^" + attributes ), -		"PSEUDO": new RegExp( "^" + pseudos ), -		"CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace + -			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + -			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ), -		"POS": new RegExp( pos, "ig" ), -		// For use in libraries implementing .is() -		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) -	}, - -	classCache = {}, -	cachedClasses = [], -	compilerCache = {}, -	cachedSelectors = [], - -	// Mark a function for use in filtering -	markFunction = function( fn ) { -		fn.sizzleFilter = true; -		return fn; -	}, - -	// Returns a function to use in pseudos for input types -	createInputFunction = function( type ) { -		return function( elem ) { -			// Check the input's nodeName and type -			return elem.nodeName.toLowerCase() === "input" && elem.type === type; -		}; -	}, - -	// Returns a function to use in pseudos for buttons -	createButtonFunction = function( type ) { -		return function( elem ) { -			var name = elem.nodeName.toLowerCase(); -			return (name === "input" || name === "button") && elem.type === type; -		}; -	}, - -	// Used for testing something on an element -	assert = function( fn ) { -		var pass = false, -			div = document.createElement("div"); -		try { -			pass = fn( div ); -		} catch (e) {} -		// release memory in IE -		div = null; -		return pass; -	}, - -	// Check if attributes should be retrieved by attribute nodes -	assertAttributes = assert(function( div ) { -		div.innerHTML = "<select></select>"; -		var type = typeof div.lastChild.getAttribute("multiple"); -		// IE8 returns a string for some attributes even when not present -		return type !== "boolean" && type !== "string"; -	}), - -	// Check if getElementById returns elements by name -	// Check if getElementsByName privileges form controls or returns elements by ID -	assertUsableName = assert(function( div ) { -		// Inject content -		div.id = expando + 0; -		div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>"; -		docElem.insertBefore( div, docElem.firstChild ); - -		// Test -		var pass = document.getElementsByName && -			// buggy browsers will return fewer than the correct 2 -			document.getElementsByName( expando ).length === -			// buggy browsers will return more than the correct 0 -			2 + document.getElementsByName( expando + 0 ).length; -		assertGetIdNotName = !document.getElementById( expando ); - -		// Cleanup -		docElem.removeChild( div ); - -		return pass; -	}), - -	// Check if the browser returns only elements -	// when doing getElementsByTagName("*") -	assertTagNameNoComments = assert(function( div ) { -		div.appendChild( document.createComment("") ); -		return div.getElementsByTagName("*").length === 0; -	}), - -	// Check if getAttribute returns normalized href attributes -	assertHrefNotNormalized = assert(function( div ) { -		div.innerHTML = "<a href='#'></a>"; -		return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && -			div.firstChild.getAttribute("href") === "#"; -	}), - -	// Check if getElementsByClassName can be trusted -	assertUsableClassName = assert(function( div ) { -		// Opera can't find a second classname (in 9.6) -		div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>"; -		if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { -			return false; -		} - -		// Safari caches class attributes, doesn't catch changes (in 3.2) -		div.lastChild.className = "e"; -		return div.getElementsByClassName("e").length !== 1; -	}); - -var Sizzle = function( selector, context, results, seed ) { -	results = results || []; -	context = context || document; -	var match, elem, xml, m, -		nodeType = context.nodeType; - -	if ( nodeType !== 1 && nodeType !== 9 ) { -		return []; -	} - -	if ( !selector || typeof selector !== "string" ) { -		return results; -	} - -	xml = isXML( context ); - -	if ( !xml && !seed ) { -		if ( (match = rquickExpr.exec( selector )) ) { -			// Speed-up: Sizzle("#ID") -			if ( (m = match[1]) ) { -				if ( nodeType === 9 ) { -					elem = context.getElementById( m ); -					// Check parentNode to catch when Blackberry 4.6 returns -					// nodes that are no longer in the document #6963 -					if ( elem && elem.parentNode ) { -						// Handle the case where IE, Opera, and Webkit return items -						// by name instead of ID -						if ( elem.id === m ) { -							results.push( elem ); -							return results; -						} -					} else { -						return results; -					} -				} else { -					// Context is not a document -					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && -						contains( context, elem ) && elem.id === m ) { -						results.push( elem ); -						return results; -					} -				} - -			// Speed-up: Sizzle("TAG") -			} else if ( match[2] ) { -				push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); -				return results; - -			// Speed-up: Sizzle(".CLASS") -			} else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { -				push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); -				return results; -			} -		} -	} - -	// All others -	return select( selector, context, results, seed, xml ); -}; - -var Expr = Sizzle.selectors = { - -	// Can be adjusted by the user -	cacheLength: 50, - -	match: matchExpr, - -	order: [ "ID", "TAG" ], - -	attrHandle: {}, - -	createPseudo: markFunction, - -	find: { -		"ID": assertGetIdNotName ? -			function( id, context, xml ) { -				if ( typeof context.getElementById !== strundefined && !xml ) { -					var m = context.getElementById( id ); -					// Check parentNode to catch when Blackberry 4.6 returns -					// nodes that are no longer in the document #6963 -					return m && m.parentNode ? [m] : []; -				} -			} : -			function( id, context, xml ) { -				if ( typeof context.getElementById !== strundefined && !xml ) { -					var m = context.getElementById( id ); - -					return m ? -						m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? -							[m] : -							undefined : -						[]; -				} -			}, - -		"TAG": assertTagNameNoComments ? -			function( tag, context ) { -				if ( typeof context.getElementsByTagName !== strundefined ) { -					return context.getElementsByTagName( tag ); -				} -			} : -			function( tag, context ) { -				var results = context.getElementsByTagName( tag ); - -				// Filter out possible comments -				if ( tag === "*" ) { -					var elem, -						tmp = [], -						i = 0; - -					for ( ; (elem = results[i]); i++ ) { -						if ( elem.nodeType === 1 ) { -							tmp.push( elem ); -						} -					} - -					return tmp; -				} -				return results; -			} -	}, - -	relative: { -		">": { dir: "parentNode", first: true }, -		" ": { dir: "parentNode" }, -		"+": { dir: "previousSibling", first: true }, -		"~": { dir: "previousSibling" } -	}, - -	preFilter: { -		"ATTR": function( match ) { -			match[1] = match[1].replace( rbackslash, "" ); - -			// Move the given value to match[3] whether quoted or unquoted -			match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); - -			if ( match[2] === "~=" ) { -				match[3] = " " + match[3] + " "; -			} - -			return match.slice( 0, 4 ); -		}, - -		"CHILD": function( match ) { -			/* matches from matchExpr.CHILD -				1 type (only|nth|...) -				2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) -				3 xn-component of xn+y argument ([+-]?\d*n|) -				4 sign of xn-component -				5 x of xn-component -				6 sign of y-component -				7 y of y-component -			*/ -			match[1] = match[1].toLowerCase(); - -			if ( match[1] === "nth" ) { -				// nth-child requires argument -				if ( !match[2] ) { -					Sizzle.error( match[0] ); -				} - -				// numeric x and y parameters for Expr.filter.CHILD -				// remember that false/true cast respectively to 0/1 -				match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); -				match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); - -			// other types prohibit arguments -			} else if ( match[2] ) { -				Sizzle.error( match[0] ); -			} - -			return match; -		}, - -		"PSEUDO": function( match ) { -			var argument, -				unquoted = match[4]; - -			if ( matchExpr["CHILD"].test( match[0] ) ) { -				return null; -			} - -			// Relinquish our claim on characters in `unquoted` from a closing parenthesis on -			if ( unquoted && (argument = rselector.exec( unquoted )) && argument.pop() ) { - -				match[0] = match[0].slice( 0, argument[0].length - unquoted.length - 1 ); -				unquoted = argument[0].slice( 0, -1 ); -			} - -			// Quoted or unquoted, we have the full argument -			// Return only captures needed by the pseudo filter method (type and argument) -			match.splice( 2, 3, unquoted || match[3] ); -			return match; -		} -	}, - -	filter: { -		"ID": assertGetIdNotName ? -			function( id ) { -				id = id.replace( rbackslash, "" ); -				return function( elem ) { -					return elem.getAttribute("id") === id; -				}; -			} : -			function( id ) { -				id = id.replace( rbackslash, "" ); -				return function( elem ) { -					var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); -					return node && node.value === id; -				}; -			}, - -		"TAG": function( nodeName ) { -			if ( nodeName === "*" ) { -				return function() { return true; }; -			} -			nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); - -			return function( elem ) { -				return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; -			}; -		}, - -		"CLASS": function( className ) { -			var pattern = classCache[ className ]; -			if ( !pattern ) { -				pattern = classCache[ className ] = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" ); -				cachedClasses.push( className ); -				// Avoid too large of a cache -				if ( cachedClasses.length > Expr.cacheLength ) { -					delete classCache[ cachedClasses.shift() ]; -				} -			} -			return function( elem ) { -				return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); -			}; -		}, - -		"ATTR": function( name, operator, check ) { -			if ( !operator ) { -				return function( elem ) { -					return Sizzle.attr( elem, name ) != null; -				}; -			} - -			return function( elem ) { -				var result = Sizzle.attr( elem, name ), -					value = result + ""; - -				if ( result == null ) { -					return operator === "!="; -				} - -				switch ( operator ) { -					case "=": -						return value === check; -					case "!=": -						return value !== check; -					case "^=": -						return check && value.indexOf( check ) === 0; -					case "*=": -						return check && value.indexOf( check ) > -1; -					case "$=": -						return check && value.substr( value.length - check.length ) === check; -					case "~=": -						return ( " " + value + " " ).indexOf( check ) > -1; -					case "|=": -						return value === check || value.substr( 0, check.length + 1 ) === check + "-"; -				} -			}; -		}, - -		"CHILD": function( type, argument, first, last ) { - -			if ( type === "nth" ) { -				var doneName = done++; - -				return function( elem ) { -					var parent, diff, -						count = 0, -						node = elem; - -					if ( first === 1 && last === 0 ) { -						return true; -					} - -					parent = elem.parentNode; - -					if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) { -						for ( node = parent.firstChild; node; node = node.nextSibling ) { -							if ( node.nodeType === 1 ) { -								node.sizset = ++count; -								if ( node === elem ) { -									break; -								} -							} -						} - -						parent[ expando ] = doneName; -					} - -					diff = elem.sizset - last; - -					if ( first === 0 ) { -						return diff === 0; - -					} else { -						return ( diff % first === 0 && diff / first >= 0 ); -					} -				}; -			} - -			return function( elem ) { -				var node = elem; - -				switch ( type ) { -					case "only": -					case "first": -						while ( (node = node.previousSibling) ) { -							if ( node.nodeType === 1 ) { -								return false; -							} -						} - -						if ( type === "first" ) { -							return true; -						} - -						node = elem; - -						/* falls through */ -					case "last": -						while ( (node = node.nextSibling) ) { -							if ( node.nodeType === 1 ) { -								return false; -							} -						} - -						return true; -				} -			}; -		}, - -		"PSEUDO": function( pseudo, argument, context, xml ) { -			// pseudo-class names are case-insensitive -			// http://www.w3.org/TR/selectors/#pseudo-classes -			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters -			var fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ]; - -			if ( !fn ) { -				Sizzle.error( "unsupported pseudo: " + pseudo ); -			} - -			// The user may set fn.sizzleFilter to indicate -			// that arguments are needed to create the filter function -			// just as Sizzle does -			if ( !fn.sizzleFilter ) { -				return fn; -			} - -			return fn( argument, context, xml ); -		} -	}, - -	pseudos: { -		"not": markFunction(function( selector, context, xml ) { -			// Trim the selector passed to compile -			// to avoid treating leading and trailing -			// spaces as combinators -			var matcher = compile( selector.replace( rtrim, "$1" ), context, xml ); -			return function( elem ) { -				return !matcher( elem ); -			}; -		}), - -		"enabled": function( elem ) { -			return elem.disabled === false; -		}, - -		"disabled": function( elem ) { -			return elem.disabled === true; -		}, - -		"checked": function( elem ) { -			// In CSS3, :checked should return both checked and selected elements -			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked -			var nodeName = elem.nodeName.toLowerCase(); -			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); -		}, - -		"selected": function( elem ) { -			// Accessing this property makes selected-by-default -			// options in Safari work properly -			if ( elem.parentNode ) { -				elem.parentNode.selectedIndex; -			} - -			return elem.selected === true; -		}, - -		"parent": function( elem ) { -			return !Expr.pseudos["empty"]( elem ); -		}, - -		"empty": function( elem ) { -			// http://www.w3.org/TR/selectors/#empty-pseudo -			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), -			//   not comment, processing instructions, or others -			// Thanks to Diego Perini for the nodeName shortcut -			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?") -			var nodeType; -			elem = elem.firstChild; -			while ( elem ) { -				if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { -					return false; -				} -				elem = elem.nextSibling; -			} -			return true; -		}, - -		"contains": markFunction(function( text ) { -			return function( elem ) { -				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; -			}; -		}), - -		"has": markFunction(function( selector ) { -			return function( elem ) { -				return Sizzle( selector, elem ).length > 0; -			}; -		}), - -		"header": function( elem ) { -			return rheader.test( elem.nodeName ); -		}, - -		"text": function( elem ) { -			var type, attr; -			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) -			// use getAttribute instead to test this case -			return elem.nodeName.toLowerCase() === "input" && -				(type = elem.type) === "text" && -				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); -		}, - -		// Input types -		"radio": createInputFunction("radio"), -		"checkbox": createInputFunction("checkbox"), -		"file": createInputFunction("file"), -		"password": createInputFunction("password"), -		"image": createInputFunction("image"), - -		"submit": createButtonFunction("submit"), -		"reset": createButtonFunction("reset"), - -		"button": function( elem ) { -			var name = elem.nodeName.toLowerCase(); -			return name === "input" && elem.type === "button" || name === "button"; -		}, - -		"input": function( elem ) { -			return rinputs.test( elem.nodeName ); -		}, - -		"focus": function( elem ) { -			var doc = elem.ownerDocument; -			return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); -		}, - -		"active": function( elem ) { -			return elem === elem.ownerDocument.activeElement; -		} -	}, - -	setFilters: { -		"first": function( elements, argument, not ) { -			return not ? elements.slice( 1 ) : [ elements[0] ]; -		}, - -		"last": function( elements, argument, not ) { -			var elem = elements.pop(); -			return not ? elements : [ elem ]; -		}, - -		"even": function( elements, argument, not ) { -			var results = [], -				i = not ? 1 : 0, -				len = elements.length; -			for ( ; i < len; i = i + 2 ) { -				results.push( elements[i] ); -			} -			return results; -		}, - -		"odd": function( elements, argument, not ) { -			var results = [], -				i = not ? 0 : 1, -				len = elements.length; -			for ( ; i < len; i = i + 2 ) { -				results.push( elements[i] ); -			} -			return results; -		}, - -		"lt": function( elements, argument, not ) { -			return not ? elements.slice( +argument ) : elements.slice( 0, +argument ); -		}, - -		"gt": function( elements, argument, not ) { -			return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 ); -		}, - -		"eq": function( elements, argument, not ) { -			var elem = elements.splice( +argument, 1 ); -			return not ? elements : elem; -		} -	} -}; - -// Deprecated -Expr.setFilters["nth"] = Expr.setFilters["eq"]; - -// Back-compat -Expr.filters = Expr.pseudos; - -// IE6/7 return a modified href -if ( !assertHrefNotNormalized ) { -	Expr.attrHandle = { -		"href": function( elem ) { -			return elem.getAttribute( "href", 2 ); -		}, -		"type": function( elem ) { -			return elem.getAttribute("type"); -		} -	}; -} - -// Add getElementsByName if usable -if ( assertUsableName ) { -	Expr.order.push("NAME"); -	Expr.find["NAME"] = function( name, context ) { -		if ( typeof context.getElementsByName !== strundefined ) { -			return context.getElementsByName( name ); -		} -	}; -} - -// Add getElementsByClassName if usable -if ( assertUsableClassName ) { -	Expr.order.splice( 1, 0, "CLASS" ); -	Expr.find["CLASS"] = function( className, context, xml ) { -		if ( typeof context.getElementsByClassName !== strundefined && !xml ) { -			return context.getElementsByClassName( className ); -		} -	}; -} - -// If slice is not available, provide a backup -try { -	slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { -	slice = function( i ) { -		var elem, results = []; -		for ( ; (elem = this[i]); i++ ) { -			results.push( elem ); -		} -		return results; -	}; -} - -var isXML = Sizzle.isXML = function( elem ) { -	// documentElement is verified for cases where it doesn't yet exist -	// (such as loading iframes in IE - #4833) -	var documentElement = elem && (elem.ownerDocument || elem).documentElement; -	return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Element contains another -var contains = Sizzle.contains = docElem.compareDocumentPosition ? -	function( a, b ) { -		return !!( a.compareDocumentPosition( b ) & 16 ); -	} : -	docElem.contains ? -	function( a, b ) { -		var adown = a.nodeType === 9 ? a.documentElement : a, -			bup = b.parentNode; -		return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); -	} : -	function( a, b ) { -		while ( (b = b.parentNode) ) { -			if ( b === a ) { -				return true; -			} -		} -		return false; -	}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -var getText = Sizzle.getText = function( elem ) { -	var node, -		ret = "", -		i = 0, -		nodeType = elem.nodeType; - -	if ( nodeType ) { -		if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { -			// Use textContent for elements -			// innerText usage removed for consistency of new lines (see #11153) -			if ( typeof elem.textContent === "string" ) { -				return elem.textContent; -			} else { -				// Traverse its children -				for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { -					ret += getText( elem ); -				} -			} -		} else if ( nodeType === 3 || nodeType === 4 ) { -			return elem.nodeValue; -		} -		// Do not include comment or processing instruction nodes -	} else { - -		// If no nodeType, this is expected to be an array -		for ( ; (node = elem[i]); i++ ) { -			// Do not traverse comment nodes -			ret += getText( node ); -		} -	} -	return ret; -}; - -Sizzle.attr = function( elem, name ) { -	var attr, -		xml = isXML( elem ); - -	if ( !xml ) { -		name = name.toLowerCase(); -	} -	if ( Expr.attrHandle[ name ] ) { -		return Expr.attrHandle[ name ]( elem ); -	} -	if ( assertAttributes || xml ) { -		return elem.getAttribute( name ); -	} -	attr = elem.getAttributeNode( name ); -	return attr ? -		typeof elem[ name ] === "boolean" ? -			elem[ name ] ? name : null : -			attr.specified ? attr.value : null : -		null; -}; - -Sizzle.error = function( msg ) { -	throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -// Check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -//   Thus far that includes Google Chrome. -[0, 0].sort(function() { -	return (baseHasDuplicate = 0); -}); - - -if ( docElem.compareDocumentPosition ) { -	sortOrder = function( a, b ) { -		if ( a === b ) { -			hasDuplicate = true; -			return 0; -		} - -		return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? -			a.compareDocumentPosition : -			a.compareDocumentPosition(b) & 4 -		) ? -1 : 1; -	}; - -} else { -	sortOrder = function( a, b ) { -		// The nodes are identical, we can exit early -		if ( a === b ) { -			hasDuplicate = true; -			return 0; - -		// Fallback to using sourceIndex (in IE) if it's available on both nodes -		} else if ( a.sourceIndex && b.sourceIndex ) { -			return a.sourceIndex - b.sourceIndex; -		} - -		var al, bl, -			ap = [], -			bp = [], -			aup = a.parentNode, -			bup = b.parentNode, -			cur = aup; - -		// If the nodes are siblings (or identical) we can do a quick check -		if ( aup === bup ) { -			return siblingCheck( a, b ); - -		// If no parents were found then the nodes are disconnected -		} else if ( !aup ) { -			return -1; - -		} else if ( !bup ) { -			return 1; -		} - -		// Otherwise they're somewhere else in the tree so we need -		// to build up a full list of the parentNodes for comparison -		while ( cur ) { -			ap.unshift( cur ); -			cur = cur.parentNode; -		} - -		cur = bup; - -		while ( cur ) { -			bp.unshift( cur ); -			cur = cur.parentNode; -		} - -		al = ap.length; -		bl = bp.length; - -		// Start walking down the tree looking for a discrepancy -		for ( var i = 0; i < al && i < bl; i++ ) { -			if ( ap[i] !== bp[i] ) { -				return siblingCheck( ap[i], bp[i] ); -			} -		} - -		// We ended someplace up the tree so do a sibling check -		return i === al ? -			siblingCheck( a, bp[i], -1 ) : -			siblingCheck( ap[i], b, 1 ); -	}; - -	siblingCheck = function( a, b, ret ) { -		if ( a === b ) { -			return ret; -		} - -		var cur = a.nextSibling; - -		while ( cur ) { -			if ( cur === b ) { -				return -1; -			} - -			cur = cur.nextSibling; -		} - -		return 1; -	}; -} - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { -	var elem, -		i = 1; - -	if ( sortOrder ) { -		hasDuplicate = baseHasDuplicate; -		results.sort( sortOrder ); - -		if ( hasDuplicate ) { -			for ( ; (elem = results[i]); i++ ) { -				if ( elem === results[ i - 1 ] ) { -					results.splice( i--, 1 ); -				} -			} -		} -	} - -	return results; -}; - -function multipleContexts( selector, contexts, results, seed ) { -	var i = 0, -		len = contexts.length; -	for ( ; i < len; i++ ) { -		Sizzle( selector, contexts[i], results, seed ); -	} -} - -function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) { -	var results, -		fn = Expr.setFilters[ posfilter.toLowerCase() ]; - -	if ( !fn ) { -		Sizzle.error( posfilter ); -	} - -	if ( selector || !(results = seed) ) { -		multipleContexts( selector || "*", contexts, (results = []), seed ); -	} - -	return results.length > 0 ? fn( results, argument, not ) : []; -} - -function handlePOS( selector, context, results, seed, groups ) { -	var match, not, anchor, ret, elements, currentContexts, part, lastIndex, -		i = 0, -		len = groups.length, -		rpos = matchExpr["POS"], -		// This is generated here in case matchExpr["POS"] is extended -		rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ), -		// This is for making sure non-participating -		// matching groups are represented cross-browser (IE6-8) -		setUndefined = function() { -			var i = 1, -				len = arguments.length - 2; -			for ( ; i < len; i++ ) { -				if ( arguments[i] === undefined ) { -					match[i] = undefined; -				} -			} -		}; - -	for ( ; i < len; i++ ) { -		// Reset regex index to 0 -		rpos.exec(""); -		selector = groups[i]; -		ret = []; -		anchor = 0; -		elements = seed; -		while ( (match = rpos.exec( selector )) ) { -			lastIndex = rpos.lastIndex = match.index + match[0].length; -			if ( lastIndex > anchor ) { -				part = selector.slice( anchor, match.index ); -				anchor = lastIndex; -				currentContexts = [ context ]; - -				if ( rcombinators.test(part) ) { -					if ( elements ) { -						currentContexts = elements; -					} -					elements = seed; -				} - -				if ( (not = rendsWithNot.test( part )) ) { -					part = part.slice( 0, -5 ).replace( rcombinators, "$&*" ); -				} - -				if ( match.length > 1 ) { -					match[0].replace( rposgroups, setUndefined ); -				} -				elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not ); -			} -		} - -		if ( elements ) { -			ret = ret.concat( elements ); - -			if ( (part = selector.slice( anchor )) && part !== ")" ) { -				if ( rcombinators.test(part) ) { -					multipleContexts( part, ret, results, seed ); -				} else { -					Sizzle( part, context, results, seed ? seed.concat(elements) : elements ); -				} -			} else { -				push.apply( results, ret ); -			} -		} else { -			Sizzle( selector, context, results, seed ); -		} -	} - -	// Do not sort if this is a single filter -	return len === 1 ? results : Sizzle.uniqueSort( results ); -} - -function tokenize( selector, context, xml ) { -	var tokens, soFar, type, -		groups = [], -		i = 0, - -		// Catch obvious selector issues: terminal ")"; nonempty fallback match -		// rselector never fails to match *something* -		match = rselector.exec( selector ), -		matched = !match.pop() && !match.pop(), -		selectorGroups = matched && selector.match( rgroups ) || [""], - -		preFilters = Expr.preFilter, -		filters = Expr.filter, -		checkContext = !xml && context !== document; - -	for ( ; (soFar = selectorGroups[i]) != null && matched; i++ ) { -		groups.push( tokens = [] ); - -		// Need to make sure we're within a narrower context if necessary -		// Adding a descendant combinator will generate what is needed -		if ( checkContext ) { -			soFar = " " + soFar; -		} - -		while ( soFar ) { -			matched = false; - -			// Combinators -			if ( (match = rcombinators.exec( soFar )) ) { -				soFar = soFar.slice( match[0].length ); - -				// Cast descendant combinators to space -				matched = tokens.push({ part: match.pop().replace( rtrim, " " ), captures: match }); -			} - -			// Filters -			for ( type in filters ) { -				if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || -					(match = preFilters[ type ]( match, context, xml )) ) ) { - -					soFar = soFar.slice( match.shift().length ); -					matched = tokens.push({ part: type, captures: match }); -				} -			} - -			if ( !matched ) { -				break; -			} -		} -	} - -	if ( !matched ) { -		Sizzle.error( selector ); -	} - -	return groups; -} - -function addCombinator( matcher, combinator, context ) { -	var dir = combinator.dir, -		doneName = done++; - -	if ( !matcher ) { -		// If there is no matcher to check, check against the context -		matcher = function( elem ) { -			return elem === context; -		}; -	} -	return combinator.first ? -		function( elem, context ) { -			while ( (elem = elem[ dir ]) ) { -				if ( elem.nodeType === 1 ) { -					return matcher( elem, context ) && elem; -				} -			} -		} : -		function( elem, context ) { -			var cache, -				dirkey = doneName + "." + dirruns, -				cachedkey = dirkey + "." + cachedruns; -			while ( (elem = elem[ dir ]) ) { -				if ( elem.nodeType === 1 ) { -					if ( (cache = elem[ expando ]) === cachedkey ) { -						return elem.sizset; -					} else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { -						if ( elem.sizset ) { -							return elem; -						} -					} else { -						elem[ expando ] = cachedkey; -						if ( matcher( elem, context ) ) { -							elem.sizset = true; -							return elem; -						} -						elem.sizset = false; -					} -				} -			} -		}; -} - -function addMatcher( higher, deeper ) { -	return higher ? -		function( elem, context ) { -			var result = deeper( elem, context ); -			return result && higher( result === true ? elem : result, context ); -		} : -		deeper; -} - -// ["TAG", ">", "ID", " ", "CLASS"] -function matcherFromTokens( tokens, context, xml ) { -	var token, matcher, -		i = 0; - -	for ( ; (token = tokens[i]); i++ ) { -		if ( Expr.relative[ token.part ] ) { -			matcher = addCombinator( matcher, Expr.relative[ token.part ], context ); -		} else { -			token.captures.push( context, xml ); -			matcher = addMatcher( matcher, Expr.filter[ token.part ].apply( null, token.captures ) ); -		} -	} - -	return matcher; -} - -function matcherFromGroupMatchers( matchers ) { -	return function( elem, context ) { -		var matcher, -			j = 0; -		for ( ; (matcher = matchers[j]); j++ ) { -			if ( matcher(elem, context) ) { -				return true; -			} -		} -		return false; -	}; -} - -var compile = Sizzle.compile = function( selector, context, xml ) { -	var tokens, group, i, -		cached = compilerCache[ selector ]; - -	// Return a cached group function if already generated (context dependent) -	if ( cached && cached.context === context ) { -		return cached; -	} - -	// Generate a function of recursive functions that can be used to check each element -	group = tokenize( selector, context, xml ); -	for ( i = 0; (tokens = group[i]); i++ ) { -		group[i] = matcherFromTokens( tokens, context, xml ); -	} - -	// Cache the compiled function -	cached = compilerCache[ selector ] = matcherFromGroupMatchers( group ); -	cached.context = context; -	cached.runs = cached.dirruns = 0; -	cachedSelectors.push( selector ); -	// Ensure only the most recent are cached -	if ( cachedSelectors.length > Expr.cacheLength ) { -		delete compilerCache[ cachedSelectors.shift() ]; -	} -	return cached; -}; - -Sizzle.matches = function( expr, elements ) { -	return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { -	return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -var select = function( selector, context, results, seed, xml ) { -	// Remove excessive whitespace -	selector = selector.replace( rtrim, "$1" ); -	var elements, matcher, i, len, elem, token, -		type, findContext, notTokens, -		match = selector.match( rgroups ), -		tokens = selector.match( rtokens ), -		contextNodeType = context.nodeType; - -	// POS handling -	if ( matchExpr["POS"].test(selector) ) { -		return handlePOS( selector, context, results, seed, match ); -	} - -	if ( seed ) { -		elements = slice.call( seed, 0 ); - -	// To maintain document order, only narrow the -	// set if there is one group -	} else if ( match && match.length === 1 ) { - -		// Take a shortcut and set the context if the root selector is an ID -		if ( tokens.length > 1 && contextNodeType === 9 && !xml && -				(match = matchExpr["ID"].exec( tokens[0] )) ) { - -			context = Expr.find["ID"]( match[1], context, xml )[0]; -			if ( !context ) { -				return results; -			} - -			selector = selector.slice( tokens.shift().length ); -		} - -		findContext = ( (match = rsibling.exec( tokens[0] )) && !match.index && context.parentNode ) || context; - -		// Get the last token, excluding :not -		notTokens = tokens.pop(); -		token = notTokens.split(":not")[0]; - -		for ( i = 0, len = Expr.order.length; i < len; i++ ) { -			type = Expr.order[i]; - -			if ( (match = matchExpr[ type ].exec( token )) ) { -				elements = Expr.find[ type ]( (match[1] || "").replace( rbackslash, "" ), findContext, xml ); - -				if ( elements == null ) { -					continue; -				} - -				if ( token === notTokens ) { -					selector = selector.slice( 0, selector.length - notTokens.length ) + -						token.replace( matchExpr[ type ], "" ); - -					if ( !selector ) { -						push.apply( results, slice.call(elements, 0) ); -					} -				} -				break; -			} -		} -	} - -	// Only loop over the given elements once -	// If selector is empty, we're already done -	if ( selector ) { -		matcher = compile( selector, context, xml ); -		dirruns = matcher.dirruns++; - -		if ( elements == null ) { -			elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context ); -		} -		for ( i = 0; (elem = elements[i]); i++ ) { -			cachedruns = matcher.runs++; -			if ( matcher(elem, context) ) { -				results.push( elem ); -			} -		} -	} - -	return results; -}; - -if ( document.querySelectorAll ) { -	(function() { -		var disconnectedMatch, -			oldSelect = select, -			rescape = /'|\\/g, -			rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, -			rbuggyQSA = [], -			// matchesSelector(:active) reports false when true (IE9/Opera 11.5) -			// A support test would require too much code (would include document ready) -			// just skip matchesSelector for :active -			rbuggyMatches = [":active"], -			matches = docElem.matchesSelector || -				docElem.mozMatchesSelector || -				docElem.webkitMatchesSelector || -				docElem.oMatchesSelector || -				docElem.msMatchesSelector; - -		// Build QSA regex -		// Regex strategy adopted from Diego Perini -		assert(function( div ) { -			div.innerHTML = "<select><option selected></option></select>"; - -			// IE8 - Some boolean attributes are not treated correctly -			if ( !div.querySelectorAll("[selected]").length ) { -				rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); -			} - -			// Webkit/Opera - :checked should return selected option elements -			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked -			// IE8 throws error here (do not put tests after this one) -			if ( !div.querySelectorAll(":checked").length ) { -				rbuggyQSA.push(":checked"); -			} -		}); - -		assert(function( div ) { - -			// Opera 10-12/IE9 - ^= $= *= and empty values -			// Should not select anything -			div.innerHTML = "<p test=''></p>"; -			if ( div.querySelectorAll("[test^='']").length ) { -				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); -			} - -			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) -			// IE8 throws error here (do not put tests after this one) -			div.innerHTML = "<input type='hidden'>"; -			if ( !div.querySelectorAll(":enabled").length ) { -				rbuggyQSA.push(":enabled", ":disabled"); -			} -		}); - -		rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - -		select = function( selector, context, results, seed, xml ) { -			// Only use querySelectorAll when not filtering, -			// when this is not xml, -			// and when no QSA bugs apply -			if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { -				if ( context.nodeType === 9 ) { -					try { -						push.apply( results, slice.call(context.querySelectorAll( selector ), 0) ); -						return results; -					} catch(qsaError) {} -				// qSA works strangely on Element-rooted queries -				// We can work around this by specifying an extra ID on the root -				// and working up from there (Thanks to Andrew Dupont for the technique) -				// IE 8 doesn't work on object elements -				} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { -					var old = context.getAttribute("id"), -						nid = old || expando, -						newContext = rsibling.test( selector ) && context.parentNode || context; - -					if ( old ) { -						nid = nid.replace( rescape, "\\$&" ); -					} else { -						context.setAttribute( "id", nid ); -					} - -					try { -						push.apply( results, slice.call( newContext.querySelectorAll( -							selector.replace( rgroups, "[id='" + nid + "'] $&" ) -						), 0 ) ); -						return results; -					} catch(qsaError) { -					} finally { -						if ( !old ) { -							context.removeAttribute("id"); -						} -					} -				} -			} - -			return oldSelect( selector, context, results, seed, xml ); -		}; - -		if ( matches ) { -			assert(function( div ) { -				// Check to see if it's possible to do matchesSelector -				// on a disconnected node (IE 9) -				disconnectedMatch = matches.call( div, "div" ); - -				// This should fail with an exception -				// Gecko does not error, returns false instead -				try { -					matches.call( div, "[test!='']:sizzle" ); -					rbuggyMatches.push( Expr.match.PSEUDO ); -				} catch ( e ) {} -			}); - -			// rbuggyMatches always contains :active, so no need for a length check -			rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); - -			Sizzle.matchesSelector = function( elem, expr ) { -				// Make sure that attribute selectors are quoted -				expr = expr.replace( rattributeQuotes, "='$1']" ); - -				// rbuggyMatches always contains :active, so no need for an existence check -				if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { -					try { -						var ret = matches.call( elem, expr ); - -						// IE 9's matchesSelector returns false on disconnected nodes -						if ( ret || disconnectedMatch || -								// As well, disconnected nodes are said to be in a document -								// fragment in IE 9 -								elem.document && elem.document.nodeType !== 11 ) { -							return ret; -						} -					} catch(e) {} -				} - -				return Sizzle( expr, null, null, [ elem ] ).length > 0; -			}; -		} -	})(); -} - +/*!
 + * Sizzle CSS Selector Engine
 + * Copyright 2012 jQuery Foundation and other contributors
 + * Released under the MIT license
 + * http://sizzlejs.com/
 + */
 +(function( window, undefined ) {
 +
 +var cachedruns,
 +	assertGetIdNotName,
 +	Expr,
 +	getText,
 +	isXML,
 +	contains,
 +	compile,
 +	sortOrder,
 +	hasDuplicate,
 +	outermostContext,
 +
 +	baseHasDuplicate = true,
 +	strundefined = "undefined",
 +
 +	expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
 +
 +	Token = String,
 +	document = window.document,
 +	docElem = document.documentElement,
 +	dirruns = 0,
 +	done = 0,
 +	pop = [].pop,
 +	push = [].push,
 +	slice = [].slice,
 +	// Use a stripped-down indexOf if a native one is unavailable
 +	indexOf = [].indexOf || function( elem ) {
 +		var i = 0,
 +			len = this.length;
 +		for ( ; i < len; i++ ) {
 +			if ( this[i] === elem ) {
 +				return i;
 +			}
 +		}
 +		return -1;
 +	},
 +
 +	// Augment a function for special use by Sizzle
 +	markFunction = function( fn, value ) {
 +		fn[ expando ] = value == null || value;
 +		return fn;
 +	},
 +
 +	createCache = function() {
 +		var cache = {},
 +			keys = [];
 +
 +		return markFunction(function( key, value ) {
 +			// Only keep the most recent entries
 +			if ( keys.push( key ) > Expr.cacheLength ) {
 +				delete cache[ keys.shift() ];
 +			}
 +
 +			// Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
 +			return (cache[ key + " " ] = value);
 +		}, cache );
 +	},
 +
 +	classCache = createCache(),
 +	tokenCache = createCache(),
 +	compilerCache = createCache(),
 +
 +	// Regex
 +
 +	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
 +	whitespace = "[\\x20\\t\\r\\n\\f]",
 +	// http://www.w3.org/TR/css3-syntax/#characters
 +	characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
 +
 +	// Loosely modeled on CSS identifier characters
 +	// An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
 +	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
 +	identifier = characterEncoding.replace( "w", "w#" ),
 +
 +	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
 +	operators = "([*^$|!~]?=)",
 +	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
 +		"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
 +
 +	// Prefer arguments not in parens/brackets,
 +	//   then attribute selectors and non-pseudos (denoted by :),
 +	//   then anything else
 +	// These preferences are here to reduce the number of selectors
 +	//   needing tokenize in the PSEUDO preFilter
 +	pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
 +
 +	// For matchExpr.POS and matchExpr.needsContext
 +	pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
 +		"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",
 +
 +	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
 +	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
 +
 +	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
 +	rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
 +	rpseudo = new RegExp( pseudos ),
 +
 +	// Easily-parseable/retrievable ID or TAG or CLASS selectors
 +	rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
 +
 +	rnot = /^:not/,
 +	rsibling = /[\x20\t\r\n\f]*[+~]/,
 +	rendsWithNot = /:not\($/,
 +
 +	rheader = /h\d/i,
 +	rinputs = /input|select|textarea|button/i,
 +
 +	rbackslash = /\\(?!\\)/g,
 +
 +	matchExpr = {
 +		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
 +		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
 +		"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
 +		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
 +		"ATTR": new RegExp( "^" + attributes ),
 +		"PSEUDO": new RegExp( "^" + pseudos ),
 +		"POS": new RegExp( pos, "i" ),
 +		"CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
 +			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
 +			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
 +		// For use in libraries implementing .is()
 +		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
 +	},
 +
 +	// Support
 +
 +	// Used for testing something on an element
 +	assert = function( fn ) {
 +		var div = document.createElement("div");
 +
 +		try {
 +			return fn( div );
 +		} catch (e) {
 +			return false;
 +		} finally {
 +			// release memory in IE
 +			div = null;
 +		}
 +	},
 +
 +	// Check if getElementsByTagName("*") returns only elements
 +	assertTagNameNoComments = assert(function( div ) {
 +		div.appendChild( document.createComment("") );
 +		return !div.getElementsByTagName("*").length;
 +	}),
 +
 +	// Check if getAttribute returns normalized href attributes
 +	assertHrefNotNormalized = assert(function( div ) {
 +		div.innerHTML = "<a href='#'></a>";
 +		return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
 +			div.firstChild.getAttribute("href") === "#";
 +	}),
 +
 +	// Check if attributes should be retrieved by attribute nodes
 +	assertAttributes = assert(function( div ) {
 +		div.innerHTML = "<select></select>";
 +		var type = typeof div.lastChild.getAttribute("multiple");
 +		// IE8 returns a string for some attributes even when not present
 +		return type !== "boolean" && type !== "string";
 +	}),
 +
 +	// Check if getElementsByClassName can be trusted
 +	assertUsableClassName = assert(function( div ) {
 +		// Opera can't find a second classname (in 9.6)
 +		div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
 +		if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
 +			return false;
 +		}
 +
 +		// Safari 3.2 caches class attributes and doesn't catch changes
 +		div.lastChild.className = "e";
 +		return div.getElementsByClassName("e").length === 2;
 +	}),
 +
 +	// Check if getElementById returns elements by name
 +	// Check if getElementsByName privileges form controls or returns elements by ID
 +	assertUsableName = assert(function( div ) {
 +		// Inject content
 +		div.id = expando + 0;
 +		div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
 +		docElem.insertBefore( div, docElem.firstChild );
 +
 +		// Test
 +		var pass = document.getElementsByName &&
 +			// buggy browsers will return fewer than the correct 2
 +			document.getElementsByName( expando ).length === 2 +
 +			// buggy browsers will return more than the correct 0
 +			document.getElementsByName( expando + 0 ).length;
 +		assertGetIdNotName = !document.getElementById( expando );
 +
 +		// Cleanup
 +		docElem.removeChild( div );
 +
 +		return pass;
 +	});
 +
 +// If slice is not available, provide a backup
 +try {
 +	slice.call( docElem.childNodes, 0 )[0].nodeType;
 +} catch ( e ) {
 +	slice = function( i ) {
 +		var elem,
 +			results = [];
 +		for ( ; (elem = this[i]); i++ ) {
 +			results.push( elem );
 +		}
 +		return results;
 +	};
 +}
 +
 +function Sizzle( selector, context, results, seed ) {
 +	results = results || [];
 +	context = context || document;
 +	var match, elem, xml, m,
 +		nodeType = context.nodeType;
 +
 +	if ( !selector || typeof selector !== "string" ) {
 +		return results;
 +	}
 +
 +	if ( nodeType !== 1 && nodeType !== 9 ) {
 +		return [];
 +	}
 +
 +	xml = isXML( context );
 +
 +	if ( !xml && !seed ) {
 +		if ( (match = rquickExpr.exec( selector )) ) {
 +			// Speed-up: Sizzle("#ID")
 +			if ( (m = match[1]) ) {
 +				if ( nodeType === 9 ) {
 +					elem = context.getElementById( m );
 +					// Check parentNode to catch when Blackberry 4.6 returns
 +					// nodes that are no longer in the document #6963
 +					if ( elem && elem.parentNode ) {
 +						// Handle the case where IE, Opera, and Webkit return items
 +						// by name instead of ID
 +						if ( elem.id === m ) {
 +							results.push( elem );
 +							return results;
 +						}
 +					} else {
 +						return results;
 +					}
 +				} else {
 +					// Context is not a document
 +					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
 +						contains( context, elem ) && elem.id === m ) {
 +						results.push( elem );
 +						return results;
 +					}
 +				}
 +
 +			// Speed-up: Sizzle("TAG")
 +			} else if ( match[2] ) {
 +				push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
 +				return results;
 +
 +			// Speed-up: Sizzle(".CLASS")
 +			} else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
 +				push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
 +				return results;
 +			}
 +		}
 +	}
 +
 +	// All others
 +	return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
 +}
 +
 +Sizzle.matches = function( expr, elements ) {
 +	return Sizzle( expr, null, null, elements );
 +};
 +
 +Sizzle.matchesSelector = function( elem, expr ) {
 +	return Sizzle( expr, null, null, [ elem ] ).length > 0;
 +};
 +
 +// Returns a function to use in pseudos for input types
 +function createInputPseudo( type ) {
 +	return function( elem ) {
 +		var name = elem.nodeName.toLowerCase();
 +		return name === "input" && elem.type === type;
 +	};
 +}
 +
 +// Returns a function to use in pseudos for buttons
 +function createButtonPseudo( type ) {
 +	return function( elem ) {
 +		var name = elem.nodeName.toLowerCase();
 +		return (name === "input" || name === "button") && elem.type === type;
 +	};
 +}
 +
 +// Returns a function to use in pseudos for positionals
 +function createPositionalPseudo( fn ) {
 +	return markFunction(function( argument ) {
 +		argument = +argument;
 +		return markFunction(function( seed, matches ) {
 +			var j,
 +				matchIndexes = fn( [], seed.length, argument ),
 +				i = matchIndexes.length;
 +
 +			// Match elements found at the specified indexes
 +			while ( i-- ) {
 +				if ( seed[ (j = matchIndexes[i]) ] ) {
 +					seed[j] = !(matches[j] = seed[j]);
 +				}
 +			}
 +		});
 +	});
 +}
 +
 +/**
 + * Utility function for retrieving the text value of an array of DOM nodes
 + * @param {Array|Element} elem
 + */
 +getText = Sizzle.getText = function( elem ) {
 +	var node,
 +		ret = "",
 +		i = 0,
 +		nodeType = elem.nodeType;
 +
 +	if ( nodeType ) {
 +		if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
 +			// Use textContent for elements
 +			// innerText usage removed for consistency of new lines (see #11153)
 +			if ( typeof elem.textContent === "string" ) {
 +				return elem.textContent;
 +			} else {
 +				// Traverse its children
 +				for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
 +					ret += getText( elem );
 +				}
 +			}
 +		} else if ( nodeType === 3 || nodeType === 4 ) {
 +			return elem.nodeValue;
 +		}
 +		// Do not include comment or processing instruction nodes
 +	} else {
 +
 +		// If no nodeType, this is expected to be an array
 +		for ( ; (node = elem[i]); i++ ) {
 +			// Do not traverse comment nodes
 +			ret += getText( node );
 +		}
 +	}
 +	return ret;
 +};
 +
 +isXML = Sizzle.isXML = function( elem ) {
 +	// documentElement is verified for cases where it doesn't yet exist
 +	// (such as loading iframes in IE - #4833)
 +	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
 +	return documentElement ? documentElement.nodeName !== "HTML" : false;
 +};
 +
 +// Element contains another
 +contains = Sizzle.contains = docElem.contains ?
 +	function( a, b ) {
 +		var adown = a.nodeType === 9 ? a.documentElement : a,
 +			bup = b && b.parentNode;
 +		return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
 +	} :
 +	docElem.compareDocumentPosition ?
 +	function( a, b ) {
 +		return b && !!( a.compareDocumentPosition( b ) & 16 );
 +	} :
 +	function( a, b ) {
 +		while ( (b = b.parentNode) ) {
 +			if ( b === a ) {
 +				return true;
 +			}
 +		}
 +		return false;
 +	};
 +
 +Sizzle.attr = function( elem, name ) {
 +	var val,
 +		xml = isXML( elem );
 +
 +	if ( !xml ) {
 +		name = name.toLowerCase();
 +	}
 +	if ( (val = Expr.attrHandle[ name ]) ) {
 +		return val( elem );
 +	}
 +	if ( xml || assertAttributes ) {
 +		return elem.getAttribute( name );
 +	}
 +	val = elem.getAttributeNode( name );
 +	return val ?
 +		typeof elem[ name ] === "boolean" ?
 +			elem[ name ] ? name : null :
 +			val.specified ? val.value : null :
 +		null;
 +};
 +
 +Expr = Sizzle.selectors = {
 +
 +	// Can be adjusted by the user
 +	cacheLength: 50,
 +
 +	createPseudo: markFunction,
 +
 +	match: matchExpr,
 +
 +	// IE6/7 return a modified href
 +	attrHandle: assertHrefNotNormalized ?
 +		{} :
 +		{
 +			"href": function( elem ) {
 +				return elem.getAttribute( "href", 2 );
 +			},
 +			"type": function( elem ) {
 +				return elem.getAttribute("type");
 +			}
 +		},
 +
 +	find: {
 +		"ID": assertGetIdNotName ?
 +			function( id, context, xml ) {
 +				if ( typeof context.getElementById !== strundefined && !xml ) {
 +					var m = context.getElementById( id );
 +					// Check parentNode to catch when Blackberry 4.6 returns
 +					// nodes that are no longer in the document #6963
 +					return m && m.parentNode ? [m] : [];
 +				}
 +			} :
 +			function( id, context, xml ) {
 +				if ( typeof context.getElementById !== strundefined && !xml ) {
 +					var m = context.getElementById( id );
 +
 +					return m ?
 +						m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
 +							[m] :
 +							undefined :
 +						[];
 +				}
 +			},
 +
 +		"TAG": assertTagNameNoComments ?
 +			function( tag, context ) {
 +				if ( typeof context.getElementsByTagName !== strundefined ) {
 +					return context.getElementsByTagName( tag );
 +				}
 +			} :
 +			function( tag, context ) {
 +				var results = context.getElementsByTagName( tag );
 +
 +				// Filter out possible comments
 +				if ( tag === "*" ) {
 +					var elem,
 +						tmp = [],
 +						i = 0;
 +
 +					for ( ; (elem = results[i]); i++ ) {
 +						if ( elem.nodeType === 1 ) {
 +							tmp.push( elem );
 +						}
 +					}
 +
 +					return tmp;
 +				}
 +				return results;
 +			},
 +
 +		"NAME": assertUsableName && function( tag, context ) {
 +			if ( typeof context.getElementsByName !== strundefined ) {
 +				return context.getElementsByName( name );
 +			}
 +		},
 +
 +		"CLASS": assertUsableClassName && function( className, context, xml ) {
 +			if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
 +				return context.getElementsByClassName( className );
 +			}
 +		}
 +	},
 +
 +	relative: {
 +		">": { dir: "parentNode", first: true },
 +		" ": { dir: "parentNode" },
 +		"+": { dir: "previousSibling", first: true },
 +		"~": { dir: "previousSibling" }
 +	},
 +
 +	preFilter: {
 +		"ATTR": function( match ) {
 +			match[1] = match[1].replace( rbackslash, "" );
 +
 +			// Move the given value to match[3] whether quoted or unquoted
 +			match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
 +
 +			if ( match[2] === "~=" ) {
 +				match[3] = " " + match[3] + " ";
 +			}
 +
 +			return match.slice( 0, 4 );
 +		},
 +
 +		"CHILD": function( match ) {
 +			/* matches from matchExpr["CHILD"]
 +				1 type (only|nth|...)
 +				2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
 +				3 xn-component of xn+y argument ([+-]?\d*n|)
 +				4 sign of xn-component
 +				5 x of xn-component
 +				6 sign of y-component
 +				7 y of y-component
 +			*/
 +			match[1] = match[1].toLowerCase();
 +
 +			if ( match[1] === "nth" ) {
 +				// nth-child requires argument
 +				if ( !match[2] ) {
 +					Sizzle.error( match[0] );
 +				}
 +
 +				// numeric x and y parameters for Expr.filter.CHILD
 +				// remember that false/true cast respectively to 0/1
 +				match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
 +				match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
 +
 +			// other types prohibit arguments
 +			} else if ( match[2] ) {
 +				Sizzle.error( match[0] );
 +			}
 +
 +			return match;
 +		},
 +
 +		"PSEUDO": function( match ) {
 +			var unquoted, excess;
 +			if ( matchExpr["CHILD"].test( match[0] ) ) {
 +				return null;
 +			}
 +
 +			if ( match[3] ) {
 +				match[2] = match[3];
 +			} else if ( (unquoted = match[4]) ) {
 +				// Only check arguments that contain a pseudo
 +				if ( rpseudo.test(unquoted) &&
 +					// Get excess from tokenize (recursively)
 +					(excess = tokenize( unquoted, true )) &&
 +					// advance to the next closing parenthesis
 +					(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
 +
 +					// excess is a negative index
 +					unquoted = unquoted.slice( 0, excess );
 +					match[0] = match[0].slice( 0, excess );
 +				}
 +				match[2] = unquoted;
 +			}
 +
 +			// Return only captures needed by the pseudo filter method (type and argument)
 +			return match.slice( 0, 3 );
 +		}
 +	},
 +
 +	filter: {
 +		"ID": assertGetIdNotName ?
 +			function( id ) {
 +				id = id.replace( rbackslash, "" );
 +				return function( elem ) {
 +					return elem.getAttribute("id") === id;
 +				};
 +			} :
 +			function( id ) {
 +				id = id.replace( rbackslash, "" );
 +				return function( elem ) {
 +					var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
 +					return node && node.value === id;
 +				};
 +			},
 +
 +		"TAG": function( nodeName ) {
 +			if ( nodeName === "*" ) {
 +				return function() { return true; };
 +			}
 +			nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
 +
 +			return function( elem ) {
 +				return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
 +			};
 +		},
 +
 +		"CLASS": function( className ) {
 +			var pattern = classCache[ expando ][ className + " " ];
 +
 +			return pattern ||
 +				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
 +				classCache( className, function( elem ) {
 +					return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
 +				});
 +		},
 +
 +		"ATTR": function( name, operator, check ) {
 +			return function( elem, context ) {
 +				var result = Sizzle.attr( elem, name );
 +
 +				if ( result == null ) {
 +					return operator === "!=";
 +				}
 +				if ( !operator ) {
 +					return true;
 +				}
 +
 +				result += "";
 +
 +				return operator === "=" ? result === check :
 +					operator === "!=" ? result !== check :
 +					operator === "^=" ? check && result.indexOf( check ) === 0 :
 +					operator === "*=" ? check && result.indexOf( check ) > -1 :
 +					operator === "$=" ? check && result.substr( result.length - check.length ) === check :
 +					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
 +					operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
 +					false;
 +			};
 +		},
 +
 +		"CHILD": function( type, argument, first, last ) {
 +
 +			if ( type === "nth" ) {
 +				return function( elem ) {
 +					var node, diff,
 +						parent = elem.parentNode;
 +
 +					if ( first === 1 && last === 0 ) {
 +						return true;
 +					}
 +
 +					if ( parent ) {
 +						diff = 0;
 +						for ( node = parent.firstChild; node; node = node.nextSibling ) {
 +							if ( node.nodeType === 1 ) {
 +								diff++;
 +								if ( elem === node ) {
 +									break;
 +								}
 +							}
 +						}
 +					}
 +
 +					// Incorporate the offset (or cast to NaN), then check against cycle size
 +					diff -= last;
 +					return diff === first || ( diff % first === 0 && diff / first >= 0 );
 +				};
 +			}
 +
 +			return function( elem ) {
 +				var node = elem;
 +
 +				switch ( type ) {
 +					case "only":
 +					case "first":
 +						while ( (node = node.previousSibling) ) {
 +							if ( node.nodeType === 1 ) {
 +								return false;
 +							}
 +						}
 +
 +						if ( type === "first" ) {
 +							return true;
 +						}
 +
 +						node = elem;
 +
 +						/* falls through */
 +					case "last":
 +						while ( (node = node.nextSibling) ) {
 +							if ( node.nodeType === 1 ) {
 +								return false;
 +							}
 +						}
 +
 +						return true;
 +				}
 +			};
 +		},
 +
 +		"PSEUDO": function( pseudo, argument ) {
 +			// pseudo-class names are case-insensitive
 +			// http://www.w3.org/TR/selectors/#pseudo-classes
 +			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
 +			// Remember that setFilters inherits from pseudos
 +			var args,
 +				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
 +					Sizzle.error( "unsupported pseudo: " + pseudo );
 +
 +			// The user may use createPseudo to indicate that
 +			// arguments are needed to create the filter function
 +			// just as Sizzle does
 +			if ( fn[ expando ] ) {
 +				return fn( argument );
 +			}
 +
 +			// But maintain support for old signatures
 +			if ( fn.length > 1 ) {
 +				args = [ pseudo, pseudo, "", argument ];
 +				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
 +					markFunction(function( seed, matches ) {
 +						var idx,
 +							matched = fn( seed, argument ),
 +							i = matched.length;
 +						while ( i-- ) {
 +							idx = indexOf.call( seed, matched[i] );
 +							seed[ idx ] = !( matches[ idx ] = matched[i] );
 +						}
 +					}) :
 +					function( elem ) {
 +						return fn( elem, 0, args );
 +					};
 +			}
 +
 +			return fn;
 +		}
 +	},
 +
 +	pseudos: {
 +		"not": markFunction(function( selector ) {
 +			// Trim the selector passed to compile
 +			// to avoid treating leading and trailing
 +			// spaces as combinators
 +			var input = [],
 +				results = [],
 +				matcher = compile( selector.replace( rtrim, "$1" ) );
 +
 +			return matcher[ expando ] ?
 +				markFunction(function( seed, matches, context, xml ) {
 +					var elem,
 +						unmatched = matcher( seed, null, xml, [] ),
 +						i = seed.length;
 +
 +					// Match elements unmatched by `matcher`
 +					while ( i-- ) {
 +						if ( (elem = unmatched[i]) ) {
 +							seed[i] = !(matches[i] = elem);
 +						}
 +					}
 +				}) :
 +				function( elem, context, xml ) {
 +					input[0] = elem;
 +					matcher( input, null, xml, results );
 +					return !results.pop();
 +				};
 +		}),
 +
 +		"has": markFunction(function( selector ) {
 +			return function( elem ) {
 +				return Sizzle( selector, elem ).length > 0;
 +			};
 +		}),
 +
 +		"contains": markFunction(function( text ) {
 +			return function( elem ) {
 +				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
 +			};
 +		}),
 +
 +		"enabled": function( elem ) {
 +			return elem.disabled === false;
 +		},
 +
 +		"disabled": function( elem ) {
 +			return elem.disabled === true;
 +		},
 +
 +		"checked": function( elem ) {
 +			// In CSS3, :checked should return both checked and selected elements
 +			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
 +			var nodeName = elem.nodeName.toLowerCase();
 +			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
 +		},
 +
 +		"selected": function( elem ) {
 +			// Accessing this property makes selected-by-default
 +			// options in Safari work properly
 +			if ( elem.parentNode ) {
 +				elem.parentNode.selectedIndex;
 +			}
 +
 +			return elem.selected === true;
 +		},
 +
 +		"parent": function( elem ) {
 +			return !Expr.pseudos["empty"]( elem );
 +		},
 +
 +		"empty": function( elem ) {
 +			// http://www.w3.org/TR/selectors/#empty-pseudo
 +			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
 +			//   not comment, processing instructions, or others
 +			// Thanks to Diego Perini for the nodeName shortcut
 +			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
 +			var nodeType;
 +			elem = elem.firstChild;
 +			while ( elem ) {
 +				if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
 +					return false;
 +				}
 +				elem = elem.nextSibling;
 +			}
 +			return true;
 +		},
 +
 +		"header": function( elem ) {
 +			return rheader.test( elem.nodeName );
 +		},
 +
 +		"text": function( elem ) {
 +			var type, attr;
 +			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
 +			// use getAttribute instead to test this case
 +			return elem.nodeName.toLowerCase() === "input" &&
 +				(type = elem.type) === "text" &&
 +				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
 +		},
 +
 +		// Input types
 +		"radio": createInputPseudo("radio"),
 +		"checkbox": createInputPseudo("checkbox"),
 +		"file": createInputPseudo("file"),
 +		"password": createInputPseudo("password"),
 +		"image": createInputPseudo("image"),
 +
 +		"submit": createButtonPseudo("submit"),
 +		"reset": createButtonPseudo("reset"),
 +
 +		"button": function( elem ) {
 +			var name = elem.nodeName.toLowerCase();
 +			return name === "input" && elem.type === "button" || name === "button";
 +		},
 +
 +		"input": function( elem ) {
 +			return rinputs.test( elem.nodeName );
 +		},
 +
 +		"focus": function( elem ) {
 +			var doc = elem.ownerDocument;
 +			return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
 +		},
 +
 +		"active": function( elem ) {
 +			return elem === elem.ownerDocument.activeElement;
 +		},
 +
 +		// Positional types
 +		"first": createPositionalPseudo(function() {
 +			return [ 0 ];
 +		}),
 +
 +		"last": createPositionalPseudo(function( matchIndexes, length ) {
 +			return [ length - 1 ];
 +		}),
 +
 +		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
 +			return [ argument < 0 ? argument + length : argument ];
 +		}),
 +
 +		"even": createPositionalPseudo(function( matchIndexes, length ) {
 +			for ( var i = 0; i < length; i += 2 ) {
 +				matchIndexes.push( i );
 +			}
 +			return matchIndexes;
 +		}),
 +
 +		"odd": createPositionalPseudo(function( matchIndexes, length ) {
 +			for ( var i = 1; i < length; i += 2 ) {
 +				matchIndexes.push( i );
 +			}
 +			return matchIndexes;
 +		}),
 +
 +		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
 +			for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
 +				matchIndexes.push( i );
 +			}
 +			return matchIndexes;
 +		}),
 +
 +		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
 +			for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
 +				matchIndexes.push( i );
 +			}
 +			return matchIndexes;
 +		})
 +	}
 +};
 +
 +function siblingCheck( a, b, ret ) {
 +	if ( a === b ) {
 +		return ret;
 +	}
 +
 +	var cur = a.nextSibling;
 +
 +	while ( cur ) {
 +		if ( cur === b ) {
 +			return -1;
 +		}
 +
 +		cur = cur.nextSibling;
 +	}
 +
 +	return 1;
 +}
 +
 +sortOrder = docElem.compareDocumentPosition ?
 +	function( a, b ) {
 +		if ( a === b ) {
 +			hasDuplicate = true;
 +			return 0;
 +		}
 +
 +		return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
 +			a.compareDocumentPosition :
 +			a.compareDocumentPosition(b) & 4
 +		) ? -1 : 1;
 +	} :
 +	function( a, b ) {
 +		// The nodes are identical, we can exit early
 +		if ( a === b ) {
 +			hasDuplicate = true;
 +			return 0;
 +
 +		// Fallback to using sourceIndex (in IE) if it's available on both nodes
 +		} else if ( a.sourceIndex && b.sourceIndex ) {
 +			return a.sourceIndex - b.sourceIndex;
 +		}
 +
 +		var al, bl,
 +			ap = [],
 +			bp = [],
 +			aup = a.parentNode,
 +			bup = b.parentNode,
 +			cur = aup;
 +
 +		// If the nodes are siblings (or identical) we can do a quick check
 +		if ( aup === bup ) {
 +			return siblingCheck( a, b );
 +
 +		// If no parents were found then the nodes are disconnected
 +		} else if ( !aup ) {
 +			return -1;
 +
 +		} else if ( !bup ) {
 +			return 1;
 +		}
 +
 +		// Otherwise they're somewhere else in the tree so we need
 +		// to build up a full list of the parentNodes for comparison
 +		while ( cur ) {
 +			ap.unshift( cur );
 +			cur = cur.parentNode;
 +		}
 +
 +		cur = bup;
 +
 +		while ( cur ) {
 +			bp.unshift( cur );
 +			cur = cur.parentNode;
 +		}
 +
 +		al = ap.length;
 +		bl = bp.length;
 +
 +		// Start walking down the tree looking for a discrepancy
 +		for ( var i = 0; i < al && i < bl; i++ ) {
 +			if ( ap[i] !== bp[i] ) {
 +				return siblingCheck( ap[i], bp[i] );
 +			}
 +		}
 +
 +		// We ended someplace up the tree so do a sibling check
 +		return i === al ?
 +			siblingCheck( a, bp[i], -1 ) :
 +			siblingCheck( ap[i], b, 1 );
 +	};
 +
 +// Always assume the presence of duplicates if sort doesn't
 +// pass them to our comparison function (as in Google Chrome).
 +[0, 0].sort( sortOrder );
 +baseHasDuplicate = !hasDuplicate;
 +
 +// Document sorting and removing duplicates
 +Sizzle.uniqueSort = function( results ) {
 +	var elem,
 +		duplicates = [],
 +		i = 1,
 +		j = 0;
 +
 +	hasDuplicate = baseHasDuplicate;
 +	results.sort( sortOrder );
 +
 +	if ( hasDuplicate ) {
 +		for ( ; (elem = results[i]); i++ ) {
 +			if ( elem === results[ i - 1 ] ) {
 +				j = duplicates.push( i );
 +			}
 +		}
 +		while ( j-- ) {
 +			results.splice( duplicates[ j ], 1 );
 +		}
 +	}
 +
 +	return results;
 +};
 +
 +Sizzle.error = function( msg ) {
 +	throw new Error( "Syntax error, unrecognized expression: " + msg );
 +};
 +
 +function tokenize( selector, parseOnly ) {
 +	var matched, match, tokens, type,
 +		soFar, groups, preFilters,
 +		cached = tokenCache[ expando ][ selector + " " ];
 +
 +	if ( cached ) {
 +		return parseOnly ? 0 : cached.slice( 0 );
 +	}
 +
 +	soFar = selector;
 +	groups = [];
 +	preFilters = Expr.preFilter;
 +
 +	while ( soFar ) {
 +
 +		// Comma and first run
 +		if ( !matched || (match = rcomma.exec( soFar )) ) {
 +			if ( match ) {
 +				// Don't consume trailing commas as valid
 +				soFar = soFar.slice( match[0].length ) || soFar;
 +			}
 +			groups.push( tokens = [] );
 +		}
 +
 +		matched = false;
 +
 +		// Combinators
 +		if ( (match = rcombinators.exec( soFar )) ) {
 +			tokens.push( matched = new Token( match.shift() ) );
 +			soFar = soFar.slice( matched.length );
 +
 +			// Cast descendant combinators to space
 +			matched.type = match[0].replace( rtrim, " " );
 +		}
 +
 +		// Filters
 +		for ( type in Expr.filter ) {
 +			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
 +				(match = preFilters[ type ]( match ))) ) {
 +
 +				tokens.push( matched = new Token( match.shift() ) );
 +				soFar = soFar.slice( matched.length );
 +				matched.type = type;
 +				matched.matches = match;
 +			}
 +		}
 +
 +		if ( !matched ) {
 +			break;
 +		}
 +	}
 +
 +	// Return the length of the invalid excess
 +	// if we're just parsing
 +	// Otherwise, throw an error or return tokens
 +	return parseOnly ?
 +		soFar.length :
 +		soFar ?
 +			Sizzle.error( selector ) :
 +			// Cache the tokens
 +			tokenCache( selector, groups ).slice( 0 );
 +}
 +
 +function addCombinator( matcher, combinator, base ) {
 +	var dir = combinator.dir,
 +		checkNonElements = base && combinator.dir === "parentNode",
 +		doneName = done++;
 +
 +	return combinator.first ?
 +		// Check against closest ancestor/preceding element
 +		function( elem, context, xml ) {
 +			while ( (elem = elem[ dir ]) ) {
 +				if ( checkNonElements || elem.nodeType === 1  ) {
 +					return matcher( elem, context, xml );
 +				}
 +			}
 +		} :
 +
 +		// Check against all ancestor/preceding elements
 +		function( elem, context, xml ) {
 +			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
 +			if ( !xml ) {
 +				var cache,
 +					dirkey = dirruns + " " + doneName + " ",
 +					cachedkey = dirkey + cachedruns;
 +				while ( (elem = elem[ dir ]) ) {
 +					if ( checkNonElements || elem.nodeType === 1 ) {
 +						if ( (cache = elem[ expando ]) === cachedkey ) {
 +							return elem.sizset;
 +						} else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
 +							if ( elem.sizset ) {
 +								return elem;
 +							}
 +						} else {
 +							elem[ expando ] = cachedkey;
 +							if ( matcher( elem, context, xml ) ) {
 +								elem.sizset = true;
 +								return elem;
 +							}
 +							elem.sizset = false;
 +						}
 +					}
 +				}
 +			} else {
 +				while ( (elem = elem[ dir ]) ) {
 +					if ( checkNonElements || elem.nodeType === 1 ) {
 +						if ( matcher( elem, context, xml ) ) {
 +							return elem;
 +						}
 +					}
 +				}
 +			}
 +		};
 +}
 +
 +function elementMatcher( matchers ) {
 +	return matchers.length > 1 ?
 +		function( elem, context, xml ) {
 +			var i = matchers.length;
 +			while ( i-- ) {
 +				if ( !matchers[i]( elem, context, xml ) ) {
 +					return false;
 +				}
 +			}
 +			return true;
 +		} :
 +		matchers[0];
 +}
 +
 +function condense( unmatched, map, filter, context, xml ) {
 +	var elem,
 +		newUnmatched = [],
 +		i = 0,
 +		len = unmatched.length,
 +		mapped = map != null;
 +
 +	for ( ; i < len; i++ ) {
 +		if ( (elem = unmatched[i]) ) {
 +			if ( !filter || filter( elem, context, xml ) ) {
 +				newUnmatched.push( elem );
 +				if ( mapped ) {
 +					map.push( i );
 +				}
 +			}
 +		}
 +	}
 +
 +	return newUnmatched;
 +}
 +
 +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
 +	if ( postFilter && !postFilter[ expando ] ) {
 +		postFilter = setMatcher( postFilter );
 +	}
 +	if ( postFinder && !postFinder[ expando ] ) {
 +		postFinder = setMatcher( postFinder, postSelector );
 +	}
 +	return markFunction(function( seed, results, context, xml ) {
 +		var temp, i, elem,
 +			preMap = [],
 +			postMap = [],
 +			preexisting = results.length,
 +
 +			// Get initial elements from seed or context
 +			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
 +
 +			// Prefilter to get matcher input, preserving a map for seed-results synchronization
 +			matcherIn = preFilter && ( seed || !selector ) ?
 +				condense( elems, preMap, preFilter, context, xml ) :
 +				elems,
 +
 +			matcherOut = matcher ?
 +				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
 +				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
 +
 +					// ...intermediate processing is necessary
 +					[] :
 +
 +					// ...otherwise use results directly
 +					results :
 +				matcherIn;
 +
 +		// Find primary matches
 +		if ( matcher ) {
 +			matcher( matcherIn, matcherOut, context, xml );
 +		}
 +
 +		// Apply postFilter
 +		if ( postFilter ) {
 +			temp = condense( matcherOut, postMap );
 +			postFilter( temp, [], context, xml );
 +
 +			// Un-match failing elements by moving them back to matcherIn
 +			i = temp.length;
 +			while ( i-- ) {
 +				if ( (elem = temp[i]) ) {
 +					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
 +				}
 +			}
 +		}
 +
 +		if ( seed ) {
 +			if ( postFinder || preFilter ) {
 +				if ( postFinder ) {
 +					// Get the final matcherOut by condensing this intermediate into postFinder contexts
 +					temp = [];
 +					i = matcherOut.length;
 +					while ( i-- ) {
 +						if ( (elem = matcherOut[i]) ) {
 +							// Restore matcherIn since elem is not yet a final match
 +							temp.push( (matcherIn[i] = elem) );
 +						}
 +					}
 +					postFinder( null, (matcherOut = []), temp, xml );
 +				}
 +
 +				// Move matched elements from seed to results to keep them synchronized
 +				i = matcherOut.length;
 +				while ( i-- ) {
 +					if ( (elem = matcherOut[i]) &&
 +						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
 +
 +						seed[temp] = !(results[temp] = elem);
 +					}
 +				}
 +			}
 +
 +		// Add elements to results, through postFinder if defined
 +		} else {
 +			matcherOut = condense(
 +				matcherOut === results ?
 +					matcherOut.splice( preexisting, matcherOut.length ) :
 +					matcherOut
 +			);
 +			if ( postFinder ) {
 +				postFinder( null, results, matcherOut, xml );
 +			} else {
 +				push.apply( results, matcherOut );
 +			}
 +		}
 +	});
 +}
 +
 +function matcherFromTokens( tokens ) {
 +	var checkContext, matcher, j,
 +		len = tokens.length,
 +		leadingRelative = Expr.relative[ tokens[0].type ],
 +		implicitRelative = leadingRelative || Expr.relative[" "],
 +		i = leadingRelative ? 1 : 0,
 +
 +		// The foundational matcher ensures that elements are reachable from top-level context(s)
 +		matchContext = addCombinator( function( elem ) {
 +			return elem === checkContext;
 +		}, implicitRelative, true ),
 +		matchAnyContext = addCombinator( function( elem ) {
 +			return indexOf.call( checkContext, elem ) > -1;
 +		}, implicitRelative, true ),
 +		matchers = [ function( elem, context, xml ) {
 +			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
 +				(checkContext = context).nodeType ?
 +					matchContext( elem, context, xml ) :
 +					matchAnyContext( elem, context, xml ) );
 +		} ];
 +
 +	for ( ; i < len; i++ ) {
 +		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
 +			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
 +		} else {
 +			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
 +
 +			// Return special upon seeing a positional matcher
 +			if ( matcher[ expando ] ) {
 +				// Find the next relative operator (if any) for proper handling
 +				j = ++i;
 +				for ( ; j < len; j++ ) {
 +					if ( Expr.relative[ tokens[j].type ] ) {
 +						break;
 +					}
 +				}
 +				return setMatcher(
 +					i > 1 && elementMatcher( matchers ),
 +					i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
 +					matcher,
 +					i < j && matcherFromTokens( tokens.slice( i, j ) ),
 +					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
 +					j < len && tokens.join("")
 +				);
 +			}
 +			matchers.push( matcher );
 +		}
 +	}
 +
 +	return elementMatcher( matchers );
 +}
 +
 +function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
 +	var bySet = setMatchers.length > 0,
 +		byElement = elementMatchers.length > 0,
 +		superMatcher = function( seed, context, xml, results, expandContext ) {
 +			var elem, j, matcher,
 +				setMatched = [],
 +				matchedCount = 0,
 +				i = "0",
 +				unmatched = seed && [],
 +				outermost = expandContext != null,
 +				contextBackup = outermostContext,
 +				// We must always have either seed elements or context
 +				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
 +				// Nested matchers should use non-integer dirruns
 +				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);
 +
 +			if ( outermost ) {
 +				outermostContext = context !== document && context;
 +				cachedruns = superMatcher.el;
 +			}
 +
 +			// Add elements passing elementMatchers directly to results
 +			for ( ; (elem = elems[i]) != null; i++ ) {
 +				if ( byElement && elem ) {
 +					for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
 +						if ( matcher( elem, context, xml ) ) {
 +							results.push( elem );
 +							break;
 +						}
 +					}
 +					if ( outermost ) {
 +						dirruns = dirrunsUnique;
 +						cachedruns = ++superMatcher.el;
 +					}
 +				}
 +
 +				// Track unmatched elements for set filters
 +				if ( bySet ) {
 +					// They will have gone through all possible matchers
 +					if ( (elem = !matcher && elem) ) {
 +						matchedCount--;
 +					}
 +
 +					// Lengthen the array for every element, matched or not
 +					if ( seed ) {
 +						unmatched.push( elem );
 +					}
 +				}
 +			}
 +
 +			// Apply set filters to unmatched elements
 +			matchedCount += i;
 +			if ( bySet && i !== matchedCount ) {
 +				for ( j = 0; (matcher = setMatchers[j]); j++ ) {
 +					matcher( unmatched, setMatched, context, xml );
 +				}
 +
 +				if ( seed ) {
 +					// Reintegrate element matches to eliminate the need for sorting
 +					if ( matchedCount > 0 ) {
 +						while ( i-- ) {
 +							if ( !(unmatched[i] || setMatched[i]) ) {
 +								setMatched[i] = pop.call( results );
 +							}
 +						}
 +					}
 +
 +					// Discard index placeholder values to get only actual matches
 +					setMatched = condense( setMatched );
 +				}
 +
 +				// Add matches to results
 +				push.apply( results, setMatched );
 +
 +				// Seedless set matches succeeding multiple successful matchers stipulate sorting
 +				if ( outermost && !seed && setMatched.length > 0 &&
 +					( matchedCount + setMatchers.length ) > 1 ) {
 +
 +					Sizzle.uniqueSort( results );
 +				}
 +			}
 +
 +			// Override manipulation of globals by nested matchers
 +			if ( outermost ) {
 +				dirruns = dirrunsUnique;
 +				outermostContext = contextBackup;
 +			}
 +
 +			return unmatched;
 +		};
 +
 +	superMatcher.el = 0;
 +	return bySet ?
 +		markFunction( superMatcher ) :
 +		superMatcher;
 +}
 +
 +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
 +	var i,
 +		setMatchers = [],
 +		elementMatchers = [],
 +		cached = compilerCache[ expando ][ selector + " " ];
 +
 +	if ( !cached ) {
 +		// Generate a function of recursive functions that can be used to check each element
 +		if ( !group ) {
 +			group = tokenize( selector );
 +		}
 +		i = group.length;
 +		while ( i-- ) {
 +			cached = matcherFromTokens( group[i] );
 +			if ( cached[ expando ] ) {
 +				setMatchers.push( cached );
 +			} else {
 +				elementMatchers.push( cached );
 +			}
 +		}
 +
 +		// Cache the compiled function
 +		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
 +	}
 +	return cached;
 +};
 +
 +function multipleContexts( selector, contexts, results ) {
 +	var i = 0,
 +		len = contexts.length;
 +	for ( ; i < len; i++ ) {
 +		Sizzle( selector, contexts[i], results );
 +	}
 +	return results;
 +}
 +
 +function select( selector, context, results, seed, xml ) {
 +	var i, tokens, token, type, find,
 +		match = tokenize( selector ),
 +		j = match.length;
 +
 +	if ( !seed ) {
 +		// Try to minimize operations if there is only one group
 +		if ( match.length === 1 ) {
 +
 +			// Take a shortcut and set the context if the root selector is an ID
 +			tokens = match[0] = match[0].slice( 0 );
 +			if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
 +					context.nodeType === 9 && !xml &&
 +					Expr.relative[ tokens[1].type ] ) {
 +
 +				context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
 +				if ( !context ) {
 +					return results;
 +				}
 +
 +				selector = selector.slice( tokens.shift().length );
 +			}
 +
 +			// Fetch a seed set for right-to-left matching
 +			for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
 +				token = tokens[i];
 +
 +				// Abort if we hit a combinator
 +				if ( Expr.relative[ (type = token.type) ] ) {
 +					break;
 +				}
 +				if ( (find = Expr.find[ type ]) ) {
 +					// Search, expanding context for leading sibling combinators
 +					if ( (seed = find(
 +						token.matches[0].replace( rbackslash, "" ),
 +						rsibling.test( tokens[0].type ) && context.parentNode || context,
 +						xml
 +					)) ) {
 +
 +						// If seed is empty or no tokens remain, we can return early
 +						tokens.splice( i, 1 );
 +						selector = seed.length && tokens.join("");
 +						if ( !selector ) {
 +							push.apply( results, slice.call( seed, 0 ) );
 +							return results;
 +						}
 +
 +						break;
 +					}
 +				}
 +			}
 +		}
 +	}
 +
 +	// Compile and execute a filtering function
 +	// Provide `match` to avoid retokenization if we modified the selector above
 +	compile( selector, match )(
 +		seed,
 +		context,
 +		xml,
 +		results,
 +		rsibling.test( selector )
 +	);
 +	return results;
 +}
 +
 +if ( document.querySelectorAll ) {
 +	(function() {
 +		var disconnectedMatch,
 +			oldSelect = select,
 +			rescape = /'|\\/g,
 +			rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
 +
 +			// qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
 +			// A support test would require too much code (would include document ready)
 +			rbuggyQSA = [ ":focus" ],
 +
 +			// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
 +			// A support test would require too much code (would include document ready)
 +			// just skip matchesSelector for :active
 +			rbuggyMatches = [ ":active" ],
 +			matches = docElem.matchesSelector ||
 +				docElem.mozMatchesSelector ||
 +				docElem.webkitMatchesSelector ||
 +				docElem.oMatchesSelector ||
 +				docElem.msMatchesSelector;
 +
 +		// Build QSA regex
 +		// Regex strategy adopted from Diego Perini
 +		assert(function( div ) {
 +			// Select is set to empty string on purpose
 +			// This is to test IE's treatment of not explictly
 +			// setting a boolean content attribute,
 +			// since its presence should be enough
 +			// http://bugs.jquery.com/ticket/12359
 +			div.innerHTML = "<select><option selected=''></option></select>";
 +
 +			// IE8 - Some boolean attributes are not treated correctly
 +			if ( !div.querySelectorAll("[selected]").length ) {
 +				rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
 +			}
 +
 +			// Webkit/Opera - :checked should return selected option elements
 +			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
 +			// IE8 throws error here (do not put tests after this one)
 +			if ( !div.querySelectorAll(":checked").length ) {
 +				rbuggyQSA.push(":checked");
 +			}
 +		});
 +
 +		assert(function( div ) {
 +
 +			// Opera 10-12/IE9 - ^= $= *= and empty values
 +			// Should not select anything
 +			div.innerHTML = "<p test=''></p>";
 +			if ( div.querySelectorAll("[test^='']").length ) {
 +				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
 +			}
 +
 +			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
 +			// IE8 throws error here (do not put tests after this one)
 +			div.innerHTML = "<input type='hidden'/>";
 +			if ( !div.querySelectorAll(":enabled").length ) {
 +				rbuggyQSA.push(":enabled", ":disabled");
 +			}
 +		});
 +
 +		// rbuggyQSA always contains :focus, so no need for a length check
 +		rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );
 +
 +		select = function( selector, context, results, seed, xml ) {
 +			// Only use querySelectorAll when not filtering,
 +			// when this is not xml,
 +			// and when no QSA bugs apply
 +			if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
 +				var groups, i,
 +					old = true,
 +					nid = expando,
 +					newContext = context,
 +					newSelector = context.nodeType === 9 && selector;
 +
 +				// qSA works strangely on Element-rooted queries
 +				// We can work around this by specifying an extra ID on the root
 +				// and working up from there (Thanks to Andrew Dupont for the technique)
 +				// IE 8 doesn't work on object elements
 +				if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
 +					groups = tokenize( selector );
 +
 +					if ( (old = context.getAttribute("id")) ) {
 +						nid = old.replace( rescape, "\\$&" );
 +					} else {
 +						context.setAttribute( "id", nid );
 +					}
 +					nid = "[id='" + nid + "'] ";
 +
 +					i = groups.length;
 +					while ( i-- ) {
 +						groups[i] = nid + groups[i].join("");
 +					}
 +					newContext = rsibling.test( selector ) && context.parentNode || context;
 +					newSelector = groups.join(",");
 +				}
 +
 +				if ( newSelector ) {
 +					try {
 +						push.apply( results, slice.call( newContext.querySelectorAll(
 +							newSelector
 +						), 0 ) );
 +						return results;
 +					} catch(qsaError) {
 +					} finally {
 +						if ( !old ) {
 +							context.removeAttribute("id");
 +						}
 +					}
 +				}
 +			}
 +
 +			return oldSelect( selector, context, results, seed, xml );
 +		};
 +
 +		if ( matches ) {
 +			assert(function( div ) {
 +				// Check to see if it's possible to do matchesSelector
 +				// on a disconnected node (IE 9)
 +				disconnectedMatch = matches.call( div, "div" );
 +
 +				// This should fail with an exception
 +				// Gecko does not error, returns false instead
 +				try {
 +					matches.call( div, "[test!='']:sizzle" );
 +					rbuggyMatches.push( "!=", pseudos );
 +				} catch ( e ) {}
 +			});
 +
 +			// rbuggyMatches always contains :active and :focus, so no need for a length check
 +			rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
 +
 +			Sizzle.matchesSelector = function( elem, expr ) {
 +				// Make sure that attribute selectors are quoted
 +				expr = expr.replace( rattributeQuotes, "='$1']" );
 +
 +				// rbuggyMatches always contains :active, so no need for an existence check
 +				if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
 +					try {
 +						var ret = matches.call( elem, expr );
 +
 +						// IE 9's matchesSelector returns false on disconnected nodes
 +						if ( ret || disconnectedMatch ||
 +								// As well, disconnected nodes are said to be in a document
 +								// fragment in IE 9
 +								elem.document && elem.document.nodeType !== 11 ) {
 +							return ret;
 +						}
 +					} catch(e) {}
 +				}
 +
 +				return Sizzle( expr, null, null, [ elem ] ).length > 0;
 +			};
 +		}
 +	})();
 +}
 +
 +// Deprecated
 +Expr.pseudos["nth"] = Expr.pseudos["eq"];
 +
 +// Back-compat
 +function setFilters() {}
 +Expr.filters = setFilters.prototype = Expr.pseudos;
 +Expr.setFilters = new setFilters();
 +
  // Override sizzle attribute retrieval  Sizzle.attr = jQuery.attr;  jQuery.find = Sizzle; @@ -5134,9 +5348,9 @@ jQuery.unique = Sizzle.uniqueSort;  jQuery.text = Sizzle.getText;  jQuery.isXMLDoc = Sizzle.isXML;  jQuery.contains = Sizzle.contains; - - -})( window ); +
 +
 +})( window );
  var runtil = /Until$/,  	rparentsprev = /^(?:parents|prev(?:Until|All))/,  	isSimple = /^.[^:#\[\.,]*$/, @@ -5923,15 +6137,11 @@ jQuery.buildFragment = function( args, context, scripts ) {  		first = args[ 0 ];  	// Set context from what may come in as undefined or a jQuery collection or a node +	// Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & +	// also doubles as fix for #8950 where plain objects caused createDocumentFragment exception  	context = context || document; -	context = (context[0] || context).ownerDocument || context[0] || context; - -	// Ensure that an attr object doesn't incorrectly stand in as a document object -	// Chrome and Firefox seem to allow this to occur and will throw exception -	// Fixes #8950 -	if ( typeof context.createDocumentFragment === "undefined" ) { -		context = document; -	} +	context = !context.nodeType && context[0] || context; +	context = context.ownerDocument || context;  	// Only cache "small" (1/2 KB) HTML strings that are associated with the main document  	// Cloning options loses the selected state, so don't cache them @@ -6076,8 +6286,8 @@ jQuery.extend({  	},  	clean: function( elems, context, fragment, scripts ) { -		var j, safe, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, -			i = 0, +		var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, +			safe = context === document && safeFragment,  			ret = [];  		// Ensure that context is a document @@ -6086,7 +6296,7 @@ jQuery.extend({  		}  		// Use the already-created safe fragment if context permits -		for ( safe = context === document && safeFragment; (elem = elems[i]) != null; i++ ) { +		for ( i = 0; (elem = elems[i]) != null; i++ ) {  			if ( typeof elem === "number" ) {  				elem += "";  			} @@ -6102,7 +6312,8 @@ jQuery.extend({  				} else {  					// Ensure a safe container in which to render the html  					safe = safe || createSafeFragment( context ); -					div = div || safe.appendChild( context.createElement("div") ); +					div = context.createElement("div"); +					safe.appendChild( div );  					// Fix "XHTML"-style tags in all browsers  					elem = elem.replace(rxhtmlTag, "<$1></$2>"); @@ -6145,21 +6356,20 @@ jQuery.extend({  					elem = div.childNodes; -					// Remember the top-level container for proper cleanup -					div = safe.lastChild; +					// Take out of fragment container (we need a fresh div each time) +					div.parentNode.removeChild( div );  				}  			}  			if ( elem.nodeType ) {  				ret.push( elem );  			} else { -				ret = jQuery.merge( ret, elem ); +				jQuery.merge( ret, elem );  			}  		}  		// Fix #11356: Clear elements from safeFragment  		if ( div ) { -			safe.removeChild( div );  			elem = div = safe = null;  		} @@ -6294,9 +6504,10 @@ if ( matched.browser ) {  	browser.version = matched.version;  } -// Deprecated, use jQuery.browser.webkit instead -// Maintained for back-compat only -if ( browser.webkit ) { +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { +	browser.webkit = true; +} else if ( browser.webkit ) {  	browser.safari = true;  } @@ -6322,23 +6533,25 @@ jQuery.sub = function() {  	var rootjQuerySub = jQuerySub(document);  	return jQuerySub;  }; -	 +  })();  var curCSS, iframe, iframeDoc,  	ralpha = /alpha\([^)]*\)/i,  	ropacity = /opacity=([^)]*)/,  	rposition = /^(top|right|bottom|left)$/, +	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption" +	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display +	rdisplayswap = /^(none|table(?!-c[ea]).+)/,  	rmargin = /^margin/,  	rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),  	rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),  	rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), -	elemdisplay = {}, +	elemdisplay = { BODY: "block" },  	cssShow = { position: "absolute", visibility: "hidden", display: "block" },  	cssNormalTransform = {  		letterSpacing: 0, -		fontWeight: 400, -		lineHeight: 1 +		fontWeight: 400  	},  	cssExpand = [ "Top", "Right", "Bottom", "Left" ], @@ -6604,18 +6817,20 @@ jQuery.extend({  	}  }); -// NOTE: To any future maintainer, we've used both window.getComputedStyle -// and getComputedStyle here to produce a better gzip size +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it.  if ( window.getComputedStyle ) {  	curCSS = function( elem, name ) {  		var ret, width, minWidth, maxWidth, -			computed = getComputedStyle( elem, null ), +			computed = window.getComputedStyle( elem, null ),  			style = elem.style;  		if ( computed ) { -			ret = computed[ name ]; -			if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { +			// getPropertyValue is only needed for .css('filter') in IE9, see #12537 +			ret = computed.getPropertyValue( name ) || computed[ name ]; + +			if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {  				ret = jQuery.style( elem, name );  			} @@ -6738,7 +6953,10 @@ function getWidthOrHeight( elem, name, extra ) {  		valueIsBorderBox = true,  		isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; -	if ( val <= 0 ) { +	// some non-html elements return undefined for offsetWidth, so check for null/undefined +	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 +	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 +	if ( val <= 0 || val == null ) {  		// Fall back to computed then uncomputed css if necessary  		val = curCSS( elem, name );  		if ( val < 0 || val == null ) { @@ -6817,12 +7035,14 @@ jQuery.each([ "height", "width" ], function( i, name ) {  	jQuery.cssHooks[ name ] = {  		get: function( elem, computed, extra ) {  			if ( computed ) { -				if ( elem.offsetWidth !== 0 || curCSS( elem, "display" ) !== "none" ) { -					return getWidthOrHeight( elem, name, extra ); -				} else { +				// certain elements can have dimension info if we invisibly show them +				// however, it must have a current display style that would benefit from this +				if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) {  					return jQuery.swap( elem, cssShow, function() {  						return getWidthOrHeight( elem, name, extra );  					}); +				} else { +					return getWidthOrHeight( elem, name, extra );  				}  			}  		}, @@ -7056,10 +7276,10 @@ function buildParams( prefix, obj, traditional, add ) {  		add( prefix, obj );  	}  } -var // Document location -	ajaxLocation, -	// Document location segments +var +	// Document location  	ajaxLocParts, +	ajaxLocation,  	rhash = /#.*$/,  	rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL @@ -7228,7 +7448,7 @@ jQuery.fn.load = function( url, params, callback ) {  		params = undefined;  	// Otherwise, build a param string -	} else if ( typeof params === "object" ) { +	} else if ( params && typeof params === "object" ) {  		type = "POST";  	} @@ -7576,7 +7796,7 @@ jQuery.extend({  			// Set data for the fake xhr object  			jqXHR.status = status; -			jqXHR.statusText = "" + ( nativeStatusText || statusText ); +			jqXHR.statusText = ( nativeStatusText || statusText ) + "";  			// Success/Error  			if ( isSuccess ) { @@ -7636,11 +7856,11 @@ jQuery.extend({  		// Extract dataTypes list  		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); -		// Determine if a cross-domain request is in order +		// A cross-domain request is in order when we have a protocol:host:port mismatch  		if ( s.crossDomain == null ) {  			parts = rurl.exec( s.url.toLowerCase() );  			s.crossDomain = !!( parts && -				( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || +				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||  					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=  						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )  			); @@ -8262,7 +8482,7 @@ if ( jQuery.support.ajax ) {  									// on any attempt to access responseText (#11426)  									try {  										responses.text = xhr.responseText; -									} catch( _ ) { +									} catch( e ) {  									}  									// Firefox throws an exception when accessing @@ -8338,12 +8558,13 @@ var fxNow, timerId,  	animationPrefilters = [ defaultPrefilter ],  	tweeners = {  		"*": [function( prop, value ) { -			var end, unit, prevScale, +			var end, unit,  				tween = this.createTween( prop, value ),  				parts = rfxnum.exec( value ),  				target = tween.cur(),  				start = +target || 0, -				scale = 1; +				scale = 1, +				maxIterations = 20;  			if ( parts ) {  				end = +parts[2]; @@ -8359,17 +8580,15 @@ var fxNow, timerId,  					do {  						// If previous iteration zeroed out, double until we get *something*  						// Use a string for doubling factor so we don't accidentally see scale as unchanged below -						prevScale = scale = scale || ".5"; +						scale = scale || ".5";  						// Adjust and apply  						start = start / scale;  						jQuery.style( tween.elem, prop, start + unit ); -						// Update scale, tolerating zeroes from tween.cur() -						scale = tween.cur() / target; - -					// Stop looping if we've hit the mark or scale is unchanged -					} while ( scale !== 1 && scale !== prevScale ); +					// Update scale, tolerating zero or NaN from tween.cur() +					// And breaking the loop if scale is unchanged or perfect, or if we've just had enough +					} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );  				}  				tween.unit = unit; @@ -8416,7 +8635,9 @@ function Animation( elem, properties, options ) {  		tick = function() {  			var currentTime = fxNow || createFxNow(),  				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), -				percent = 1 - ( remaining / animation.duration || 0 ), +				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) +				temp = remaining / animation.duration || 0, +				percent = 1 - temp,  				index = 0,  				length = animation.tweens.length; @@ -8568,7 +8789,7 @@ jQuery.Animation = jQuery.extend( Animation, {  });  function defaultPrefilter( elem, props, opts ) { -	var index, prop, value, length, dataShow, tween, hooks, oldfire, +	var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire,  		anim = this,  		style = elem.style,  		orig = {}, @@ -8642,6 +8863,7 @@ function defaultPrefilter( elem, props, opts ) {  		value = props[ index ];  		if ( rfxtypes.exec( value ) ) {  			delete props[ index ]; +			toggle = toggle || value === "toggle";  			if ( value === ( hidden ? "hide" : "show" ) ) {  				continue;  			} @@ -8652,6 +8874,14 @@ function defaultPrefilter( elem, props, opts ) {  	length = handled.length;  	if ( length ) {  		dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); +		if ( "hidden" in dataShow ) { +			hidden = dataShow.hidden; +		} + +		// store state if its toggle - enables .stop().toggle() to "reverse" +		if ( toggle ) { +			dataShow.hidden = !hidden; +		}  		if ( hidden ) {  			jQuery( elem ).show();  		} else { @@ -8709,7 +8939,13 @@ Tween.prototype = {  		var eased,  			hooks = Tween.propHooks[ this.prop ]; -		this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); +		if ( this.options.duration ) { +			this.pos = eased = jQuery.easing[ this.easing ]( +				percent, this.options.duration * percent, 0, 1, this.options.duration +			); +		} else { +			this.pos = eased = percent; +		}  		this.now = ( this.end - this.start ) * eased + this.start;  		if ( this.options.step ) { @@ -8867,6 +9103,7 @@ function genFx( type, includeWidth ) {  	// if we include width, step value is 1 to do all cssExpand values,  	// if we don't include width, step value is 2 to skip over Left and Right +	includeWidth = includeWidth? 1 : 0;  	for( ; i < 4 ; i += 2 - includeWidth ) {  		which = cssExpand[ i ];  		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; @@ -8941,6 +9178,8 @@ jQuery.fx.tick = function() {  		timers = jQuery.timers,  		i = 0; +	fxNow = jQuery.now(); +  	for ( ; i < timers.length; i++ ) {  		timer = timers[ i ];  		// Checks the timer has not already been removed @@ -8952,6 +9191,7 @@ jQuery.fx.tick = function() {  	if ( !timers.length ) {  		jQuery.fx.stop();  	} +	fxNow = undefined;  };  jQuery.fx.timer = function( timer ) { @@ -8995,7 +9235,8 @@ jQuery.fn.offset = function( options ) {  			});  	} -	var box, docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, top, left, +	var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, +		box = { top: 0, left: 0 },  		elem = this[ 0 ],  		doc = elem && elem.ownerDocument; @@ -9009,21 +9250,25 @@ jQuery.fn.offset = function( options ) {  	docElem = doc.documentElement; -	// Make sure we're not dealing with a disconnected DOM node +	// Make sure it's not a disconnected DOM node  	if ( !jQuery.contains( docElem, elem ) ) { -		return { top: 0, left: 0 }; +		return box;  	} -	box = elem.getBoundingClientRect(); +	// If we don't have gBCR, just use 0,0 rather than error +	// BlackBerry 5, iOS 3 (original iPhone) +	if ( typeof elem.getBoundingClientRect !== "undefined" ) { +		box = elem.getBoundingClientRect(); +	}  	win = getWindow( doc );  	clientTop  = docElem.clientTop  || body.clientTop  || 0;  	clientLeft = docElem.clientLeft || body.clientLeft || 0;  	scrollTop  = win.pageYOffset || docElem.scrollTop;  	scrollLeft = win.pageXOffset || docElem.scrollLeft; -	top  = box.top  + scrollTop  - clientTop; -	left = box.left + scrollLeft - clientLeft; - -	return { top: top, left: left }; +	return { +		top: box.top  + scrollTop  - clientTop, +		left: box.left + scrollLeft - clientLeft +	};  };  jQuery.offset = { @@ -9201,7 +9446,7 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {  					// Set width or height on the element  					jQuery.style( elem, type, value, extra ); -			}, type, chainable ? margin : undefined, chainable ); +			}, type, chainable ? margin : undefined, chainable, null );  		};  	});  }); @@ -9224,4 +9469,4 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {  	define( "jquery", [], function () { return jQuery; } );  } -})( window );
\ No newline at end of file +})( window ); diff --git a/module/web/static/js/libs/jquery.peity-0.6.js b/module/web/static/js/libs/jquery.peity-0.6.js deleted file mode 100644 index 323ba6fac..000000000 --- a/module/web/static/js/libs/jquery.peity-0.6.js +++ /dev/null @@ -1,184 +0,0 @@ -// Peity jQuery plugin version 0.6.0 -// (c) 2011 Ben Pickles -// -// http://benpickles.github.com/peity/ -// -// Released under MIT license. -(function($, document) { -  var peity = $.fn.peity = function(type, options) { -    if (document.createElement("canvas").getContext) { -      this.each(function() { -        $(this).change(function() { -          var opts = $.extend({}, options) -          var self = this - -          $.each(opts, function(name, value) { -            if ($.isFunction(value)) opts[name] = value.call(self) -          }) - -          var value = $(this).html(); -          peity.graphers[type].call(this, $.extend({}, peity.defaults[type], opts)); -          $(this).trigger("chart:changed", value); -        }).trigger("change"); -      }); -    } - -    return this; -  }; - -  peity.graphers = {}; -  peity.defaults = {}; - -  peity.add = function(type, defaults, grapher){ -    peity.graphers[type] = grapher; -    peity.defaults[type] = defaults; -  }; - -  var devicePixelRatio = window.devicePixelRatio || 1 - -  function createCanvas(width, height) { -    var canvas = document.createElement("canvas") -    canvas.setAttribute("width", width * devicePixelRatio) -    canvas.setAttribute("height", height * devicePixelRatio) - -    if (devicePixelRatio != 1) { -      var style = "width:" + width + "px;height:" + height + "px" -      canvas.setAttribute("style", style) -    } - -    return canvas -  } - -  peity.add( -    'pie', -    { -      colours: ['#FFF4DD', '#FF9900'], -      delimeter: '/', -      diameter: 16 -    }, -    function(opts) { -      var $this = $(this) -      var values = $this.text().split(opts.delimeter) -      var v1 = parseFloat(values[0]); -      var v2 = parseFloat(values[1]); -      var adjust = -Math.PI / 2; -      var slice = (v1 / v2) * Math.PI * 2; - -      var canvas = createCanvas(opts.diameter, opts.diameter) -      var context = canvas.getContext("2d"); -      var centre = canvas.width / 2; - -      // Plate. -      context.beginPath(); -      context.moveTo(centre, centre); -      context.arc(centre, centre, centre, slice + adjust, (slice == 0) ? Math.PI * 2 : adjust, false); -      context.fillStyle = opts.colours[0]; -      context.fill(); - -      // Slice of pie. -      context.beginPath(); -      context.moveTo(centre, centre); -      context.arc(centre, centre, centre, adjust, slice + adjust, false); -      context.fillStyle = opts.colours[1]; -      context.fill(); - -      $this.wrapInner($("<span>").hide()).append(canvas) -  }); - -  peity.add( -    "line", -    { -      colour: "#c6d9fd", -      strokeColour: "#4d89f9", -      strokeWidth: 1, -      delimeter: ",", -      height: 16, -      max: null, -      min: 0, -      width: 32 -    }, -    function(opts) { -      var $this = $(this) -      var canvas = createCanvas(opts.width, opts.height) -      var values = $this.text().split(opts.delimeter) -      if (values.length == 1) values.push(values[0]) -      var max = Math.max.apply(Math, values.concat([opts.max])); -      var min = Math.min.apply(Math, values.concat([opts.min])) - -      var context = canvas.getContext("2d"); -      var width = canvas.width -      var height = canvas.height -      var xQuotient = width / (values.length - 1) -      var yQuotient = height / (max - min) - -      var coords = []; -      var i; - -      context.beginPath(); -      context.moveTo(0, height + (min * yQuotient)) - -      for (i = 0; i < values.length; i++) { -        var x = i * xQuotient -        var y = height - (yQuotient * (values[i] - min)) - -        coords.push({ x: x, y: y }); -        context.lineTo(x, y); -      } - -      context.lineTo(width, height + (min * yQuotient)) -      context.fillStyle = opts.colour; -      context.fill(); - -      if (opts.strokeWidth) { -        context.beginPath(); -        context.moveTo(0, coords[0].y); -        for (i = 0; i < coords.length; i++) { -          context.lineTo(coords[i].x, coords[i].y); -        } -        context.lineWidth = opts.strokeWidth * devicePixelRatio; -        context.strokeStyle = opts.strokeColour; -        context.stroke(); -      } - -      $this.wrapInner($("<span>").hide()).append(canvas) -    } -  ); - -  peity.add( -    'bar', -    { -      colour: "#4D89F9", -      delimeter: ",", -      height: 16, -      max: null, -      min: 0, -      width: 32 -    }, -    function(opts) { -      var $this = $(this) -      var values = $this.text().split(opts.delimeter) -      var max = Math.max.apply(Math, values.concat([opts.max])); -      var min = Math.min.apply(Math, values.concat([opts.min])) - -      var canvas = createCanvas(opts.width, opts.height) -      var context = canvas.getContext("2d"); - -      var width = canvas.width -      var height = canvas.height -      var yQuotient = height / (max - min) -      var space = devicePixelRatio / 2 -      var xQuotient = (width + space) / values.length - -      context.fillStyle = opts.colour; - -      for (var i = 0; i < values.length; i++) { -        var x = i * xQuotient -        var y = height - (yQuotient * (values[i] - min)) - -        context.fillRect(x, y, xQuotient - space, yQuotient * values[i]) -      } - -      $this.wrapInner($("<span>").hide()).append(canvas) -    } -  ); -})(jQuery, document); diff --git a/module/web/static/js/libs/jquery.peity-1.0.js b/module/web/static/js/libs/jquery.peity-1.0.js new file mode 100644 index 000000000..8c2abc236 --- /dev/null +++ b/module/web/static/js/libs/jquery.peity-1.0.js @@ -0,0 +1,246 @@ +// Peity jQuery plugin version 1.0.0 +// (c) 2012 Ben Pickles +// +// http://benpickles.github.com/peity +// +// Released under MIT license. +(function($, document, Math, devicePixelRatio) { +  var canvasSupported = document.createElement("canvas").getContext + +  var peity = $.fn.peity = function(type, options) { +    if (canvasSupported) { +      this.each(function() { +        var defaults = peity.defaults[type] +        var data = {} +        var $this = $(this) + +        $.each($this.data(), function(name, value) { +          if (name in defaults) data[name] = value +        }) + +        var opts = $.extend({}, defaults, data, options) +        var chart = new Peity($this, type, opts) +        chart.draw() + +        $this.change(function() { +          chart.draw() +        }) +      }); +    } + +    return this; +  }; + +  var Peity = function($elem, type, opts) { +    this.$elem = $elem +    this.type = type +    this.opts = opts +  } + +  var PeityPrototype = Peity.prototype + +  PeityPrototype.colours = function() { +    var colours = this.opts.colours +    var func = colours + +    if (!$.isFunction(func)) { +      func = function(_, i) { +        return colours[i % colours.length] +      } +    } + +    return func +  } + +  PeityPrototype.draw = function() { +    peity.graphers[this.type].call(this, this.opts) +  } + +  PeityPrototype.prepareCanvas = function(width, height) { +    var canvas = this.canvas + +    if (canvas) { +      this.context.clearRect(0, 0, canvas.width, canvas.height) +    } else { +      canvas = $("<canvas>").attr({ +        height: height * devicePixelRatio, +        width: width * devicePixelRatio +      }) + +      if (devicePixelRatio != 1) { +        canvas.css({ +          height: height, +          width: width +        }) +      } + +      this.canvas = canvas = canvas[0] +      this.context = canvas.getContext("2d") +      this.$elem.hide().before(canvas) +    } + +    return canvas +  } + +  PeityPrototype.values = function() { +    return $.map(this.$elem.text().split(this.opts.delimiter), function(value) { +      return parseFloat(value) +    }) +  } + +  peity.defaults = {} +  peity.graphers = {} + +  peity.register = function(type, defaults, grapher) { +    this.defaults[type] = defaults +    this.graphers[type] = grapher +  } + +  peity.register( +    'pie', +    { +      colours: ["#ff9900", "#fff4dd", "#ffc66e"], +      delimiter: null, +      diameter: 16 +    }, +    function(opts) { +      if (!opts.delimiter) { +        var delimiter = this.$elem.text().match(/[^0-9\.]/) +        opts.delimiter = delimiter ? delimiter[0] : "," +      } + +      var values = this.values() + +      if (opts.delimiter == "/") { +        var v1 = values[0] +        var v2 = values[1] +        values = [v1, v2 - v1] +      } + +      var i = 0 +      var length = values.length +      var sum = 0 + +      for (; i < length; i++) { +        sum += values[i] +      } + +      var canvas = this.prepareCanvas(opts.diameter, opts.diameter) +      var context = this.context +      var half = canvas.width / 2 +      var pi = Math.PI +      var colours = this.colours() + +      context.save() +      context.translate(half, half) +      context.rotate(-pi / 2) + +      for (i = 0; i < length; i++) { +        var value = values[i] +        var slice = (value / sum) * pi * 2 + +        context.beginPath() +        context.moveTo(0, 0) +        context.arc(0, 0, half, 0, slice, false) +        context.fillStyle = colours.call(this, value, i, values) +        context.fill() +        context.rotate(slice) +      } + +      context.restore() +    } +  ) + +  peity.register( +    "line", +    { +      colour: "#c6d9fd", +      strokeColour: "#4d89f9", +      strokeWidth: 1, +      delimiter: ",", +      height: 16, +      max: null, +      min: 0, +      width: 32 +    }, +    function(opts) { +      var values = this.values() +      if (values.length == 1) values.push(values[0]) +      var max = Math.max.apply(Math, values.concat([opts.max])); +      var min = Math.min.apply(Math, values.concat([opts.min])) + +      var canvas = this.prepareCanvas(opts.width, opts.height) +      var context = this.context +      var width = canvas.width +      var height = canvas.height +      var xQuotient = width / (values.length - 1) +      var yQuotient = height / (max - min) + +      var coords = []; +      var i; + +      context.beginPath(); +      context.moveTo(0, height + (min * yQuotient)) + +      for (i = 0; i < values.length; i++) { +        var x = i * xQuotient +        var y = height - (yQuotient * (values[i] - min)) + +        coords.push({ x: x, y: y }); +        context.lineTo(x, y); +      } + +      context.lineTo(width, height + (min * yQuotient)) +      context.fillStyle = opts.colour; +      context.fill(); + +      if (opts.strokeWidth) { +        context.beginPath(); +        context.moveTo(0, coords[0].y); +        for (i = 0; i < coords.length; i++) { +          context.lineTo(coords[i].x, coords[i].y); +        } +        context.lineWidth = opts.strokeWidth * devicePixelRatio; +        context.strokeStyle = opts.strokeColour; +        context.stroke(); +      } +    } +  ); + +  peity.register( +    'bar', +    { +      colours: ["#4D89F9"], +      delimiter: ",", +      height: 16, +      max: null, +      min: 0, +      spacing: devicePixelRatio, +      width: 32 +    }, +    function(opts) { +      var values = this.values() +      var max = Math.max.apply(Math, values.concat([opts.max])); +      var min = Math.min.apply(Math, values.concat([opts.min])) + +      var canvas = this.prepareCanvas(opts.width, opts.height) +      var context = this.context + +      var width = canvas.width +      var height = canvas.height +      var yQuotient = height / (max - min) +      var space = opts.spacing +      var xQuotient = (width + space) / values.length +      var colours = this.colours() + +      for (var i = 0; i < values.length; i++) { +        var value = values[i] +        var x = i * xQuotient +        var y = height - (yQuotient * (value - min)) + +        context.fillStyle = colours.call(this, value, i, values) +        context.fillRect(x, y, xQuotient - space, yQuotient * values[i]) +      } +    } +  ); +})(jQuery, document, Math, window.devicePixelRatio || 1); diff --git a/module/web/static/js/libs/jquery.transit-0.1.3.js b/module/web/static/js/libs/jquery.transit-0.9.9.js index 2314f2ca2..f0d9719dd 100644 --- a/module/web/static/js/libs/jquery.transit-0.1.3.js +++ b/module/web/static/js/libs/jquery.transit-0.9.9.js @@ -1,6 +1,6 @@  /*!   * jQuery Transit - CSS3 transitions and transformations - * Copyright(c) 2011 Rico Sta. Cruz <rico@ricostacruz.com> + * (c) 2011-2012 Rico Sta. Cruz <rico@ricostacruz.com>   * MIT Licensed.   *   * http://ricostacruz.com/jquery.transit @@ -8,10 +8,8 @@   */  (function($) { -  "use strict"; -    $.transit = { -    version: "0.1.3", +    version: "0.9.9",      // Map of $.css() keys to values for 'transitionProperty'.      // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated @@ -39,6 +37,9 @@    // Helper function to get the proper vendor property name.    // (`transition` => `WebkitTransition`)    function getVendorPropertyName(prop) { +    // Handle unprefixed versions (FF16+, for example) +    if (prop in div.style) return prop; +      var prefixes = ['Moz', 'Webkit', 'O', 'ms'];      var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1); @@ -61,18 +62,14 @@    var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;    // Check for the browser's transitions support. -  // You can access this in jQuery's `$.support.transition`. -  // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/), -  // we set $.support.transition to a string of the actual property name used.    support.transition      = getVendorPropertyName('transition');    support.transitionDelay = getVendorPropertyName('transitionDelay');    support.transform       = getVendorPropertyName('transform');    support.transformOrigin = getVendorPropertyName('transformOrigin');    support.transform3d     = checkTransform3dSupport(); -  $.extend($.support, support); -    var eventNames = { +    'transition':       'transitionEnd',      'MozTransition':    'transitionend',      'OTransition':      'oTransitionEnd',      'WebkitTransition': 'webkitTransitionEnd', @@ -82,17 +79,50 @@    // Detect the 'transitionend' event needed.    var transitionEnd = support.transitionEnd = eventNames[support.transition] || null; +  // Populate jQuery's `$.support` with the vendor prefixes we know. +  // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/), +  // we set $.support.transition to a string of the actual property name used. +  for (var key in support) { +    if (support.hasOwnProperty(key) && typeof $.support[key] === 'undefined') { +      $.support[key] = support[key]; +    } +  } +    // Avoid memory leak in IE.    div = null;    // ## $.cssEase    // List of easing aliases that you can use with `$.fn.transition`.    $.cssEase = { -    '_default': 'ease', -    'in':       'ease-in', -    'out':      'ease-out', -    'in-out':   'ease-in-out', -    'snap':     'cubic-bezier(0,1,.5,1)' +    '_default':       'ease', +    'in':             'ease-in', +    'out':            'ease-out', +    'in-out':         'ease-in-out', +    'snap':           'cubic-bezier(0,1,.5,1)', +    // Penner equations +    'easeOutCubic':   'cubic-bezier(.215,.61,.355,1)', +    'easeInOutCubic': 'cubic-bezier(.645,.045,.355,1)', +    'easeInCirc':     'cubic-bezier(.6,.04,.98,.335)', +    'easeOutCirc':    'cubic-bezier(.075,.82,.165,1)', +    'easeInOutCirc':  'cubic-bezier(.785,.135,.15,.86)', +    'easeInExpo':     'cubic-bezier(.95,.05,.795,.035)', +    'easeOutExpo':    'cubic-bezier(.19,1,.22,1)', +    'easeInOutExpo':  'cubic-bezier(1,0,0,1)', +    'easeInQuad':     'cubic-bezier(.55,.085,.68,.53)', +    'easeOutQuad':    'cubic-bezier(.25,.46,.45,.94)', +    'easeInOutQuad':  'cubic-bezier(.455,.03,.515,.955)', +    'easeInQuart':    'cubic-bezier(.895,.03,.685,.22)', +    'easeOutQuart':   'cubic-bezier(.165,.84,.44,1)', +    'easeInOutQuart': 'cubic-bezier(.77,0,.175,1)', +    'easeInQuint':    'cubic-bezier(.755,.05,.855,.06)', +    'easeOutQuint':   'cubic-bezier(.23,1,.32,1)', +    'easeInOutQuint': 'cubic-bezier(.86,0,.07,1)', +    'easeInSine':     'cubic-bezier(.47,0,.745,.715)', +    'easeOutSine':    'cubic-bezier(.39,.575,.565,1)', +    'easeInOutSine':  'cubic-bezier(.445,.05,.55,.95)', +    'easeInBack':     'cubic-bezier(.6,-.28,.735,.045)', +    'easeOutBack':    'cubic-bezier(.175, .885,.32,1.275)', +    'easeInOutBack':  'cubic-bezier(.68,-.55,.265,1.55)'    };    // ## 'transform' CSS hook @@ -103,10 +133,10 @@    //     $("#hello").css('transform');    //     //=> { rotate: '90deg' }    // -  $.cssHooks.transform = { +  $.cssHooks['transit:transform'] = {      // The getter returns a `Transform` object.      get: function(elem) { -      return $(elem).data('transform'); +      return $(elem).data('transform') || new Transform();      },      // The setter accepts a `Transform` object or a string. @@ -132,34 +162,45 @@      }    }; -  // ## 'transformOrigin' CSS hook -  // Allows the use for `transformOrigin` to define where scaling and rotation -  // is pivoted. -  // -  //     $("#hello").css({ transformOrigin: '0 0' }); -  // -  $.cssHooks.transformOrigin = { -    get: function(elem) { -      return elem.style[support.transformOrigin]; -    }, -    set: function(elem, value) { -      elem.style[support.transformOrigin] = value; -    } +  // Add a CSS hook for `.css({ transform: '...' })`. +  // In jQuery 1.8+, this will intentionally override the default `transform` +  // CSS hook so it'll play well with Transit. (see issue #62) +  $.cssHooks.transform = { +    set: $.cssHooks['transit:transform'].set    }; -  // ## 'transition' CSS hook -  // Allows you to use the `transition` property in CSS. -  // -  //     $("#hello").css({ transition: 'all 0 ease 0' });  -  // -  $.cssHooks.transition = { -    get: function(elem) { -      return elem.style[support.transition]; -    }, -    set: function(elem, value) { -      elem.style[support.transition] = value; -    } -  }; +  // jQuery 1.8+ supports prefix-free transitions, so these polyfills will not +  // be necessary. +  if ($.fn.jquery < "1.8") { +    // ## 'transformOrigin' CSS hook +    // Allows the use for `transformOrigin` to define where scaling and rotation +    // is pivoted. +    // +    //     $("#hello").css({ transformOrigin: '0 0' }); +    // +    $.cssHooks.transformOrigin = { +      get: function(elem) { +        return elem.style[support.transformOrigin]; +      }, +      set: function(elem, value) { +        elem.style[support.transformOrigin] = value; +      } +    }; + +    // ## 'transition' CSS hook +    // Allows you to use the `transition` property in CSS. +    // +    //     $("#hello").css({ transition: 'all 0 ease 0' }); +    // +    $.cssHooks.transition = { +      get: function(elem) { +        return elem.style[support.transition]; +      }, +      set: function(elem, value) { +        elem.style[support.transition] = value; +      } +    }; +  }    // ## Other CSS hooks    // Allows you to rotate, scale and translate. @@ -310,8 +351,8 @@          if (this._translateX === undefined) { this._translateX = 0; }          if (this._translateY === undefined) { this._translateY = 0; } -        if (x !== null) { this._translateX = unit(x, 'px'); } -        if (y !== null) { this._translateY = unit(y, 'px'); } +        if (x !== null && x !== undefined) { this._translateX = unit(x, 'px'); } +        if (y !== null && y !== undefined) { this._translateY = unit(y, 'px'); }          this.translate = this._translateX + "," + this._translateY;        } @@ -405,7 +446,7 @@      $.each(props, function(key) {        key = $.camelCase(key); // Convert "text-align" => "textAlign" -      key = $.transit.propertyMap[key] || key; +      key = $.transit.propertyMap[key] || $.cssProps[key] || key;        key = uncamel(key); // Convert back to dasherized        if ($.inArray(key, re) === -1) { re.push(key); } @@ -603,17 +644,18 @@      $.cssHooks[prop] = {        get: function(elem) { -        var t = $(elem).css('transform') || new Transform(); +        var t = $(elem).css('transit:transform');          return t.get(prop);        },        set: function(elem, value) { -          var t = $(elem).css('transform'); -          t = (typeof t === 'string' || t === null) ? new Transform() : t; -          t.setFromString(prop, value); -          $(elem).css({ transform: t }); +        var t = $(elem).css('transit:transform'); +        t.setFromString(prop, value); + +        $(elem).css({ 'transit:transform': t });        }      }; +    }    // ### uncamel(str) diff --git a/module/web/static/js/libs/lodash-0.7.0.js b/module/web/static/js/libs/lodash-1.0.rc3.js index 7843feb80..f4a8bb12d 100644 --- a/module/web/static/js/libs/lodash-0.7.0.js +++ b/module/web/static/js/libs/lodash-1.0.rc3.js @@ -1,34 +1,39 @@  /*! - * Lo-Dash v0.7.0 <http://lodash.com> + * Lo-Dash 1.0.0-rc.3 <http://lodash.com>   * (c) 2012 John-David Dalton <http://allyoucanleet.com/> - * Based on Underscore.js 1.4.0 <http://underscorejs.org> + * Based on Underscore.js 1.4.3 <http://underscorejs.org>   * (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.   * Available under MIT license <http://lodash.com/license>   */  ;(function(window, undefined) { -  'use strict';    /** Detect free variable `exports` */ -  var freeExports = typeof exports == 'object' && exports && -    (typeof global == 'object' && global && global == global.global && (window = global), exports); +  var freeExports = typeof exports == 'object' && exports; -  /** Native prototype shortcuts */ -  var ArrayProto = Array.prototype, -      BoolProto = Boolean.prototype, -      ObjectProto = Object.prototype, -      NumberProto = Number.prototype, -      StringProto = String.prototype; +  /** Detect free variable `global` and use it as `window` */ +  var freeGlobal = typeof global == 'object' && global; +  if (freeGlobal.global === freeGlobal) { +    window = freeGlobal; +  } + +  /** Used for array and object method references */ +  var arrayRef = [], +      // avoid a Closure Compiler bug by creatively creating an object +      objectRef = new function(){};    /** Used to generate unique IDs */    var idCounter = 0; +  /** Used internally to indicate various things */ +  var indicatorObject = objectRef; +    /** Used by `cachedContains` as the default size when optimizations are enabled for large arrays */    var largeArraySize = 30;    /** Used to restore the original `_` reference in `noConflict` */    var oldDash = window._; -  /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ +  /** Used to detect template delimiter values that require a with-statement */    var reComplexDelimiter = /[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/;    /** Used to match HTML entities */ @@ -47,12 +52,21 @@    /** Used to detect if a method is native */    var reNative = RegExp('^' + -    (ObjectProto.valueOf + '') +    (objectRef.valueOf + '')        .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&')        .replace(/valueOf|for [^\]]+/g, '.+?') + '$'    ); -  /** Used to ensure capturing order and avoid matches for undefined delimiters */ +  /** +   * Used to match ES6 template delimiters +   * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6 +   */ +  var reEsTemplate = /\$\{((?:(?=\\?)\\?[\s\S])*?)}/g; + +  /** Used to match "interpolate" template delimiters */ +  var reInterpolate = /<%=([\s\S]+?)%>/g; + +  /** Used to ensure capturing order of template delimiters */    var reNoMatch = /($^)/;    /** Used to match HTML characters */ @@ -71,19 +85,20 @@    var templateCounter = 0;    /** Native method shortcuts */ -  var concat = ArrayProto.concat, -      hasOwnProperty = ObjectProto.hasOwnProperty, -      push = ArrayProto.push, -      propertyIsEnumerable = ObjectProto.propertyIsEnumerable, -      slice = ArrayProto.slice, -      toString = ObjectProto.toString; +  var ceil = Math.ceil, +      concat = arrayRef.concat, +      floor = Math.floor, +      getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, +      hasOwnProperty = objectRef.hasOwnProperty, +      push = arrayRef.push, +      propertyIsEnumerable = objectRef.propertyIsEnumerable, +      toString = objectRef.toString;    /* Native method shortcuts for methods with the same name as other `lodash` methods */    var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, -      nativeFloor = Math.floor, -      nativeGetPrototypeOf = reNative.test(nativeGetPrototypeOf = Object.getPrototypeOf) && nativeGetPrototypeOf,        nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,        nativeIsFinite = window.isFinite, +      nativeIsNaN = window.isNaN,        nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys,        nativeMax = Math.max,        nativeMin = Math.min, @@ -100,9 +115,15 @@        regexpClass = '[object RegExp]',        stringClass = '[object String]'; -  /** Timer shortcuts */ -  var clearTimeout = window.clearTimeout, -      setTimeout = window.setTimeout; +  /** Detect various environments */ +  var isIeOpera = !!window.attachEvent, +      isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera); + +  /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ +  var isBindFast = nativeBind && !isV8; + +  /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ +  var isKeysFast = nativeKeys && (isIeOpera || isV8);    /**     * Detect the JScript [[DontEnum]] bug: @@ -112,6 +133,9 @@     */    var hasDontEnumBug; +  /** Detect if own properties are iterated after inherited properties (IE < 9) */ +  var iteratesOwnLast; +    /**     * Detect if `Array#shift` and `Array#splice` augment array-like objects     * incorrectly: @@ -122,33 +146,28 @@     * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`     * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.     */ -  var hasObjectSpliceBug; - -  /** Detect if own properties are iterated after inherited properties (IE < 9) */ -  var iteratesOwnLast; +  var hasObjectSpliceBug = (hasObjectSpliceBug = { '0': 1, 'length': 1 }, +    arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]);    /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ -  var noArgsEnum = true; +  var nonEnumArgs = true;    (function() { -    var object = { '0': 1, 'length': 1 }, -        props = []; - +    var props = [];      function ctor() { this.x = 1; }      ctor.prototype = { 'valueOf': 1, 'y': 1 };      for (var prop in new ctor) { props.push(prop); } -    for (prop in arguments) { noArgsEnum = !prop; } +    for (prop in arguments) { nonEnumArgs = !prop; } -    hasDontEnumBug = (props + '').length < 4; +    hasDontEnumBug = !/valueOf/.test(props);      iteratesOwnLast = props[0] != 'x'; -    hasObjectSpliceBug = (props.splice.call(object, 0, 1), object[0]);    }(1)); -  /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ -  var noArgsClass = !isArguments(arguments); +  /** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */ +  var argsAreObjects = arguments.constructor == Object; -  /** Detect if `Array#slice` cannot be used to convert strings to arrays (Opera < 10.52) */ -  var noArraySliceOnStrings = slice.call('x')[0] != 'x'; +  /** Detect if `arguments` objects [[Class]] is unresolvable (Firefox < 4, IE < 9) */ +  var noArgsClass = !isArguments(arguments);    /**     * Detect lack of support for accessing string characters by index: @@ -164,23 +183,14 @@     * a string without a `toString` property value of `typeof` "function".     */    try { -    var noNodeClass = ({ 'toString': 0 } + '', toString.call(window.document || 0) == objectClass); +    var noNodeClass = ({ 'toString': 0 } + '', toString.call(document) == objectClass);    } catch(e) { } -  /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ -  var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); - -  /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ -  var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); - -  /* Detect if strict mode, "use strict", is inferred to be fast (V8) */ -  var isStrictFast = !isBindFast; -    /**     * Detect if sourceURL syntax is usable without erroring:     * -   * The JS engine in Adobe products, like InDesign, will throw a syntax error -   * when it encounters a single line comment beginning with the `@` symbol. +   * The JS engine embedded in Adobe products will throw a syntax error when +   * it encounters a single line comment beginning with the `@` symbol.     *     * The JS engine in Narwhal will generate the function `function anonymous(){//}`     * and throw a syntax error. @@ -190,21 +200,26 @@     * http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx     */    try { -    var useSourceURL = (Function('//@')(), !window.attachEvent); +    var useSourceURL = (Function('//@')(), !isIeOpera);    } catch(e) { } -  /** Used to identify object classifications that are array-like */ -  var arrayLikeClasses = {}; -  arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] = -  arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false; -  arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true; -    /** Used to identify object classifications that `_.clone` supports */    var cloneableClasses = {}; -  cloneableClasses[argsClass] = cloneableClasses[funcClass] = false; -  cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] = -  cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = -  cloneableClasses[stringClass] = true; +  cloneableClasses[funcClass] = false; +  cloneableClasses[argsClass] = cloneableClasses[arrayClass] = +  cloneableClasses[boolClass] = cloneableClasses[dateClass] = +  cloneableClasses[numberClass] = cloneableClasses[objectClass] = +  cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; + +  /** Used to lookup a built-in constructor by [[Class]] */ +  var ctorByClass = {}; +  ctorByClass[arrayClass] = Array; +  ctorByClass[boolClass] = Boolean; +  ctorByClass[dateClass] = Date; +  ctorByClass[objectClass] = Object; +  ctorByClass[numberClass] = Number; +  ctorByClass[regexpClass] = RegExp; +  ctorByClass[stringClass] = String;    /** Used to determine if values are of the language type Object */    var objectTypes = { @@ -213,8 +228,7 @@      'object': true,      'number': false,      'string': false, -    'undefined': false, -    'unknown': true +    'undefined': false    };    /** Used to escape characters for inclusion in compiled string literals */ @@ -231,16 +245,39 @@    /*--------------------------------------------------------------------------*/    /** -   * The `lodash` function. +   * Creates a `lodash` object, that wraps the given `value`, to enable +   * method chaining. +   * +   * The chainable wrapper functions are: +   * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, +   * `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, +   * `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, +   * `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, +   * `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`, +   * `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, +   * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, +   * `unshift`, `values`, `where`, `without`, `wrap`, and `zip` +   * +   * The non-chainable wrapper functions are: +   * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`, +   * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, +   * `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, +   * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, +   * `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`, +   * `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` +   * +   * The wrapper functions `first` and `last` return wrapped values when `n` is +   * passed, otherwise they return unwrapped values.     *     * @name _     * @constructor +   * @category Chaining     * @param {Mixed} value The value to wrap in a `lodash` instance.     * @returns {Object} Returns a `lodash` instance.     */    function lodash(value) { -    // exit early if already wrapped -    if (value && value.__wrapped__) { +    // exit early if already wrapped, even if wrapped by a different `lodash` constructor +    if (value && typeof value == 'object' && value.__wrapped__) {        return value;      }      // allow invoking `lodash` without the `new` operator @@ -286,7 +323,7 @@       * @memberOf _.templateSettings       * @type RegExp       */ -    'interpolate': /<%=([\s\S]+?)%>/g, +    'interpolate': reInterpolate,      /**       * Used to reference the data object in the template text. @@ -309,46 +346,44 @@     */    var iteratorTemplate = template(      // conditional strict mode -    '<% if (useStrict) { %>\'use strict\';\n<% } %>' + +    "<% if (obj.useStrict) { %>'use strict';\n<% } %>" +      // the `iteratee` may be reassigned by the `top` snippet -    'var index, value, iteratee = <%= firstArg %>, ' + +    'var index, iteratee = <%= firstArg %>, ' +      // assign the `result` variable an initial value -    'result<% if (init) { %> = <%= init %><% } %>;\n' + +    'result = <%= firstArg %>;\n' + +    // exit early if the first argument is falsey +    'if (!<%= firstArg %>) return result;\n' +      // add code before the iteration branches      '<%= top %>;\n' + -    // the following branch is for iterating arrays and array-like objects -    '<% if (arrayBranch) { %>' + -    'var length = iteratee.length; index = -1;' + -    '  <% if (objectBranch) { %>\nif (length === +length) {<% } %>' + +    // array-like iteration: +    '<% if (arrayLoop) { %>' + +    'var length = iteratee.length; index = -1;\n' + +    "if (typeof length == 'number') {" +      // add support for accessing string characters by index if needed      '  <% if (noCharByIndex) { %>\n' + -    '  if (toString.call(iteratee) == stringClass) {\n' + -    '    iteratee = iteratee.split(\'\')\n' + +    '  if (isString(iteratee)) {\n' + +    "    iteratee = iteratee.split('')\n" +      '  }' +      '  <% } %>\n' + -    '  <%= arrayBranch.beforeLoop %>;\n' + +    // iterate over the array-like value      '  while (++index < length) {\n' + -    '    value = iteratee[index];\n' + -    '    <%= arrayBranch.inLoop %>\n' + -    '  }' + -    '  <% if (objectBranch) { %>\n}<% } %>' + -    '<% } %>' + - -    // the following branch is for iterating an object's own/inherited properties -    '<% if (objectBranch) { %>' + -    '  <% if (arrayBranch) { %>\nelse {' + +    '    <%= arrayLoop %>\n' + +    '  }\n' + +    '}\n' + +    'else {' + +    // object iteration:      // add support for iterating over `arguments` objects if needed -    '  <%  } else if (noArgsEnum) { %>\n' + +    '  <%  } else if (nonEnumArgs) { %>\n' +      '  var length = iteratee.length; index = -1;\n' +      '  if (length && isArguments(iteratee)) {\n' +      '    while (++index < length) {\n' + -    '      value = iteratee[index += \'\'];\n' + -    '      <%= objectBranch.inLoop %>\n' + +    "      index += '';\n" + +    '      <%= objectLoop %>\n' +      '    }\n' +      '  } else {' +      '  <% } %>' + @@ -360,8 +395,8 @@      // the the `prototype` property of functions regardless of its      // [[Enumerable]] value.      '  <% if (!hasDontEnumBug) { %>\n' + -    '  var skipProto = typeof iteratee == \'function\' && \n' + -    '    propertyIsEnumerable.call(iteratee, \'prototype\');\n' + +    "  var skipProto = typeof iteratee == 'function' && \n" + +    "    propertyIsEnumerable.call(iteratee, 'prototype');\n" +      '  <% } %>' +      // iterate own properties using `Object.keys` if it's fast @@ -369,27 +404,23 @@      '  var ownIndex = -1,\n' +      '      ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' +      '      length = ownProps.length;\n\n' + -    '  <%= objectBranch.beforeLoop %>;\n' +      '  while (++ownIndex < length) {\n' +      '    index = ownProps[ownIndex];\n' + -    '    <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n  <% } %>' + -    '    value = iteratee[index];\n' + -    '    <%= objectBranch.inLoop %>\n' + +    "    <% if (!hasDontEnumBug) { %>if (!(skipProto && index == 'prototype')) {\n  <% } %>" + +    '    <%= objectLoop %>\n' +      '    <% if (!hasDontEnumBug) { %>}\n<% } %>' +      '  }' +      // else using a for-in loop      '  <% } else { %>\n' + -    '  <%= objectBranch.beforeLoop %>;\n' +      '  for (index in iteratee) {<%' +      '    if (!hasDontEnumBug || useHas) { %>\n    if (<%' + -    '      if (!hasDontEnumBug) { %>!(skipProto && index == \'prototype\')<% }' + +    "      if (!hasDontEnumBug) { %>!(skipProto && index == 'prototype')<% }" +      '      if (!hasDontEnumBug && useHas) { %> && <% }' +      '      if (useHas) { %>hasOwnProperty.call(iteratee, index)<% }' +      '    %>) {' +      '    <% } %>\n' + -    '    value = iteratee[index];\n' + -    '    <%= objectBranch.inLoop %>;' + +    '    <%= objectLoop %>;' +      '    <% if (!hasDontEnumBug || useHas) { %>\n    }<% } %>\n' +      '  }' +      '  <% } %>' + @@ -401,18 +432,16 @@      '  <% if (hasDontEnumBug) { %>\n\n' +      '  var ctor = iteratee.constructor;\n' +      '    <% for (var k = 0; k < 7; k++) { %>\n' + -    '  index = \'<%= shadowed[k] %>\';\n' + +    "  index = '<%= shadowed[k] %>';\n" +      '  if (<%' + -    '      if (shadowed[k] == \'constructor\') {' + +    "      if (shadowed[k] == 'constructor') {" +      '        %>!(ctor && ctor.prototype === iteratee) && <%' +      '      } %>hasOwnProperty.call(iteratee, index)) {\n' + -    '    value = iteratee[index];\n' + -    '    <%= objectBranch.inLoop %>\n' + +    '    <%= objectLoop %>\n' +      '  }' +      '    <% } %>' +      '  <% } %>' + -    '  <% if (arrayBranch || noArgsEnum) { %>\n}<% } %>' + -    '<% } %>\n' + +    '  <% if (arrayLoop || nonEnumArgs) { %>\n}<% } %>\n' +      // add code to the bottom of the iteration function      '<%= bottom %>;\n' + @@ -420,103 +449,41 @@      'return result'    ); -  /** -   * Reusable iterator options shared by -   * `countBy`, `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, -   * `map`, `reject`, `some`, and `sortBy`. -   */ -  var baseIteratorOptions = { -    'args': 'collection, callback, thisArg', -    'init': 'collection', -    'top': 'callback = createCallback(callback, thisArg)', -    'inLoop': 'if (callback(value, index, collection) === false) return result' -  }; - -  /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */ -  var countByIteratorOptions = { -    'init': '{}', -    'top': 'callback = createCallback(callback, thisArg)', -    'inLoop': -      'var prop = callback(value, index, collection);\n' + -      '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)' -  }; - -  /** Reusable iterator options for `every` and `some` */ -  var everyIteratorOptions = { -    'init': 'true', -    'inLoop': 'if (!callback(value, index, collection)) return !result' -  }; - -  /** Reusable iterator options for `defaults` and `extend` */ -  var extendIteratorOptions = { -    'useHas': false, -    'useStrict': false, -    'args': 'object', -    'init': 'object', +  /** Reusable iterator options for `assign` and `defaults` */ +  var assignIteratorOptions = { +    'args': 'object, source, guard',      'top': -      'for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n' + -      '  if (iteratee = arguments[argsIndex]) {', -    'inLoop': 'result[index] = value', +      "for (var argsIndex = 1, argsLength = typeof guard == 'number' ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n" + +      '  if ((iteratee = arguments[argsIndex])) {', +    'objectLoop': 'result[index] = iteratee[index]',      'bottom': '  }\n}'    }; -  /** Reusable iterator options for `filter`, `reject`, and `where` */ -  var filterIteratorOptions = { -    'init': '[]', -    'inLoop': 'callback(value, index, collection) && result.push(value)' -  }; - -  /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ -  var forEachIteratorOptions = { -    'top': 'callback = createCallback(callback, thisArg)' +  /** +   * Reusable iterator options shared by `each`, `forIn`, and `forOwn`. +   */ +  var eachIteratorOptions = { +    'args': 'collection, callback, thisArg', +    'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)", +    'arrayLoop': 'if (callback(iteratee[index], index, collection) === false) return result', +    'objectLoop': 'if (callback(iteratee[index], index, collection) === false) return result'    };    /** Reusable iterator options for `forIn` and `forOwn` */    var forOwnIteratorOptions = { -    'inLoop': { -      'object': baseIteratorOptions.inLoop -    } -  }; - -  /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ -  var mapIteratorOptions = { -    'init': '', -    'beforeLoop': { -      'array':  'result = Array(length)', -      'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') -    }, -    'inLoop': { -      'array':  'result[index] = callback(value, index, collection)', -      'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(callback(value, index, collection))' -    } -  }; - -  /** Reusable iterator options for `omit` and `pick` */ -  var omitIteratorOptions = { -    'useHas': false, -    'args': 'object, callback, thisArg', -    'init': '{}', -    'top': -      'var isFunc = typeof callback == \'function\';\n' + -      'if (isFunc) callback = createCallback(callback, thisArg);\n' + -      'else var props = concat.apply(ArrayProto, arguments)', -    'inLoop': -      'if (isFunc\n' + -      '  ? !callback(value, index, object)\n' + -      '  : indexOf(props, index) < 0\n' + -      ') result[index] = value' +    'arrayLoop': null    };    /*--------------------------------------------------------------------------*/    /** -   * Creates a function optimized for searching large arrays for a given `value`, +   * Creates a function optimized to search large arrays for a given `value`,     * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`.     *     * @private     * @param {Array} array The array to search.     * @param {Mixed} value The value to search for. -   * @param {Number} [fromIndex=0] The index to start searching from. +   * @param {Number} [fromIndex=0] The index to search from.     * @param {Number} [largeSize=30] The length at which an array is considered large.     * @returns {Boolean} Returns `true` if `value` is found, else `false`.     */ @@ -524,18 +491,16 @@      fromIndex || (fromIndex = 0);      var length = array.length, -        isLarge = (length - fromIndex) >= (largeSize || largeArraySize), -        cache = isLarge ? {} : array; +        isLarge = (length - fromIndex) >= (largeSize || largeArraySize);      if (isLarge) { -      // init value cache -      var key, +      var cache = {},            index = fromIndex - 1;        while (++index < length) { -        // manually coerce `value` to string because `hasOwnProperty`, in some +        // manually coerce `value` to a string because `hasOwnProperty`, in some          // older versions of Firefox, coerces objects incorrectly -        key = array[index] + ''; +        var key = array[index] + '';          (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]);        }      } @@ -544,11 +509,23 @@          var key = value + '';          return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1;        } -      return indexOf(cache, value, fromIndex) > -1; +      return indexOf(array, value, fromIndex) > -1;      }    }    /** +   * Used by `_.max` and `_.min` as the default `callback` when a given +   * `collection` is a string value. +   * +   * @private +   * @param {String} value The character to inspect. +   * @returns {Number} Returns the code unit of given character. +   */ +  function charAtCallback(value) { +    return value.charCodeAt(0); +  } + +  /**     * Used by `sortBy` to compare transformed `collection` values, stable sorting     * them in ascending order.     * @@ -567,10 +544,10 @@      // ensure a stable sort in V8 and other engines      // http://code.google.com/p/v8/issues/detail?id=90      if (a !== b) { -      if (a > b || a === undefined) { +      if (a > b || typeof a == 'undefined') {          return 1;        } -      if (a < b || b === undefined) { +      if (a < b || typeof b == 'undefined') {          return -1;        }      } @@ -591,12 +568,15 @@    function createBound(func, thisArg, partialArgs) {      var isFunc = isFunction(func),          isPartial = !partialArgs, -        methodName = func; +        key = thisArg;      // juggle arguments      if (isPartial) {        partialArgs = thisArg;      } +    if (!isFunc) { +      thisArg = func; +    }      function bound() {        // `Function#bind` spec @@ -605,24 +585,23 @@            thisBinding = isPartial ? this : thisArg;        if (!isFunc) { -        func = thisArg[methodName]; +        func = thisArg[key];        }        if (partialArgs.length) {          args = args.length -          ? partialArgs.concat(slice.call(args)) +          ? partialArgs.concat(slice(args))            : partialArgs;        }        if (this instanceof bound) { -        // get `func` instance if `bound` is invoked in a `new` expression +        // ensure `new bound` is an instance of `bound` and `func`          noop.prototype = func.prototype;          thisBinding = new noop; +        noop.prototype = null;          // mimic the constructor's `return` behavior          // http://es5.github.com/#x13.2.2          var result = func.apply(thisBinding, args); -        return result && objectTypes[typeof result] -          ? result -          : thisBinding +        return isObject(result) ? result : thisBinding;        }        return func.apply(thisBinding, args);      } @@ -637,9 +616,11 @@     * @param {Function|String} [func=identity|property] The function called per     * iteration or property name to query.     * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @param {Object} [accumulating] Used to indicate that the callback should +   *  accept an `accumulator` argument.     * @returns {Function} Returns a callback function.     */ -  function createCallback(func, thisArg) { +  function createCallback(func, thisArg, accumulating) {      if (!func) {        return identity;      } @@ -648,7 +629,12 @@          return object[func];        };      } -    if (thisArg !== undefined) { +    if (typeof thisArg != 'undefined') { +      if (accumulating) { +        return function(accumulator, value, index, object) { +          return func.call(thisArg, accumulator, value, index, object); +        }; +      }        return function(value, index, object) {          return func.call(thisArg, value, index, object);        }; @@ -657,108 +643,71 @@    }    /** -   * Creates compiled iteration functions. The iteration function will be created -   * to iterate over only objects if the first argument of `options.args` is -   * "object" or `options.inLoop.array` is falsey. +   * Creates compiled iteration functions.     *     * @private -   * @param {Object} [options1, options2, ...] The compile options objects. -   * -   *  useHas - A boolean to specify whether or not to use `hasOwnProperty` checks -   *   in the object loop. -   * -   *  useStrict - A boolean to specify whether or not to include the ES5 -   *   "use strict" directive. -   * -   *  args - A string of comma separated arguments the iteration function will -   *   accept. -   * -   *  init - A string to specify the initial value of the `result` variable. -   * +   * @param {Object} [options1, options2, ...] The compile options object(s). +   *  useHas - A boolean to specify using `hasOwnProperty` checks in the object loop. +   *  args - A string of comma separated arguments the iteration function will accept.     *  top - A string of code to execute before the iteration branches. -   * -   *  beforeLoop - A string or object containing an "array" or "object" property -   *   of code to execute before the array or object loops. -   * -   *  inLoop - A string or object containing an "array" or "object" property -   *   of code to execute in the array or object loops. -   * -   *  bottom - A string of code to execute after the iteration branches but -   *   before the `result` is returned. +   *  arrayLoop - A string of code to execute in the array loop. +   *  objectLoop - A string of code to execute in the object loop. +   *  bottom - A string of code to execute after the iteration branches.     *     * @returns {Function} Returns the compiled function.     */    function createIterator() { -    var object, -        prop, -        value, -        index = -1, -        length = arguments.length; - -    // merge options into a template data object      var data = { +      'arrayLoop': '',        'bottom': '', +      'hasDontEnumBug': hasDontEnumBug, +      'isKeysFast': isKeysFast, +      'objectLoop': '', +      'nonEnumArgs': nonEnumArgs, +      'noCharByIndex': noCharByIndex, +      'shadowed': shadowed,        'top': '', -      'arrayBranch': { 'beforeLoop': '' }, -      'objectBranch': { 'beforeLoop': '' } +      'useHas': true      }; -    while (++index < length) { -      object = arguments[index]; -      for (prop in object) { -        value = (value = object[prop]) == null ? '' : value; -        // keep this regexp explicit for the build pre-process -        if (/beforeLoop|inLoop/.test(prop)) { -          if (typeof value == 'string') { -            value = { 'array': value, 'object': value }; -          } -          data.arrayBranch[prop] = value.array || ''; -          data.objectBranch[prop] = value.object || ''; -        } else { -          data[prop] = value; -        } +    // merge options into a template data object +    for (var object, index = 0; object = arguments[index]; index++) { +      for (var key in object) { +        data[key] = object[key];        }      } -    // set additional template `data` values -    var args = data.args, -        firstArg = /^[^,]+/.exec(args)[0], -        init = data.init, -        useStrict = data.useStrict; - -    data.firstArg = firstArg; -    data.hasDontEnumBug = hasDontEnumBug; -    data.init = init == null ? firstArg : init; -    data.isKeysFast = isKeysFast; -    data.noArgsEnum = noArgsEnum; -    data.shadowed = shadowed; -    data.useHas = data.useHas !== false; -    data.useStrict = useStrict == null ? isStrictFast : useStrict; - -    if (data.noCharByIndex == null) { -      data.noCharByIndex = noCharByIndex; -    } -    if (firstArg != 'collection' || !data.arrayBranch.inLoop) { -      data.arrayBranch = null; -    } +    var args = data.args; +    data.firstArg = /^[^,]+/.exec(args)[0]; +      // create the function factory      var factory = Function( -        'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, ' + -        'forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + -        'isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' + -        'slice, stringClass, toString, undefined', -      'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + -      'return callee' +        'createCallback, hasOwnProperty, isArguments, isString, objectTypes, ' + +        'nativeKeys, propertyIsEnumerable', +      'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'      );      // return the compiled function      return factory( -      arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, -      forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, -      isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, -      slice, stringClass, toString +      createCallback, hasOwnProperty, isArguments, isString, objectTypes, +      nativeKeys, propertyIsEnumerable      );    }    /** +   * A function compiled to iterate `arguments` objects, arrays, objects, and +   * strings consistenly across environments, executing the `callback` for each +   * element in the `collection`. The `callback` is bound to `thisArg` and invoked +   * with three arguments; (value, index|key, collection). Callbacks may exit +   * iteration early by explicitly returning `false`. +   * +   * @private +   * @param {Array|Object|String} collection The collection to iterate over. +   * @param {Function} [callback=identity] The function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Array|Object|String} Returns `collection`. +   */ +  var each = createIterator(eachIteratorOptions); + +  /**     * Used by `template` to escape characters for inclusion in compiled     * string literals.     * @@ -782,6 +731,19 @@    }    /** +   * Checks if `value` is a DOM node in IE < 9. +   * +   * @private +   * @param {Mixed} value The value to check. +   * @returns {Boolean} Returns `true` if the `value` is a DOM node, else `false`. +   */ +  function isNode(value) { +    // IE < 9 presents DOM nodes as `Object` objects except they have `toString` +    // methods that are `typeof` "string" and still can coerce nodes to strings +    return typeof value.toString != 'function' && typeof (value + '') == 'string'; +  } + +  /**     * A no-operation function.     *     * @private @@ -791,6 +753,34 @@    }    /** +   * Slices the `collection` from the `start` index up to, but not including, +   * the `end` index. +   * +   * Note: This function is used, instead of `Array#slice`, to support node lists +   * in IE < 9 and to ensure dense arrays are returned. +   * +   * @private +   * @param {Array|Object|String} collection The collection to slice. +   * @param {Number} start The start index. +   * @param {Number} end The end index. +   * @returns {Array} Returns the new array. +   */ +  function slice(array, start, end) { +    start || (start = 0); +    if (typeof end == 'undefined') { +      end = array ? array.length : 0; +    } +    var index = -1, +        length = end - start || 0, +        result = Array(length < 0 ? 0 : length); + +    while (++index < length) { +      result[index] = array[start + index]; +    } +    return result; +  } + +  /**     * Used by `unescape` to convert HTML entities to characters.     *     * @private @@ -804,23 +794,23 @@    /*--------------------------------------------------------------------------*/    /** -   * Creates an object composed of the inverted keys and values of the given `object`. +   * Assigns own enumerable properties of source object(s) to the `destination` +   * object. Subsequent sources will overwrite propery assignments of previous +   * sources.     *     * @static     * @memberOf _ +   * @alias extend     * @category Objects -   * @param {Object} object The object to invert. -   * @returns {Object} Returns the created inverted object. +   * @param {Object} object The destination object. +   * @param {Object} [source1, source2, ...] The source objects. +   * @returns {Object} Returns the destination object.     * @example     * -   *  _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); -   * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed) +   * _.assign({ 'name': 'moe' }, { 'age': 40 }); +   * // => { 'name': 'moe', 'age': 40 }     */ -  var invert = createIterator({ -    'args': 'object', -    'init': '{}', -    'inLoop': 'result[value] = index' -  }); +  var assign = createIterator(assignIteratorOptions);    /**     * Checks if `value` is an `arguments` object. @@ -844,88 +834,63 @@    // fallback for browsers that can't detect `arguments` objects by [[Class]]    if (noArgsClass) {      isArguments = function(value) { -      return !!(value && hasOwnProperty.call(value, 'callee')); +      return value ? hasOwnProperty.call(value, 'callee') : false;      };    }    /** -   * Checks if `value` is an array. +   * Iterates over `object`'s own and inherited enumerable properties, executing +   * the `callback` for each property. The `callback` is bound to `thisArg` and +   * invoked with three arguments; (value, key, object). Callbacks may exit iteration +   * early by explicitly returning `false`.     *     * @static     * @memberOf _     * @category Objects -   * @param {Mixed} value The value to check. -   * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. +   * @param {Object} object The object to iterate over. +   * @param {Function} [callback=identity] The function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Object} Returns `object`.     * @example     * -   * (function() { return _.isArray(arguments); })(); -   * // => false -   * -   * _.isArray([1, 2, 3]); -   * // => true -   */ -  var isArray = nativeIsArray || function(value) { -    return toString.call(value) == arrayClass; -  }; - -  /** -   * Checks if `value` is a function. +   * function Dog(name) { +   *   this.name = name; +   * }     * -   * @static -   * @memberOf _ -   * @category Objects -   * @param {Mixed} value The value to check. -   * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. -   * @example +   * Dog.prototype.bark = function() { +   *   alert('Woof, woof!'); +   * };     * -   * _.isFunction(_); -   * // => true +   * _.forIn(new Dog('Dagny'), function(value, key) { +   *   alert(key); +   * }); +   * // => alerts 'name' and 'bark' (order is not guaranteed)     */ -  function isFunction(value) { -    return typeof value == 'function'; -  } -  // fallback for older versions of Chrome and Safari -  if (isFunction(/x/)) { -    isFunction = function(value) { -      return toString.call(value) == funcClass; -    }; -  } +  var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { +    'useHas': false +  });    /** -   * Checks if a given `value` is an object created by the `Object` constructor. +   * Iterates over an object's own enumerable properties, executing the `callback` +   * for each property. The `callback` is bound to `thisArg` and invoked with three +   * arguments; (value, key, object). Callbacks may exit iteration early by explicitly +   * returning `false`.     *     * @static     * @memberOf _     * @category Objects -   * @param {Mixed} value The value to check. -   * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. +   * @param {Object} object The object to iterate over. +   * @param {Function} [callback=identity] The function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Object} Returns `object`.     * @example     * -   * function Stooge(name, age) { -   *   this.name = name; -   *   this.age = age; -   * } -   * -   * _.isPlainObject(new Stooge('moe', 40)); -   * // false -   * -   * _.isPlainObject([1, 2, 3]); -   * // false -   * -   * _.isPlainObject({ 'name': 'moe', 'age': 40 }); -   * // => true +   * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { +   *   alert(key); +   * }); +   * // => alerts '0', '1', and 'length' (order is not guaranteed)     */ -  var isPlainObject = !nativeGetPrototypeOf ? isPlainFallback : function(value) { -    if (!(value && typeof value == 'object')) { -      return false; -    } -    var valueOf = value.valueOf, -        objProto = typeof valueOf == 'function' && (objProto = nativeGetPrototypeOf(valueOf)) && nativeGetPrototypeOf(objProto); - -    return objProto -      ? value == objProto || (nativeGetPrototypeOf(value) == objProto && !isArguments(value)) -      : isPlainFallback(value); -  }; +  var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions);    /**     * A fallback implementation of `isPlainObject` that checks if a given `value` @@ -937,18 +902,15 @@     * @param {Mixed} value The value to check.     * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`.     */ -  function isPlainFallback(value) { +  function shimIsPlainObject(value) {      // avoid non-objects and false positives for `arguments` objects      var result = false;      if (!(value && typeof value == 'object') || isArguments(value)) {        return result;      } -    // IE < 9 presents DOM nodes as `Object` objects except they have `toString` -    // methods that are `typeof` "string" and still can coerce nodes to strings. -    // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) +    // check that the constructor is `Object` (i.e. `Object instanceof Object`)      var ctor = value.constructor; -    if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && -        (!isFunction(ctor) || ctor instanceof ctor)) { +    if ((!isFunction(ctor) && (!noNodeClass || !isNode(value))) || ctor instanceof ctor) {        // IE < 9 iterates inherited properties before own properties. If the first        // iterated property is an object's own property then there are no inherited        // enumerable properties. @@ -971,18 +933,20 @@    }    /** -   * A shim implementation of `Object.keys` that produces an array of the given -   * object's own enumerable property names. +   * A fallback implementation of `Object.keys` that produces an array of the +   * given object's own enumerable property names.     *     * @private     * @param {Object} object The object to inspect.     * @returns {Array} Returns a new array of property names.     */ -  var shimKeys = createIterator({ -    'args': 'object', -    'init': '[]', -    'inLoop': 'result.push(index)' -  }); +  function shimKeys(object) { +    var result = []; +    forOwn(object, function(value, key) { +      result.push(key); +    }); +    return result; +  }    /**     * Used to convert characters to HTML entities: @@ -1006,10 +970,8 @@    /*--------------------------------------------------------------------------*/    /** -   * Creates a clone of `value`. If `deep` is `true`, all nested objects will -   * also be cloned otherwise they will be assigned by reference. Functions, DOM -   * nodes, `arguments` objects, and objects created by constructors other than -   * `Object` are **not** cloned. +   * Creates a clone of `value`. If `deep` is `true`, nested objects will also +   * be cloned, otherwise they will be assigned by reference.     *     * @static     * @memberOf _ @@ -1030,15 +992,12 @@     *   { 'name': 'curly', 'age': 60 }     * ];     * -   * _.clone({ 'name': 'moe' }); -   * // => { 'name': 'moe' } -   *     * var shallow = _.clone(stooges);     * shallow[0] === stooges[0];     * // => true     *     * var deep = _.clone(stooges, true); -   * shallow[0] === stooges[0]; +   * deep[0] === stooges[0];     * // => false     */    function clone(value, deep, guard, stackA, stackB) { @@ -1049,29 +1008,23 @@        deep = false;      }      // inspect [[Class]] -    var isObj = objectTypes[typeof value]; +    var isObj = isObject(value);      if (isObj) { -      // don't clone `arguments` objects, functions, or non-object Objects        var className = toString.call(value); -      if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) { +      if (!cloneableClasses[className] || (noNodeClass && isNode(value))) {          return value;        } -      var isArr = className == arrayClass; -      isObj = isArr || (className == objectClass ? isPlainObject(value) : isObj); +      var isArr = isArray(value);      }      // shallow clone      if (!isObj || !deep) { -      // don't clone functions        return isObj -        ? (isArr ? slice.call(value) : extend({}, value)) +        ? (isArr ? slice(value) : assign({}, value))          : value;      } - -    var ctor = value.constructor; +    var ctor = ctorByClass[className];      switch (className) {        case boolClass: -        return new ctor(value == true); -        case dateClass:          return new ctor(+value); @@ -1082,7 +1035,6 @@        case regexpClass:          return ctor(value.source, reFlags.exec(value));      } -      // check for circular references and return corresponding clone      stackA || (stackA = []);      stackB || (stackB = []); @@ -1093,9 +1045,8 @@          return stackB[length];        }      } -      // init cloned object -    var result = isArr ? ctor(length = value.length) : {}; +    var result = isArr ? ctor(value.length) : {};      // add the source value to the stack of traversed objects      // and associate it with its clone @@ -1103,154 +1054,159 @@      stackB.push(result);      // recursively populate clone (susceptible to call stack limits) +    (isArr ? forEach : forOwn)(value, function(objValue, key) { +      result[key] = clone(objValue, deep, null, stackA, stackB); +    }); + +    // add array properties assigned by `RegExp#exec`      if (isArr) { -      var index = -1; -      while (++index < length) { -        result[index] = clone(value[index], deep, null, stackA, stackB); +      if (hasOwnProperty.call(value, 'index')) { +        result.index = value.index; +      } +      if (hasOwnProperty.call(value, 'input')) { +        result.input = value.input;        } -    } else { -      forOwn(value, function(objValue, key) { -        result[key] = clone(objValue, deep, null, stackA, stackB); -      });      }      return result;    }    /** -   * Assigns enumerable properties of the default object(s) to the `destination` -   * object for all `destination` properties that resolve to `null`/`undefined`. -   * Once a property is set, additional defaults of the same property will be -   * ignored. +   * Creates a deep clone of `value`. Functions and DOM nodes are **not** cloned. +   * The enumerable properties of `arguments` objects and objects created by +   * constructors other than `Object` are cloned to plain `Object` objects. +   * +   * Note: This function is loosely based on the structured clone algorithm. +   * See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm.     *     * @static     * @memberOf _     * @category Objects -   * @param {Object} object The destination object. -   * @param {Object} [default1, default2, ...] The default objects. -   * @returns {Object} Returns the destination object. +   * @param {Mixed} value The value to deep clone. +   * @returns {Mixed} Returns the deep cloned `value`.     * @example     * -   * var iceCream = { 'flavor': 'chocolate' }; -   * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); -   * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } +   * var stooges = [ +   *   { 'name': 'moe', 'age': 40 }, +   *   { 'name': 'larry', 'age': 50 }, +   *   { 'name': 'curly', 'age': 60 } +   * ]; +   * +   * var deep = _.cloneDeep(stooges); +   * deep[0] === stooges[0]; +   * // => false     */ -  var defaults = createIterator(extendIteratorOptions, { -    'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop -  }); +  function cloneDeep(value) { +    return clone(value, true); +  }    /** -   * Assigns enumerable properties of the source object(s) to the `destination` -   * object. Subsequent sources will overwrite propery assignments of previous -   * sources. +   * Assigns own enumerable properties of source object(s) to the `destination` +   * object for all `destination` properties that resolve to `null`/`undefined`. +   * Once a property is set, additional defaults of the same property will be +   * ignored.     *     * @static     * @memberOf _     * @category Objects     * @param {Object} object The destination object. -   * @param {Object} [source1, source2, ...] The source objects. +   * @param {Object} [default1, default2, ...] The default objects.     * @returns {Object} Returns the destination object.     * @example     * -   * _.extend({ 'name': 'moe' }, { 'age': 40 }); -   * // => { 'name': 'moe', 'age': 40 } +   * var iceCream = { 'flavor': 'chocolate' }; +   * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); +   * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' }     */ -  var extend = createIterator(extendIteratorOptions); +  var defaults = createIterator(assignIteratorOptions, { +    'objectLoop': 'if (result[index] == null) ' + assignIteratorOptions.objectLoop +  });    /** -   * Iterates over `object`'s own and inherited enumerable properties, executing -   * the `callback` for each property. The `callback` is bound to `thisArg` and -   * invoked with three arguments; (value, key, object). Callbacks may exit iteration -   * early by explicitly returning `false`. +   * Creates a sorted array of all enumerable properties, own and inherited, +   * of `object` that have function values.     *     * @static     * @memberOf _ +   * @alias methods     * @category Objects -   * @param {Object} object The object to iterate over. -   * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding of `callback`. -   * @returns {Object} Returns `object`. +   * @param {Object} object The object to inspect. +   * @returns {Array} Returns a new array of property names that have function values.     * @example     * -   * function Dog(name) { -   *   this.name = name; -   * } -   * -   * Dog.prototype.bark = function() { -   *   alert('Woof, woof!'); -   * }; -   * -   * _.forIn(new Dog('Dagny'), function(value, key) { -   *   alert(key); -   * }); -   * // => alerts 'name' and 'bark' (order is not guaranteed) +   * _.functions(_); +   * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]     */ -  var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { -    'useHas': false -  }); +  function functions(object) { +    var result = []; +    forIn(object, function(value, key) { +      if (isFunction(value)) { +        result.push(key); +      } +    }); +    return result.sort(); +  }    /** -   * Iterates over `object`'s own enumerable properties, executing the `callback` -   * for each property. The `callback` is bound to `thisArg` and invoked with three -   * arguments; (value, key, object). Callbacks may exit iteration early by explicitly -   * returning `false`. +   * Checks if the specified object `property` exists and is a direct property, +   * instead of an inherited property.     *     * @static     * @memberOf _     * @category Objects -   * @param {Object} object The object to iterate over. -   * @param {Function} callback The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding of `callback`. -   * @returns {Object} Returns `object`. +   * @param {Object} object The object to check. +   * @param {String} property The property to check for. +   * @returns {Boolean} Returns `true` if key is a direct property, else `false`.     * @example     * -   * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { -   *   alert(key); -   * }); -   * // => alerts '0', '1', and 'length' (order is not guaranteed) +   * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); +   * // => true     */ -  var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); +  function has(object, property) { +    return object ? hasOwnProperty.call(object, property) : false; +  }    /** -   * Creates a sorted array of all enumerable properties, own and inherited, -   * of `object` that have function values. +   * Creates an object composed of the inverted keys and values of the given `object`.     *     * @static     * @memberOf _ -   * @alias methods     * @category Objects -   * @param {Object} object The object to inspect. -   * @returns {Array} Returns a new array of property names that have function values. +   * @param {Object} object The object to invert. +   * @returns {Object} Returns the created inverted object.     * @example     * -   * _.functions(_); -   * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] +   *  _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); +   * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed)     */ -  var functions = createIterator({ -    'useHas': false, -    'args': 'object', -    'init': '[]', -    'inLoop': 'if (isFunction(value)) result.push(index)', -    'bottom': 'result.sort()' -  }); +  function invert(object) { +    var result = {}; +    forOwn(object, function(value, key) { +      result[value] = key; +    }); +    return result; +  }    /** -   * Checks if the specified object `property` exists and is a direct property, -   * instead of an inherited property. +   * Checks if `value` is an array.     *     * @static     * @memberOf _     * @category Objects -   * @param {Object} object The object to check. -   * @param {String} property The property to check for. -   * @returns {Boolean} Returns `true` if key is a direct property, else `false`. +   * @param {Mixed} value The value to check. +   * @returns {Boolean} Returns `true` if the `value` is an array, else `false`.     * @example     * -   * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); +   * (function() { return _.isArray(arguments); })(); +   * // => false +   * +   * _.isArray([1, 2, 3]);     * // => true     */ -  function has(object, property) { -    return hasOwnProperty.call(object, property); -  } +  var isArray = nativeIsArray || function(value) { +    // `instanceof` may cause a memory leak in IE 7 if `value` is a host object +    // http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak +    return (argsAreObjects && value instanceof Array) || toString.call(value) == arrayClass; +  };    /**     * Checks if `value` is a boolean (`true` or `false`) value. @@ -1283,7 +1239,7 @@     * // => true     */    function isDate(value) { -    return toString.call(value) == dateClass; +    return value instanceof Date || toString.call(value) == dateClass;    }    /** @@ -1324,22 +1280,24 @@     * _.isEmpty('');     * // => true     */ -  var isEmpty = createIterator({ -    'args': 'value', -    'init': 'true', -    'top': -      'if (!value) return result;\n' + -      'var className = toString.call(value),\n' + -      '    length = value.length;\n' + -      'if (arrayLikeClasses[className]' + -      (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + -      '  (className == objectClass && length === +length &&\n' + -      '  isFunction(value.splice))' + -      ') return !length', -    'inLoop': { -      'object': 'return false' +  function isEmpty(value) { +    var result = true; +    if (!value) { +      return result;      } -  }); +    var className = toString.call(value), +        length = value.length; + +    if ((className == arrayClass || className == stringClass || +        className == argsClass || (noArgsClass && isArguments(value))) || +        (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { +      return !length; +    } +    forOwn(value, function() { +      return (result = false); +    }); +    return result; +  }    /**     * Performs a deep comparison between two values to determine if they are @@ -1365,23 +1323,26 @@     * // => true     */    function isEqual(a, b, stackA, stackB) { -    // a strict comparison is necessary because `null == undefined` -    if (a == null || b == null) { -      return a === b; -    }      // exit early for identical values      if (a === b) {        // treat `+0` vs. `-0` as not equal        return a !== 0 || (1 / a == 1 / b);      } -    // unwrap any `lodash` wrapped values -    if (objectTypes[typeof a] || objectTypes[typeof b]) { -      a = a.__wrapped__ || a; -      b = b.__wrapped__ || b; +    // a strict comparison is necessary because `null == undefined` +    if (a == null || b == null) { +      return a === b;      }      // compare [[Class]] names -    var className = toString.call(a); -    if (className != toString.call(b)) { +    var className = toString.call(a), +        otherName = toString.call(b); + +    if (className == argsClass) { +      className = objectClass; +    } +    if (otherName == argsClass) { +      otherName = objectClass; +    } +    if (className != otherName) {        return false;      }      switch (className) { @@ -1404,18 +1365,28 @@          // treat string primitives and their corresponding object instances as equal          return a == b + '';      } -    // exit early, in older browsers, if `a` is array-like but not `b` -    var isArr = arrayLikeClasses[className]; -    if (noArgsClass && !isArr && (isArr = isArguments(a)) && !isArguments(b)) { -      return false; -    } -    // exit for functions and DOM nodes -    if (!isArr && (className != objectClass || (noNodeClass && ( -        (typeof a.toString != 'function' && typeof (a + '') == 'string') || -        (typeof b.toString != 'function' && typeof (b + '') == 'string'))))) { -      return false; +    var isArr = className == arrayClass; +    if (!isArr) { +      // unwrap any `lodash` wrapped values +      if (a.__wrapped__ || b.__wrapped__) { +        return isEqual(a.__wrapped__ || a, b.__wrapped__ || b); +      } +      // exit for functions and DOM nodes +      if (className != objectClass || (noNodeClass && (isNode(a) || isNode(b)))) { +        return false; +      } +      // in older versions of Opera, `arguments` objects have `Array` constructors +      var ctorA = !argsAreObjects && isArguments(a) ? Object : a.constructor, +          ctorB = !argsAreObjects && isArguments(b) ? Object : b.constructor; + +      // non `Object` object instances with different constructors are not equal +      if (ctorA != ctorB && !( +            isFunction(ctorA) && ctorA instanceof ctorA && +            isFunction(ctorB) && ctorB instanceof ctorB +          )) { +        return false; +      }      } -      // assume cyclic structures are equal      // the algorithm for detecting cyclic structures is adapted from ES 5.1      // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) @@ -1428,7 +1399,6 @@          return stackB[length] == b;        }      } -      var index = -1,          result = true,          size = 0; @@ -1453,58 +1423,35 @@        }        return result;      } - -    var ctorA = a.constructor, -        ctorB = b.constructor; - -    // non `Object` object instances with different constructors are not equal -    if (ctorA != ctorB && !( -          isFunction(ctorA) && ctorA instanceof ctorA && -          isFunction(ctorB) && ctorB instanceof ctorB -        )) { -      return false; -    } -    // deep compare objects -    for (var prop in a) { -      if (hasOwnProperty.call(a, prop)) { +    // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` +    // which, in this case, is more costly +    forIn(a, function(value, key, a) { +      if (hasOwnProperty.call(a, key)) {          // count the number of properties.          size++;          // deep compare each property value. -        if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) { -          return false; -        } -      } -    } -    // ensure both objects have the same number of properties -    for (prop in b) { -      // The JS engine in Adobe products, like InDesign, has a bug that causes -      // `!size--` to throw an error so it must be wrapped in parentheses. -      // https://github.com/documentcloud/underscore/issues/355 -      if (hasOwnProperty.call(b, prop) && !(size--)) { -        // `size` will be `-1` if `b` has more properties than `a` -        return false; +        return (result = hasOwnProperty.call(b, key) && isEqual(value, b[key], stackA, stackB));        } -    } -    // handle JScript [[DontEnum]] bug -    if (hasDontEnumBug) { -      while (++index < 7) { -        prop = shadowed[index]; -        if (hasOwnProperty.call(a, prop) && -            !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) { -          return false; +    }); + +    if (result) { +      // ensure both objects have the same number of properties +      forIn(b, function(value, key, b) { +        if (hasOwnProperty.call(b, key)) { +          // `size` will be `-1` if `b` has more properties than `a` +          return (result = --size > -1);          } -      } +      });      } -    return true; +    return result;    }    /** -   * Checks if `value` is a finite number. +   * Checks if `value` is, or can be coerced to, a finite number.     *     * Note: This is not the same as native `isFinite`, which will return true for -   * booleans and other values. See http://es5.github.com/#x15.1.2.5. +   * booleans and empty strings. See http://es5.github.com/#x15.1.2.5.     * -   * @deprecated     * @static     * @memberOf _     * @category Objects @@ -1516,13 +1463,42 @@     * // => true     *     * _.isFinite('10'); +   * // => true +   * +   * _.isFinite(true); +   * // => false +   * +   * _.isFinite('');     * // => false     *     * _.isFinite(Infinity);     * // => false     */    function isFinite(value) { -    return nativeIsFinite(value) && toString.call(value) == numberClass; +    return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); +  } + +  /** +   * Checks if `value` is a function. +   * +   * @static +   * @memberOf _ +   * @category Objects +   * @param {Mixed} value The value to check. +   * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. +   * @example +   * +   * _.isFunction(_); +   * // => true +   */ +  function isFunction(value) { +    return typeof value == 'function'; +  } +  // fallback for older versions of Chrome and Safari +  if (isFunction(/x/)) { +    isFunction = function(value) { +      return value instanceof Function || toString.call(value) == funcClass; +    };    }    /** @@ -1556,10 +1532,9 @@    /**     * Checks if `value` is `NaN`.     * -   * Note: This is not the same as native `isNaN`, which will return true for +   * Note: This is not the same as native `isNaN`, which will return `true` for     * `undefined` and other values. See http://es5.github.com/#x15.1.2.4.     * -   * @deprecated     * @static     * @memberOf _     * @category Objects @@ -1582,13 +1557,12 @@    function isNaN(value) {      // `NaN` as a primitive is the only value that is not equal to itself      // (perform the [[Class]] check first to avoid errors with some host objects in IE) -    return toString.call(value) == numberClass && value != +value +    return isNumber(value) && value != +value    }    /**     * Checks if `value` is `null`.     * -   * @deprecated     * @static     * @memberOf _     * @category Objects @@ -1620,10 +1594,46 @@     * // => true     */    function isNumber(value) { -    return toString.call(value) == numberClass; +    return typeof value == 'number' || toString.call(value) == numberClass;    }    /** +   * Checks if a given `value` is an object created by the `Object` constructor. +   * +   * @static +   * @memberOf _ +   * @category Objects +   * @param {Mixed} value The value to check. +   * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. +   * @example +   * +   * function Stooge(name, age) { +   *   this.name = name; +   *   this.age = age; +   * } +   * +   * _.isPlainObject(new Stooge('moe', 40)); +   * // => false +   * +   * _.isPlainObject([1, 2, 3]); +   * // => false +   * +   * _.isPlainObject({ 'name': 'moe', 'age': 40 }); +   * // => true +   */ +  var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { +    if (!(value && typeof value == 'object')) { +      return false; +    } +    var valueOf = value.valueOf, +        objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); + +    return objProto +      ? value == objProto || (getPrototypeOf(value) == objProto && !isArguments(value)) +      : shimIsPlainObject(value); +  }; + +  /**     * Checks if `value` is a regular expression.     *     * @static @@ -1637,7 +1647,7 @@     * // => true     */    function isRegExp(value) { -    return toString.call(value) == regexpClass; +    return value instanceof RegExp || toString.call(value) == regexpClass;    }    /** @@ -1654,13 +1664,12 @@     * // => true     */    function isString(value) { -    return toString.call(value) == stringClass; +    return typeof value == 'string' || toString.call(value) == stringClass;    }    /**     * Checks if `value` is `undefined`.     * -   * @deprecated     * @static     * @memberOf _     * @category Objects @@ -1672,7 +1681,7 @@     * // => true     */    function isUndefined(value) { -    return value === undefined; +    return typeof value == 'undefined';    }    /** @@ -1692,7 +1701,7 @@      // avoid iterating over the `prototype` property      return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype')        ? shimKeys(object) -      : nativeKeys(object); +      : (isObject(object) ? nativeKeys(object) : []);    };    /** @@ -1708,7 +1717,7 @@     * @param- {Object} [indicator] Internally used to indicate that the `stack`     *  argument is an array of traversed objects instead of another source object.     * @param- {Array} [stackA=[]] Internally used to track traversed source objects. -   * @param- {Array} [stackB=[]] Internally used to associate clones with their +   * @param- {Array} [stackB=[]] Internally used to associate values with their     *  source counterparts.     * @returns {Object} Returns the destination object.     * @example @@ -1726,37 +1735,54 @@     * _.merge(stooges, ages);     * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]     */ -  var merge = createIterator(extendIteratorOptions, { -    'args': 'object, source, indicator', -    'top': -      'var isArr, args = arguments, argsIndex = 0;\n' + -      'if (indicator == compareAscending) {\n' + -      '  var argsLength = 2, stackA = args[3], stackB = args[4]\n' + -      '} else {\n' + -      '  var argsLength = args.length, stackA = [], stackB = []\n' + -      '}\n' + -      'while (++argsIndex < argsLength) {\n' + -      '  if (iteratee = args[argsIndex]) {', -    'inLoop': -      'if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n' + -      '  var found = false, stackLength = stackA.length;\n' + -      '  while (stackLength--) {\n' + -      '    if (found = stackA[stackLength] == source) break\n' + -      '  }\n' + -      '  if (found) {\n' + -      '    result[index] = stackB[stackLength]\n' + -      '  } else {\n' + -      '    stackA.push(source);\n' + -      '    stackB.push(value = (value = result[index]) && isArr\n' + -      '      ? (isArray(value) ? value : [])\n' + -      '      : (isPlainObject(value) ? value : {})\n' + -      '    );\n' + -      '    result[index] = callee(value, source, compareAscending, stackA, stackB)\n' + -      '  }\n' + -      '} else if (source != null) {\n' + -      '  result[index] = source\n' + -      '}' -  }); +  function merge(object, source, indicator) { +    var args = arguments, +        index = 0, +        length = 2, +        stackA = args[3], +        stackB = args[4]; + +    if (indicator !== indicatorObject) { +      stackA = []; +      stackB = []; + +      // work with `_.reduce` by only using its callback `accumulator` and `value` arguments +      if (typeof indicator != 'number') { +        length = args.length; +      } +    } +    while (++index < length) { +      forOwn(args[index], function(source, key) { +        var found, isArr, value; +        if (source && ((isArr = isArray(source)) || isPlainObject(source))) { +          // avoid merging previously merged cyclic sources +          var stackLength = stackA.length; +          while (stackLength--) { +            found = stackA[stackLength] == source; +            if (found) { +              break; +            } +          } +          if (found) { +            object[key] = stackB[stackLength]; +          } +          else { +            // add `source` and associated `value` to the stack of traversed objects +            stackA.push(source); +            stackB.push(value = (value = object[key], isArr) +              ? (isArray(value) ? value : []) +              : (isPlainObject(value) ? value : {}) +            ); +            // recursively merge objects and arrays (susceptible to call stack limits) +            object[key] = merge(value, source, indicatorObject, stackA, stackB); +          } +        } else if (source != null) { +          object[key] = source; +        } +      }); +    } +    return object; +  }    /**     * Creates a shallow clone of `object` excluding the specified properties. @@ -1783,7 +1809,25 @@     * });     * // => { 'name': 'moe' }     */ -  var omit = createIterator(omitIteratorOptions); +  function omit(object, callback, thisArg) { +    var isFunc = typeof callback == 'function', +        result = {}; + +    if (isFunc) { +      callback = createCallback(callback, thisArg); +    } else { +      var props = concat.apply(arrayRef, arguments); +    } +    forIn(object, function(value, key, object) { +      if (isFunc +            ? !callback(value, key, object) +            : indexOf(props, key, 1) < 0 +          ) { +        result[key] = value; +      } +    }); +    return result; +  }    /**     * Creates a two dimensional array of the given object's key-value pairs, @@ -1799,11 +1843,13 @@     * _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 });     * // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed)     */ -  var pairs = createIterator({ -    'args': 'object', -    'init':'[]', -    'inLoop': 'result'  + (isKeysFast ? '[ownIndex] = ' : '.push') + '([index, value])' -  }); +  function pairs(object) { +    var result = []; +    forOwn(object, function(value, key) { +      result.push([key, value]); +    }); +    return result; +  }    /**     * Creates a shallow clone of `object` composed of the specified properties. @@ -1830,22 +1876,29 @@     * });     * // => { 'name': 'moe' }     */ -  var pick = createIterator(omitIteratorOptions, { -    'top': -      'if (typeof callback != \'function\') {\n' + -      '  var prop,\n' + -      '      props = concat.apply(ArrayProto, arguments),\n' + -      '      length = props.length;\n' + -      '  for (index = 1; index < length; index++) {\n' + -      '    prop = props[index];\n' + -      '    if (prop in object) result[prop] = object[prop]\n' + -      '  }\n' + -      '} else {\n' + -      '  callback = createCallback(callback, thisArg)', -    'inLoop': -      'if (callback(value, index, object)) result[index] = value', -    'bottom': '}' -  }); +  function pick(object, callback, thisArg) { +    var result = {}; +    if (typeof callback != 'function') { +      var index = 0, +          props = concat.apply(arrayRef, arguments), +          length = props.length; + +      while (++index < length) { +        var key = props[index]; +        if (key in object) { +          result[key] = object[key]; +        } +      } +    } else { +      callback = createCallback(callback, thisArg); +      forIn(object, function(value, key, object) { +        if (callback(value, key, object)) { +          result[key] = value; +        } +      }); +    } +    return result; +  }    /**     * Creates an array composed of the own enumerable property values of `object`. @@ -1860,17 +1913,20 @@     * _.values({ 'one': 1, 'two': 2, 'three': 3 });     * // => [1, 2, 3]     */ -  var values = createIterator({ -    'args': 'object', -    'init': '[]', -    'inLoop': 'result.push(value)' -  }); +  function values(object) { +    var result = []; +    forOwn(object, function(value) { +      result.push(value); +    }); +    return result; +  }    /*--------------------------------------------------------------------------*/    /**     * Checks if a given `target` element is present in a `collection` using strict -   * equality for comparisons, i.e. `===`. +   * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used +   * as the offset from the end of the collection.     *     * @static     * @memberOf _ @@ -1878,27 +1934,42 @@     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over.     * @param {Mixed} target The value to check for. +   * @param {Number} [fromIndex=0] The index to search from.     * @returns {Boolean} Returns `true` if the `target` element is found, else `false`.     * @example     * -   * _.contains([1, 2, 3], 3); +   * _.contains([1, 2, 3], 1);     * // => true     * +   * _.contains([1, 2, 3], 1, 2); +   * // => false +   *     * _.contains({ 'name': 'moe', 'age': 40 }, 'moe');     * // => true     *     * _.contains('curly', 'ur');     * // => true     */ -  var contains = createIterator({ -    'args': 'collection, target', -    'init': 'false', -    'noCharByIndex': false, -    'beforeLoop': { -      'array': 'if (toString.call(collection) == stringClass) return collection.indexOf(target) > -1' -    }, -    'inLoop': 'if (value === target) return true' -  }); +  function contains(collection, target, fromIndex) { +    var index = -1, +        length = collection ? collection.length : 0, +        result = false; + +    fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; +    if (typeof length == 'number') { +      result = (isString(collection) +        ? collection.indexOf(target, fromIndex) +        : indexOf(collection, target, fromIndex) +      ) > -1; +    } else { +      each(collection, function(value) { +        if (++index >= fromIndex) { +          return !(result = value === target); +        } +      }); +    } +    return result; +  }    /**     * Creates an object composed of keys returned from running each element of @@ -1926,7 +1997,16 @@     * _.countBy(['one', 'two', 'three'], 'length');     * // => { '3': 2, '5': 1 }     */ -  var countBy = createIterator(baseIteratorOptions, countByIteratorOptions); +  function countBy(collection, callback, thisArg) { +    var result = {}; +    callback = createCallback(callback, thisArg); + +    forEach(collection, function(value, key, collection) { +      key = callback(value, key, collection); +      (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); +    }); +    return result; +  }    /**     * Checks if the `callback` returns a truthy value for **all** elements of a @@ -1947,7 +2027,26 @@     * _.every([true, 1, null, 'yes'], Boolean);     * // => false     */ -  var every = createIterator(baseIteratorOptions, everyIteratorOptions); +  function every(collection, callback, thisArg) { +    var result = true; +    callback = createCallback(callback, thisArg); + +    if (isArray(collection)) { +      var index = -1, +          length = collection.length; + +      while (++index < length) { +        if (!(result = !!callback(collection[index], index, collection))) { +          break; +        } +      } +    } else { +      each(collection, function(value, index, collection) { +        return (result = !!callback(value, index, collection)); +      }); +    } +    return result; +  }    /**     * Examines each element in a `collection`, returning an array of all elements @@ -1967,7 +2066,29 @@     * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });     * // => [2, 4, 6]     */ -  var filter = createIterator(baseIteratorOptions, filterIteratorOptions); +  function filter(collection, callback, thisArg) { +    var result = []; +    callback = createCallback(callback, thisArg); + +    if (isArray(collection)) { +      var index = -1, +          length = collection.length; + +      while (++index < length) { +        var value = collection[index]; +        if (callback(value, index, collection)) { +          result.push(value); +        } +      } +    } else { +      each(collection, function(value, index, collection) { +        if (callback(value, index, collection)) { +          result.push(value); +        } +      }); +    } +    return result; +  }    /**     * Examines each element in a `collection`, returning the first one the `callback` @@ -1980,7 +2101,7 @@     * @alias detect     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function} callback The function called per iteration. +   * @param {Function} [callback=identity] The function called per iteration.     * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the element that passed the callback check,     *  else `undefined`. @@ -1989,10 +2110,18 @@     * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });     * // => 2     */ -  var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { -    'init': '', -    'inLoop': 'if (callback(value, index, collection)) return value' -  }); +  function find(collection, callback, thisArg) { +    var result; +    callback = createCallback(callback, thisArg); + +    forEach(collection, function(value, index, collection) { +      if (callback(value, index, collection)) { +        result = value; +        return false; +      } +    }); +    return result; +  }    /**     * Iterates over a `collection`, executing the `callback` for each element in @@ -2005,7 +2134,7 @@     * @alias each     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function} callback The function called per iteration. +   * @param {Function} [callback=identity] The function called per iteration.     * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Array|Object|String} Returns `collection`.     * @example @@ -2014,16 +2143,30 @@     * // => alerts each number and returns '1,2,3'     *     * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); -   * // => alerts each number (order is not guaranteed) +   * // => alerts each number value (order is not guaranteed)     */ -  var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); +  function forEach(collection, callback, thisArg) { +    if (callback && typeof thisArg == 'undefined' && isArray(collection)) { +      var index = -1, +          length = collection.length; + +      while (++index < length) { +        if (callback(collection[index], index, collection) === false) { +          break; +        } +      } +    } else { +      each(collection, callback, thisArg); +    } +    return collection; +  }    /**     * Creates an object composed of keys returned from running each element of     * `collection` through a `callback`. The corresponding value of each key is an     * array of elements passed to `callback` that returned the key. The `callback`     * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). -   * The `callback` argument may also be the name of a property to count by (e.g. 'length'). +   * The `callback` argument may also be the name of a property to group by (e.g. 'length').     *     * @static     * @memberOf _ @@ -2044,11 +2187,16 @@     * _.groupBy(['one', 'two', 'three'], 'length');     * // => { '3': ['one', 'two'], '5': ['three'] }     */ -  var groupBy = createIterator(baseIteratorOptions, countByIteratorOptions, { -    'inLoop': -      'var prop = callback(value, index, collection);\n' + -      '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)' -  }); +  function groupBy(collection, callback, thisArg) { +    var result = {}; +    callback = createCallback(callback, thisArg); + +    forEach(collection, function(value, key, collection) { +      key = callback(value, key, collection); +      (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); +    }); +    return result; +  }    /**     * Invokes the method named by `methodName` on each element in the `collection`, @@ -2072,19 +2220,16 @@     * _.invoke([123, 456], String.prototype.split, '');     * // => [['1', '2', '3'], ['4', '5', '6']]     */ -  var invoke = createIterator(mapIteratorOptions, { -    'args': 'collection, methodName', -    'top': -      'var args = slice.call(arguments, 2),\n' + -      '    isFunc = typeof methodName == \'function\'', -    'inLoop': { -      'array': -        'result[index] = (isFunc ? methodName : value[methodName]).apply(value, args)', -      'object': -        'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + -        '((isFunc ? methodName : value[methodName]).apply(value, args))' -    } -  }); +  function invoke(collection, methodName) { +    var args = slice(arguments, 2), +        isFunc = typeof methodName == 'function', +        result = []; + +    forEach(collection, function(value) { +      result.push((isFunc ? methodName : value[methodName]).apply(value, args)); +    }); +    return result; +  }    /**     * Creates an array of values by running each element in the `collection` @@ -2107,7 +2252,121 @@     * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });     * // => [3, 6, 9] (order is not guaranteed)     */ -  var map = createIterator(baseIteratorOptions, mapIteratorOptions); +  function map(collection, callback, thisArg) { +    var index = -1, +        length = collection ? collection.length : 0, +        result = Array(typeof length == 'number' ? length : 0); + +    callback = createCallback(callback, thisArg); +    if (isArray(collection)) { +      while (++index < length) { +        result[index] = callback(collection[index], index, collection); +      } +    } else { +      each(collection, function(value, key, collection) { +        result[++index] = callback(value, key, collection); +      }); +    } +    return result; +  } + +  /** +   * Retrieves the maximum value of an `array`. If `callback` is passed, +   * it will be executed for each value in the `array` to generate the +   * criterion by which the value is ranked. The `callback` is bound to +   * `thisArg` and invoked with three arguments; (value, index, collection). +   * +   * @static +   * @memberOf _ +   * @category Collections +   * @param {Array|Object|String} collection The collection to iterate over. +   * @param {Function} [callback] The function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Mixed} Returns the maximum value. +   * @example +   * +   * var stooges = [ +   *   { 'name': 'moe', 'age': 40 }, +   *   { 'name': 'larry', 'age': 50 }, +   *   { 'name': 'curly', 'age': 60 } +   * ]; +   * +   * _.max(stooges, function(stooge) { return stooge.age; }); +   * // => { 'name': 'curly', 'age': 60 }; +   */ +  function max(collection, callback, thisArg) { +    var computed = -Infinity, +        index = -1, +        length = collection ? collection.length : 0, +        result = computed; + +    if (callback || !isArray(collection)) { +      callback = !callback && isString(collection) +        ? charAtCallback +        : createCallback(callback, thisArg); + +      each(collection, function(value, index, collection) { +        var current = callback(value, index, collection); +        if (current > computed) { +          computed = current; +          result = value; +        } +      }); +    } else { +      while (++index < length) { +        if (collection[index] > result) { +          result = collection[index]; +        } +      } +    } +    return result; +  } + +  /** +   * Retrieves the minimum value of an `array`. If `callback` is passed, +   * it will be executed for each value in the `array` to generate the +   * criterion by which the value is ranked. The `callback` is bound to `thisArg` +   * and invoked with three arguments; (value, index, collection). +   * +   * @static +   * @memberOf _ +   * @category Collections +   * @param {Array|Object|String} collection The collection to iterate over. +   * @param {Function} [callback] The function called per iteration. +   * @param {Mixed} [thisArg] The `this` binding of `callback`. +   * @returns {Mixed} Returns the minimum value. +   * @example +   * +   * _.min([10, 5, 100, 2, 1000]); +   * // => 2 +   */ +  function min(collection, callback, thisArg) { +    var computed = Infinity, +        index = -1, +        length = collection ? collection.length : 0, +        result = computed; + +    if (callback || !isArray(collection)) { +      callback = !callback && isString(collection) +        ? charAtCallback +        : createCallback(callback, thisArg); + +      each(collection, function(value, index, collection) { +        var current = callback(value, index, collection); +        if (current < computed) { +          computed = current; +          result = value; +        } +      }); +    } else { +      while (++index < length) { +        if (collection[index] < result) { +          result = collection[index]; +        } +      } +    } +    return result; +  }    /**     * Retrieves the value of a specified property from all elements in @@ -2130,13 +2389,9 @@     * _.pluck(stooges, 'name');     * // => ['moe', 'larry', 'curly']     */ -  var pluck = createIterator(mapIteratorOptions, { -    'args': 'collection, property', -    'inLoop': { -      'array':  'result[index] = value[property]', -      'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(value[property])' -    } -  }); +  function pluck(collection, property) { +    return map(collection, property + ''); +  }    /**     * Boils down a `collection` to a single value. The initial state of the @@ -2149,7 +2404,7 @@     * @alias foldl, inject     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function} callback The function called per iteration. +   * @param {Function} [callback=identity] The function called per iteration.     * @param {Mixed} [accumulator] Initial value of the accumulator.     * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the accumulated value. @@ -2158,24 +2413,29 @@     * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; });     * // => 6     */ -  var reduce = createIterator({ -    'args': 'collection, callback, accumulator, thisArg', -    'init': 'accumulator', -    'top': -      'var noaccum = arguments.length < 3;\n' + -      'callback = createCallback(callback, thisArg)', -    'beforeLoop': { -      'array': 'if (noaccum) result = iteratee[++index]' -    }, -    'inLoop': { -      'array': -        'result = callback(result, value, index, collection)', -      'object': -        'result = noaccum\n' + -        '  ? (noaccum = false, value)\n' + -        '  : callback(result, value, index, collection)' +  function reduce(collection, callback, accumulator, thisArg) { +    var noaccum = arguments.length < 3; +    callback = createCallback(callback, thisArg, indicatorObject); + +    if (isArray(collection)) { +      var index = -1, +          length = collection.length; + +      if (noaccum) { +        accumulator = collection[++index]; +      } +      while (++index < length) { +        accumulator = callback(accumulator, collection[index], index, collection); +      } +    } else { +      each(collection, function(value, index, collection) { +        accumulator = noaccum +          ? (noaccum = false, value) +          : callback(accumulator, value, index, collection) +      });      } -  }); +    return accumulator; +  }    /**     * The right-associative version of `_.reduce`. @@ -2185,7 +2445,7 @@     * @alias foldr     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Function} callback The function called per iteration. +   * @param {Function} [callback=identity] The function called per iteration.     * @param {Mixed} [accumulator] Initial value of the accumulator.     * @param {Mixed} [thisArg] The `this` binding of `callback`.     * @returns {Mixed} Returns the accumulated value. @@ -2197,20 +2457,21 @@     */    function reduceRight(collection, callback, accumulator, thisArg) {      var iteratee = collection, -        length = collection.length, +        length = collection ? collection.length : 0,          noaccum = arguments.length < 3; -    if (length !== +length) { +    if (typeof length != 'number') {        var props = keys(collection);        length = props.length; -    } else if (noCharByIndex && toString.call(collection) == stringClass) { +    } else if (noCharByIndex && isString(collection)) {        iteratee = collection.split('');      } -    forEach(collection, function(value, index, object) { +    callback = createCallback(callback, thisArg, indicatorObject); +    forEach(collection, function(value, index, collection) {        index = props ? props[--length] : --length;        accumulator = noaccum          ? (noaccum = false, iteratee[index]) -        : callback.call(thisArg, accumulator, iteratee[index], index, object); +        : callback(accumulator, iteratee[index], index, collection);      });      return accumulator;    } @@ -2232,9 +2493,38 @@     * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });     * // => [1, 3, 5]     */ -  var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { -    'inLoop': '!' + filterIteratorOptions.inLoop -  }); +  function reject(collection, callback, thisArg) { +    callback = createCallback(callback, thisArg); +    return filter(collection, function(value, index, collection) { +      return !callback(value, index, collection); +    }); +  } + +  /** +   * Creates an array of shuffled `array` values, using a version of the +   * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. +   * +   * @static +   * @memberOf _ +   * @category Collections +   * @param {Array|Object|String} collection The collection to shuffle. +   * @returns {Array} Returns a new shuffled collection. +   * @example +   * +   * _.shuffle([1, 2, 3, 4, 5, 6]); +   * // => [4, 1, 6, 3, 5, 2] +   */ +  function shuffle(collection) { +    var index = -1, +        result = Array(collection ? collection.length : 0); + +    forEach(collection, function(value) { +      var rand = floor(nativeRandom() * (++index + 1)); +      result[index] = result[rand]; +      result[rand] = value; +    }); +    return result; +  }    /**     * Gets the size of the `collection` by returning `collection.length` for arrays @@ -2258,7 +2548,7 @@     */    function size(collection) {      var length = collection ? collection.length : 0; -    return length === +length ? length : keys(collection).length; +    return typeof length == 'number' ? length : keys(collection).length;    }    /** @@ -2278,13 +2568,29 @@     *  else `false`.     * @example     * -   * _.some([null, 0, 'yes', false]); +   * _.some([null, 0, 'yes', false], Boolean);     * // => true     */ -  var some = createIterator(baseIteratorOptions, everyIteratorOptions, { -    'init': 'false', -    'inLoop': everyIteratorOptions.inLoop.replace('!', '') -  }); +  function some(collection, callback, thisArg) { +    var result; +    callback = createCallback(callback, thisArg); + +    if (isArray(collection)) { +      var index = -1, +          length = collection.length; + +      while (++index < length) { +        if ((result = callback(collection[index], index, collection))) { +          break; +        } +      } +    } else { +      each(collection, function(value, index, collection) { +        return !(result = callback(value, index, collection)); +      }); +    } +    return !!result; +  }    /**     * Creates an array, stable sorted in ascending order by the results of @@ -2311,31 +2617,28 @@     * _.sortBy(['larry', 'brendan', 'moe'], 'length');     * // => ['moe', 'larry', 'brendan']     */ -  var sortBy = createIterator(baseIteratorOptions, countByIteratorOptions, mapIteratorOptions, { -    'inLoop': { -      'array': -        'result[index] = {\n' + -        '  criteria: callback(value, index, collection),\n' + -        '  index: index,\n' + -        '  value: value\n' + -        '}', -      'object': -        'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '({\n' + -        '  criteria: callback(value, index, collection),\n' + -        '  index: index,\n' + -        '  value: value\n' + -        '})' -    }, -    'bottom': -      'result.sort(compareAscending);\n' + -      'length = result.length;\n' + -      'while (length--) {\n' + -      '  result[length] = result[length].value\n' + -      '}' -  }); +  function sortBy(collection, callback, thisArg) { +    var result = []; +    callback = createCallback(callback, thisArg); + +    forEach(collection, function(value, index, collection) { +      result.push({ +        'criteria': callback(value, index, collection), +        'index': index, +        'value': value +      }); +    }); + +    var length = result.length; +    result.sort(compareAscending); +    while (length--) { +      result[length] = result[length].value; +    } +    return result; +  }    /** -   * Converts the `collection`, to an array. +   * Converts the `collection` to an array.     *     * @static     * @memberOf _ @@ -2349,10 +2652,10 @@     */    function toArray(collection) {      var length = collection ? collection.length : 0; -    if (length === +length) { -      return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') +    if (typeof length == 'number') { +      return noCharByIndex && isString(collection)          ? collection.split('') -        : slice.call(collection); +        : slice(collection);      }      return values(collection);    } @@ -2365,7 +2668,7 @@     * @memberOf _     * @category Collections     * @param {Array|Object|String} collection The collection to iterate over. -   * @param {Object} properties The object of properties/values to filter by. +   * @param {Object} properties The object of property values to filter by.     * @returns {Array} Returns a new array of elements that contain the given `properties`.     * @example     * @@ -2378,19 +2681,19 @@     * _.where(stooges, { 'age': 40 });     * // => [{ 'name': 'moe', 'age': 40 }]     */ -  var where = createIterator(filterIteratorOptions, { -    'args': 'collection, properties', -    'top': -      'var props = [];\n' + -      'forIn(properties, function(value, prop) { props.push(prop) });\n' + -      'var propsLength = props.length', -    'inLoop': -      'for (var prop, pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n' + -      '  prop = props[propIndex];\n' + -      '  if (!(pass = value[prop] === properties[prop])) break\n' + -      '}\n' + -      'pass && result.push(value)' -  }); +  function where(collection, properties) { +    var props = keys(properties); +    return filter(collection, function(object) { +      var length = props.length; +      while (length--) { +        var result = object[props[length]] === properties[props[length]]; +        if (!result) { +          break; +        } +      } +      return !!result; +    }); +  }    /*--------------------------------------------------------------------------*/ @@ -2410,12 +2713,13 @@     */    function compact(array) {      var index = -1, -        length = array.length, +        length = array ? array.length : 0,          result = [];      while (++index < length) { -      if (array[index]) { -        result.push(array[index]); +      var value = array[index]; +      if (value) { +        result.push(value);        }      }      return result; @@ -2439,14 +2743,15 @@     */    function difference(array) {      var index = -1, -        length = array.length, -        flattened = concat.apply(ArrayProto, arguments), +        length = array ? array.length : 0, +        flattened = concat.apply(arrayRef, arguments),          contains = cachedContains(flattened, length),          result = [];      while (++index < length) { -      if (!contains(array[index])) { -        result.push(array[index]); +      var value = array[index]; +      if (!contains(value)) { +        result.push(value);        }      }      return result; @@ -2464,15 +2769,20 @@     * @param {Number} [n] The number of elements to return.     * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`. -   * @returns {Mixed} Returns the first element or an array of the first `n` -   *  elements of `array`. +   * @returns {Mixed} Returns the first element, or an array of the first `n` +   *  elements, of `array`.     * @example     *     * _.first([5, 4, 3, 2, 1]);     * // => 5     */    function first(array, n, guard) { -    return (n == null || guard) ? array[0] : slice.call(array, 0, n); +    if (array) { +      var length = array.length; +      return (n == null || guard) +        ? array[0] +        : slice(array, 0, nativeMin(nativeMax(0, n), length)); +    }    }    /** @@ -2494,13 +2804,12 @@     * // => [1, 2, 3, [[4]]];     */    function flatten(array, shallow) { -    var value, -        index = -1, -        length = array.length, +    var index = -1, +        length = array ? array.length : 0,          result = [];      while (++index < length) { -      value = array[index]; +      var value = array[index];        // recursively flatten arrays (susceptible to call stack limits)        if (isArray(value)) { @@ -2515,15 +2824,15 @@    /**     * Gets the index at which the first occurrence of `value` is found using     * strict equality for comparisons, i.e. `===`. If the `array` is already -   * sorted, passing `true` for `isSorted` will run a faster binary search. +   * sorted, passing `true` for `fromIndex` will run a faster binary search.     *     * @static     * @memberOf _     * @category Arrays     * @param {Array} array The array to search.     * @param {Mixed} value The value to search for. -   * @param {Boolean|Number} [fromIndex=0] The index to start searching from or -   *  `true` to perform a binary search on a sorted `array`. +   * @param {Boolean|Number} [fromIndex=0] The index to search from or `true` to +   *  perform a binary search on a sorted `array`.     * @returns {Number} Returns the index of the matched value or `-1`.     * @example     * @@ -2538,15 +2847,13 @@     */    function indexOf(array, value, fromIndex) {      var index = -1, -        length = array.length; +        length = array ? array.length : 0; -    if (fromIndex) { -      if (typeof fromIndex == 'number') { -        index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) - 1; -      } else { -        index = sortedIndex(array, value); -        return array[index] === value ? index : -1; -      } +    if (typeof fromIndex == 'number') { +      index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1; +    } else if (fromIndex) { +      index = sortedIndex(array, value); +      return array[index] === value ? index : -1;      }      while (++index < length) {        if (array[index] === value) { @@ -2564,17 +2871,22 @@     * @memberOf _     * @category Arrays     * @param {Array} array The array to query. -   * @param {Number} [n] The number of elements to return. +   * @param {Number} [n=1] The number of elements to exclude.     * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`. -   * @returns {Array} Returns all but the last element or `n` elements of `array`. +   * @returns {Array} Returns all but the last element, or `n` elements, of `array`.     * @example     *     * _.initial([3, 2, 1]);     * // => [3, 2]     */    function initial(array, n, guard) { -    return slice.call(array, 0, -((n == null || guard) ? 1 : n)); +    if (!array) { +      return []; +    } +    var length = array.length; +    n = n == null || guard ? 1 : n || 0; +    return slice(array, 0, nativeMin(nativeMax(0, length - n), length));    }    /** @@ -2585,27 +2897,40 @@     * @memberOf _     * @category Arrays     * @param {Array} [array1, array2, ...] Arrays to process. -   * @returns {Array} Returns a new array of unique elements, in order, that are -   *  present in **all** of the arrays. +   * @returns {Array} Returns a new array of unique elements that are present +   *  in **all** of the arrays.     * @example     *     * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);     * // => [1, 2]     */    function intersection(array) { -    var value, -        argsLength = arguments.length, -        cache = [], +    var args = arguments, +        argsLength = args.length, +        cache = { '0': {} },          index = -1, -        length = array.length, -        result = []; +        length = array ? array.length : 0, +        isLarge = length >= 100, +        result = [], +        seen = result; -    array: while (++index < length) { -      value = array[index]; -      if (indexOf(result, value) < 0) { -        for (var argsIndex = 1; argsIndex < argsLength; argsIndex++) { -          if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(arguments[argsIndex])))(value)) { -            continue array; +    outer: +    while (++index < length) { +      var value = array[index]; +      if (isLarge) { +        var key = value + ''; +        var inited = hasOwnProperty.call(cache[0], key) +          ? !(seen = cache[0][key]) +          : (seen = cache[0][key] = []); +      } +      if (inited || indexOf(seen, value) < 0) { +        if (isLarge) { +          seen.push(value); +        } +        var argsIndex = argsLength; +        while (--argsIndex) { +          if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) { +            continue outer;            }          }          result.push(value); @@ -2625,28 +2950,31 @@     * @param {Number} [n] The number of elements to return.     * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`. -   * @returns {Mixed} Returns the last element or an array of the last `n` -   *  elements of `array`. +   * @returns {Mixed} Returns the last element, or an array of the last `n` +   *  elements, of `array`.     * @example     *     * _.last([3, 2, 1]);     * // => 1     */    function last(array, n, guard) { -    var length = array.length; -    return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); +    if (array) { +      var length = array.length; +      return (n == null || guard) ? array[length - 1] : slice(array, nativeMax(0, length - n)); +    }    }    /** -   * Gets the index at which the last occurrence of `value` is found using -   * strict equality for comparisons, i.e. `===`. +   * Gets the index at which the last occurrence of `value` is found using strict +   * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used +   * as the offset from the end of the collection.     *     * @static     * @memberOf _     * @category Arrays     * @param {Array} array The array to search.     * @param {Mixed} value The value to search for. -   * @param {Number} [fromIndex=array.length-1] The index to start searching from. +   * @param {Number} [fromIndex=array.length-1] The index to search from.     * @returns {Number} Returns the index of the matched value or `-1`.     * @example     * @@ -2657,8 +2985,8 @@     * // => 1     */    function lastIndexOf(array, value, fromIndex) { -    var index = array.length; -    if (fromIndex && typeof fromIndex == 'number') { +    var index = array ? array.length : 0; +    if (typeof fromIndex == 'number') {        index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;      }      while (index--) { @@ -2670,84 +2998,6 @@    }    /** -   * Retrieves the maximum value of an `array`. If `callback` is passed, -   * it will be executed for each value in the `array` to generate the -   * criterion by which the value is ranked. The `callback` is bound to -   * `thisArg` and invoked with three arguments; (value, index, array). -   * -   * @static -   * @memberOf _ -   * @category Arrays -   * @param {Array} array The array to iterate over. -   * @param {Function} [callback] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding of `callback`. -   * @returns {Mixed} Returns the maximum value. -   * @example -   * -   * var stooges = [ -   *   { 'name': 'moe', 'age': 40 }, -   *   { 'name': 'larry', 'age': 50 }, -   *   { 'name': 'curly', 'age': 60 } -   * ]; -   * -   * _.max(stooges, function(stooge) { return stooge.age; }); -   * // => { 'name': 'curly', 'age': 60 }; -   */ -  function max(array, callback, thisArg) { -    var current, -        computed = -Infinity, -        index = -1, -        length = array ? array.length : 0, -        result = computed; - -    callback = createCallback(callback, thisArg); -    while (++index < length) { -      current = callback(array[index], index, array); -      if (current > computed) { -        computed = current; -        result = array[index]; -      } -    } -    return result; -  } - -  /** -   * Retrieves the minimum value of an `array`. If `callback` is passed, -   * it will be executed for each value in the `array` to generate the -   * criterion by which the value is ranked. The `callback` is bound to `thisArg` -   * and invoked with three arguments; (value, index, array). -   * -   * @static -   * @memberOf _ -   * @category Arrays -   * @param {Array} array The array to iterate over. -   * @param {Function} [callback] The function called per iteration. -   * @param {Mixed} [thisArg] The `this` binding of `callback`. -   * @returns {Mixed} Returns the minimum value. -   * @example -   * -   * _.min([10, 5, 100, 2, 1000]); -   * // => 2 -   */ -  function min(array, callback, thisArg) { -    var current, -        computed = Infinity, -        index = -1, -        length = array ? array.length : 0, -        result = computed; - -    callback = createCallback(callback, thisArg); -    while (++index < length) { -      current = callback(array[index], index, array); -      if (current < computed) { -        computed = current; -        result = array[index]; -      } -    } -    return result; -  } - -  /**     * Creates an object composed from arrays of `keys` and `values`. Pass either     * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or     * two arrays, one of `keys` and one of corresponding `values`. @@ -2766,14 +3016,15 @@     */    function object(keys, values) {      var index = -1, -        length = keys.length, +        length = keys ? keys.length : 0,          result = {};      while (++index < length) { +      var key = keys[index];        if (values) { -        result[keys[index]] = values[index]; +        result[key] = values[index];        } else { -        result[keys[index][0]] = keys[index][1]; +        result[key[0]] = key[1];        }      }      return result; @@ -2817,9 +3068,9 @@        start = 0;      }      // use `Array(length)` so V8 will avoid the slower "dictionary" mode -    // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s +    // http://youtu.be/XAqIpGU8ZZk#t=17m25s      var index = -1, -        length = nativeMax(0, Math.ceil((end - start) / step)), +        length = nativeMax(0, ceil((end - start) / step)),          result = Array(length);      while (++index < length) { @@ -2838,45 +3089,17 @@     * @alias drop, tail     * @category Arrays     * @param {Array} array The array to query. -   * @param {Number} [n] The number of elements to return. +   * @param {Number} [n=1] The number of elements to exclude.     * @param- {Object} [guard] Internally used to allow this method to work with     *  others like `_.map` without using their callback `index` argument for `n`. -   * @returns {Array} Returns all but the first value or `n` values of `array`. +   * @returns {Array} Returns all but the first element, or `n` elements, of `array`.     * @example     *     * _.rest([3, 2, 1]);     * // => [2, 1]     */    function rest(array, n, guard) { -    return slice.call(array, (n == null || guard) ? 1 : n); -  } - -  /** -   * Creates an array of shuffled `array` values, using a version of the -   * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. -   * -   * @static -   * @memberOf _ -   * @category Arrays -   * @param {Array} array The array to shuffle. -   * @returns {Array} Returns a new shuffled array. -   * @example -   * -   * _.shuffle([1, 2, 3, 4, 5, 6]); -   * // => [4, 1, 6, 3, 5, 2] -   */ -  function shuffle(array) { -    var rand, -        index = -1, -        length = array.length, -        result = Array(length); - -    while (++index < length) { -      rand = nativeFloor(nativeRandom() * (index + 1)); -      result[index] = result[rand]; -      result[rand] = array[index]; -    } -    return result; +    return slice(array, (n == null || guard) ? 1 : nativeMax(0, n));    }    /** @@ -2920,15 +3143,18 @@     * // => 2     */    function sortedIndex(array, value, callback, thisArg) { -    var mid, -        low = 0, -        high = array.length; +    var low = 0, +        high = array ? array.length : low; -    callback = createCallback(callback, thisArg); +    // explicitly reference `identity` for better inlining in Firefox +    callback = callback ? createCallback(callback, thisArg) : identity;      value = callback(value); +      while (low < high) { -      mid = (low + high) >>> 1; -      callback(array[mid]) < value ? low = mid + 1 : high = mid; +      var mid = (low + high) >>> 1; +      callback(array[mid]) < value +        ? low = mid + 1 +        : high = mid;      }      return low;    } @@ -2949,17 +3175,7 @@     * // => [1, 2, 3, 101, 10]     */    function union() { -    var index = -1, -        flattened = concat.apply(ArrayProto, arguments), -        length = flattened.length, -        result = []; - -    while (++index < length) { -      if (indexOf(result, flattened[index]) < 0) { -        result.push(flattened[index]); -      } -    } -    return result; +    return uniq(concat.apply(arrayRef, arguments));    }    /** @@ -2993,11 +3209,10 @@     * // => [1, 2, 3]     */    function uniq(array, isSorted, callback, thisArg) { -    var computed, -        index = -1, -        length = array.length, +    var index = -1, +        length = array ? array.length : 0,          result = [], -        seen = []; +        seen = result;      // juggle arguments      if (typeof isSorted == 'function') { @@ -3005,15 +3220,33 @@        callback = isSorted;        isSorted = false;      } -    callback = createCallback(callback, thisArg); +    // init value cache for large arrays +    var isLarge = !isSorted && length >= 75; +    if (isLarge) { +      var cache = {}; +    } +    if (callback) { +      seen = []; +      callback = createCallback(callback, thisArg); +    }      while (++index < length) { -      computed = callback(array[index], index, array); +      var value = array[index], +          computed = callback ? callback(value, index, array) : value; + +      if (isLarge) { +        var key = computed + ''; +        var inited = hasOwnProperty.call(cache, key) +          ? !(seen = cache[key]) +          : (seen = cache[key] = []); +      }        if (isSorted              ? !index || seen[seen.length - 1] !== computed -            : indexOf(seen, computed) < 0 +            : inited || indexOf(seen, computed) < 0            ) { -        seen.push(computed); -        result.push(array[index]); +        if (callback || isLarge) { +          seen.push(computed); +        } +        result.push(value);        }      }      return result; @@ -3036,13 +3269,14 @@     */    function without(array) {      var index = -1, -        length = array.length, +        length = array ? array.length : 0,          contains = cachedContains(arguments, 1, 20),          result = [];      while (++index < length) { -      if (!contains(array[index])) { -        result.push(array[index]); +      var value = array[index]; +      if (!contains(value)) { +        result.push(value);        }      }      return result; @@ -3066,7 +3300,7 @@     */    function zip(array) {      var index = -1, -        length = max(pluck(arguments, 'length')), +        length = array ? max(pluck(arguments, 'length')) : 0,          result = Array(length);      while (++index < length) { @@ -3078,8 +3312,9 @@    /*--------------------------------------------------------------------------*/    /** -   * Creates a function that is restricted to executing only after it is -   * called `n` times. +   * Creates a function that is restricted to executing `func` only after it is +   * called `n` times. The `func` is executed with the `this` binding of the +   * created function.     *     * @static     * @memberOf _ @@ -3134,7 +3369,7 @@      // (in V8 `Function#bind` is slower except when partially applied)      return isBindFast || (nativeBind && arguments.length > 2)        ? nativeBind.call.apply(nativeBind, arguments) -      : createBound(func, thisArg, slice.call(arguments, 2)); +      : createBound(func, thisArg, slice(arguments, 2));    }    /** @@ -3159,29 +3394,61 @@     * jQuery('#lodash_button').on('click', buttonView.onClick);     * // => When the button is clicked, `this.label` will have the correct value     */ -  var bindAll = createIterator({ -    'useHas': false, -    'useStrict': false, -    'args': 'object', -    'top': -      'var funcs = arguments,\n' + -      '    length = funcs.length;\n' + -      'if (length > 1) {\n' + -      '  for (var index = 1; index < length; index++) {\n' + -      '    result[funcs[index]] = bind(result[funcs[index]], result)\n' + -      '  }\n' + -      '  return result\n' + -      '}', -    'inLoop': -      'if (isFunction(result[index])) {\n' + -      '  result[index] = bind(result[index], result)\n' + -      '}' -  }); +  function bindAll(object) { +    var funcs = arguments, +        index = funcs.length > 1 ? 0 : (funcs = functions(object), -1), +        length = funcs.length; + +    while (++index < length) { +      var key = funcs[index]; +      object[key] = bind(object[key], object); +    } +    return object; +  } + +  /** +   * Creates a function that, when called, invokes the method at `object[key]` +   * and prepends any additional `bindKey` arguments to those passed to the bound +   * function. This method differs from `_.bind` by allowing bound functions to +   * reference methods that will be redefined or don't yet exist. +   * See http://michaux.ca/articles/lazy-function-definition-pattern. +   * +   * @static +   * @memberOf _ +   * @category Functions +   * @param {Object} object The object the method belongs to. +   * @param {String} key The key of the method. +   * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. +   * @returns {Function} Returns the new bound function. +   * @example +   * +   * var object = { +   *   'name': 'moe', +   *   'greet': function(greeting) { +   *     return greeting + ' ' + this.name; +   *   } +   * }; +   * +   * var func = _.bindKey(object, 'greet', 'hi'); +   * func(); +   * // => 'hi moe' +   * +   * object.greet = function(greeting) { +   *   return greeting + ', ' + this.name + '!'; +   * }; +   * +   * func(); +   * // => 'hi, moe!' +   */ +  function bindKey(object, key) { +    return createBound(object, key, slice(arguments, 2)); +  }    /**     * Creates a function that is the composition of the passed functions,     * where each function consumes the return value of the function that follows.     * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. +   * Each function is executed with the `this` binding of the composed function.     *     * @static     * @memberOf _ @@ -3241,7 +3508,6 @@          result = func.apply(thisArg, args);        }      } -      return function() {        var isImmediate = immediate && !timeoutId;        args = arguments; @@ -3275,8 +3541,8 @@     * // => 'logged later' (Appears after one second.)     */    function delay(func, wait) { -    var args = slice.call(arguments, 2); -    return setTimeout(function() { return func.apply(undefined, args); }, wait); +    var args = slice(arguments, 2); +    return setTimeout(function() { func.apply(undefined, args); }, wait);    }    /** @@ -3295,51 +3561,16 @@     * // returns from the function before `alert` is called     */    function defer(func) { -    var args = slice.call(arguments, 1); -    return setTimeout(function() { return func.apply(undefined, args); }, 1); -  } - -  /** -   * Creates a function that, when called, invokes `object[methodName]` and -   * prepends any additional `lateBind` arguments to those passed to the bound -   * function. This method -   * -   * @static -   * @memberOf _ -   * @category Functions -   * @param {Object} object The object the method belongs to. -   * @param {String} methodName The method name. -   * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. -   * @returns {Function} Returns the new bound function. -   * @example -   * -   * var object = { -   *   'name': 'moe', -   *   'greet': function(greeting) { -   *     return greeting + ' ' + this.name; -   *   } -   * }; -   * -   * var func = _.bind(object, 'greet', 'hi'); -   * func(); -   * // => 'hi moe' -   * -   * object.greet = function(greeting) { -   *   return greeting + ', ' + this.name + '!'; -   * }; -   * -   * func(); -   * // => 'hi, moe!' -   */ -  function lateBind(object, methodName) { -    return createBound(methodName, object, slice.call(arguments, 2)); +    var args = slice(arguments, 1); +    return setTimeout(function() { func.apply(undefined, args); }, 1);    }    /**     * Creates a function that memoizes the result of `func`. If `resolver` is     * passed, it will be used to determine the cache key for storing the result     * based on the arguments passed to the memoized function. By default, the first -   * argument passed to the memoized function is used as the cache key. +   * argument passed to the memoized function is used as the cache key. The `func` +   * is executed with the `this` binding of the memoized function.     *     * @static     * @memberOf _ @@ -3356,16 +3587,17 @@    function memoize(func, resolver) {      var cache = {};      return function() { -      var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; -      return hasOwnProperty.call(cache, prop) -        ? cache[prop] -        : (cache[prop] = func.apply(this, arguments)); +      var key = resolver ? resolver.apply(this, arguments) : arguments[0]; +      return hasOwnProperty.call(cache, key) +        ? cache[key] +        : (cache[key] = func.apply(this, arguments));      };    }    /** -   * Creates a function that is restricted to one execution. Repeat calls to -   * the function will return the value of the first call. +   * Creates a function that is restricted to execute `func` once. Repeat calls to +   * the function will return the value of the first call. The `func` is executed +   * with the `this` binding of the created function.     *     * @static     * @memberOf _ @@ -3398,8 +3630,8 @@    /**     * Creates a function that, when called, invokes `func` with any additional -   * `partial` arguments prepended to those passed to the new function. This method -   * is similar to `bind`, except it does **not** alter the `this` binding. +   * `partial` arguments prepended to those passed to the new function. This +   * method is similar to `bind`, except it does **not** alter the `this` binding.     *     * @static     * @memberOf _ @@ -3415,7 +3647,7 @@     * // => 'hi: moe'     */    function partial(func) { -    return createBound(func, slice.call(arguments, 1)); +    return createBound(func, slice(arguments, 1));    }    /** @@ -3448,20 +3680,21 @@        timeoutId = null;        result = func.apply(thisArg, args);      } -      return function() {        var now = new Date, -          remain = wait - (now - lastCalled); +          remaining = wait - (now - lastCalled);        args = arguments;        thisArg = this; -      if (remain <= 0) { +      if (remaining <= 0) { +        clearTimeout(timeoutId); +        timeoutId = null;          lastCalled = now;          result = func.apply(thisArg, args);        }        else if (!timeoutId) { -        timeoutId = setTimeout(trailingCall, remain); +        timeoutId = setTimeout(trailingCall, remaining);        }        return result;      }; @@ -3469,8 +3702,9 @@    /**     * Creates a function that passes `value` to the `wrapper` function as its -   * first argument. Additional arguments passed to the new function are appended -   * to those passed to the `wrapper` function. +   * first argument. Additional arguments passed to the function are appended +   * to those passed to the `wrapper` function. The `wrapper` is executed with +   * the `this` binding of the created function.     *     * @static     * @memberOf _ @@ -3480,19 +3714,17 @@     * @returns {Function} Returns the new function.     * @example     * -   * var hello = function(name) { return 'hello: ' + name; }; +   * var hello = function(name) { return 'hello ' + name; };     * hello = _.wrap(hello, function(func) {     *   return 'before, ' + func('moe') + ', after';     * });     * hello(); -   * // => 'before, hello: moe, after' +   * // => 'before, hello moe, after'     */    function wrap(value, wrapper) {      return function() {        var args = [value]; -      if (arguments.length) { -        push.apply(args, arguments); -      } +      push.apply(args, arguments);        return wrapper.apply(this, args);      };    } @@ -3511,7 +3743,7 @@     * @example     *     * _.escape('Moe, Larry & Curly'); -   * // => "Moe, Larry & Curly" +   * // => 'Moe, Larry & Curly'     */    function escape(string) {      return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); @@ -3520,8 +3752,6 @@    /**     * This function returns the first argument passed to it.     * -   * Note: It is used throughout Lo-Dash as a default callback. -   *     * @static     * @memberOf _     * @category Utilities @@ -3565,15 +3795,10 @@        lodash.prototype[methodName] = function() {          var args = [this.__wrapped__]; -        if (arguments.length) { -          push.apply(args, arguments); -        } +        push.apply(args, arguments); +          var result = func.apply(lodash, args); -        if (this.__chain__) { -          result = new lodash(result); -          result.__chain__ = true; -        } -        return result; +        return new lodash(result);        };      });    } @@ -3622,7 +3847,7 @@        max = min;        min = 0;      } -    return min + nativeFloor(nativeRandom() * ((+max || 0) - min + 1)); +    return min + floor(nativeRandom() * ((+max || 0) - min + 1));    }    /** @@ -3630,7 +3855,6 @@     * it will be invoked and its result returned, else the property value is     * returned. If `object` is falsey, then `null` is returned.     * -   * @deprecated     * @static     * @memberOf _     * @category Utilities @@ -3676,14 +3900,20 @@     * @param {String} text The template text.     * @param {Obect} data The data object used to populate the text.     * @param {Object} options The options object. +   *  escape - The "escape" delimiter regexp. +   *  evaluate - The "evaluate" delimiter regexp. +   *  interpolate - The "interpolate" delimiter regexp. +   *  sourceURL - The sourceURL of the template's compiled source. +   *  variable - The data object variable name. +   *     * @returns {Function|String} Returns a compiled function when no `data` object     *  is given, else it returns the interpolated text.     * @example     *     * // using a compiled template -   * var compiled = _.template('hello: <%= name %>'); +   * var compiled = _.template('hello <%= name %>');     * compiled({ 'name': 'moe' }); -   * // => 'hello: moe' +   * // => 'hello moe'     *     * var list = '<% _.forEach(people, function(name) { %><li><%= name %></li><% }); %>';     * _.template(list, { 'people': ['moe', 'larry', 'curly'] }); @@ -3691,26 +3921,35 @@     *     * // using the "escape" delimiter to escape HTML in data property values     * _.template('<b><%- value %></b>', { 'value': '<script>' }); -   * // => '<b><script></b>' +   * // => '<b><script></b>' +   * +   * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter +   * _.template('hello ${ name }', { 'name': 'curly' }); +   * // => 'hello curly'     *     * // using the internal `print` function in "evaluate" delimiters -   * _.template('<% print("Hello " + epithet); %>.', { 'epithet': 'stooge' }); -   * // => 'Hello stooge.' +   * _.template('<% print("hello " + epithet); %>!', { 'epithet': 'stooge' }); +   * // => 'hello stooge!'     * -   * // using custom template delimiter settings +   * // using custom template delimiters     * _.templateSettings = { -   *   'interpolate': /\{\{([\s\S]+?)\}\}/g +   *   'interpolate': /{{([\s\S]+?)}}/g     * };     * -   * _.template('Hello {{ name }}!', { 'name': 'Mustache' }); -   * // => 'Hello Mustache!' +   * _.template('hello {{ name }}!', { 'name': 'mustache' }); +   * // => 'hello mustache!' +   * +   * // using the `sourceURL` option to specify a custom sourceURL for the template +   * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' }); +   * compiled(data); +   * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector     *     * // using the `variable` option to ensure a with-statement isn't used in the compiled template -   * var compiled = _.template('hello: <%= data.name %>', null, { 'variable': 'data' }); +   * var compiled = _.template('hello <%= data.name %>!', null, { 'variable': 'data' });     * compiled.source;     * // => function(data) {     *   var __t, __p = '', __e = _.escape; -   *   __p += 'hello: ' + ((__t = ( data.name )) == null ? '' : __t); +   *   __p += 'hello ' + ((__t = ( data.name )) == null ? '' : __t) + '!';     *   return __p;     * }     * @@ -3727,12 +3966,14 @@      // http://ejohn.org/blog/javascript-micro-templating/      // and Laura Doktorova's doT.js      // https://github.com/olado/doT +    text || (text = '');      options || (options = {});      var isEvaluating,          result, -        index = 0,          settings = lodash.templateSettings, +        index = 0, +        interpolate = options.interpolate || settings.interpolate || reNoMatch,          source = "__p += '",          variable = options.variable || settings.variable,          hasVariable = variable; @@ -3740,22 +3981,33 @@      // compile regexp to match each delimiter      var reDelimiters = RegExp(        (options.escape || settings.escape || reNoMatch).source + '|' + -      (options.interpolate || settings.interpolate || reNoMatch).source + '|' + +      interpolate.source + '|' + +      (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +        (options.evaluate || settings.evaluate || reNoMatch).source + '|$'      , 'g'); -    text.replace(reDelimiters, function(match, escapeValue, interpolateValue, evaluateValue, offset) { +    text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) { +      interpolateValue || (interpolateValue = esTemplateValue); +        // escape characters that cannot be included in string literals        source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);        // replace delimiters with snippets -      source += -        escapeValue ? "' +\n__e(" + escapeValue + ") +\n'" : -        evaluateValue ? "';\n" + evaluateValue + ";\n__p += '" : -        interpolateValue ? "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'" : ''; - +      if (escapeValue) { +        source += "' +\n__e(" + escapeValue + ") +\n'"; +      } +      if (evaluateValue) { +        source += "';\n" + evaluateValue + ";\n__p += '"; +      } +      if (interpolateValue) { +        source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; +      }        isEvaluating || (isEvaluating = evaluateValue || reComplexDelimiter.test(escapeValue || interpolateValue));        index = offset + match.length; + +      // the JS engine embedded in Adobe products requires returning the `match` +      // string in order to produce the correct `offset` value +      return match;      });      source += "';\n"; @@ -3785,10 +4037,10 @@      // frame code as the function body      source = 'function(' + variable + ') {\n' +        (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') + -      'var __t, __p = \'\', __e = _.escape' + +      "var __t, __p = '', __e = _.escape" +        (isEvaluating          ? ', __j = Array.prototype.join;\n' + -          'function print() { __p += __j.call(arguments, \'\') }\n' +          "function print() { __p += __j.call(arguments, '') }\n"          : (hasVariable ? '' : ', __d = ' + variable + '.' + variable + ' || ' + variable) + ';\n'        ) +        source + @@ -3797,7 +4049,7 @@      // use a sourceURL for easier debugging      // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl      var sourceURL = useSourceURL -      ? '\n//@ sourceURL=/lodash/template/source[' + (templateCounter++) + ']' +      ? '\n//@ sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']')        : '';      try { @@ -3852,8 +4104,9 @@    }    /** -   * Converts the HTML entities `&`, `<`, `>`, `"`, and `'` -   * in `string` to their corresponding characters. +   * The opposite of `_.escape`, this method converts the HTML entities +   * `&`, `<`, `>`, `"`, and `'` in `string` to their +   * corresponding characters.     *     * @static     * @memberOf _ @@ -3863,63 +4116,35 @@     * @example     *     * _.unescape('Moe, Larry & Curly'); -   * // => "Moe, Larry & Curly" +   * // => 'Moe, Larry & Curly'     */    function unescape(string) {      return string == null ? '' : (string + '').replace(reEscapedHtml, unescapeHtmlChar);    }    /** -   * Generates a unique id. If `prefix` is passed, the id will be appended to it. +   * Generates a unique ID. If `prefix` is passed, the ID will be appended to it.     *     * @static     * @memberOf _     * @category Utilities -   * @param {String} [prefix] The value to prefix the id with. -   * @returns {Number|String} Returns a numeric id if no prefix is passed, else -   *  a string id may be returned. +   * @param {String} [prefix] The value to prefix the ID with. +   * @returns {String} Returns the unique ID.     * @example     *     * _.uniqueId('contact_');     * // => 'contact_104' +   * +   * _.uniqueId(); +   * // => '105'     */    function uniqueId(prefix) { -    var id = idCounter++; -    return prefix ? prefix + id : id; +    return (prefix == null ? '' : prefix + '') + (++idCounter);    }    /*--------------------------------------------------------------------------*/    /** -   * Wraps the value in a `lodash` wrapper object. -   * -   * @static -   * @memberOf _ -   * @category Chaining -   * @param {Mixed} value The value to wrap. -   * @returns {Object} Returns the wrapper object. -   * @example -   * -   * var stooges = [ -   *   { 'name': 'moe', 'age': 40 }, -   *   { 'name': 'larry', 'age': 50 }, -   *   { 'name': 'curly', 'age': 60 } -   * ]; -   * -   * var youngest = _.chain(stooges) -   *     .sortBy(function(stooge) { return stooge.age; }) -   *     .map(function(stooge) { return stooge.name + ' is ' + stooge.age; }) -   *     .first() -   *     .value(); -   * // => 'moe is 40' -   */ -  function chain(value) { -    value = new lodash(value); -    value.__chain__ = true; -    return value; -  } - -  /**     * Invokes `interceptor` with the `value` as the first argument, and then     * returns `value`. The purpose of this method is to "tap into" a method chain,     * in order to perform operations on intermediate results within the chain. @@ -3935,7 +4160,7 @@     * _.chain([1, 2, 3, 200])     *  .filter(function(num) { return num % 2 == 0; })     *  .tap(alert) -   *  .map(function(num) { return num * num }) +   *  .map(function(num) { return num * num; })     *  .value();     * // => // [2, 200] (alerted)     * // => [4, 40000] @@ -3946,84 +4171,120 @@    }    /** -   * Enables method chaining on the wrapper object. +   * Produces the `toString` result of the wrapped value.     * -   * @name chain -   * @deprecated +   * @name toString     * @memberOf _     * @category Chaining -   * @returns {Mixed} Returns the wrapper object. +   * @returns {String} Returns the string result.     * @example     * -   * _([1, 2, 3]).value(); -   * // => [1, 2, 3] +   * _([1, 2, 3]).toString(); +   * // => '1,2,3'     */ -  function wrapperChain() { -    this.__chain__ = true; -    return this; +  function wrapperToString() { +    return this.__wrapped__ + '';    }    /**     * Extracts the wrapped value.     * -   * @name value +   * @name valueOf     * @memberOf _ +   * @alias value     * @category Chaining     * @returns {Mixed} Returns the wrapped value.     * @example     * -   * _([1, 2, 3]).value(); +   * _([1, 2, 3]).valueOf();     * // => [1, 2, 3]     */ -  function wrapperValue() { +  function wrapperValueOf() {      return this.__wrapped__;    }    /*--------------------------------------------------------------------------*/ -  /** -   * The semantic version number. -   * -   * @static -   * @memberOf _ -   * @type String -   */ -  lodash.VERSION = '0.7.0'; - -  // assign static methods +  // add functions that return wrapped values when chaining    lodash.after = after; +  lodash.assign = assign;    lodash.bind = bind;    lodash.bindAll = bindAll; -  lodash.chain = chain; -  lodash.clone = clone; +  lodash.bindKey = bindKey;    lodash.compact = compact;    lodash.compose = compose; -  lodash.contains = contains;    lodash.countBy = countBy;    lodash.debounce = debounce;    lodash.defaults = defaults;    lodash.defer = defer;    lodash.delay = delay;    lodash.difference = difference; -  lodash.escape = escape; -  lodash.every = every; -  lodash.extend = extend;    lodash.filter = filter; -  lodash.find = find; -  lodash.first = first;    lodash.flatten = flatten;    lodash.forEach = forEach;    lodash.forIn = forIn;    lodash.forOwn = forOwn;    lodash.functions = functions;    lodash.groupBy = groupBy; -  lodash.has = has; -  lodash.identity = identity; -  lodash.indexOf = indexOf;    lodash.initial = initial;    lodash.intersection = intersection;    lodash.invert = invert;    lodash.invoke = invoke; +  lodash.keys = keys; +  lodash.map = map; +  lodash.max = max; +  lodash.memoize = memoize; +  lodash.merge = merge; +  lodash.min = min; +  lodash.object = object; +  lodash.omit = omit; +  lodash.once = once; +  lodash.pairs = pairs; +  lodash.partial = partial; +  lodash.pick = pick; +  lodash.pluck = pluck; +  lodash.range = range; +  lodash.reject = reject; +  lodash.rest = rest; +  lodash.shuffle = shuffle; +  lodash.sortBy = sortBy; +  lodash.tap = tap; +  lodash.throttle = throttle; +  lodash.times = times; +  lodash.toArray = toArray; +  lodash.union = union; +  lodash.uniq = uniq; +  lodash.values = values; +  lodash.where = where; +  lodash.without = without; +  lodash.wrap = wrap; +  lodash.zip = zip; + +  // add aliases +  lodash.collect = map; +  lodash.drop = rest; +  lodash.each = forEach; +  lodash.extend = assign; +  lodash.methods = functions; +  lodash.select = filter; +  lodash.tail = rest; +  lodash.unique = uniq; + +  // add functions to `lodash.prototype` +  mixin(lodash); + +  /*--------------------------------------------------------------------------*/ + +  // add functions that return unwrapped values when chaining +  lodash.clone = clone; +  lodash.cloneDeep = cloneDeep; +  lodash.contains = contains; +  lodash.escape = escape; +  lodash.every = every; +  lodash.find = find; +  lodash.has = has; +  lodash.identity = identity; +  lodash.indexOf = indexOf;    lodash.isArguments = isArguments;    lodash.isArray = isArray;    lodash.isBoolean = isBoolean; @@ -4041,120 +4302,123 @@    lodash.isRegExp = isRegExp;    lodash.isString = isString;    lodash.isUndefined = isUndefined; -  lodash.keys = keys; -  lodash.last = last;    lodash.lastIndexOf = lastIndexOf; -  lodash.lateBind = lateBind; -  lodash.map = map; -  lodash.max = max; -  lodash.memoize = memoize; -  lodash.merge = merge; -  lodash.min = min;    lodash.mixin = mixin;    lodash.noConflict = noConflict; -  lodash.object = object; -  lodash.omit = omit; -  lodash.once = once; -  lodash.pairs = pairs; -  lodash.partial = partial; -  lodash.pick = pick; -  lodash.pluck = pluck;    lodash.random = random; -  lodash.range = range;    lodash.reduce = reduce;    lodash.reduceRight = reduceRight; -  lodash.reject = reject; -  lodash.rest = rest;    lodash.result = result; -  lodash.shuffle = shuffle;    lodash.size = size;    lodash.some = some; -  lodash.sortBy = sortBy;    lodash.sortedIndex = sortedIndex; -  lodash.tap = tap;    lodash.template = template; -  lodash.throttle = throttle; -  lodash.times = times; -  lodash.toArray = toArray;    lodash.unescape = unescape; -  lodash.union = union; -  lodash.uniq = uniq;    lodash.uniqueId = uniqueId; -  lodash.values = values; -  lodash.where = where; -  lodash.without = without; -  lodash.wrap = wrap; -  lodash.zip = zip; -  // assign aliases +  // add aliases    lodash.all = every;    lodash.any = some; -  lodash.collect = map;    lodash.detect = find; -  lodash.drop = rest; -  lodash.each = forEach;    lodash.foldl = reduce;    lodash.foldr = reduceRight; -  lodash.head = first;    lodash.include = contains;    lodash.inject = reduce; -  lodash.methods = functions; -  lodash.select = filter; -  lodash.tail = rest; -  lodash.take = first; -  lodash.unique = uniq; -  // add pseudo private properties used and removed during the build process -  lodash._iteratorTemplate = iteratorTemplate; -  lodash._shimKeys = shimKeys; +  forOwn(lodash, function(func, methodName) { +    if (!lodash.prototype[methodName]) { +      lodash.prototype[methodName] = function() { +        var args = [this.__wrapped__]; +        push.apply(args, arguments); +        return func.apply(lodash, args); +      }; +    } +  });    /*--------------------------------------------------------------------------*/ -  // add all static functions to `lodash.prototype` -  mixin(lodash); +  // add functions capable of returning wrapped and unwrapped values when chaining +  lodash.first = first; +  lodash.last = last; + +  // add aliases +  lodash.take = first; +  lodash.head = first; -  // add `lodash.prototype.chain` after calling `mixin()` to avoid overwriting -  // it with the wrapped `lodash.chain` -  lodash.prototype.chain = wrapperChain; -  lodash.prototype.value = wrapperValue; +  forOwn(lodash, function(func, methodName) { +    if (!lodash.prototype[methodName]) { +      lodash.prototype[methodName]= function(n, guard) { +        var result = func(this.__wrapped__, n, guard); +        return (n == null || guard) ? result : new lodash(result); +      }; +    } +  }); -  // add all mutator Array functions to the wrapper. -  forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { -    var func = ArrayProto[methodName]; +  /*--------------------------------------------------------------------------*/ -    lodash.prototype[methodName] = function() { -      var value = this.__wrapped__; -      func.apply(value, arguments); +  /** +   * The semantic version number. +   * +   * @static +   * @memberOf _ +   * @type String +   */ +  lodash.VERSION = '1.0.0-rc.3'; -      // avoid array-like object bugs with `Array#shift` and `Array#splice` in -      // Firefox < 10 and IE < 9 -      if (hasObjectSpliceBug && value.length === 0) { -        delete value[0]; -      } -      if (this.__chain__) { -        value = new lodash(value); -        value.__chain__ = true; -      } -      return value; +  // add "Chaining" functions to the wrapper +  lodash.prototype.toString = wrapperToString; +  lodash.prototype.value = wrapperValueOf; +  lodash.prototype.valueOf = wrapperValueOf; + +  // add `Array` functions that return unwrapped values +  each(['join', 'pop', 'shift'], function(methodName) { +    var func = arrayRef[methodName]; +    lodash.prototype[methodName] = function() { +      return func.apply(this.__wrapped__, arguments);      };    }); -  // add all accessor Array functions to the wrapper. -  forEach(['concat', 'join', 'slice'], function(methodName) { -    var func = ArrayProto[methodName]; - +  // add `Array` functions that return the wrapped value +  each(['push', 'reverse', 'sort', 'unshift'], function(methodName) { +    var func = arrayRef[methodName];      lodash.prototype[methodName] = function() { -      var value = this.__wrapped__, -          result = func.apply(value, arguments); +      func.apply(this.__wrapped__, arguments); +      return this; +    }; +  }); -      if (this.__chain__) { -        result = new lodash(result); -        result.__chain__ = true; -      } -      return result; +  // add `Array` functions that return new wrapped values +  each(['concat', 'slice', 'splice'], function(methodName) { +    var func = arrayRef[methodName]; +    lodash.prototype[methodName] = function() { +      var result = func.apply(this.__wrapped__, arguments); +      return new lodash(result);      };    }); +  // avoid array-like object bugs with `Array#shift` and `Array#splice` +  // in Firefox < 10 and IE < 9 +  if (hasObjectSpliceBug) { +    each(['pop', 'shift', 'splice'], function(methodName) { +      var func = arrayRef[methodName], +          isSplice = methodName == 'splice'; + +      lodash.prototype[methodName] = function() { +        var value = this.__wrapped__, +            result = func.apply(value, arguments); + +        if (value.length === 0) { +          delete value[0]; +        } +        return isSplice ? new lodash(result) : result; +      }; +    }); +  } + +  // add pseudo private property to be used and removed during the build process +  lodash._each = each; +  lodash._iteratorTemplate = iteratorTemplate; +    /*--------------------------------------------------------------------------*/    // expose Lo-Dash diff --git a/module/web/static/js/libs/require-2.0.6.js b/module/web/static/js/libs/require-2.1.2.js index b592d5f22..0e7b81bc4 100644 --- a/module/web/static/js/libs/require-2.0.6.js +++ b/module/web/static/js/libs/require-2.1.2.js @@ -1,5 +1,5 @@  /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.0.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.   * Available via the MIT or new BSD license.   * see: http://github.com/jrburke/requirejs for details   */ @@ -12,7 +12,7 @@ var requirejs, require, define;  (function (global) {      var req, s, head, baseElement, dataMain, src,          interactiveScript, currentlyAddingScript, mainScript, subPath, -        version = '2.0.6', +        version = '2.1.2',          commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,          cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,          jsSuffixRegExp = /\.js$/, @@ -81,6 +81,10 @@ var requirejs, require, define;          return hasOwn.call(obj, prop);      } +    function getOwn(obj, prop) { +        return hasProp(obj, prop) && obj[prop]; +    } +      /**       * Cycles over properties in an object and calls a function for each       * property value. If the function returns a truthy value, then the @@ -89,7 +93,7 @@ var requirejs, require, define;      function eachProp(obj, func) {          var prop;          for (prop in obj) { -            if (obj.hasOwnProperty(prop)) { +            if (hasProp(obj, prop)) {                  if (func(obj[prop], prop)) {                      break;                  } @@ -100,9 +104,6 @@ var requirejs, require, define;      /**       * Simple function to mix in properties from source into target,       * but only if target does not already have a property of the same name. -     * This is not robust in IE for transferring methods that match -     * Object.prototype names, but the uses of mixin here seem unlikely to -     * trigger a problem related to that.       */      function mixin(target, source, force, deepStringMixin) {          if (source) { @@ -147,41 +148,6 @@ var requirejs, require, define;          return g;      } -    function makeContextModuleFunc(func, relMap, enableBuildCallback) { -        return function () { -            //A version of a require function that passes a moduleName -            //value for items that may need to -            //look up paths relative to the moduleName -            var args = aps.call(arguments, 0), lastArg; -            if (enableBuildCallback && -                    isFunction((lastArg = args[args.length - 1]))) { -                lastArg.__requireJsBuild = true; -            } -            args.push(relMap); -            return func.apply(null, args); -        }; -    } - -    function addRequireMethods(req, context, relMap) { -        each([ -            ['toUrl'], -            ['undef'], -            ['defined', 'requireDefined'], -            ['specified', 'requireSpecified'] -        ], function (item) { -            var prop = item[1] || item[0]; -            req[item[0]] = context ? makeContextModuleFunc(context[prop], relMap) : -                    //If no context, then use default context. Reference from -                    //contexts instead of early binding to default context, so -                    //that during builds, the latest instance of the default -                    //context with its config gets used. -                    function () { -                        var ctx = contexts[defContextName]; -                        return ctx[prop].apply(ctx, arguments); -                    }; -        }); -    } -      /**       * Constructs an error with a pointer to an URL with more information.       * @param {String} id the error ID that maps to an ID on a web page. @@ -230,7 +196,9 @@ var requirejs, require, define;                  baseUrl: './',                  paths: {},                  pkgs: {}, -                shim: {} +                shim: {}, +                map: {}, +                config: {}              },              registry = {},              undefEvents = {}, @@ -238,12 +206,7 @@ var requirejs, require, define;              defined = {},              urlFetched = {},              requireCounter = 1, -            unnormalizedCounter = 1, -            //Used to track the order in which modules -            //should be executed, by the order they -            //load. Important for consistent cycle resolution -            //behavior. -            waitAry = []; +            unnormalizedCounter = 1;          /**           * Trims the . and .. from an array of path segments. @@ -302,7 +265,7 @@ var requirejs, require, define;                  //otherwise, assume it is a top-level require that will                  //be relative to baseUrl in the end.                  if (baseName) { -                    if (config.pkgs[baseName]) { +                    if (getOwn(config.pkgs, baseName)) {                          //If the baseName is a package name, then just treat it as one                          //name to concat the name with.                          normalizedBaseParts = baseParts = [baseName]; @@ -320,7 +283,7 @@ var requirejs, require, define;                      //Some use of packages may use a . path to reference the                      //'main' module name, so normalize for that. -                    pkgConfig = config.pkgs[(pkgName = name[0])]; +                    pkgConfig = getOwn(config.pkgs, (pkgName = name[0]));                      name = name.join('/');                      if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {                          name = pkgName; @@ -343,12 +306,12 @@ var requirejs, require, define;                          //Find the longest baseName segment match in the config.                          //So, do joins on the biggest to smallest lengths of baseParts.                          for (j = baseParts.length; j > 0; j -= 1) { -                            mapValue = map[baseParts.slice(0, j).join('/')]; +                            mapValue = getOwn(map, baseParts.slice(0, j).join('/'));                              //baseName segment has config, find if it has one for                              //this name.                              if (mapValue) { -                                mapValue = mapValue[nameSegment]; +                                mapValue = getOwn(mapValue, nameSegment);                                  if (mapValue) {                                      //Match, update name to the new value.                                      foundMap = mapValue; @@ -366,8 +329,8 @@ var requirejs, require, define;                      //Check for a star map match, but just hold on to it,                      //if there is a shorter segment match later in a matching                      //config, then favor over this star map. -                    if (!foundStarMap && starMap && starMap[nameSegment]) { -                        foundStarMap = starMap[nameSegment]; +                    if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { +                        foundStarMap = getOwn(starMap, nameSegment);                          starI = i;                      }                  } @@ -399,18 +362,31 @@ var requirejs, require, define;          }          function hasPathFallback(id) { -            var pathConfig = config.paths[id]; +            var pathConfig = getOwn(config.paths, id);              if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {                  removeScript(id);                  //Pop off the first array value, since it failed, and                  //retry                  pathConfig.shift(); -                context.undef(id); +                context.require.undef(id);                  context.require([id]);                  return true;              }          } +        //Turns a plugin!resource to [plugin, resource] +        //with the plugin being undefined if the name +        //did not have a plugin prefix. +        function splitPrefix(name) { +            var prefix, +                index = name ? name.indexOf('!') : -1; +            if (index > -1) { +                prefix = name.substring(0, index); +                name = name.substring(index + 1, name.length); +            } +            return [prefix, name]; +        } +          /**           * Creates a module mapping that includes plugin prefix, module           * name, and path. If parentModuleMap is provided it will @@ -427,8 +403,7 @@ var requirejs, require, define;           * @returns {Object}           */          function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { -            var url, pluginModule, suffix, -                index = name ? name.indexOf('!') : -1, +            var url, pluginModule, suffix, nameParts,                  prefix = null,                  parentName = parentModuleMap ? parentModuleMap.name : null,                  originalName = name, @@ -442,14 +417,13 @@ var requirejs, require, define;                  name = '_@r' + (requireCounter += 1);              } -            if (index !== -1) { -                prefix = name.substring(0, index); -                name = name.substring(index + 1, name.length); -            } +            nameParts = splitPrefix(name); +            prefix = nameParts[0]; +            name = nameParts[1];              if (prefix) {                  prefix = normalize(prefix, parentName, applyMap); -                pluginModule = defined[prefix]; +                pluginModule = getOwn(defined, prefix);              }              //Account for relative paths if there is a base name. @@ -466,6 +440,15 @@ var requirejs, require, define;                  } else {                      //A regular module.                      normalizedName = normalize(name, parentName, applyMap); + +                    //Normalized name may be a plugin ID due to map config +                    //application in normalize. The map config values must +                    //already be normalized, so do not need to redo that part. +                    nameParts = splitPrefix(normalizedName); +                    prefix = nameParts[0]; +                    normalizedName = nameParts[1]; +                    isNormalized = true; +                      url = context.nameToUrl(normalizedName);                  }              } @@ -493,7 +476,7 @@ var requirejs, require, define;          function getModule(depMap) {              var id = depMap.id, -                mod = registry[id]; +                mod = getOwn(registry, id);              if (!mod) {                  mod = registry[id] = new context.Module(depMap); @@ -504,7 +487,7 @@ var requirejs, require, define;          function on(depMap, name, fn) {              var id = depMap.id, -                mod = registry[id]; +                mod = getOwn(registry, id);              if (hasProp(defined, id) &&                      (!mod || mod.defineEmitComplete)) { @@ -524,7 +507,7 @@ var requirejs, require, define;                  errback(err);              } else {                  each(ids, function (id) { -                    var mod = registry[id]; +                    var mod = getOwn(registry, id);                      if (mod) {                          //Set error on module, so it skips timeout checks.                          mod.error = err; @@ -557,148 +540,71 @@ var requirejs, require, define;              }          } -        /** -         * Helper function that creates a require function object to give to -         * modules that ask for it as a dependency. It needs to be specific -         * per module because of the implication of path mappings that may -         * need to be relative to the module name. -         */ -        function makeRequire(mod, enableBuildCallback, altRequire) { -            var relMap = mod && mod.map, -                modRequire = makeContextModuleFunc(altRequire || context.require, -                                                   relMap, -                                                   enableBuildCallback); - -            addRequireMethods(modRequire, context, relMap); -            modRequire.isBrowser = isBrowser; - -            return modRequire; -        } -          handlers = {              'require': function (mod) { -                return makeRequire(mod); +                if (mod.require) { +                    return mod.require; +                } else { +                    return (mod.require = context.makeRequire(mod.map)); +                }              },              'exports': function (mod) {                  mod.usingExports = true;                  if (mod.map.isDefine) { -                    return (mod.exports = defined[mod.map.id] = {}); +                    if (mod.exports) { +                        return mod.exports; +                    } else { +                        return (mod.exports = defined[mod.map.id] = {}); +                    }                  }              },              'module': function (mod) { -                return (mod.module = { -                    id: mod.map.id, -                    uri: mod.map.url, -                    config: function () { -                        return (config.config && config.config[mod.map.id]) || {}; -                    }, -                    exports: defined[mod.map.id] -                }); +                if (mod.module) { +                    return mod.module; +                } else { +                    return (mod.module = { +                        id: mod.map.id, +                        uri: mod.map.url, +                        config: function () { +                            return (config.config && getOwn(config.config, mod.map.id)) || {}; +                        }, +                        exports: defined[mod.map.id] +                    }); +                }              }          }; -        function removeWaiting(id) { +        function cleanRegistry(id) {              //Clean up machinery used for waiting modules.              delete registry[id]; - -            each(waitAry, function (mod, i) { -                if (mod.map.id === id) { -                    waitAry.splice(i, 1); -                    if (!mod.defined) { -                        context.waitCount -= 1; -                    } -                    return true; -                } -            });          } -        function findCycle(mod, traced, processed) { -            var id = mod.map.id, -                depArray = mod.depMaps, -                foundModule; - -            //Do not bother with unitialized modules or not yet enabled -            //modules. -            if (!mod.inited) { -                return; -            } - -            //Found the cycle. -            if (traced[id]) { -                return mod; -            } - -            traced[id] = true; - -            //Trace through the dependencies. -            each(depArray, function (depMap) { -                var depId = depMap.id, -                    depMod = registry[depId]; +        function breakCycle(mod, traced, processed) { +            var id = mod.map.id; -                if (!depMod || processed[depId] || -                        !depMod.inited || !depMod.enabled) { -                    return; -                } - -                return (foundModule = findCycle(depMod, traced, processed)); -            }); - -            processed[id] = true; - -            return foundModule; -        } - -        function forceExec(mod, traced, uninited) { -            var id = mod.map.id, -                depArray = mod.depMaps; - -            if (!mod.inited || !mod.map.isDefine) { -                return; -            } - -            if (traced[id]) { -                return defined[id]; -            } - -            traced[id] = mod; - -            each(depArray, function (depMap) { -                var depId = depMap.id, -                    depMod = registry[depId], -                    value; - -                if (handlers[depId]) { -                    return; -                } - -                if (depMod) { -                    if (!depMod.inited || !depMod.enabled) { -                        //Dependency is not inited, -                        //so this module cannot be -                        //given a forced value yet. -                        uninited[id] = true; -                        return; -                    } - -                    //Get the value for the current dependency -                    value = forceExec(depMod, traced, uninited); - -                    //Even with forcing it may not be done, -                    //in particular if the module is waiting -                    //on a plugin resource. -                    if (!uninited[depId]) { -                        mod.defineDepById(depId, value); +            if (mod.error) { +                mod.emit('error', mod.error); +            } else { +                traced[id] = true; +                each(mod.depMaps, function (depMap, i) { +                    var depId = depMap.id, +                        dep = getOwn(registry, depId); + +                    //Only force things that have not completed +                    //being defined, so still in the registry, +                    //and only if it has not been matched up +                    //in the module already. +                    if (dep && !mod.depMatched[i] && !processed[depId]) { +                        if (getOwn(traced, depId)) { +                            mod.defineDep(i, defined[depId]); +                            mod.check(); //pass false? +                        } else { +                            breakCycle(dep, traced, processed); +                        }                      } -                } -            }); - -            mod.check(true); - -            return defined[id]; -        } - -        function modCheck(mod) { -            mod.check(); +                }); +                processed[id] = true; +            }          }          function checkLoaded() { @@ -707,6 +613,7 @@ var requirejs, require, define;                  //It is possible to disable the wait interval by using waitSeconds of 0.                  expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),                  noLoads = [], +                reqCalls = [],                  stillLoading = false,                  needCycleCheck = true; @@ -727,6 +634,10 @@ var requirejs, require, define;                      return;                  } +                if (!map.isDefine) { +                    reqCalls.push(mod); +                } +                  if (!mod.error) {                      //If the module should be executed, and it has not                      //been inited and time is up, remember it. @@ -761,31 +672,9 @@ var requirejs, require, define;              //Not expired, check for a cycle.              if (needCycleCheck) { - -                each(waitAry, function (mod) { -                    if (mod.defined) { -                        return; -                    } - -                    var cycleMod = findCycle(mod, {}, {}), -                        traced = {}; - -                    if (cycleMod) { -                        forceExec(cycleMod, traced, {}); - -                        //traced modules may have been -                        //removed from the registry, but -                        //their listeners still need to -                        //be called. -                        eachProp(traced, modCheck); -                    } +                each(reqCalls, function (mod) { +                    breakCycle(mod, {}, {});                  }); - -                //Now that dependencies have -                //been satisfied, trigger the -                //completion check that then -                //notifies listeners. -                eachProp(registry, modCheck);              }              //If still waiting on loads, and the waiting load is something @@ -806,9 +695,9 @@ var requirejs, require, define;          }          Module = function (map) { -            this.events = undefEvents[map.id] || {}; +            this.events = getOwn(undefEvents, map.id) || {};              this.map = map; -            this.shim = config.shim[map.id]; +            this.shim = getOwn(config.shim, map.id);              this.depExports = [];              this.depMaps = [];              this.depMatched = []; @@ -851,7 +740,6 @@ var requirejs, require, define;                  //doing a direct modification of the depMaps array                  //would affect that config.                  this.depMaps = depMaps && depMaps.slice(0); -                this.depMaps.rjsSkipMap = depMaps.rjsSkipMap;                  this.errback = errback; @@ -873,20 +761,6 @@ var requirejs, require, define;                  }              }, -            defineDepById: function (id, depExports) { -                var i; - -                //Find the index for this dependency. -                each(this.depMaps, function (map, index) { -                    if (map.id === id) { -                        i = index; -                        return true; -                    } -                }); - -                return this.defineDep(i, depExports); -            }, -              defineDep: function (i, depExports) {                  //Because of cycles, defined callback for a given                  //export can be called more than once. @@ -910,7 +784,9 @@ var requirejs, require, define;                  //If the manager is for a plugin managed resource,                  //ask the plugin to load it now.                  if (this.shim) { -                    makeRequire(this, true)(this.shim.deps || [], bind(this, function () { +                    context.makeRequire(this.map, { +                        enableBuildCallback: true +                    })(this.shim.deps || [], bind(this, function () {                          return map.prefix ? this.callPlugin() : this.load();                      }));                  } else { @@ -931,11 +807,9 @@ var requirejs, require, define;              /**               * Checks is the module is ready to define itself, and if so, -             * define it. If the silent argument is true, then it will just -             * define, but not notify listeners, and not ask for a context-wide -             * check of all loaded modules. That is useful for cycle breaking. +             * define it.               */ -            check: function (silent) { +            check: function () {                  if (!this.enabled || this.enabling) {                      return;                  } @@ -1013,11 +887,6 @@ var requirejs, require, define;                          delete registry[id];                          this.defined = true; -                        context.waitCount -= 1; -                        if (context.waitCount === 0) { -                            //Clear the wait array used for cycles. -                            waitAry = []; -                        }                      }                      //Finished the define stage. Allow calling check again @@ -1025,25 +894,33 @@ var requirejs, require, define;                      //cycle.                      this.defining = false; -                    if (!silent) { -                        if (this.defined && !this.defineEmitted) { -                            this.defineEmitted = true; -                            this.emit('defined', this.exports); -                            this.defineEmitComplete = true; -                        } +                    if (this.defined && !this.defineEmitted) { +                        this.defineEmitted = true; +                        this.emit('defined', this.exports); +                        this.defineEmitComplete = true;                      } +                  }              },              callPlugin: function () {                  var map = this.map,                      id = map.id, -                    pluginMap = makeModuleMap(map.prefix, null, false, true); +                    //Map already normalized the prefix. +                    pluginMap = makeModuleMap(map.prefix); + +                //Mark this as a dependency for this plugin, so it +                //can be traced for cycles. +                this.depMaps.push(pluginMap);                  on(pluginMap, 'defined', bind(this, function (plugin) {                      var load, normalizedMap, normalizedMod,                          name = this.map.name, -                        parentName = this.map.parentMap ? this.map.parentMap.name : null; +                        parentName = this.map.parentMap ? this.map.parentMap.name : null, +                        localRequire = context.makeRequire(map.parentMap, { +                            enableBuildCallback: true, +                            skipMap: true +                        });                      //If current map is not normalized, wait for that                      //normalized name to load instead of continuing. @@ -1055,10 +932,10 @@ var requirejs, require, define;                              }) || '';                          } +                        //prefix and name should already be normalized, no need +                        //for applying map config again either.                          normalizedMap = makeModuleMap(map.prefix + '!' + name, -                                                      this.map.parentMap, -                                                      false, -                                                      true); +                                                      this.map.parentMap);                          on(normalizedMap,                              'defined', bind(this, function (value) {                                  this.init([], function () { return value; }, null, { @@ -1066,8 +943,13 @@ var requirejs, require, define;                                      ignore: true                                  });                              })); -                        normalizedMod = registry[normalizedMap.id]; + +                        normalizedMod = getOwn(registry, normalizedMap.id);                          if (normalizedMod) { +                            //Mark this as a dependency for this plugin, so it +                            //can be traced for cycles. +                            this.depMaps.push(normalizedMap); +                              if (this.events.error) {                                  normalizedMod.on('error', bind(this, function (err) {                                      this.emit('error', err); @@ -1094,7 +976,7 @@ var requirejs, require, define;                          //since they will never be resolved otherwise now.                          eachProp(registry, function (mod) {                              if (mod.map.id.indexOf(id + '_unnormalized') === 0) { -                                removeWaiting(mod.map.id); +                                cleanRegistry(mod.map.id);                              }                          }); @@ -1103,9 +985,19 @@ var requirejs, require, define;                      //Allow plugins to load other code without having to know the                      //context or how to 'complete' the load. -                    load.fromText = function (moduleName, text) { +                    load.fromText = bind(this, function (text, textAlt) {                          /*jslint evil: true */ -                        var hasInteractive = useInteractive; +                        var moduleName = map.name, +                            moduleMap = makeModuleMap(moduleName), +                            hasInteractive = useInteractive; + +                        //As of 2.1.0, support just passing the text, to reinforce +                        //fromText only being called once per resource. Still +                        //support old style of passing moduleName but discard +                        //that moduleName in favor of the internal ref. +                        if (textAlt) { +                            text = textAlt; +                        }                          //Turn off interactive script matching for IE for any define                          //calls in the text, then turn it back on at the end. @@ -1115,25 +1007,40 @@ var requirejs, require, define;                          //Prime the system by creating a module instance for                          //it. -                        getModule(makeModuleMap(moduleName)); +                        getModule(moduleMap); + +                        //Transfer any config to this other module. +                        if (hasProp(config.config, id)) { +                            config.config[moduleName] = config.config[id]; +                        } -                        req.exec(text); +                        try { +                            req.exec(text); +                        } catch (e) { +                            throw new Error('fromText eval for ' + moduleName + +                                            ' failed: ' + e); +                        }                          if (hasInteractive) {                              useInteractive = true;                          } +                        //Mark this as a dependency for the plugin +                        //resource +                        this.depMaps.push(moduleMap); +                          //Support anonymous modules.                          context.completeLoad(moduleName); -                    }; + +                        //Bind the value of that module to the value for this +                        //resource ID. +                        localRequire([moduleName], load); +                    });                      //Use parentName here since the plugin's name is not reliable,                      //could be some weird string with no path that actually wants to                      //reference the parentName's path. -                    plugin.load(map.name, makeRequire(map.parentMap, true, function (deps, cb, er) { -                        deps.rjsSkipMap = true; -                        return context.require(deps, cb, er); -                    }), load, config); +                    plugin.load(map.name, localRequire, load, config);                  }));                  context.enable(pluginMap, this); @@ -1143,12 +1050,6 @@ var requirejs, require, define;              enable: function () {                  this.enabled = true; -                if (!this.waitPushed) { -                    waitAry.push(this); -                    context.waitCount += 1; -                    this.waitPushed = true; -                } -                  //Set flag mentioning that the module is enabling,                  //so that immediate calls to the defined callbacks                  //for dependencies do not trigger inadvertent load @@ -1165,10 +1066,10 @@ var requirejs, require, define;                          depMap = makeModuleMap(depMap,                                                 (this.map.isDefine ? this.map : this.map.parentMap),                                                 false, -                                               !this.depMaps.rjsSkipMap); +                                               !this.skipMap);                          this.depMaps[i] = depMap; -                        handler = handlers[depMap.id]; +                        handler = getOwn(handlers, depMap.id);                          if (handler) {                              this.depExports[i] = handler(this); @@ -1193,7 +1094,7 @@ var requirejs, require, define;                      //Skip special modules like 'require', 'exports', 'module'                      //Also, don't call enable if it is already enabled,                      //important in circular dependency cases. -                    if (!handlers[id] && mod && !mod.enabled) { +                    if (!hasProp(handlers, id) && mod && !mod.enabled) {                          context.enable(depMap, this);                      }                  })); @@ -1201,7 +1102,7 @@ var requirejs, require, define;                  //Enable each plugin that is used in                  //a dependency                  eachProp(this.pluginMaps, bind(this, function (pluginMap) { -                    var mod = registry[pluginMap.id]; +                    var mod = getOwn(registry, pluginMap.id);                      if (mod && !mod.enabled) {                          context.enable(pluginMap, this);                      } @@ -1227,14 +1128,17 @@ var requirejs, require, define;                  if (name === 'error') {                      //Now that the error handler was triggered, remove                      //the listeners, since this broken Module instance -                    //can stay around for a while in the registry/waitAry. +                    //can stay around for a while in the registry.                      delete this.events[name];                  }              }          };          function callGetModule(args) { -            getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); +            //Skip modules already defined. +            if (!hasProp(defined, args[0])) { +                getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); +            }          }          function removeListener(node, func, name, ieName) { @@ -1274,16 +1178,35 @@ var requirejs, require, define;              };          } -        return (context = { +        function intakeDefines() { +            var args; + +            //Any defined modules in the global queue, intake them now. +            takeGlobalQueue(); + +            //Make sure any remaining defQueue items get properly processed. +            while (defQueue.length) { +                args = defQueue.shift(); +                if (args[0] === null) { +                    return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); +                } else { +                    //args are id, deps, factory. Should be normalized by the +                    //define() function. +                    callGetModule(args); +                } +            } +        } + +        context = {              config: config,              contextName: contextName,              registry: registry,              defined: defined,              urlFetched: urlFetched, -            waitCount: 0,              defQueue: defQueue,              Module: Module,              makeModuleMap: makeModuleMap, +            nextTick: req.nextTick,              /**               * Set a configuration for the context. @@ -1301,20 +1224,23 @@ var requirejs, require, define;                  //they are additive.                  var pkgs = config.pkgs,                      shim = config.shim, -                    paths = config.paths, -                    map = config.map; - -                //Mix in the config values, favoring the new values over -                //existing ones in context.config. -                mixin(config, cfg, true); - -                //Merge paths. -                config.paths = mixin(paths, cfg.paths, true); +                    objs = { +                        paths: true, +                        config: true, +                        map: true +                    }; -                //Merge map -                if (cfg.map) { -                    config.map = mixin(map || {}, cfg.map, true, true); -                } +                eachProp(cfg, function (value, prop) { +                    if (objs[prop]) { +                        if (prop === 'map') { +                            mixin(config[prop], value, true, true); +                        } else { +                            mixin(config[prop], value, true); +                        } +                    } else { +                        config[prop] = value; +                    } +                });                  //Merge shim                  if (cfg.shim) { @@ -1325,8 +1251,8 @@ var requirejs, require, define;                                  deps: value                              };                          } -                        if (value.exports && !value.exports.__buildReady) { -                            value.exports = context.makeShimExports(value.exports); +                        if ((value.exports || value.init) && !value.exportsFn) { +                            value.exportsFn = context.makeShimExports(value);                          }                          shim[id] = value;                      }); @@ -1381,125 +1307,144 @@ var requirejs, require, define;                  }              }, -            makeShimExports: function (exports) { -                var func; -                if (typeof exports === 'string') { -                    func = function () { -                        return getGlobal(exports); -                    }; -                    //Save the exports for use in nodefine checking. -                    func.exports = exports; -                    return func; -                } else { -                    return function () { -                        return exports.apply(global, arguments); -                    }; +            makeShimExports: function (value) { +                function fn() { +                    var ret; +                    if (value.init) { +                        ret = value.init.apply(global, arguments); +                    } +                    return ret || (value.exports && getGlobal(value.exports));                  } +                return fn;              }, -            requireDefined: function (id, relMap) { -                return hasProp(defined, makeModuleMap(id, relMap, false, true).id); -            }, +            makeRequire: function (relMap, options) { +                options = options || {}; -            requireSpecified: function (id, relMap) { -                id = makeModuleMap(id, relMap, false, true).id; -                return hasProp(defined, id) || hasProp(registry, id); -            }, +                function localRequire(deps, callback, errback) { +                    var id, map, requireMod; -            require: function (deps, callback, errback, relMap) { -                var moduleName, id, map, requireMod, args; -                if (typeof deps === 'string') { -                    if (isFunction(callback)) { -                        //Invalid call -                        return onError(makeError('requireargs', 'Invalid require call'), errback); +                    if (options.enableBuildCallback && callback && isFunction(callback)) { +                        callback.__requireJsBuild = true;                      } -                    //Synchronous access to one module. If require.get is -                    //available (as in the Node adapter), prefer that. -                    //In this case deps is the moduleName and callback is -                    //the relMap -                    if (req.get) { -                        return req.get(context, deps, callback); -                    } +                    if (typeof deps === 'string') { +                        if (isFunction(callback)) { +                            //Invalid call +                            return onError(makeError('requireargs', 'Invalid require call'), errback); +                        } -                    //Just return the module wanted. In this scenario, the -                    //second arg (if passed) is just the relMap. -                    moduleName = deps; -                    relMap = callback; +                        //If require|exports|module are requested, get the +                        //value for them from the special handlers. Caveat: +                        //this only works while module is being defined. +                        if (relMap && hasProp(handlers, deps)) { +                            return handlers[deps](registry[relMap.id]); +                        } -                    //Normalize module name, if it contains . or .. -                    map = makeModuleMap(moduleName, relMap, false, true); -                    id = map.id; +                        //Synchronous access to one module. If require.get is +                        //available (as in the Node adapter), prefer that. +                        if (req.get) { +                            return req.get(context, deps, relMap); +                        } + +                        //Normalize module name, if it contains . or .. +                        map = makeModuleMap(deps, relMap, false, true); +                        id = map.id; -                    if (!hasProp(defined, id)) { -                        return onError(makeError('notloaded', 'Module name "' + -                                    id + -                                    '" has not been loaded yet for context: ' + -                                    contextName)); +                        if (!hasProp(defined, id)) { +                            return onError(makeError('notloaded', 'Module name "' + +                                        id + +                                        '" has not been loaded yet for context: ' + +                                        contextName + +                                        (relMap ? '' : '. Use require([])'))); +                        } +                        return defined[id];                      } -                    return defined[id]; -                } -                //Callback require. Normalize args. if callback or errback is -                //not a function, it means it is a relMap. Test errback first. -                if (errback && !isFunction(errback)) { -                    relMap = errback; -                    errback = undefined; -                } -                if (callback && !isFunction(callback)) { -                    relMap = callback; -                    callback = undefined; -                } +                    //Grab defines waiting in the global queue. +                    intakeDefines(); -                //Any defined modules in the global queue, intake them now. -                takeGlobalQueue(); +                    //Mark all the dependencies as needing to be loaded. +                    context.nextTick(function () { +                        //Some defines could have been added since the +                        //require call, collect them. +                        intakeDefines(); -                //Make sure any remaining defQueue items get properly processed. -                while (defQueue.length) { -                    args = defQueue.shift(); -                    if (args[0] === null) { -                        return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); -                    } else { -                        //args are id, deps, factory. Should be normalized by the -                        //define() function. -                        callGetModule(args); -                    } -                } +                        requireMod = getModule(makeModuleMap(null, relMap)); -                //Mark all the dependencies as needing to be loaded. -                requireMod = getModule(makeModuleMap(null, relMap)); +                        //Store if map config should be applied to this require +                        //call for dependencies. +                        requireMod.skipMap = options.skipMap; -                requireMod.init(deps, callback, errback, { -                    enabled: true -                }); +                        requireMod.init(deps, callback, errback, { +                            enabled: true +                        }); -                checkLoaded(); +                        checkLoaded(); +                    }); -                return context.require; -            }, +                    return localRequire; +                } -            undef: function (id) { -                //Bind any waiting define() calls to this context, -                //fix for #408 -                takeGlobalQueue(); +                mixin(localRequire, { +                    isBrowser: isBrowser, + +                    /** +                     * Converts a module name + .extension into an URL path. +                     * *Requires* the use of a module name. It does not support using +                     * plain URLs like nameToUrl. +                     */ +                    toUrl: function (moduleNamePlusExt) { +                        var index = moduleNamePlusExt.lastIndexOf('.'), +                            ext = null; + +                        if (index !== -1) { +                            ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); +                            moduleNamePlusExt = moduleNamePlusExt.substring(0, index); +                        } -                var map = makeModuleMap(id, null, true), -                    mod = registry[id]; +                        return context.nameToUrl(normalize(moduleNamePlusExt, +                                                relMap && relMap.id, true), ext); +                    }, -                delete defined[id]; -                delete urlFetched[map.url]; -                delete undefEvents[id]; +                    defined: function (id) { +                        return hasProp(defined, makeModuleMap(id, relMap, false, true).id); +                    }, -                if (mod) { -                    //Hold on to listeners in case the -                    //module will be attempted to be reloaded -                    //using a different config. -                    if (mod.events.defined) { -                        undefEvents[id] = mod.events; +                    specified: function (id) { +                        id = makeModuleMap(id, relMap, false, true).id; +                        return hasProp(defined, id) || hasProp(registry, id);                      } +                }); + +                //Only allow undef on top level require calls +                if (!relMap) { +                    localRequire.undef = function (id) { +                        //Bind any waiting define() calls to this context, +                        //fix for #408 +                        takeGlobalQueue(); + +                        var map = makeModuleMap(id, relMap, true), +                            mod = getOwn(registry, id); + +                        delete defined[id]; +                        delete urlFetched[map.url]; +                        delete undefEvents[id]; + +                        if (mod) { +                            //Hold on to listeners in case the +                            //module will be attempted to be reloaded +                            //using a different config. +                            if (mod.events.defined) { +                                undefEvents[id] = mod.events; +                            } -                    removeWaiting(id); +                            cleanRegistry(id); +                        } +                    };                  } + +                return localRequire;              },              /** @@ -1508,7 +1453,7 @@ var requirejs, require, define;               * used by the optimizer.               */              enable: function (depMap, parent) { -                var mod = registry[depMap.id]; +                var mod = getOwn(registry, depMap.id);                  if (mod) {                      getModule(depMap).enable();                  } @@ -1522,8 +1467,8 @@ var requirejs, require, define;               */              completeLoad: function (moduleName) {                  var found, args, mod, -                    shim = config.shim[moduleName] || {}, -                    shExports = shim.exports && shim.exports.exports; +                    shim = getOwn(config.shim, moduleName) || {}, +                    shExports = shim.exports;                  takeGlobalQueue(); @@ -1548,9 +1493,9 @@ var requirejs, require, define;                  //Do this after the cycle of callGetModule in case the result                  //of those calls/init calls changes the registry. -                mod = registry[moduleName]; +                mod = getOwn(registry, moduleName); -                if (!found && !defined[moduleName] && mod && !mod.inited) { +                if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {                      if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {                          if (hasPathFallback(moduleName)) {                              return; @@ -1563,7 +1508,7 @@ var requirejs, require, define;                      } else {                          //A script that does not call define(), so just simulate                          //the call for it. -                        callGetModule([moduleName, (shim.deps || []), shim.exports]); +                        callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);                      }                  } @@ -1571,24 +1516,6 @@ var requirejs, require, define;              },              /** -             * Converts a module name + .extension into an URL path. -             * *Requires* the use of a module name. It does not support using -             * plain URLs like nameToUrl. -             */ -            toUrl: function (moduleNamePlusExt, relModuleMap) { -                var index = moduleNamePlusExt.lastIndexOf('.'), -                    ext = null; - -                if (index !== -1) { -                    ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); -                    moduleNamePlusExt = moduleNamePlusExt.substring(0, index); -                } - -                return context.nameToUrl(normalize(moduleNamePlusExt, relModuleMap && relModuleMap.id, true), -                                         ext); -            }, - -            /**               * Converts a module name to a file path. Supports cases where               * moduleName may actually be just an URL.               * Note that it **does not** call normalize on the moduleName, @@ -1619,8 +1546,8 @@ var requirejs, require, define;                      //and work up from it.                      for (i = syms.length; i > 0; i -= 1) {                          parentModule = syms.slice(0, i).join('/'); -                        pkg = pkgs[parentModule]; -                        parentPath = paths[parentModule]; +                        pkg = getOwn(pkgs, parentModule); +                        parentPath = getOwn(paths, parentModule);                          if (parentPath) {                              //If an array, it means there are a few choices,                              //Choose the one that is desired @@ -1701,7 +1628,10 @@ var requirejs, require, define;                      return onError(makeError('scripterror', 'Script error', evt, [data.id]));                  }              } -        }); +        }; + +        context.require = context.makeRequire(); +        return context;      }      /** @@ -1742,7 +1672,7 @@ var requirejs, require, define;              contextName = config.context;          } -        context = contexts[contextName]; +        context = getOwn(contexts, contextName);          if (!context) {              context = contexts[contextName] = req.s.newContext(contextName);          } @@ -1763,6 +1693,16 @@ var requirejs, require, define;      };      /** +     * Execute something after the current tick +     * of the event loop. Override for other envs +     * that have a better solution than setTimeout. +     * @param  {Function} fn function to execute later. +     */ +    req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { +        setTimeout(fn, 4); +    } : function (fn) { fn(); }; + +    /**       * Export require as a global, but only if it does not already exist.       */      if (!require) { @@ -1782,9 +1722,21 @@ var requirejs, require, define;      //Create default context.      req({}); -    //Exports some context-sensitive methods on global require, using -    //default context if no context specified. -    addRequireMethods(req); +    //Exports some context-sensitive methods on global require. +    each([ +        'toUrl', +        'undef', +        'defined', +        'specified' +    ], function (prop) { +        //Reference from contexts instead of early binding to default context, +        //so that during builds, the latest instance of the default context +        //with its config gets used. +        req[prop] = function () { +            var ctx = contexts[defContextName]; +            return ctx.require[prop].apply(ctx, arguments); +        }; +    });      if (isBrowser) {          head = s.head = document.getElementsByTagName('head')[0]; @@ -1962,7 +1914,7 @@ var requirejs, require, define;      define = function (name, deps, callback) {          var node, context; -        //Allow for anonymous functions +        //Allow for anonymous modules          if (typeof name !== 'string') {              //Adjust args appropriately              callback = deps; diff --git a/module/web/static/js/views/packageView.js b/module/web/static/js/views/packageView.js index a0de18827..5bc23a7b3 100644 --- a/module/web/static/js/views/packageView.js +++ b/module/web/static/js/views/packageView.js @@ -38,6 +38,7 @@ define(['jquery', 'views/abstract/itemView', 'underscore', 'views/fileView', 'ut               ];              var pie = this.$('.package-graph');              pie.peity('pie'); +            this.$('canvas').addClass('pull-right');              if (this.model.isLoaded()) {                  var ul = $('<ul></ul>'); diff --git a/module/web/templates/default/base.html b/module/web/templates/default/base.html index 2b50cc2ad..88eee8f5b 100644 --- a/module/web/templates/default/base.html +++ b/module/web/templates/default/base.html @@ -16,7 +16,7 @@      {% endblock %}
      <script src="/static/js/libs/less-1.3.0.min.js" type="text/javascript"></script>
 -    <script type="text/javascript" data-main="static/js/default" src="/static/js/libs/require-2.0.6.js"></script>
 +    <script type="text/javascript" data-main="static/js/default" src="/static/js/libs/require-2.1.2.js"></script>
      <script>
          require(['default'], function(App) {
              App.init();
 diff --git a/module/web/templates/mobile/base.html b/module/web/templates/mobile/base.html index ef2f06ffe..c40d1449b 100644 --- a/module/web/templates/mobile/base.html +++ b/module/web/templates/mobile/base.html @@ -17,7 +17,7 @@          <script src="my.js"></script> -        <script type="text/javascript" data-main="static/js/default" src="/static/js/libs/require-2.0.6.js"></script> +        <script type="text/javascript" data-main="static/js/default" src="/static/js/libs/require-2.1.2.js"></script>          <script>              require(['mobile'], function(App) {                  App.init(); | 
