Skip to content

Instantly share code, notes, and snippets.

@groks
Created November 27, 2010 13:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save groks/717904 to your computer and use it in GitHub Desktop.
Save groks/717904 to your computer and use it in GitHub Desktop.
changes in the google appengine 1.4.0 pre-release
Only in local/google_appengine/demos: taskqueue_examples
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/apiproxy_stub_map.py google-appengine-1.4.0-prerelease/google/appengine/api/apiproxy_stub_map.py
--- local/google_appengine/google/appengine/api/apiproxy_stub_map.py 2010-10-14 21:40:44.027837653 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/apiproxy_stub_map.py 2010-11-18 21:44:32.000000000 +0000
@@ -367,10 +367,12 @@ def __init__(self, service, deadline=N
self.__rpc = CreateRPC(service, stubmap)
self.__rpc.deadline = deadline
self.__rpc.callback = self.__internal_callback
self.callback = callback
+ self.__class__.__local.may_interrupt_wait = False
+
def __internal_callback(self):
"""This is the callback set on the low-level RPC object.
It sets a flag on the current object indicating that the high-level
callback should now be called. If interrupts are enabled, it also
@@ -586,13 +588,13 @@ def wait_any(cls, rpcs):
return None
try:
cls.__local.may_interrupt_wait = True
try:
running.__rpc.Wait()
- except apiproxy_errors.InterruptedError:
- running.__rpc._RPC__exception = None
- running.__rpc._RPC__traceback = None
+ except apiproxy_errors.InterruptedError, err:
+ err.rpc._RPC__exception = None
+ err.rpc._RPC__traceback = None
finally:
cls.__local.may_interrupt_wait = False
finished, runnning = cls.__check_one(rpcs)
return finished
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/appinfo.py google-appengine-1.4.0-prerelease/google/appengine/api/appinfo.py
--- local/google_appengine/google/appengine/api/appinfo.py 2010-10-14 21:40:44.025837374 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/appinfo.py 2010-11-18 21:44:32.000000000 +0000
@@ -39,11 +38,11 @@
_FILES_REGEX = r'(?!\^).*(?!\$).'
_DELTA_REGEX = r'([0-9]+)([DdHhMm]|[sS]?)'
_EXPIRATION_REGEX = r'\s*(%s)(\s+%s)*\s*' % (_DELTA_REGEX, _DELTA_REGEX)
-_SERVICE_RE_STRING = r'(mail|xmpp_message|rest|startup)'
+_SERVICE_RE_STRING = r'(mail|xmpp_message|xmpp_subscribe|xmpp_presence|rest|warmup)'
_PAGE_NAME_REGEX = r'^.+$'
_EXPIRATION_CONVERSIONS = {
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/channel/channel.py google-appengine-1.4.0-prerelease/google/appengine/api/channel/channel.py
--- local/google_appengine/google/appengine/api/channel/channel.py 2010-07-01 22:17:27.632779713 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/channel/channel.py 2010-11-18 21:44:32.000000000 +0000
@@ -30,33 +30,23 @@
from google.appengine.api import api_base_pb
from google.appengine.api import apiproxy_stub_map
from google.appengine.api.channel import channel_service_pb
from google.appengine.runtime import apiproxy_errors
-MAX_DURATION = 60 * 60 * 4
-
-MAX_SIMULTANEOUS_CONNECTIONS = 10
-
class Error(Exception):
"""Base error class for this module."""
class InvalidChannelKeyError(Error):
"""Error that indicates a bad channel id."""
-class InvalidChannelKeyError(Error):
- """Error that indicates a bad channel key."""
class InvalidMessageError(Error):
"""Error that indicates a message is malformed."""
-class ChannelTimeoutError(Error):
- """Error that indicates the given channel has timed out."""
-
-
def _ToChannelError(error):
"""Translate an application error to a channel Error, if possible.
Args:
error: An ApplicationError to translate.
@@ -68,12 +58,10 @@ def _ToChannelError(error):
error_map = {
channel_service_pb.ChannelServiceError.INVALID_CHANNEL_KEY:
InvalidChannelKeyError,
channel_service_pb.ChannelServiceError.BAD_MESSAGE:
InvalidMessageError,
- channel_service_pb.ChannelServiceError.CHANNEL_TIMEOUT:
- ChannelTimeoutError
}
if error.application_error in error_map:
return error_map[error.application_error](error.error_detail)
else:
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/channel/channel_service_pb.py google-appengine-1.4.0-prerelease/google/appengine/api/channel/channel_service_pb.py
--- local/google_appengine/google/appengine/api/channel/channel_service_pb.py 2010-07-01 22:17:27.633779922 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/channel/channel_service_pb.py 2010-11-18 21:44:32.000000000 +0000
@@ -28,18 +28,16 @@
OK = 0
INTERNAL_ERROR = 1
INVALID_CHANNEL_KEY = 2
BAD_MESSAGE = 3
- CHANNEL_TIMEOUT = 4
_ErrorCode_NAMES = {
0: "OK",
1: "INTERNAL_ERROR",
2: "INVALID_CHANNEL_KEY",
3: "BAD_MESSAGE",
- 4: "CHANNEL_TIMEOUT",
}
def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
ErrorCode_Name = classmethod(ErrorCode_Name)
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/datastore_file_stub.py google-appengine-1.4.0-prerelease/google/appengine/api/datastore_file_stub.py
--- local/google_appengine/google/appengine/api/datastore_file_stub.py 2010-10-14 21:40:44.036837863 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/datastore_file_stub.py 2010-11-18 21:44:32.000000000 +0000
@@ -40,58 +40,43 @@
import os
import struct
import sys
import tempfile
import threading
-import warnings
import cPickle as pickle
from google.appengine.api import api_base_pb
from google.appengine.api import apiproxy_stub
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
from google.appengine.api import users
+from google.appengine.api.taskqueue import taskqueue_service_pb
from google.appengine.datastore import datastore_pb
from google.appengine.datastore import datastore_index
from google.appengine.datastore import datastore_stub_util
from google.appengine.runtime import apiproxy_errors
from google.net.proto import ProtocolBuffer
from google.appengine.datastore import entity_pb
-try:
- __import__('google.appengine.api.labs.taskqueue.taskqueue_service_pb')
- taskqueue_service_pb = sys.modules.get(
- 'google.appengine.api.labs.taskqueue.taskqueue_service_pb')
-except ImportError:
- from google.appengine.api.taskqueue import taskqueue_service_pb
entity_pb.Reference.__hash__ = lambda self: hash(self.Encode())
datastore_pb.Query.__hash__ = lambda self: hash(self.Encode())
datastore_pb.Transaction.__hash__ = lambda self: hash(self.Encode())
-_MAXIMUM_RESULTS = 1000
-
-
-_MAX_QUERY_OFFSET = 1000
-
-
_MAX_QUERY_COMPONENTS = 100
_BATCH_SIZE = 20
_MAX_ACTIONS_PER_TXN = 5
-_CURSOR_CONCAT_STR = '!CURSOR!'
-
-
class _StoredEntity(object):
"""Simple wrapper around an entity stored by the stub.
Public properties:
protobuf: Native protobuf Python object, entity_pb.EntityProto.
@@ -107,286 +92,66 @@ def __init__(self, entity):
"""
self.protobuf = entity
self.encoded_protobuf = entity.Encode()
- self.native = datastore.Entity._FromPb(entity)
-
-
-class _Cursor(object):
- """A query cursor.
-
- Public properties:
- cursor: the integer cursor
- count: the original total number of results
- keys_only: whether the query is keys_only
- app: the app for which this cursor was created
-
- Class attributes:
- _next_cursor: the next cursor to allocate
- _next_cursor_lock: protects _next_cursor
- """
- _next_cursor = 1
- _next_cursor_lock = threading.Lock()
-
- def __init__(self, query, results, order_compare_entities):
- """Constructor.
-
- Args:
- query: the query request proto
- # the query results, in order, such that results[self.offset+1] is
- # the next result
- results: list of datastore.Entity
- order_compare_entities: a __cmp__ function for datastore.Entity that
- follows sort order as specified by the query
- """
-
- if query.has_compiled_cursor() and query.compiled_cursor().position_list():
- (self.__last_result, inclusive) = self._DecodeCompiledCursor(
- query, query.compiled_cursor())
- start_cursor_position = _Cursor._GetCursorOffset(results,
- self.__last_result,
- inclusive,
- order_compare_entities)
- else:
- self.__last_result = None
- start_cursor_position = 0
-
- if query.has_end_compiled_cursor():
- (end_cursor_entity, inclusive) = self._DecodeCompiledCursor(
- query, query.end_compiled_cursor())
- end_cursor_position = _Cursor._GetCursorOffset(results,
- end_cursor_entity,
- inclusive,
- order_compare_entities)
- else:
- end_cursor_position = len(results)
-
- results = results[start_cursor_position:end_cursor_position]
-
- if query.has_limit():
- limit = query.limit()
- if query.offset():
- limit += query.offset()
- if limit > 0 and limit < len(results):
- results = results[:limit]
-
- self.__results = results
- self.__query = query
- self.__offset = 0
-
- self.app = query.app()
- self.keys_only = query.keys_only()
- self.count = len(self.__results)
- self.cursor = self._AcquireCursorID()
-
- def _AcquireCursorID(self):
- """Acquires the next cursor id in a thread safe manner.
- """
- self._next_cursor_lock.acquire()
- try:
- cursor_id = _Cursor._next_cursor
- _Cursor._next_cursor += 1
- finally:
- self._next_cursor_lock.release()
- return cursor_id
-
- @staticmethod
- def _GetCursorOffset(results, cursor_entity, inclusive, compare):
- """Converts a cursor entity into a offset into the result set even if the
- cursor_entity no longer exists.
-
- Args:
- cursor_entity: the decoded datastore.Entity from the compiled query
- inclusive: boolean that specifies if to offset past the cursor_entity
- compare: a function that takes two datastore.Entity and compares them
- Returns:
- the integer offset
- """
- lo = 0
- hi = len(results)
- if inclusive:
- while lo < hi:
- mid = (lo + hi) // 2
- if compare(results[mid], cursor_entity) < 0:
- lo = mid + 1
- else:
- hi = mid
- else:
- while lo < hi:
- mid = (lo + hi) // 2
- if compare(cursor_entity, results[mid]) < 0:
- hi = mid
- else:
- lo = mid + 1
- return lo
-
- def _ValidateQuery(self, query, query_info):
- """Ensure that the given query matches the query_info.
-
- Args:
- query: datastore_pb.Query instance we are chacking
- query_info: datastore_pb.Query instance we want to match
-
- Raises BadRequestError on failure.
- """
- error_msg = 'Cursor does not match query: %s'
- exc = datastore_errors.BadRequestError
- if query_info.filter_list() != query.filter_list():
- raise exc(error_msg % 'filters do not match')
- if query_info.order_list() != query.order_list():
- raise exc(error_msg % 'orders do not match')
+ self.native = datastore.Entity._FromPb(entity,
+ validate_reserved_properties=False)
- for attr in ('ancestor', 'kind', 'name_space', 'search_query'):
- query_info_has_attr = getattr(query_info, 'has_%s' % attr)
- query_info_attr = getattr(query_info, attr)
- query_has_attr = getattr(query, 'has_%s' % attr)
- query_attr = getattr(query, attr)
- if query_info_has_attr():
- if not query_has_attr() or query_info_attr() != query_attr():
- raise exc(error_msg % ('%s does not match' % attr))
- elif query_has_attr():
- raise exc(error_msg % ('%s does not match' % attr))
- def _MinimalQueryInfo(self, query):
- """Extract the minimal set of information for query matching.
+class KindPseudoKind(object):
+ """Pseudo-kind for schema queries.
- Args:
- query: datastore_pb.Query instance from which to extract info.
+ Provides a Query method to perform the actual query.
- Returns:
- datastore_pb.Query instance suitable for matching against when
- validating cursors.
+ Public properties:
+ name: the pseudo-kind name
"""
- query_info = datastore_pb.Query()
- query_info.set_app(query.app())
-
- for filter in query.filter_list():
- query_info.filter_list().append(filter)
- for order in query.order_list():
- query_info.order_list().append(order)
-
- if query.has_ancestor():
- query_info.mutable_ancestor().CopyFrom(query.ancestor())
-
- for attr in ('kind', 'name_space', 'search_query'):
- query_has_attr = getattr(query, 'has_%s' % attr)
- query_attr = getattr(query, attr)
- query_info_set_attr = getattr(query_info, 'set_%s' % attr)
- if query_has_attr():
- query_info_set_attr(query_attr())
-
- return query_info
+ name = '__kind__'
- def _MinimalEntityInfo(self, entity_proto, query):
- """Extract the minimal set of information that preserves entity order.
+ def Query(self, entities, query, filters, orders):
+ """Perform a query on this pseudo-kind.
Args:
- entity_proto: datastore_pb.EntityProto instance from which to extract
- information
- query: datastore_pb.Query instance for which ordering must be preserved.
-
- Returns:
- datastore_pb.EntityProto instance suitable for matching against a list of
- results when finding cursor positions.
- """
- entity_info = datastore_pb.EntityProto();
- order_names = [o.property() for o in query.order_list()]
- entity_info.mutable_key().MergeFrom(entity_proto.key())
- entity_info.mutable_entity_group().MergeFrom(entity_proto.entity_group())
- for prop in entity_proto.property_list():
- if prop.name() in order_names:
- entity_info.add_property().MergeFrom(prop)
- return entity_info;
-
- def _DecodeCompiledCursor(self, query, compiled_cursor):
- """Converts a compiled_cursor into a cursor_entity.
+ entities: all the app's entities.
+ query: the original datastore_pb.Query.
+ filters: the filters from query.
+ orders: the orders from query.
Returns:
- (cursor_entity, inclusive): a datastore.Entity and if it should be
- included in the result set.
- """
- assert len(compiled_cursor.position_list()) == 1
-
- position = compiled_cursor.position(0)
- entity_pb = datastore_pb.EntityProto()
- (query_info_encoded, entity_encoded) = position.start_key().split(
- _CURSOR_CONCAT_STR, 1)
- query_info_pb = datastore_pb.Query()
- query_info_pb.ParseFromString(query_info_encoded)
- self._ValidateQuery(query, query_info_pb)
-
- entity_pb.ParseFromString(entity_encoded)
- return (datastore.Entity._FromPb(entity_pb, True),
- position.start_inclusive())
-
- def _EncodeCompiledCursor(self, query, compiled_cursor):
- """Converts the current state of the cursor into a compiled_cursor
-
- Args:
- query: the datastore_pb.Query this cursor is related to
- compiled_cursor: an empty datstore_pb.CompiledCursor
- """
- if self.__last_result is not None:
- position = compiled_cursor.add_position()
- query_info = self._MinimalQueryInfo(query)
- entity_info = self._MinimalEntityInfo(self.__last_result.ToPb(), query)
- start_key = _CURSOR_CONCAT_STR.join((
- query_info.Encode(),
- entity_info.Encode()))
- position.set_start_key(str(start_key))
- position.set_start_inclusive(False)
-
- def PopulateQueryResult(self, result, count, offset, compile=False):
- """Populates a QueryResult with this cursor and the given number of results.
-
- Args:
- result: datastore_pb.QueryResult
- count: integer of how many results to return
- offset: integer of how many results to skip
- compile: boolean, whether we are compiling this query
+ (results, remaining_filters, remaining_orders)
+ results is a list of datastore.Entity
+ remaining_filters and remaining_orders are the filters and orders that
+ should be applied in memory
"""
- offset = min(offset, self.count - self.__offset)
- limited_offset = min(offset, _MAX_QUERY_OFFSET)
- if limited_offset:
- self.__offset += limited_offset
- result.set_skipped_results(limited_offset)
-
- if offset == limited_offset and count:
- if count > _MAXIMUM_RESULTS:
- count = _MAXIMUM_RESULTS
- results = self.__results[self.__offset:self.__offset + count]
- count = len(results)
- self.__offset += count
- result.result_list().extend(r._ToPb() for r in results)
+ kind_range = datastore_stub_util.ParseKindQuery(query, filters, orders)
+ app_namespace_str = datastore_types.EncodeAppIdNamespace(
+ query.app(), query.name_space())
+ kinds = []
- if self.__offset:
- self.__last_result = self.__results[self.__offset - 1]
+ for app_namespace, kind in entities:
+ if app_namespace != app_namespace_str: continue
+ if not kind_range.Contains(kind): continue
+ kinds.append(datastore.Entity(self.name, name=kind))
- result.mutable_cursor().set_app(self.app)
- result.mutable_cursor().set_cursor(self.cursor)
- result.set_keys_only(self.keys_only)
- result.set_more_results(self.__offset < self.count)
- if compile:
- self._EncodeCompiledCursor(
- self.__query, result.mutable_compiled_cursor())
+ return (kinds, [], [])
-class KindPseudoKind(object):
+class PropertyPseudoKind(object):
"""Pseudo-kind for schema queries.
Provides a Query method to perform the actual query.
Public properties:
name: the pseudo-kind name
"""
- name = '__kind__'
+ name = '__property__'
def __init__(self, filestub):
"""Constructor.
- Initializes a __kind__ pseudo-kind definition.
+ Initializes a __property__ pseudo-kind definition.
Args:
filestub: the DatastoreFileStub instance being served by this
pseudo-kind.
"""
@@ -394,78 +159,84 @@ def __init__(self, filestub):
def Query(self, entities, query, filters, orders):
"""Perform a query on this pseudo-kind.
Args:
- entities: all the app's entities
- query: the original datastore_pb.Query
- filters: the filters from query
- orders: the orders from query
+ entities: all the app's entities.
+ query: the original datastore_pb.Query.
+ filters: the filters from query.
+ orders: the orders from query.
Returns:
(results, remaining_filters, remaining_orders)
results is a list of datastore.Entity
remaining_filters and remaining_orders are the filters and orders that
should be applied in memory
"""
- start_kind, start_inclusive, end_kind, end_inclusive = (
- datastore_stub_util.ParseKindQuery(query, filters, orders))
- keys_only = query.keys_only()
- app_str = query.app()
- namespace_str = query.name_space()
+ property_range = datastore_stub_util.ParsePropertyQuery(query, filters,
+ orders)
keys_only = query.keys_only()
- app_namespace_str = datastore_types.EncodeAppIdNamespace(app_str,
- namespace_str)
- kinds = []
+ app_namespace_str = datastore_types.EncodeAppIdNamespace(
+ query.app(), query.name_space())
+
+ properties = []
if keys_only:
- usekey = '__kind__keys'
+ usekey = '__property__keys'
else:
- usekey = '__kind__'
+ usekey = '__property__'
for app_namespace, kind in entities:
if app_namespace != app_namespace_str: continue
- if start_kind is not None:
- if start_inclusive and kind < start_kind: continue
- if not start_inclusive and kind <= start_kind: continue
- if end_kind is not None:
- if end_inclusive and kind > end_kind: continue
- if not end_inclusive and kind >= end_kind: continue
- app_kind = (app_namespace_str, kind)
+ (start_cmp, end_cmp) = property_range.MapExtremes(
+ lambda extreme, inclusive, is_end: cmp(kind, extreme[0]))
+ if not((start_cmp is None or start_cmp >= 0) and
+ (end_cmp is None or end_cmp <= 0)):
+ continue
- kind_e = self.filestub._GetSchemaCache(app_kind, usekey)
- if not kind_e:
- kind_e = datastore.Entity(self.name, name=kind)
+ app_kind = (app_namespace_str, kind)
- if not keys_only:
+ kind_properties = self.filestub._GetSchemaCache(app_kind, usekey)
+ if not kind_properties:
+ kind_properties = []
+ kind_key = datastore_types.Key.from_path(KindPseudoKind.name, kind)
props = {}
for entity in entities[app_kind].values():
for prop in entity.protobuf.property_list():
prop_name = prop.name()
+ if (prop_name in
+ datastore_stub_util.GetInvisibleSpecialPropertyNames()):
+ continue
if prop_name not in props:
props[prop_name] = set()
- cls = entity.native[prop_name].__class__
- tag = self.filestub._PROPERTY_TYPE_TAGS.get(cls)
+ native_value = entity.native[prop_name]
+ if not isinstance(native_value, list):
+ native_value = [native_value]
+ for value in native_value:
+ tag = self.filestub._PROPERTY_TYPE_TAGS.get(value.__class__)
+ if tag is not None:
props[prop_name].add(tag)
+ else:
+ logging.warning('Unexpected value of class %s in datastore', value.__class__)
- properties = []
- types = []
- for name in sorted(props):
- for tag in sorted(props[name]):
- properties.append(name)
- types.append(tag)
- if properties:
- kind_e['property'] = properties
- if types:
- kind_e['representation'] = types
+ for prop in sorted(props):
+ property_e = datastore.Entity(self.name, name=prop, parent=kind_key)
+ kind_properties.append(property_e)
- self.filestub._SetSchemaCache(app_kind, usekey, kind_e)
+ if not keys_only and props[prop]:
+ property_e['property_representation'] = [
+ datastore_stub_util._PROPERTY_TYPE_NAMES[tag]
+ for tag in sorted(props[prop])]
- kinds.append(kind_e)
+ self.filestub._SetSchemaCache(app_kind, usekey, kind_properties)
- return (kinds, [], [])
+ def InQuery(property_e):
+ return property_range.Contains((kind, property_e.key().name()))
+ properties += filter(InQuery, kind_properties)
+
+ return (properties, [], [])
class NamespacePseudoKind(object):
"""Pseudo-kind for namespace queries.
@@ -474,53 +245,34 @@ def Query(self, entities, query, filte
Public properties:
name: the pseudo-kind name
"""
name = '__namespace__'
- def __init__(self, filestub):
- """Constructor.
-
- Initializes a __namespace__ pseudo-kind definition.
-
- Args:
- filestub: the DatastoreFileStub instance being served by this
- pseudo-kind.
- """
- self.filestub = filestub
-
def Query(self, entities, query, filters, orders):
"""Perform a query on this pseudo-kind.
Args:
- entities: all the app's entities
- query: the original datastore_pb.Query
- filters: the filters from query
- orders: the orders from query
+ entities: all the app's entities.
+ query: the original datastore_pb.Query.
+ filters: the filters from query.
+ orders: the orders from query.
Returns:
(results, remaining_filters, remaining_orders)
results is a list of datastore.Entity
remaining_filters and remaining_orders are the filters and orders that
should be applied in memory
"""
- start_namespace, start_inclusive, end_namespace, end_inclusive = (
- datastore_stub_util.ParseNamespaceQuery(query, filters, orders))
+ namespace_range = datastore_stub_util.ParseNamespaceQuery(query, filters,
+ orders)
app_str = query.app()
namespaces = set()
for app_namespace, kind in entities:
(app_id, namespace) = datastore_types.DecodeAppIdNamespace(app_namespace)
- if app_id != app_str: continue
-
- if start_namespace is not None:
- if start_inclusive and namespace < start_namespace: continue
- if not start_inclusive and namespace <= start_namespace: continue
- if end_namespace is not None:
- if end_inclusive and namespace > end_namespace: continue
- if not end_inclusive and namespace >= end_namespace: continue
-
+ if app_id == app_str and namespace_range.Contains(namespace):
namespaces.add(namespace)
namespace_entities = []
for namespace in namespaces:
if namespace:
@@ -633,12 +385,13 @@ def __init__(self,
self.__entities_lock = threading.Lock()
self.__file_lock = threading.Lock()
self.__indexes_lock = threading.Lock()
self.__pseudo_kinds = {}
- self._RegisterPseudoKind(KindPseudoKind(self))
- self._RegisterPseudoKind(NamespacePseudoKind(self))
+ self._RegisterPseudoKind(KindPseudoKind())
+ self._RegisterPseudoKind(PropertyPseudoKind(self))
+ self._RegisterPseudoKind(NamespacePseudoKind())
self.Read()
def _RegisterPseudoKind(self, kind):
self.__pseudo_kinds[kind.name] = kind
@@ -824,18 +577,18 @@ def __ReadPickled(self, filename):
finally:
self.__file_lock.release()
return []
- def __WritePickled(self, obj, filename, openfile=file):
+ def __WritePickled(self, obj, filename):
"""Pickles the object and writes it to the given file.
"""
if not filename or filename == '/dev/null' or not obj:
return
descriptor, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename))
- tmpfile = openfile(tmp_filename, 'wb')
+ tmpfile = os.fdopen(descriptor, 'wb')
pickler = pickle.Pickler(tmpfile, protocol=1)
pickler.fast = True
pickler.dump(obj)
tmpfile.close()
@@ -919,10 +672,12 @@ def _Dynamic_Put(self, put_request, pu
else:
assert (clone.has_entity_group() and
clone.entity_group().element_size() > 0)
+ datastore_stub_util.PrepareSpecialPropertiesForStore(clone)
+
self.__entities_lock.acquire()
try:
for clone in clones:
self._StoreEntity(clone)
@@ -954,10 +709,12 @@ def _Dynamic_Get(self, get_request, ge
except KeyError:
entity = None
if entity:
group.mutable_entity().CopyFrom(entity)
+ datastore_stub_util.PrepareSpecialPropertiesForLoad(
+ group.mutable_entity())
def _Dynamic_Delete(self, delete_request, delete_response):
if delete_request.has_transaction():
self.__ValidateTransaction(delete_request.transaction())
@@ -1164,10 +921,18 @@ def order_compare_entities(a, b):
return cmped
if cmped == 0:
return cmp(a.key(), b.key())
+ def order_compare_entities_pb(a, b):
+ """ Return a negative, zero or positive number depending on whether
+ entity a is considered smaller than, equal to, or larger than b,
+ according to the query's orderings. a and b are protobuf-encoded
+ entities."""
+ return order_compare_entities(datastore.Entity.FromPb(a),
+ datastore.Entity.FromPb(b))
+
def order_compare_properties(x, y):
"""Return a negative, zero or positive number depending on whether
property value x is considered smaller than, equal to, or larger than
property value y. If x and y are different types, they're compared based
on the type ordering used in the real datastore, which is based on the
@@ -1199,11 +964,15 @@ def order_compare_properties(x, y):
if clone in self.__query_history:
self.__query_history[clone] += 1
else:
self.__query_history[clone] = 1
- cursor = _Cursor(query, results, order_compare_entities)
+ results = [r._ToPb() for r in results]
+ for result in results:
+ datastore_stub_util.PrepareSpecialPropertiesForLoad(result)
+ cursor = datastore_stub_util.ListCursor(query, results,
+ order_compare_entities_pb)
self.__queries[cursor.cursor] = cursor
if query.has_count():
count = query.count()
elif query.has_limit():
@@ -1241,11 +1010,12 @@ def _Dynamic_Next(self, next_request,
def _Dynamic_Count(self, query, integer64proto):
query_result = datastore_pb.QueryResult()
self._Dynamic_RunQuery(query, query_result)
cursor = query_result.cursor().cursor()
- integer64proto.set_value(min(self.__queries[cursor].count, _MAXIMUM_RESULTS))
+ integer64proto.set_value(min(self.__queries[cursor].Count(),
+ datastore_stub_util._MAXIMUM_RESULTS))
del self.__queries[cursor]
def _Dynamic_BeginTransaction(self, request, transaction):
self.__ValidateAppId(request.app())
@@ -1346,10 +1116,13 @@ def _Dynamic_GetSchema(self, req, sche
props = {}
for entity in self.__entities[app_kind].values():
for prop in entity.protobuf.property_list():
+ if (prop.name() in
+ datastore_stub_util.GetInvisibleSpecialPropertyNames()):
+ continue
if prop.name() not in props:
props[prop.name()] = entity_pb.PropertyValue()
props[prop.name()].MergeFrom(prop.value())
for value_pb in props.values():
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/datastore.py google-appengine-1.4.0-prerelease/google/appengine/api/datastore.py
--- local/google_appengine/google/appengine/api/datastore.py 2010-10-14 21:40:44.038838421 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/datastore.py 2010-11-18 21:44:32.000000000 +0000
@@ -34,33 +35,24 @@
import heapq
import itertools
import logging
import os
import re
-import string
import sys
+import threading
import traceback
from xml.sax import saxutils
-from google.appengine.api import api_base_pb
-from google.appengine.api import apiproxy_rpc
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import capabilities
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
-from google.appengine.datastore import datastore_index
from google.appengine.datastore import datastore_pb
-from google.appengine.runtime import apiproxy_errors
+from google.appengine.datastore import datastore_rpc
+from google.appengine.datastore import datastore_query
from google.appengine.datastore import entity_pb
-try:
- __import__('google.appengine.api.labs.taskqueue.taskqueue_service_pb')
- taskqueue_service_pb = sys.modules.get(
- 'google.appengine.api.labs.taskqueue.taskqueue_service_pb')
-except ImportError:
- from google.appengine.api.taskqueue import taskqueue_service_pb
-
MAX_ALLOWABLE_QUERIES = 30
MAXIMUM_RESULTS = 1000
DEFAULT_TRANSACTION_RETRIES = 3
@@ -70,26 +62,19 @@
'datastore_v3',
capabilities=['write'])
_MAX_INDEXED_PROPERTIES = 5000
-_MAX_ID_BATCH_SIZE = 1000 * 1000 * 1000
+_MAX_ID_BATCH_SIZE = datastore_rpc._MAX_ID_BATCH_SIZE
Key = datastore_types.Key
typename = datastore_types.typename
-_txes = {}
-
-_ALLOWED_API_KWARGS = frozenset(['rpc'])
-
-_ALLOWED_FAILOVER_READ_METHODS = set(
- ('Get', 'RunQuery', 'RunCompiledQuery', 'Count', 'Next'))
-
-ARBITRARY_FAILOVER_MS = -1
+_ALLOWED_API_KWARGS = frozenset(['rpc', 'config'])
-STRONG_CONSISTENCY = 0
-EVENTUAL_CONSISTENCY = 1
+STRONG_CONSISTENCY = datastore_rpc.Configuration.STRONG_CONSISTENCY
+EVENTUAL_CONSISTENCY = datastore_rpc.Configuration.EVENTUAL_CONSISTENCY
_MAX_INT_32 = 2**31-1
def NormalizeAndTypeCheck(arg, types):
@@ -159,97 +144,212 @@ def NormalizeAndTypeCheckKeys(keys):
keys = [_GetCompleteKeyOrError(key) for key in keys]
return (keys, multiple)
-def GetRpcFromKwargs(kwargs):
+def _GetConfigFromKwargs(kwargs):
+ """Get a Configuration object from the keyword arguments.
+
+ This is purely an internal helper for the various public APIs below
+ such as Get().
+
+ Args:
+ kwargs: A dict containing the keyword arguments passed to a public API.
+
+ Returns:
+ A UserRPC instance, or a Configuration instance, or None.
+
+ Raises:
+ TypeError if unexpected keyword arguments are present.
+ """
if not kwargs:
return None
args_diff = set(kwargs) - _ALLOWED_API_KWARGS
if args_diff:
- raise TypeError('Invalid arguments: %s' % ', '.join(args_diff))
- return kwargs.get('rpc')
+ raise datastore_errors.BadArgumentError(
+ 'Unexpected keyword arguments: %s' % ', '.join(args_diff))
+ rpc = kwargs.get('rpc')
+ config = kwargs.get('config')
+ if rpc is not None:
+ if config is not None:
+ raise datastore_errors.BadArgumentError(
+ 'Expected rpc= or config= argument but not both')
+ if isinstance(rpc, (apiproxy_stub_map.UserRPC,
+ datastore_rpc.Configuration)):
+ return rpc
+ raise datastore_errors.BadArgumentError(
+ 'rpc= argument should be None or a UserRPC instance')
+ if config is not None:
+ if not isinstance(config, (datastore_rpc.Configuration,
+ apiproxy_stub_map.UserRPC)):
+ raise datastore_errors.BadArgumentError(
+ 'config= argument should be None or a Configuration instance')
+ return config
-def _MakeSyncCall(service, call, request, response, rpc=None):
+class DatastoreAdapter(datastore_rpc.AbstractAdapter):
+ """Adapter between datatypes defined here (Entity etc.) and protobufs.
+
+ See the base class in datastore_rpc.py for more docs.
+ """
+
+ def key_to_pb(self, key):
+ return key._Key__reference
+
+ def pb_to_key(self, pb):
+ return Key._FromPb(pb)
+
+ def entity_to_pb(self, entity):
+ return entity._ToPb()
+
+ def pb_to_entity(self, pb):
+ return Entity._FromPb(pb)
+
+
+_adapter = DatastoreAdapter()
+_thread_local = threading.local()
+
+_ENV_KEY = '__DATASTORE_CONNECTION_INITIALIZED__'
+
+
+def _GetConnection():
+ """Retrieve a datastore connection local to the thread."""
+ connection = None
+ if os.getenv(_ENV_KEY):
+ try:
+ connection = _thread_local.connection
+ except AttributeError:
+ pass
+ if connection is None:
+ connection = datastore_rpc.Connection(adapter=_adapter)
+ _SetConnection(connection)
+ return connection
+
+
+def _SetConnection(connection):
+ """Sets the datastore connection local to the thread."""
+ _thread_local.connection = connection
+ os.environ[_ENV_KEY] = '1'
+
+
+
+def _MakeSyncCall(service, call, request, response, config=None):
"""The APIProxy entry point for a synchronous API call.
Args:
- service: string representing which service to call
- call: string representing which function to call
- request: protocol buffer for the request
- response: protocol buffer for the response
- rpc: datastore.DatastoreRPC to use for this request.
+ service: For backwards compatibility, must be 'datastore_v3'.
+ call: String representing which function to call.
+ request: Protocol buffer for the request.
+ response: Protocol buffer for the response.
+ config: Optional Configuration to use for this request.
Returns:
Response protocol buffer. Caller should always use returned value
which may or may not be same as passed in 'response'.
Raises:
apiproxy_errors.Error or a subclass.
"""
- if not rpc:
- rpc = CreateRPC(service)
-
- rpc.make_call(call, request, response)
- rpc.wait()
- rpc.check_success()
+ conn = _GetConnection()
+ if isinstance(request, datastore_pb.Query):
+ conn._set_request_read_policy(request, config)
+ conn._set_request_transaction(request)
+ rpc = conn.make_rpc_call(config, call, request, response)
+ conn.check_rpc_success(rpc)
return response
-def CreateRPC(service='datastore_v3', deadline=None, callback=None,
- read_policy=STRONG_CONSISTENCY):
+def CreateRPC(service='datastore_v3',
+ deadline=None, callback=None, read_policy=None):
"""Create an rpc for use in configuring datastore calls.
+ NOTE: This functions exists for backwards compatibility. Please use
+ CreateConfig() instead. NOTE: the latter uses 'on_completion',
+ which is a function taking an argument, wherease CreateRPC uses
+ 'callback' which is a function without arguments.
+
Args:
- deadline: float, deadline for calls in seconds.
- callback: callable, a callback triggered when this rpc completes,
- accepts one argument: the returned rpc.
- read_policy: flag, set to EVENTUAL_CONSISTENCY to enable eventually
- consistent reads
+ service: Optional string; for backwards compatibility, must be
+ 'datastore_v3'.
+ deadline: Optional int or float, deadline for calls in seconds.
+ callback: Optional callable, a callback triggered when this rpc
+ completes; takes no arguments.
+ read_policy: Optional read policy; set to EVENTUAL_CONSISTENCY to
+ enable eventually consistent reads (i.e. reads that may be
+ satisfied from an older version of the datastore in some cases).
+ The default read policy may have to wait until in-flight
+ transactions are committed.
Returns:
- A datastore.DatastoreRPC instance.
+ A UserRPC instance.
"""
- return DatastoreRPC(service, deadline, callback, read_policy)
+ assert service == 'datastore_v3'
+ conn = _GetConnection()
+ config = None
+ if deadline is not None:
+ config = datastore_rpc.Configuration(deadline=deadline)
+ rpc = conn.create_rpc(config)
+ rpc.callback = callback
+ if read_policy is not None:
+ rpc.read_policy = read_policy
+ return rpc
-class DatastoreRPC(apiproxy_stub_map.UserRPC):
- """Specialized RPC for the datastore.
+def CreateConfig(**kwds):
+ """Create a Configuration object for use in configuring datastore calls.
- Wraps the default RPC class and sets appropriate values for use by the
- datastore.
+ This configuration can be passed to most datastore calls using the
+ 'config=...' argument.
- This class or a sublcass of it is intended to be instatiated by
- developers interested in setting specific request parameters, such as
- deadline, on API calls. It will be used to make the actual call.
+ Args:
+ deadline: Optional deadline; default None (which means the
+ system default deadline will be used, typically 5 seconds).
+ on_completion: Optional callback function; default None. If
+ specified, it will be called with a UserRPC object as argument
+ when an RPC completes.
+ read_policy: Optional read policy; set to EVENTUAL_CONSISTENCY to
+ enable eventually consistent reads (i.e. reads that may be
+ satisfied from an older version of the datastore in some cases).
+ The default read policy may have to wait until in-flight
+ transactions are committed.
+ **kwds: Other keyword arguments as long as they are supported by
+ datastore_rpc.Configuration().
+
+ Returns:
+ A datastore_rpc.Configuration instance.
"""
+ return datastore_rpc.Configuration(**kwds)
- def __init__(self, service='datastore_v3', deadline=None, callback=None,
- read_policy=STRONG_CONSISTENCY):
- super(DatastoreRPC, self).__init__(service, deadline, callback)
- self.read_policy = read_policy
- def make_call(self, call, request, response):
- if self.read_policy == EVENTUAL_CONSISTENCY:
- if call not in _ALLOWED_FAILOVER_READ_METHODS:
- raise datastore_errors.BadRequestError(
- 'read_policy is only supported on read operations.')
- if call != 'Next':
- request.set_failover_ms(ARBITRARY_FAILOVER_MS)
- super(DatastoreRPC, self).make_call(call, request, response)
+def _Rpc2Config(rpc):
+ """Internal helper to construct a Configuration from a UserRPC object.
- def clone(self):
- """Make a shallow copy of this instance.
+ If the argument is a UserRPC object, it returns a Configuration
+ object constructed using the same deadline and read_policy;
+ otherwise it returns the argument unchanged.
- This is usually used when an RPC has been specified with some configuration
- options and is being used as a template for multiple RPCs outside of a
- developer's easy control.
+ NOTE: If the argument is a UserRPC object, its callback is *not*
+ transferred to the Configuration object; the Configuration's
+ on_completion attribute is set to None. This is done because (a)
+ the signature of on_completion differs from the callback signature;
+ (b) the caller probably doesn't expect the callback to be called
+ more than once; and (c) the callback, being argument-less, wouldn't
+ know which UserRPC object was actually completing. But yes,
+ technically, this is a backwards incompatibility.
+
+ Args:
+ rpc: None, a UserRPC object, or a datastore_rpc.Configuration object.
+
+ Returns:
+ None or a datastore_rpc.Configuration object.
"""
- assert self.state == apiproxy_rpc.RPC.IDLE
- return self.__class__(
- self.service, self.deadline, self.callback, self.read_policy)
+ if rpc is None or isinstance(rpc, datastore_rpc.Configuration):
+ return rpc
+ read_policy = getattr(rpc, 'read_policy', None)
+ return datastore_rpc.Configuration(deadline=rpc.deadline,
+ read_policy=read_policy,
+ config=_GetConnection().config)
def Put(entities, **kwargs):
"""Store one or more entities in the datastore.
@@ -259,59 +359,51 @@ def Put(entities, **kwargs):
If the argument is a single Entity, a single Key will be returned. If the
argument is a list of Entity, a list of Keys will be returned.
Args:
entities: Entity or list of Entities
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
Key or list of Keys
Raises:
TransactionFailedError, if the Put could not be committed.
"""
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
+ if getattr(config, 'read_policy', None) == EVENTUAL_CONSISTENCY:
+ raise datastore_errors.BadRequestError(
+ 'read_policy is only supported on read operations.')
entities, multiple = NormalizeAndTypeCheck(entities, Entity)
if multiple and not entities:
return []
for entity in entities:
if not entity.kind() or not entity.app():
raise datastore_errors.BadRequestError(
'App and kind must not be empty, in entity: %s' % entity)
- req = datastore_pb.PutRequest()
- req.entity_list().extend([e._ToPb() for e in entities])
-
- keys = [e.key() for e in entities]
- tx = _MaybeSetupTransaction(req, keys)
-
- try:
- resp = _MakeSyncCall(
- 'datastore_v3', 'Put', req, datastore_pb.PutResponse(), rpc)
- except apiproxy_errors.ApplicationError, err:
- raise _ToDatastoreError(err)
-
- keys = resp.key_list()
+ def extra_hook(keys):
num_keys = len(keys)
num_entities = len(entities)
if num_keys != num_entities:
raise datastore_errors.InternalError(
'Put accepted %d entities but returned %d keys.' %
(num_entities, num_keys))
for entity, key in zip(entities, keys):
- entity._Entity__key._Key__reference.CopyFrom(key)
-
- if tx:
- tx.entity_group = entities[0].entity_group()
+ if entity._Entity__key._Key__reference != key._Key__reference:
+ assert not entity._Entity__key.has_id_or_name()
+ entity._Entity__key._Key__reference.CopyFrom(key._Key__reference)
if multiple:
- return [Key._FromPb(k) for k in keys]
+ return keys
else:
- return Key._FromPb(resp.key(0))
+ return keys[0]
+
+ return _GetConnection().async_put(config, entities, extra_hook).get_result()
def Get(keys, **kwargs):
"""Retrieves one or more entities from the datastore.
@@ -327,44 +419,31 @@ def Get(keys, **kwargs):
that were found and None placeholders for keys that were not found.
Args:
# the primary key(s) of the entity(ies) to retrieve
keys: Key or string or list of Keys or strings
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
Entity or list of Entity objects
"""
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
keys, multiple = NormalizeAndTypeCheckKeys(keys)
if multiple and not keys:
return []
- req = datastore_pb.GetRequest()
- req.key_list().extend([key._Key__reference for key in keys])
- _MaybeSetupTransaction(req, keys)
-
- try:
- resp = _MakeSyncCall(
- 'datastore_v3', 'Get', req, datastore_pb.GetResponse(), rpc)
- except apiproxy_errors.ApplicationError, err:
- raise _ToDatastoreError(err)
-
- entities = []
- for group in resp.entity_list():
- if group.has_entity():
- entities.append(Entity._FromPb(group.entity()))
- else:
- entities.append(None)
+ def extra_hook(entities):
if multiple:
return entities
else:
if entities[0] is None:
raise datastore_errors.EntityNotFoundError()
return entities[0]
+ return _GetConnection().async_get(config, keys, extra_hook).get_result()
+
def Delete(keys, **kwargs):
"""Deletes one or more entities from the datastore. Use with care!
Deletes the given entity(ies) from the datastore. You can only delete
@@ -372,31 +451,25 @@ def Delete(keys, **kwargs):
datastore_errors.Error.
Args:
# the primary key(s) of the entity(ies) to delete
keys: Key or string or list of Keys or strings
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Raises:
TransactionFailedError, if the Delete could not be committed.
"""
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
+ if getattr(config, 'read_policy', None) == EVENTUAL_CONSISTENCY:
+ raise datastore_errors.BadRequestError(
+ 'read_policy is only supported on read operations.')
keys, multiple = NormalizeAndTypeCheckKeys(keys)
if multiple and not keys:
return
- req = datastore_pb.DeleteRequest()
- req.key_list().extend([key._Key__reference for key in keys])
-
- tx = _MaybeSetupTransaction(req, keys)
-
- try:
- _MakeSyncCall(
- 'datastore_v3', 'Delete', req, datastore_pb.DeleteResponse(), rpc)
- except apiproxy_errors.ApplicationError, err:
- raise _ToDatastoreError(err)
+ _GetConnection().async_delete(config, keys).get_result()
class Entity(dict):
"""A datastore entity.
@@ -702,11 +775,11 @@ def _ToPb(self, mark_key_as_saved=True
'Too many indexed properties for entity %r.' % self.key())
return pb
@staticmethod
- def FromPb(pb):
+ def FromPb(pb, validate_reserved_properties=True):
"""Static factory method. Returns the Entity representation of the
given protocol buffer (datastore_pb.Entity).
Args:
pb: datastore_pb.Entity or str encoding of a datastore_pb.Entity
@@ -717,14 +790,16 @@ def FromPb(pb):
if isinstance(pb, str):
real_pb = entity_pb.EntityProto()
real_pb.ParseFromString(pb)
pb = real_pb
- return Entity._FromPb(pb, require_valid_key=False)
+ return Entity._FromPb(
+ pb, require_valid_key=False,
+ validate_reserved_properties=validate_reserved_properties)
@staticmethod
- def _FromPb(pb, require_valid_key=True):
+ def _FromPb(pb, require_valid_key=True, validate_reserved_properties=True):
"""Static factory method. Returns the Entity representation of the
given protocol buffer (datastore_pb.Entity). Not intended to be used by
application developers.
The Entity PB's key must be complete. If it isn't, an AssertionError is
@@ -788,11 +863,12 @@ def _FromPb(pb, require_valid_key=True
cur_value.extend(value)
for name, value in temporary_values.iteritems():
decoded_name = unicode(name.decode('utf-8'))
- datastore_types.ValidateReadProperty(decoded_name, value)
+ datastore_types.ValidateReadProperty(
+ decoded_name, value, read_only=(not validate_reserved_properties))
dict.__setitem__(e, decoded_name, value)
return e
@@ -872,37 +948,33 @@ def _FromPb(pb, require_valid_key=True
Finally, the Count() method returns the number of result entities matched by
the query. The returned count is cached; successive Count() calls will not
re-scan the datastore unless the query is changed.
"""
- ASCENDING = datastore_pb.Query_Order.ASCENDING
- DESCENDING = datastore_pb.Query_Order.DESCENDING
+ ASCENDING = datastore_query.PropertyOrder.ASCENDING
+ DESCENDING = datastore_query.PropertyOrder.DESCENDING
- ORDER_FIRST = datastore_pb.Query.ORDER_FIRST
- ANCESTOR_FIRST = datastore_pb.Query.ANCESTOR_FIRST
- FILTER_FIRST = datastore_pb.Query.FILTER_FIRST
+ ORDER_FIRST = datastore_query.QueryOptions.ORDER_FIRST
+ ANCESTOR_FIRST = datastore_query.QueryOptions.ANCESTOR_FIRST
+ FILTER_FIRST = datastore_query.QueryOptions.FILTER_FIRST
+
+ OPERATORS = {'==': datastore_query.PropertyFilter._OPERATORS['=']}
+ OPERATORS.update(datastore_query.PropertyFilter._OPERATORS)
+
+ INEQUALITY_OPERATORS = datastore_query.PropertyFilter._INEQUALITY_OPERATORS
- OPERATORS = {'<': datastore_pb.Query_Filter.LESS_THAN,
- '<=': datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL,
- '>': datastore_pb.Query_Filter.GREATER_THAN,
- '>=': datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL,
- '=': datastore_pb.Query_Filter.EQUAL,
- '==': datastore_pb.Query_Filter.EQUAL,
- }
- INEQUALITY_OPERATORS = frozenset(['<', '<=', '>', '>='])
UPPERBOUND_INEQUALITY_OPERATORS = frozenset(['<', '<='])
FILTER_REGEX = re.compile(
- '^\s*([^\s]+)(\s+(%s)\s*)?$' % '|'.join(OPERATORS.keys()),
+ '^\s*([^\s]+)(\s+(%s)\s*)?$' % '|'.join(OPERATORS),
re.IGNORECASE | re.UNICODE)
__kind = None
__app = None
__namespace = None
__orderings = None
- __cached_count = None
__hint = None
- __ancestor = None
+ __ancestor_pb = None
__compile = None
__cursor = None
__end_cursor = None
@@ -1097,127 +1169,145 @@ def Ancestor(self, ancestor):
Returns:
# this query
Query
"""
- self.__ancestor = _GetCompleteKeyOrError(ancestor)
+ self.__ancestor_pb = _GetCompleteKeyOrError(ancestor)._ToPb()
return self
def IsKeysOnly(self):
"""Returns True if this query is keys only, false otherwise."""
return self.__keys_only
- def GetCompiledCursor(self):
+ def GetQueryOptions(self):
+ """Returns a datastore_query.QueryOptions for the current instance."""
+ return datastore_query.QueryOptions(keys_only=self.__keys_only,
+ produce_cursors=self.__compile,
+ start_cursor=self.__cursor,
+ end_cursor=self.__end_cursor,
+ hint=self.__hint)
+
+ def GetQuery(self):
+ """Returns a datastore_query.Query for the current instance."""
+ return datastore_query.Query(app=self.__app,
+ namespace=self.__namespace,
+ kind=self.__kind,
+ ancestor=self.__ancestor_pb,
+ filter_predicate=self.GetFilterPredicate(),
+ order=self.GetOrder())
+
+ def GetOrder(self):
+ """Gets a datastore_query.Order for the current instance.
+
+ Returns:
+ datastore_query.Order or None if there are no sort orders set on the
+ current Query.
+ """
+
+ orders = [datastore_query.PropertyOrder(property, direction)
+ for property, direction in self.__orderings]
+ if orders:
+ return datastore_query.CompositeOrder(orders)
+ return None
+
+ def GetFilterPredicate(self):
+ """Returns a datastore_query.FilterPredicate for the current instance.
+
+ Returns:
+ datastore_query.FilterPredicate or None if no filters are set on the
+ current Query.
+ """
+ ordered_filters = [(i, f) for f, i in self.__filter_order.iteritems()]
+ ordered_filters.sort()
+
+ property_filters = []
+ for _, filter_str in ordered_filters:
+ if filter_str not in self:
+ continue
+
+ values = self[filter_str]
+ match = self._CheckFilter(filter_str, values)
+ name = match.group(1)
+
+ op = match.group(3)
+ if op is None or op == '==':
+ op = '='
+
+ property_filters.append(datastore_query.make_filter(name, op, values))
+
+ if property_filters:
+ return datastore_query.CompositeFilter(
+ datastore_query.CompositeFilter.AND,
+ property_filters)
+ return None
+
+ def GetCursor(self):
+ """Get the cursor from the last run of this query.
+
+ The source of this cursor varies depending on what the last call was:
+ - Run: A cursor that points immediately after the last result pulled off
+ the returned iterator.
+ - Get: A cursor that points immediately after the last result in the
+ returned list.
+ - Count: A cursor that points immediately after the last result counted.
+
+ Returns:
+ A datastore_query.Cursor object that can be used in subsiquent query
+ requests.
+ """
try:
- compiled_cursor = self.__last_iterator.GetCompiledCursor(self)
- if not compiled_cursor:
+ cursor = self.__cursor_source()
+ if not cursor:
raise AttributeError()
except AttributeError:
raise AssertionError('No cursor available, either this query has not '
'been executed or there is no compilation '
'available for this kind of query')
- return compiled_cursor
-
- def GetCompiledQuery(self):
- try:
- if not self.__compiled_query:
- raise AttributeError()
- except AttributeError:
- raise AssertionError('No compiled query available, either this query has '
- 'not been executed or there is no compilation '
- 'available for this kind of query')
- return self.__compiled_query
-
- def Run(self, **kwargs):
- """Runs this query.
+ return cursor
- If a filter string is invalid, raises BadFilterError. If a filter value is
- invalid, raises BadValueError. If an IN filter is provided, and a sort
- order on another property is provided, raises BadQueryError.
+ def GetBatcher(self, config=None):
+ """Runs this query and returns a datastore_query.Batcher.
- If you know in advance how many results you want, use Get() instead. It's
- more efficient.
+ This is not intended to be used by application developers. Use Get()
+ instead!
Args:
- limit: integer, limit for the query.
- offset: integer, offset for the query.
- prefetch_count: integer, number of results to return in the first query.
- next_count: number of results to return in subsequent next queries.
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
# an iterator that provides access to the query results
Iterator
"""
- return self._Run(**kwargs)
+ query_options = self.GetQueryOptions().merge(config)
+ return self.GetQuery().run(_GetConnection(), query_options)
- def _Run(self, limit=None, offset=None,
- prefetch_count=None, next_count=None, **kwargs):
- """Runs this query, with an optional result limit and an optional offset.
+ def Run(self, **kwargs):
+ """Runs this query.
- Identical to Run, with the extra optional limit, offset, prefetch_count,
- next_count parameters. These parameters must be integers >= 0.
+ If a filter string is invalid, raises BadFilterError. If a filter value is
+ invalid, raises BadValueError. If an IN filter is provided, and a sort
+ order on another property is provided, raises BadQueryError.
- This is not intended to be used by application developers. Use Get()
- instead!
+ If you know in advance how many results you want, use Get() instead. It's
+ more efficient.
Args:
limit: integer, limit for the query.
offset: integer, offset for the query.
prefetch_count: integer, number of results to return in the first query.
next_count: number of results to return in subsequent next queries.
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
# an iterator that provides access to the query results
Iterator
"""
- rpc = GetRpcFromKwargs(kwargs)
- self.__last_iterator, self.__compiled_query = Query._RunInternal(
- self._ToPb(limit, offset, prefetch_count),
- next_count=next_count,
- rpc=rpc)
-
- return self.__last_iterator
-
- @staticmethod
- def _RunInternal(request, next_count=None, rpc=None):
- """Runs the given request and wraps the result in an iterator.
-
- Args:
- request: datastore_pb.query, the request to run.
- next_count: number of results to return in subsequent next queries.
- rpc: datastore.RPC to use for this request.
-
- Returns:
- (Iterator, datastore_pb.CompiledQuery), the iterator and compiled query
- that result from running the given request.
- """
-
- if rpc:
- rpc_clone = rpc.clone()
- else:
- rpc_clone = None
-
- try:
- result = _MakeSyncCall('datastore_v3', 'RunQuery', request,
- datastore_pb.QueryResult(), rpc)
- except apiproxy_errors.ApplicationError, err:
- try:
- raise _ToDatastoreError(err)
- except datastore_errors.NeedIndexError, exc:
- yaml = datastore_index.IndexYamlForQuery(
- *datastore_index.CompositeIndexForQuery(request)[1:-1])
- raise datastore_errors.NeedIndexError(
- str(exc) + '\nThis query needs this index:\n' + yaml)
-
- iterator = Iterator(result, query_request_pb=request, batch_size=next_count,
- rpc=rpc_clone)
- if result.has_compiled_query():
- return iterator, result.compiled_query()
- else:
- return iterator, None
+ config = _Rpc2Config(_GetConfigFromKwargs(kwargs))
+ itr = Iterator(self.GetBatcher(config=config))
+ self.__cursor_source = itr.cursor
+ self.__compiled_query_source = itr._compiled_query
+ return itr
def Get(self, limit, offset=0, **kwargs):
"""Fetches and returns a maximum number of results from the query.
This method fetches and returns a list of resulting entities that matched
@@ -1247,58 +1337,68 @@ def Get(self, limit, offset=0, **kwarg
Args:
# the maximum number of entities to return
int or long
# the number of entities to skip
int or long
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request. If limit and
+ offset are specified in the config, they are ignored.
Returns:
# a list of entities
[Entity, ...]
"""
- if not isinstance(limit, (int, long)) or limit < 0:
- raise datastore_errors.BadArgumentError(
- 'Argument to Get named \'limit\' must be an int greater than or '
- 'equal to 0; received %s (a %s)' % (limit, typename(limit)))
-
- if not isinstance(offset, (int, long)) or offset < 0:
- raise datastore_errors.BadArgumentError(
- 'Argument to Get named \'offset\' must be an int greater than or '
- 'equal to 0; received %s (a %s)' % (offset, typename(offset)))
+ config = _Rpc2Config(_GetConfigFromKwargs(kwargs))
+ batcher = self.GetBatcher(datastore_query.QueryOptions(
+ config=config, limit=limit, offset=offset, prefetch_size=limit))
- return self._Run(
- limit=limit, offset=offset, prefetch_count=limit, **kwargs)._Get(limit)
+ if limit is None:
+ batch = batcher.next_batch(_MAX_INT_32)
+ else:
+ batch = batcher.next_batch(limit)
+ self.__cursor_source = lambda: batch.end_cursor
+ self.__compiled_query_source = lambda: batch._compiled_query
+ return batch.results
def Count(self, limit=1000, **kwargs):
- """Returns the number of entities that this query matches. The returned
- count is cached; successive Count() calls will not re-scan the datastore
- unless the query is changed.
+ """Returns the number of entities that this query matches.
Args:
limit, a number or None. If there are more results than this, stop short
and just return this number. Providing this argument makes the count
operation more efficient.
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
The number of results.
"""
- if not self.__cached_count:
if limit is None:
offset = _MAX_INT_32
else:
offset = limit
- iterator = self._Run(limit=0, offset=offset, **kwargs)
- self.__cached_count = iterator._SkippedResults()
+ config = datastore_query.QueryOptions(
+ config=_Rpc2Config(_GetConfigFromKwargs(kwargs)),
+ limit=0,
+ offset=offset)
- return self.__cached_count
+ batch = self.GetBatcher(config=config).next()
+ self.__cursor_source = lambda: batch.cursor(0)
+ self.__compiled_query_source = lambda: batch._compiled_query
+ return batch.skipped_results
def __iter__(self):
raise NotImplementedError(
'Query objects should not be used as iterators. Call Run() first.')
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ if '_Query__cursor_source' in state:
+ del state['_Query__cursor_source']
+ if '_Query__compiled_query_source' in state:
+ del state['_Query__compiled_query_source']
+ return state
+
def __setitem__(self, filter, value):
"""Implements the [] operator. Used to set filters.
If the filter string is empty or not a string, raises BadFilterError. If
the value is not a supported type, raises BadValueError.
@@ -1323,30 +1423,26 @@ def __setitem__(self, filter, value):
if filter not in self.__filter_order:
self.__filter_order[filter] = self.__filter_counter
self.__filter_counter += 1
- self.__cached_count = None
-
def setdefault(self, filter, value):
"""If the filter exists, returns its value. Otherwise sets it to value.
If the property name is the empty string or not a string, raises
BadPropertyError. If the value is not a supported type, raises
BadValueError.
"""
datastore_types.ValidateProperty(' ', value)
self._CheckFilter(filter, value)
- self.__cached_count = None
return dict.setdefault(self, filter, value)
def __delitem__(self, filter):
"""Implements the del [] operator. Used to remove filters.
"""
dict.__delitem__(self, filter)
del self.__filter_order[filter]
- self.__cached_count = None
match = Query.FILTER_REGEX.match(filter)
property = match.group(1)
operator = match.group(3)
@@ -1445,96 +1541,46 @@ def _CheckFilter(self, filter, values)
'%s filter value must be a Key; received %s (a %s)' %
(datastore_types._KEY_SPECIAL_PROPERTY, value, typename(value)))
return match
- def _ToPb(self, limit=None, offset=None, count=None):
- """Converts this Query to its protocol buffer representation. Not
- intended to be used by application developers. Enforced by hiding the
- datastore_pb classes.
+ def _Run(self, limit=None, offset=None,
+ prefetch_count=None, next_count=None, **kwargs):
+ """Deprecated, use .Run instead."""
+ config = _Rpc2Config(_GetConfigFromKwargs(kwargs))
+ return self.Run(config=datastore_query.QueryOptions(
+ config=config,
+ limit=limit,
+ offset=offset,
+ prefetch_size=prefetch_count,
+ batch_size=next_count))
- Args:
- # an upper bound on the number of results returned by the query.
- limit: int
- # number of results that match the query to skip. limit is applied
- # after the offset is fulfilled
- offset: int
- # the requested initial batch size
- count: int
+ def _ToPb(self, limit=None, offset=None, count=None):
+ query_options = datastore_query.QueryOptions(
+ config=self.GetQueryOptions(),
+ limit=limit,
+ offset=offset,
+ batch_size=count)
+ return self.GetQuery()._to_pb(_GetConnection(), query_options)
- Returns:
- # the PB representation of this Query
- datastore_pb.Query
+ def _GetCompiledQuery(self):
+ """Returns the internal-only pb representation of the last query run.
- Raises:
- BadRequestError if called inside a transaction and the query does not
- include an ancestor.
+ Do not use.
"""
+ try:
+ compiled_query = self.__compiled_query_source()
+ if not compiled_query:
+ raise AttributeError()
+ except AttributeError:
+ raise AssertionError('No compiled query available, either this query has '
+ 'not been executed or there is no compilation '
+ 'available for this kind of query')
+ return compiled_query
- if not self.__ancestor and IsInTransaction():
- raise datastore_errors.BadRequestError(
- 'Only ancestor queries are allowed inside transactions.')
-
- pb = datastore_pb.Query()
- _MaybeSetupTransaction(pb, [self.__ancestor])
-
- if self.__kind is not None:
- pb.set_kind(self.__kind.encode('utf-8'))
- pb.set_keys_only(bool(self.__keys_only))
- if self.__app:
- pb.set_app(self.__app.encode('utf-8'))
- datastore_types.SetNamespace(pb, self.__namespace)
- if self.__compile:
- pb.set_compile(True)
- if limit is not None:
- pb.set_limit(limit)
- if offset is not None:
- pb.set_offset(offset)
- if count is not None:
- pb.set_count(count)
- if self.__ancestor:
- pb.mutable_ancestor().CopyFrom(self.__ancestor._Key__reference)
-
- if ((self.__hint == self.ORDER_FIRST and self.__orderings) or
- (self.__hint == self.ANCESTOR_FIRST and self.__ancestor) or
- (self.__hint == self.FILTER_FIRST and len(self) > 0)):
- pb.set_hint(self.__hint)
-
- ordered_filters = [(i, f) for f, i in self.__filter_order.iteritems()]
- ordered_filters.sort()
-
- for i, filter_str in ordered_filters:
- if filter_str not in self:
- continue
-
- values = self[filter_str]
- match = self._CheckFilter(filter_str, values)
- name = match.group(1)
-
- props = datastore_types.ToPropertyPb(name, values)
- if not isinstance(props, list):
- props = [props]
-
- op = match.group(3)
- if op is None:
- op = '='
-
- for prop in props:
- filter = pb.add_filter()
- filter.set_op(self.OPERATORS[op])
- filter.add_property().CopyFrom(prop)
-
- for property, direction in self.__orderings:
- order = pb.add_order()
- order.set_property(property.encode('utf-8'))
- order.set_direction(direction)
-
- if self.__cursor:
- pb.mutable_compiled_cursor().CopyFrom(self.__cursor)
- if self.__end_cursor:
- pb.mutable_end_compiled_cursor().CopyFrom(self.__end_cursor)
- return pb
+ GetCompiledQuery = _GetCompiledQuery
+ GetCompiledCursor = GetCursor
def AllocateIds(model_key, size=None, **kwargs):
"""Allocates a range of IDs of size or with max for the given key.
@@ -1555,52 +1601,28 @@ def AllocateIds(model_key, size=None, **
Args:
model_key: Key or string to serve as a model specifying the ID sequence
in which to allocate IDs
size: integer, number of IDs to allocate.
max: integer, upper bound of the range of IDs to allocate.
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
(start, end) of the allocated range, inclusive.
"""
max = kwargs.pop('max', None)
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
+ if getattr(config, 'read_policy', None) == EVENTUAL_CONSISTENCY:
+ raise datastore_errors.BadRequestError(
+ 'read_policy is only supported on read operations.')
keys, _ = NormalizeAndTypeCheckKeys(model_key)
if len(keys) > 1:
raise datastore_errors.BadArgumentError(
'Cannot allocate IDs for more than one model key at a time')
- req = datastore_pb.AllocateIdsRequest()
- if size is not None:
- if max is not None:
- raise datastore_errors.BadArgumentError(
- 'Cannot allocate ids using both size and max')
- if size > _MAX_ID_BATCH_SIZE:
- raise datastore_errors.BadArgumentError(
- 'Cannot allocate more than %s ids at a time; received %s'
- % (_MAX_ID_BATCH_SIZE, size))
- if size <= 0:
- raise datastore_errors.BadArgumentError(
- 'Cannot allocate less than 1 id; received %s' % size)
- req.set_size(size)
- if max:
- if max < 0:
- raise datastore_errors.BadArgumentError(
- 'Cannot allocate a range with a max less than 0 id; received %s' %
- size)
- req.set_max(max)
-
- req.mutable_model_key().CopyFrom(keys[0]._ToPb())
-
- try:
- resp = _MakeSyncCall('datastore_v3', 'AllocateIds', req,
- datastore_pb.AllocateIdsResponse(), rpc)
- except apiproxy_errors.ApplicationError, err:
- raise _ToDatastoreError(err)
-
- return resp.start(), resp.end()
+ rpc = _GetConnection().async_allocate_ids(config, keys[0], size, max)
+ return rpc.get_result()
class MultiQuery(Query):
"""Class representing a query which requires multiple datastore queries.
@@ -1640,21 +1662,21 @@ def Get(self, limit, offset=0, **kwarg
Args:
limit: maximum number of values to return.
offset: offset requested -- if nonzero, this will override the offset in
the original query
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
A list of entities with at most "limit" entries (less if the query
completes before reading limit values).
"""
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
count = 1
result = []
- iterator = self.Run(rpc=rpc)
+ iterator = self.Run(config=config)
try:
for i in xrange(offset):
val = iterator.next()
except StopIteration:
@@ -1780,21 +1802,18 @@ def Run(self, **kwargs):
"""Return an iterable output with all results in order.
Merge sort the results. First create a list of iterators, then walk
though them and yield results in order.
"""
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
+ config = _Rpc2Config(config)
results = []
count = 1
log_level = logging.DEBUG - 1
for bound_query in self.__bound_queries:
logging.log(log_level, 'Running query #%i' % count)
- if rpc:
- rpc_clone = rpc.clone()
- else:
- rpc_clone = None
- results.append(bound_query.Run(rpc=rpc_clone))
+ results.append(bound_query.Run(config=config))
count += 1
def IterateResults(results):
"""Iterator function to return all results in sorted order.
@@ -1850,29 +1869,31 @@ def Count(self, limit=None, **kwargs):
efficient Get() function if a limit is given.
Args:
limit: maximum number of entries to count (for any result > limit, return
limit).
- rpc: datastore.RPC to use for this request.
+ config: Optional Configuration to use for this request.
Returns:
count of the number of entries returned.
"""
- rpc = GetRpcFromKwargs(kwargs)
+ config = _GetConfigFromKwargs(kwargs)
if limit is None:
count = 0
- for i in self.Run(rpc=rpc):
+ for _ in self.Run(config=config):
count += 1
return count
else:
- return len(self.Get(limit, rpc=rpc))
+ return len(self.Get(limit, config=config))
- def GetCompiledCursor(self):
+ def GetCursor(self):
raise AssertionError('No cursor available for a MultiQuery (queries '
'using "IN" or "!=" operators)')
- def GetCompiledQuery(self):
+
+ def _GetCompiledQuery(self):
+ """Internal only, do not use."""
raise AssertionError('No compilation available for a MultiQuery (queries '
'using "IN" or "!=" operators)')
def __setitem__(self, query_filter, value):
"""Add a new filter by setting it on all subqueries.
@@ -1932,257 +1953,12 @@ def __delitem__(self, query_filter):
raise KeyError(query_filter)
def __iter__(self):
return iter(self.__bound_queries)
-
-
-class Iterator(object):
- """An iterator over the results of a datastore query.
-
- Iterators are used to access the results of a Query. An iterator is
- obtained by building a Query, then calling Run() on it.
-
- Iterator implements Python's iterator protocol, so results can be accessed
- with the for and in statements:
-
- > it = Query('Person').Run()
- > for person in it:
- > print 'Hi, %s!' % person['name']
- """
- def __init__(self, query_result_pb, batch_size=None, rpc=None,
- query_request_pb=None):
- """Constructor.
-
- kwargs gets stored and passed on to Next calls made by this iterator.
- """
- self.__cursor = query_result_pb.cursor()
- self.__keys_only = query_result_pb.keys_only()
- self.__batch_size = batch_size
- self.__rpc = rpc
- self.__skipped_results = 0
-
- self.__results_since_prev = 0
- self.__prev_compiled_cursor = None
- self.__next_compiled_cursor = None
-
- if query_request_pb:
- self.__remaining_offset = query_request_pb.offset()
- else:
- self.__remaining_offset = 0
-
- if query_request_pb and query_result_pb.has_compiled_cursor():
- if query_request_pb.has_compiled_cursor():
- self.__next_compiled_cursor = query_request_pb.compiled_cursor()
- else:
- self.__next_compiled_cursor = datastore_pb.CompiledCursor()
- self.__buffer = self._ProcessQueryResult(query_result_pb)
- self.__results_since_prev = query_request_pb.offset()
- else:
- self.__buffer = self._ProcessQueryResult(query_result_pb)
-
- def _Get(self, count):
- """Gets the next count result(s) of the query.
-
- Not intended to be used by application developers. Use the python
- iterator protocol instead.
-
- This method uses _Next to returns the next entities or keys from the list of
- matching results. If the query specified a sort order, results are returned
- in that order. Otherwise, the order is undefined.
-
- The argument, count, specifies the number of results to return. However, the
- length of the returned list may be smaller than count. This is the case only
- if count is greater than the number of remaining results.
-
- The results are always returned as a list. If there are no results left,
- an empty list is returned.
-
- Args:
- # the number of results to return; must be >= 1
- count: int or long
-
- Returns:
- # a list of entities or keys
- [Entity or Key, ...]
- """
- entity_list = self._Next(count)
- while len(entity_list) < count and self.__more_results:
- entity_list += self._Next(count - len(entity_list))
- return entity_list;
-
- def _Next(self, count=None):
- """Returns the next batch of results.
-
- Not intended to be used by application developers. Use the python
- iterator protocol instead.
-
- Values are returned in the order they are recieved from the datastore.
-
- If there are values in the internal buffer they are returned, otherwise a
- single RPC is run in an attempt to fulfill the request.
-
- The optional argument, count, specifies the number of results to return.
- However, the length of the returned list may be smaller than count. This is
- the case if:
- - the local buffer has results and count is greater than the number of
- results in the buffer.
- - count is greater than the number of remaining results
- - the size of the remaining results exceeds the RPC buffer limit
- Use _Get to ensure all possible entities are retrieved.
-
- When count is None, if there are items in the local buffer, they are
- all returned, otherwise the datastore backend is allowed to decide how many
- entities to send.
-
- The internal buffer is also used by the next() method so it is best not to
- mix _Next() and next().
-
- The results are always returned as a list. If there are results left, at
- least one result will be returned in this list. If there are no results
- left, an empty list is returned.
-
- Args:
- # the number of results to return; must be >= 1
- count: int or long or None
-
- Returns:
- # a list of entities or keys
- [Entity or Key, ...]
- """
- if count is not None and (not isinstance(count, (int, long)) or count < 0):
- raise datastore_errors.BadArgumentError(
- 'Argument to _Next must be an int greater than or equal to 0; received '
- '%s (a %s)' % (count, typename(count)))
-
- if self.__buffer:
- if count is None:
- entity_list = self.__buffer
- self.__buffer = []
- elif count <= len(self.__buffer):
- entity_list = self.__buffer[:count]
- del self.__buffer[:count]
- else:
- entity_list = self.__buffer
- self.__buffer = []
- self.__results_since_prev += len(entity_list)
- return entity_list
-
-
- if not self.__more_results:
- return []
-
- req = datastore_pb.NextRequest()
- if self.__remaining_offset:
- req.set_offset(self.__remaining_offset)
- if count is not None:
- req.set_count(count)
- if self.__next_compiled_cursor:
- req.set_compile(True)
- req.mutable_cursor().CopyFrom(self.__cursor)
- try:
- rpc = self.__rpc
- if rpc:
- self.__rpc = rpc.clone()
-
- result = _MakeSyncCall('datastore_v3', 'Next', req,
- datastore_pb.QueryResult(), rpc)
- except apiproxy_errors.ApplicationError, err:
- raise _ToDatastoreError(err)
-
- new_batch = self._ProcessQueryResult(result)
- if not self.__has_advanced:
- self.__more_results = False
- return new_batch
-
- def _ProcessQueryResult(self, result):
- """Returns all results from datastore_pb.QueryResult and updates
- self.__more_results
-
- Not intended to be used by application developers. Use the python
- iterator protocol instead.
-
- The results are always returned as a list. If there are no results left,
- an empty list is returned.
-
- Args:
- # the instance of datastore_pb.QueryResult to be stored
- result: datastore_pb.QueryResult
-
- Returns:
- # a list of entities or keys
- [Entity or Key, ...]
- """
- if self.__next_compiled_cursor and result.has_compiled_cursor():
- self.__prev_compiled_cursor = self.__next_compiled_cursor
- self.__next_compiled_cursor = result.compiled_cursor()
- self.__results_since_prev = 0
-
- self.__more_results = result.more_results()
- if result.skipped_results():
- self.__has_advanced = True
- self.__skipped_results += result.skipped_results()
- self.__remaining_offset -= result.skipped_results()
- else:
- self.__has_advanced = result.result_size() > 0
-
- if self.__keys_only:
- return [Key._FromPb(e.key()) for e in result.result_list()]
- else:
- return [Entity._FromPb(e) for e in result.result_list()]
-
- def _SkippedResults(self):
- self.__PrepBuffer()
- return self.__skipped_results
-
- def GetCompiledCursor(self, query):
- if not self.__buffer:
- return self.__next_compiled_cursor
- elif not self.__results_since_prev:
- return self.__prev_compiled_cursor
- elif self.__prev_compiled_cursor:
- return Query._RunInternal(query._ToPb(limit=0,
- offset=self.__results_since_prev),
- rpc=self.__rpc)[0].GetCompiledCursor(query)
- else:
- return None
-
- def next(self):
- self.__PrepBuffer()
- try:
- result = self.__buffer.pop(0)
- except IndexError:
- raise StopIteration
- self.__results_since_prev += 1
- return result
-
- def __PrepBuffer(self):
- """Loads the next set of values into the local buffer if needed."""
- while not self.__buffer and self.__more_results:
- self.__buffer = self._Next(self.__batch_size)
-
- def __iter__(self): return self
-
-class _Transaction(object):
- """Encapsulates a transaction currently in progress.
-
- If we've sent a BeginTransaction call, then handle will be a
- datastore_pb.Transaction that holds the transaction handle.
-
- If we know the entity group for this transaction, it's stored in the
- entity_group attribute, which is set by RunInTransaction().
-
- modified_keys is a set containing the Keys of all entities modified (ie put
- or deleted) in this transaction. If an entity is modified more than once, a
- BadRequestError is raised.
- """
- def __init__(self):
- """Initializes modified_keys to the empty set."""
- self.handle = None
- self.entity_group = None
- self.modified_keys = None
- self.modified_keys = set()
+ GetCompiledCursor = GetCursor
+ GetCompiledQuery = _GetCompiledQuery
def RunInTransaction(function, *args, **kwargs):
"""Runs a function inside a datastore transaction.
@@ -2209,11 +1985,12 @@ def RunInTransactionCustomRetries(retrie
"""Runs a function inside a datastore transaction.
Runs the user-provided function inside a full-featured, ACID datastore
transaction. Every Put, Get, and Delete call in the function is made within
the transaction. All entities involved in these calls must belong to the
- same entity group. Queries are not supported.
+ same entity group. Queries are supported as long as they specify an
+ ancestor belonging to the same entity group.
The trailing arguments are passed to the function as positional arguments.
If the function returns a value, that value will be returned by
RunInTransaction. Otherwise, it will return None.
@@ -2258,11 +2035,11 @@ def RunInTransactionCustomRetries(retrie
- Durable. On commit, all writes are persisted to the datastore.
Nested transactions are not supported.
Args:
- # number of retries
+ # number of retries (not counting the initial try)
retries: integer
# a function to be run inside the transaction
function: callable
# positional arguments to pass to the function
args: variable number of any type
@@ -2272,200 +2049,91 @@ def RunInTransactionCustomRetries(retrie
Raises:
TransactionFailedError, if the transaction could not be committed.
"""
- if _CurrentTransactionKey():
- raise datastore_errors.BadRequestError(
- 'Nested transactions are not supported.')
-
if retries < 0:
raise datastore_errors.BadRequestError(
'Number of retries should be non-negative number.')
- tx_key = None
+ if IsInTransaction():
+ raise datastore_errors.BadRequestError(
+ 'Nested transactions are not supported.')
+ old_connection = _GetConnection()
+ for i in range(0, retries + 1):
+ new_connection = old_connection.new_transaction()
+ _SetConnection(new_connection)
try:
- tx_key = _NewTransactionKey()
- tx = _Transaction()
- _txes[tx_key] = tx
+ ok, result = _DoOneTry(new_connection, function, args, kwargs)
+ if ok:
+ return result
+ finally:
+ _SetConnection(old_connection)
- for i in range(0, retries + 1):
- tx.modified_keys.clear()
+ raise datastore_errors.TransactionFailedError(
+ 'The transaction could not be committed. Please try again.')
+
+
+def _DoOneTry(new_connection, function, args, kwargs):
+ """Helper to call a function in a transaction, once.
+
+ Args:
+ new_connection: The new, transactional, connection object.
+ function: The function to call.
+ args: Tuple of positional arguments.
+ kwargs: Dict of keyword arguments.
+ """
try:
result = function(*args, **kwargs)
+
except:
original_exception = sys.exc_info()
- if tx.handle:
try:
- _MakeSyncCall('datastore_v3', 'Rollback',
- tx.handle, api_base_pb.VoidProto())
- except:
- logging.info('Exception sending Rollback:\n' +
- traceback.format_exc())
+ new_connection.rollback()
+ except Exception:
+ logging.exception('Exception sending Rollback:')
type, value, trace = original_exception
- if type is datastore_errors.Rollback:
- return
+ if isinstance(value, datastore_errors.Rollback):
+ return True, None
else:
raise type, value, trace
- if tx.handle:
- try:
- _MakeSyncCall('datastore_v3', 'Commit',
- tx.handle, datastore_pb.CommitResponse())
- except apiproxy_errors.ApplicationError, err:
- if (err.application_error ==
- datastore_pb.Error.CONCURRENT_TRANSACTION):
- logging.warning('Transaction collision for entity group with '
- 'key %r. Retrying...', tx.entity_group)
- tx.handle = None
- tx.entity_group = None
- continue
else:
- raise _ToDatastoreError(err)
-
- return result
-
- raise datastore_errors.TransactionFailedError(
- 'The transaction could not be committed. Please try again.')
-
- finally:
- if tx_key in _txes:
- del _txes[tx_key]
- del tx_key
+ if new_connection.commit():
+ return True, result
+ else:
+ logging.warning('Transaction collision. Retrying... %s', '')
+ return False, None
def _MaybeSetupTransaction(request, keys):
- """Begins a transaction, if necessary, and populates it in the request.
-
- If we're currently inside a transaction, this records the entity group,
- checks that the keys are all in that entity group, creates the transaction
- PB, and sends the BeginTransaction. It then populates the transaction handle
- in the request.
+ """Begin a transaction, if necessary, and populate it in the request.
- Raises BadRequestError if the entity has a different entity group than the
- current transaction.
+ This API exists for internal backwards compatibility, primarily with
+ api/taskqueue/taskqueue.py.
Args:
- request: GetRequest, PutRequest, DeleteRequest, or Query
- keys: sequence of Keys
+ request: A protobuf with a mutable_transaction() method.
+ keys: Unused.
Returns:
- _Transaction if we're inside a transaction, otherwise None
+ A transaction if we're inside a transaction, otherwise None
"""
- assert isinstance(request, (datastore_pb.GetRequest, datastore_pb.PutRequest,
- datastore_pb.DeleteRequest, datastore_pb.Query,
- taskqueue_service_pb.TaskQueueAddRequest,
- )), request.__class__
- tx_key = None
-
- try:
- tx_key = _CurrentTransactionKey()
- if tx_key:
- tx = _txes[tx_key]
-
- groups = [k.entity_group() for k in keys]
- if tx.entity_group:
- expected_group = tx.entity_group
- elif groups:
- expected_group = groups[0]
- else:
- expected_group = None
-
- for group in groups:
- if (group != expected_group or
-
-
-
-
-
-
-
- (not group.has_id_or_name() and group is not expected_group)):
- raise _DifferentEntityGroupError(expected_group, group)
-
- if not tx.entity_group and group.has_id_or_name():
- tx.entity_group = group
-
- if not tx.handle:
- req = datastore_pb.BeginTransactionRequest()
- if keys:
- req.set_app(keys[0].app())
- else:
- assert isinstance(request, taskqueue_service_pb.TaskQueueAddRequest)
- req.set_app(os.environ['APPLICATION_ID'])
- assert req.app()
-
- tx.handle = _MakeSyncCall('datastore_v3', 'BeginTransaction',
- req, datastore_pb.Transaction())
-
- if not tx.handle.app():
- tx.handle.set_app(req.app())
-
- request.mutable_transaction().CopyFrom(tx.handle)
-
- return tx
-
- finally:
- del tx_key
+ return _GetConnection()._set_request_transaction(request)
def IsInTransaction():
"""Determine whether already running in transaction.
Returns:
True if already running in transaction, else False.
"""
- return bool(_CurrentTransactionKey())
-
-
-def _DifferentEntityGroupError(a, b):
- """Raises a BadRequestError that says the given entity groups are different.
-
- Includes the two entity groups in the message, formatted more clearly and
- concisely than repr(Key).
-
- Args:
- a, b are both Keys that represent entity groups.
- """
- def id_or_name(key):
- if key.name():
- return 'name=%r' % key.name()
- else:
- return 'id=%r' % key.id()
-
- raise datastore_errors.BadRequestError(
- 'Cannot operate on different entity groups in a transaction: '
- '(kind=%r, %s) and (kind=%r, %s).' % (a.kind(), id_or_name(a),
- b.kind(), id_or_name(b)))
-
-
-def _FindTransactionFrameInStack():
- """Walks the stack to find a RunInTransaction() call.
-
- Returns:
- # this is the RunInTransactionCustomRetries() frame record, if found
- frame record or None
- """
- frame = sys._getframe()
- filename = frame.f_code.co_filename
-
- frame = frame.f_back.f_back
- while frame:
- if (frame.f_code.co_filename == filename and
- frame.f_code.co_name == 'RunInTransactionCustomRetries'):
- return frame
- frame = frame.f_back
-
- return None
-
-_CurrentTransactionKey = _FindTransactionFrameInStack
-
-_NewTransactionKey = sys._getframe
+ return isinstance(_GetConnection(), datastore_rpc.TransactionalConnection)
def _GetCompleteKeyOrError(arg):
"""Expects an Entity or a Key, and returns the corresponding Key. Raises
BadArgumentError or BadKeyError if arg is a different type or is incomplete.
@@ -2539,46 +2207,31 @@ def _AddOrAppend(dictionary, key, value)
dictionary[key] = [existing_value, value]
else:
dictionary[key] = value
-def _ToDatastoreError(err):
- """Converts an apiproxy.ApplicationError to an error in datastore_errors.
-
- Args:
- err: apiproxy.ApplicationError
+class Iterator(datastore_query.ResultsIterator):
+ """Thin wrapper of datastore_query.ResultsIterator.
- Returns:
- a subclass of datastore_errors.Error
+ Deprecated, do not use, only for backwards compatability.
"""
- return _DatastoreExceptionFromErrorCodeAndDetail(err.application_error,
- err.error_detail)
-
+ def _Next(self, count=None):
+ if count is None:
+ count = 20
+ result = []
+ for r in self:
+ if len(result) >= count:
+ break;
+ result.append(r)
+ return result
-def _DatastoreExceptionFromErrorCodeAndDetail(error, detail):
- """Converts a datastore_pb.Error into a datastore_errors.Error.
+ def GetCompiledCursor(self, query):
+ return self.cursor()
- Args:
- error: A member of the datastore_pb.Error enumeration.
- detail: A string providing extra details about the error.
+ _Get = _Next
- Returns:
- A subclass of datastore_errors.Error.
- """
- exception_class = {
- datastore_pb.Error.BAD_REQUEST: datastore_errors.BadRequestError,
- datastore_pb.Error.CONCURRENT_TRANSACTION:
- datastore_errors.TransactionFailedError,
- datastore_pb.Error.INTERNAL_ERROR: datastore_errors.InternalError,
- datastore_pb.Error.NEED_INDEX: datastore_errors.NeedIndexError,
- datastore_pb.Error.TIMEOUT: datastore_errors.Timeout,
- datastore_pb.Error.BIGTABLE_ERROR: datastore_errors.Timeout,
- datastore_pb.Error.COMMITTED_BUT_STILL_APPLYING:
- datastore_errors.CommittedButStillApplying,
- datastore_pb.Error.CAPABILITY_DISABLED:
- apiproxy_errors.CapabilityDisabledError,
- }.get(error, datastore_errors.Error)
- if detail is None:
- return exception_class()
- else:
- return exception_class(detail)
+DatastoreRPC = apiproxy_stub_map.UserRPC
+GetRpcFromKwargs = _GetConfigFromKwargs
+_CurrentTransactionKey = IsInTransaction
+_ToDatastoreError = datastore_rpc._ToDatastoreError
+_DatastoreExceptionFromErrorCodeAndDetail = datastore_rpc._DatastoreExceptionFromErrorCodeAndDetail
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/datastore_types.py google-appengine-1.4.0-prerelease/google/appengine/api/datastore_types.py
--- local/google_appengine/google/appengine/api/datastore_types.py 2010-10-14 21:40:44.034837932 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/datastore_types.py 2010-11-18 21:44:32.000000000 +0000
@@ -677,11 +677,11 @@ def __hash__(self):
Raises BadValueError if tag is not a string or subtype.
"""
TERM = 'user-tag'
def __init__(self, tag):
- super(Category, self).__init__(self, tag)
+ super(Category, self).__init__()
ValidateString(tag, 'tag')
def ToXml(self):
return u'<category term="%s" label=%s />' % (Category.TERM,
saxutils.quoteattr(self))
@@ -699,11 +699,11 @@ def ToXml(self):
http://www.atomenabled.org/developers/syndication/#link
Raises BadValueError if link is not a fully qualified, well-formed URL.
"""
def __init__(self, link):
- super(Link, self).__init__(self, link)
+ super(Link, self).__init__()
ValidateString(link, 'link', max_len=_MAX_LINK_PROPERTY_LENGTH)
scheme, domain, path, params, query, fragment = urlparse.urlparse(link)
if (not scheme or (scheme != 'file' and not domain) or
(scheme == 'file' and not path)):
@@ -722,11 +722,11 @@ def ToXml(self):
http://code.google.com/apis/gdata/common-elements.html#gdEmail
Raises BadValueError if email is not a valid email address.
"""
def __init__(self, email):
- super(Email, self).__init__(self, email)
+ super(Email, self).__init__()
ValidateString(email, 'email')
def ToXml(self):
return u'<gd:email address=%s />' % saxutils.quoteattr(self)
@@ -913,11 +913,11 @@ def __len__(self):
http://code.google.com/apis/gdata/common-elements.html#gdPhoneNumber
Raises BadValueError if phone is not a string or subtype.
"""
def __init__(self, phone):
- super(PhoneNumber, self).__init__(self, phone)
+ super(PhoneNumber, self).__init__()
ValidateString(phone, 'phone')
def ToXml(self):
return u'<gd:phoneNumber>%s</gd:phoneNumber>' % saxutils.escape(self)
@@ -931,11 +931,11 @@ def ToXml(self):
http://code.google.com/apis/gdata/common-elements.html#gdPostalAddress
Raises BadValueError if address is not a string or subtype.
"""
def __init__(self, address):
- super(PostalAddress, self).__init__(self, address)
+ super(PostalAddress, self).__init__()
ValidateString(address, 'address')
def ToXml(self):
return u'<gd:postalAddress>%s</gd:postalAddress>' % saxutils.escape(self)
@@ -953,11 +953,11 @@ def ToXml(self):
"""
MIN = 0
MAX = 100
def __init__(self, rating):
- super(Rating, self).__init__(self, rating)
+ super(Rating, self).__init__()
if isinstance(rating, float) or isinstance(rating, complex):
raise datastore_errors.BadValueError(
'Expected int or long; received %s (a %s).' %
(rating, typename(rating)))
@@ -1503,11 +1503,11 @@ def ToPropertyPb(name, values):
values: The values for this property, either a single one or a list of them.
All values must be a supported type. Lists of values must all be of the
same type.
Returns:
- A list of entity_pb.PropertyValue instances.
+ A list of entity_pb.Property instances.
"""
encoded_name = name.encode('utf-8')
values_type = type(values)
if values_type is list:
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/images/__init__.py google-appengine-1.4.0-prerelease/google/appengine/api/images/__init__.py
--- local/google_appengine/google/appengine/api/images/__init__.py 2010-10-14 21:40:43.605837793 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/images/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -895,10 +895,12 @@ def histogram(image_data):
"""
image = Image(image_data)
return image.histogram()
+IMG_SERVING_SIZES_LIMIT = 1600
+
IMG_SERVING_SIZES = [
32, 48, 64, 72, 80, 90, 94, 104, 110, 120, 128, 144,
150, 160, 200, 220, 288, 320, 400, 512, 576, 640, 720,
800, 912, 1024, 1152, 1280, 1440, 1600]
@@ -925,24 +927,12 @@ def get_serving_url(blob_key,
To get a 32 pixel cropped version simply append "=s32-c":
"http://lh3.ggpht.com/SomeCharactersGoesHere=s32-c"
- Available sizes for resize are:
- (e.g. "=sX" where X is one of the following values)
-
- 0, 32, 48, 64, 72, 80, 90, 94, 104, 110, 120, 128, 144,
- 150, 160, 200, 220, 288, 320, 400, 512, 576, 640, 720,
- 800, 912, 1024, 1152, 1280, 1440, 1600
-
- Available sizes for crop are:
- (e.g. "=sX-c" where X is one of the following values)
-
- 32, 48, 64, 72, 80, 104, 136, 144, 150, 160
-
- These values are also available as IMG_SERVING_SIZES and
- IMG_SERVING_CROP_SIZES integer lists.
+ Available sizes are any interger in the range [0, 1600] and is available as
+ IMG_SERVING_SIZES_LIMIT.
Args:
size: int, size of resulting images
crop: bool, True requests a cropped image, False a resized one.
@@ -958,14 +948,11 @@ def get_serving_url(blob_key,
raise BlobKeyRequiredError("A Blobkey is required for this operation.")
if crop and not size:
raise BadRequestError("Size should be set for crop operation")
- if size and crop and not size in IMG_SERVING_CROP_SIZES:
- raise UnsupportedSizeError("Unsupported crop size")
-
- if size and not crop and not size in IMG_SERVING_SIZES:
+ if size and (size > IMG_SERVING_SIZES_LIMIT or size < 0):
raise UnsupportedSizeError("Unsupported size")
request = images_service_pb.ImagesGetUrlBaseRequest()
response = images_service_pb.ImagesGetUrlBaseResponse()
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/labs/taskqueue/__init__.py google-appengine-1.4.0-prerelease/google/appengine/api/labs/taskqueue/__init__.py
--- local/google_appengine/google/appengine/api/labs/taskqueue/__init__.py 2009-06-20 20:51:38.000000000 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/labs/taskqueue/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -13,8 +13,60 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
-"""Task Queue API module."""
+"""Shim module so that the old labs import path still works."""
-from taskqueue import *
+
+
+__all__ = [
+
+ 'BadTaskStateError', 'BadTransactionState', 'BadTransactionStateError',
+ 'DatastoreError', 'DuplicateTaskNameError', 'Error', 'InternalError',
+ 'InvalidQueueError', 'InvalidQueueNameError', 'InvalidTaskError',
+ 'InvalidTaskNameError', 'InvalidUrlError', 'PermissionDeniedError',
+ 'TaskAlreadyExistsError', 'TaskTooLargeError', 'TombstonedTaskError',
+ 'TooManyTasksError', 'TransientError', 'UnknownQueueError',
+
+ 'MAX_QUEUE_NAME_LENGTH', 'MAX_TASK_NAME_LENGTH', 'MAX_TASK_SIZE_BYTES',
+ 'MAX_URL_LENGTH',
+
+ 'Queue', 'Task', 'add']
+
+
+import os
+import sys
+import warnings
+
+from google.appengine.api.taskqueue import *
+
+
+if os.environ.get('DATACENTER', None) is None:
+ warnings.warn('google.appengine.api.labs.taskqueue is deprecated, please use '
+ 'google.appengine.api.taskqueue', DeprecationWarning,
+ stacklevel=2)
+
+
+def _map_module(module_name):
+ """Map a module from the new path to the labs path.
+
+ Args:
+ module_name: Name of the module to be mapped.
+
+ Raises:
+ ImportError: If the specified module we are mapping from does not exist.
+
+ Returns:
+ The module object of the module that was mapped.
+ """
+ labs_module_name = '%s.%s' % (__name__, module_name)
+ module_prefix = '.'.join(__name__.split('.')[:2])
+ new_module_name = '%s.api.taskqueue.%s' % (module_prefix, module_name)
+
+ __import__(new_module_name)
+ sys.modules[labs_module_name] = sys.modules[new_module_name]
+ return sys.modules[labs_module_name]
+
+taskqueue = _map_module('taskqueue')
+taskqueue_service_pb = _map_module('taskqueue_service_pb')
+taskqueue_stub = _map_module('taskqueue_stub')
Only in local/google_appengine/google/appengine/api/labs/taskqueue: taskqueue.py
Only in local/google_appengine/google/appengine/api/labs/taskqueue: taskqueue_service_pb.py
Only in local/google_appengine/google/appengine/api/labs/taskqueue: taskqueue_stub.py
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/matcher/matcher_stub.py google-appengine-1.4.0-prerelease/google/appengine/api/matcher/matcher_stub.py
--- local/google_appengine/google/appengine/api/matcher/matcher_stub.py 2010-10-14 21:40:43.299836605 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/matcher/matcher_stub.py 2010-11-18 21:44:32.000000000 +0000
@@ -31,15 +31,11 @@
import cPickle as pickle
from collections import deque
from google.appengine.api import apiproxy_stub
-
-try:
- from google.appengine.api.labs.taskqueue import taskqueue_service_pb
-except ImportError:
- from google.appengine.api.taskqueue import taskqueue_service_pb
+from google.appengine.api.taskqueue import taskqueue_service_pb
class _TrueExpr(object):
"""Trivially true callable. Should generally use _EMPTY singleton."""
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/api/queueinfo.py google-appengine-1.4.0-prerelease/google/appengine/api/queueinfo.py
--- local/google_appengine/google/appengine/api/queueinfo.py 2010-10-14 21:40:44.033838002 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/api/queueinfo.py 2010-11-18 21:44:32.000000000 +0000
@@ -18,11 +18,11 @@
"""QueueInfo tools.
A library for working with QueueInfo records, describing task queue entries
for an application. Supports loading the records from queue.yaml.
-A queue has two required parameters and one optional one. The required
+A queue has two required parameters and various optional ones. The required
parameters are 'name' (must be unique for an appid) and 'rate' (the rate
at which jobs in the queue are run). There is an optional parameter
'bucket_size' that will allow tokens to be 'saved up' (for more on the
algorithm, see http://en.wikipedia.org/wiki/Token_Bucket). rate is expressed
as number/unit, with number being an int or a float, and unit being one of
@@ -40,10 +40,60 @@
If this queue had been idle for a while before some jobs were submitted to it,
the first 10 jobs submitted would be run immediately, then subsequent ones
would be run once every 40s or so. The limit of 2000 per day would still apply.
+Another optional parameter is 'max_concurrent_requests', which pertains to the
+requests being made by the queue. It specifies the maximum number of requests
+that may be in-flight at any one time. An example:
+
+queue:
+- name: server_queue
+ rate: 50/s
+ max_concurrent_requests: 5
+
+A queue may also optionally specify retry_parameters.
+
+ retry_parameters:
+ task_retry_limit: 100
+ task_age_limit: 1d
+ min_backoff_seconds: 0.1
+ max_backoff_seconds: 3600
+ max_doublings: 10
+
+Each task in the queue that fails during execution will be retried using these
+parameters. All these fields are optional.
+
+task_retry_limit: A non-negative integer. Tasks will be retried a maximum of
+ task_retry_limit times before failing permanently. If task_age_limit is also
+ specified, both task_retry_limit and task_age_limit must be exceeded before a
+ task fails permanently.
+
+task_age_limit: A non-negative floating point number followed by a suffix s
+ (seconds), m (minutes), h (hours) or d (days). If the time since a task was
+ first tried exceeds task_age_limit, it will fail permanently. If
+ task_retry_limit is also specified, both task_retry_limit and task_age_limit
+ must be exceeded before a task fails permanently.
+
+min_backoff_seconds: A non-negative floating point number. This is the minimum
+ interval after the first failure and the first retry of a task. If
+ max_backoff_seconds is also specified, min_backoff_seconds must not be greater
+ than max_backoff_seconds.
+
+max_backoff_seconds: A non-negative floating point number. This is the maximum
+ allowed interval between successive retries of a failed task. If
+ min_backoff_seconds is also specified, min_backoff_seconds must not be greater
+ than max_backoff_seconds.
+
+max_doublings: A non-negative integer. On successive failures, the retry backoff
+ interval will be successively doubled up to max_doublings times, starting at
+ min_backoff_seconds and not exceeding max_backoff_seconds. For retries after
+ max_doublings, the retry backoff will increase by the value of the backoff
+ when doubling ceased. e.g. for min_backoff_seconds of 1 ,max_doublings of 5,
+ we have successive retry backoffs of 1, 2, 4, 8, 16, 32, 64, 96, 128, ...
+ not exceeding max_backoff_seconds.
+
An app's queues are also subject to storage quota limits for their stored tasks,
i.e. those tasks that have been added to queues but not yet executed. This quota
is part of their total storage quota (including datastore and blobstore quota).
We allow an app to override the default portion of this quota available for
taskqueue storage (100M) with a top level field "total_storage_limit".
@@ -71,31 +121,32 @@
QUEUE = 'queue'
NAME = 'name'
RATE = 'rate'
BUCKET_SIZE = 'bucket_size'
+MAX_CONCURRENT_REQUESTS = 'max_concurrent_requests'
TOTAL_STORAGE_LIMIT = 'total_storage_limit'
BYTE_SUFFIXES = 'BKMGT'
RETRY_PARAMETERS = 'retry_parameters'
TASK_RETRY_LIMIT = 'task_retry_limit'
TASK_AGE_LIMIT = 'task_age_limit'
-MIN_BACKOFF_SEC = 'min_backoff_sec'
-MAX_BACKOFF_SEC = 'max_backoff_sec'
+MIN_BACKOFF_SECONDS = 'min_backoff_seconds'
+MAX_BACKOFF_SECONDS = 'max_backoff_seconds'
MAX_DOUBLINGS = 'max_doublings'
class MalformedQueueConfiguration(Exception):
"""Configuration file for Task Queue is malformed."""
class RetryParameters(validation.Validated):
"""Retry parameters for a single task queue."""
ATTRIBUTES = {
TASK_RETRY_LIMIT: validation.Optional(validation.TYPE_INT),
TASK_AGE_LIMIT: validation.Optional(_TASK_AGE_LIMIT_REGEX),
- MIN_BACKOFF_SEC: validation.Optional(validation.TYPE_INT),
- MAX_BACKOFF_SEC: validation.Optional(validation.TYPE_INT),
+ MIN_BACKOFF_SECONDS: validation.Optional(validation.TYPE_FLOAT),
+ MAX_BACKOFF_SECONDS: validation.Optional(validation.TYPE_FLOAT),
MAX_DOUBLINGS: validation.Optional(validation.TYPE_INT),
}
@@ -103,10 +154,11 @@
"""A queue entry describes a single task queue."""
ATTRIBUTES = {
NAME: _NAME_REGEX,
RATE: _RATE_REGEX,
BUCKET_SIZE: validation.Optional(validation.TYPE_INT),
+ MAX_CONCURRENT_REQUESTS: validation.Optional(validation.TYPE_INT),
RETRY_PARAMETERS: validation.Optional(RetryParameters),
}
class QueueInfoExternal(validation.Validated):
Only in google-appengine-1.4.0-prerelease/google/appengine/api: taskqueue
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/datastore/datastore_pb.py google-appengine-1.4.0-prerelease/google/appengine/datastore/datastore_pb.py
--- local/google_appengine/google/appengine/datastore/datastore_pb.py 2010-10-14 21:40:43.208837441 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/datastore/datastore_pb.py 2010-11-18 21:44:32.000000000 +0000
@@ -85,25 +85,28 @@ def IsInitialized(self, debug_strs=Non
initialized = 1
if (not self.has_handle_):
initialized = 0
if debug_strs is not None:
debug_strs.append('Required field: handle not set.')
+ if (not self.has_app_):
+ initialized = 0
+ if debug_strs is not None:
+ debug_strs.append('Required field: app not set.')
return initialized
def ByteSize(self):
n = 0
- if (self.has_app_): n += 1 + self.lengthString(len(self.app_))
- return n + 9
+ n += self.lengthString(len(self.app_))
+ return n + 10
def Clear(self):
self.clear_handle()
self.clear_app()
def OutputUnchecked(self, out):
out.putVarInt32(9)
out.put64(self.handle_)
- if (self.has_app_):
out.putVarInt32(18)
out.putPrefixedString(self.app_)
def TryMerge(self, d):
while d.avail() > 0:
@@ -431,10 +434,12 @@ def Hint_Name(cls, x): return cls._Hin
distinct_ = 0
has_compile_ = 0
compile_ = 0
has_failover_ms_ = 0
failover_ms_ = 0
+ has_strong_ = 0
+ strong_ = 0
def __init__(self, contents=None):
self.filter_ = []
self.order_ = []
self.composite_index_ = []
@@ -728,10 +733,23 @@ def clear_failover_ms(self):
self.has_failover_ms_ = 0
self.failover_ms_ = 0
def has_failover_ms(self): return self.has_failover_ms_
+ def strong(self): return self.strong_
+
+ def set_strong(self, x):
+ self.has_strong_ = 1
+ self.strong_ = x
+
+ def clear_strong(self):
+ if self.has_strong_:
+ self.has_strong_ = 0
+ self.strong_ = 0
+
+ def has_strong(self): return self.has_strong_
+
def MergeFrom(self, x):
assert x is not self
if (x.has_app()): self.set_app(x.app())
if (x.has_name_space()): self.set_name_space(x.name_space())
@@ -751,10 +769,11 @@ def MergeFrom(self, x):
if (x.has_keys_only()): self.set_keys_only(x.keys_only())
if (x.has_transaction()): self.mutable_transaction().MergeFrom(x.transaction())
if (x.has_distinct()): self.set_distinct(x.distinct())
if (x.has_compile()): self.set_compile(x.compile())
if (x.has_failover_ms()): self.set_failover_ms(x.failover_ms())
+ if (x.has_strong()): self.set_strong(x.strong())
def Equals(self, x):
if x is self: return 1
if self.has_app_ != x.has_app_: return 0
if self.has_app_ and self.app_ != x.app_: return 0
@@ -797,10 +816,12 @@ def Equals(self, x):
if self.has_distinct_ and self.distinct_ != x.distinct_: return 0
if self.has_compile_ != x.has_compile_: return 0
if self.has_compile_ and self.compile_ != x.compile_: return 0
if self.has_failover_ms_ != x.has_failover_ms_: return 0
if self.has_failover_ms_ and self.failover_ms_ != x.failover_ms_: return 0
+ if self.has_strong_ != x.has_strong_: return 0
+ if self.has_strong_ and self.strong_ != x.strong_: return 0
return 1
def IsInitialized(self, debug_strs=None):
initialized = 1
if (not self.has_app_):
@@ -842,10 +863,11 @@ def ByteSize(self):
if (self.has_keys_only_): n += 3
if (self.has_transaction_): n += 2 + self.lengthString(self.transaction_.ByteSize())
if (self.has_distinct_): n += 3
if (self.has_compile_): n += 3
if (self.has_failover_ms_): n += 2 + self.lengthVarInt64(self.failover_ms_)
+ if (self.has_strong_): n += 3
return n + 1
def Clear(self):
self.clear_app()
self.clear_name_space()
@@ -865,10 +887,11 @@ def Clear(self):
self.clear_keys_only()
self.clear_transaction()
self.clear_distinct()
self.clear_compile()
self.clear_failover_ms()
+ self.clear_strong()
def OutputUnchecked(self, out):
out.putVarInt32(10)
out.putPrefixedString(self.app_)
if (self.has_kind_):
@@ -933,10 +956,13 @@ def OutputUnchecked(self, out):
self.compiled_cursor_.OutputUnchecked(out)
if (self.has_end_compiled_cursor_):
out.putVarInt32(250)
out.putVarInt32(self.end_compiled_cursor_.ByteSize())
self.end_compiled_cursor_.OutputUnchecked(out)
+ if (self.has_strong_):
+ out.putVarInt32(256)
+ out.putBoolean(self.strong_)
def TryMerge(self, d):
while d.avail() > 0:
tt = d.getVarInt32()
if tt == 10:
@@ -1012,10 +1038,13 @@ def TryMerge(self, d):
length = d.getVarInt32()
tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
d.skip(length)
self.mutable_end_compiled_cursor().TryMerge(tmp)
continue
+ if tt == 256:
+ self.set_strong(d.getBoolean())
+ continue
if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
d.skipData(tt)
def __str__(self, prefix="", printElemNumber=0):
@@ -1071,10 +1100,11 @@ def __str__(self, prefix="", printElem
res+=self.transaction_.__str__(prefix + " ", printElemNumber)
res+=prefix+">\n"
if self.has_distinct_: res+=prefix+("distinct: %s\n" % self.DebugFormatBool(self.distinct_))
if self.has_compile_: res+=prefix+("compile: %s\n" % self.DebugFormatBool(self.compile_))
if self.has_failover_ms_: res+=prefix+("failover_ms: %s\n" % self.DebugFormatInt64(self.failover_ms_))
+ if self.has_strong_: res+=prefix+("strong: %s\n" % self.DebugFormatBool(self.strong_))
return res
def _BuildTagLookupTable(sparse, maxtag, default=None):
return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
@@ -1101,10 +1131,11 @@ def _BuildTagLookupTable(sparse, maxta
kkeys_only = 21
ktransaction = 22
kdistinct = 24
kcompile = 25
kfailover_ms = 26
+ kstrong = 32
_TEXT = _BuildTagLookupTable({
0: "ErrorCode",
1: "app",
3: "kind",
@@ -1128,11 +1159,12 @@ def _BuildTagLookupTable(sparse, maxta
25: "compile",
26: "failover_ms",
29: "name_space",
30: "compiled_cursor",
31: "end_compiled_cursor",
- }, 31)
+ 32: "strong",
+ }, 32)
_TYPES = _BuildTagLookupTable({
0: ProtocolBuffer.Encoder.NUMERIC,
1: ProtocolBuffer.Encoder.STRING,
3: ProtocolBuffer.Encoder.STRING,
@@ -1156,11 +1188,12 @@ def _BuildTagLookupTable(sparse, maxta
25: ProtocolBuffer.Encoder.NUMERIC,
26: ProtocolBuffer.Encoder.NUMERIC,
29: ProtocolBuffer.Encoder.STRING,
30: ProtocolBuffer.Encoder.STRING,
31: ProtocolBuffer.Encoder.STRING,
- }, 31, ProtocolBuffer.Encoder.MAX_TYPE)
+ 32: ProtocolBuffer.Encoder.NUMERIC,
+ }, 32, ProtocolBuffer.Encoder.MAX_TYPE)
_STYLE = """"""
_STYLE_CONTENT_TYPE = """"""
class CompiledQuery_PrimaryScan(ProtocolBuffer.ProtocolMessage):
has_index_name_ = 0
@@ -5650,22 +5683,25 @@ def Equals(self, x):
if self.has_app_ and self.app_ != x.app_: return 0
return 1
def IsInitialized(self, debug_strs=None):
initialized = 1
+ if (not self.has_app_):
+ initialized = 0
+ if debug_strs is not None:
+ debug_strs.append('Required field: app not set.')
return initialized
def ByteSize(self):
n = 0
- if (self.has_app_): n += 1 + self.lengthString(len(self.app_))
- return n + 0
+ n += self.lengthString(len(self.app_))
+ return n + 1
def Clear(self):
self.clear_app()
def OutputUnchecked(self, out):
- if (self.has_app_):
out.putVarInt32(10)
out.putPrefixedString(self.app_)
def TryMerge(self, d):
while d.avail() > 0:
Only in google-appengine-1.4.0-prerelease/google/appengine/datastore: datastore_query.py
Only in google-appengine-1.4.0-prerelease/google/appengine/datastore: datastore_rpc.py
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/datastore/datastore_sqlite_stub.py google-appengine-1.4.0-prerelease/google/appengine/datastore/datastore_sqlite_stub.py
--- local/google_appengine/google/appengine/datastore/datastore_sqlite_stub.py 2010-10-14 21:40:43.206837581 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/datastore/datastore_sqlite_stub.py 2010-11-18 21:44:32.000000000 +0000
@@ -39,10 +39,11 @@
from google.appengine.api import api_base_pb
from google.appengine.api import apiproxy_stub
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
+from google.appengine.api.taskqueue import taskqueue_service_pb
from google.appengine.datastore import datastore_index
from google.appengine.datastore import datastore_pb
from google.appengine.datastore import datastore_stub_util
from google.appengine.datastore import sortable_pb_encoder
from google.appengine.runtime import apiproxy_errors
@@ -50,17 +51,10 @@
try:
import pysqlite2.dbapi2 as sqlite3
except ImportError:
import sqlite3
-try:
- __import__('google.appengine.api.labs.taskqueue.taskqueue_service_pb')
- taskqueue_service_pb = sys.modules.get(
- 'google.appengine.api.labs.taskqueue.taskqueue_service_pb')
-except ImportError:
- from google.appengine.api.taskqueue import taskqueue_service_pb
-
import __builtin__
buffer = __builtin__.buffer
@@ -68,16 +62,10 @@
datastore_pb.Query.__hash__ = lambda self: hash(self.Encode())
datastore_pb.Transaction.__hash__ = lambda self: hash(self.Encode())
datastore_pb.Cursor.__hash__ = lambda self: hash(self.Encode())
-_MAXIMUM_RESULTS = 1000
-
-
-_MAX_QUERY_OFFSET = 1000
-
-
_MAX_QUERY_COMPONENTS = 63
_BATCH_SIZE = 20
@@ -92,10 +80,13 @@
datastore_pb.Query_Filter.LESS_THAN: '<',
datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL: '<=',
datastore_pb.Query_Filter.EQUAL: '=',
datastore_pb.Query_Filter.GREATER_THAN: '>',
datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL: '>=',
+
+
+ '!=': '!=',
}
_ORDER_MAP = {
datastore_pb.Query_Order.ASCENDING: 'ASC',
@@ -157,11 +148,11 @@ def ReferencePropertyToReference(refprop
for pathelem in refprop.pathelement_list():
ref.mutable_path().add_element().CopyFrom(pathelem)
return ref
-class QueryCursor(object):
+class QueryCursor(datastore_stub_util.BaseCursor):
"""Encapsulates a database cursor and provides methods to fetch results."""
def __init__(self, query, db_cursor):
"""Constructor.
@@ -169,19 +160,24 @@ def __init__(self, query, db_cursor):
query: A Query PB.
db_cursor: An SQLite cursor returning n+2 columns. The first 2 columns
must be the path of the entity and the entity itself, while the
remaining columns must be the sort columns for the query.
"""
+ super(QueryCursor, self).__init__(query.app())
self.__query = query
self.app = query.app()
self.__cursor = db_cursor
self.__seen = set()
self.__position = ('', '')
self.__next_result = (None, None)
+ if (query.has_compiled_cursor() and
+ query.compiled_cursor().position_size()):
+ self.ResumeFromCompiledCursor(query.compiled_cursor())
+
if query.has_limit():
self.limit = query.limit() + query.offset()
else:
self.limit = None
@@ -206,10 +202,11 @@ def _EncodeCompiledCursor(self, cc):
"""Encodes the current position in the query as a compiled cursor.
Args:
cc: The compiled cursor to fill out.
"""
+ if self.__position[0]:
position = cc.add_position()
position.set_start_key(self.__position[0])
def _GetResult(self):
"""Returns the next result from the result set, without deduplication.
@@ -226,12 +223,13 @@ def _GetResult(self):
if not row:
self.__cursor = None
return None, None
path, data, position_parts = str(row[0]), row[1], row[2:]
position = ''.join(str(x) for x in position_parts)
- if (self.__query.has_end_compiled_cursor() and position >
- self.__query.end_compiled_cursor().position(0).start_key()):
+ if self.__query.has_end_compiled_cursor() and (
+ not self.__query.end_compiled_cursor().position_list() or
+ position > self.__query.end_compiled_cursor().position(0).start_key()):
self.__cursor = None
return None, None
self.__position = (self.__position[0], position)
return path, data
@@ -243,23 +241,28 @@ def _Next(self):
A datastore_pb.EntityProto instance.
"""
if self._HasNext():
self.__seen.add(self.__next_result[0])
entity = entity_pb.EntityProto(self.__next_result[1])
+ datastore_stub_util.PrepareSpecialPropertiesForLoad(entity)
self.__next_result = None, None
return entity
return None
def _HasNext(self):
- """Prefetches the next result and returns true if successful
+ """Prefetches the next result and returns true if successful.
Returns:
A boolean that indicates if there are more results.
"""
while self.__cursor and (
not self.__next_result[0] or self.__next_result[0] in self.__seen):
self.__next_result = self._GetResult()
+
+ if self.limit is not None and len(self.__seen) >= self.limit:
+ return False
+
if self.__next_result[0]:
return True
return False
def Skip(self, count):
@@ -291,117 +294,155 @@ def ResumeFromCompiledCursor(self, cc)
return
while self.__position[1] <= target_position and self.__cursor:
self.__next_result = self._GetResult()
- def PopulateQueryResult(self, count, offset, result):
+ def PopulateQueryResult(self, result, count, offset):
"""Populates a QueryResult PB with results from the cursor.
Args:
+ result: out: A query_result PB.
count: The number of results to retrieve.
offset: The number of results to skip
- result: out: A query_result PB.
"""
- limited_offset = min(offset, _MAX_QUERY_OFFSET)
+ limited_offset = min(offset, datastore_stub_util._MAX_QUERY_OFFSET)
if limited_offset:
result.set_skipped_results(self.Skip(limited_offset))
if offset == limited_offset:
- if count > _MAXIMUM_RESULTS:
- count = _MAXIMUM_RESULTS
+ if count > datastore_stub_util._MAXIMUM_RESULTS:
+ count = datastore_stub_util._MAXIMUM_RESULTS
result_list = result.result_list()
while len(result_list) < count:
- if self.limit is not None and len(self.__seen) >= self.limit:
- break
entity = self._Next()
if entity is None:
break
result_list.append(entity)
result.set_keys_only(self.__query.keys_only())
result.set_more_results(self._HasNext())
+ self.PopulateCursor(result.mutable_cursor())
self._EncodeCompiledCursor(result.mutable_compiled_cursor())
-class ListQueryCursor(object):
- """Encapsulates a list of entities in a datastore cursor."""
-
- def __init__(self, query, entities):
- """Constructor.
+def MakeEntityForQuery(query, *path):
+ """Make an entity to be returned by a pseudo-kind query.
Args:
- query: A Query PB.
- entities: A list of entities that are the result of some query
+ query: the query which will return the entity.
+ path: pairs of type/name-or-id values specifying the entity's key
+ Returns:
+ An entity_pb.EntityProto with app and namespace as in query and the key
+ specified by path.
"""
- self.__query = query
- self.app = query.app()
- self.__entities = entities
+ pseudo_pb = entity_pb.EntityProto()
+ pseudo_pb.mutable_entity_group()
+ pseudo_pk = pseudo_pb.mutable_key()
+ pseudo_pk.set_app(query.app())
+ if query.has_name_space():
+ pseudo_pk.set_name_space(query.name_space())
- if query.has_limit():
- self.__entities = self.__entities[:query.limit() + query.offset()]
+ for i in xrange(0, len(path), 2):
+ pseudo_pe = pseudo_pk.mutable_path().add_element()
+ pseudo_pe.set_type(path[i])
+ if isinstance(path[i + 1], basestring):
+ pseudo_pe.set_name(path[i + 1])
+ else:
+ pseudo_pe.set_id(path[i + 1])
- def Count(self):
- """Counts results, up to the query's limit.
+ return pseudo_pb
- Note this method does not deduplicate results, so the query it was generated
- from should have the 'distinct' clause applied.
- Returns:
- int: Result count.
+class KindPseudoKind(object):
+ """Pseudo-kind for __kind__ queries.
+
+ Provides a Query method to perform the actual query.
+
+ Public properties:
+ name: the pseudo-kind name
"""
- return len(self.__entities)
+ name = '__kind__'
- def ResumeFromCompiledCursor(self, cc):
- """Resumes a query from a compiled cursor.
+ def __init__(self, sqlitestub):
+ """Constructor.
+
+ Initializes a __kind__ pseudo-kind definition.
Args:
- cc: The compiled cursor to resume from.
+ sqlitestub: the DatastoreSqliteStub instance being served by this
+ pseudo-kind.
"""
- raise datastore_errors.InternalError('unexpected compiled cursor')
+ self.sqlitestub = sqlitestub
- def PopulateQueryResult(self, count, offset, result):
- """Populates a QueryResult PB with results from the cursor.
+ def Query(self, query, filters, orders):
+ """Perform a query on this pseudo-kind.
Args:
- count: The number of results to retrieve.
- offset: The number of results to skip
- result: out: A query_result PB.
+ query: the original datastore_pb.Query
+ filters: the filters from query
+ orders: the orders from query
+
+ Returns:
+ A query cursor to iterate over the query results, or None if the query
+ is invalid.
"""
+ kind_range = datastore_stub_util.ParseKindQuery(query, filters, orders)
+ conn = self.sqlitestub._GetConnection(None)
+ cursor = None
+ try:
+ prefix = self.sqlitestub._GetTablePrefix(query)
+ filters = []
- limited_offset = min(offset, _MAX_QUERY_OFFSET, len(self.__entities))
- skipped = 0
- if limited_offset:
- skipped = limited_offset
- result.set_skipped_results(limited_offset)
+ def AddExtremeFilter(extreme, inclusive, is_end):
+ """Add filter for kind start/end."""
+ if not is_end:
+ if inclusive:
+ op = datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL
+ else:
+ op = datastore_pb.Query_Filter.GREATER_THAN
+ else:
+ if inclusive:
+ op = datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL
+ else:
+ op = datastore_pb.Query_Filter.LESS_THAN
+ filters.append(('kind', op, extreme))
+ kind_range.MapExtremes(AddExtremeFilter)
- if offset == limited_offset:
- if count > _MAXIMUM_RESULTS:
- count = _MAXIMUM_RESULTS
+ params = []
+ sql_filters = self.sqlitestub._CreateFilterString(filters, params)
+ sql_stmt = ('SELECT kind FROM "%s!Entities" %s GROUP BY kind'
+ % (prefix, sql_filters))
+ c = conn.execute(sql_stmt, params)
- result_list = result.result_list()
- result_list += self.__entities[offset:offset + count]
+ kinds = []
+ for row in c.fetchall():
+ kind = row[0].encode('utf-8')
+ kinds.append(MakeEntityForQuery(query, self.name, kind))
- self.__entities = self.__entities[skipped + result.result_size():]
- result.set_keys_only(self.__query.keys_only())
- result.set_more_results(len(self.__entities) > 0)
+ cursor = datastore_stub_util.ListCursor(
+ query, kinds, datastore_stub_util.CompareEntityPbByKey)
+ finally:
+ self.sqlitestub._ReleaseConnection(conn, None)
+ return cursor
-class KindPseudoKind(object):
- """Pseudo-kind for schema queries.
+
+class PropertyPseudoKind(object):
+ """Pseudo-kind for __property__ queries.
Provides a Query method to perform the actual query.
Public properties:
name: the pseudo-kind name
"""
- name = '__kind__'
+ name = '__property__'
def __init__(self, sqlitestub):
"""Constructor.
- Initializes a __kind__ pseudo-kind definition.
+ Initializes a __property__ pseudo-kind definition.
Args:
sqlitestub: the DatastoreSqliteStub instance being served by this
pseudo-kind.
"""
@@ -417,12 +458,12 @@ def Query(self, query, filters, orders
Returns:
A query cursor to iterate over the query results, or None if the query
is invalid.
"""
- start_kind, start_inclusive, end_kind, end_inclusive = (
- datastore_stub_util.ParseKindQuery(query, filters, orders))
+ property_range = datastore_stub_util.ParsePropertyQuery(query, filters,
+ orders)
keys_only = query.keys_only()
conn = self.sqlitestub._GetConnection(None)
cursor = None
try:
prefix = self.sqlitestub._GetTablePrefix(query)
@@ -424,102 +465,97 @@ def Query(self, query, filters, orders
keys_only = query.keys_only()
conn = self.sqlitestub._GetConnection(None)
cursor = None
try:
prefix = self.sqlitestub._GetTablePrefix(query)
-
filters = []
- if start_kind is not None:
- if start_inclusive:
- start_op = datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL
- else:
- start_op = datastore_pb.Query_Filter.GREATER_THAN
- filters.append(('kind', start_op, start_kind))
- if end_kind is not None:
- if end_inclusive:
- end_op = datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL
+
+ def AddExtremeFilter(extreme, inclusive, is_end):
+ """Add filter for kind start/end."""
+ if not is_end:
+ op = datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL
else:
- end_op = datastore_pb.Query_Filter.LESS_THAN
- filters.append(('kind', end_op, end_kind))
+ op = datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL
+ filters.append(('kind', op, extreme[0]))
+ property_range.MapExtremes(AddExtremeFilter)
+
+ for name in datastore_stub_util.GetInvisibleSpecialPropertyNames():
+ filters.append(('name', '!=', name))
params = []
sql_filters = self.sqlitestub._CreateFilterString(filters, params)
if not keys_only:
sql_stmt = ('SELECT kind, name, value FROM "%s!EntitiesByProperty" %s '
- 'GROUP BY kind, name, substr(value, 1, 1) ORDER BY kind'
+ 'GROUP BY kind, name, substr(value, 1, 1) '
+ 'ORDER BY kind, name'
% (prefix, sql_filters))
else:
- sql_stmt = ('SELECT kind FROM "%s!Entities" %s GROUP BY kind'
+ sql_stmt = ('SELECT kind, name FROM "%s!EntitiesByProperty" %s '
+ 'GROUP BY kind, name ORDER BY kind, name'
% (prefix, sql_filters))
c = conn.execute(sql_stmt, params)
- kinds = []
+ properties = []
kind = None
- kind_pb = None
+ name = None
+ property_pb = None
for row in c.fetchall():
- if row[0] != kind:
- if kind_pb:
- kinds.append(kind_pb)
- kind = row[0].encode('utf-8')
- kind_pb = entity_pb.EntityProto()
- kind_pk = kind_pb.mutable_key()
- kind_pk.set_app(query.app())
- if query.has_name_space():
- kind_pk.set_name_space(query.name_space())
- kind_pe = kind_pk.mutable_path().add_element()
- kind_pe.set_type(self.name)
- kind_pe.set_name(kind)
- kind_pb.mutable_entity_group()
-
- if not keys_only:
- name, value_data = row[1:]
+ if not (row[0] == kind and row[1] == name):
+ new_kind = row[0].encode('utf-8')
+ new_name = row[1].encode('utf-8')
+ if not property_range.Contains((new_kind, new_name)):
+ continue
+ kind = new_kind
+ name = new_name
- prop_pb = kind_pb.add_property()
- prop_pb.set_name(u'property')
- prop_pb.set_multiple(True)
- value_pb = prop_pb.mutable_value()
- value_pb.set_stringvalue(name.encode('utf-8'))
+ if property_pb:
+ properties.append(property_pb)
+ property_pb = MakeEntityForQuery(query, KindPseudoKind.name, kind,
+ self.name, name)
- prop_pb = kind_pb.add_property()
- prop_pb.set_name(u'representation')
- prop_pb.set_multiple(True)
+ if not keys_only:
+ value_data = row[2]
value_decoder = sortable_pb_encoder.Decoder(
array.array('B', str(value_data)))
raw_value_pb = entity_pb.PropertyValue()
raw_value_pb.Merge(value_decoder)
- value_pb = prop_pb.mutable_value()
-
if raw_value_pb.has_int64value():
- value_pb.set_int64value(entity_pb.PropertyValue.kint64Value)
+ tag = entity_pb.PropertyValue.kint64Value
elif raw_value_pb.has_booleanvalue():
- value_pb.set_int64value(entity_pb.PropertyValue.kbooleanValue)
+ tag = entity_pb.PropertyValue.kbooleanValue
elif raw_value_pb.has_stringvalue():
- value_pb.set_int64value(entity_pb.PropertyValue.kstringValue)
+ tag = entity_pb.PropertyValue.kstringValue
elif raw_value_pb.has_doublevalue():
- value_pb.set_int64value(entity_pb.PropertyValue.kdoubleValue)
+ tag = entity_pb.PropertyValue.kdoubleValue
elif raw_value_pb.has_pointvalue():
- value_pb.set_int64value(entity_pb.PropertyValue.kPointValueGroup)
+ tag = entity_pb.PropertyValue.kPointValueGroup
elif raw_value_pb.has_uservalue():
- value_pb.set_int64value(entity_pb.PropertyValue.kUserValueGroup)
+ tag = entity_pb.PropertyValue.kUserValueGroup
elif raw_value_pb.has_referencevalue():
- value_pb.set_int64value(entity_pb.PropertyValue.
- kReferenceValueGroup)
+ tag = entity_pb.PropertyValue. kReferenceValueGroup
else:
- value_pb.set_int64value(0)
+ tag = 0
+ tag_name = datastore_stub_util._PROPERTY_TYPE_NAMES[tag]
- if kind_pb:
- kinds.append(kind_pb)
+ representation_pb = property_pb.add_property()
+ representation_pb.set_name(u'property_representation')
+ representation_pb.set_multiple(True)
+ representation_pb.mutable_value().set_stringvalue(tag_name)
- cursor = ListQueryCursor(query, kinds)
+ if property_pb:
+ properties.append(property_pb)
+
+ cursor = datastore_stub_util.ListCursor(
+ query, properties, datastore_stub_util.CompareEntityPbByKey)
finally:
self.sqlitestub._ReleaseConnection(conn, None)
return cursor
class NamespacePseudoKind(object):
- """Pseudo-kind for namespace queries.
+ """Pseudo-kind for __namespace__ queries.
Provides a Query method to perform the actual query.
Public properties:
name: the pseudo-kind name
@@ -547,43 +583,27 @@ def Query(self, query, filters, orders
Returns:
A query cursor to iterate over the query results, or None if the query
is invalid.
"""
- start_namespace, start_inclusive, end_namespace, end_inclusive = (
- datastore_stub_util.ParseNamespaceQuery(query, filters, orders))
+ namespace_range = datastore_stub_util.ParseNamespaceQuery(query, filters,
+ orders)
app_str = query.app()
namespace_entities = []
namespaces = self.sqlitestub._DatastoreSqliteStub__namespaces
for app_id, namespace in sorted(namespaces):
- if app_id != app_str: continue
-
- if start_namespace is not None:
- if start_inclusive and namespace < start_namespace: continue
- if not start_inclusive and namespace <= start_namespace: continue
- if end_namespace is not None:
- if end_inclusive and namespace > end_namespace: continue
- if not end_inclusive and namespace >= end_namespace: continue
-
- namespace_pb = entity_pb.EntityProto()
- namespace_entities.append(namespace_pb)
-
- namespace_pb.mutable_entity_group()
- namespace_pk = namespace_pb.mutable_key()
- namespace_pk.set_app(query.app())
- if query.has_name_space():
- namespace_pk.set_name_space(query.name_space())
- namespace_pe = namespace_pk.mutable_path().add_element()
- namespace_pe.set_type(self.name)
+ if app_id == app_str and namespace_range.Contains(namespace):
if namespace:
- namespace_pe.set_name(namespace)
+ ns_id = namespace
else:
- namespace_pe.set_id(datastore_types._EMPTY_NAMESPACE_ID)
+ ns_id = datastore_types._EMPTY_NAMESPACE_ID
+ namespace_entities.append(MakeEntityForQuery(query, self.name, ns_id))
- return ListQueryCursor(query, namespace_entities)
+ return datastore_stub_util.ListCursor(
+ query, namespace_entities, datastore_stub_util.CompareEntityPbByKey)
class DatastoreSqliteStub(apiproxy_stub.APIProxyStub):
"""Persistent stub for the Python datastore API.
@@ -652,12 +672,10 @@ def __init__(self,
self.__next_tx_handle = 1
self.__tx_writes = {}
self.__tx_deletes = set()
- self.__next_cursor_id = 1
- self.__cursor_lock = threading.Lock()
self.__cursors = {}
self.__namespaces = set()
self.__indexes = {}
@@ -665,10 +683,11 @@ def __init__(self,
self.__query_history = {}
self.__pseudo_kinds = {}
self._RegisterPseudoKind(KindPseudoKind(self))
+ self._RegisterPseudoKind(PropertyPseudoKind(self))
self._RegisterPseudoKind(NamespacePseudoKind(self))
try:
self.__Init()
except sqlite3.DatabaseError, e:
@@ -1098,10 +1117,12 @@ def QueryHistory(self):
return dict((pb, times) for pb, times in self.__query_history.items() if
pb.app() == self.__app_id)
def __PutEntities(self, conn, entities):
self.__DeleteIndexEntries(conn, [e.key() for e in entities])
+ for entity in entities:
+ datastore_stub_util.PrepareSpecialPropertiesForStore(entity)
self.__InsertEntities(conn, entities)
self.__InsertIndexEntries(conn, entities)
def __DeleteEntities(self, conn, keys):
self.__DeleteIndexEntries(conn, keys)
@@ -1156,10 +1177,12 @@ def _Dynamic_Get(self, get_request, ge
(self.__EncodeIndexPB(key.path()),))
group = get_response.add_entity()
row = c.fetchone()
if row:
group.mutable_entity().ParseFromString(row[0])
+ datastore_stub_util.PrepareSpecialPropertiesForLoad(
+ group.mutable_entity())
finally:
self._ReleaseConnection(conn, get_request.transaction())
def _Dynamic_Delete(self, delete_request, delete_response):
conn = self._GetConnection(delete_request.transaction())
@@ -1457,11 +1480,11 @@ def __FindIndexForQuery(self, query):
__MergeJoinQuery,
__LastResortQuery,
]
def __GetQueryCursor(self, conn, query):
- """Returns an SQLite query cursor for the provided query.
+ """Returns a query cursor for the provided query.
Args:
conn: The SQLite connection.
query: A datastore_pb.Query protocol buffer.
Returns:
@@ -1501,13 +1524,10 @@ def __GetQueryCursor(self, conn, query
logging.info("Executing statement '%s' with arguments %r",
sql_stmt, [str(x) for x in params])
db_cursor = conn.execute(sql_stmt, params)
cursor = QueryCursor(query, db_cursor)
- if query.has_compiled_cursor() and query.compiled_cursor().position_size():
- cursor.ResumeFromCompiledCursor(query.compiled_cursor())
-
clone = datastore_pb.Query()
clone.CopyFrom(query)
clone.clear_hint()
clone.clear_limit()
clone.clear_count()
@@ -1519,53 +1539,44 @@ def __GetQueryCursor(self, conn, query
def _Dynamic_RunQuery(self, query, query_result):
conn = self._GetConnection(query.transaction())
try:
cursor = self.__GetQueryCursor(conn, query)
- self.__cursor_lock.acquire()
- cursor_id = self.__next_cursor_id
- self.__next_cursor_id += 1
- self.__cursor_lock.release()
-
- cursor_pb = query_result.mutable_cursor()
- cursor_pb.set_app(query.app())
- cursor_pb.set_cursor(cursor_id)
-
if query.has_count():
count = query.count()
elif query.has_limit():
count = query.limit()
else:
count = _BATCH_SIZE
- cursor.PopulateQueryResult(count, query.offset(), query_result)
- self.__cursors[cursor_pb] = cursor
+ cursor.PopulateQueryResult(query_result, count, query.offset())
+ self.__cursors[query_result.cursor().cursor()] = cursor
finally:
self._ReleaseConnection(conn, query.transaction())
def _Dynamic_Next(self, next_request, query_result):
self.__ValidateAppId(next_request.cursor().app())
try:
- cursor = self.__cursors[next_request.cursor()]
+ cursor = self.__cursors[next_request.cursor().cursor()]
except KeyError:
raise apiproxy_errors.ApplicationError(
datastore_pb.Error.BAD_REQUEST,
'Cursor %d not found' % next_request.cursor().cursor())
assert cursor.app == next_request.cursor().app()
count = _BATCH_SIZE
if next_request.has_count():
count = next_request.count()
- cursor.PopulateQueryResult(count, next_request.offset(), query_result)
+ cursor.PopulateQueryResult(query_result, count, next_request.offset())
def _Dynamic_Count(self, query, integer64proto):
if query.has_limit():
- query.set_limit(min(query.limit(), _MAXIMUM_RESULTS))
+ query.set_limit(min(query.limit(), datastore_stub_util._MAXIMUM_RESULTS))
else:
- query.set_limit(_MAXIMUM_RESULTS)
+ query.set_limit(datastore_stub_util._MAXIMUM_RESULTS)
conn = self._GetConnection(query.transaction())
try:
cursor = self.__GetQueryCursor(conn, query)
integer64proto.set_value(cursor.Count())
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/datastore/datastore_stub_util.py google-appengine-1.4.0-prerelease/google/appengine/datastore/datastore_stub_util.py
--- local/google_appengine/google/appengine/datastore/datastore_stub_util.py 2010-10-14 21:40:43.205837862 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/datastore/datastore_stub_util.py 2010-11-18 21:44:32.000000000 +0000
@@ -16,18 +16,126 @@
#
"""Utility functions shared between the file and sqlite datastore stubs."""
-import md5
+
+try:
+ import hashlib
+ _MD5_FUNC = hashlib.md5
+except ImportError:
+ import md5
+ _MD5_FUNC = md5.new
+
+import struct
+import threading
from google.appengine.api import datastore_types
from google.appengine.api.datastore_errors import BadRequestError
from google.appengine.datastore import datastore_index
from google.appengine.datastore import datastore_pb
-from google.appengine.datastore import datastore_pb
from google.appengine.runtime import apiproxy_errors
+from google.appengine.datastore import entity_pb
+
+
+_MAXIMUM_RESULTS = 1000
+
+
+_MAX_QUERY_OFFSET = 1000
+
+
+_CURSOR_CONCAT_STR = '!CURSOR!'
+
+_PROPERTY_TYPE_NAMES = {
+ 0: 'NULL',
+ entity_pb.PropertyValue.kint64Value: 'INT64',
+ entity_pb.PropertyValue.kbooleanValue: 'BOOLEAN',
+ entity_pb.PropertyValue.kstringValue: 'STRING',
+ entity_pb.PropertyValue.kdoubleValue: 'DOUBLE',
+ entity_pb.PropertyValue.kPointValueGroup: 'POINT',
+ entity_pb.PropertyValue.kUserValueGroup: 'USER',
+ entity_pb.PropertyValue.kReferenceValueGroup: 'REFERENCE'
+ }
+
+_SCATTER_PROPORTION = 32768
+
+def _GetScatterProperty(entity_proto):
+ """Gets the scatter property for an object.
+
+ For ease of implementation, this is not synchronized with the actual
+ value on the App Engine server, but should work equally well.
+
+ Note: This property may change, either here or in production. No client
+ other than the mapper framework should rely on it directly.
+
+ Returns:
+ The PropertyValue of the scatter property or None if this entity should not
+ have a scatter property.
+ """
+ hash_obj = _MD5_FUNC()
+ for element in entity_proto.key().path().element_list():
+ if element.has_name():
+ hash_obj.update(element.name())
+ elif element.has_id():
+ hash_obj.update(str(element.id()))
+ hash_bytes = hash_obj.digest()[0:2]
+ (hash_int,) = struct.unpack('H', hash_bytes)
+
+ if hash_int >= _SCATTER_PROPORTION:
+ return None
+
+ scatter_property = entity_pb.Property()
+ scatter_property.set_name('__scatter__')
+ scatter_property.set_meaning(entity_pb.Property.BYTESTRING)
+ scatter_property.set_multiple(False)
+ property_value = scatter_property.mutable_value()
+ property_value.set_stringvalue(hash_bytes)
+ return scatter_property
+
+
+_SPECIAL_PROPERTY_MAP = {
+ '__scatter__' : (False, True, _GetScatterProperty)
+ }
+
+def GetInvisibleSpecialPropertyNames():
+ """Gets the names of all non user-visible special properties."""
+ invisible_names = []
+ for name, value in _SPECIAL_PROPERTY_MAP.items():
+ is_visible, is_stored, property_func = value
+ if not is_visible:
+ invisible_names.append(name)
+ return invisible_names
+
+def _PrepareSpecialProperties(entity_proto, is_load):
+ """Computes special properties for loading or storing.
+ Strips other special properties."""
+ for i in xrange(entity_proto.property_size() - 1, -1, -1):
+ if _SPECIAL_PROPERTY_MAP.has_key(entity_proto.property(i).name()):
+ del entity_proto.property_list()[i]
+
+ for is_visible, is_stored, property_func in _SPECIAL_PROPERTY_MAP.values():
+ if is_load:
+ should_process = is_visible
+ else:
+ should_process = is_stored
+
+ if should_process:
+ special_property = property_func(entity_proto)
+ if special_property:
+ entity_proto.property_list().append(special_property)
+
+
+def PrepareSpecialPropertiesForStore(entity_proto):
+ """Computes special properties for storing.
+ Strips other special properties."""
+ _PrepareSpecialProperties(entity_proto, False)
+
+
+def PrepareSpecialPropertiesForLoad(entity_proto):
+ """Computes special properties that are user-visible.
+ Strips other special properties."""
+ _PrepareSpecialProperties(entity_proto, True)
def ValidateQuery(query, filters, orders, max_query_components):
"""Validate a datastore query with normalized filters, orders.
@@ -125,26 +233,105 @@ def BadRequest(message):
if not (prop_name == key_prop_name and
order.direction() is datastore_pb.Query_Order.ASCENDING):
BadRequest('kind is required for all orders except __key__ ascending')
+class ValueRange(object):
+ """A range of values defined by its two extremes (inclusive or exclusive)."""
+
+ def __init__(self):
+ """Constructor.
+
+ Creates an unlimited range.
+ """
+ self.__start = self.__end = None
+ self.__start_inclusive = self.__end_inclusive = False
+
+ def Update(self, rel_op, limit):
+ """Filter the range by 'rel_op limit'.
+
+ Args:
+ rel_op: relational operator from datastore_pb.Query_Filter.
+ limit: the value to limit the range by.
+ """
+ if rel_op == datastore_pb.Query_Filter.LESS_THAN:
+ if self.__end is None or limit <= self.__end:
+ self.__end = limit
+ self.__end_inclusive = False
+ elif (rel_op == datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL or
+ rel_op == datastore_pb.Query_Filter.EQUAL):
+ if self.__end is None or limit < self.__end:
+ self.__end = limit
+ self.__end_inclusive = True
+
+ if rel_op == datastore_pb.Query_Filter.GREATER_THAN:
+ if self.__start is None or limit >= self.__start:
+ self.__start = limit
+ self.__start_inclusive = False
+ elif (rel_op == datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL or
+ rel_op == datastore_pb.Query_Filter.EQUAL):
+ if self.__start is None or limit > self.__start:
+ self.__start = limit
+ self.__start_inclusive = True
+
+ def Contains(self, value):
+ """Check if the range contains a specific value.
+
+ Args:
+ value: the value to check.
+ Returns:
+ True iff value is contained in this range.
+ """
+ if self.__start is not None:
+ if self.__start_inclusive and value < self.__start: return False
+ if not self.__start_inclusive and value <= self.__start: return False
+ if self.__end is not None:
+ if self.__end_inclusive and value > self.__end: return False
+ if not self.__end_inclusive and value >= self.__end: return False
+ return True
+
+ def Remap(self, mapper):
+ """Transforms the range extremes with a function.
+
+ The function mapper must preserve order, i.e.
+ x rel_op y iff mapper(x) rel_op y
+
+ Args:
+ mapper: function to apply to the range extremes.
+ """
+ self.__start = self.__start and mapper(self.__start)
+ self.__end = self.__end and mapper(self.__end)
+
+ def MapExtremes(self, mapper):
+ """Evaluate a function on the range extremes.
+
+ Args:
+ mapper: function to apply to the range extremes.
+ Returns:
+ (x, y) where x = None if the range has no start,
+ mapper(start, start_inclusive, False) otherwise
+ y = None if the range has no end,
+ mapper(end, end_inclusive, True) otherwise
+ """
+ return (
+ self.__start and mapper(self.__start, self.__start_inclusive, False),
+ self.__end and mapper(self.__end, self.__end_inclusive, True))
+
+
def ParseKeyFilteredQuery(filters, orders):
"""Parse queries which only allow filters and ascending-orders on __key__.
Raises exceptions for illegal queries.
Args:
filters: the normalized filters of a query.
orders: the normalized orders of a query.
Returns:
- The key range (start, start_inclusive, end, end_inclusive) requested
- in the query.
+ The key range (a ValueRange over datastore_types.Key) requested in the
+ query.
"""
remaining_filters = []
- start_key = None
- start_inclusive = False
- end_key = None
- end_inclusive = False
+ key_range = ValueRange()
key_prop = datastore_types._KEY_SPECIAL_PROPERTY
for f in filters:
op = f.op()
if not (f.property_size() == 1 and
f.property(0).name() == key_prop and
@@ -155,30 +342,11 @@ def ParseKeyFilteredQuery(filters, order
val = f.property(0).value()
if not val.has_referencevalue():
raise BadRequestError('__key__ kind must be compared to a key')
limit = datastore_types.FromReferenceProperty(val)
-
- if op == datastore_pb.Query_Filter.LESS_THAN:
- if end_key is None or limit <= end_key:
- end_key = limit
- end_inclusive = False
- elif (op == datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL or
- op == datastore_pb.Query_Filter.EQUAL):
- if end_key is None or limit < end_key:
- end_key = limit
- end_inclusive = True
-
- if op == datastore_pb.Query_Filter.GREATER_THAN:
- if start_key is None or limit >= start_key:
- start_key = limit
- start_inclusive = False
- elif (op == datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL or
- op == datastore_pb.Query_Filter.EQUAL):
- if start_key is None or limit > start_key:
- start_key = limit
- start_inclusive = True
+ key_range.Update(op, limit)
remaining_orders = []
for o in orders:
if not (o.direction() == datastore_pb.Query_Order.ASCENDING and
o.property() == datastore_types._KEY_SPECIAL_PROPERTY):
@@ -190,11 +358,11 @@ def ParseKeyFilteredQuery(filters, order
raise BadRequestError(
'Only comparison filters on ' + key_prop + ' supported')
if remaining_orders:
raise BadRequestError('Only ascending order on ' + key_prop + ' supported')
- return (start_key, start_inclusive, end_key, end_inclusive)
+ return key_range
def ParseKindQuery(query, filters, orders):
"""Parse __kind__ (schema) queries.
@@ -202,35 +370,31 @@ def ParseKindQuery(query, filters, order
Args:
query: A Query PB.
filters: the normalized filters from query.
orders: the normalized orders from query.
Returns:
- The kind range (start, start_inclusive, end, end_inclusive) requested
- in the query.
+ The kind range (a ValueRange over string) requested in the query.
"""
if query.has_ancestor():
- raise BadRequestError('ancestor queries not allowed')
+ raise BadRequestError('ancestor queries on __kind__ not allowed')
- start_kind, start_inclusive, end_kind, end_inclusive = ParseKeyFilteredQuery(
- filters, orders)
+ key_range = ParseKeyFilteredQuery(filters, orders)
+ key_range.Remap(_KindKeyToString)
- return (_KindKeyToString(start_kind), start_inclusive,
- _KindKeyToString(end_kind), end_inclusive)
+ return key_range
def _KindKeyToString(key):
"""Extract kind name from __kind__ key.
Raises an ApplicationError if the key is not of the form '__kind__'/name.
Args:
- key: a key for a __kind__ instance, or a false value.
+ key: a key for a __kind__ instance.
Returns:
- kind specified by key, or key if key is a false value.
+ kind specified by key.
"""
- if not key:
- return key
key_path = key.to_path()
if (len(key_path) == 2 and key_path[0] == '__kind__' and
isinstance(key_path[1], basestring)):
return key_path[1]
raise BadRequestError('invalid Key for __kind__ table')
@@ -243,44 +407,100 @@ def ParseNamespaceQuery(query, filters,
Args:
query: A Query PB.
filters: the normalized filters from query.
orders: the normalized orders from query.
Returns:
- The kind range (start, start_inclusive, end, end_inclusive) requested
- in the query.
+ The kind range (a ValueRange over string) requested in the query.
"""
if query.has_ancestor():
- raise BadRequestError('ancestor queries not allowed')
+ raise BadRequestError('ancestor queries on __namespace__ not allowed')
- start_kind, start_inclusive, end_kind, end_inclusive = ParseKeyFilteredQuery(
- filters, orders)
+ key_range = ParseKeyFilteredQuery(filters, orders)
+ key_range.Remap(_NamespaceKeyToString)
+
+ return key_range
- return (_NamespaceKeyToString(start_kind), start_inclusive,
- _NamespaceKeyToString(end_kind), end_inclusive)
def _NamespaceKeyToString(key):
"""Extract namespace name from __namespace__ key.
Raises an ApplicationError if the key is not of the form '__namespace__'/name
or '__namespace__'/_EMPTY_NAMESPACE_ID.
Args:
- key: a key for a __namespace__ instance, or a false value.
+ key: a key for a __namespace__ instance.
Returns:
- namespace specified by key, or key if key is a false value.
+ namespace specified by key.
"""
- if not key:
- return key
key_path = key.to_path()
if len(key_path) == 2 and key_path[0] == '__namespace__':
if key_path[1] == datastore_types._EMPTY_NAMESPACE_ID:
return ''
if isinstance(key_path[1], basestring):
return key_path[1]
raise BadRequestError('invalid Key for __namespace__ table')
+def ParsePropertyQuery(query, filters, orders):
+ """Parse __property__ queries.
+
+ Raises exceptions for illegal queries.
+ Args:
+ query: A Query PB.
+ filters: the normalized filters from query.
+ orders: the normalized orders from query.
+ Returns:
+ The kind range (a ValueRange over (kind, property) pairs) requested
+ in the query.
+ """
+ if query.has_transaction():
+ raise BadRequestError('transactional queries on __property__ not allowed')
+
+ key_range = ParseKeyFilteredQuery(filters, orders)
+ key_range.Remap(lambda x: _PropertyKeyToString(x, ''))
+
+ if query.has_ancestor():
+ ancestor = datastore_types.Key._FromPb(query.ancestor())
+ ancestor_kind, ancestor_property = _PropertyKeyToString(ancestor, None)
+
+ if ancestor_property is not None:
+ key_range.Update(datastore_pb.Query_Filter.EQUAL,
+ (ancestor_kind, ancestor_property))
+ else:
+ key_range.Update(datastore_pb.Query_Filter.GREATER_THAN_OR_EQUAL,
+ (ancestor_kind, ''))
+ key_range.Update(datastore_pb.Query_Filter.LESS_THAN_OR_EQUAL,
+ (ancestor_kind + '\0', ''))
+ query.clear_ancestor()
+
+ return key_range
+
+def _PropertyKeyToString(key, default_property):
+ """Extract property name from __property__ key.
+
+ Raises an ApplicationError if the key is not of the form
+ '__kind__'/kind, '__property__'/property or '__kind__'/kind
+
+ Args:
+ key: a key for a __property__ instance.
+ default_property: property value to return when key only has a kind.
+ Returns:
+ kind, property if key = '__kind__'/kind, '__property__'/property
+ kind, default_property if key = '__kind__'/kind
+ """
+ key_path = key.to_path()
+ if (len(key_path) == 2 and
+ key_path[0] == '__kind__' and isinstance(key_path[1], basestring)):
+ return (key_path[1], default_property)
+ if (len(key_path) == 4 and
+ key_path[0] == '__kind__' and isinstance(key_path[1], basestring) and
+ key_path[2] == '__property__' and isinstance(key_path[3], basestring)):
+ return (key_path[1], key_path[3])
+
+ raise BadRequestError('invalid Key for __property__ table')
+
+
def SynthesizeUserId(email):
"""Return a synthetic user ID from an email address.
Note that this is not the same user ID found in the production system.
@@ -288,11 +508,11 @@ def SynthesizeUserId(email):
email: An email address.
Returns:
A string userid derived from the email address.
"""
- user_id_digest = md5.new(email.lower()).digest()
+ user_id_digest = _MD5_FUNC(email.lower()).digest()
user_id = '1' + ''.join(['%02d' % ord(x) for x in user_id_digest])[:20]
return user_id
def FillUsersInQuery(filters):
@@ -314,5 +534,304 @@ def FillUser(property):
"""
if property.value().has_uservalue():
uid = SynthesizeUserId(property.value().uservalue().email())
if uid:
property.mutable_value().mutable_uservalue().set_obfuscated_gaiaid(uid)
+
+
+class BaseCursor(object):
+ """A base query cursor over a list of entities.
+
+ Public properties:
+ cursor: the integer cursor
+ app: the app for which this cursor was created
+
+ Class attributes:
+ _next_cursor: the next cursor to allocate
+ _next_cursor_lock: protects _next_cursor
+ """
+ _next_cursor = 1
+ _next_cursor_lock = threading.Lock()
+
+ def __init__(self, app):
+ """Constructor.
+
+ Args:
+ app: The app this cursor is being created for.
+ """
+ self.app = app
+ self.cursor = self._AcquireCursorID()
+
+ def PopulateCursor(self, cursor):
+ cursor.set_app(self.app)
+ cursor.set_cursor(self.cursor)
+
+ @classmethod
+ def _AcquireCursorID(cls):
+ """Acquires the next cursor id in a thread safe manner."""
+ cls._next_cursor_lock.acquire()
+ try:
+ cursor_id = cls._next_cursor
+ cls._next_cursor += 1
+ finally:
+ cls._next_cursor_lock.release()
+ return cursor_id
+
+
+class ListCursor(BaseCursor):
+ """A query cursor over a list of entities.
+
+ Public properties:
+ keys_only: whether the query is keys_only
+ """
+
+ def __init__(self, query, results, order_compare_entities):
+ """Constructor.
+
+ Args:
+ query: the query request proto
+ # the query results, in order, such that results[self.offset+1] is
+ # the next result
+ results: list of datastore_pb.EntityProto
+ order_compare_entities: a __cmp__ function for datastore_pb.EntityProto
+ that follows sort order as specified by the query
+ """
+ super(ListCursor, self).__init__(query.app())
+
+ if query.has_compiled_cursor() and query.compiled_cursor().position_list():
+ (self.__last_result, inclusive) = self._DecodeCompiledCursor(
+ query, query.compiled_cursor())
+ start_cursor_position = ListCursor._GetCursorOffset(
+ results, self.__last_result, inclusive, order_compare_entities)
+ else:
+ self.__last_result = None
+ start_cursor_position = 0
+
+ if query.has_end_compiled_cursor():
+ if query.end_compiled_cursor().position_list():
+ (end_cursor_entity, inclusive) = self._DecodeCompiledCursor(
+ query, query.end_compiled_cursor())
+ end_cursor_position = ListCursor._GetCursorOffset(
+ results, end_cursor_entity, inclusive, order_compare_entities)
+ else:
+ end_cursor_position = 0
+ else:
+ end_cursor_position = len(results)
+
+ results = results[start_cursor_position:end_cursor_position]
+
+ if query.has_limit():
+ limit = query.limit()
+ if query.offset():
+ limit += query.offset()
+ if limit >= 0 and limit < len(results):
+ results = results[:limit]
+
+ self.__results = results
+ self.__query = query
+ self.__offset = 0
+ self.__count = len(self.__results)
+
+ self.keys_only = query.keys_only()
+
+ @staticmethod
+ def _GetCursorOffset(results, cursor_entity, inclusive, compare):
+ """Converts a cursor entity into a offset into the result set even if the
+ cursor_entity no longer exists.
+
+ Args:
+ results: the query's results (sequence of datastore_pb.EntityProto)
+ cursor_entity: the datastore_pb.EntityProto from the compiled query
+ inclusive: boolean that specifies if to offset past the cursor_entity
+ compare: a function that takes two datastore_pb.EntityProto and compares
+ them.
+ Returns:
+ the integer offset
+ """
+ lo = 0
+ hi = len(results)
+ if inclusive:
+ while lo < hi:
+ mid = (lo + hi) // 2
+ if compare(results[mid], cursor_entity) < 0:
+ lo = mid + 1
+ else:
+ hi = mid
+ else:
+ while lo < hi:
+ mid = (lo + hi) // 2
+ if compare(cursor_entity, results[mid]) < 0:
+ hi = mid
+ else:
+ lo = mid + 1
+ return lo
+
+ def _ValidateQuery(self, query, query_info):
+ """Ensure that the given query matches the query_info.
+
+ Args:
+ query: datastore_pb.Query instance we are chacking
+ query_info: datastore_pb.Query instance we want to match
+
+ Raises BadRequestError on failure.
+ """
+ error_msg = 'Cursor does not match query: %s'
+ if query_info.filter_list() != query.filter_list():
+ raise BadRequestError(error_msg % 'filters do not match')
+ if query_info.order_list() != query.order_list():
+ raise BadRequestError(error_msg % 'orders do not match')
+
+ for attr in ('ancestor', 'kind', 'name_space', 'search_query'):
+ query_info_has_attr = getattr(query_info, 'has_%s' % attr)
+ query_info_attr = getattr(query_info, attr)
+ query_has_attr = getattr(query, 'has_%s' % attr)
+ query_attr = getattr(query, attr)
+ if query_info_has_attr():
+ if not query_has_attr() or query_info_attr() != query_attr():
+ raise BadRequestError(error_msg % ('%s does not match' % attr))
+ elif query_has_attr():
+ raise BadRequestError(error_msg % ('%s does not match' % attr))
+
+ def _MinimalQueryInfo(self, query):
+ """Extract the minimal set of information for query matching.
+
+ Args:
+ query: datastore_pb.Query instance from which to extract info.
+
+ Returns:
+ datastore_pb.Query instance suitable for matching against when
+ validating cursors.
+ """
+ query_info = datastore_pb.Query()
+ query_info.set_app(query.app())
+
+ for filter in query.filter_list():
+ query_info.filter_list().append(filter)
+ for order in query.order_list():
+ query_info.order_list().append(order)
+
+ if query.has_ancestor():
+ query_info.mutable_ancestor().CopyFrom(query.ancestor())
+
+ for attr in ('kind', 'name_space', 'search_query'):
+ query_has_attr = getattr(query, 'has_%s' % attr)
+ query_attr = getattr(query, attr)
+ query_info_set_attr = getattr(query_info, 'set_%s' % attr)
+ if query_has_attr():
+ query_info_set_attr(query_attr())
+
+ return query_info
+
+ def _MinimalEntityInfo(self, entity_proto, query):
+ """Extract the minimal set of information that preserves entity order.
+
+ Args:
+ entity_proto: datastore_pb.EntityProto instance from which to extract
+ information
+ query: datastore_pb.Query instance for which ordering must be preserved.
+
+ Returns:
+ datastore_pb.EntityProto instance suitable for matching against a list of
+ results when finding cursor positions.
+ """
+ entity_info = datastore_pb.EntityProto()
+ order_names = [o.property() for o in query.order_list()]
+ entity_info.mutable_key().MergeFrom(entity_proto.key())
+ entity_info.mutable_entity_group().MergeFrom(entity_proto.entity_group())
+ for prop in entity_proto.property_list():
+ if prop.name() in order_names:
+ entity_info.add_property().MergeFrom(prop)
+ return entity_info
+
+ def _DecodeCompiledCursor(self, query, compiled_cursor):
+ """Converts a compiled_cursor into a cursor_entity.
+
+ Returns:
+ (cursor_entity, inclusive): a datastore_pb.EntityProto and if it should
+ be included in the result set.
+ """
+ assert len(compiled_cursor.position_list()) == 1
+
+ position = compiled_cursor.position(0)
+ entity_as_pb = datastore_pb.EntityProto()
+ (query_info_encoded, entity_encoded) = position.start_key().split(
+ _CURSOR_CONCAT_STR, 1)
+ query_info_pb = datastore_pb.Query()
+ query_info_pb.ParseFromString(query_info_encoded)
+ self._ValidateQuery(query, query_info_pb)
+
+ entity_as_pb.ParseFromString(entity_encoded)
+ return (entity_as_pb, position.start_inclusive())
+
+ def _EncodeCompiledCursor(self, query, compiled_cursor):
+ """Converts the current state of the cursor into a compiled_cursor.
+
+ Args:
+ query: the datastore_pb.Query this cursor is related to
+ compiled_cursor: an empty datstore_pb.CompiledCursor
+ """
+ if self.__last_result is not None:
+ position = compiled_cursor.add_position()
+ query_info = self._MinimalQueryInfo(query)
+ entity_info = self._MinimalEntityInfo(self.__last_result, query)
+ start_key = _CURSOR_CONCAT_STR.join((
+ query_info.Encode(),
+ entity_info.Encode()))
+ position.set_start_key(str(start_key))
+ position.set_start_inclusive(False)
+
+ def Count(self):
+ """Counts results, up to the query's limit.
+
+ Note this method does not deduplicate results, so the query it was generated
+ from should have the 'distinct' clause applied.
+
+ Returns:
+ int: Result count.
+ """
+ return self.__count
+
+ def PopulateQueryResult(self, result, count, offset, compile=False):
+ """Populates a QueryResult with this cursor and the given number of results.
+
+ Args:
+ result: datastore_pb.QueryResult
+ count: integer of how many results to return
+ offset: integer of how many results to skip
+ compile: boolean, whether we are compiling this query
+ """
+ offset = min(offset, self.__count - self.__offset)
+ limited_offset = min(offset, _MAX_QUERY_OFFSET)
+ if limited_offset:
+ self.__offset += limited_offset
+ result.set_skipped_results(limited_offset)
+
+ if offset == limited_offset and count:
+ if count > _MAXIMUM_RESULTS:
+ count = _MAXIMUM_RESULTS
+ results = self.__results[self.__offset:self.__offset + count]
+ count = len(results)
+ self.__offset += count
+ result.result_list().extend(results)
+
+ if self.__offset:
+ self.__last_result = self.__results[self.__offset - 1]
+
+ result.set_keys_only(self.keys_only)
+ result.set_more_results(self.__offset < self.__count)
+ self.PopulateCursor(result.mutable_cursor())
+ if compile:
+ self._EncodeCompiledCursor(
+ self.__query, result.mutable_compiled_cursor())
+
+
+def CompareEntityPbByKey(a, b):
+ """Compare two entity protobuf's by key.
+
+ Args:
+ a: datastore_pb.EntityProto to compare
+ b: datastore_pb.EntityProto to compare
+ Returns:
+ <0 if a's key is before b's, =0 if they are the same key, and >0 otherwise.
+ """
+ return cmp(datastore_types.Key._FromPb(a.key()),
+ datastore_types.Key._FromPb(b.key()))
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/admin/__init__.py google-appengine-1.4.0-prerelease/google/appengine/ext/admin/__init__.py
--- local/google_appengine/google/appengine/ext/admin/__init__.py 2010-10-14 21:40:41.972837792 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/admin/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -115,10 +115,11 @@ def get(self):
def generate(self, template_name, template_values={}):
base_path = self.base_path()
values = {
'application_name': self.request.environ['APPLICATION_ID'],
+ 'sdk_version': self.request.environ.get('SDK_VERSION', 'Unknown'),
'user': users.get_current_user(),
'request': self.request,
'home_path': base_path + DefaultPageHandler.PATH,
'datastore_path': base_path + DatastoreQueryHandler.PATH,
'datastore_edit_path': base_path + DatastoreEditHandler.PATH,
@@ -302,11 +303,11 @@ def get(self):
self.generate('queues.html', values)
def post(self):
"""Handle modifying actions and/or redirect to GET page."""
- if self.request.get('action:flushqueue'):
+ if self.request.get('action:purgequeue'):
self.stub.FlushQueue(self.request.get('queue'))
self.redirect(self.request.path_url)
class TasksPageHandler(BaseRequestHandler):
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/admin/templates/base.html google-appengine-1.4.0-prerelease/google/appengine/ext/admin/templates/base.html
--- local/google_appengine/google/appengine/ext/admin/templates/base.html 2009-10-14 01:26:24.988238801 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/admin/templates/base.html 2010-11-18 21:44:32.000000000 +0000
@@ -11,14 +11,19 @@
<body {% block bodyattributes %}{% endblock %}>
<div class="g-doc">
<div id="hd" class="g-section">
- <div class="g-section">
+ <div class="g-section g-tpl-50-50 g-split">
+ <div class="g-unit g-first">
<img id="ae-logo" src="./images/google.gif" width="153" height="47"
alt="Google App Engine"/>
</div>
+ <div class="g-unit">
+ SDK v{{ sdk_version }}
+ </div>
+ </div>
<div id="ae-appbar-lrg" class="g-section">
<h1>{{ application_name }} Development Console</h1>
</div>
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/admin/templates/queues.html google-appengine-1.4.0-prerelease/google/appengine/ext/admin/templates/queues.html
--- local/google_appengine/google/appengine/ext/admin/templates/queues.html 2010-07-01 22:17:26.794780970 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/admin/templates/queues.html 2010-11-18 21:44:32.000000000 +0000
@@ -53,14 +53,14 @@
</td>
<td valign="top">
{{ queue.tasks_in_queue|escape }}
</td>
<td valign="top">
- <form id="flushform" action="/_ah/admin/queues" method="post">
+ <form id="purgeform" action="/_ah/admin/queues" method="post">
<input type="hidden" name="queue" value="{{ queue.name|escape }}"/>
- <input type="submit" name="action:flushqueue" value="Flush Queue"
- onclick="return confirm('Are you sure you want to flush all ' +
+ <input type="submit" name="action:purgequeue" value="Purge Queue"
+ onclick="return confirm('Are you sure you want to purge all ' +
'tasks from {{ queue.name|escape }}?');"/>
</form>
</td>
</tr>
{% endfor %}
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/appstats/sample_appengine_config.py google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/sample_appengine_config.py
--- local/google_appengine/google/appengine/ext/appstats/sample_appengine_config.py 2010-07-01 22:17:26.468780901 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/sample_appengine_config.py 2010-11-18 21:44:32.000000000 +0000
@@ -128,12 +128,13 @@
# all in UTC) to local time. The default is US/Pacific winter time.
appstats_TZOFFSET = 8*3600
# URL path (sans host) leading to the stats UI. Should match app.yaml.
+# If "builtins: - appstats: on" is used, the path should be /_ah/stats.
-appstats_stats_url = '/stats'
+appstats_stats_url = '/_ah/stats'
# Fraction of requests to record. Set this to a float between 0.0
# and 1.0 to record that fraction of all requests.
appstats_RECORD_FRACTION = 1.0
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/appstats/static/appstats_js.js google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/static/appstats_js.js
--- local/google_appengine/google/appengine/ext/appstats/static/appstats_js.js 2010-10-14 21:40:42.390837792 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/static/appstats_js.js 2010-11-18 21:44:32.000000000 +0000
@@ -1,83 +1,83 @@
/* Copyright 2008-10 Google Inc. All Rights Reserved. */ (function(){function e(a){throw a;}
-var h=true,j=null,k=false,p,r=this,aa=function(a,b,c){a=a.split(".");c=c||r;!(a[0]in c)&&c.execScript&&c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)if(!a.length&&b!==undefined)c[d]=b;else c=c[d]?c[d]:c[d]={}},ba=function(){},ca=function(a){a.Q=function(){return a.bc||(a.bc=new a)}},da=function(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array||!(a instanceof Object)&&Object.prototype.toString.call(a)=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&
+var h=true,j=null,k=false,q,r=this,aa=function(a,b,c){a=a.split(".");c=c||r;!(a[0]in c)&&c.execScript&&c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)if(!a.length&&b!==undefined)c[d]=b;else c=c[d]?c[d]:c[d]={}},ba=function(){},ca=function(a){a.Q=function(){return a.bc||(a.bc=new a)}},da=function(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array||!(a instanceof Object)&&Object.prototype.toString.call(a)=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&
typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(!(a instanceof Object)&&(Object.prototype.toString.call(a)=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call")))return"function"}else return"null";else if(b=="function"&&typeof a.call=="undefined")return"object";return b},s=function(a){return da(a)=="array"},ea=function(a){var b=da(a);return b=="array"||b=="object"&&typeof a.length==
"number"},t=function(a){return typeof a=="string"},u=function(a){return da(a)=="function"},fa=function(a){a=da(a);return a=="object"||a=="array"||a=="function"},v=function(a){return a[ga]||(a[ga]=++ha)},ga="closure_uid_"+Math.floor(Math.random()*2147483648).toString(36),ha=0,ia=function(a){var b=Array.prototype.slice.call(arguments,1);return function(){var c=Array.prototype.slice.call(arguments);c.unshift.apply(c,b);return a.apply(this,c)}},w=function(a,b){function c(){}c.prototype=b.prototype;a.c=
b.prototype;a.prototype=new c;a.prototype.constructor=a};var ja=function(a){this.stack=Error().stack||"";if(a)this.message=String(a)};w(ja,Error);ja.prototype.name="CustomError";var ka=function(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a},la=function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},ra=function(a,b){if(b)return a.replace(ma,"&amp;").replace(na,"&lt;").replace(oa,"&gt;").replace(pa,"&quot;");else{if(!qa.test(a))return a;if(a.indexOf("&")!=-1)a=a.replace(ma,"&amp;");if(a.indexOf("<")!=-1)a=a.replace(na,"&lt;");if(a.indexOf(">")!=-1)a=a.replace(oa,"&gt;");if(a.indexOf('"')!=-1)a=a.replace(pa,
-"&quot;");return a}},ma=/&/g,na=/</g,oa=/>/g,pa=/\"/g,qa=/[&<>\"]/,ta=function(a,b){for(var c=0,d=la(String(a)).split("."),f=la(String(b)).split("."),g=Math.max(d.length,f.length),i=0;c==0&&i<g;i++){var l=d[i]||"",m=f[i]||"",n=RegExp("(\\d*)(\\D*)","g"),z=RegExp("(\\d*)(\\D*)","g");do{var o=n.exec(l)||["","",""],q=z.exec(m)||["","",""];if(o[0].length==0&&q[0].length==0)break;c=sa(o[1].length==0?0:parseInt(o[1],10),q[1].length==0?0:parseInt(q[1],10))||sa(o[2].length==0,q[2].length==0)||sa(o[2],q[2])}while(c==
+"&quot;");return a}},ma=/&/g,na=/</g,oa=/>/g,pa=/\"/g,qa=/[&<>\"]/,ta=function(a,b){for(var c=0,d=la(String(a)).split("."),f=la(String(b)).split("."),g=Math.max(d.length,f.length),i=0;c==0&&i<g;i++){var l=d[i]||"",m=f[i]||"",n=RegExp("(\\d*)(\\D*)","g"),z=RegExp("(\\d*)(\\D*)","g");do{var o=n.exec(l)||["","",""],p=z.exec(m)||["","",""];if(o[0].length==0&&p[0].length==0)break;c=sa(o[1].length==0?0:parseInt(o[1],10),p[1].length==0?0:parseInt(p[1],10))||sa(o[2].length==0,p[2].length==0)||sa(o[2],p[2])}while(c==
0)}return c},sa=function(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};var ua=function(a,b){b.unshift(a);ja.call(this,ka.apply(j,b));b.shift();this.lc=a};w(ua,ja);ua.prototype.name="AssertionError";var va=function(a,b){if(!a){var c=Array.prototype.slice.call(arguments,2),d="Assertion failed";if(b){d+=": "+b;var f=c}e(new ua(""+d,f||[]))}return a};var x=Array.prototype,wa=x.indexOf?function(a,b,c){va(a.length!=j);return x.indexOf.call(a,b,c)}:function(a,b,c){c=c==j?0:c<0?Math.max(0,a.length+c):c;if(t(a)){if(!t(b)||b.length!=1)return-1;return a.indexOf(b,c)}for(c=c;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},xa=x.forEach?function(a,b,c){va(a.length!=j);x.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,f=t(a)?a.split(""):a,g=0;g<d;g++)g in f&&b.call(c,f[g],g,a)},ya=x.every?function(a,b,c){va(a.length!=j);return x.every.call(a,
b,c)}:function(a,b,c){for(var d=a.length,f=t(a)?a.split(""):a,g=0;g<d;g++)if(g in f&&!b.call(c,f[g],g,a))return k;return h},za=function(a,b){return wa(a,b)>=0},Aa=function(a,b){var c=wa(a,b),d;if(d=c>=0){va(a.length!=j);x.splice.call(a,c,1)}return d},Ba=function(){return x.concat.apply(x,arguments)},Ca=function(a){if(s(a))return Ba(a);else{for(var b=[],c=0,d=a.length;c<d;c++)b[c]=a[c];return b}},Ea=function(a){va(a.length!=j);return x.splice.apply(a,Da(arguments,1))},Da=function(a,b,c){va(a.length!=
-j);return arguments.length<=2?x.slice.call(a,b):x.slice.call(a,b,c)};var Fa=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},Ga=function(a,b){var c;if(c=b in a)delete a[b];return c},Ha=function(a,b,c){if(b in a)e(Error('The object already contains the key "'+b+'"'));a[b]=c},Ia=function(a){var b={};for(var c in a)b[a[c]]=c;return b},Ja=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Ka=function(a){for(var b,c,d=1;d<arguments.length;d++){c=arguments[d];for(b in c)a[b]=c[b];for(var f=0;f<Ja.length;f++){b=
-Ja[f];if(Object.prototype.hasOwnProperty.call(c,b))a[b]=c[b]}}},La=function(){var a=arguments.length;if(a==1&&s(arguments[0]))return La.apply(j,arguments[0]);if(a%2)e(Error("Uneven number of arguments"));for(var b={},c=0;c<a;c+=2)b[arguments[c]]=arguments[c+1];return b};var Ma,Na,Oa,Pa,Qa=function(){return r.navigator?r.navigator.userAgent:j};Pa=Oa=Na=Ma=k;var Ra;if(Ra=Qa()){var Sa=r.navigator;Ma=Ra.indexOf("Opera")==0;Na=!Ma&&Ra.indexOf("MSIE")!=-1;Oa=!Ma&&Ra.indexOf("WebKit")!=-1;Pa=!Ma&&!Oa&&Sa.product=="Gecko"}var Ta=Ma,y=Na,A=Pa,B=Oa,Ua=r.navigator,Va=(Ua&&Ua.platform||"").indexOf("Mac")!=-1,Wa;
-a:{var Xa="",Ya;if(Ta&&r.opera){var Za=r.opera.version;Xa=typeof Za=="function"?Za():Za}else{if(A)Ya=/rv\:([^\);]+)(\)|;)/;else if(y)Ya=/MSIE\s+([^\);]+)(\)|;)/;else if(B)Ya=/WebKit\/(\S+)/;if(Ya){var $a=Ya.exec(Qa());Xa=$a?$a[1]:""}}if(y){var ab,bb=r.document;ab=bb?bb.documentMode:undefined;if(ab>parseFloat(Xa)){Wa=String(ab);break a}}Wa=Xa}var cb=Wa,db={},C=function(a){return db[a]||(db[a]=ta(cb,a)>=0)};var eb,fb=!y||C("9"),gb=y&&!C("9");var hb=function(a){return(a=a.className)&&typeof a.split=="function"?a.split(/\s+/):[]},D=function(a){var b=hb(a),c;c=Da(arguments,1);for(var d=0,f=0;f<c.length;f++)if(!za(b,c[f])){b.push(c[f]);d++}c=d==c.length;a.className=b.join(" ");return c},ib=function(a){var b=hb(a),c;c=Da(arguments,1);for(var d=0,f=0;f<b.length;f++)if(za(c,b[f])){Ea(b,f--,1);d++}c=d==c.length;a.className=b.join(" ");return c};var lb=function(a){return a?new jb(kb(a)):eb||(eb=new jb)},E=function(a){return t(a)?document.getElementById(a):a},mb=function(a,b,c,d){a=d||a;b=b&&b!="*"?b.toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(!B||document.compatMode=="CSS1Compat"||C("528"))&&(b||c))return a.querySelectorAll(b+(c?"."+c:""));if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};for(var f=0,g=0,i;i=a[g];g++)if(b==i.nodeName)d[f++]=i;d.length=f;return d}else return a}a=a.getElementsByTagName(b||
-"*");if(c){d={};for(g=f=0;i=a[g];g++){b=i.className;if(typeof b.split=="function"&&za(b.split(/\s+/),c))d[f++]=i}d.length=f;return d}else return a},ob=function(a,b){Fa(b,function(c,d){if(d=="style")a.style.cssText=c;else if(d=="class")a.className=c;else if(d=="for")a.htmlFor=c;else if(d in nb)a.setAttribute(nb[d],c);else a[d]=c})},nb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",height:"height",width:"width",usemap:"useMap",frameborder:"frameBorder",
-type:"type"},qb=function(){return pb(document,arguments)},pb=function(a,b){var c=b[0],d=b[1];if(!fb&&d&&(d.name||d.type)){c=["<",c];d.name&&c.push(' name="',ra(d.name),'"');if(d.type){c.push(' type="',ra(d.type),'"');var f={};Ka(f,d);d=f;delete d.type}c.push(">");c=c.join("")}c=a.createElement(c);if(d)if(t(d))c.className=d;else s(d)?D.apply(j,[c].concat(d)):ob(c,d);b.length>2&&rb(a,c,b,2);return c},rb=function(a,b,c,d){function f(i){if(i)b.appendChild(t(i)?a.createTextNode(i):i)}for(d=d;d<c.length;d++){var g=
-c[d];ea(g)&&!(fa(g)&&g.nodeType>0)?xa(sb(g)?Ca(g):g,f):f(g)}},tb=function(a){return a&&a.parentNode?a.parentNode.removeChild(a):j},ub=function(a,b){if(a.contains&&b.nodeType==1)return a==b||a.contains(b);if(typeof a.compareDocumentPosition!="undefined")return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a},kb=function(a){return a.nodeType==9?a:a.ownerDocument||a.document},vb=function(a,b){if("textContent"in a)a.textContent=b;else if(a.firstChild&&a.firstChild.nodeType==
-3){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else{for(var c;c=a.firstChild;)a.removeChild(c);a.appendChild(kb(a).createTextNode(b))}},wb={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},xb={IMG:" ",BR:"\n"},yb=function(a){var b=a.getAttributeNode("tabindex");if(b&&b.specified){a=a.tabIndex;return typeof a=="number"&&a>=0}return k},zb=function(a,b,c){if(!(a.nodeName in wb))if(a.nodeType==3)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);
-else if(a.nodeName in xb)b.push(xb[a.nodeName]);else for(a=a.firstChild;a;){zb(a,b,c);a=a.nextSibling}},sb=function(a){if(a&&typeof a.length=="number")if(fa(a))return typeof a.item=="function"||typeof a.item=="string";else if(u(a))return typeof a.item=="function";return k},jb=function(a){this.F=a||r.document||document};p=jb.prototype;p.Fa=lb;p.d=function(a){return t(a)?this.F.getElementById(a):a};p.m=function(){return pb(this.F,arguments)};p.createElement=function(a){return this.F.createElement(a)};
-p.createTextNode=function(a){return this.F.createTextNode(a)};p.appendChild=function(a,b){a.appendChild(b)};p.contains=ub;var Ab,Bb=!y||C("9"),Cb=y&&!C("8");var Db=function(){};Db.prototype.Sa=k;Db.prototype.M=function(){if(!this.Sa){this.Sa=h;this.f()}};Db.prototype.f=function(){};var F=function(a,b){this.type=a;this.currentTarget=this.target=b};w(F,Db);p=F.prototype;p.f=function(){delete this.type;delete this.target;delete this.currentTarget};p.ca=k;p.ua=h;p.stopPropagation=function(){this.ca=h};p.preventDefault=function(){this.ua=k};var G=function(a,b){a&&this.ta(a,b)};w(G,F);var Eb=[1,4,2];p=G.prototype;p.target=j;p.relatedTarget=j;p.offsetX=0;p.offsetY=0;p.clientX=0;p.clientY=0;p.screenX=0;p.screenY=0;p.button=0;p.keyCode=0;p.charCode=0;p.ctrlKey=k;p.altKey=k;p.shiftKey=k;p.metaKey=k;p.hc=k;p.N=j;
-p.ta=function(a,b){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(A)try{d=d.nodeName&&d}catch(f){d=j}}else if(c=="mouseover")d=a.fromElement;else if(c=="mouseout")d=a.toElement;this.relatedTarget=d;this.offsetX=a.offsetX!==undefined?a.offsetX:a.layerX;this.offsetY=a.offsetY!==undefined?a.offsetY:a.layerY;this.clientX=a.clientX!==undefined?a.clientX:a.pageX;this.clientY=a.clientY!==undefined?a.clientY:a.pageY;this.screenX=a.screenX||0;
-this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||(c=="keypress"?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.hc=Va?a.metaKey:a.ctrlKey;this.N=a;delete this.ua;delete this.ca};var Fb=function(a,b){return Bb?a.N.button==b:a.type=="click"?b==0:!!(a.N.button&Eb[b])};
-G.prototype.stopPropagation=function(){G.c.stopPropagation.call(this);if(this.N.stopPropagation)this.N.stopPropagation();else this.N.cancelBubble=h};G.prototype.preventDefault=function(){G.c.preventDefault.call(this);var a=this.N;if(a.preventDefault)a.preventDefault();else{a.returnValue=k;if(Cb)try{if(a.ctrlKey||a.keyCode>=112&&a.keyCode<=123)a.keyCode=-1}catch(b){}}};G.prototype.f=function(){G.c.f.call(this);this.relatedTarget=this.currentTarget=this.target=this.N=j};var H=function(a,b){this.xb=b;this.aa=[];if(a>this.xb)e(Error("[goog.structs.SimplePool] Initial cannot be greater than max"));for(var c=0;c<a;c++)this.aa.push(this.K?this.K():{})};w(H,Db);H.prototype.K=j;H.prototype.ob=j;var Gb=function(a){if(a.aa.length)return a.aa.pop();return a.K?a.K():{}},Ib=function(a,b){a.aa.length<a.xb?a.aa.push(b):Hb(a,b)},Hb=function(a,b){if(a.ob)a.ob(b);else if(fa(b))if(u(b.M))b.M();else for(var c in b)delete b[c]};
-H.prototype.f=function(){H.c.f.call(this);for(var a=this.aa;a.length;)Hb(this,a.pop());delete this.aa};var Jb;var Kb=(Jb="ScriptEngine"in r&&r.ScriptEngine()=="JScript")?r.ScriptEngineMajorVersion()+"."+r.ScriptEngineMinorVersion()+"."+r.ScriptEngineBuildVersion():"0";var Lb=function(){},Mb=0;p=Lb.prototype;p.key=0;p.ka=k;p.lb=k;p.ta=function(a,b,c,d,f,g){if(u(a))this.tb=h;else if(a&&a.handleEvent&&u(a.handleEvent))this.tb=k;else e(Error("Invalid listener argument"));this.ia=a;this.zb=b;this.src=c;this.type=d;this.capture=!!f;this.Ha=g;this.lb=k;this.key=++Mb;this.ka=k};p.handleEvent=function(a){if(this.tb)return this.ia.call(this.Ha||this.src,a);return this.ia.handleEvent.call(this.ia,a)};var Nb,Ob,Pb,Qb,Rb,Sb,Tb,Ub,Vb,Wb,Xb;
-(function(){function a(){return{D:0,A:0}}function b(){return[]}function c(){var q=function(I){return i.call(q.src,q.key,I)};return q}function d(){return new Lb}function f(){return new G}var g=Jb&&!(ta(Kb,"5.7")>=0),i;Sb=function(q){i=q};if(g){Nb=function(){return Gb(l)};Ob=function(q){Ib(l,q)};Pb=function(){return Gb(m)};Qb=function(q){Ib(m,q)};Rb=function(){return Gb(n)};Tb=function(){Ib(n,c())};Ub=function(){return Gb(z)};Vb=function(q){Ib(z,q)};Wb=function(){return Gb(o)};Xb=function(q){Ib(o,q)};
-var l=new H(0,600);l.K=a;var m=new H(0,600);m.K=b;var n=new H(0,600);n.K=c;var z=new H(0,600);z.K=d;var o=new H(0,600);o.K=f}else{Nb=a;Ob=ba;Pb=b;Qb=ba;Rb=c;Tb=ba;Ub=d;Vb=ba;Wb=f;Xb=ba}})();var Yb={},J={},K={},Zb={},L=function(a,b,c,d,f){if(b)if(s(b)){for(var g=0;g<b.length;g++)L(a,b[g],c,d,f);return j}else{d=!!d;var i=J;b in i||(i[b]=Nb());i=i[b];if(!(d in i)){i[d]=Nb();i.D++}i=i[d];var l=v(a),m;i.A++;if(i[l]){m=i[l];for(g=0;g<m.length;g++){i=m[g];if(i.ia==c&&i.Ha==f){if(i.ka)break;return m[g].key}}}else{m=i[l]=Pb();i.D++}g=Rb();g.src=a;i=Ub();i.ta(c,g,a,b,d,f);c=i.key;g.key=c;m.push(i);Yb[c]=i;K[l]||(K[l]=Pb());K[l].push(i);if(a.addEventListener){if(a==r||!a.nb)a.addEventListener(b,
-g,d)}else a.attachEvent($b(b),g);return c}else e(Error("Invalid event type"))},ac=function(a,b,c,d,f){if(s(b)){for(var g=0;g<b.length;g++)ac(a,b[g],c,d,f);return j}d=!!d;a=bc(a,b,d);if(!a)return k;for(g=0;g<a.length;g++)if(a[g].ia==c&&a[g].capture==d&&a[g].Ha==f)return M(a[g].key);return k},M=function(a){if(!Yb[a])return k;var b=Yb[a];if(b.ka)return k;var c=b.src,d=b.type,f=b.zb,g=b.capture;if(c.removeEventListener){if(c==r||!c.nb)c.removeEventListener(d,f,g)}else c.detachEvent&&c.detachEvent($b(d),
-f);c=v(c);f=J[d][g][c];if(K[c]){var i=K[c];Aa(i,b);i.length==0&&delete K[c]}b.ka=h;f.yb=h;cc(d,g,c,f);delete Yb[a];return h},cc=function(a,b,c,d){if(!d.Ka)if(d.yb){for(var f=0,g=0;f<d.length;f++)if(d[f].ka){var i=d[f].zb;i.src=j;Tb(i);Vb(d[f])}else{if(f!=g)d[g]=d[f];g++}d.length=g;d.yb=k;if(g==0){Qb(d);delete J[a][b][c];J[a][b].D--;if(J[a][b].D==0){Ob(J[a][b]);delete J[a][b];J[a].D--}if(J[a].D==0){Ob(J[a]);delete J[a]}}}},dc=function(a,b,c){var d=0,f=a==j,g=b==j,i=c==j;c=!!c;if(f)Fa(K,function(m){for(var n=
-m.length-1;n>=0;n--){var z=m[n];if((g||b==z.type)&&(i||c==z.capture)){M(z.key);d++}}});else{a=v(a);if(K[a]){a=K[a];for(f=a.length-1;f>=0;f--){var l=a[f];if((g||b==l.type)&&(i||c==l.capture)){M(l.key);d++}}}}return d},bc=function(a,b,c){var d=J;if(b in d){d=d[b];if(c in d){d=d[c];a=v(a);if(d[a])return d[a]}}return j},$b=function(a){if(a in Zb)return Zb[a];return Zb[a]="on"+a},fc=function(a,b,c,d,f){var g=1;b=v(b);if(a[b]){a.A--;a=a[b];if(a.Ka)a.Ka++;else a.Ka=1;try{for(var i=a.length,l=0;l<i;l++){var m=
-a[l];if(m&&!m.ka)g&=ec(m,f)!==k}}finally{a.Ka--;cc(c,d,b,a)}}return Boolean(g)},ec=function(a,b){var c=a.handleEvent(b);a.lb&&M(a.key);return c};
-Sb(function(a,b){if(!Yb[a])return h;var c=Yb[a],d=c.type,f=J;if(!(d in f))return h;f=f[d];var g,i;if(Ab===undefined)Ab=y&&!r.addEventListener;if(Ab){var l;if(!(l=b))a:{l="window.event".split(".");for(var m=r;g=l.shift();)if(m[g])m=m[g];else{l=j;break a}l=m}g=l;l=h in f;m=k in f;if(l){if(g.keyCode<0||g.returnValue!=undefined)return h;a:{var n=k;if(g.keyCode==0)try{g.keyCode=-1;break a}catch(z){n=h}if(n||g.returnValue==undefined)g.returnValue=h}}n=Wb();n.ta(g,this);g=h;try{if(l){for(var o=Pb(),q=n.currentTarget;q;q=
-q.parentNode)o.push(q);i=f[h];i.A=i.D;for(var I=o.length-1;!n.ca&&I>=0&&i.A;I--){n.currentTarget=o[I];g&=fc(i,o[I],d,h,n)}if(m){i=f[k];i.A=i.D;for(I=0;!n.ca&&I<o.length&&i.A;I++){n.currentTarget=o[I];g&=fc(i,o[I],d,k,n)}}}else g=ec(c,n)}finally{if(o){o.length=0;Qb(o)}n.M();Xb(n)}return g}d=new G(b,this);try{g=ec(c,d)}finally{d.M()}return g});var gc=function(a){this.rb=a};w(gc,Db);
-var hc=new H(0,100),N=function(a,b,c,d,f,g){if(s(c))for(var i=0;i<c.length;i++)N(a,b,c[i],d,f,g);else{b=L(b,c,d||a,f||k,g||a.rb||a);if(a.t)a.t[b]=h;else if(a.T){a.t=Gb(hc);a.t[a.T]=h;a.T=j;a.t[b]=h}else a.T=b}return a},O=function(a,b,c,d,f,g){if(a.T||a.t)if(s(c))for(var i=0;i<c.length;i++)O(a,b,c[i],d,f,g);else{a:{d=d||a;g=g||a.rb||a;f=!!(f||k);if(b=bc(b,c,f))for(c=0;c<b.length;c++)if(b[c].ia==d&&b[c].capture==f&&b[c].Ha==g){b=b[c];break a}b=j}if(b){b=b.key;M(b);if(a.t)Ga(a.t,b);else if(a.T==b)a.T=
-j}}return a},ic=function(a){if(a.t){for(var b in a.t){M(b);delete a.t[b]}Ib(hc,a.t);a.t=j}else a.T&&M(a.T)};gc.prototype.f=function(){gc.c.f.call(this);ic(this)};gc.prototype.handleEvent=function(){e(Error("EventHandler.handleEvent not implemented"))};var jc=function(){};w(jc,Db);p=jc.prototype;p.nb=h;p.La=j;p.hb=function(a){this.La=a};p.addEventListener=function(a,b,c,d){L(this,a,b,c,d)};p.removeEventListener=function(a,b,c,d){ac(this,a,b,c,d)};
-p.dispatchEvent=function(a){a=a;if(t(a))a=new F(a,this);else if(a instanceof F)a.target=a.target||this;else{var b=a;a=new F(a.type,this);Ka(a,b)}b=1;var c,d=a.type,f=J;if(d in f){f=f[d];d=h in f;var g;if(d){c=[];for(g=this;g;g=g.La)c.push(g);g=f[h];g.A=g.D;for(var i=c.length-1;!a.ca&&i>=0&&g.A;i--){a.currentTarget=c[i];b&=fc(g,c[i],a.type,h,a)&&a.ua!=k}}if(k in f){g=f[k];g.A=g.D;if(d)for(i=0;!a.ca&&i<c.length&&g.A;i++){a.currentTarget=c[i];b&=fc(g,c[i],a.type,k,a)&&a.ua!=k}else for(c=this;!a.ca&&
-c&&g.A;c=c.La){a.currentTarget=c;b&=fc(g,c,a.type,k,a)&&a.ua!=k}}a=Boolean(b)}else a=h;return a};p.f=function(){jc.c.f.call(this);dc(this);this.La=j};var kc=function(a,b){a.style.display=b?"":"none"},lc=A?"MozUserSelect":B?"WebkitUserSelect":j,mc=function(a,b,c){c=!c?a.getElementsByTagName("*"):j;if(lc){b=b?"none":"";a.style[lc]=b;if(c){a=0;for(var d;d=c[a];a++)d.style[lc]=b}}else if(y||Ta){b=b?"on":"";a.setAttribute("unselectable",b);if(c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)}};var nc=function(){};ca(nc);nc.prototype.dc=0;nc.Q();var P=function(a){this.G=a||lb();this.va=oc};w(P,jc);P.prototype.ac=nc.Q();var oc=j,pc=function(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}e(Error("Invalid component state"))};p=P.prototype;p.ga=j;p.e=k;p.b=j;p.va=j;p.cc=j;p.j=j;p.p=j;p.w=j;p.Db=k;
-var qc=function(a){return a.ga||(a.ga=":"+(a.ac.dc++).toString(36))},rc=function(a,b){if(a.j&&a.j.w){Ga(a.j.w,a.ga);Ha(a.j.w,b,a)}a.ga=b};P.prototype.d=function(){return this.b};var sc=function(a){return a.fa||(a.fa=new gc(a))},uc=function(a,b){if(a==b)e(Error("Unable to set parent component"));if(b&&a.j&&a.ga&&tc(a.j,a.ga)&&a.j!=b)e(Error("Unable to set parent component"));a.j=b;P.c.hb.call(a,b)};p=P.prototype;
-p.hb=function(a){if(this.j&&this.j!=a)e(Error("Method not supported"));P.c.hb.call(this,a)};p.Fa=function(){return this.G};p.m=function(){this.b=this.G.createElement("div")};p.L=function(a){if(this.e)e(Error("Component already rendered"));else if(a&&this.X(a)){this.Db=h;if(!this.G||this.G.F!=kb(a))this.G=lb(a);this.Ra(a);this.H()}else e(Error("Invalid element to decorate"))};p.X=function(){return h};p.Ra=function(a){this.b=a};p.H=function(){this.e=h;vc(this,function(a){!a.e&&a.d()&&a.H()})};
-p.$=function(){vc(this,function(a){a.e&&a.$()});this.fa&&ic(this.fa);this.e=k};p.f=function(){P.c.f.call(this);this.e&&this.$();if(this.fa){this.fa.M();delete this.fa}vc(this,function(a){a.M()});!this.Db&&this.b&&tb(this.b);this.j=this.cc=this.b=this.w=this.p=j};p.Ba=function(a,b){this.Pa(a,wc(this),b)};
-p.Pa=function(a,b,c){if(a.e&&(c||!this.e))e(Error("Component already rendered"));if(b<0||b>wc(this))e(Error("Child component index out of bounds"));if(!this.w||!this.p){this.w={};this.p=[]}if(a.j==this){this.w[qc(a)]=a;Aa(this.p,a)}else Ha(this.w,qc(a),a);uc(a,this);Ea(this.p,b,0,a);if(a.e&&this.e&&a.j==this){c=this.I();c.insertBefore(a.d(),c.childNodes[b]||j)}else if(c){this.b||this.m();c=Q(this,b+1);b=this.I();c=c?c.b:j;if(a.e)e(Error("Component already rendered"));a.b||a.m();b?b.insertBefore(a.b,
-c||j):a.G.F.body.appendChild(a.b);if(!a.j||a.j.e)a.H()}else this.e&&!a.e&&a.b&&a.H()};p.I=function(){return this.b};var xc=function(a){if(a.va==j){var b;a:{b=a.e?a.b:a.G.F.body;var c=kb(b);if(c.defaultView&&c.defaultView.getComputedStyle)if(b=c.defaultView.getComputedStyle(b,"")){b=b.direction;break a}b=j}a.va="rtl"==(b||((a.e?a.b:a.G.F.body).currentStyle?(a.e?a.b:a.G.F.body).currentStyle.direction:j)||(a.e?a.b:a.G.F.body).style.direction)}return a.va};
-P.prototype.ya=function(a){if(this.e)e(Error("Component already rendered"));this.va=a};var wc=function(a){return a.p?a.p.length:0},tc=function(a,b){var c;if(a.w&&b){c=a.w;c=b in c?c[b]:void 0;c=c||j}else c=j;return c},Q=function(a,b){return a.p?a.p[b]||j:j},vc=function(a,b,c){a.p&&xa(a.p,b,c)},yc=function(a,b){return a.p&&b?wa(a.p,b):-1};
-P.prototype.removeChild=function(a,b){if(a){var c=t(a)?a:qc(a);a=tc(this,c);if(c&&a){Ga(this.w,c);Aa(this.p,a);if(b){a.$();a.b&&tb(a.b)}uc(a,j)}}if(!a)e(Error("Child is not in parent component"));return a};var zc=function(a,b){if(A){a.setAttribute("role",b);a.mc=b}};var Bc=function(a,b,c,d,f){if(!y&&!(B&&C("525")))return h;if(Va&&f)return Ac(a);if(f&&!d)return k;if(!c&&(b==17||b==18))return k;if(y&&d&&b==a)return k;switch(a){case 13:return h;case 27:return!B}return Ac(a)},Ac=function(a){if(a>=48&&a<=57)return h;if(a>=96&&a<=106)return h;if(a>=65&&a<=90)return h;if(B&&a==0)return h;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return h;default:return k}};var R=function(a,b){a&&Cc(this,a,b)};w(R,jc);p=R.prototype;p.b=j;p.Ia=j;p.cb=j;p.Ja=j;p.U=-1;p.S=-1;
-var Dc={"3":13,"12":144,"63232":38,"63233":40,"63234":37,"63235":39,"63236":112,"63237":113,"63238":114,"63239":115,"63240":116,"63241":117,"63242":118,"63243":119,"63244":120,"63245":121,"63246":122,"63247":123,"63248":44,"63272":46,"63273":36,"63275":35,"63276":33,"63277":34,"63289":144,"63302":45},Ec={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Fc={61:187,
-59:186},Gc=y||B&&C("525");R.prototype.Tb=function(a){if(B&&(this.U==17&&!a.ctrlKey||this.U==18&&!a.altKey))this.S=this.U=-1;if(Gc&&!Bc(a.keyCode,this.U,a.shiftKey,a.ctrlKey,a.altKey))this.handleEvent(a);else this.S=A&&a.keyCode in Fc?Fc[a.keyCode]:a.keyCode};R.prototype.Ub=function(){this.S=this.U=-1};
-R.prototype.handleEvent=function(a){var b=a.N,c,d;if(y&&a.type=="keypress"){c=this.S;d=c!=13&&c!=27?b.keyCode:0}else if(B&&a.type=="keypress"){c=this.S;d=b.charCode>=0&&b.charCode<63232&&Ac(c)?b.charCode:0}else if(Ta){c=this.S;d=Ac(c)?b.keyCode:0}else{c=b.keyCode||this.S;d=b.charCode||0;if(Va&&d==63&&!c)c=191}var f=c,g=b.keyIdentifier;if(c)if(c>=63232&&c in Dc)f=Dc[c];else{if(c==25&&a.shiftKey)f=9}else if(g&&g in Ec)f=Ec[g];a=f==this.U;this.U=f;b=new Hc(f,d,a,b);try{this.dispatchEvent(b)}finally{b.M()}};
-R.prototype.d=function(){return this.b};var Cc=function(a,b,c){a.Ja&&a.detach();a.b=b;a.Ia=L(a.b,"keypress",a,c);a.cb=L(a.b,"keydown",a.Tb,c,a);a.Ja=L(a.b,"keyup",a.Ub,c,a)};R.prototype.detach=function(){if(this.Ia){M(this.Ia);M(this.cb);M(this.Ja);this.Ja=this.cb=this.Ia=j}this.b=j;this.S=this.U=-1};R.prototype.f=function(){R.c.f.call(this);this.detach()};var Hc=function(a,b,c,d){d&&this.ta(d,void 0);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};w(Hc,G);var Jc=function(a,b){if(!a)e(Error("Invalid class name "+a));if(!u(b))e(Error("Invalid decorator function "+b));Ic[a]=b},Kc={},Ic={};var Lc=function(){},Mc;ca(Lc);p=Lc.prototype;p.ea=function(){};p.m=function(a){return a.Fa().m("div",this.oa(a).join(" "),a.la)};p.I=function(a){return a};p.na=function(a,b,c){if(a=a.d?a.d():a)if(y&&!C("7")){var d=Nc(this,hb(a),b);d.push(b);ia(c?D:ib,a).apply(j,d)}else c?D(a,b):ib(a,b)};p.X=function(){return h};
-p.L=function(a,b){b.id&&rc(a,b.id);var c=this.I(b);if(c&&c.firstChild)Oc(a,c.firstChild.nextSibling?Ca(c.childNodes):c.firstChild);else a.la=j;var d=0,f=this.o(),g=this.o(),i=k,l=k;c=k;var m=hb(b);xa(m,function(o){if(!i&&o==f){i=h;if(g==f)l=h}else if(!l&&o==g)l=h;else{var q=d;if(!this.Ab){this.Ea||Pc(this);this.Ab=Ia(this.Ea)}o=parseInt(this.Ab[o],10);d=q|(isNaN(o)?0:o)}},this);a.l=d;if(!i){m.push(f);if(g==f)l=h}l||m.push(g);var n=a.z;n&&m.push.apply(m,n);if(y&&!C("7")){var z=Nc(this,m);if(z.length>
-0){m.push.apply(m,z);c=h}}if(!i||!l||n||c)b.className=m.join(" ");return b};p.bb=function(a){xc(a)&&this.ya(a.d(),h);a.i()&&this.xa(a,a.J())};p.Ma=function(a,b){mc(a,!b,!y&&!Ta)};p.ya=function(a,b){this.na(a,this.o()+"-rtl",b)};p.ba=function(a){var b;if(a.v&32&&(b=a.n()))return yb(b);return k};p.xa=function(a,b){var c;if(a.v&32&&(c=a.n())){if(!b&&a.l&32){try{c.blur()}catch(d){}a.l&32&&a.pa(j)}if(yb(c)!=b){c=c;if(b)c.tabIndex=0;else c.removeAttribute("tabIndex")}}};p.za=function(a,b){kc(a,b)};
-p.u=function(a,b,c){var d=a.d();if(d){var f=Qc(this,b);f&&this.na(a,f,c);if(A){Mc||(Mc=La(1,"disabled",4,"pressed",8,"selected",16,"checked",64,"expanded"));(a=Mc[b])&&A&&d.setAttribute("aria-"+a,c)}}};p.n=function(a){return a.d()};p.o=function(){return"goog-control"};p.oa=function(a){var b=this.o(),c=[b],d=this.o();d!=b&&c.push(d);b=a.l;for(d=[];b;){var f=b&-b;d.push(Qc(this,f));b&=~f}c.push.apply(c,d);(a=a.z)&&c.push.apply(c,a);y&&!C("7")&&c.push.apply(c,Nc(this,c));return c};
-var Nc=function(a,b,c){var d=[];if(c)b=b.concat([c]);xa([],function(f){if(ya(f,ia(za,b))&&(!c||za(f,c)))d.push(f.join("_"))});return d},Qc=function(a,b){a.Ea||Pc(a);return a.Ea[b]},Pc=function(a){var b=a.o();a.Ea=La(1,b+"-disabled",2,b+"-hover",4,b+"-active",8,b+"-selected",16,b+"-checked",32,b+"-focused",64,b+"-open")};var S=function(a,b,c){P.call(this,c);if(!(b=b)){b=this.constructor;for(var d;b;){d=v(b);if(d=Kc[d])break;b=b.c?b.c.constructor:j}b=d?u(d.Q)?d.Q():new d:j}this.a=b;this.la=a};w(S,P);p=S.prototype;p.la=j;p.l=0;p.v=39;p.Ib=255;p.Na=0;p.q=h;p.z=j;p.sa=h;p.Ca=k;p.n=function(){return this.a.n(this)};p.Ga=function(){return this.s||(this.s=new R)};p.pb=function(){return this.a};
-p.na=function(a,b){if(b){if(a){if(this.z)za(this.z,a)||this.z.push(a);else this.z=[a];this.a.na(this,a,h)}}else if(a&&this.z){Aa(this.z,a);if(this.z.length==0)this.z=j;this.a.na(this,a,k)}};p.m=function(){var a=this.a.m(this);this.b=a;if(A){var b=this.a.ea();b&&zc(a,b)}this.Ca||this.a.Ma(a,k);this.J()||this.a.za(a,k)};p.I=function(){return this.a.I(this.d())};p.X=function(a){return this.a.X(a)};
-p.Ra=function(a){this.b=a=this.a.L(this,a);if(A){var b=this.a.ea();b&&zc(a,b)}this.Ca||this.a.Ma(a,k);this.q=a.style.display!="none"};p.H=function(){S.c.H.call(this);this.a.bb(this);if(this.v&-2){this.sa&&Rc(this,h);if(this.v&32){var a=this.n();if(a){var b=this.Ga();Cc(b,a);N(N(N(sc(this),b,"key",this.R),a,"focus",this.qa),a,"blur",this.pa)}}}};
-var Rc=function(a,b){var c=sc(a),d=a.d();if(b){N(N(N(N(c,d,"mouseover",a.Za),d,"mousedown",a.ra),d,"mouseup",a.$a),d,"mouseout",a.Ya);y&&N(c,d,"dblclick",a.qb)}else{O(O(O(O(c,d,"mouseover",a.Za),d,"mousedown",a.ra),d,"mouseup",a.$a),d,"mouseout",a.Ya);y&&O(c,d,"dblclick",a.qb)}};S.prototype.$=function(){S.c.$.call(this);this.s&&this.s.detach();this.J()&&this.i()&&this.a.xa(this,k)};S.prototype.f=function(){S.c.f.call(this);if(this.s){this.s.M();delete this.s}delete this.a;this.z=this.la=j};
-var Oc=function(a,b){a.la=b};p=S.prototype;p.ya=function(a){S.c.ya.call(this,a);var b=this.d();b&&this.a.ya(b,a)};p.Ma=function(a){this.Ca=a;var b=this.d();b&&this.a.Ma(b,a)};p.J=function(){return this.q};p.za=function(a,b){if(b||this.q!=a&&this.dispatchEvent(a?"show":"hide")){var c=this.d();c&&this.a.za(c,a);this.i()&&this.a.xa(this,a);this.q=a;return h}return k};p.i=function(){return!!!(this.l&1)};
-p.wa=function(a){var b=this.j;if(!(b&&typeof b.i=="function"&&!b.i())&&T(this,1,!a)){if(!a){this.setActive(k);this.C(k)}this.J()&&this.a.xa(this,a);this.u(1,!a)}};p.C=function(a){T(this,2,a)&&this.u(2,a)};p.setActive=function(a){T(this,4,a)&&this.u(4,a)};var Sc=function(a,b){T(a,8,b)&&a.u(8,b)},Tc=function(a,b){T(a,64,b)&&a.u(64,b)};S.prototype.u=function(a,b){if(this.v&a&&b!=!!(this.l&a)){this.a.u(this,a,b);this.l=b?this.l|a:this.l&~a}};
-var Uc=function(a,b,c){if(a.e&&a.l&b&&!c)e(Error("Component already rendered"));!c&&a.l&b&&a.u(b,k);a.v=c?a.v|b:a.v&~b},U=function(a,b){return!!(a.Ib&b)&&!!(a.v&b)},T=function(a,b,c){return!!(a.v&b)&&!!(a.l&b)!=c&&(!(a.Na&b)||a.dispatchEvent(pc(b,c)))&&!a.Sa};S.prototype.Za=function(a){!Vc(a,this.d())&&this.dispatchEvent("enter")&&this.i()&&U(this,2)&&this.C(h)};S.prototype.Ya=function(a){if(!Vc(a,this.d())&&this.dispatchEvent("leave")){U(this,4)&&this.setActive(k);U(this,2)&&this.C(k)}};
-var Vc=function(a,b){return!!a.relatedTarget&&ub(b,a.relatedTarget)};S.prototype.ra=function(a){if(this.i()){U(this,2)&&this.C(h);if(Fb(a,0)){U(this,4)&&this.setActive(h);this.a.ba(this)&&this.n().focus()}}!this.Ca&&Fb(a,0)&&a.preventDefault()};S.prototype.$a=function(a){if(this.i()){U(this,2)&&this.C(h);this.l&4&&Wc(this,a)&&U(this,4)&&this.setActive(k)}};S.prototype.qb=function(a){this.i()&&Wc(this,a)};
-var Wc=function(a,b){if(U(a,16)){var c=!!!(a.l&16);T(a,16,c)&&a.u(16,c)}U(a,8)&&Sc(a,h);U(a,64)&&Tc(a,!!!(a.l&64));c=new F("action",a);if(b)for(var d=["altKey","ctrlKey","metaKey","shiftKey","platformModifierKey"],f,g=0;f=d[g];g++)c[f]=b[f];return a.dispatchEvent(c)};S.prototype.qa=function(){U(this,32)&&T(this,32,h)&&this.u(32,h)};S.prototype.pa=function(){U(this,4)&&this.setActive(k);U(this,32)&&T(this,32,k)&&this.u(32,k)};
-S.prototype.R=function(a){if(this.J()&&this.i()&&this.Xa(a)){a.preventDefault();a.stopPropagation();return h}return k};S.prototype.Xa=function(a){return a.keyCode==13&&Wc(this,a)};if(!u(S))e(Error("Invalid component class "+S));if(!u(Lc))e(Error("Invalid renderer class "+Lc));var Xc=v(S);Kc[Xc]=Lc;Jc("goog-control",function(){return new S(j)});var Yc=function(){};w(Yc,Lc);ca(Yc);Yc.prototype.m=function(a){return a.Fa().m("div",this.o())};Yc.prototype.L=function(a,b){if(b.tagName=="HR"){var c=b;b=this.m(a);c.parentNode&&c.parentNode.insertBefore(b,c);tb(c)}else D(b,this.o());return b};Yc.prototype.o=function(){return"goog-menuseparator"};var Zc=function(a,b){S.call(this,j,a||Yc.Q(),b);Uc(this,1,k);Uc(this,2,k);Uc(this,4,k);Uc(this,32,k);this.l=1};w(Zc,S);Zc.prototype.H=function(){Zc.c.H.call(this);zc(this.d(),"separator")};Jc("goog-menuseparator",function(){return new Zc});var V=function(){};ca(V);V.prototype.ea=function(){};var $c=function(a,b,c){if(b)b.tabIndex=c?0:-1};p=V.prototype;p.m=function(a){return a.Fa().m("div",this.oa(a).join(" "))};p.I=function(a){return a};p.X=function(a){return a.tagName=="DIV"};p.L=function(a,b){b.id&&rc(a,b.id);var c=this.o(),d=k,f=hb(b);f&&xa(f,function(g){if(g==c)d=h;else g&&this.ib(a,g,c)},this);d||D(b,c);ad(this,a,this.I(b));return b};
-p.ib=function(a,b,c){if(b==c+"-disabled")a.wa(k);else if(b==c+"-horizontal")bd(a,"horizontal");else b==c+"-vertical"&&bd(a,"vertical")};var ad=function(a,b,c,d){if(c)for(a=d||c.firstChild;a&&a.parentNode==c;){d=a.nextSibling;if(a.nodeType==1){var f;a:{f=void 0;for(var g=hb(a),i=0,l=g.length;i<l;i++)if(f=g[i]in Ic?Ic[g[i]]():j){f=f;break a}f=j}if(f){f.b=a;b.i()||f.wa(k);b.Ba(f);f.L(a)}}else if(!a.nodeValue||la(a.nodeValue)=="")c.removeChild(a);a=d}};
-V.prototype.bb=function(a){a=a.d();mc(a,h,A);if(y)a.hideFocus=h;var b=this.ea();b&&zc(a,b)};V.prototype.n=function(a){return a.d()};V.prototype.o=function(){return"goog-container"};V.prototype.oa=function(a){var b=this.o(),c=[b,a.V=="horizontal"?b+"-horizontal":b+"-vertical"];a.i()||c.push(b+"-disabled");return c};var W=function(a,b,c){P.call(this,c);this.a=b||V.Q();this.V=a||"vertical"};w(W,P);p=W.prototype;p.ub=j;p.s=j;p.a=j;p.V=j;p.q=h;p.Z=h;p.Va=h;p.k=-1;p.g=j;p.ja=k;p.Gb=k;p.gc=h;p.O=j;p.n=function(){return this.ub||this.a.n(this)};p.Ga=function(){return this.s||(this.s=new R(this.n()))};p.pb=function(){return this.a};p.m=function(){this.b=this.a.m(this)};p.I=function(){return this.a.I(this.d())};p.X=function(a){return this.a.X(a)};
-p.Ra=function(a){this.b=this.a.L(this,a);if(a.style.display=="none")this.q=k};p.H=function(){W.c.H.call(this);vc(this,function(b){b.e&&cd(this,b)},this);var a=this.d();this.a.bb(this);this.za(this.q,h);N(N(N(N(N(N(N(N(sc(this),this,"enter",this.Rb),this,"highlight",this.Sb),this,"unhighlight",this.$b),this,"open",this.Vb),this,"close",this.Pb),a,"mousedown",this.ra),kb(a),"mouseup",this.Qb),a,["mousedown","mouseup","mouseover","mouseout"],this.Ob);this.ba()&&dd(this,h)};
-var dd=function(a,b){var c=sc(a),d=a.n();b?N(N(N(c,d,"focus",a.qa),d,"blur",a.pa),a.Ga(),"key",a.R):O(O(O(c,d,"focus",a.qa),d,"blur",a.pa),a.Ga(),"key",a.R)};p=W.prototype;p.$=function(){ed(this,-1);this.g&&Tc(this.g,k);this.ja=k;W.c.$.call(this)};p.f=function(){W.c.f.call(this);if(this.s){this.s.M();this.s=j}this.a=this.g=this.O=j};p.Rb=function(){return h};
-p.Sb=function(a){var b=yc(this,a.target);if(b>-1&&b!=this.k){var c=Q(this,this.k);c&&c.C(k);this.k=b;c=Q(this,this.k);this.ja&&c.setActive(h);if(this.gc&&this.g&&c!=this.g)c.v&64?Tc(c,h):Tc(this.g,k)}b=this.d();a=a.target.d().id;A&&b.setAttribute("aria-activedescendant",a)};p.$b=function(a){if(a.target==Q(this,this.k))this.k=-1;a=this.d();A&&a.setAttribute("aria-activedescendant","")};p.Vb=function(a){if((a=a.target)&&a!=this.g&&a.j==this){this.g&&Tc(this.g,k);this.g=a}};
-p.Pb=function(a){if(a.target==this.g)this.g=j};p.ra=function(a){if(this.Z)this.ja=h;var b=this.n();b&&yb(b)?b.focus():a.preventDefault()};p.Qb=function(){this.ja=k};p.Ob=function(a){var b;a:{b=a.target;if(this.O)for(var c=this.d();b&&b!==c;){var d=b.id;if(d in this.O){b=this.O[d];break a}b=b.parentNode}b=j}if(b)switch(a.type){case "mousedown":b.ra(a);break;case "mouseup":b.$a(a);break;case "mouseover":b.Za(a);break;case "mouseout":b.Ya(a)}};p.qa=function(){};
-p.pa=function(){ed(this,-1);this.ja=k;this.g&&Tc(this.g,k)};p.R=function(a){if(this.i()&&this.J()&&(wc(this)!=0||this.ub)&&this.Xa(a)){a.preventDefault();a.stopPropagation();return h}return k};
-p.Xa=function(a){var b=Q(this,this.k);if(b&&typeof b.R=="function"&&b.R(a))return h;if(this.g&&this.g!=b&&typeof this.g.R=="function"&&this.g.R(a))return h;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return k;switch(a.keyCode){case 27:if(this.ba())this.n().blur();else return k;break;case 36:fd(this);break;case 35:gd(this);break;case 38:if(this.V=="vertical")hd(this);else return k;break;case 37:if(this.V=="horizontal")xc(this)?id(this):hd(this);else return k;break;case 40:if(this.V=="vertical")id(this);
-else return k;break;case 39:if(this.V=="horizontal")xc(this)?hd(this):id(this);else return k;break;default:return k}return h};var cd=function(a,b){var c=b.d();c=c.id||(c.id=qc(b));if(!a.O)a.O={};a.O[c]=b};W.prototype.Ba=function(a,b){W.c.Ba.call(this,a,b)};W.prototype.Pa=function(a,b,c){a.Na|=2;a.Na|=64;if(this.ba()||!this.Gb)Uc(a,32,k);a.e&&k!=a.sa&&Rc(a,k);a.sa=k;W.c.Pa.call(this,a,b,c);c&&this.e&&cd(this,a);b<=this.k&&this.k++};
-W.prototype.removeChild=function(a,b){if(a=t(a)?tc(this,a):a){var c=yc(this,a);if(c!=-1)if(c==this.k)a.C(k);else c<this.k&&this.k--;(c=a.d())&&c.id&&Ga(this.O,c.id)}c=a=W.c.removeChild.call(this,a,b);c.e&&h!=c.sa&&Rc(c,h);c.sa=h;return a};var bd=function(a,b){if(a.d())e(Error("Component already rendered"));a.V=b};p=W.prototype;p.J=function(){return this.q};
-p.za=function(a,b){if(b||this.q!=a&&this.dispatchEvent(a?"show":"hide")){this.q=a;var c=this.d();if(c){kc(c,a);this.ba()&&$c(this.a,this.n(),this.Z&&this.q);b||this.dispatchEvent(this.q?"aftershow":"afterhide")}return h}return k};p.i=function(){return this.Z};
-p.wa=function(a){if(this.Z!=a&&this.dispatchEvent(a?"enable":"disable")){if(a){this.Z=h;vc(this,function(b){if(b.Eb)delete b.Eb;else b.wa(h)})}else{vc(this,function(b){if(b.i())b.wa(k);else b.Eb=h});this.ja=this.Z=k}this.ba()&&$c(this.a,this.n(),a&&this.q)}};p.ba=function(){return this.Va};p.xa=function(a){a!=this.Va&&this.e&&dd(this,a);this.Va=a;this.Z&&this.q&&$c(this.a,this.n(),a)};var ed=function(a,b){var c=Q(a,b);if(c)c.C(h);else a.k>-1&&Q(a,a.k).C(k)};
-W.prototype.C=function(a){ed(this,yc(this,a))};var fd=function(a){jd(a,function(b,c){return(b+1)%c},wc(a)-1)},gd=function(a){jd(a,function(b,c){b--;return b<0?c-1:b},0)},id=function(a){jd(a,function(b,c){return(b+1)%c},a.k)},hd=function(a){jd(a,function(b,c){b--;return b<0?c-1:b},a.k)},jd=function(a,b,c){c=c<0?yc(a,a.g):c;var d=wc(a);c=b.call(a,c,d);for(var f=0;f<=d;){var g=Q(a,c);if(g&&g.J()&&g.i()&&g.v&2){a.gb(c);return h}f++;c=b.call(a,c,d)}return k};W.prototype.gb=function(a){ed(this,a)};var kd=function(){};w(kd,Lc);ca(kd);p=kd.prototype;p.o=function(){return"goog-tab"};p.ea=function(){return"tab"};p.m=function(a){var b=kd.c.m.call(this,a);(a=a.Wa())&&this.jb(b,a);return b};p.L=function(a,b){b=kd.c.L.call(this,a,b);var c=this.Wa(b);if(c)a.Cb=c;if(a.l&8)if((c=a.j)&&u(c.da)){a.u(8,k);c.da(a)}return b};p.Wa=function(a){return a.title||""};p.jb=function(a,b){if(a)a.title=b||""};var ld=function(a,b,c){S.call(this,a,b||kd.Q(),c);Uc(this,8,h);this.Na|=9};w(ld,S);ld.prototype.Wa=function(){return this.Cb};ld.prototype.jb=function(a){this.pb().jb(this.d(),a);this.Cb=a};Jc("goog-tab",function(){return new ld(j)});var X=function(){};w(X,V);ca(X);X.prototype.o=function(){return"goog-tab-bar"};X.prototype.ea=function(){return"tablist"};X.prototype.ib=function(a,b,c){if(!this.vb){this.Da||md(this);this.vb=Ia(this.Da)}var d=this.vb[b];if(d){bd(a,nd(d));a.wb=d}else X.c.ib.call(this,a,b,c)};X.prototype.oa=function(a){var b=X.c.oa.call(this,a);this.Da||md(this);b.push(this.Da[a.wb]);return b};var md=function(a){var b=a.o();a.Da=La("top",b+"-top","bottom",b+"-bottom","start",b+"-start","end",b+"-end")};var Y=function(a,b,c){a=a||"top";bd(this,nd(a));this.wb=a;W.call(this,this.V,b||X.Q(),c);b=sc(this);N(b,this,"select",this.Yb);N(b,this,"unselect",this.Zb);N(b,this,"disable",this.Wb);N(b,this,"hide",this.Xb)};w(Y,W);p=Y.prototype;p.Hb=h;p.B=j;p.f=function(){Y.c.f.call(this);this.B=j};p.removeChild=function(a,b){od(this,a);return Y.c.removeChild.call(this,a,b)};p.gb=function(a){Y.c.gb.call(this,a);this.Hb&&this.da(Q(this,a))};p.da=function(a){if(a)Sc(a,h);else this.B&&Sc(this.B,k)};
-var od=function(a,b){if(b&&b==a.B){for(var c=yc(a,b),d=c-1;b=Q(a,d);d--)if(b.J()&&b.i()){a.da(b);return}for(c=c+1;b=Q(a,c);c++)if(b.J()&&b.i()){a.da(b);return}a.da(j)}};p=Y.prototype;p.Yb=function(a){this.B&&this.B!=a.target&&Sc(this.B,k);this.B=a.target};p.Zb=function(a){if(a.target==this.B)this.B=j};p.Wb=function(a){od(this,a.target)};p.Xb=function(a){od(this,a.target)};p.qa=function(){Q(this,this.k)||this.C(this.B||Q(this,0))};var nd=function(a){return a=="start"||a=="end"?"vertical":"horizontal"};
-Jc("goog-tab-bar",function(){return new Y});var Z=function(a,b,c,d){function f(i){if(i){i.tabIndex=0;L(i,"click",g.ec,k,g);L(i,"keydown",g.fc,k,g)}}this.Y=E(a)||j;this.ma=E(d||j);this.Ta=(this.db=u(b)?b:j)||!b?j:E(b);this.h=c==h;var g=this;f(this.Y);f(this.ma);this.W(this.h)};w(Z,jc);Z.prototype.f=function(){this.Y&&dc(this.Y);this.ma&&dc(this.ma);Z.c.f.call(this)};
-Z.prototype.W=function(a){if(this.Ta)kc(this.Ta,a);else if(a&&this.db)this.Ta=this.db();if(this.ma){kc(this.Y,!a);kc(this.ma,a)}else if(this.Y){var b=this.Y;a?D(b,"goog-zippy-expanded"):ib(b,"goog-zippy-expanded");b=this.Y;!a?D(b,"goog-zippy-collapsed"):ib(b,"goog-zippy-collapsed")}this.h=a;this.dispatchEvent(new pd("toggle",this,this.h))};Z.prototype.fc=function(a){if(a.keyCode==13||a.keyCode==32){this.W(!this.h);a.preventDefault();a.stopPropagation()}};Z.prototype.ec=function(){this.W(!this.h)};
-var pd=function(a,b,c){F.call(this,a,b);this.kc=c};w(pd,F);var rd=function(a,b){this.kb=[];var c=E(a);c=mb(document,"span","ae-zippy",c);for(var d=0,f;f=c[d];d++){for(var g=f.parentNode.parentNode.parentNode.nextSibling;g&&g.nodeType!=1;)g=g.nextSibling;this.kb.push(new Z(f,g,k))}this.Lb=new qd(this.kb,E(b))};rd.prototype.Mb=function(){return this.Lb};rd.prototype.Nb=function(){return this.kb};
-var qd=function(a,b){this.Aa=a;if(this.Aa.length)for(var c=0,d;d=this.Aa[c];c++)L(d,"toggle",this.jc,k,this);this.eb=0;this.h=k;c="ae-toggle ae-plus ae-action";this.Aa.length||(c+=" ae-disabled");this.P=qb("span",{className:c},"Expand All");L(this.P,"click",this.Jb,k,this);b.appendChild(this.P)};qd.prototype.Jb=function(){this.Aa.length&&this.W(!this.h)};
-qd.prototype.jc=function(a){a=a.currentTarget;if(a.h)this.eb+=1;else this.eb-=1;if(a.h!=this.h)if(a.h){this.h=h;sd(this,h)}else if(this.eb==0){this.h=k;sd(this,k)}};qd.prototype.W=function(a){this.h=a;a=0;for(var b;b=this.Aa[a];a++)b.h!=this.h&&b.W(this.h);sd(this)};
-var sd=function(a,b){if(b!==undefined?b:a.h){ib(a.P,"ae-plus");D(a.P,"ae-minus");vb(a.P,"Collapse All")}else{ib(a.P,"ae-minus");D(a.P,"ae-plus");vb(a.P,"Expand All")}},td=function(a){this.ic=a;this.Bb={};var b,c=qb("div",{},b=qb("div",{id:"ae-stats-details-tabs",className:"goog-tab-bar goog-tab-bar-top"}),qb("div",{className:"goog-tab-bar-clear"}),a=qb("div",{id:"ae-stats-details-tabs-content",className:"goog-tab-content"})),d=new Y;d.L(b);L(d,"select",this.mb,k,this);L(d,"unselect",this.mb,k,this);
-b=0;for(var f;f=this.ic[b];b++)if(f=E("ae-stats-details-"+f)){var g=mb(document,"h2",j,f)[0],i;i=g;var l=void 0;if(gb&&"innerText"in i)l=i.innerText.replace(/(\r\n|\r|\n)/g,"\n");else{l=[];zb(i,l,h);l=l.join("")}l=l.replace(/ \xAD /g," ").replace(/\xAD/g,"");y||(l=l.replace(/ +/g," "));if(l!=" ")l=l.replace(/^\s*/,"");i=l;tb(g);g=new ld(i);this.Bb[v(g)]=f;d.Ba(g,h);a.appendChild(f);b==0?d.da(g):kc(f,k)}E("bd").appendChild(c)};td.prototype.mb=function(a){var b=this.Bb[v(a.target)];kc(b,a.type=="select")};
-aa("ae.Stats.Details.Tabs",td,void 0);aa("goog.ui.Zippy",Z,void 0);Z.prototype.setExpanded=Z.prototype.W;aa("ae.Stats.MakeZippys",rd,void 0);rd.prototype.getExpandCollapse=rd.prototype.Mb;rd.prototype.getZippys=rd.prototype.Nb;qd.prototype.setExpanded=qd.prototype.W;var $=function(){this.Qa=[];this.fb=[]},ud=[[5,0.2,1],[6,0.2,1.2],[5,0.25,1.25],[6,0.25,1.5],[4,0.5,2],[5,0.5,2.5],[6,0.5,3],[4,1,4],[5,1,5],[6,1,6],[4,2,8],[5,2,10]],vd=function(a){if(a<=0)return[2,0.5,1];for(var b=1;a<1;){a*=10;b/=10}for(;a>=10;){a/=10;b*=10}for(var c=0;c<ud.length;c++)if(a<=ud[c][2])return[ud[c][0],ud[c][1]*b,ud[c][2]*b];return[5,2*b,10*b]};$.prototype.Oa="stats/static/pix.gif";$.prototype.r="ae-stats-gantt-";$.prototype.ab=0;$.prototype.write=function(a){this.fb.push(a)};
-var wd=function(a,b,c,d){a.write('<tr class="'+a.r+'axisrow"><td width="20%"></td><td>');a.write('<div class="'+a.r+'axis">');for(var f=0;f<=b;f++){a.write('<img class="'+a.r+'tick" src="'+a.Oa+'" alt="" ');a.write('style="left:'+f*c*d+'%"\n>');a.write('<span class="'+a.r+'scale" style="left:'+f*c*d+'%">');a.write("&nbsp;"+f*c+"</span>")}a.write("</div></td></tr>\n")};
-$.prototype.Kb=function(){this.fb=[];var a=vd(this.ab),b=a[0],c=a[1];a=100/a[2];this.write('<table class="'+this.r+'table">\n');wd(this,b,c,a);for(var d=0;d<this.Qa.length;d++){var f=this.Qa[d];this.write('<tr class="'+this.r+'datarow"><td width="20%">');if(f.label.length>0){f.ha.length>0&&this.write('<a class="'+this.r+'link" href="'+f.ha+'">');this.write(f.label);f.ha.length>0&&this.write("</a>")}this.write("</td>\n<td>");this.write('<div class="'+this.r+'container">');f.ha.length>0&&this.write('<a class="'+
-this.r+'link" href="'+f.ha+'"\n>');this.write('<img class="'+this.r+'bar" src="'+this.Oa+'" alt="" ');this.write('style="left:'+f.start*a+"%;width:"+f.duration*a+'%;min-width:1px"\n>');if(f.Ua>0){this.write('<img class="'+this.r+'extra" src="'+this.Oa+'" alt="" ');this.write('style="left:'+f.start*a+"%;width:"+f.Ua*a+'%"\n>')}if(f.sb.length>0){this.write('<span class="'+this.r+'inline" style="left:'+(f.start+Math.max(f.duration,f.Ua))*a+'%">&nbsp;');this.write(f.sb);this.write("</span>")}f.ha.length>
-0&&this.write("</a>");this.write("</div></td></tr>\n")}wd(this,b,c,a);this.write("</table>\n");return this.fb.join("")};$.prototype.Fb=function(a,b,c,d,f,g){this.ab=Math.max(this.ab,Math.max(b+c,b+d));this.Qa.push({label:a,start:b,duration:c,Ua:d,sb:f,ha:g})};aa("Gantt",$,void 0);$.prototype.add_bar=$.prototype.Fb;$.prototype.draw=$.prototype.Kb;})();
+j);return arguments.length<=2?x.slice.call(a,b):x.slice.call(a,b,c)};var Fa=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},Ga=function(a,b){var c;if(c=b in a)delete a[b];return c},Ha=function(a,b,c){if(b in a)e(Error('The object already contains the key "'+b+'"'));a[b]=c},Ia=function(a){var b={},c;for(c in a)b[a[c]]=c;return b},Ja=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Ka=function(a){for(var b,c,d=1;d<arguments.length;d++){c=arguments[d];for(b in c)a[b]=c[b];for(var f=0;f<Ja.length;f++){b=Ja[f];
+if(Object.prototype.hasOwnProperty.call(c,b))a[b]=c[b]}}},La=function(){var a=arguments.length;if(a==1&&s(arguments[0]))return La.apply(j,arguments[0]);if(a%2)e(Error("Uneven number of arguments"));for(var b={},c=0;c<a;c+=2)b[arguments[c]]=arguments[c+1];return b};var Ma,Na,Oa,Pa,Qa=function(){return r.navigator?r.navigator.userAgent:j};Pa=Oa=Na=Ma=k;var Ra;if(Ra=Qa()){var Sa=r.navigator;Ma=Ra.indexOf("Opera")==0;Na=!Ma&&Ra.indexOf("MSIE")!=-1;Oa=!Ma&&Ra.indexOf("WebKit")!=-1;Pa=!Ma&&!Oa&&Sa.product=="Gecko"}var Ta=Ma,y=Na,A=Pa,B=Oa,Ua=r.navigator,Va=(Ua&&Ua.platform||"").indexOf("Mac")!=-1,Wa;
+a:{var Xa="",Ya;if(Ta&&r.opera){var Za=r.opera.version;Xa=typeof Za=="function"?Za():Za}else{if(A)Ya=/rv\:([^\);]+)(\)|;)/;else if(y)Ya=/MSIE\s+([^\);]+)(\)|;)/;else if(B)Ya=/WebKit\/(\S+)/;if(Ya){var $a=Ya.exec(Qa());Xa=$a?$a[1]:""}}if(y){var ab,bb=r.document;ab=bb?bb.documentMode:undefined;if(ab>parseFloat(Xa)){Wa=String(ab);break a}}Wa=Xa}var cb=Wa,db={},C=function(a){return db[a]||(db[a]=ta(cb,a)>=0)};var eb,fb=!y||C("9"),gb=y&&!C("9");var hb=function(a){return(a=a.className)&&typeof a.split=="function"?a.split(/\s+/):[]},D=function(a){var b=hb(a),c;c=Da(arguments,1);for(var d=0,f=0;f<c.length;f++)if(!za(b,c[f])){b.push(c[f]);d++}c=d==c.length;a.className=b.join(" ");return c},ib=function(a){var b=hb(a),c;c=Da(arguments,1);for(var d=0,f=0;f<b.length;f++)if(za(c,b[f])){Ea(b,f--,1);d++}c=d==c.length;a.className=b.join(" ");return c};var lb=function(a){return a?new jb(kb(a)):eb||(eb=new jb)},mb=function(a){return t(a)?document.getElementById(a):a},nb=function(a,b,c,d){a=d||a;b=b&&b!="*"?b.toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(!B||document.compatMode=="CSS1Compat"||C("528"))&&(b||c))return a.querySelectorAll(b+(c?"."+c:""));if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};for(var f=0,g=0,i;i=a[g];g++)if(b==i.nodeName)d[f++]=i;d.length=f;return d}else return a}a=a.getElementsByTagName(b||
+"*");if(c){d={};for(g=f=0;i=a[g];g++){b=i.className;if(typeof b.split=="function"&&za(b.split(/\s+/),c))d[f++]=i}d.length=f;return d}else return a},pb=function(a,b){Fa(b,function(c,d){if(d=="style")a.style.cssText=c;else if(d=="class")a.className=c;else if(d=="for")a.htmlFor=c;else if(d in ob)a.setAttribute(ob[d],c);else a[d]=c})},ob={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",height:"height",width:"width",usemap:"useMap",frameborder:"frameBorder",
+type:"type"},rb=function(){return qb(document,arguments)},qb=function(a,b){var c=b[0],d=b[1];if(!fb&&d&&(d.name||d.type)){c=["<",c];d.name&&c.push(' name="',ra(d.name),'"');if(d.type){c.push(' type="',ra(d.type),'"');var f={};Ka(f,d);d=f;delete d.type}c.push(">");c=c.join("")}c=a.createElement(c);if(d)if(t(d))c.className=d;else s(d)?D.apply(j,[c].concat(d)):pb(c,d);b.length>2&&sb(a,c,b,2);return c},sb=function(a,b,c,d){function f(l){if(l)b.appendChild(t(l)?a.createTextNode(l):l)}for(d=d;d<c.length;d++){var g=
+c[d];if(ea(g)&&!(fa(g)&&g.nodeType>0)){var i;a:{if(g&&typeof g.length=="number")if(fa(g)){i=typeof g.item=="function"||typeof g.item=="string";break a}else if(u(g)){i=typeof g.item=="function";break a}i=k}xa(i?Ca(g):g,f)}else f(g)}},tb=function(a){return a&&a.parentNode?a.parentNode.removeChild(a):j},ub=function(a,b){if(a.contains&&b.nodeType==1)return a==b||a.contains(b);if(typeof a.compareDocumentPosition!="undefined")return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;
+return b==a},kb=function(a){return a.nodeType==9?a:a.ownerDocument||a.document},vb=function(a,b){if("textContent"in a)a.textContent=b;else if(a.firstChild&&a.firstChild.nodeType==3){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else{for(var c;c=a.firstChild;)a.removeChild(c);a.appendChild(kb(a).createTextNode(b))}},wb={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},xb={IMG:" ",BR:"\n"},yb=function(a){var b=a.getAttributeNode("tabindex");if(b&&b.specified){a=a.tabIndex;
+return typeof a=="number"&&a>=0}return k},zb=function(a,b,c){if(!(a.nodeName in wb))if(a.nodeType==3)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in xb)b.push(xb[a.nodeName]);else for(a=a.firstChild;a;){zb(a,b,c);a=a.nextSibling}},jb=function(a){this.H=a||r.document||document};q=jb.prototype;q.Fa=lb;q.b=function(a){return t(a)?this.H.getElementById(a):a};q.m=function(){return qb(this.H,arguments)};q.createElement=function(a){return this.H.createElement(a)};
+q.createTextNode=function(a){return this.H.createTextNode(a)};q.appendChild=function(a,b){a.appendChild(b)};q.contains=ub;var Ab,Bb=!y||C("9"),Cb=y&&!C("8");var E=function(){};E.prototype.Sa=k;E.prototype.M=function(){if(!this.Sa){this.Sa=h;this.f()}};E.prototype.f=function(){};var F=function(a,b){this.type=a;this.currentTarget=this.target=b};w(F,E);q=F.prototype;q.f=function(){delete this.type;delete this.target;delete this.currentTarget};q.ca=k;q.ua=h;q.stopPropagation=function(){this.ca=h};q.preventDefault=function(){this.ua=k};var G=function(a,b){a&&this.ta(a,b)};w(G,F);var Db=[1,4,2];q=G.prototype;q.target=j;q.relatedTarget=j;q.offsetX=0;q.offsetY=0;q.clientX=0;q.clientY=0;q.screenX=0;q.screenY=0;q.button=0;q.keyCode=0;q.charCode=0;q.ctrlKey=k;q.altKey=k;q.shiftKey=k;q.metaKey=k;q.hc=k;q.N=j;
+q.ta=function(a,b){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(A)try{d=d.nodeName&&d}catch(f){d=j}}else if(c=="mouseover")d=a.fromElement;else if(c=="mouseout")d=a.toElement;this.relatedTarget=d;this.offsetX=a.offsetX!==undefined?a.offsetX:a.layerX;this.offsetY=a.offsetY!==undefined?a.offsetY:a.layerY;this.clientX=a.clientX!==undefined?a.clientX:a.pageX;this.clientY=a.clientY!==undefined?a.clientY:a.pageY;this.screenX=a.screenX||0;
+this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||(c=="keypress"?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.hc=Va?a.metaKey:a.ctrlKey;this.state=a.state;this.N=a;delete this.ua;delete this.ca};var Eb=function(a,b){return Bb?a.N.button==b:a.type=="click"?b==0:!!(a.N.button&Db[b])};
+G.prototype.stopPropagation=function(){G.c.stopPropagation.call(this);if(this.N.stopPropagation)this.N.stopPropagation();else this.N.cancelBubble=h};G.prototype.preventDefault=function(){G.c.preventDefault.call(this);var a=this.N;if(a.preventDefault)a.preventDefault();else{a.returnValue=k;if(Cb)try{if(a.ctrlKey||a.keyCode>=112&&a.keyCode<=123)a.keyCode=-1}catch(b){}}};G.prototype.f=function(){G.c.f.call(this);this.relatedTarget=this.currentTarget=this.target=this.N=j};var H=function(a,b){this.xb=b;this.aa=[];if(a>this.xb)e(Error("[goog.structs.SimplePool] Initial cannot be greater than max"));for(var c=0;c<a;c++)this.aa.push(this.K?this.K():{})};w(H,E);H.prototype.K=j;H.prototype.ob=j;var Fb=function(a){if(a.aa.length)return a.aa.pop();return a.K?a.K():{}},Hb=function(a,b){a.aa.length<a.xb?a.aa.push(b):Gb(a,b)},Gb=function(a,b){if(a.ob)a.ob(b);else if(fa(b))if(u(b.M))b.M();else for(var c in b)delete b[c]};
+H.prototype.f=function(){H.c.f.call(this);for(var a=this.aa;a.length;)Gb(this,a.pop());delete this.aa};var Ib;var Jb=(Ib="ScriptEngine"in r&&r.ScriptEngine()=="JScript")?r.ScriptEngineMajorVersion()+"."+r.ScriptEngineMinorVersion()+"."+r.ScriptEngineBuildVersion():"0";var Kb=function(){},Lb=0;q=Kb.prototype;q.key=0;q.ka=k;q.lb=k;q.ta=function(a,b,c,d,f,g){if(u(a))this.tb=h;else if(a&&a.handleEvent&&u(a.handleEvent))this.tb=k;else e(Error("Invalid listener argument"));this.ia=a;this.zb=b;this.src=c;this.type=d;this.capture=!!f;this.Ha=g;this.lb=k;this.key=++Lb;this.ka=k};q.handleEvent=function(a){if(this.tb)return this.ia.call(this.Ha||this.src,a);return this.ia.handleEvent.call(this.ia,a)};var Mb,Nb,Ob,Pb,Qb,Rb,Sb,Tb,Ub,Vb,Wb;
+(function(){function a(){return{G:0,C:0}}function b(){return[]}function c(){var p=function(I){return i.call(p.src,p.key,I)};return p}function d(){return new Kb}function f(){return new G}var g=Ib&&!(ta(Jb,"5.7")>=0),i;Rb=function(p){i=p};if(g){Mb=function(){return Fb(l)};Nb=function(p){Hb(l,p)};Ob=function(){return Fb(m)};Pb=function(p){Hb(m,p)};Qb=function(){return Fb(n)};Sb=function(){Hb(n,c())};Tb=function(){return Fb(z)};Ub=function(p){Hb(z,p)};Vb=function(){return Fb(o)};Wb=function(p){Hb(o,p)};
+var l=new H(0,600);l.K=a;var m=new H(0,600);m.K=b;var n=new H(0,600);n.K=c;var z=new H(0,600);z.K=d;var o=new H(0,600);o.K=f}else{Mb=a;Nb=ba;Ob=b;Pb=ba;Qb=c;Sb=ba;Tb=d;Ub=ba;Vb=f;Wb=ba}})();var Xb={},J={},K={},Yb={},L=function(a,b,c,d,f){if(b)if(s(b)){for(var g=0;g<b.length;g++)L(a,b[g],c,d,f);return j}else{d=!!d;var i=J;b in i||(i[b]=Mb());i=i[b];if(!(d in i)){i[d]=Mb();i.G++}i=i[d];var l=v(a),m;i.C++;if(i[l]){m=i[l];for(g=0;g<m.length;g++){i=m[g];if(i.ia==c&&i.Ha==f){if(i.ka)break;return m[g].key}}}else{m=i[l]=Ob();i.G++}g=Qb();g.src=a;i=Tb();i.ta(c,g,a,b,d,f);c=i.key;g.key=c;m.push(i);Xb[c]=i;K[l]||(K[l]=Ob());K[l].push(i);if(a.addEventListener){if(a==r||!a.nb)a.addEventListener(b,
+g,d)}else a.attachEvent(Zb(b),g);return c}else e(Error("Invalid event type"))},$b=function(a,b,c,d,f){if(s(b)){for(var g=0;g<b.length;g++)$b(a,b[g],c,d,f);return j}d=!!d;a=ac(a,b,d);if(!a)return k;for(g=0;g<a.length;g++)if(a[g].ia==c&&a[g].capture==d&&a[g].Ha==f)return M(a[g].key);return k},M=function(a){if(!Xb[a])return k;var b=Xb[a];if(b.ka)return k;var c=b.src,d=b.type,f=b.zb,g=b.capture;if(c.removeEventListener){if(c==r||!c.nb)c.removeEventListener(d,f,g)}else c.detachEvent&&c.detachEvent(Zb(d),
+f);c=v(c);f=J[d][g][c];if(K[c]){var i=K[c];Aa(i,b);i.length==0&&delete K[c]}b.ka=h;f.yb=h;bc(d,g,c,f);delete Xb[a];return h},bc=function(a,b,c,d){if(!d.Ka)if(d.yb){for(var f=0,g=0;f<d.length;f++)if(d[f].ka){var i=d[f].zb;i.src=j;Sb(i);Ub(d[f])}else{if(f!=g)d[g]=d[f];g++}d.length=g;d.yb=k;if(g==0){Pb(d);delete J[a][b][c];J[a][b].G--;if(J[a][b].G==0){Nb(J[a][b]);delete J[a][b];J[a].G--}if(J[a].G==0){Nb(J[a]);delete J[a]}}}},cc=function(a,b,c){var d=0,f=a==j,g=b==j,i=c==j;c=!!c;if(f)Fa(K,function(m){for(var n=
+m.length-1;n>=0;n--){var z=m[n];if((g||b==z.type)&&(i||c==z.capture)){M(z.key);d++}}});else{a=v(a);if(K[a]){a=K[a];for(f=a.length-1;f>=0;f--){var l=a[f];if((g||b==l.type)&&(i||c==l.capture)){M(l.key);d++}}}}return d},ac=function(a,b,c){var d=J;if(b in d){d=d[b];if(c in d){d=d[c];a=v(a);if(d[a])return d[a]}}return j},Zb=function(a){if(a in Yb)return Yb[a];return Yb[a]="on"+a},ec=function(a,b,c,d,f){var g=1;b=v(b);if(a[b]){a.C--;a=a[b];if(a.Ka)a.Ka++;else a.Ka=1;try{for(var i=a.length,l=0;l<i;l++){var m=
+a[l];if(m&&!m.ka)g&=dc(m,f)!==k}}finally{a.Ka--;bc(c,d,b,a)}}return Boolean(g)},dc=function(a,b){var c=a.handleEvent(b);a.lb&&M(a.key);return c};
+Rb(function(a,b){if(!Xb[a])return h;var c=Xb[a],d=c.type,f=J;if(!(d in f))return h;f=f[d];var g,i;if(Ab===undefined)Ab=y&&!r.addEventListener;if(Ab){var l;if(!(l=b))a:{l="window.event".split(".");for(var m=r;g=l.shift();)if(m[g])m=m[g];else{l=j;break a}l=m}g=l;l=h in f;m=k in f;if(l){if(g.keyCode<0||g.returnValue!=undefined)return h;a:{var n=k;if(g.keyCode==0)try{g.keyCode=-1;break a}catch(z){n=h}if(n||g.returnValue==undefined)g.returnValue=h}}n=Vb();n.ta(g,this);g=h;try{if(l){for(var o=Ob(),p=n.currentTarget;p;p=
+p.parentNode)o.push(p);i=f[h];i.C=i.G;for(var I=o.length-1;!n.ca&&I>=0&&i.C;I--){n.currentTarget=o[I];g&=ec(i,o[I],d,h,n)}if(m){i=f[k];i.C=i.G;for(I=0;!n.ca&&I<o.length&&i.C;I++){n.currentTarget=o[I];g&=ec(i,o[I],d,k,n)}}}else g=dc(c,n)}finally{if(o){o.length=0;Pb(o)}n.M();Wb(n)}return g}d=new G(b,this);try{g=dc(c,d)}finally{d.M()}return g});var fc=function(a){this.rb=a};w(fc,E);
+var gc=new H(0,100),N=function(a,b,c,d,f,g){if(s(c))for(var i=0;i<c.length;i++)N(a,b,c[i],d,f,g);else{b=L(b,c,d||a,f||k,g||a.rb||a);if(a.v)a.v[b]=h;else if(a.T){a.v=Fb(gc);a.v[a.T]=h;a.T=j;a.v[b]=h}else a.T=b}return a},O=function(a,b,c,d,f,g){if(a.T||a.v)if(s(c))for(var i=0;i<c.length;i++)O(a,b,c[i],d,f,g);else{a:{d=d||a;g=g||a.rb||a;f=!!(f||k);if(b=ac(b,c,f))for(c=0;c<b.length;c++)if(b[c].ia==d&&b[c].capture==f&&b[c].Ha==g){b=b[c];break a}b=j}if(b){b=b.key;M(b);if(a.v)Ga(a.v,b);else if(a.T==b)a.T=
+j}}return a},hc=function(a){if(a.v){for(var b in a.v){M(b);delete a.v[b]}Hb(gc,a.v);a.v=j}else a.T&&M(a.T)};fc.prototype.f=function(){fc.c.f.call(this);hc(this)};fc.prototype.handleEvent=function(){e(Error("EventHandler.handleEvent not implemented"))};var ic=function(){};w(ic,E);q=ic.prototype;q.nb=h;q.La=j;q.hb=function(a){this.La=a};q.addEventListener=function(a,b,c,d){L(this,a,b,c,d)};q.removeEventListener=function(a,b,c,d){$b(this,a,b,c,d)};
+q.dispatchEvent=function(a){a=a;if(t(a))a=new F(a,this);else if(a instanceof F)a.target=a.target||this;else{var b=a;a=new F(a.type,this);Ka(a,b)}b=1;var c,d=a.type,f=J;if(d in f){f=f[d];d=h in f;var g;if(d){c=[];for(g=this;g;g=g.La)c.push(g);g=f[h];g.C=g.G;for(var i=c.length-1;!a.ca&&i>=0&&g.C;i--){a.currentTarget=c[i];b&=ec(g,c[i],a.type,h,a)&&a.ua!=k}}if(k in f){g=f[k];g.C=g.G;if(d)for(i=0;!a.ca&&i<c.length&&g.C;i++){a.currentTarget=c[i];b&=ec(g,c[i],a.type,k,a)&&a.ua!=k}else for(c=this;!a.ca&&
+c&&g.C;c=c.La){a.currentTarget=c;b&=ec(g,c,a.type,k,a)&&a.ua!=k}}a=Boolean(b)}else a=h;return a};q.f=function(){ic.c.f.call(this);cc(this);this.La=j};var jc=function(a,b){a.style.display=b?"":"none"},kc=A?"MozUserSelect":B?"WebkitUserSelect":j,lc=function(a,b,c){c=!c?a.getElementsByTagName("*"):j;if(kc){b=b?"none":"";a.style[kc]=b;if(c){a=0;for(var d;d=c[a];a++)d.style[kc]=b}}else if(y||Ta){b=b?"on":"";a.setAttribute("unselectable",b);if(c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)}};var mc=function(){};ca(mc);mc.prototype.dc=0;mc.Q();var P=function(a){this.q=a||lb();this.va=nc};w(P,ic);P.prototype.ac=mc.Q();var nc=j,oc=function(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}e(Error("Invalid component state"))};q=P.prototype;q.ga=j;q.e=k;q.d=j;q.va=j;q.cc=j;q.j=j;q.p=j;q.A=j;q.Db=k;
+var pc=function(a){return a.ga||(a.ga=":"+(a.ac.dc++).toString(36))},qc=function(a,b){if(a.j&&a.j.A){Ga(a.j.A,a.ga);Ha(a.j.A,b,a)}a.ga=b};P.prototype.b=function(){return this.d};var rc=function(a){return a.fa||(a.fa=new fc(a))},tc=function(a,b){if(a==b)e(Error("Unable to set parent component"));if(b&&a.j&&a.ga&&sc(a.j,a.ga)&&a.j!=b)e(Error("Unable to set parent component"));a.j=b;P.c.hb.call(a,b)};q=P.prototype;
+q.hb=function(a){if(this.j&&this.j!=a)e(Error("Method not supported"));P.c.hb.call(this,a)};q.Fa=function(){return this.q};q.m=function(){this.d=this.q.createElement("div")};q.L=function(a){if(this.e)e(Error("Component already rendered"));else if(a&&this.X(a)){this.Db=h;if(!this.q||this.q.H!=kb(a))this.q=lb(a);this.Ra(a);this.t()}else e(Error("Invalid element to decorate"))};q.X=function(){return h};q.Ra=function(a){this.d=a};q.t=function(){this.e=h;uc(this,function(a){!a.e&&a.b()&&a.t()})};
+q.$=function(){uc(this,function(a){a.e&&a.$()});this.fa&&hc(this.fa);this.e=k};q.f=function(){P.c.f.call(this);this.e&&this.$();if(this.fa){this.fa.M();delete this.fa}uc(this,function(a){a.M()});!this.Db&&this.d&&tb(this.d);this.j=this.cc=this.d=this.A=this.p=j};q.Ba=function(a,b){this.Pa(a,vc(this),b)};
+q.Pa=function(a,b,c){if(a.e&&(c||!this.e))e(Error("Component already rendered"));if(b<0||b>vc(this))e(Error("Child component index out of bounds"));if(!this.A||!this.p){this.A={};this.p=[]}if(a.j==this){this.A[pc(a)]=a;Aa(this.p,a)}else Ha(this.A,pc(a),a);tc(a,this);Ea(this.p,b,0,a);if(a.e&&this.e&&a.j==this){c=this.I();c.insertBefore(a.b(),c.childNodes[b]||j)}else if(c){this.d||this.m();c=Q(this,b+1);b=this.I();c=c?c.d:j;if(a.e)e(Error("Component already rendered"));a.d||a.m();b?b.insertBefore(a.d,
+c||j):a.q.H.body.appendChild(a.d);if(!a.j||a.j.e)a.t()}else this.e&&!a.e&&a.d&&a.t()};q.I=function(){return this.d};var wc=function(a){if(a.va==j){var b;a:{b=a.e?a.d:a.q.H.body;var c=kb(b);if(c.defaultView&&c.defaultView.getComputedStyle)if(b=c.defaultView.getComputedStyle(b,"")){b=b.direction;break a}b=j}a.va="rtl"==(b||((a.e?a.d:a.q.H.body).currentStyle?(a.e?a.d:a.q.H.body).currentStyle.direction:j)||(a.e?a.d:a.q.H.body).style.direction)}return a.va};
+P.prototype.ya=function(a){if(this.e)e(Error("Component already rendered"));this.va=a};var vc=function(a){return a.p?a.p.length:0},sc=function(a,b){var c;if(a.A&&b){c=a.A;c=b in c?c[b]:void 0;c=c||j}else c=j;return c},Q=function(a,b){return a.p?a.p[b]||j:j},uc=function(a,b,c){a.p&&xa(a.p,b,c)},xc=function(a,b){return a.p&&b?wa(a.p,b):-1};
+P.prototype.removeChild=function(a,b){if(a){var c=t(a)?a:pc(a);a=sc(this,c);if(c&&a){Ga(this.A,c);Aa(this.p,a);if(b){a.$();a.d&&tb(a.d)}tc(a,j)}}if(!a)e(Error("Child is not in parent component"));return a};var yc=function(a,b){if(A){a.setAttribute("role",b);a.mc=b}};var Ac=function(a,b,c,d,f){if(!y&&!(B&&C("525")))return h;if(Va&&f)return zc(a);if(f&&!d)return k;if(!c&&(b==17||b==18))return k;if(y&&d&&b==a)return k;switch(a){case 13:return h;case 27:return!B}return zc(a)},zc=function(a){if(a>=48&&a<=57)return h;if(a>=96&&a<=106)return h;if(a>=65&&a<=90)return h;if(B&&a==0)return h;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return h;default:return k}};var R=function(a,b){a&&Bc(this,a,b)};w(R,ic);q=R.prototype;q.d=j;q.Ia=j;q.cb=j;q.Ja=j;q.U=-1;q.S=-1;
+var Cc={"3":13,"12":144,"63232":38,"63233":40,"63234":37,"63235":39,"63236":112,"63237":113,"63238":114,"63239":115,"63240":116,"63241":117,"63242":118,"63243":119,"63244":120,"63245":121,"63246":122,"63247":123,"63248":44,"63272":46,"63273":36,"63275":35,"63276":33,"63277":34,"63289":144,"63302":45},Dc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Ec={61:187,
+59:186},Fc=y||B&&C("525");R.prototype.Tb=function(a){if(B&&(this.U==17&&!a.ctrlKey||this.U==18&&!a.altKey))this.S=this.U=-1;if(Fc&&!Ac(a.keyCode,this.U,a.shiftKey,a.ctrlKey,a.altKey))this.handleEvent(a);else this.S=A&&a.keyCode in Ec?Ec[a.keyCode]:a.keyCode};R.prototype.Ub=function(){this.S=this.U=-1};
+R.prototype.handleEvent=function(a){var b=a.N,c,d;if(y&&a.type=="keypress"){c=this.S;d=c!=13&&c!=27?b.keyCode:0}else if(B&&a.type=="keypress"){c=this.S;d=b.charCode>=0&&b.charCode<63232&&zc(c)?b.charCode:0}else if(Ta){c=this.S;d=zc(c)?b.keyCode:0}else{c=b.keyCode||this.S;d=b.charCode||0;if(Va&&d==63&&!c)c=191}var f=c,g=b.keyIdentifier;if(c)if(c>=63232&&c in Cc)f=Cc[c];else{if(c==25&&a.shiftKey)f=9}else if(g&&g in Dc)f=Dc[g];a=f==this.U;this.U=f;b=new Gc(f,d,a,b);try{this.dispatchEvent(b)}finally{b.M()}};
+R.prototype.b=function(){return this.d};var Bc=function(a,b,c){a.Ja&&a.detach();a.d=b;a.Ia=L(a.d,"keypress",a,c);a.cb=L(a.d,"keydown",a.Tb,c,a);a.Ja=L(a.d,"keyup",a.Ub,c,a)};R.prototype.detach=function(){if(this.Ia){M(this.Ia);M(this.cb);M(this.Ja);this.Ja=this.cb=this.Ia=j}this.d=j;this.S=this.U=-1};R.prototype.f=function(){R.c.f.call(this);this.detach()};var Gc=function(a,b,c,d){d&&this.ta(d,void 0);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};w(Gc,G);var Ic=function(a,b){if(!a)e(Error("Invalid class name "+a));if(!u(b))e(Error("Invalid decorator function "+b));Hc[a]=b},Jc={},Hc={};var Kc=function(){},Lc;ca(Kc);q=Kc.prototype;q.ea=function(){};q.m=function(a){return a.Fa().m("div",this.oa(a).join(" "),a.la)};q.I=function(a){return a};q.na=function(a,b,c){if(a=a.b?a.b():a)if(y&&!C("7")){var d=Mc(this,hb(a),b);d.push(b);ia(c?D:ib,a).apply(j,d)}else c?D(a,b):ib(a,b)};q.X=function(){return h};
+q.L=function(a,b){b.id&&qc(a,b.id);var c=this.I(b);if(c&&c.firstChild)Nc(a,c.firstChild.nextSibling?Ca(c.childNodes):c.firstChild);else a.la=j;var d=0,f=this.o(),g=this.o(),i=k,l=k;c=k;var m=hb(b);xa(m,function(o){if(!i&&o==f){i=h;if(g==f)l=h}else if(!l&&o==g)l=h;else{var p=d;if(!this.Ab){this.Ea||Oc(this);this.Ab=Ia(this.Ea)}o=parseInt(this.Ab[o],10);d=p|(isNaN(o)?0:o)}},this);a.l=d;if(!i){m.push(f);if(g==f)l=h}l||m.push(g);var n=a.B;n&&m.push.apply(m,n);if(y&&!C("7")){var z=Mc(this,m);if(z.length>
+0){m.push.apply(m,z);c=h}}if(!i||!l||n||c)b.className=m.join(" ");return b};q.bb=function(a){wc(a)&&this.ya(a.b(),h);a.i()&&this.xa(a,a.J())};q.Ma=function(a,b){lc(a,!b,!y&&!Ta)};q.ya=function(a,b){this.na(a,this.o()+"-rtl",b)};q.ba=function(a){var b;if(a.z&32&&(b=a.n()))return yb(b);return k};q.xa=function(a,b){var c;if(a.z&32&&(c=a.n())){if(!b&&a.l&32){try{c.blur()}catch(d){}a.l&32&&a.pa(j)}if(yb(c)!=b){c=c;if(b)c.tabIndex=0;else c.removeAttribute("tabIndex")}}};q.za=function(a,b){jc(a,b)};
+q.w=function(a,b,c){var d=a.b();if(d){var f=Pc(this,b);f&&this.na(a,f,c);if(A){Lc||(Lc=La(1,"disabled",4,"pressed",8,"selected",16,"checked",64,"expanded"));(a=Lc[b])&&A&&d.setAttribute("aria-"+a,c)}}};q.n=function(a){return a.b()};q.o=function(){return"goog-control"};q.oa=function(a){var b=this.o(),c=[b],d=this.o();d!=b&&c.push(d);b=a.l;for(d=[];b;){var f=b&-b;d.push(Pc(this,f));b&=~f}c.push.apply(c,d);(a=a.B)&&c.push.apply(c,a);y&&!C("7")&&c.push.apply(c,Mc(this,c));return c};
+var Mc=function(a,b,c){var d=[];if(c)b=b.concat([c]);xa([],function(f){if(ya(f,ia(za,b))&&(!c||za(f,c)))d.push(f.join("_"))});return d},Pc=function(a,b){a.Ea||Oc(a);return a.Ea[b]},Oc=function(a){var b=a.o();a.Ea=La(1,b+"-disabled",2,b+"-hover",4,b+"-active",8,b+"-selected",16,b+"-checked",32,b+"-focused",64,b+"-open")};var S=function(a,b,c){P.call(this,c);if(!(b=b)){b=this.constructor;for(var d;b;){d=v(b);if(d=Jc[d])break;b=b.c?b.c.constructor:j}b=d?u(d.Q)?d.Q():new d:j}this.a=b;this.la=a};w(S,P);q=S.prototype;q.la=j;q.l=0;q.z=39;q.Ib=255;q.Na=0;q.r=h;q.B=j;q.sa=h;q.Ca=k;q.n=function(){return this.a.n(this)};q.Ga=function(){return this.u||(this.u=new R)};q.pb=function(){return this.a};
+q.na=function(a,b){if(b){if(a){if(this.B)za(this.B,a)||this.B.push(a);else this.B=[a];this.a.na(this,a,h)}}else if(a&&this.B){Aa(this.B,a);if(this.B.length==0)this.B=j;this.a.na(this,a,k)}};q.m=function(){var a=this.a.m(this);this.d=a;if(A){var b=this.a.ea();b&&yc(a,b)}this.Ca||this.a.Ma(a,k);this.J()||this.a.za(a,k)};q.I=function(){return this.a.I(this.b())};q.X=function(a){return this.a.X(a)};
+q.Ra=function(a){this.d=a=this.a.L(this,a);if(A){var b=this.a.ea();b&&yc(a,b)}this.Ca||this.a.Ma(a,k);this.r=a.style.display!="none"};q.t=function(){S.c.t.call(this);this.a.bb(this);if(this.z&-2){this.sa&&Qc(this,h);if(this.z&32){var a=this.n();if(a){var b=this.Ga();Bc(b,a);N(N(N(rc(this),b,"key",this.R),a,"focus",this.qa),a,"blur",this.pa)}}}};
+var Qc=function(a,b){var c=rc(a),d=a.b();if(b){N(N(N(N(c,d,"mouseover",a.Za),d,"mousedown",a.ra),d,"mouseup",a.$a),d,"mouseout",a.Ya);y&&N(c,d,"dblclick",a.qb)}else{O(O(O(O(c,d,"mouseover",a.Za),d,"mousedown",a.ra),d,"mouseup",a.$a),d,"mouseout",a.Ya);y&&O(c,d,"dblclick",a.qb)}};S.prototype.$=function(){S.c.$.call(this);this.u&&this.u.detach();this.J()&&this.i()&&this.a.xa(this,k)};S.prototype.f=function(){S.c.f.call(this);if(this.u){this.u.M();delete this.u}delete this.a;this.B=this.la=j};
+var Nc=function(a,b){a.la=b};q=S.prototype;q.ya=function(a){S.c.ya.call(this,a);var b=this.b();b&&this.a.ya(b,a)};q.Ma=function(a){this.Ca=a;var b=this.b();b&&this.a.Ma(b,a)};q.J=function(){return this.r};q.za=function(a,b){if(b||this.r!=a&&this.dispatchEvent(a?"show":"hide")){var c=this.b();c&&this.a.za(c,a);this.i()&&this.a.xa(this,a);this.r=a;return h}return k};q.i=function(){return!!!(this.l&1)};
+q.wa=function(a){var b=this.j;if(!(b&&typeof b.i=="function"&&!b.i())&&T(this,1,!a)){if(!a){this.setActive(k);this.F(k)}this.J()&&this.a.xa(this,a);this.w(1,!a)}};q.F=function(a){T(this,2,a)&&this.w(2,a)};q.setActive=function(a){T(this,4,a)&&this.w(4,a)};var Rc=function(a,b){T(a,8,b)&&a.w(8,b)},Sc=function(a,b){T(a,64,b)&&a.w(64,b)};S.prototype.w=function(a,b){if(this.z&a&&b!=!!(this.l&a)){this.a.w(this,a,b);this.l=b?this.l|a:this.l&~a}};
+var Tc=function(a,b,c){if(a.e&&a.l&b&&!c)e(Error("Component already rendered"));!c&&a.l&b&&a.w(b,k);a.z=c?a.z|b:a.z&~b},U=function(a,b){return!!(a.Ib&b)&&!!(a.z&b)},T=function(a,b,c){return!!(a.z&b)&&!!(a.l&b)!=c&&(!(a.Na&b)||a.dispatchEvent(oc(b,c)))&&!a.Sa};q=S.prototype;q.Za=function(a){!(a.relatedTarget&&ub(this.b(),a.relatedTarget))&&this.dispatchEvent("enter")&&this.i()&&U(this,2)&&this.F(h)};
+q.Ya=function(a){if(!(a.relatedTarget&&ub(this.b(),a.relatedTarget))&&this.dispatchEvent("leave")){U(this,4)&&this.setActive(k);U(this,2)&&this.F(k)}};q.ra=function(a){if(this.i()){U(this,2)&&this.F(h);if(Eb(a,0)){U(this,4)&&this.setActive(h);this.a.ba(this)&&this.n().focus()}}!this.Ca&&Eb(a,0)&&a.preventDefault()};q.$a=function(a){if(this.i()){U(this,2)&&this.F(h);this.l&4&&Uc(this,a)&&U(this,4)&&this.setActive(k)}};q.qb=function(a){this.i()&&Uc(this,a)};
+var Uc=function(a,b){if(U(a,16)){var c=!!!(a.l&16);T(a,16,c)&&a.w(16,c)}U(a,8)&&Rc(a,h);U(a,64)&&Sc(a,!!!(a.l&64));c=new F("action",a);if(b)for(var d=["altKey","ctrlKey","metaKey","shiftKey","platformModifierKey"],f,g=0;f=d[g];g++)c[f]=b[f];return a.dispatchEvent(c)};S.prototype.qa=function(){U(this,32)&&T(this,32,h)&&this.w(32,h)};S.prototype.pa=function(){U(this,4)&&this.setActive(k);U(this,32)&&T(this,32,k)&&this.w(32,k)};
+S.prototype.R=function(a){if(this.J()&&this.i()&&this.Xa(a)){a.preventDefault();a.stopPropagation();return h}return k};S.prototype.Xa=function(a){return a.keyCode==13&&Uc(this,a)};if(!u(S))e(Error("Invalid component class "+S));if(!u(Kc))e(Error("Invalid renderer class "+Kc));var Vc=v(S);Jc[Vc]=Kc;Ic("goog-control",function(){return new S(j)});var Wc=function(){};w(Wc,Kc);ca(Wc);Wc.prototype.m=function(a){return a.Fa().m("div",this.o())};Wc.prototype.L=function(a,b){if(b.tagName=="HR"){var c=b;b=this.m(a);c.parentNode&&c.parentNode.insertBefore(b,c);tb(c)}else D(b,this.o());return b};Wc.prototype.o=function(){return"goog-menuseparator"};var Xc=function(a,b){S.call(this,j,a||Wc.Q(),b);Tc(this,1,k);Tc(this,2,k);Tc(this,4,k);Tc(this,32,k);this.l=1};w(Xc,S);Xc.prototype.t=function(){Xc.c.t.call(this);yc(this.b(),"separator")};Ic("goog-menuseparator",function(){return new Xc});var V=function(){};ca(V);V.prototype.ea=function(){};var Yc=function(a,b,c){if(b)b.tabIndex=c?0:-1};q=V.prototype;q.m=function(a){return a.Fa().m("div",this.oa(a).join(" "))};q.I=function(a){return a};q.X=function(a){return a.tagName=="DIV"};q.L=function(a,b){b.id&&qc(a,b.id);var c=this.o(),d=k,f=hb(b);f&&xa(f,function(g){if(g==c)d=h;else g&&this.ib(a,g,c)},this);d||D(b,c);Zc(this,a,this.I(b));return b};
+q.ib=function(a,b,c){if(b==c+"-disabled")a.wa(k);else if(b==c+"-horizontal")$c(a,"horizontal");else b==c+"-vertical"&&$c(a,"vertical")};var Zc=function(a,b,c,d){if(c)for(a=d||c.firstChild;a&&a.parentNode==c;){d=a.nextSibling;if(a.nodeType==1){var f;a:{f=void 0;for(var g=hb(a),i=0,l=g.length;i<l;i++)if(f=g[i]in Hc?Hc[g[i]]():j){f=f;break a}f=j}if(f){f.d=a;b.i()||f.wa(k);b.Ba(f);f.L(a)}}else if(!a.nodeValue||la(a.nodeValue)=="")c.removeChild(a);a=d}};
+V.prototype.bb=function(a){a=a.b();lc(a,h,A);if(y)a.hideFocus=h;var b=this.ea();b&&yc(a,b)};V.prototype.n=function(a){return a.b()};V.prototype.o=function(){return"goog-container"};V.prototype.oa=function(a){var b=this.o(),c=[b,a.V=="horizontal"?b+"-horizontal":b+"-vertical"];a.i()||c.push(b+"-disabled");return c};var W=function(a,b,c){P.call(this,c);this.a=b||V.Q();this.V=a||"vertical"};w(W,P);q=W.prototype;q.ub=j;q.u=j;q.a=j;q.V=j;q.r=h;q.Z=h;q.Va=h;q.k=-1;q.g=j;q.ja=k;q.Gb=k;q.gc=h;q.O=j;q.n=function(){return this.ub||this.a.n(this)};q.Ga=function(){return this.u||(this.u=new R(this.n()))};q.pb=function(){return this.a};q.m=function(){this.d=this.a.m(this)};q.I=function(){return this.a.I(this.b())};q.X=function(a){return this.a.X(a)};
+q.Ra=function(a){this.d=this.a.L(this,a);if(a.style.display=="none")this.r=k};q.t=function(){W.c.t.call(this);uc(this,function(b){b.e&&ad(this,b)},this);var a=this.b();this.a.bb(this);this.za(this.r,h);N(N(N(N(N(N(N(N(rc(this),this,"enter",this.Rb),this,"highlight",this.Sb),this,"unhighlight",this.$b),this,"open",this.Vb),this,"close",this.Pb),a,"mousedown",this.ra),kb(a),"mouseup",this.Qb),a,["mousedown","mouseup","mouseover","mouseout"],this.Ob);this.ba()&&bd(this,h)};
+var bd=function(a,b){var c=rc(a),d=a.n();b?N(N(N(c,d,"focus",a.qa),d,"blur",a.pa),a.Ga(),"key",a.R):O(O(O(c,d,"focus",a.qa),d,"blur",a.pa),a.Ga(),"key",a.R)};q=W.prototype;q.$=function(){cd(this,-1);this.g&&Sc(this.g,k);this.ja=k;W.c.$.call(this)};q.f=function(){W.c.f.call(this);if(this.u){this.u.M();this.u=j}this.a=this.g=this.O=j};q.Rb=function(){return h};
+q.Sb=function(a){var b=xc(this,a.target);if(b>-1&&b!=this.k){var c=Q(this,this.k);c&&c.F(k);this.k=b;c=Q(this,this.k);this.ja&&c.setActive(h);if(this.gc&&this.g&&c!=this.g)c.z&64?Sc(c,h):Sc(this.g,k)}A&&this.b().setAttribute("aria-activedescendant",a.target.b().id)};q.$b=function(a){if(a.target==Q(this,this.k))this.k=-1;A&&this.b().setAttribute("aria-activedescendant","")};q.Vb=function(a){if((a=a.target)&&a!=this.g&&a.j==this){this.g&&Sc(this.g,k);this.g=a}};
+q.Pb=function(a){if(a.target==this.g)this.g=j};q.ra=function(a){if(this.Z)this.ja=h;var b=this.n();b&&yb(b)?b.focus():a.preventDefault()};q.Qb=function(){this.ja=k};q.Ob=function(a){var b;a:{b=a.target;if(this.O)for(var c=this.b();b&&b!==c;){var d=b.id;if(d in this.O){b=this.O[d];break a}b=b.parentNode}b=j}if(b)switch(a.type){case "mousedown":b.ra(a);break;case "mouseup":b.$a(a);break;case "mouseover":b.Za(a);break;case "mouseout":b.Ya(a)}};q.qa=function(){};
+q.pa=function(){cd(this,-1);this.ja=k;this.g&&Sc(this.g,k)};q.R=function(a){if(this.i()&&this.J()&&(vc(this)!=0||this.ub)&&this.Xa(a)){a.preventDefault();a.stopPropagation();return h}return k};
+q.Xa=function(a){var b=Q(this,this.k);if(b&&typeof b.R=="function"&&b.R(a))return h;if(this.g&&this.g!=b&&typeof this.g.R=="function"&&this.g.R(a))return h;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return k;switch(a.keyCode){case 27:if(this.ba())this.n().blur();else return k;break;case 36:dd(this);break;case 35:ed(this);break;case 38:if(this.V=="vertical")fd(this);else return k;break;case 37:if(this.V=="horizontal")wc(this)?gd(this):fd(this);else return k;break;case 40:if(this.V=="vertical")gd(this);
+else return k;break;case 39:if(this.V=="horizontal")wc(this)?fd(this):gd(this);else return k;break;default:return k}return h};var ad=function(a,b){var c=b.b();c=c.id||(c.id=pc(b));if(!a.O)a.O={};a.O[c]=b};W.prototype.Ba=function(a,b){W.c.Ba.call(this,a,b)};W.prototype.Pa=function(a,b,c){a.Na|=2;a.Na|=64;if(this.ba()||!this.Gb)Tc(a,32,k);a.e&&k!=a.sa&&Qc(a,k);a.sa=k;W.c.Pa.call(this,a,b,c);c&&this.e&&ad(this,a);b<=this.k&&this.k++};
+W.prototype.removeChild=function(a,b){if(a=t(a)?sc(this,a):a){var c=xc(this,a);if(c!=-1)if(c==this.k)a.F(k);else c<this.k&&this.k--;(c=a.b())&&c.id&&Ga(this.O,c.id)}c=a=W.c.removeChild.call(this,a,b);c.e&&h!=c.sa&&Qc(c,h);c.sa=h;return a};var $c=function(a,b){if(a.b())e(Error("Component already rendered"));a.V=b};q=W.prototype;q.J=function(){return this.r};
+q.za=function(a,b){if(b||this.r!=a&&this.dispatchEvent(a?"show":"hide")){this.r=a;var c=this.b();if(c){jc(c,a);this.ba()&&Yc(this.a,this.n(),this.Z&&this.r);b||this.dispatchEvent(this.r?"aftershow":"afterhide")}return h}return k};q.i=function(){return this.Z};
+q.wa=function(a){if(this.Z!=a&&this.dispatchEvent(a?"enable":"disable")){if(a){this.Z=h;uc(this,function(b){if(b.Eb)delete b.Eb;else b.wa(h)})}else{uc(this,function(b){if(b.i())b.wa(k);else b.Eb=h});this.ja=this.Z=k}this.ba()&&Yc(this.a,this.n(),a&&this.r)}};q.ba=function(){return this.Va};q.xa=function(a){a!=this.Va&&this.e&&bd(this,a);this.Va=a;this.Z&&this.r&&Yc(this.a,this.n(),a)};var cd=function(a,b){var c=Q(a,b);if(c)c.F(h);else a.k>-1&&Q(a,a.k).F(k)};
+W.prototype.F=function(a){cd(this,xc(this,a))};var dd=function(a){hd(a,function(b,c){return(b+1)%c},vc(a)-1)},ed=function(a){hd(a,function(b,c){b--;return b<0?c-1:b},0)},gd=function(a){hd(a,function(b,c){return(b+1)%c},a.k)},fd=function(a){hd(a,function(b,c){b--;return b<0?c-1:b},a.k)},hd=function(a,b,c){c=c<0?xc(a,a.g):c;var d=vc(a);c=b.call(a,c,d);for(var f=0;f<=d;){var g=Q(a,c);if(g&&g.J()&&g.i()&&g.z&2){a.gb(c);return h}f++;c=b.call(a,c,d)}return k};W.prototype.gb=function(a){cd(this,a)};var id=function(){};w(id,Kc);ca(id);q=id.prototype;q.o=function(){return"goog-tab"};q.ea=function(){return"tab"};q.m=function(a){var b=id.c.m.call(this,a);(a=a.Wa())&&this.jb(b,a);return b};q.L=function(a,b){b=id.c.L.call(this,a,b);var c=this.Wa(b);if(c)a.Cb=c;if(a.l&8)if((c=a.j)&&u(c.da)){a.w(8,k);c.da(a)}return b};q.Wa=function(a){return a.title||""};q.jb=function(a,b){if(a)a.title=b||""};var jd=function(a,b,c){S.call(this,a,b||id.Q(),c);Tc(this,8,h);this.Na|=9};w(jd,S);jd.prototype.Wa=function(){return this.Cb};jd.prototype.jb=function(a){this.pb().jb(this.b(),a);this.Cb=a};Ic("goog-tab",function(){return new jd(j)});var X=function(){};w(X,V);ca(X);X.prototype.o=function(){return"goog-tab-bar"};X.prototype.ea=function(){return"tablist"};X.prototype.ib=function(a,b,c){if(!this.vb){this.Da||kd(this);this.vb=Ia(this.Da)}var d=this.vb[b];if(d){$c(a,ld(d));a.wb=d}else X.c.ib.call(this,a,b,c)};X.prototype.oa=function(a){var b=X.c.oa.call(this,a);this.Da||kd(this);b.push(this.Da[a.wb]);return b};var kd=function(a){var b=a.o();a.Da=La("top",b+"-top","bottom",b+"-bottom","start",b+"-start","end",b+"-end")};var Y=function(a,b,c){a=a||"top";$c(this,ld(a));this.wb=a;W.call(this,this.V,b||X.Q(),c);md(this)};w(Y,W);q=Y.prototype;q.Hb=h;q.D=j;q.t=function(){Y.c.t.call(this);md(this)};q.f=function(){Y.c.f.call(this);this.D=j};q.removeChild=function(a,b){nd(this,a);return Y.c.removeChild.call(this,a,b)};q.gb=function(a){Y.c.gb.call(this,a);this.Hb&&this.da(Q(this,a))};q.da=function(a){if(a)Rc(a,h);else this.D&&Rc(this.D,k)};
+var nd=function(a,b){if(b&&b==a.D){for(var c=xc(a,b),d=c-1;b=Q(a,d);d--)if(b.J()&&b.i()){a.da(b);return}for(c=c+1;b=Q(a,c);c++)if(b.J()&&b.i()){a.da(b);return}a.da(j)}};q=Y.prototype;q.Yb=function(a){this.D&&this.D!=a.target&&Rc(this.D,k);this.D=a.target};q.Zb=function(a){if(a.target==this.D)this.D=j};q.Wb=function(a){nd(this,a.target)};q.Xb=function(a){nd(this,a.target)};q.qa=function(){Q(this,this.k)||this.F(this.D||Q(this,0))};
+var md=function(a){N(N(N(N(rc(a),a,"select",a.Yb),a,"unselect",a.Zb),a,"disable",a.Wb),a,"hide",a.Xb)},ld=function(a){return a=="start"||a=="end"?"vertical":"horizontal"};Ic("goog-tab-bar",function(){return new Y});var Z=function(a,b,c,d,f){function g(l){if(l){l.tabIndex=0;L(l,"click",i.ec,k,i);L(l,"keydown",i.fc,k,i)}}this.q=f||lb();this.Y=this.q.b(a)||j;this.ma=this.q.b(d||j);this.Ta=(this.db=u(b)?b:j)||!b?j:this.q.b(b);this.h=c==h;var i=this;g(this.Y);g(this.ma);this.W(this.h)};w(Z,ic);Z.prototype.f=function(){this.Y&&cc(this.Y);this.ma&&cc(this.ma);Z.c.f.call(this)};
+Z.prototype.W=function(a){if(this.Ta)jc(this.Ta,a);else if(a&&this.db)this.Ta=this.db();if(this.ma){jc(this.Y,!a);jc(this.ma,a)}else if(this.Y){var b=this.Y;a?D(b,"goog-zippy-expanded"):ib(b,"goog-zippy-expanded");b=this.Y;!a?D(b,"goog-zippy-collapsed"):ib(b,"goog-zippy-collapsed")}this.h=a;this.dispatchEvent(new od("toggle",this,this.h))};Z.prototype.fc=function(a){if(a.keyCode==13||a.keyCode==32){this.W(!this.h);a.preventDefault();a.stopPropagation()}};Z.prototype.ec=function(){this.W(!this.h)};
+var od=function(a,b,c){F.call(this,a,b);this.kc=c};w(od,F);var qd=function(a,b){this.kb=[];var c=mb(a);c=nb(document,"span","ae-zippy",c);for(var d=0,f;f=c[d];d++){for(var g=f.parentNode.parentNode.parentNode.nextSibling;g&&g.nodeType!=1;)g=g.nextSibling;this.kb.push(new Z(f,g,k))}this.Lb=new pd(this.kb,mb(b))};qd.prototype.Mb=function(){return this.Lb};qd.prototype.Nb=function(){return this.kb};
+var pd=function(a,b){this.Aa=a;if(this.Aa.length)for(var c=0,d;d=this.Aa[c];c++)L(d,"toggle",this.jc,k,this);this.eb=0;this.h=k;c="ae-toggle ae-plus ae-action";this.Aa.length||(c+=" ae-disabled");this.P=rb("span",{className:c},"Expand All");L(this.P,"click",this.Jb,k,this);b.appendChild(this.P)};pd.prototype.Jb=function(){this.Aa.length&&this.W(!this.h)};
+pd.prototype.jc=function(a){a=a.currentTarget;if(a.h)this.eb+=1;else this.eb-=1;if(a.h!=this.h)if(a.h){this.h=h;rd(this,h)}else if(this.eb==0){this.h=k;rd(this,k)}};pd.prototype.W=function(a){this.h=a;a=0;for(var b;b=this.Aa[a];a++)b.h!=this.h&&b.W(this.h);rd(this)};
+var rd=function(a,b){if(b!==undefined?b:a.h){ib(a.P,"ae-plus");D(a.P,"ae-minus");vb(a.P,"Collapse All")}else{ib(a.P,"ae-minus");D(a.P,"ae-plus");vb(a.P,"Expand All")}},sd=function(a){this.ic=a;this.Bb={};var b,c=rb("div",{},b=rb("div",{id:"ae-stats-details-tabs",className:"goog-tab-bar goog-tab-bar-top"}),rb("div",{className:"goog-tab-bar-clear"}),a=rb("div",{id:"ae-stats-details-tabs-content",className:"goog-tab-content"})),d=new Y;d.L(b);L(d,"select",this.mb,k,this);L(d,"unselect",this.mb,k,this);
+b=0;for(var f;f=this.ic[b];b++)if(f=mb("ae-stats-details-"+f)){var g=nb(document,"h2",j,f)[0],i;i=g;var l=void 0;if(gb&&"innerText"in i)l=i.innerText.replace(/(\r\n|\r|\n)/g,"\n");else{l=[];zb(i,l,h);l=l.join("")}l=l.replace(/ \xAD /g," ").replace(/\xAD/g,"");y||(l=l.replace(/ +/g," "));if(l!=" ")l=l.replace(/^\s*/,"");i=l;tb(g);g=new jd(i);this.Bb[v(g)]=f;d.Ba(g,h);a.appendChild(f);b==0?d.da(g):jc(f,k)}mb("bd").appendChild(c)};sd.prototype.mb=function(a){var b=this.Bb[v(a.target)];jc(b,a.type=="select")};
+aa("ae.Stats.Details.Tabs",sd,void 0);aa("goog.ui.Zippy",Z,void 0);Z.prototype.setExpanded=Z.prototype.W;aa("ae.Stats.MakeZippys",qd,void 0);qd.prototype.getExpandCollapse=qd.prototype.Mb;qd.prototype.getZippys=qd.prototype.Nb;pd.prototype.setExpanded=pd.prototype.W;var $=function(){this.Qa=[];this.fb=[]},td=[[5,0.2,1],[6,0.2,1.2],[5,0.25,1.25],[6,0.25,1.5],[4,0.5,2],[5,0.5,2.5],[6,0.5,3],[4,1,4],[5,1,5],[6,1,6],[4,2,8],[5,2,10]],ud=function(a){if(a<=0)return[2,0.5,1];for(var b=1;a<1;){a*=10;b/=10}for(;a>=10;){a/=10;b*=10}for(var c=0;c<td.length;c++)if(a<=td[c][2])return[td[c][0],td[c][1]*b,td[c][2]*b];return[5,2*b,10*b]};$.prototype.Oa="stats/static/pix.gif";$.prototype.s="ae-stats-gantt-";$.prototype.ab=0;$.prototype.write=function(a){this.fb.push(a)};
+var vd=function(a,b,c,d){a.write('<tr class="'+a.s+'axisrow"><td width="20%"></td><td>');a.write('<div class="'+a.s+'axis">');for(var f=0;f<=b;f++){a.write('<img class="'+a.s+'tick" src="'+a.Oa+'" alt="" ');a.write('style="left:'+f*c*d+'%"\n>');a.write('<span class="'+a.s+'scale" style="left:'+f*c*d+'%">');a.write("&nbsp;"+f*c+"</span>")}a.write("</div></td></tr>\n")};
+$.prototype.Kb=function(){this.fb=[];var a=ud(this.ab),b=a[0],c=a[1];a=100/a[2];this.write('<table class="'+this.s+'table">\n');vd(this,b,c,a);for(var d=0;d<this.Qa.length;d++){var f=this.Qa[d];this.write('<tr class="'+this.s+'datarow"><td width="20%">');if(f.label.length>0){f.ha.length>0&&this.write('<a class="'+this.s+'link" href="'+f.ha+'">');this.write(f.label);f.ha.length>0&&this.write("</a>")}this.write("</td>\n<td>");this.write('<div class="'+this.s+'container">');f.ha.length>0&&this.write('<a class="'+
+this.s+'link" href="'+f.ha+'"\n>');this.write('<img class="'+this.s+'bar" src="'+this.Oa+'" alt="" ');this.write('style="left:'+f.start*a+"%;width:"+f.duration*a+'%;min-width:1px"\n>');if(f.Ua>0){this.write('<img class="'+this.s+'extra" src="'+this.Oa+'" alt="" ');this.write('style="left:'+f.start*a+"%;width:"+f.Ua*a+'%"\n>')}if(f.sb.length>0){this.write('<span class="'+this.s+'inline" style="left:'+(f.start+Math.max(f.duration,f.Ua))*a+'%">&nbsp;');this.write(f.sb);this.write("</span>")}f.ha.length>
+0&&this.write("</a>");this.write("</div></td></tr>\n")}vd(this,b,c,a);this.write("</table>\n");return this.fb.join("")};$.prototype.Fb=function(a,b,c,d,f,g){this.ab=Math.max(this.ab,Math.max(b+c,b+d));this.Qa.push({label:a,start:b,duration:c,Ua:d,sb:f,ha:g})};aa("Gantt",$,void 0);$.prototype.add_bar=$.prototype.Fb;$.prototype.draw=$.prototype.Kb;})();
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/appstats/templates/main.html google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/templates/main.html
--- local/google_appengine/google/appengine/ext/appstats/templates/main.html 2010-10-14 21:40:42.461837233 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/templates/main.html 2010-11-18 21:44:32.000000000 +0000
@@ -1,10 +1,10 @@
{% extends "base.html" %}
{% block content %}
-<form id="ae-stats-refresh">
+<form id="ae-stats-refresh" action=".">
{% comment %}
<input type="checkbox" name="refresh" value="refresh" id="ae-stats-refresh"><label for="ae-stats-refresh">Automatically update every 30 seconds</label><br>
{% endcomment %}
<button id="ae-refresh">Refresh Now</button>
</form>
@@ -159,11 +159,11 @@
{% else %}
<div>
No requests have been recorded yet. While it is possible that you
simply need to wait until your server receives some requests, this
is often caused by a configuration problem.
- <a "href=http://code.google.com/appengine/docs/python/tools/appstats.html#Installing_the_Event_Recorder"
+ <a href="http://code.google.com/appengine/docs/python/tools/appstats.html#Installing_the_Event_Recorder"
>Learn more</a>
</div>
{% endif %}
{% endblock %}
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/appstats/ui.py google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/ui.py
--- local/google_appengine/google/appengine/ext/appstats/ui.py 2010-02-11 01:55:23.413014323 +0000
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/appstats/ui.py 2010-11-18 21:44:32.000000000 +0000
@@ -65,12 +65,12 @@ def render(tmplname, data):
"""Request handler for the main stats page (/stats/)."""
def get(self):
recording.dont_record()
- if not self.request.path.endswith('stats/'):
- self.redirect('stats/')
+ if not self.request.path.endswith('/'):
+ self.redirect(self.request.path + '/')
return
summaries = recording.load_summary_protos()
allstats = {}
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/builtins/appstats/include.yaml google-appengine-1.4.0-prerelease/google/appengine/ext/builtins/appstats/include.yaml
--- local/google_appengine/google/appengine/ext/builtins/appstats/include.yaml 2010-10-14 21:40:41.983836884 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/builtins/appstats/include.yaml 2010-11-18 21:44:32.000000000 +0000
@@ -1,4 +1,3 @@
handlers:
- url: /_ah/stats.*
script: $PYTHON_LIB/google/appengine/ext/appstats/ui.py
- login: admin
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/bulkload/bulkloader_config.py google-appengine-1.4.0-prerelease/google/appengine/ext/bulkload/bulkloader_config.py
--- local/google_appengine/google/appengine/ext/bulkload/bulkloader_config.py 2010-10-14 21:40:42.184837862 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/bulkload/bulkloader_config.py 2010-11-18 21:44:32.000000000 +0000
@@ -387,13 +387,16 @@ def __track_max_id(self, entity):
Args:
entity: An entity with a key.
"""
if not self.increment_id:
return
- key = entity.key()
- if not key:
+ if isinstance(entity, datastore.Entity):
+ if not entity.key():
return
+ elif not entity.has_key():
+ return
+ key = entity.key()
key_id = key.id()
if not key_id:
return
path = tuple(key.to_path()[:-1])
if self.high_ids.get(path, 0) < key_id:
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/datastore_admin/static/css/compiled.css google-appengine-1.4.0-prerelease/google/appengine/ext/datastore_admin/static/css/compiled.css
--- local/google_appengine/google/appengine/ext/datastore_admin/static/css/compiled.css 2010-10-14 21:40:42.236837234 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/datastore_admin/static/css/compiled.css 2010-11-18 21:44:32.000000000 +0000
@@ -1,2 +1,2 @@
/* Copyright 2010 Google Inc. All Rights Reserved. */
-html,body,div,h1,h2,h3,h4,h5,h6,p,img,dl,dt,dd,ol,ul,li,table,caption,tbody,tfoot,thead,tr,th,td,form,fieldset,embed,object,applet{margin:0;padding:0;border:0;}body{font-size:62.5%;font-family:Arial,sans-serif;color:#000;background:#fff}a{color:#00c}a:active{color:#f00}a:visited{color:#551a8b}table{border-collapse:collapse;border-width:0;empty-cells:show}ul{padding:0 0 1em 1em}ol{padding:0 0 1em 1.3em}li{line-height:1.5em;padding:0 0 .5em 0}p{padding:0 0 1em 0}h1,h2,h3,h4,h5{padding:0 0 1em 0}h1,h2{font-size:1.3em}h3{font-size:1.1em}h4,h5,table{font-size:1em}sup,sub{font-size:.7em}input,select,textarea,option{font-family:inherit;font-size:inherit}.g-doc,.g-doc-1024,.g-doc-800{font-size:130%}.g-doc{width:100%;text-align:left}.g-section{width:100%;vertical-align:top;display:inline-block}*:first-child+html .g-section{display:block}* html .g-section{overflow:hidden}@-moz-document url-prefix(){.g-section{overflow:hidden}}@-moz-document url-prefix(){.g-section,tt:default{overflow:visible}}.g-section,.g-unit{zoom:1}.g-split .g-unit{text-align:right}.g-split .g-first{text-align:left}.g-doc-1024{width:73.074em;*width:71.313em;min-width:950px;margin:0 auto;text-align:left}.g-doc-800{width:57.69em;*width:56.3em;min-width:750px;margin:0 auto;text-align:left}.g-tpl-160 .g-unit,.g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-unit .g-tpl-160 .g-unit{margin:0 0 0 160px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-tpl-160 .g-first,.g-tpl-160 .g-first{margin:0;width:160px;float:left}.g-tpl-160-alt .g-unit,.g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit{margin:0 160px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-tpl-160-alt .g-first,.g-tpl-160-alt .g-first{margin:0;width:160px;float:right}.g-tpl-180 .g-unit,.g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-unit .g-tpl-180 .g-unit{margin:0 0 0 180px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-tpl-180 .g-first,.g-tpl-180 .g-first{margin:0;width:180px;float:left}.g-tpl-180-alt .g-unit,.g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit{margin:0 180px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-tpl-180-alt .g-first,.g-tpl-180-alt .g-first{margin:0;width:180px;float:right}.g-tpl-300 .g-unit,.g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-unit .g-tpl-300 .g-unit{margin:0 0 0 300px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-tpl-300 .g-first,.g-tpl-300 .g-first{margin:0;width:300px;float:left}.g-tpl-300-alt .g-unit,.g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit{margin:0 300px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-tpl-300-alt .g-first,.g-tpl-300-alt .g-first{margin:0;width:300px;float:right}.g-tpl-25-75 .g-unit,.g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit{width:74.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-tpl-25-75 .g-first,.g-tpl-25-75 .g-first{width:24.999%;float:left;margin:0}.g-tpl-25-75-alt .g-unit,.g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit{width:24.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-tpl-25-75-alt .g-first,.g-tpl-25-75-alt .g-first{width:74.999%;float:right;margin:0}.g-tpl-75-25 .g-unit,.g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit{width:24.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-tpl-75-25 .g-first,.g-tpl-75-25 .g-first{width:74.999%;float:left;margin:0}.g-tpl-75-25-alt .g-unit,.g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit{width:74.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-tpl-75-25-alt .g-first,.g-tpl-75-25-alt .g-first{width:24.999%;float:right;margin:0}.g-tpl-33-67 .g-unit,.g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit{width:66.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-tpl-33-67 .g-first,.g-tpl-33-67 .g-first{width:32.999%;float:left;margin:0}.g-tpl-33-67-alt .g-unit,.g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit{width:32.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-tpl-33-67-alt .g-first,.g-tpl-33-67-alt .g-first{width:66.999%;float:right;margin:0}.g-tpl-67-33 .g-unit,.g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit{width:32.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-tpl-67-33 .g-first,.g-tpl-67-33 .g-first{width:66.999%;float:left;margin:0}.g-tpl-67-33-alt .g-unit,.g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit{width:66.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-tpl-67-33-alt .g-first,.g-tpl-67-33-alt .g-first{width:32.999%;float:right;margin:0}.g-tpl-50-50 .g-unit,.g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit{width:49.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-tpl-50-50 .g-first,.g-tpl-50-50 .g-first{width:49.999%;float:left;margin:0}.g-tpl-50-50-alt .g-unit,.g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit{width:49.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-tpl-50-50-alt .g-first,.g-tpl-50-50-alt .g-first{width:49.999%;float:right;margin:0}.g-tpl-nest{width:auto}.g-tpl-nest .g-section{display:inline}.g-tpl-nest .g-unit,.g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest .g-unit{float:left;width:auto;margin:0}.g-tpl-nest-alt .g-unit,.g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit{float:right;width:auto;margin:0}.goog-button{border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;font:normal normal normal 13px/13px Arial,sans-serif;color:#000;text-align:middle;text-decoration:none;text-shadow:0 1px 1px rgba(255,255,255,1);background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;margin:0;display:inline;display:-moz-inline-box;display:inline-block;*overflow:visible;padding:4px 8px 5px}a.goog-button,span.goog-button,div.goog-button{padding:4px 8px 5px}.goog-button:visited{color:#000}.goog-button{*display:inline}.goog-button:focus,.goog-button:hover{border-color:#000}.goog-button:active,.goog-button-active{color:#000;background-color:#bbb;border-color:#999 #bbb #bbb #999;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff));background-image:-moz-linear-gradient(top,#ddd,#fff);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#ffffff',StartColorstr='#dddddd',GradientType=0)}.goog-button[disabled],.goog-button[disabled]:active,.goog-button[disabled]:hover{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.goog-button{padding:5px 8px 4px\9}.goog-button{*padding:4px 7px 2px}html>body input.goog-button,x:-moz-any-link,x:default,html>body button.goog-button,x:-moz-any-link,x:default{padding-top:3px;padding-bottom:2px}a.goog-button,x:-moz-any-link,x:default,span.goog-button,x:-moz-any-link,x:default,div.goog-button,x:-moz-any-link,x:default{padding:4px 8px 5px}.goog-button-fixed{padding-left:0!important;padding-right:0!important;width:100%}button.goog-button-icon-c{padding-top:1px;padding-bottom:1px}button.goog-button-icon-c{padding-top:3px\9;padding-bottom:2px\9}button.goog-button-icon-c{*padding-top:0;*padding-bottom:0}html>body button.goog-button-icon-c,x:-moz-any-link,x:default{padding-top:1px;padding-bottom:1px}.goog-button-icon{display:block;margin:0 auto;height:18px;width:18px}html>body .goog-inline-block{display:-moz-inline-box;display:inline-block;}.goog-inline-block{position:relative;display:inline-block}* html .goog-inline-block{display:inline}*:first-child+html .goog-inline-block{display:inline}.goog-custom-button{margin:0 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0)}.goog-custom-button-outer-box,.goog-custom-button-inner-box{border-style:solid;border-color:#bbb #999 #999 #bbb;vertical-align:top}.goog-custom-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-custom-button-inner-box{margin:0 -1px;border-width:0 1px;padding:3px 4px}* html .goog-custom-button-inner-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-outer-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-inner-box{left:-1px}*:first-child+html .goog-custom-button-rtl .goog-custom-button-inner-box{left:1px}::root .goog-custom-button,::root .goog-custom-button-outer-box{line-height:0}::root .goog-custom-button-inner-box{line-height:normal}.goog-custom-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-custom-button-disabled .goog-custom-button-outer-box,.goog-custom-button-disabled .goog-custom-button-inner-box{color:#333!important;border-color:#999!important}* html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-custom-button-hover .goog-custom-button-outer-box,.goog-custom-button-hover .goog-custom-button-inner-box{border-color:#000!important;}.goog-custom-button-active,.goog-custom-button-checked{background-color:#bbb;background-position:bottom left}.goog-custom-button-focused .goog-custom-button-outer-box,.goog-custom-button-focused .goog-custom-button-inner-box{border-color:#3366cc}.goog-custom-button-collapse-right,.goog-custom-button-collapse-right .goog-custom-button-outer-box,.goog-custom-button-collapse-right .goog-custom-button-inner-box{margin-right:0}.goog-custom-button-collapse-left,.goog-custom-button-collapse-left .goog-custom-button-outer-box,.goog-custom-button-collapse-left .goog-custom-button-inner-box{margin-left:0}.goog-custom-button-collapse-left .goog-custom-button-inner-box{border-left:1px solid #fff}.goog-custom-button-collapse-left.goog-custom-button-checked .goog-custom-button-inner-box{border-left:1px solid #ddd}* html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}.goog-date-picker th,.goog-date-picker td{font-family:arial,sans-serif;text-align:center}.goog-date-picker th{font-size:.9em;font-weight:bold;color:#666667;background-color:#c3d9ff}.goog-date-picker td{vertical-align:middle;padding:2px 3px}.goog-date-picker{-moz-user-focus:normal;-moz-user-select:none;position:absolute;border:1px solid gray;float:left;font-family:arial,sans-serif;padding-left:1px;background:white}.goog-date-picker-menu{position:absolute;background:threedface;border:1px solid gray;-moz-user-focus:normal}.goog-date-picker-menu ul{list-style:none;margin:0;padding:0}.goog-date-picker-menu ul li{cursor:default}.goog-date-picker-menu-selected{background-color:#aaccee}.goog-date-picker td div{float:left}.goog-date-picker button{padding:0;margin:1px;border:1px outset gray}.goog-date-picker-week{padding:1px 3px}.goog-date-picker-wday{padding:1px 3px}.goog-date-picker-today-cont{text-align:left!important}.goog-date-picker-none-cont{text-align:right!important}.goog-date-picker-head td{text-align:center}.goog-date-picker-month{width:12ex}.goog-date-picker-year{width:6ex}.goog-date-picker table{border-collapse:collapse}.goog-date-picker-selected{background-color:#aaccee!important;color:blue!important}.goog-date-picker-today{font-weight:bold!important}.goog-date-picker-other-month{-moz-opacity:0.3;filter:Alpha(Opacity=30)}.sat,.sun{background:#eee}#button1,#button2{display:block;width:60px;text-align:center;margin:10px;padding:10px;font:normal .8em arial,sans-serif;border:1px solid #000}.goog-menu{position:absolute;color:#000;border:1px solid #b5b6b5;background-color:#f3f3f7;cursor:default;font:normal small arial,helvetica,sans-serif;margin:0;padding:0;outline:none}.goog-menuitem{padding:2px 5px;margin:0;list-style:none}.goog-menuitem-highlight{background-color:#4279a5;color:#fff}.goog-menuitem-disabled{color:#999}.goog-option{padding-left:15px!important}.goog-option-selected{background-image:url(/img/check.gif);background-position:4px 50%;background-repeat:no-repeat}.goog-menuseparator{position:relative;margin:2px 0;border-top:1px solid #999;padding:0;outline:none}.goog-submenu{position:relative}.goog-submenu-arrow{position:absolute;display:block;width:11px;height:11px;right:3px;top:4px;background-image:url(/img/menu-arrows.gif);background-repeat:no-repeat;background-position:0 0;font-size:1px}.goog-menuitem-highlight .goog-submenu-arrow{background-position:0 -11px}.goog-menuitem-disabled .goog-submenu-arrow{display:none}.goog-menu-filter{margin:2px;border:1px solid silver;background:white;overflow:hidden}.goog-menu-filter div{color:gray;position:absolute;padding:1px}.goog-menu-filter input{margin:0;border:0;background:transparent;width:100%}.goog-menuitem-partially-checked{background-image:url(/img/check-outline.gif);background-position:4px 50%;background-repeat:no-repeat}.goog-menuitem-fully-checked{background-image:url(/img/check.gif);background-position:4px 50%;background-repeat:no-repeat}.goog-menu-button{margin:0 2px 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;background:#ddd url("/img/button-bg.gif") repeat-x top left;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none}.goog-menu-button-outer-box,.goog-menu-button-inner-box{border-style:solid;border-color:#aaa;vertical-align:middle}.goog-menu-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-menu-button-inner-box{margin:0 -1px;border-width:0 1px;padding:0 4px 2px 4px}* html .goog-menu-button-inner-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-outer-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-inner-box{left:0}*:first-child+html .goog-menu-button-inner-box{left:-1px}*:first-child+html .goog-menu-button-rtl .goog-menu-button-inner-box{left:1px}::root .goog-menu-button,::root .goog-menu-button-outer-box,::root .goog-menu-button-inner-box{line-height:0}::root .goog-menu-button-caption,::root .goog-menu-button-dropdown{line-height:normal}.goog-menu-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-menu-button-disabled .goog-menu-button-outer-box,.goog-menu-button-disabled .goog-menu-button-inner-box,.goog-menu-button-disabled .goog-menu-button-caption,.goog-menu-button-disabled .goog-menu-button-dropdown{color:#333!important;border-color:#999!important}* html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-menu-button-hover .goog-menu-button-outer-box,.goog-menu-button-hover .goog-menu-button-inner-box{border-color:#9cf #69e #69e #7af!important;}.goog-menu-button-active,.goog-menu-button-open{background-color:#bbb;background-position:bottom left}.goog-menu-button-focused .goog-menu-button-outer-box,.goog-menu-button-focused .goog-menu-button-inner-box{border-color:#3366cc}.goog-menu-button-caption{padding:0 4px 0 0;vertical-align:middle}.goog-menu-button-rtl .goog-menu-button-caption{padding:0 0 0 4px}.goog-menu-button-dropdown{width:7px;background:url(/img/toolbar_icons.gif) no-repeat -176px;vertical-align:middle}.goog-flat-menu-button{margin:0 2px;padding:1px 4px;font:normal 95% Tahoma,Arial,sans-serif;color:#333;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;-moz-outline:none;border-width:1px;border-style:solid;border-color:#c9c9c9;background-color:#fff}.goog-flat-menu-button-disabled *{color:#999;border-color:#ccc;cursor:default}.goog-flat-menu-button-hover,.goog-flat-menu-button-hover{border-color:#9cf #69e #69e #7af!important;}.goog-flat-menu-button-active{background-color:#bbb;background-position:bottom left}.goog-flat-menu-button-focused{border-color:#3366cc}.goog-flat-menu-button-caption{padding-right:10px;vertical-align:middle}.goog-flat-menu-button-dropdown{width:7px;background:url(/img/toolbar_icons.gif) no-repeat -176px;vertical-align:middle}h1{font-size:1.8em}.g-doc{width:auto;margin:0 10px}.g-doc-1024{margin-left:10px}#ae-logo{background:url(//www.google.com/images/logos/app_engine_logo_sm.gif) 0 0 no-repeat;display:block;width:178px;height:30px;margin:4px 0 0 0}.ae-ir span{position:absolute;display:block;width:0;height:0;overflow:hidden}.ae-noscript{position:absolute;left:-5000px}#ae-lhs-nav{border-right:3px solid #e5ecf9}.ae-notification{margin-bottom:.6em;text-align:center}.ae-notification strong{display:block;width:55%;margin:0 auto;text-align:center;padding:.6em;background-color:#fff1a8;font-weight:bold}.ae-alert{font-weight:bold;background:url(/img/icn/icn-warning.gif) no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-info{background:url(/img/icn/icn-info.gif) no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-promo{padding:.5em .8em;margin:.6em 0;background-color:#fffbe8;border:1px solid #fff1a9;text-align:left}.ae-promo strong{position:relative;top:.3em}.ae-alert-text,.ae-warning-text{background-color:transparent;background-position:right 1px;padding:0 18px 0 0}.ae-alert-text{color:#c00}.ae-warning-text{color:#f90}.ae-alert-c span{display:inline-block}.ae-message{border:1px solid #e5ecf9;background-color:#f6f9ff;margin-bottom:1em;padding:.5em}.ae-errorbox{border:1px solid #f00;background-color:#fee;margin-bottom:1em;padding:1em}#bd .ae-errorbox ul{padding-bottom:0}.ae-form dt{font-weight:bold}.ae-form dt em,.ae-field-hint{margin-top:.2em;color:#666667;font-size:.85em}.ae-field-yyyymmdd,.ae-field-hhmmss{width:6em}.ae-field-hint-hhmmss{margin-left:2.3em}.ae-form label{display:block;margin:0 0 .2em 0;font-weight:bold}.ae-radio{margin-bottom:.3em}.ae-radio label{display:inline}.ae-form dd,.ae-input-row{margin-bottom:.6em}.ae-input-row-group{border:1px solid #fff1a9;background:#fffbe8;padding:8px}.ae-btn-row{margin-top:1.4em;margin-bottom:1em}.ae-btn-row-note{padding:5px 0 6px 0}.ae-btn-row-note span{padding-left:18px;padding-right:.5em;background:transparent url(/img/icn/icn-info.gif) 0 0 no-repeat}.ae-btn-primary{font-weight:bold}form .ae-cancel{margin-left:.5em}.ae-submit-inline{margin-left:.8em}.ae-radio-bullet{width:20px;float:left}.ae-label-hanging-indent{margin-left:5px}.ae-divider{margin:0 .6em 0 .5em}.ae-nowrap{white-space:nowrap}.ae-pre-wrap{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;_white-space:pre;}wbr:after{content:"\00200B"}a button{text-decoration:none}.ae-alert ul{margin-bottom:.75em;margin-top:.25em;line-height:1.5em}.ae-alert h4{color:#000;font-weight:bold;padding:0 0 .5em}.ae-form-simple-list{list-style-type:none;padding:0;margin-bottom:1em}.ae-form-simple-list li{padding:.3em 0 .5em .5em;border-bottom:1px solid #c3d9ff}div.ae-datastore-index-to-delete,div.ae-datastore-index-to-build{color:#aaa}#hd p{padding:0}#hd li{display:inline}ul{padding:0 0 1em 1.2em}#ae-userinfo{text-align:right;white-space:nowrap;}#ae-userinfo ul{padding-bottom:0;padding-top:5px}#ae-appbar-lrg{margin:0 0 1.25em 0;padding:.25em .5em;background-color:#e5ecf9;border-top:1px solid #36c}#ae-appbar-lrg h1{font-size:1.2em;padding:0}#ae-appbar-lrg h1 span{font-size:80%;font-weight:normal}#ae-appbar-lrg form{display:inline;padding-right:.1em;margin-right:.5em}#ae-appbar-sml{margin:0 0 1.25em 0;height:8px;padding:0 .5em;background:#e5ecf9}.ae-rounded-sml{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px}#ae-appbar-lrg a{margin-top:.3em}a.ae-ext-link{background:url(/img/icn/icn-open-in-new-window.png) no-repeat right;padding-right:18px;margin-right:8px}.ae-no-pad{padding-left:1em}.ae-message h4{margin-bottom:.3em;padding-bottom:0}#ft{text-align:center;margin:2.5em 0 1em;padding-top:.5em;border-top:2px solid #c3d9ff}#bd h3{font-weight:bold;font-size:1.4em}#bd h3 .ae-apps-switch{font-weight:normal;font-size:.7em;margin-left:2em}#bd p{padding:0 0 1em 0}#ae-content{padding-left:1em}.ae-unimportant{color:#666}.ae-new-usr td{border-top:1px solid #ccccce;background-color:#ffe}.ae-error-td td{border:2px solid #f00;background-color:#fee}.ae-delete{cursor:pointer;border:none;background:transparent;}.ae-btn-large{background:#039 url(/img/icn/button_back.png) repeat-x;color:#fff;font-weight:bold;font-size:1.2em;padding:.5em;border:2px outset #000;cursor:pointer}.ae-breadcrumb{margin:0 0 1em}.ae-disabled,a.ae-disabled,a.ae-disabled:hover,a.ae-disabled:active{color:#666!important;text-decoration:none!important;cursor:default!important;opacity:.4!important;-moz-opacity:.4!important;filter:alpha(opacity=40)!important}input.ae-readonly{border:2px solid transparent;border-left:0;background-color:transparent}span.ae-text-input-clone{padding:5px 5px 5px 0}.ae-loading{opacity:.4;-moz-opacity:.4;filter:alpha(opacity=40)}.ae-tip{margin-top:1em;background:url(/img/tip.png) top left no-repeat;padding:5px 0 0 25px;height:1.5em}sup.ae-new-sup{color:red}.ae-action{color:#00c;cursor:pointer;text-decoration:underline}.ae-toggle{padding-left:16px;background-position:left center;background-repeat:no-repeat;cursor:pointer}.ae-minus{background-image:url(/img/wgt/minus.gif)}.ae-plus{background-image:url(/img/wgt/plus.gif)}.ae-print{background-image:url(/img/print.gif);padding-left:19px}.ae-currency,.ae-table thead th.ae-currency{text-align:right;white-space:nowrap}#ae-loading{font-size:1.2em;position:absolute;text-align:center;top:0;width:100%}#ae-loading div{margin:0 auto;background:#fff1a9;width:5em;font-weight:bold;padding:4px 10px;-moz-border-radius-bottomleft:3px;-moz-border-radius-bottomright:3px;-webkit-border-radius-bottomleft:3px;-webkit-border-radius-bottomright:3px}.ae-occlude{filter:alpha(opacity=0);position:absolute}.g-tpl-66-34 .g-unit,.g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-unit .g-tpl-66-34 .g-unit{display:inline;margin:0;width:33.999%;float:right}.g-unit .g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-tpl-66-34 .g-first,.g-tpl-66-34 .g-first{display:inline;margin:0;width:65.999%;float:left}.ae-ie6-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}h2.ae-section-header{background:#e5ecf9;padding:.2em .4em;margin-bottom:.5em}.ae-field-span{padding:3px 0}select{font:13px/13px Arial,sans-serif;color:#000;border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;-webkit-border-radius:2px;-moz-border-radius:2px;background:#eee;background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;padding:2px 1px;margin:0}select:hover{border-color:#000}select[disabled],select[disabled]:active{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.ae-table-plain{border-collapse:collapse;width:100%}.ae-table{border:1px solid #c5d7ef;border-collapse:collapse;width:100%}#bd h2.ae-table-title{background:#e5ecf9;margin:0;color:#000;font-size:1em;padding:3px 0 3px 5px;border-left:1px solid #c5d7ef;border-right:1px solid #c5d7ef;border-top:1px solid #c5d7ef}.ae-table-caption,.ae-table caption{border:1px solid #c5d7ef;background:#e5ecf9;-moz-margin-start:-1px}.ae-table caption{padding:3px 5px;text-align:left}.ae-table th,.ae-table td{background-color:#fff;padding:.35em 1em .25em .35em;margin:0}.ae-table thead th{font-weight:bold;text-align:left;background:#c5d7ef;vertical-align:bottom}.ae-table thead th .ae-no-bold{font-weight:normal}.ae-table tfoot tr td{border-top:1px solid #c5d7ef;background-color:#e5ecf9}.ae-table td{border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even td,.ae-even th,.ae-even-top td,.ae-even-tween td,.ae-even-bottom td,ol.ae-even{background-color:#e9e9e9;border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even-top td{border-bottom:0}.ae-even-bottom td{border-top:0}.ae-even-tween td{border:0}.ae-table .ae-tween td{border:0}.ae-table .ae-tween-top td{border-bottom:0}.ae-table .ae-tween-bottom td{border-top:0}#bd .ae-table .cbc{width:1.5em;padding-right:0}.ae-table #ae-live td{background-color:#ffeac0}.ae-table-fixed{table-layout:fixed}.ae-table-fixed td,.ae-table-nowrap{overflow:hidden;white-space:nowrap}.ae-paginate strong{margin:0 .5em}tfoot .ae-paginate{text-align:right}.ae-table-caption .ae-paginate,.ae-table-caption .ae-orderby{padding:2px 5px}.modal-dialog{background:#c1d9ff;border:1px solid #3a5774;color:#000;padding:4px;position:absolute;font-size:1.3em;-moz-box-shadow:0 1px 4px #333;-webkit-box-shadow:0 1px 4px #333;box-shadow:0 1px 4px #333}.modal-dialog a,.modal-dialog a:link,.modal-dialog a:visited{color:#06c;cursor:pointer}.modal-dialog-bg{background:#666;left:0;position:absolute;top:0}.modal-dialog-title{background:#e0edfe;color:#000;cursor:pointer;font-size:120%;font-weight:bold;padding:8px 15px 8px 8px;position:relative;_zoom:1;}.modal-dialog-title-close{background:#e0edfe url(https://ssl.gstatic.com/editor/editortoolbar.png) no-repeat -528px 0;cursor:default;height:15px;position:absolute;right:10px;top:8px;width:15px;vertical-align:middle}.modal-dialog-buttons,.modal-dialog-content{background-color:#fff;padding:8px}.modal-dialog-buttons button{margin-right:.75em}.goog-buttonset-default{font-weight:bold}.goog-tab{position:relative;border:1px solid #8ac;padding:4px 9px;color:#000;background:#e5ecf9;border-top-left-radius:2px;border-top-right-radius:2px;-moz-border-radius-topleft:2px;-webkit-border-top-left-radius:2px;-moz-border-radius-topright:2px;-webkit-border-top-right-radius:2px}.goog-tab-bar-top .goog-tab{margin:1px 4px 0 0;border-bottom:0;float:left}.goog-tab-bar-bottom .goog-tab{margin:0 4px 1px 0;border-top:0;float:left}.goog-tab-bar-start .goog-tab{margin:0 0 4px 1px;border-right:0}.goog-tab-bar-end .goog-tab{margin:0 1px 4px 0;border-left:0}.goog-tab-hover{text-decoration:underline;cursor:pointer}.goog-tab-disabled{color:#fff;background:#ccc;border-color:#ccc}.goog-tab-selected{background:#fff!important;color:black;font-weight:bold}.goog-tab-bar-top .goog-tab-selected{top:1px;margin-top:0;padding-bottom:5px}.goog-tab-bar-bottom .goog-tab-selected{top:-1px;margin-bottom:0;padding-top:5px}.goog-tab-bar-start .goog-tab-selected{left:1px;margin-left:0;padding-right:9px}.goog-tab-bar-end .goog-tab-selected{left:-1px;margin-right:0;padding-left:9px}.goog-tab-content{padding:.1em .8em .8em .8em;border:1px solid #8ac;border-top:none}.goog-tab-bar{position:relative;margin:0 0 0 5px;border:0;padding:0;list-style:none;cursor:default;outline:none}.goog-tab-bar-clear{border-top:1px solid #8ac;clear:both;height:0;overflow:hidden}.goog-tab-bar-start{float:left}.goog-tab-bar-end{float:right}* html .goog-tab-bar-start{margin-right:-3px}* html .goog-tab-bar-end{margin-left:-3px}#ae-nav ul{list-style-type:none;margin:0;padding:1em 0}#ae-nav ul li{padding-left:.5em}#ae-nav .ae-nav-selected{color:#000;display:block;font-weight:bold;background-color:#e5ecf9;margin-right:-1px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px}#ae-nav .ae-nav-bold{font-weight:bold}#ae-nav ul li span.ae-nav-disabled{color:#666}#ae-nav ul ul{margin:0;padding:0 0 0 .5em}#ae-nav ul ul li{padding-left:.5em}#ae-nav ul li a,#ae-nav ul li span,#ae-nav ul ul li a{padding-left:.5em}#ae-nav li a:link,#ae-nav li a:visited{color:#00c}.ae-nav-group{padding:.5em;margin:0 .75em 0 0;background-color:#fffbe8;border:1px solid #fff1a9}.ae-nav-group h4{font-weight:bold;padding:auto auto .5em .5em;padding-left:.4em;margin-bottom:.5em;padding-bottom:0}.ae-nav-group ul{margin:0 0 .5em 0;padding:0 0 0 1.3em;list-style-type:none}.ae-nav-group ul li{padding-bottom:.5em}.ae-nav-group li a:link,.ae-nav-group li a:visited{color:#00c}.ae-nav-group li a:hover{color:#00c}@media print{body{font-size:13px;width:8.5in;background:#fff}table,.ae-table-fixed{table-layout:automatic}tr{display:table-row!important}.g-doc-1024{width:8.5in}#ae-appbar-lrg,.ae-table-caption,.ae-table-nowrap,.ae-nowrap,th,td{overflow:visible!important;white-space:normal!important;background:#fff!important}.ae-print,.ae-toggle{display:none}#ae-lhs-nav-c{display:none}#ae-content{margin:0;padding:0}.goog-zippy-collapsed,.goog-zippy-expanded{background:none!important;padding:0!important}}#ae-logs-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-logs{background-color:#c5d7ef;padding:1px;line-height:1.65}#ae-logs .ae-table-caption{border:0}#ae-logs-c ol,#ae-logs-c li{list-style:none;padding:0;margin:0}#ae-logs-c li li{margin:0 0 0 3px;padding:0 0 0 17px}.ae-log-noerror{padding-left:23px}#ae-logs-form .goog-inline-block{margin-top:0}.ae-logs-reqlog .snippet{margin:.1em}.ae-logs-applog .snippet{color:#666}.ae-logs-severity{display:block;float:left;height:1.2em;width:1.2em;line-height:1.2;text-align:center;text-transform:capitalize;font-weight:bold;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px}.ae-logs-severity-4{background-color:#f22;color:#000}.ae-logs-severity-3{background-color:#f90;color:#000}.ae-logs-severity-2{background-color:#fd0}.ae-logs-severity-1{background-color:#3c0;color:#000}.ae-logs-severity-0{background-color:#09f;color:#000}#ae-logs-legend{margin:1em 0 0 0}#ae-logs-legend ul{list-style:none;margin:0;padding:0}#ae-logs-legend li,#ae-logs-legend strong{float:left;margin:0 1em 0 0}#ae-logs-legend li span{margin-right:.3em}.ae-logs-timestamp{padding:0 5px;font-size:85%}#ae-logs-form-c{margin-bottom:5px;padding-bottom:.5em;padding-left:1em}#ae-logs-form{padding:.3em 0 0}#ae-logs-form .ae-label-row{float:left;padding-top:.2em}#ae-logs-form .ae-input-row,#ae-logs-form .ae-btn-row{margin-left:3.5em}#ae-logs-form .ae-btn-row{margin-bottom:0}#ae-logs-requests-c{margin-bottom:.1em}#ae-logs-requests-c input{margin:0}#ae-logs-requests-all-label{margin-right:1.5em}#ae-logs-form-options{margin-top:8px}#ae-logs-tip{margin:.2em 0}#ae-logs-expand{margin-right:.2em}#ae-logs-severity-level-label{margin-top:.3em;display:block}#ae-logs-filter-hint-labels-list{margin:2px 0}#ae-logs-filter-hint-labels-list span{position:absolute}#ae-logs-filter-hint-labels-list ul{margin-left:5.5em;padding:0}#ae-logs-filter-hint-labels-list li{float:left;margin-right:.4em;line-height:1.2}.ae-toggle .ae-logs-getdetails,.ae-toggle pre{display:none}.ae-log-expanded .ae-toggle pre{display:block}#ae-logs-c .ae-log .ae-toggle{cursor:default;background:none;padding-left:0}#ae-logs-c .ae-log .ae-toggle h5{cursor:pointer;background-position:0 .55em;background-repeat:no-repeat;padding-left:17px}.ae-log .ae-plus h5{background-image:url(/img/wgt/plus.gif)}.ae-log .ae-minus h5{background-image:url(/img/wgt/minus.gif)}.ae-log{overflow:hidden;background-color:#fff;padding:.3em 0;line-height:1.65;border-bottom:1px solid #c5d7ef}.ae-log .ae-even{background-color:#e9e9e9;border:0}.ae-log h5{font-weight:normal;white-space:nowrap;padding:.4em 0 0 0}.ae-log span,.ae-log strong{margin:0 .3em}.ae-log .ae-logs-snippet{color:#666}.ae-log pre,.ae-logs-expanded{padding:.3em 0 .5em 1.5em;margin:0;font-family:"Courier New"}.ae-log .file{font-weight:bold}.ae-logs-app .ae-logs-req{display:none}.ae-logs-req .ae-app,.ae-logs-both .ae-app{padding-left:1em}.ae-cron-status-ok{color:#008000;font-size:90%;font-weight:bold}.ae-cron-status-error{color:#a03;font-size:90%;font-weight:bold}#ae-cronjobs-table .ae-table td{vertical-align:top}#ae-tasks-table td{vertical-align:top}#ae-tasks-quota{margin:0 0 1em 0}#ae-tasks-quota .ae-dash-quota-bar{width:150px}#ae-tasks-quota #ae-dash-quota-bar-col,#ae-tasks-quota .ae-dash-quota-bar{width:200px}.ae-taskqueues-paused-row{color:#666;font-style:italic;font-weight:bold}#ae-taskqueues-quota{margin-bottom:.75em}#ae-taskqueues-quota .ae-quota-safety-limit{width:30%}#ae-taskqueues-table{margin-top:1em}#ae-queuedetails-queuecontrols{margin-top:1em;margin-bottom:1em}#ae-dos-blacklist-rejects-table{text-align:left}#ae-dash-quota-percent-col{width:3.5em}#ae-createapp-start{background-color:#c6d5f1;padding:1em;padding-bottom:2em;text-align:center}#ae-createapp-id-check{margin:0 0 0 1em}#ae-createapp-id-content{width:100%}#ae-createapp-id-content td{vertical-align:top}#ae-createapp-id-td{white-space:nowrap;width:1%}#ae-createapp-id-td #ae-createapp-id-error{position:absolute;width:24em;padding-left:1em;white-space:normal}#ae-createapp-id-error-td{padding-left:1em}#ae-admin-dev-invite label{float:left;width:3.6em;position:relative;top:.3em}#ae-admin-dev-invite .ae-radio{margin-left:3.6em}#ae-admin-dev-invite .ae-radio label{float:none;width:auto;font-weight:normal;position:static}#ae-admin-dev-invite .goog-button{margin-left:3.6em}#ae-admin-dev-invite .ae-field-hint{margin-left:4.2em}#ae-admin-dev-invite .ae-radio .ae-field-hint{margin-left:0}.ae-you{color:#008000}#ae-authdomain-opts{margin-bottom:1em}#ae-authdomain-content .ae-input-text,#ae-authdomain-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-authdomain-opts a{margin-left:1em}#ae-authdomain-opts-hint{margin-top:.2em;color:#666667;font-size:.85em}#ae-authdomain-content #ae-authdomain-desc .ae-field-hint{margin-left:0}#ae-dash .g-section{margin:0 0 1em}#ae-dash * .g-section{margin:0}#ae-dash-quota .ae-alert{padding-left:1.5em}#ae-dash-graph-c{border:1px solid #c5d7ef;padding:5px 0}#ae-dash-graph-change{margin:0 0 0 5px}#ae-dash-graph-img{padding:5px;margin-top:.5em;background-color:#fff;display:block}#ae-dash-graph-nodata{text-align:center}#ae-dash .ae-logs-severity{margin-right:.5em}#ae-dash .g-c{padding:0 0 0 .1em}#ae-dash .g-tpl-50-50 .g-unit .g-c{padding:0 0 0 1em}#ae-dash .g-tpl-50-50 .g-first .g-c{padding:0 1em 0 .1em}.ae-quota-warnings{background-color:#fffbe8;margin:0;padding:.5em .5em 0;text-align:left}.ae-quota-warnings div{padding:0 0 .5em}#ae-dash-quota-refresh-info{font-size:85%}#ae-dash #ae-dash-quota-bar-col,#ae-dash .ae-dash-quota-bar{width:100px}#ae-dash-quotadetails #ae-dash-quota-bar-col,#ae-dash-quotadetails .ae-dash-quota-bar{width:200px}#ae-dash-quota-percent-col{width:3.5em}#ae-dash-quota-cost-col{width:15%}#ae-dash-quota-alert-col{width:1%}#ae-dash .ae-dash-quota-alert-td{padding:0}.ae-dash-quota-alert-td a{display:block;width:14px;height:14px}#ae-dash .ae-dash-quota-alert-td .ae-alert{display:block;width:14px;height:14px;margin:0;padding:0}#ae-dash-quota tbody th{font-weight:normal}#ae-dash-quota caption{padding:0}#ae-dash-quota caption .g-c{padding:3px}.ae-dash-quota-bar{float:left;background-color:#c0c0c0;height:13px;margin:.1em 0 0 0;position:relative}.ae-dash-quota-bar-free{background:url(/img/free_marker.png) top left no-repeat;width:7px;height:13px;position:absolute;top:0;left:0}#ae-dash-quota-footnote{margin:5px 0 0;font-weight:normal}.ae-quota-warning{background-color:#f90}.ae-quota-alert{background-color:#c00}.ae-quota-normal{background-color:#0b0}.ae-quota-alert-text{color:#c00}.ae-favicon-text{font-size:.85em}#ae-dash-popular{width:97%}#ae-dash-popular-reqsec-col{width:6.5em}#ae-dash-popular-req-col{width:7em}#ae-dash-popular-cpu-avg-col{width:9.5em}#ae-dash-popular-cpu-percent-col{width:7em}#ae-dash-popular .ae-unimportant{font-size:80%}#ae-dash-popular .ae-nowrap,#ae-dash-errors .ae-nowrap{margin-right:5px;overflow:hidden}#ae-dash-popular th span,#ae-dash-errors th span{font-size:.8em;font-weight:normal;display:block}#ae-dash-errors caption .g-unit{width:9em}#ae-dash-errors-count-col{width:5em}#ae-dash-errors-percent-col{width:7em}#ae-dash-graph-chart-type{float:left;margin-right:1em}#ae-apps-all strong.ae-disabled{color:#000;background:#eee}.ae-quota-resource{width:30%}.ae-quota-safety-limit{width:10%}#ae-quota-details h3{padding-bottom:0;margin-bottom:.25em}#ae-quota-details table{margin-bottom:1.75em}#ae-quota-details table.ae-quota-requests{margin-bottom:.5em}#ae-quota-refresh-note p{text-align:right;padding-top:.5em;padding-bottom:0;margin-bottom:0}#ae-quota-first-api.g-section{padding-bottom:0;margin-bottom:.25em}#ae-instances-summary-table,#ae-instances-reserved-table,#ae-instances-dynamic-table{margin-bottom:1em}#ae-admin-dev-table{margin:0 0 15px 0}#ae-sms-countryselect{margin-right:.5em}#ae-admin-enable-form{margin-bottom:1em}#ae-admin-services-c{margin-top:2em}#ae-admin-services{padding:0 0 0 3em;margin-bottom:1em;font-weight:bold}#ae-admin-logs-table-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-admin-logs-table{margin:0;padding:0}#ae-admin-logs-filters{padding:3px 0 3px 5px}#ae-admin-logs-pagination{padding:6px 5px 0 0;text-align:right;width:45%}#ae-admin-logs-pagination span.ae-disabled{color:#666;background-color:transparent}#ae-admin-logs-table td{white-space:nowrap}#ae-datastore-explorer-c{_margin-right:-3000px;_position:relative;_width:100%}#ae-datastore-explorer form dt{margin:1em 0 0 0}#ae-datastore-explorer #ae-datastore-explorer-labels{margin:0 0 3px}#ae-datastore-explorer-header .ae-action{margin-left:1em}#ae-datastore-explorer .id{white-space:nowrap}#ae-datastore-explorer caption{text-align:right;padding:5px}#ae-datastore-explorer-submit{margin-top:5px}#ae-datastore-explorer-namespace{margin-top:7px;margin-right:5px}#ae-datastore-explorer-gql-spacer{margin-top:22px}h4 #ae-datastore-explorer-gql-label{font-weight:normal}#ae-datastore-form em{font-style:normal;font-weight:normal;margin:0 0 0 .2em;color:#666}#ae-datastore-form dt{font-weight:bold}#ae-datastore-form dd{margin:.4em 0 .3em 1.5em;overflow:auto;zoom:1}#ae-datastore-form dd em{width:4em;float:left}#ae-datastore-form dd.ae-last{margin-bottom:1em}#ae-datastore-explorer-tabs-content{margin-bottom:1em}#ae-datastore-explorer-list .ae-label-row,#ae-datastore-explorer-new .ae-label-row{float:left;padding-top:.2em}#ae-datastore-explorer-list .ae-input-row,#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-input-row,#ae-datastore-explorer-new .ae-btn-row{margin-left:6em}#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-btn-row{margin-bottom:0}.ae-datastore-index-name{font-size:1.2em;font-weight:bold}.ae-table .ae-datastore-index-defs{padding-left:20px}.ae-datastore-index-defs-row{border-top:1px solid #ddd}.ae-datastore-index-defs .ae-unimportant{font-size:.8em}.ae-datastore-index-status{border:1px solid #c0dfbf;background:#f3f7f3;margin:0 25px 0 0;padding:3px}#ae-datastore-index-status-col{width:15%}.ae-datastore-index-status-Building{border-color:#edebcd;background:#fefdec}.ae-datastore-index-status-Deleting{border-color:#ccc;background:#eee}.ae-datastore-index-status-Error{border-color:#ffd3b4;background:#ffeae0}.ae-datastore-pathlink{font-size:.9em}#ae-datastore-stats-top-level-c{padding-bottom:1em;margin-bottom:1em;border-bottom:1px solid #e5ecf9}#ae-datastore-stats-top-level{width:100%}#ae-datastore-stats-piecharts-c{margin-bottom:1em}.ae-datastore-stats-piechart-label{font-size:.85em;font-weight:normal;text-align:center;padding:0}#ae-datastore-stats-property-type{width:65%}#ae-datastore-stats-size-all{width:35%}#ae-datastore-stats-property-name{width:60%}#ae-datastore-stats-type{width:10%}#ae-datastore-stats-size-entity{width:30%}#ae-datastore-blob-filter-form{margin-bottom:1em}#ae-datastore-blob-query-filter-label{padding-right:.5em}#ae-datastore-blob-filter-contents{padding-top:.5em}#ae-datastore-blob-date-after,#ae-datastore-blob-date-before{float:left}#ae-datastore-blob-date-after{margin-right:1em}#ae-datastore-blob-order label{font-weight:normal}#ae-datastore-blob-col-check{width:2%}#ae-datastore-blob-col-file{width:45%}#ae-datastore-blob-col-type{width:14%}#ae-datastore-blob-col-size{width:16%}#ae-blobstore-col-date{width:18%}#ae-blob-detail-filename{padding-bottom:0}#ae-blob-detail-filename span{font-weight:normal}#ae-blob-detail-key{font-size:85%}#ae-blob-detail-preview{margin-top:1em}#ae-blob-detail-dl{text-align:right}#ae-billing-form-c{_margin-right:-3000px;_position:relative;_width:100%}.ae-rounded-top-small{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px}.ae-progress-content{height:400px}#ae-billing-tos{text-align:left;width:100%;margin-bottom:.5em}.ae-billing-budget-section{margin-bottom:1.5em}.ae-billing-budget-section .g-unit,.g-unit .ae-billing-budget-section .g-unit,.g-unit .g-unit .ae-billing-budget-section .g-unit{margin:0 0 0 11em;width:auto;float:none}.g-unit .g-unit .ae-billing-budget-section .g-first,.g-unit .ae-billing-budget-section .g-first,.ae-billing-budget-section .g-first{margin:0;width:11em;float:left}#ae-billing-form .ae-btn-row{margin-left:11em}#ae-billing-form .ae-btn-row .ae-info{margin-top:10px}#ae-billing-checkout{width:150px;float:left}#ae-billing-alloc-table{border:1px solid #c5d7ef;border-bottom:none;width:100%;margin-top:.5em}#ae-billing-alloc-table th,#ae-billing-alloc-table td{padding:.35em 1em .25em .35em;border-bottom:1px solid #c5d7ef;color:#000;white-space:nowrap}.ae-billing-resource{background-color:transparent;font-weight:normal}#ae-billing-alloc-table tr th span{font-weight:normal}#ae-billing-alloc-table th{white-space:nowrap}#ae-billing-alloc-table .ae-editable span,#ae-billing-alloc-table .ae-readonly input{display:none}#ae-billing-alloc-table .ae-readonly span,#ae-billing-alloc-table .ae-editable input{display:auto}.ae-billing-percent{font-size:80%;color:#666;margin-left:3px}#ae-billing-week-info{margin-top:5px;line-height:1.4}#ae-billing-storage-fn{margin-top:.3em}#ae-billing-allocation-noscript{margin-top:1.5em}#ae-billing-allocation-custom-opts{margin-left:2.2em}#ae-billing-settings h2{font-size:1em;display:inline}#ae-billing-settings p{padding:.3em 0 .5em}#ae-billing-settings-table{margin:.4em 0 .5em}#ae-settings-resource-col{width:19%}#ae-settings-budget-col{width:11%}#ae-billing-settings-table .ae-settings-budget-col{padding-right:2em}.ae-table th.ae-settings-unit-cell,.ae-table td.ae-settings-unit-cell,.ae-table th.ae-total-unit-cell,.ae-table td.ae-total-unit-cell{padding-left:1.2em}#ae-settings-unit-col{width:18%}#ae-settings-paid-col{width:15%}#ae-settings-free-col{width:15%}#ae-settings-total-col{width:22%}.ae-billing-inline-link{margin-left:.5em}.ae-billing-settings-section{margin-bottom:2em}.ae-billing-settings-formbutton{margin-top:.5em}#ae-billing-budget-setup-checkout{margin-bottom:0}#ae-billing-vat-c .ae-field-hint{width:85%}#ae-billing-checkout-note{margin-top:.8em}.ae-table thead th.ae-currency-th{text-align:right}#ae-billing-logs-date{width:15%}#ae-billing-logs-admin{width:15%}#ae-billing-logs-event{width:54%}#ae-billing-logs-amount{text-align:right;width:8%}#ae-billing-logs-balance{text-align:right;width:8%}#ae-billing-history-expand .ae-action{margin-left:1em}.ae-table .ae-billing-usage-report{width:100%;*width:auto;margin:0 0 1em 0}.ae-table .ae-billing-usage-report th{color:#666;border-top:0}.ae-table .ae-billing-usage-report th,.ae-table .ae-billing-usage-report td{background-color:transparent;padding:.4em 0;border-bottom:1px solid #ddd}.ae-table .ae-billing-usage-report tfoot td{border-bottom:none}.ae-billing-report-resource{width:30%}.ae-billing-report-used{width:20%}.ae-billing-report-free{width:20%}.ae-billing-report-paid{width:15%}.ae-billing-report-charge{width:15%}.ae-billing-change-resource{width:85%}.ae-billing-change-budget{width:15%}.goog-zippy-expanded{background-image:url(/img/wgt/minus.gif);cursor:pointer;background-repeat:no-repeat;padding-left:17px}.goog-zippy-collapsed{background-image:url(/img/wgt/plus.gif);cursor:pointer;background-repeat:no-repeat;padding-left:17px}#ae-admin-logs-pagination{width:auto}#ae-domain-admins-list li{margin-bottom:.3em}#ae-domain-admins-list button{margin-left:.5em}#ae-new-app-dialog-c{width:500px}#ae-new-app-dialog-c .g-section{margin-bottom:1em}
\ No newline at end of file
+html,body,div,h1,h2,h3,h4,h5,h6,p,img,dl,dt,dd,ol,ul,li,table,caption,tbody,tfoot,thead,tr,th,td,form,fieldset,embed,object,applet{margin:0;padding:0;border:0;}body{font-size:62.5%;font-family:Arial,sans-serif;color:#000;background:#fff}a{color:#00c}a:active{color:#f00}a:visited{color:#551a8b}table{border-collapse:collapse;border-width:0;empty-cells:show}ul{padding:0 0 1em 1em}ol{padding:0 0 1em 1.3em}li{line-height:1.5em;padding:0 0 .5em 0}p{padding:0 0 1em 0}h1,h2,h3,h4,h5{padding:0 0 1em 0}h1,h2{font-size:1.3em}h3{font-size:1.1em}h4,h5,table{font-size:1em}sup,sub{font-size:.7em}input,select,textarea,option{font-family:inherit;font-size:inherit}.g-doc,.g-doc-1024,.g-doc-800{font-size:130%}.g-doc{width:100%;text-align:left}.g-section{width:100%;vertical-align:top;display:inline-block}*:first-child+html .g-section{display:block}* html .g-section{overflow:hidden}@-moz-document url-prefix(){.g-section{overflow:hidden}}@-moz-document url-prefix(){.g-section,tt:default{overflow:visible}}.g-section,.g-unit{zoom:1}.g-split .g-unit{text-align:right}.g-split .g-first{text-align:left}.g-doc-1024{width:73.074em;*width:71.313em;min-width:950px;margin:0 auto;text-align:left}.g-doc-800{width:57.69em;*width:56.3em;min-width:750px;margin:0 auto;text-align:left}.g-tpl-160 .g-unit,.g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-tpl-160 .g-unit,.g-unit .g-unit .g-unit .g-tpl-160 .g-unit{margin:0 0 0 160px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-unit .g-tpl-160 .g-first,.g-unit .g-tpl-160 .g-first,.g-tpl-160 .g-first{margin:0;width:160px;float:left}.g-tpl-160-alt .g-unit,.g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-tpl-160-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit{margin:0 160px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-unit .g-tpl-160-alt .g-first,.g-unit .g-tpl-160-alt .g-first,.g-tpl-160-alt .g-first{margin:0;width:160px;float:right}.g-tpl-180 .g-unit,.g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-tpl-180 .g-unit,.g-unit .g-unit .g-unit .g-tpl-180 .g-unit{margin:0 0 0 180px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-unit .g-tpl-180 .g-first,.g-unit .g-tpl-180 .g-first,.g-tpl-180 .g-first{margin:0;width:180px;float:left}.g-tpl-180-alt .g-unit,.g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-tpl-180-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit{margin:0 180px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-unit .g-tpl-180-alt .g-first,.g-unit .g-tpl-180-alt .g-first,.g-tpl-180-alt .g-first{margin:0;width:180px;float:right}.g-tpl-300 .g-unit,.g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-tpl-300 .g-unit,.g-unit .g-unit .g-unit .g-tpl-300 .g-unit{margin:0 0 0 300px;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-unit .g-tpl-300 .g-first,.g-unit .g-tpl-300 .g-first,.g-tpl-300 .g-first{margin:0;width:300px;float:left}.g-tpl-300-alt .g-unit,.g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-tpl-300-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit{margin:0 300px 0 0;width:auto;float:none}.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-unit .g-tpl-300-alt .g-first,.g-unit .g-tpl-300-alt .g-first,.g-tpl-300-alt .g-first{margin:0;width:300px;float:right}.g-tpl-25-75 .g-unit,.g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-tpl-25-75 .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit{width:74.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-unit .g-tpl-25-75 .g-first,.g-unit .g-tpl-25-75 .g-first,.g-tpl-25-75 .g-first{width:24.999%;float:left;margin:0}.g-tpl-25-75-alt .g-unit,.g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-tpl-25-75-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit{width:24.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-unit .g-tpl-25-75-alt .g-first,.g-unit .g-tpl-25-75-alt .g-first,.g-tpl-25-75-alt .g-first{width:74.999%;float:right;margin:0}.g-tpl-75-25 .g-unit,.g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-tpl-75-25 .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit{width:24.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-unit .g-tpl-75-25 .g-first,.g-unit .g-tpl-75-25 .g-first,.g-tpl-75-25 .g-first{width:74.999%;float:left;margin:0}.g-tpl-75-25-alt .g-unit,.g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-tpl-75-25-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit{width:74.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-unit .g-tpl-75-25-alt .g-first,.g-unit .g-tpl-75-25-alt .g-first,.g-tpl-75-25-alt .g-first{width:24.999%;float:right;margin:0}.g-tpl-33-67 .g-unit,.g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-tpl-33-67 .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit{width:66.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-unit .g-tpl-33-67 .g-first,.g-unit .g-tpl-33-67 .g-first,.g-tpl-33-67 .g-first{width:32.999%;float:left;margin:0}.g-tpl-33-67-alt .g-unit,.g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-tpl-33-67-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit{width:32.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-unit .g-tpl-33-67-alt .g-first,.g-unit .g-tpl-33-67-alt .g-first,.g-tpl-33-67-alt .g-first{width:66.999%;float:right;margin:0}.g-tpl-67-33 .g-unit,.g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-tpl-67-33 .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit{width:32.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-unit .g-tpl-67-33 .g-first,.g-unit .g-tpl-67-33 .g-first,.g-tpl-67-33 .g-first{width:66.999%;float:left;margin:0}.g-tpl-67-33-alt .g-unit,.g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-tpl-67-33-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit{width:66.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-unit .g-tpl-67-33-alt .g-first,.g-unit .g-tpl-67-33-alt .g-first,.g-tpl-67-33-alt .g-first{width:32.999%;float:right;margin:0}.g-tpl-50-50 .g-unit,.g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-tpl-50-50 .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit{width:49.999%;float:right;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-unit .g-tpl-50-50 .g-first,.g-unit .g-tpl-50-50 .g-first,.g-tpl-50-50 .g-first{width:49.999%;float:left;margin:0}.g-tpl-50-50-alt .g-unit,.g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-tpl-50-50-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit{width:49.999%;float:left;margin:0}.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-unit .g-tpl-50-50-alt .g-first,.g-unit .g-tpl-50-50-alt .g-first,.g-tpl-50-50-alt .g-first{width:49.999%;float:right;margin:0}.g-tpl-nest{width:auto}.g-tpl-nest .g-section{display:inline}.g-tpl-nest .g-unit,.g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-tpl-nest .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest .g-unit{float:left;width:auto;margin:0}.g-tpl-nest-alt .g-unit,.g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-tpl-nest-alt .g-unit,.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit{float:right;width:auto;margin:0}.goog-button{border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;font:normal normal normal 13px/13px Arial,sans-serif;color:#000;text-align:middle;text-decoration:none;text-shadow:0 1px 1px rgba(255,255,255,1);background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;margin:0;display:inline;display:-moz-inline-box;display:inline-block;*overflow:visible;padding:4px 8px 5px}a.goog-button,span.goog-button,div.goog-button{padding:4px 8px 5px}.goog-button:visited{color:#000}.goog-button{*display:inline}.goog-button:focus,.goog-button:hover{border-color:#000}.goog-button:active,.goog-button-active{color:#000;background-color:#bbb;border-color:#999 #bbb #bbb #999;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff));background-image:-moz-linear-gradient(top,#ddd,#fff);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#ffffff',StartColorstr='#dddddd',GradientType=0)}.goog-button[disabled],.goog-button[disabled]:active,.goog-button[disabled]:hover{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.goog-button{padding:5px 8px 4px\9}.goog-button{*padding:4px 7px 2px}html>body input.goog-button,x:-moz-any-link,x:default,html>body button.goog-button,x:-moz-any-link,x:default{padding-top:3px;padding-bottom:2px}a.goog-button,x:-moz-any-link,x:default,span.goog-button,x:-moz-any-link,x:default,div.goog-button,x:-moz-any-link,x:default{padding:4px 8px 5px}.goog-button-fixed{padding-left:0!important;padding-right:0!important;width:100%}button.goog-button-icon-c{padding-top:1px;padding-bottom:1px}button.goog-button-icon-c{padding-top:3px\9;padding-bottom:2px\9}button.goog-button-icon-c{*padding-top:0;*padding-bottom:0}html>body button.goog-button-icon-c,x:-moz-any-link,x:default{padding-top:1px;padding-bottom:1px}.goog-button-icon{display:block;margin:0 auto;height:18px;width:18px}html>body .goog-inline-block{display:-moz-inline-box;display:inline-block;}.goog-inline-block{position:relative;display:inline-block}* html .goog-inline-block{display:inline}*:first-child+html .goog-inline-block{display:inline}.goog-custom-button{margin:0 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;background:#eee;background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ddd));background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0)}.goog-custom-button-outer-box,.goog-custom-button-inner-box{border-style:solid;border-color:#bbb #999 #999 #bbb;vertical-align:top}.goog-custom-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-custom-button-inner-box{margin:0 -1px;border-width:0 1px;padding:3px 4px}* html .goog-custom-button-inner-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-outer-box{left:-1px}* html .goog-custom-button-rtl .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-inner-box{left:-1px}*:first-child+html .goog-custom-button-rtl .goog-custom-button-inner-box{left:1px}::root .goog-custom-button,::root .goog-custom-button-outer-box{line-height:0}::root .goog-custom-button-inner-box{line-height:normal}.goog-custom-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-custom-button-disabled .goog-custom-button-outer-box,.goog-custom-button-disabled .goog-custom-button-inner-box{color:#333!important;border-color:#999!important}* html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-custom-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-custom-button-hover .goog-custom-button-outer-box,.goog-custom-button-hover .goog-custom-button-inner-box{border-color:#000!important;}.goog-custom-button-active,.goog-custom-button-checked{background-color:#bbb;background-position:bottom left;background-image:-webkit-gradient(linear,0 0,0 100%,from(#ddd),to(#fff))}.goog-custom-button-focused .goog-custom-button-outer-box,.goog-custom-button-focused .goog-custom-button-inner-box{border-color:#000}.goog-custom-button-collapse-right,.goog-custom-button-collapse-right .goog-custom-button-outer-box,.goog-custom-button-collapse-right .goog-custom-button-inner-box{margin-right:0}.goog-custom-button-collapse-left,.goog-custom-button-collapse-left .goog-custom-button-outer-box,.goog-custom-button-collapse-left .goog-custom-button-inner-box{margin-left:0}.goog-custom-button-collapse-left .goog-custom-button-inner-box{border-left:1px solid #fff}.goog-custom-button-collapse-left.goog-custom-button-checked .goog-custom-button-inner-box{border-left:1px solid #ddd}* html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}*:first-child+html .goog-custom-button-collapse-left .goog-custom-button-inner-box{left:0}.goog-date-picker th,.goog-date-picker td{font-family:arial,sans-serif;text-align:center}.goog-date-picker th{font-size:.9em;font-weight:bold;color:#666667;background-color:#c3d9ff}.goog-date-picker td{vertical-align:middle;padding:2px 3px}.goog-date-picker{-moz-user-focus:normal;-moz-user-select:none;position:absolute;border:1px solid gray;float:left;font-family:arial,sans-serif;padding-left:1px;background:white}.goog-date-picker-menu{position:absolute;background:threedface;border:1px solid gray;-moz-user-focus:normal}.goog-date-picker-menu ul{list-style:none;margin:0;padding:0}.goog-date-picker-menu ul li{cursor:default}.goog-date-picker-menu-selected{background-color:#aaccee}.goog-date-picker td div{float:left}.goog-date-picker button{padding:0;margin:1px;border:1px outset gray}.goog-date-picker-week{padding:1px 3px}.goog-date-picker-wday{padding:1px 3px}.goog-date-picker-today-cont{text-align:left!important}.goog-date-picker-none-cont{text-align:right!important}.goog-date-picker-head td{text-align:center}.goog-date-picker-month{width:12ex}.goog-date-picker-year{width:6ex}.goog-date-picker table{border-collapse:collapse}.goog-date-picker-selected{background-color:#aaccee!important;color:blue!important}.goog-date-picker-today{font-weight:bold!important}.goog-date-picker-other-month{-moz-opacity:0.3;filter:Alpha(Opacity=30)}.sat,.sun{background:#eee}#button1,#button2{display:block;width:60px;text-align:center;margin:10px;padding:10px;font:normal .8em arial,sans-serif;border:1px solid #000}.goog-menu{position:absolute;color:#000;border:1px solid #b5b6b5;background-color:#f3f3f7;cursor:default;font:normal small arial,helvetica,sans-serif;margin:0;padding:0;outline:none}.goog-menuitem{padding:2px 5px;margin:0;list-style:none}.goog-menuitem-highlight{background-color:#4279a5;color:#fff}.goog-menuitem-disabled{color:#999}.goog-option{padding-left:15px!important}.goog-option-selected{background-image:url(/img/check.gif);background-position:4px 50%;background-repeat:no-repeat}.goog-menuseparator{position:relative;margin:2px 0;border-top:1px solid #999;padding:0;outline:none}.goog-submenu{position:relative}.goog-submenu-arrow{position:absolute;display:block;width:11px;height:11px;right:3px;top:4px;background-image:url(/img/menu-arrows.gif);background-repeat:no-repeat;background-position:0 0;font-size:1px}.goog-menuitem-highlight .goog-submenu-arrow{background-position:0 -11px}.goog-menuitem-disabled .goog-submenu-arrow{display:none}.goog-menu-filter{margin:2px;border:1px solid silver;background:white;overflow:hidden}.goog-menu-filter div{color:gray;position:absolute;padding:1px}.goog-menu-filter input{margin:0;border:0;background:transparent;width:100%}.goog-menuitem-partially-checked{background-image:url(/img/check-outline.gif);background-position:4px 50%;background-repeat:no-repeat}.goog-menuitem-fully-checked{background-image:url(/img/check.gif);background-position:4px 50%;background-repeat:no-repeat}.goog-menu-button{margin:0 2px 2px 2px;border:0;padding:0;font:normal Tahoma,Arial,sans-serif;color:#000;background:#ddd url("/img/button-bg.gif") repeat-x top left;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none}.goog-menu-button-outer-box,.goog-menu-button-inner-box{border-style:solid;border-color:#aaa;vertical-align:middle}.goog-menu-button-outer-box{margin:0;border-width:1px 0;padding:0}.goog-menu-button-inner-box{margin:0 -1px;border-width:0 1px;padding:0 4px 2px 4px}* html .goog-menu-button-inner-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-outer-box{left:-1px}* html .goog-menu-button-rtl .goog-menu-button-inner-box{left:0}*:first-child+html .goog-menu-button-inner-box{left:-1px}*:first-child+html .goog-menu-button-rtl .goog-menu-button-inner-box{left:1px}::root .goog-menu-button,::root .goog-menu-button-outer-box,::root .goog-menu-button-inner-box{line-height:0}::root .goog-menu-button-caption,::root .goog-menu-button-dropdown{line-height:normal}.goog-menu-button-disabled{background-image:none!important;opacity:0.4;-moz-opacity:0.4;filter:alpha(opacity=40)}.goog-menu-button-disabled .goog-menu-button-outer-box,.goog-menu-button-disabled .goog-menu-button-inner-box,.goog-menu-button-disabled .goog-menu-button-caption,.goog-menu-button-disabled .goog-menu-button-dropdown{color:#333!important;border-color:#999!important}* html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}*:first-child+html .goog-menu-button-disabled{margin:2px 1px!important;padding:0 1px!important}.goog-menu-button-hover .goog-menu-button-outer-box,.goog-menu-button-hover .goog-menu-button-inner-box{border-color:#9cf #69e #69e #7af!important;}.goog-menu-button-active,.goog-menu-button-open{background-color:#bbb;background-position:bottom left}.goog-menu-button-focused .goog-menu-button-outer-box,.goog-menu-button-focused .goog-menu-button-inner-box{border-color:#3366cc}.goog-menu-button-caption{padding:0 4px 0 0;vertical-align:middle}.goog-menu-button-rtl .goog-menu-button-caption{padding:0 0 0 4px}.goog-menu-button-dropdown{width:7px;background:url(/img/toolbar_icons.gif) no-repeat -176px;vertical-align:middle}.goog-flat-menu-button{margin:0 2px;padding:1px 4px;font:normal 95% Tahoma,Arial,sans-serif;color:#333;text-decoration:none;list-style:none;vertical-align:middle;cursor:pointer;outline:none;-moz-outline:none;border-width:1px;border-style:solid;border-color:#c9c9c9;background-color:#fff}.goog-flat-menu-button-disabled *{color:#999;border-color:#ccc;cursor:default}.goog-flat-menu-button-hover,.goog-flat-menu-button-hover{border-color:#9cf #69e #69e #7af!important;}.goog-flat-menu-button-active{background-color:#bbb;background-position:bottom left}.goog-flat-menu-button-focused{border-color:#3366cc}.goog-flat-menu-button-caption{padding-right:10px;vertical-align:middle}.goog-flat-menu-button-dropdown{width:7px;background:url(/img/toolbar_icons.gif) no-repeat -176px;vertical-align:middle}h1{font-size:1.8em}.g-doc{width:auto;margin:0 10px}.g-doc-1024{margin-left:10px}#ae-logo{background:url(//www.google.com/images/logos/app_engine_logo_sm.gif) 0 0 no-repeat;display:block;width:178px;height:30px;margin:4px 0 0 0}.ae-ir span{position:absolute;display:block;width:0;height:0;overflow:hidden}.ae-noscript{position:absolute;left:-5000px}#ae-lhs-nav{border-right:3px solid #e5ecf9}.ae-notification{margin-bottom:.6em;text-align:center}.ae-notification strong{display:block;width:55%;margin:0 auto;text-align:center;padding:.6em;background-color:#fff1a8;font-weight:bold}.ae-alert{font-weight:bold;background:url(/img/icn/icn-warning.gif) no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-info{background:url(/img/icn/icn-info.gif) no-repeat;margin-bottom:.5em;padding-left:1.8em}.ae-promo{padding:.5em .8em;margin:.6em 0;background-color:#fffbe8;border:1px solid #fff1a9;text-align:left}.ae-promo strong{position:relative;top:.3em}.ae-alert-text,.ae-warning-text{background-color:transparent;background-position:right 1px;padding:0 18px 0 0}.ae-alert-text{color:#c00}.ae-warning-text{color:#f90}.ae-alert-c span{display:inline-block}.ae-message{border:1px solid #e5ecf9;background-color:#f6f9ff;margin-bottom:1em;padding:.5em}.ae-errorbox{border:1px solid #f00;background-color:#fee;margin-bottom:1em;padding:1em}#bd .ae-errorbox ul{padding-bottom:0}.ae-form dt{font-weight:bold}.ae-form dt em,.ae-field-hint{margin-top:.2em;color:#666667;font-size:.85em}.ae-field-yyyymmdd,.ae-field-hhmmss{width:6em}.ae-field-hint-hhmmss{margin-left:2.3em}.ae-form label{display:block;margin:0 0 .2em 0;font-weight:bold}.ae-radio{margin-bottom:.3em}.ae-radio label{display:inline}.ae-form dd,.ae-input-row{margin-bottom:.6em}.ae-input-row-group{border:1px solid #fff1a9;background:#fffbe8;padding:8px}.ae-btn-row{margin-top:1.4em;margin-bottom:1em}.ae-btn-row-note{padding:5px 0 6px 0}.ae-btn-row-note span{padding-left:18px;padding-right:.5em;background:transparent url(/img/icn/icn-info.gif) 0 0 no-repeat}.ae-btn-primary{font-weight:bold}form .ae-cancel{margin-left:.5em}.ae-submit-inline{margin-left:.8em}.ae-radio-bullet{width:20px;float:left}.ae-label-hanging-indent{margin-left:5px}.ae-divider{margin:0 .6em 0 .5em}.ae-nowrap{white-space:nowrap}.ae-pre-wrap{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;_white-space:pre;}wbr:after{content:"\00200B"}a button{text-decoration:none}.ae-alert ul{margin-bottom:.75em;margin-top:.25em;line-height:1.5em}.ae-alert h4{color:#000;font-weight:bold;padding:0 0 .5em}.ae-form-simple-list{list-style-type:none;padding:0;margin-bottom:1em}.ae-form-simple-list li{padding:.3em 0 .5em .5em;border-bottom:1px solid #c3d9ff}div.ae-datastore-index-to-delete,div.ae-datastore-index-to-build{color:#aaa}#hd p{padding:0}#hd li{display:inline}ul{padding:0 0 1em 1.2em}#ae-userinfo{text-align:right;white-space:nowrap;}#ae-userinfo ul{padding-bottom:0;padding-top:5px}#ae-appbar-lrg{margin:0 0 1.25em 0;padding:.25em .5em;background-color:#e5ecf9;border-top:1px solid #36c}#ae-appbar-lrg h1{font-size:1.2em;padding:0}#ae-appbar-lrg h1 span{font-size:80%;font-weight:normal}#ae-appbar-lrg form{display:inline;padding-right:.1em;margin-right:.5em}#ae-appbar-sml{margin:0 0 1.25em 0;height:8px;padding:0 .5em;background:#e5ecf9}.ae-rounded-sml{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px}#ae-appbar-lrg a{margin-top:.3em}a.ae-ext-link{background:url(/img/icn/icn-open-in-new-window.png) no-repeat right;padding-right:18px;margin-right:8px}.ae-no-pad{padding-left:1em}.ae-message h4{margin-bottom:.3em;padding-bottom:0}#ft{text-align:center;margin:2.5em 0 1em;padding-top:.5em;border-top:2px solid #c3d9ff}#bd h3{font-weight:bold;font-size:1.4em}#bd h3 .ae-apps-switch{font-weight:normal;font-size:.7em;margin-left:2em}#bd p{padding:0 0 1em 0}#ae-content{padding-left:1em}.ae-unimportant{color:#666}.ae-new-usr td{border-top:1px solid #ccccce;background-color:#ffe}.ae-error-td td{border:2px solid #f00;background-color:#fee}.ae-delete{cursor:pointer;border:none;background:transparent;}.ae-btn-large{background:#039 url(/img/icn/button_back.png) repeat-x;color:#fff;font-weight:bold;font-size:1.2em;padding:.5em;border:2px outset #000;cursor:pointer}.ae-breadcrumb{margin:0 0 1em}.ae-disabled,a.ae-disabled,a.ae-disabled:hover,a.ae-disabled:active{color:#666!important;text-decoration:none!important;cursor:default!important;opacity:.4!important;-moz-opacity:.4!important;filter:alpha(opacity=40)!important}input.ae-readonly{border:2px solid transparent;border-left:0;background-color:transparent}span.ae-text-input-clone{padding:5px 5px 5px 0}.ae-loading{opacity:.4;-moz-opacity:.4;filter:alpha(opacity=40)}.ae-tip{margin-top:1em;background:url(/img/tip.png) top left no-repeat;padding:5px 0 0 25px;height:1.5em}sup.ae-new-sup{color:red}.ae-action{color:#00c;cursor:pointer;text-decoration:underline}.ae-toggle{padding-left:16px;background-position:left center;background-repeat:no-repeat;cursor:pointer}.ae-minus{background-image:url(/img/wgt/minus.gif)}.ae-plus{background-image:url(/img/wgt/plus.gif)}.ae-print{background-image:url(/img/print.gif);padding-left:19px}.ae-currency,.ae-table thead th.ae-currency{text-align:right;white-space:nowrap}#ae-loading{font-size:1.2em;position:absolute;text-align:center;top:0;width:100%}#ae-loading div{margin:0 auto;background:#fff1a9;width:5em;font-weight:bold;padding:4px 10px;-moz-border-radius-bottomleft:3px;-moz-border-radius-bottomright:3px;-webkit-border-radius-bottomleft:3px;-webkit-border-radius-bottomright:3px}.ae-occlude{filter:alpha(opacity=0);position:absolute}.g-tpl-66-34 .g-unit,.g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-tpl-66-34 .g-unit,.g-unit .g-unit .g-unit .g-tpl-66-34 .g-unit{display:inline;margin:0;width:33.999%;float:right}.g-unit .g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-unit .g-tpl-66-34 .g-first,.g-unit .g-tpl-66-34 .g-first,.g-tpl-66-34 .g-first{display:inline;margin:0;width:65.999%;float:left}.ae-ie6-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}h2.ae-section-header{background:#e5ecf9;padding:.2em .4em;margin-bottom:.5em}.ae-field-span{padding:3px 0}select{font:13px/13px Arial,sans-serif;color:#000;border-width:1px;border-style:solid;border-color:#bbb #999 #999 #bbb;-webkit-border-radius:2px;-moz-border-radius:2px;background:#eee;background:-moz-linear-gradient(top,#fff,#ddd);filter:progid:DXImageTransform.Microsoft.Gradient(EndColorstr='#dddddd',StartColorstr='#ffffff',GradientType=0);cursor:pointer;padding:2px 1px;margin:0}select:hover{border-color:#000}select[disabled],select[disabled]:active{color:#666;border-color:#ddd;background-color:#f3f3f3;background-image:none;text-shadow:none;cursor:auto}.ae-table-plain{border-collapse:collapse;width:100%}.ae-table{border:1px solid #c5d7ef;border-collapse:collapse;width:100%}#bd h2.ae-table-title{background:#e5ecf9;margin:0;color:#000;font-size:1em;padding:3px 0 3px 5px;border-left:1px solid #c5d7ef;border-right:1px solid #c5d7ef;border-top:1px solid #c5d7ef}.ae-table-caption,.ae-table caption{border:1px solid #c5d7ef;background:#e5ecf9;-moz-margin-start:-1px}.ae-table caption{padding:3px 5px;text-align:left}.ae-table th,.ae-table td{background-color:#fff;padding:.35em 1em .25em .35em;margin:0}.ae-table thead th{font-weight:bold;text-align:left;background:#c5d7ef;vertical-align:bottom}.ae-table thead th .ae-no-bold{font-weight:normal}.ae-table tfoot tr td{border-top:1px solid #c5d7ef;background-color:#e5ecf9}.ae-table td{border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even td,.ae-even th,.ae-even-top td,.ae-even-tween td,.ae-even-bottom td,ol.ae-even{background-color:#e9e9e9;border-top:1px solid #c5d7ef;border-bottom:1px solid #c5d7ef}.ae-even-top td{border-bottom:0}.ae-even-bottom td{border-top:0}.ae-even-tween td{border:0}.ae-table .ae-tween td{border:0}.ae-table .ae-tween-top td{border-bottom:0}.ae-table .ae-tween-bottom td{border-top:0}#bd .ae-table .cbc{width:1.5em;padding-right:0}.ae-table #ae-live td{background-color:#ffeac0}.ae-table-fixed{table-layout:fixed}.ae-table-fixed td,.ae-table-nowrap{overflow:hidden;white-space:nowrap}.ae-paginate strong{margin:0 .5em}tfoot .ae-paginate{text-align:right}.ae-table-caption .ae-paginate,.ae-table-caption .ae-orderby{padding:2px 5px}.modal-dialog{background:#c1d9ff;border:1px solid #3a5774;color:#000;padding:4px;position:absolute;font-size:1.3em;-moz-box-shadow:0 1px 4px #333;-webkit-box-shadow:0 1px 4px #333;box-shadow:0 1px 4px #333}.modal-dialog a,.modal-dialog a:link,.modal-dialog a:visited{color:#06c;cursor:pointer}.modal-dialog-bg{background:#666;left:0;position:absolute;top:0}.modal-dialog-title{background:#e0edfe;color:#000;cursor:pointer;font-size:120%;font-weight:bold;padding:8px 15px 8px 8px;position:relative;_zoom:1;}.modal-dialog-title-close{background:#e0edfe url(https://ssl.gstatic.com/editor/editortoolbar.png) no-repeat -528px 0;cursor:default;height:15px;position:absolute;right:10px;top:8px;width:15px;vertical-align:middle}.modal-dialog-buttons,.modal-dialog-content{background-color:#fff;padding:8px}.modal-dialog-buttons button{margin-right:.75em}.goog-buttonset-default{font-weight:bold}.goog-tab{position:relative;border:1px solid #8ac;padding:4px 9px;color:#000;background:#e5ecf9;border-top-left-radius:2px;border-top-right-radius:2px;-moz-border-radius-topleft:2px;-webkit-border-top-left-radius:2px;-moz-border-radius-topright:2px;-webkit-border-top-right-radius:2px}.goog-tab-bar-top .goog-tab{margin:1px 4px 0 0;border-bottom:0;float:left}.goog-tab-bar-bottom .goog-tab{margin:0 4px 1px 0;border-top:0;float:left}.goog-tab-bar-start .goog-tab{margin:0 0 4px 1px;border-right:0}.goog-tab-bar-end .goog-tab{margin:0 1px 4px 0;border-left:0}.goog-tab-hover{text-decoration:underline;cursor:pointer}.goog-tab-disabled{color:#fff;background:#ccc;border-color:#ccc}.goog-tab-selected{background:#fff!important;color:black;font-weight:bold}.goog-tab-bar-top .goog-tab-selected{top:1px;margin-top:0;padding-bottom:5px}.goog-tab-bar-bottom .goog-tab-selected{top:-1px;margin-bottom:0;padding-top:5px}.goog-tab-bar-start .goog-tab-selected{left:1px;margin-left:0;padding-right:9px}.goog-tab-bar-end .goog-tab-selected{left:-1px;margin-right:0;padding-left:9px}.goog-tab-content{padding:.1em .8em .8em .8em;border:1px solid #8ac;border-top:none}.goog-tab-bar{position:relative;margin:0 0 0 5px;border:0;padding:0;list-style:none;cursor:default;outline:none}.goog-tab-bar-clear{border-top:1px solid #8ac;clear:both;height:0;overflow:hidden}.goog-tab-bar-start{float:left}.goog-tab-bar-end{float:right}* html .goog-tab-bar-start{margin-right:-3px}* html .goog-tab-bar-end{margin-left:-3px}#ae-nav ul{list-style-type:none;margin:0;padding:1em 0}#ae-nav ul li{padding-left:.5em}#ae-nav .ae-nav-selected{color:#000;display:block;font-weight:bold;background-color:#e5ecf9;margin-right:-1px;border-top-left-radius:4px;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px}#ae-nav .ae-nav-bold{font-weight:bold}#ae-nav ul li span.ae-nav-disabled{color:#666}#ae-nav ul ul{margin:0;padding:0 0 0 .5em}#ae-nav ul ul li{padding-left:.5em}#ae-nav ul li a,#ae-nav ul li span,#ae-nav ul ul li a{padding-left:.5em}#ae-nav li a:link,#ae-nav li a:visited{color:#00c}.ae-nav-group{padding:.5em;margin:0 .75em 0 0;background-color:#fffbe8;border:1px solid #fff1a9}.ae-nav-group h4{font-weight:bold;padding:auto auto .5em .5em;padding-left:.4em;margin-bottom:.5em;padding-bottom:0}.ae-nav-group ul{margin:0 0 .5em 0;padding:0 0 0 1.3em;list-style-type:none}.ae-nav-group ul li{padding-bottom:.5em}.ae-nav-group li a:link,.ae-nav-group li a:visited{color:#00c}.ae-nav-group li a:hover{color:#00c}@media print{body{font-size:13px;width:8.5in;background:#fff}table,.ae-table-fixed{table-layout:automatic}tr{display:table-row!important}.g-doc-1024{width:8.5in}#ae-appbar-lrg,.ae-table-caption,.ae-table-nowrap,.ae-nowrap,th,td{overflow:visible!important;white-space:normal!important;background:#fff!important}.ae-print,.ae-toggle{display:none}#ae-lhs-nav-c{display:none}#ae-content{margin:0;padding:0}.goog-zippy-collapsed,.goog-zippy-expanded{background:none!important;padding:0!important}}#ae-logs-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-logs{background-color:#c5d7ef;padding:1px;line-height:1.65}#ae-logs .ae-table-caption{border:0}#ae-logs-c ol,#ae-logs-c li{list-style:none;padding:0;margin:0}#ae-logs-c li li{margin:0 0 0 3px;padding:0 0 0 17px}.ae-log-noerror{padding-left:23px}#ae-logs-form .goog-inline-block{margin-top:0}.ae-logs-reqlog .snippet{margin:.1em}.ae-logs-applog .snippet{color:#666}.ae-logs-severity{display:block;float:left;height:1.2em;width:1.2em;line-height:1.2;text-align:center;text-transform:capitalize;font-weight:bold;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px}.ae-logs-severity-4{background-color:#f22;color:#000}.ae-logs-severity-3{background-color:#f90;color:#000}.ae-logs-severity-2{background-color:#fd0}.ae-logs-severity-1{background-color:#3c0;color:#000}.ae-logs-severity-0{background-color:#09f;color:#000}#ae-logs-legend{margin:1em 0 0 0}#ae-logs-legend ul{list-style:none;margin:0;padding:0}#ae-logs-legend li,#ae-logs-legend strong{float:left;margin:0 1em 0 0}#ae-logs-legend li span{margin-right:.3em}.ae-logs-timestamp{padding:0 5px;font-size:85%}#ae-logs-form-c{margin-bottom:5px;padding-bottom:.5em;padding-left:1em}#ae-logs-form{padding:.3em 0 0}#ae-logs-form .ae-label-row{float:left;padding-top:.2em}#ae-logs-form .ae-input-row,#ae-logs-form .ae-btn-row{margin-left:3.5em}#ae-logs-form .ae-btn-row{margin-bottom:0}#ae-logs-requests-c{margin-bottom:.1em}#ae-logs-requests-c input{margin:0}#ae-logs-requests-all-label{margin-right:1.5em}#ae-logs-form-options{margin-top:8px}#ae-logs-tip{margin:.2em 0}#ae-logs-expand{margin-right:.2em}#ae-logs-severity-level-label{margin-top:.3em;display:block}#ae-logs-filter-hint-labels-list{margin:2px 0}#ae-logs-filter-hint-labels-list span{position:absolute}#ae-logs-filter-hint-labels-list ul{margin-left:5.5em;padding:0}#ae-logs-filter-hint-labels-list li{float:left;margin-right:.4em;line-height:1.2}.ae-toggle .ae-logs-getdetails,.ae-toggle pre{display:none}.ae-log-expanded .ae-toggle pre{display:block}#ae-logs-c .ae-log .ae-toggle{cursor:default;background:none;padding-left:0}#ae-logs-c .ae-log .ae-toggle h5{cursor:pointer;background-position:0 .55em;background-repeat:no-repeat;padding-left:17px}.ae-log .ae-plus h5{background-image:url(/img/wgt/plus.gif)}.ae-log .ae-minus h5{background-image:url(/img/wgt/minus.gif)}.ae-log{overflow:hidden;background-color:#fff;padding:.3em 0;line-height:1.65;border-bottom:1px solid #c5d7ef}.ae-log .ae-even{background-color:#e9e9e9;border:0}.ae-log h5{font-weight:normal;white-space:nowrap;padding:.4em 0 0 0}.ae-log span,.ae-log strong{margin:0 .3em}.ae-log .ae-logs-snippet{color:#666}.ae-log pre,.ae-logs-expanded{padding:.3em 0 .5em 1.5em;margin:0;font-family:"Courier New"}.ae-log .file{font-weight:bold}.ae-logs-app .ae-logs-req{display:none}.ae-logs-req .ae-app,.ae-logs-both .ae-app{padding-left:1em}.ae-cron-status-ok{color:#008000;font-size:90%;font-weight:bold}.ae-cron-status-error{color:#a03;font-size:90%;font-weight:bold}#ae-cronjobs-table .ae-table td{vertical-align:top}#ae-tasks-table td{vertical-align:top}#ae-tasks-quota{margin:0 0 1em 0}#ae-tasks-quota .ae-dash-quota-bar{width:150px}#ae-tasks-quota #ae-dash-quota-bar-col,#ae-tasks-quota .ae-dash-quota-bar{width:200px}.ae-taskqueues-paused-row{color:#666;font-style:italic;font-weight:bold}#ae-taskqueues-quota{margin-bottom:.75em}#ae-taskqueues-quota .ae-quota-safety-limit{width:30%}#ae-taskqueues-table{margin-top:1em}#ae-queuedetails-queuecontrols{margin-top:1em;margin-bottom:1em}#ae-dos-blacklist-rejects-table{text-align:left}#ae-dash-quota-percent-col{width:3.5em}#ae-tasks-delete-col{width:1em}#ae-tasks-eta-col,#ae-tasks-creation-col{width:11em}#ae-tasks-actions-col{width:7em}#ae-tasks-retry-col,#ae-tasks-body-col{width:4em}#ae-tasks-headers-col{width:7em}#ae-createapp-start{background-color:#c6d5f1;padding:1em;padding-bottom:2em;text-align:center}#ae-createapp-id-check{margin:0 0 0 1em}#ae-createapp-id-content{width:100%}#ae-createapp-id-content td{vertical-align:top}#ae-createapp-id-td{white-space:nowrap;width:1%}#ae-createapp-id-td #ae-createapp-id-error{position:absolute;width:24em;padding-left:1em;white-space:normal}#ae-createapp-id-error-td{padding-left:1em}#ae-admin-dev-invite label{float:left;width:3.6em;position:relative;top:.3em}#ae-admin-dev-invite .ae-radio{margin-left:3.6em}#ae-admin-dev-invite .ae-radio label{float:none;width:auto;font-weight:normal;position:static}#ae-admin-dev-invite .goog-button{margin-left:3.6em}#ae-admin-dev-invite .ae-field-hint{margin-left:4.2em}#ae-admin-dev-invite .ae-radio .ae-field-hint{margin-left:0}.ae-you{color:#008000}#ae-authdomain-opts{margin-bottom:1em}#ae-authdomain-content .ae-input-text,#ae-authdomain-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-authdomain-opts a{margin-left:1em}#ae-authdomain-opts-hint{margin-top:.2em;color:#666667;font-size:.85em}#ae-authdomain-content #ae-authdomain-desc .ae-field-hint{margin-left:0}#ae-storage-opts{margin-bottom:1em}#ae-storage-content .ae-input-text,#ae-storage-content .ae-field-hint{margin:.3em 0 .4em 2.5em}#ae-storage-opts a{margin-left:1em}#ae-storage-opts-hint{margin-top:.2em;color:#666667;font-size:.85em}#ae-storage-content #ae-storage-desc .ae-field-hint{margin-left:0}#ae-dash .g-section{margin:0 0 1em}#ae-dash * .g-section{margin:0}#ae-dash-quota .ae-alert{padding-left:1.5em}#ae-dash-graph-c{border:1px solid #c5d7ef;padding:5px 0}#ae-dash-graph-change{margin:0 0 0 5px}#ae-dash-graph-img{padding:5px;margin-top:.5em;background-color:#fff;display:block}#ae-dash-graph-nodata{text-align:center}#ae-dash .ae-logs-severity{margin-right:.5em}#ae-dash .g-c{padding:0 0 0 .1em}#ae-dash .g-tpl-50-50 .g-unit .g-c{padding:0 0 0 1em}#ae-dash .g-tpl-50-50 .g-first .g-c{padding:0 1em 0 .1em}.ae-quota-warnings{background-color:#fffbe8;margin:0;padding:.5em .5em 0;text-align:left}.ae-quota-warnings div{padding:0 0 .5em}#ae-dash-quota-refresh-info{font-size:85%}#ae-dash #ae-dash-quota-bar-col,#ae-dash .ae-dash-quota-bar{width:100px}#ae-dash-quotadetails #ae-dash-quota-bar-col,#ae-dash-quotadetails .ae-dash-quota-bar{width:200px}#ae-dash-quota-percent-col{width:3.5em}#ae-dash-quota-cost-col{width:15%}#ae-dash-quota-alert-col{width:1%}#ae-dash .ae-dash-quota-alert-td{padding:0}.ae-dash-quota-alert-td a{display:block;width:14px;height:14px}#ae-dash .ae-dash-quota-alert-td .ae-alert{display:block;width:14px;height:14px;margin:0;padding:0}#ae-dash-quota tbody th{font-weight:normal}#ae-dash-quota caption{padding:0}#ae-dash-quota caption .g-c{padding:3px}.ae-dash-quota-bar{float:left;background-color:#c0c0c0;height:13px;margin:.1em 0 0 0;position:relative}.ae-dash-quota-bar-free{background:url(/img/free_marker.png) top left no-repeat;width:7px;height:13px;position:absolute;top:0;left:0}#ae-dash-quota-footnote{margin:5px 0 0;font-weight:normal}.ae-quota-warning{background-color:#f90}.ae-quota-alert{background-color:#c00}.ae-quota-normal{background-color:#0b0}.ae-quota-alert-text{color:#c00}.ae-favicon-text{font-size:.85em}#ae-dash-popular{width:97%}#ae-dash-popular-reqsec-col{width:6.5em}#ae-dash-popular-req-col{width:7em}#ae-dash-popular-cpu-avg-col{width:9.5em}#ae-dash-popular-cpu-percent-col{width:7em}#ae-dash-popular .ae-unimportant{font-size:80%}#ae-dash-popular .ae-nowrap,#ae-dash-errors .ae-nowrap{margin-right:5px;overflow:hidden}#ae-dash-popular th span,#ae-dash-errors th span{font-size:.8em;font-weight:normal;display:block}#ae-dash-errors caption .g-unit{width:9em}#ae-dash-errors-count-col{width:5em}#ae-dash-errors-percent-col{width:7em}#ae-dash-graph-chart-type{float:left;margin-right:1em}#ae-apps-all strong.ae-disabled{color:#000;background:#eee}.ae-quota-resource{width:30%}.ae-quota-safety-limit{width:10%}#ae-quota-details h3{padding-bottom:0;margin-bottom:.25em}#ae-quota-details table{margin-bottom:1.75em}#ae-quota-details table.ae-quota-requests{margin-bottom:.5em}#ae-quota-refresh-note p{text-align:right;padding-top:.5em;padding-bottom:0;margin-bottom:0}#ae-quota-first-api.g-section{padding-bottom:0;margin-bottom:.25em}#ae-instances-summary-table,#ae-instances-details-table{margin-bottom:1em}.ae-instances-details-availability-image{float:left;margin-right:.5em}#ae-admin-dev-table{margin:0 0 15px 0}#ae-sms-countryselect{margin-right:.5em}#ae-admin-enable-form{margin-bottom:1em}#ae-admin-services-c{margin-top:2em}#ae-admin-services{padding:0 0 0 3em;margin-bottom:1em;font-weight:bold}#ae-admin-logs-table-c{_margin-right:-2000px;_position:relative;_width:100%;background:#fff}#ae-admin-logs-table{margin:0;padding:0}#ae-admin-logs-filters{padding:3px 0 3px 5px}#ae-admin-logs-pagination{padding:6px 5px 0 0;text-align:right;width:45%}#ae-admin-logs-pagination span.ae-disabled{color:#666;background-color:transparent}#ae-admin-logs-table td{white-space:nowrap}#ae-datastore-explorer-c{_margin-right:-3000px;_position:relative;_width:100%}#ae-datastore-explorer form dt{margin:1em 0 0 0}#ae-datastore-explorer #ae-datastore-explorer-labels{margin:0 0 3px}#ae-datastore-explorer-header .ae-action{margin-left:1em}#ae-datastore-explorer .id{white-space:nowrap}#ae-datastore-explorer caption{text-align:right;padding:5px}#ae-datastore-explorer-submit{margin-top:5px}#ae-datastore-explorer-namespace{margin-top:7px;margin-right:5px}#ae-datastore-explorer-gql-spacer{margin-top:22px}h4 #ae-datastore-explorer-gql-label{font-weight:normal}#ae-datastore-form em{font-style:normal;font-weight:normal;margin:0 0 0 .2em;color:#666}#ae-datastore-form dt{font-weight:bold}#ae-datastore-form dd{margin:.4em 0 .3em 1.5em;overflow:auto;zoom:1}#ae-datastore-form dd em{width:4em;float:left}#ae-datastore-form dd.ae-last{margin-bottom:1em}#ae-datastore-explorer-tabs-content{margin-bottom:1em}#ae-datastore-explorer-list .ae-label-row,#ae-datastore-explorer-new .ae-label-row{float:left;padding-top:.2em}#ae-datastore-explorer-list .ae-input-row,#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-input-row,#ae-datastore-explorer-new .ae-btn-row{margin-left:6em}#ae-datastore-explorer-list .ae-btn-row,#ae-datastore-explorer-new .ae-btn-row{margin-bottom:0}.ae-datastore-index-name{font-size:1.2em;font-weight:bold}.ae-table .ae-datastore-index-defs{padding-left:20px}.ae-datastore-index-defs-row{border-top:1px solid #ddd}.ae-datastore-index-defs .ae-unimportant{font-size:.8em}.ae-datastore-index-status{border:1px solid #c0dfbf;background:#f3f7f3;margin:0 25px 0 0;padding:3px}#ae-datastore-index-status-col{width:15%}.ae-datastore-index-status-Building{border-color:#edebcd;background:#fefdec}.ae-datastore-index-status-Deleting{border-color:#ccc;background:#eee}.ae-datastore-index-status-Error{border-color:#ffd3b4;background:#ffeae0}.ae-datastore-pathlink{font-size:.9em}#ae-datastore-stats-top-level-c{padding-bottom:1em;margin-bottom:1em;border-bottom:1px solid #e5ecf9}#ae-datastore-stats-top-level{width:100%}#ae-datastore-stats-piecharts-c{margin-bottom:1em}.ae-datastore-stats-piechart-label{font-size:.85em;font-weight:normal;text-align:center;padding:0}#ae-datastore-stats-property-type{width:65%}#ae-datastore-stats-size-all{width:35%}#ae-datastore-stats-property-name{width:60%}#ae-datastore-stats-type{width:10%}#ae-datastore-stats-size-entity{width:30%}#ae-datastore-blob-filter-form{margin-bottom:1em}#ae-datastore-blob-query-filter-label{padding-right:.5em}#ae-datastore-blob-filter-contents{padding-top:.5em}#ae-datastore-blob-date-after,#ae-datastore-blob-date-before{float:left}#ae-datastore-blob-date-after{margin-right:1em}#ae-datastore-blob-order label{font-weight:normal}#ae-datastore-blob-col-check{width:2%}#ae-datastore-blob-col-file{width:45%}#ae-datastore-blob-col-type{width:14%}#ae-datastore-blob-col-size{width:16%}#ae-blobstore-col-date{width:18%}#ae-blob-detail-filename{padding-bottom:0}#ae-blob-detail-filename span{font-weight:normal}#ae-blob-detail-key{font-size:85%}#ae-blob-detail-preview{margin-top:1em}#ae-blob-detail-dl{text-align:right}#ae-billing-form-c{_margin-right:-3000px;_position:relative;_width:100%}.ae-rounded-top-small{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px}.ae-progress-content{height:400px}#ae-billing-tos{text-align:left;width:100%;margin-bottom:.5em}.ae-billing-budget-section{margin-bottom:1.5em}.ae-billing-budget-section .g-unit,.g-unit .ae-billing-budget-section .g-unit,.g-unit .g-unit .ae-billing-budget-section .g-unit{margin:0 0 0 11em;width:auto;float:none}.g-unit .g-unit .ae-billing-budget-section .g-first,.g-unit .ae-billing-budget-section .g-first,.ae-billing-budget-section .g-first{margin:0;width:11em;float:left}#ae-billing-form .ae-btn-row{margin-left:11em}#ae-billing-form .ae-btn-row .ae-info{margin-top:10px}#ae-billing-checkout{width:150px;float:left}#ae-billing-alloc-table{border:1px solid #c5d7ef;border-bottom:none;width:100%;margin-top:.5em}#ae-billing-alloc-table th,#ae-billing-alloc-table td{padding:.35em 1em .25em .35em;border-bottom:1px solid #c5d7ef;color:#000;white-space:nowrap}.ae-billing-resource{background-color:transparent;font-weight:normal}#ae-billing-alloc-table tr th span{font-weight:normal}#ae-billing-alloc-table th{white-space:nowrap}#ae-billing-alloc-table .ae-editable span,#ae-billing-alloc-table .ae-readonly input{display:none}#ae-billing-alloc-table .ae-readonly span,#ae-billing-alloc-table .ae-editable input{display:auto}.ae-billing-percent{font-size:80%;color:#666;margin-left:3px}#ae-billing-week-info{margin-top:5px;line-height:1.4}#ae-billing-storage-fn{margin-top:.3em}#ae-billing-allocation-noscript{margin-top:1.5em}#ae-billing-allocation-custom-opts{margin-left:2.2em}#ae-billing-settings h2{font-size:1em;display:inline}#ae-billing-settings p{padding:.3em 0 .5em}#ae-billing-settings-table{margin:.4em 0 .5em}#ae-settings-resource-col{width:19%}#ae-settings-budget-col{width:11%}#ae-billing-settings-table .ae-settings-budget-col{padding-right:2em}.ae-table th.ae-settings-unit-cell,.ae-table td.ae-settings-unit-cell,.ae-table th.ae-total-unit-cell,.ae-table td.ae-total-unit-cell{padding-left:1.2em}#ae-settings-unit-col{width:18%}#ae-settings-paid-col{width:15%}#ae-settings-free-col{width:15%}#ae-settings-total-col{width:22%}.ae-billing-inline-link{margin-left:.5em}.ae-billing-settings-section{margin-bottom:2em}.ae-billing-settings-formbutton{margin-top:.5em}#ae-billing-budget-setup-checkout{margin-bottom:0}#ae-billing-vat-c .ae-field-hint{width:85%}#ae-billing-checkout-note{margin-top:.8em}.ae-table thead th.ae-currency-th{text-align:right}#ae-billing-logs-date{width:15%}#ae-billing-logs-admin{width:15%}#ae-billing-logs-event{width:54%}#ae-billing-logs-amount{text-align:right;width:8%}#ae-billing-logs-balance{text-align:right;width:8%}#ae-billing-history-expand .ae-action{margin-left:1em}.ae-table .ae-billing-usage-report{width:100%;*width:auto;margin:0 0 1em 0}.ae-table .ae-billing-usage-report th,.ae-billing-charges th{color:#666;border-top:0}.ae-table .ae-billing-usage-report th,.ae-table .ae-billing-usage-report td,.ae-billing-charges th,.ae-billing-charges td{background-color:transparent;padding:.4em 0;border-bottom:1px solid #ddd}.ae-table .ae-billing-usage-report tfoot td,.ae-billing-charges tfoot td{border-bottom:none}.ae-billing-report-resource{width:30%}.ae-billing-report-used{width:20%}.ae-billing-report-free{width:20%}.ae-billing-report-paid{width:15%}.ae-billing-report-charge{width:15%}.ae-billing-change-resource{width:85%}.ae-billing-change-budget{width:15%}#ae-billing-always-on-label{display:inline}#ae-billing-budget-buffer-label{display:inline}.ae-billing-charges{width:50%}.ae-billing-charges-charge{text-align:right}.goog-zippy-expanded{background-image:url(/img/wgt/minus.gif);cursor:pointer;background-repeat:no-repeat;padding-left:17px}.goog-zippy-collapsed{background-image:url(/img/wgt/plus.gif);cursor:pointer;background-repeat:no-repeat;padding-left:17px}#ae-admin-logs-pagination{width:auto}#ae-domain-admins-list li{margin-bottom:.3em}#ae-domain-admins-list button{margin-left:.5em}#ae-new-app-dialog-c{width:500px}#ae-new-app-dialog-c .g-section{margin-bottom:1em}div#dombilling-tt-setup-note{border:1px solid #ccc;padding:1em;background:#efe}div#dombilling-tt-setup-error{padding:0.5em;background:#fee}p.light-note{color:#555}.ae-bottom-message{margin-top:1em}
\ No newline at end of file
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/datastore_admin/static/js/compiled.js google-appengine-1.4.0-prerelease/google/appengine/ext/datastore_admin/static/js/compiled.js
--- local/google_appengine/google/appengine/ext/datastore_admin/static/js/compiled.js 2010-10-14 21:40:42.261836605 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/datastore_admin/static/js/compiled.js 2010-11-18 21:44:32.000000000 +0000
@@ -7,11 +7,11 @@
a:{var Sa="",R;if(Ma&&H.opera){var Ta=H.opera.version;Sa=typeof Ta=="function"?Ta():Ta}else{if(Na)R=/rv\:([^\);]+)(\)|;)/;else if(Q)R=/MSIE\s+([^\);]+)(\)|;)/;else if(Oa)R=/WebKit\/(\S+)/;if(R){var Ua=R.exec(Ja());Sa=Ua?Ua[1]:""}}if(Q){var Va,Wa=H.document;Va=Wa?Wa.documentMode:q;if(Va>parseFloat(Sa)){Ra=r(Va);break a}}Ra=Sa}var Xa=Ra,Ya={},S=function(a){return Ya[a]||(Ya[a]=ua(Xa,a)>=0)};var Za=!Q||S("9");Q&&S("9");var $a=function(a){var b;b=(b=a.className)&&typeof b[A]=="function"?b[A](/\s+/):[];var c;c=Ba(arguments,1);for(var d=0,f=0;f<c[x];f++)if(!(xa(b,c[f])>=0)){b[w](c[f]);d++}c=d==c[x];a.className=b.join(" ");return c};var ab=function(a,b,c,d){a=d||a;var f=b&&b!="*"?b.toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(!Oa||t.compatMode=="CSS1Compat"||S("528"))&&(f||c))return a.querySelectorAll(f+(c?"."+c:""));if(c&&a.getElementsByClassName){b=a.getElementsByClassName(c);if(f){a={};for(var e=d=0,g;g=b[e];e++)if(f==g.nodeName)a[d++]=g;aa(a,d);return a}else return b}b=a.getElementsByTagName(f||"*");if(c){a={};for(e=d=0;g=b[e];e++){f=g.className;var j;if(j=typeof f[A]=="function"){f=f[A](/\s+/);j=xa(f,c)>=0}if(j)a[d++]=
g}aa(a,d);return a}else return b},cb=function(a,b){Ca(b,function(c,d){if(d=="style")a.style.cssText=c;else if(d=="class")a.className=c;else if(d=="for")a.htmlFor=c;else if(d in bb)a.setAttribute(bb[d],c);else a[d]=c})},bb={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",height:"height",width:"width",usemap:"useMap",frameborder:"frameBorder",type:"type"},eb=function(a,b,c,d){function f(g){if(g)b.appendChild(K(g)?a.createTextNode(g):g)}for(d=d;d<
c[x];d++){var e=c[d];ha(e)&&!(ja(e)&&e.nodeType>0)?ya(db(e)?Aa(e):e,f):f(e)}},fb=function(){var a=t,b=arguments,c=b[0],d=b[1];if(!Za&&d&&(d[ga]||d[F])){c=["<",c];d[ga]&&c[w](' name="',sa(d[ga]),'"');if(d[F]){c[w](' type="',sa(d[F]),'"');var f={};Ea(f,d);d=f;delete d[F]}c[w](">");c=c.join("")}c=a.createElement(c);if(d)if(K(d))c.className=d;else J(d)=="array"?$a.apply(i,[c].concat(d)):cb(c,d);b[x]>2&&eb(a,c,b,2);return c},db=function(a){if(a&&typeof a[x]=="number")if(ja(a))return typeof a.item=="function"||
typeof a.item=="string";else if(ia(a))return typeof a.item=="function";return l};var gb;!Q||S("9");Q&&S("8");var T=function(){};T[y].z=l;T[y].n=function(){if(!this.z){this.z=h;this.c()}};T[y].c=function(){};var U=function(a,b){this.type=a;ca(this,b);u(this,this[D])};M(U,T);U[y].c=function(){delete this[F];delete this[D];delete this.currentTarget};U[y].s=l;U[y].N=h;var V=function(a,b){a&&this.o(a,b)};M(V,U);G=V[y];ca(G,i);G.relatedTarget=i;G.offsetX=0;G.offsetY=0;G.clientX=0;G.clientY=0;G.screenX=0;G.screenY=0;G.button=0;G.keyCode=0;G.charCode=0;G.ctrlKey=l;G.altKey=l;G.shiftKey=l;G.metaKey=l;G.M=l;G.A=i;
G.o=function(a,b){var c=this.type=a[F];ca(this,a[D]||a.srcElement);u(this,b);var d=a.relatedTarget;if(d){if(Na)try{d=d.nodeName&&d}catch(f){d=i}}else if(c=="mouseover")d=a.fromElement;else if(c=="mouseout")d=a.toElement;this.relatedTarget=d;this.offsetX=a.offsetX!==q?a.offsetX:a.layerX;this.offsetY=a.offsetY!==q?a.offsetY:a.layerY;this.clientX=a.clientX!==q?a.clientX:a.pageX;this.clientY=a.clientY!==q?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=
-a[ea]||0;this.charCode=a.charCode||(c=="keypress"?a[ea]:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.M=Ia?a.metaKey:a.ctrlKey;this.A=a;delete this.N;delete this.s};G.c=function(){V.H.c[E](this);this.A=i;ca(this,i);u(this,i);this.relatedTarget=i};var W=function(a,b){this.D=b;this.b=[];this.K(a)};M(W,T);G=W[y];G.r=i;G.w=i;G.k=function(a){this.r=a};G.i=function(){if(this.b[x])return this.b.pop();return this.u()};G.j=function(a){this.b[x]<this.D?this.b[w](a):this.v(a)};G.K=function(a){if(a>this.D)throw o("[goog.structs.SimplePool] Initial cannot be greater than max");for(var b=0;b<a;b++)this.b[w](this.u())};G.u=function(){return this.r?this.r():{}};G.v=function(a){if(this.w)this.w(a);else if(ja(a))if(ia(a.n))a.n();else for(var b in a)delete a[b]};
+a[ea]||0;this.charCode=a.charCode||(c=="keypress"?a[ea]:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.M=Ia?a.metaKey:a.ctrlKey;this.state=a.state;this.A=a;delete this.N;delete this.s};G.c=function(){V.H.c[E](this);this.A=i;ca(this,i);u(this,i);this.relatedTarget=i};var W=function(a,b){this.D=b;this.b=[];this.K(a)};M(W,T);G=W[y];G.r=i;G.w=i;G.k=function(a){this.r=a};G.i=function(){if(this.b[x])return this.b.pop();return this.u()};G.j=function(a){this.b[x]<this.D?this.b[w](a):this.v(a)};G.K=function(a){if(a>this.D)throw o("[goog.structs.SimplePool] Initial cannot be greater than max");for(var b=0;b<a;b++)this.b[w](this.u())};G.u=function(){return this.r?this.r():{}};G.v=function(a){if(this.w)this.w(a);else if(ja(a))if(ia(a.n))a.n();else for(var b in a)delete a[b]};
G.c=function(){W.H.c[E](this);for(var a=this.b;a[x];)this.v(a.pop());delete this.b};var hb;var ib=(hb="ScriptEngine"in H&&H.ScriptEngine()=="JScript")?H.ScriptEngineMajorVersion()+"."+H.ScriptEngineMinorVersion()+"."+H.ScriptEngineBuildVersion():"0";var jb=function(){},kb=0;G=jb[y];G.d=0;G.f=l;G.t=l;G.o=function(a,b,c,d,f,e){if(ia(a))this.C=h;else if(a&&a[fa]&&ia(a[fa]))this.C=l;else throw o("Invalid listener argument");this.p=a;this.G=b;this.src=c;this.type=d;this.I=!!f;this.B=e;this.t=l;this.d=++kb;this.f=l};G.handleEvent=function(a){if(this.C)return this.p[E](this.B||this.src,a);return this.p[fa][E](this.p,a)};var lb,mb,nb,ob,pb,qb,rb,sb,tb,ub,vb;
(function(){function a(){return{a:0,e:0}}function b(){return[]}function c(){var n=function(v){return g[E](n.src,n.d,v)};return n}function d(){return new jb}function f(){return new V}var e=hb&&!(ua(ib,"5.7")>=0),g;qb=function(n){g=n};if(e){lb=function(){return j.i()};mb=function(n){j.j(n)};nb=function(){return k.i()};ob=function(n){k.j(n)};pb=function(){return m.i()};rb=function(){m.j(c())};sb=function(){return C.i()};tb=function(n){C.j(n)};ub=function(){return p.i()};vb=function(n){p.j(n)};var j=
new W(0,600);j.k(a);var k=new W(0,600);k.k(b);var m=new W(0,600);m.k(c);var C=new W(0,600);C.k(d);var p=new W(0,600);p.k(f)}else{lb=a;mb=I;nb=b;ob=I;pb=c;rb=I;sb=d;tb=I;ub=f;vb=I}})();var X={},Y={},Z={},wb={},xb=function(a,b,c,d,f){if(b)if(J(b)=="array"){for(var e=0;e<b[x];e++)xb(a,b[e],c,d,f);return i}else{d=!!d;var g=Y;b in g||(g[b]=lb());g=g[b];if(!(d in g)){g[d]=lb();g.a++}g=g[d];var j=a[L]||(a[L]=++ka),k;g.e++;if(g[j]){k=g[j];for(e=0;e<k[x];e++){g=k[e];if(g.p==c&&g.B==f){if(g.f)break;return k[e].d}}}else{k=g[j]=nb();g.a++}e=pb();e.src=a;g=sb();g.o(c,e,a,b,d,f);c=g.d;e.d=c;k[w](g);X[c]=g;Z[j]||(Z[j]=nb());Z[j][w](g);if(a.addEventListener){if(a==H||!a.L)a.addEventListener(b,
e,d)}else a.attachEvent(yb(b),e);return c}else throw o("Invalid event type");},zb=function(a,b,c,d){if(!d.q)if(d.F){for(var f=0,e=0;f<d[x];f++)if(d[f].f){var g=d[f].G;g.src=i;rb(g);tb(d[f])}else{if(f!=e)d[e]=d[f];e++}aa(d,e);d.F=l;if(e==0){ob(d);delete Y[a][b][c];Y[a][b].a--;if(Y[a][b].a==0){mb(Y[a][b]);delete Y[a][b];Y[a].a--}if(Y[a].a==0){mb(Y[a]);delete Y[a]}}}},yb=function(a){if(a in wb)return wb[a];return wb[a]="on"+a},Bb=function(a,b,c,d,f){var e=1;b=b[L]||(b[L]=++ka);if(a[b]){a.e--;a=a[b];
if(a.q)a.q++;else a.q=1;try{for(var g=a[x],j=0;j<g;j++){var k=a[j];if(k&&!k.f)e&=Ab(k,f)!==l}}finally{a.q--;zb(c,d,b,a)}}return Boolean(e)},Ab=function(a,b){var c=a[fa](b);if(a.t){var d=a.d;if(X[d]){var f=X[d];if(!f.f){var e=f.src,g=f[F],j=f.G,k=f.I;if(e.removeEventListener){if(e==H||!e.L)e.removeEventListener(g,j,k)}else e.detachEvent&&e.detachEvent(yb(g),j);e=e[L]||(e[L]=++ka);j=Y[g][k][e];if(Z[e]){var m=Z[e],C=xa(m,f);if(C>=0){wa(m[x]!=i);N.splice[E](m,C,1)}m[x]==0&&delete Z[e]}f.f=h;j.F=h;zb(g,
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/datastore_admin/testutil.py google-appengine-1.4.0-prerelease/google/appengine/ext/datastore_admin/testutil.py
--- local/google_appengine/google/appengine/ext/datastore_admin/testutil.py 2010-10-14 21:40:42.281836815 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/datastore_admin/testutil.py 2010-11-18 21:44:32.000000000 +0000
@@ -26,11 +26,11 @@
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_file_stub
from google.appengine.api import queueinfo
from google.appengine.api import user_service_stub
from google.appengine.api.memcache import memcache_stub
-from google.appengine.api.labs.taskqueue import taskqueue_stub
+from google.appengine.api.taskqueue import taskqueue_stub
from google.testing.pybase import googletest
class HandlerTestBase(googletest.TestCase):
"""Base class for all webapp.RequestHandler tests."""
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/db/__init__.py google-appengine-1.4.0-prerelease/google/appengine/ext/db/__init__.py
--- local/google_appengine/google/appengine/ext/db/__init__.py 2010-10-14 21:40:41.893838072 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/db/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -76,11 +76,10 @@
-import base64
import copy
import datetime
import logging
import re
import time
@@ -90,11 +89,11 @@
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
from google.appengine.api import namespace_manager
from google.appengine.api import users
-from google.appengine.datastore import datastore_pb
+from google.appengine.datastore import datastore_query
Error = datastore_errors.Error
BadValueError = datastore_errors.BadValueError
BadPropertyError = datastore_errors.BadPropertyError
BadRequestError = datastore_errors.BadRequestError
@@ -880,19 +879,22 @@ def put(self, **kwargs):
If this instance is new, we add an entity to the datastore.
Otherwise, we update this instance, and the key will remain the
same.
+ Args:
+ config: datastore_rpc.Configuration to use for this request.
+
Returns:
The key of the instance (either the existing key or a new key).
Raises:
TransactionFailedError if the data could not be committed.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
self._populate_internal_entity()
- return datastore.Put(self._entity, rpc=rpc)
+ return datastore.Put(self._entity, config=config)
save = put
def _populate_entity(self, _entity_class=datastore.Entity):
"""Internal helper -- Populate self._entity or create a new one
@@ -931,15 +933,18 @@ def _populate_entity(self, _entity_cla
return entity
def delete(self, **kwargs):
"""Deletes this entity from the datastore.
+ Args:
+ config: datastore_rpc.Configuration to use for this request.
+
Raises:
TransactionFailedError if the data could not be committed.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
- datastore.Delete(self.key(), rpc=rpc)
+ config = datastore._GetConfigFromKwargs(kwargs)
+ datastore.Delete(self.key(), config=config)
self._key = self.key()
self._key_name = None
self._parent_key = None
self._entity = None
@@ -1035,10 +1040,11 @@ def get(cls, keys, **kwargs):
story = Story.get(story_key)
Args:
keys: Key within datastore entity collection to find; or string key;
or list of Keys or string keys.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
If a single key was given: a Model instance associated with key
for provided class if it exists in the datastore, otherwise
None; if a list of keys was given: a list whose items are either
@@ -1046,12 +1052,12 @@ def get(cls, keys, **kwargs):
Raises:
KindError if any of the retreived objects are not instances of the
type associated with call to 'get'.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
- results = get(keys, rpc=rpc)
+ config = datastore._GetConfigFromKwargs(kwargs)
+ results = get(keys, config=config)
if results is None:
return None
if isinstance(results, Model):
instances = [results]
@@ -1070,43 +1076,45 @@ def get_by_key_name(cls, key_names, pa
"""Get instance of Model class by its key's name.
Args:
key_names: A single key-name or a list of key-names.
parent: Parent of instances to get. Can be a model or key.
+ config: datastore_rpc.Configuration to use for this request.
"""
try:
parent = _coerce_to_key(parent)
except BadKeyError, e:
raise BadArgumentError(str(e))
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
key_names, multiple = datastore.NormalizeAndTypeCheck(key_names, basestring)
keys = [datastore.Key.from_path(cls.kind(), name, parent=parent)
for name in key_names]
if multiple:
- return get(keys, rpc=rpc)
+ return get(keys, config=config)
else:
- return get(keys[0], rpc=rpc)
+ return get(keys[0], config=config)
@classmethod
def get_by_id(cls, ids, parent=None, **kwargs):
"""Get instance of Model class by id.
Args:
key_names: A single id or a list of ids.
parent: Parent of instances to get. Can be a model or key.
+ config: datastore_rpc.Configuration to use for this request.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
if isinstance(parent, Model):
parent = parent.key()
ids, multiple = datastore.NormalizeAndTypeCheck(ids, (int, long))
keys = [datastore.Key.from_path(cls.kind(), id, parent=parent)
for id in ids]
if multiple:
- return get(keys, rpc=rpc)
+ return get(keys, config=config)
else:
- return get(keys[0], rpc=rpc)
+ return get(keys[0], config=config)
@classmethod
def get_or_insert(cls, key_name, **kwds):
"""Transactionally retrieve or create an instance of Model class.
@@ -1268,10 +1276,15 @@ def fields(cls):
def create_rpc(deadline=None, callback=None, read_policy=STRONG_CONSISTENCY):
"""Create an rpc for use in configuring datastore calls.
+ NOTE: This functions exists for backwards compatibility. Please use
+ create_config() instead. NOTE: the latter uses 'on_completion',
+ which is a function taking an argument, wherease create_rpc uses
+ 'callback' which is a function without arguments.
+
Args:
deadline: float, deadline for calls in seconds.
callback: callable, a callback triggered when this rpc completes,
accepts one argument: the returned rpc.
read_policy: flag, set to EVENTUAL_CONSISTENCY to enable eventually
@@ -1290,21 +1304,22 @@ def get(keys, **kwargs):
automatically).
Args:
keys: Key within datastore entity collection to find; or string key;
or list of Keys or string keys.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
If a single key was given: a Model instance associated with key
for if it exists in the datastore, otherwise None; if a list of
keys was given: a list whose items are either a Model instance or
None.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
try:
- entities = datastore.Get(keys, rpc=rpc)
+ entities = datastore.Get(keys, config=config)
except datastore_errors.EntityNotFoundError:
assert not multiple
return None
models = []
for entity in entities:
@@ -1323,21 +1338,22 @@ def get(keys, **kwargs):
def put(models, **kwargs):
"""Store one or more Model instances.
Args:
models: Model instance or list of Model instances.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
A Key or a list of Keys (corresponding to the argument's plurality).
Raises:
TransactionFailedError if the data could not be committed.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
models, multiple = datastore.NormalizeAndTypeCheck(models, Model)
entities = [model._populate_internal_entity() for model in models]
- keys = datastore.Put(entities, rpc=rpc)
+ keys = datastore.Put(entities, config=config)
if multiple:
return keys
assert len(keys) == 1
return keys[0]
@@ -1347,26 +1363,27 @@ def put(models, **kwargs):
def delete(models, **kwargs):
"""Delete one or more Model instances.
Args:
models: Model instance, key, key string or iterable thereof.
+ config: datastore_rpc.Configuration to use for this request.
Raises:
TransactionFailedError if the data could not be committed.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
if isinstance(models, (basestring, Model, Key)):
models = [models]
else:
try:
models = iter(models)
except TypeError:
models = [models]
keys = [_coerce_to_key(v) for v in models]
- datastore.Delete(keys, rpc=rpc)
+ datastore.Delete(keys, config=config)
def allocate_ids(model, size, **kwargs):
"""Allocates a range of IDs of size for the model_key defined by model.
@@ -1377,10 +1394,12 @@ def allocate_ids(model, size, **kwargs):
Args:
model: Model instance, Key or string to serve as a template specifying the
ID sequence in which to allocate IDs. Returned ids should only be used
in entities with the same parent (if any) and kind as this key.
+ size: Number of IDs to allocate.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
(start, end) of the allocated range, inclusive.
"""
return datastore.AllocateIds(_coerce_to_key(model), size=size, **kwargs)
@@ -1406,13 +1425,13 @@ def allocate_id_range(model, start, end,
Args:
model: Model instance, Key or string to serve as a template specifying the
ID sequence in which to allocate IDs. Allocated ids should only be used
in entities with the same parent (if any) and kind as this key.
- start: first id of the range to allocate, inclusive
- end: last id of the range to allocate, inclusive
- rpc: datastore.RPC to use for this request.
+ start: first id of the range to allocate, inclusive.
+ end: last id of the range to allocate, inclusive.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
One of (KEY_RANGE_EMPTY, KEY_RANGE_CONTENTION, KEY_RANGE_COLLISION). If not
KEY_RANGE_EMPTY, this represents a potential issue with using the allocated
key range.
@@ -1667,57 +1686,10 @@ def _load_entity_values(cls, entity):
if key not in entity_values:
entity_values[str(key)] = value
return entity_values
-def websafe_encode_cursor(compiled_cursor):
- """Get a serialized cursor given a compiled cursor object.
-
- Args:
- compiled_cursor: The datastore_pb.CompiledCursor cursor to serialize.
-
- Returns:
- A base64-encoded serialized cursor.
- """
- return base64.urlsafe_b64encode(compiled_cursor.Encode())
-
-
-def websafe_decode_cursor(cursor):
- """Gets a datastore_pb.CompiledCursor given its serialized form.
-
- Args:
- cursor: A serialized cursor as returned by websafe_encode_cursor.
-
- Returns:
- A datastore_pb.CompiledCursor.
-
- Raises:
- BadValueError: if the cursor argument is not a string type of does not
- represent a serialized cursor.
- """
- if not isinstance(cursor, basestring):
- raise BadValueError(
- 'Cursor must be a str or unicode instance, not a %s'
- % type(cursor).__name__)
- else:
- cursor = str(cursor)
- try:
- decoded = base64.urlsafe_b64decode(cursor)
- cursor = datastore_pb.CompiledCursor(decoded)
- except (ValueError, TypeError), e:
- raise datastore_errors.BadValueError(
- 'Invalid cursor %s. Details: %s' % (cursor, e))
- except Exception, e:
- if e.__class__.__name__ == 'ProtocolBufferDecodeError':
- raise datastore_errors.BadValueError('Invalid cursor %s. '
- 'Details: %s' % (cursor, e))
- else:
- raise
-
- return cursor
-
-
class _BaseQuery(object):
"""Base class for both Query and GqlQuery."""
_compile = False
def __init__(self, model_class=None, keys_only=False, compile=True,
@@ -1758,18 +1730,18 @@ def run(self, **kwargs):
If you know the number of results you need, consider fetch() instead,
or use a GQL query with a LIMIT clause. It's more efficient.
Args:
- rpc: datastore.DatastoreRPC to use for this request.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
Iterator for this query.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
raw_query = self._get_query()
- iterator = raw_query.Run(rpc=rpc)
+ iterator = raw_query.Run(config=config)
if self._compile:
self._last_raw_query = raw_query
if self._keys_only:
@@ -1783,20 +1755,29 @@ def __iter__(self):
If you know the number of results you need, consider fetch() instead,
or use a GQL query with a LIMIT clause. It's more efficient.
"""
return self.run()
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ if '_last_raw_query' in state:
+ del state['_last_raw_query']
+ return state
+
def get(self, **kwargs):
"""Get first result from this.
+ Args:
+ config: datastore_rpc.Configuration to use for this request.
+
Beware: get() ignores the LIMIT clause on GQL queries.
Returns:
First result from running the query if there are any, else None.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
- results = self.fetch(1, rpc=rpc)
+ config = datastore._GetConfigFromKwargs(kwargs)
+ results = self.fetch(1, config=config)
try:
return results[0]
except IndexError:
return None
@@ -1804,20 +1785,21 @@ def count(self, limit=1000, **kwargs):
"""Number of entities this query fetches.
Beware: count() ignores the LIMIT clause on GQL queries.
Args:
- limit, a number. If there are more results than this, stop short and
+ limit: A number. If there are more results than this, stop short and
just return this number. Providing this argument makes the count
operation more efficient.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
Number of entities this query fetches.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
raw_query = self._get_query()
- result = raw_query.Count(limit=limit, rpc=rpc)
+ result = raw_query.Count(limit=limit, config=config)
if self._compile:
self._last_raw_query = raw_query
return result
def fetch(self, limit, offset=0, **kwargs):
@@ -1829,26 +1811,26 @@ def fetch(self, limit, offset=0, **kwa
Beware: fetch() ignores the LIMIT clause on GQL queries.
Args:
limit: Maximum number of results to return.
offset: Optional number of results to skip first; default zero.
- rpc: datastore.DatastoreRPC to use for this request.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
A list of db.Model instances. There may be fewer than 'limit'
results if there aren't enough results to satisfy the request.
"""
- rpc = datastore.GetRpcFromKwargs(kwargs)
+ config = datastore._GetConfigFromKwargs(kwargs)
accepted = (int, long)
if not (isinstance(limit, accepted) and isinstance(offset, accepted)):
raise TypeError('Arguments to fetch() must be integers')
if limit < 0 or offset < 0:
raise ValueError('Arguments to fetch() must be >= 0')
raw_query = self._get_query()
- raw = raw_query.Get(limit, offset, rpc=rpc)
+ raw = raw_query.Get(limit, offset, config=config)
if self._compile:
self._last_raw_query = raw_query
if self._keys_only:
@@ -2352,11 +2334,11 @@ def run(self, **kwargs):
If the GQL query string contains a LIMIT clause, this function fetches
all results before returning an iterator. Otherwise results are retrieved
in batches by the iterator.
Args:
- rpc: datastore.DatastoreRPC to use for this request.
+ config: datastore_rpc.Configuration to use for this request.
Returns:
Iterator for this query.
"""
if self._proto_query.limit() >= 0:
@@ -3426,7 +3408,11 @@ def __get__(self, model_instance, mode
run_in_transaction = datastore.RunInTransaction
run_in_transaction_custom_retries = datastore.RunInTransactionCustomRetries
RunInTransaction = run_in_transaction
RunInTransactionCustomRetries = run_in_transaction_custom_retries
+websafe_encode_cursor = datastore_query.Cursor.to_websafe_string
+websafe_decode_cursor = datastore_query.Cursor.from_websafe_string
is_in_transaction = datastore.IsInTransaction
+
+create_config = datastore.CreateConfig
Only in google-appengine-1.4.0-prerelease/google/appengine/ext/db: metadata.py
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/deferred/deferred.py google-appengine-1.4.0-prerelease/google/appengine/ext/deferred/deferred.py
--- local/google_appengine/google/appengine/ext/deferred/deferred.py 2010-10-14 21:40:41.692836745 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/deferred/deferred.py 2010-11-18 21:44:32.000000000 +0000
@@ -87,11 +87,11 @@ def do_something_later(key, amount):
import logging
import os
import pickle
import types
-from google.appengine.api.labs import taskqueue
+from google.appengine.api import taskqueue
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/key_range/__init__.py google-appengine-1.4.0-prerelease/google/appengine/ext/key_range/__init__.py
--- local/google_appengine/google/appengine/ext/key_range/__init__.py 2010-10-14 21:40:42.518894016 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/key_range/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -197,10 +197,11 @@ def make_directed_query(self, kind_cla
A db.Query instance.
Raises:
KeyRangeError: if self.direction is not in (KeyRange.ASC, KeyRange.DESC).
"""
+ assert self._app is None, '_app is not supported for db.Query'
direction = self.__get_direction("", "-")
query = db.Query(kind_class, namespace=self.namespace, keys_only=keys_only)
query.order("%s__key__" % direction)
query = self.filter_query(query)
@@ -235,10 +236,11 @@ def make_ascending_query(self, kind_cl
keys_only: bool, default False, query only for keys.
Returns:
A db.Query instance.
"""
+ assert self._app is None, '_app is not supported for db.Query'
query = db.Query(kind_class, namespace=self.namespace, keys_only=keys_only)
query.order("__key__")
query = self.filter_query(query)
return query
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/mapreduce/handlers.py google-appengine-1.4.0-prerelease/google/appengine/ext/mapreduce/handlers.py
--- local/google_appengine/google/appengine/ext/mapreduce/handlers.py 2010-10-14 21:40:42.593836466 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/mapreduce/handlers.py 2010-11-18 21:44:32.000000000 +0000
@@ -25,11 +25,11 @@
import os
import simplejson
import time
from google.appengine.api import memcache
-from google.appengine.api.labs import taskqueue
+from google.appengine.api import taskqueue
from google.appengine.ext import db
from google.appengine.ext.mapreduce import base_handler
from google.appengine.ext.mapreduce import context
from google.appengine.ext.mapreduce import model
from google.appengine.ext.mapreduce import quota
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/mapreduce/input_readers.py google-appengine-1.4.0-prerelease/google/appengine/ext/mapreduce/input_readers.py
--- local/google_appengine/google/appengine/ext/mapreduce/input_readers.py 2010-10-14 21:40:42.595836536 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/mapreduce/input_readers.py 2010-11-18 21:44:32.000000000 +0000
@@ -321,12 +321,16 @@ def split_input(cls, mapper_spec):
query.order('-__key__') query is not present, an inaccurate guess at
sharding will be made by splitting the full key range.
Args:
mapper_spec: MapperSpec with params containing 'entity_kind'.
- May also have 'batch_size' in the params to specify the number
- of entities to process in each batch.
+ May have 'namespaces' in the params as either a list of namespace
+ strings or a comma-seperated list of namespaces. If specified then the
+ input reader will only yield entities in the given namespaces. If
+ 'namespaces' is not given then the current namespace will be used. May
+ also have 'batch_size' in the params to specify the number of entities
+ to process in each batch.
Returns:
A list of InputReader objects of length <= number_of_shards. These
may be DatastoreInputReader or DatastoreKeyInputReader objects.
"""
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/remote_api/remote_api_services.py google-appengine-1.4.0-prerelease/google/appengine/ext/remote_api/remote_api_services.py
--- local/google_appengine/google/appengine/ext/remote_api/remote_api_services.py 2010-10-14 21:40:41.800837792 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/remote_api/remote_api_services.py 2010-11-18 21:44:32.000000000 +0000
@@ -27,16 +27,11 @@
from google.appengine.api import urlfetch_service_pb
from google.appengine.api.blobstore import blobstore_service_pb
from google.appengine.api.capabilities import capability_service_pb
from google.appengine.api.images import images_service_pb
from google.appengine.api.memcache import memcache_service_pb
-try:
- __import__('google.appengine.api.labs.taskqueue.taskqueue_service_pb')
- taskqueue_service_pb = sys.modules.get(
- 'google.appengine.api.labs.taskqueue.taskqueue_service_pb')
-except ImportError:
- from google.appengine.api.taskqueue import taskqueue_service_pb
+from google.appengine.api.taskqueue import taskqueue_service_pb
from google.appengine.api.xmpp import xmpp_service_pb
from google.appengine.datastore import datastore_pb
from google.appengine.ext.remote_api import remote_api_pb
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py google-appengine-1.4.0-prerelease/google/appengine/ext/remote_api/remote_api_stub.py
--- local/google_appengine/google/appengine/ext/remote_api/remote_api_stub.py 2010-10-14 21:40:41.799837652 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/remote_api/remote_api_stub.py 2010-11-18 21:44:32.000000000 +0000
@@ -368,10 +368,11 @@ def _Dynamic_BeginTransaction(self, re
self.__transactions[txid] = TransactionData(thread.get_ident())
self.__next_local_tx += 1
finally:
self.__local_tx_lock.release()
transaction.set_handle(txid)
+ transaction.set_app(request.app())
def _Dynamic_Commit(self, transaction, transaction_response):
txid = transaction.handle()
if txid not in self.__transactions:
raise apiproxy_errors.ApplicationError(
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/search/__init__.py google-appengine-1.4.0-prerelease/google/appengine/ext/search/__init__.py
--- local/google_appengine/google/appengine/ext/search/__init__.py 2009-08-07 10:45:01.441422920 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/search/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -112,11 +112,11 @@ def SearchableProperties(cls):
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
from google.appengine.ext import db
-from google.appengine.datastore import datastore_pb
+from google.appengine.datastore import datastore_query
ALL_PROPERTIES = []
class SearchableEntity(datastore.Entity):
"""A subclass of datastore.Entity that supports full text indexing.
@@ -297,41 +297,42 @@ def Search(self, search_query, word_de
self._search_query = search_query
self._word_delimiter_regex = word_delimiter_regex
self._properties = properties
return self
- def _ToPb(self, *args, **kwds):
+ def GetFilterPredicate(self, *args, **kwds):
"""Adds filters for the search query, then delegates to the superclass.
- Mimics Query._ToPb()'s signature. Raises BadFilterError if a filter on the
- index property already exists.
+ Mimics Query.GetFilterPredicate()'s signature. Raises BadFilterError if a
+ filter on the index property already exists.
Returns:
- datastore_pb.Query
+ datastore_query.FilterPredicate
"""
properties = getattr(self, "_properties", ALL_PROPERTIES)
index_property_name = SearchableEntity.IndexPropertyName(properties)
if index_property_name in self:
raise datastore_errors.BadFilterError(
'%s is a reserved name.' % index_property_name)
- pb = super(SearchableQuery, self)._ToPb(*args, **kwds)
+ filter = super(SearchableQuery, self).GetFilterPredicate(*args, **kwds)
if hasattr(self, '_search_query'):
keywords = SearchableEntity._FullTextIndex(
self._search_query, self._word_delimiter_regex)
- for keyword in keywords:
- filter = pb.add_filter()
- filter.set_op(datastore_pb.Query_Filter.EQUAL)
- prop = filter.add_property()
- prop.set_name(index_property_name)
- prop.set_multiple(len(keywords) > 1)
- prop.mutable_value().set_stringvalue(unicode(keyword).encode('utf-8'))
-
- return pb
+ if keywords:
+ search_filter = datastore_query.make_filter(
+ index_property_name, '=', list(keywords))
+ if filter:
+ filter = datastore_query.CompositeFilter(
+ datastore_query.CompositeFilter.AND,
+ [filter, search_filter])
+ else:
+ filter = search_filter
+ return filter
class SearchableMultiQuery(datastore.MultiQuery):
"""A multiquery that supports Search() by searching subqueries."""
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/webapp/__init__.py google-appengine-1.4.0-prerelease/google/appengine/ext/webapp/__init__.py
--- local/google_appengine/google/appengine/ext/webapp/__init__.py 2010-07-01 22:17:26.935780900 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/webapp/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -228,10 +228,14 @@ def set_status(self, code, message=Non
"""
if not message:
message = Response.http_status_message(code)
self.__status = (code, message)
+ def has_error(self):
+ """Indicates whether the response was an error response."""
+ return self.__status[0] >= 400
+
def clear(self):
"""Clears all data written to the output stream so that it is empty."""
self.out.seek(0)
self.out.truncate(0)
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/webapp/template.py google-appengine-1.4.0-prerelease/google/appengine/ext/webapp/template.py
--- local/google_appengine/google/appengine/ext/webapp/template.py 2009-05-06 00:05:26.000000000 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/webapp/template.py 2010-11-18 21:44:32.000000000 +0000
@@ -43,32 +43,94 @@
import md5
import os
-try:
+from google.appengine.api import lib_config
+from google.appengine.ext import webapp
+
+
+def _django_setup():
+ """Imports and configures Django.
+
+ This can be overridden by defining a function named
+ webapp_django_setup() in the app's appengine_config.py file (see
+ lib_config docs). Such a function should import and configure
+ Django.
+
+ You can also just configure the Django version to be used by setting
+ webapp_django_version in that file.
+
+ Finally, calling use_library('django', <version>) in that file
+ should also work, followed by code to configure Django settings:
+
+ # The first two sections of this example are taken from
+ # http://code.google.com/appengine/docs/python/tools/libraries.html#Django
+
+ import os
+ os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+
+ from google.appengine.dist import use_library
+ use_library('django', '1.1')
+
+ # This last section is necessary to be able to switch between
+ # Django and webapp.template freely, regardless of which was
+ # imported first.
+
+ from django.conf import settings
+ settings._target = None
+
+ If your application also imports Django directly it should ensure
+ that the same code is executed before your app imports Django
+ (directly or indirectly). Perhaps the simplest way to ensure that
+ is to include the following in your main.py (and in each alternate
+ main script):
+
+ from google.appengine.ext.webapp import template
+ import django
+
+ This will ensure that whatever Django setup code you have included
+ in appengine_config.py is executed, as a side effect of importing
+ the webapp.template module.
+ """
+ django_version = _config_handle.django_version
+
+ if django_version is not None:
+ from google.appengine.dist import use_library
+ use_library('django', str(django_version))
+ else:
+ try:
from django import v0_96
-except ImportError:
+ except ImportError:
pass
-import django
-import django.conf
-try:
+ import django
+
+ import django.conf
+ try:
django.conf.settings.configure(
DEBUG=False,
TEMPLATE_DEBUG=False,
TEMPLATE_LOADERS=(
'django.template.loaders.filesystem.load_template_source',
),
)
-except (EnvironmentError, RuntimeError):
+ except (EnvironmentError, RuntimeError):
pass
+
+_config_handle = lib_config.register(
+ 'webapp',
+ {'django_setup': _django_setup,
+ 'django_version': None,
+ })
+
+_config_handle.django_setup()
+
+
import django.template
import django.template.loader
-from google.appengine.ext import webapp
-
def render(template_path, template_dict, debug=False):
"""Renders the template at the given path with the given dict of values.
Example usage:
render("templates/index.html", {"name": "Bret", "values": [1, 2, 3]})
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/ext/zipserve/__init__.py google-appengine-1.4.0-prerelease/google/appengine/ext/zipserve/__init__.py
--- local/google_appengine/google/appengine/ext/zipserve/__init__.py 2009-05-05 23:23:30.000000000 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/ext/zipserve/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -119,11 +119,11 @@ def ServeFromZipFile(self, zipfilename
"""
zipfile_object = self.zipfile_cache.get(zipfilename)
if zipfile_object is None:
try:
zipfile_object = zipfile.ZipFile(zipfilename)
- except (IOError, RuntimeError), err:
+ except (IOError, RuntimeError, zipfile.BadZipfile), err:
logging.error('Can\'t open zipfile %s: %s', zipfilename, err)
zipfile_object = ''
self.zipfile_cache[zipfilename] = zipfile_object
if zipfile_object == '':
self.error(404)
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/runtime/apiproxy_errors.py google-appengine-1.4.0-prerelease/google/appengine/runtime/apiproxy_errors.py
--- local/google_appengine/google/appengine/runtime/apiproxy_errors.py 2009-12-15 21:03:29.947291946 +0000
+++ google-appengine-1.4.0-prerelease/google/appengine/runtime/apiproxy_errors.py 2010-11-18 21:44:32.000000000 +0000
@@ -60,10 +60,13 @@ def __str__(self):
available quota."""
class RequestTooLargeError(Error):
"""Raised by APIProxy calls if the request was too large."""
+class ResponseTooLargeError(Error):
+ """Raised by APIProxy calls if the response was too large."""
+
class CapabilityDisabledError(Error):
"""Raised by APIProxy when API calls are temporarily disabled."""
class FeatureNotEnabledError(Error):
"""Raised by APIProxy when the app must enable a feature to use this call."""
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/runtime/apiproxy.py google-appengine-1.4.0-prerelease/google/appengine/runtime/apiproxy.py
--- local/google_appengine/google/appengine/runtime/apiproxy.py 2010-10-14 21:40:43.079837306 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/runtime/apiproxy.py 2010-11-18 21:44:32.000000000 +0000
@@ -27,10 +27,11 @@
from google.net.proto import ProtocolBuffer
from google.appengine import runtime
from google.appengine.api import apiproxy_rpc
from google3.apphosting.runtime import _apphosting_runtime___python__apiproxy
from google.appengine.runtime import apiproxy_errors
+from google.net.proto2.python.public import message
OK = 0
RPC_FAILED = 1
CALL_NOT_FOUND = 2
ARGUMENT_ERROR = 3
@@ -40,10 +41,11 @@
OTHER_ERROR = 7
OVER_QUOTA = 8
REQUEST_TOO_LARGE = 9
CAPABILITY_DISABLED = 10
FEATURE_DISABLED = 11
+RESPONSE_TOO_LARGE = 12
_ExceptionsMap = {
RPC_FAILED:
(apiproxy_errors.RPCFailedError,
"The remote RPC to the application server failed for the call %s.%s()."),
@@ -66,10 +68,13 @@
(apiproxy_errors.OverQuotaError,
"The API call %s.%s() required more quota than is available."),
REQUEST_TOO_LARGE:
(apiproxy_errors.RequestTooLargeError,
"The request to API call %s.%s() was too large."),
+ RESPONSE_TOO_LARGE:
+ (apiproxy_errors.ResponseTooLargeError,
+ "The response from API call %s.%s() was too large."),
@@ -77,10 +82,13 @@
}
+PROTO_BASE_CLASSES = (ProtocolBuffer.ProtocolMessage, message.Message)
+
+
class RPC(apiproxy_rpc.RPC):
"""A RPC object, suitable for talking to remote services.
Each instance of this object can be used only once, and should not be reused.
@@ -119,28 +127,32 @@ def _WaitImpl(self):
new_exc = apiproxy_errors.InterruptedError(exc, rpc)
raise new_exc.__class__, new_exc, tb
return True
def _MakeCallImpl(self):
- assert isinstance(self.request, ProtocolBuffer.ProtocolMessage), 'not isinstance(%r, %r): sys.modules=%r, sys.path=%r' % (
+ assert isinstance(self.request, PROTO_BASE_CLASSES), 'not isinstance(%r, %r): sys.modules=%r, sys.path=%r' % (
self.request.__class__,
- ProtocolBuffer.ProtocolMessage,
+ PROTO_BASE_CLASSES,
sys.modules,
sys.path)
- assert isinstance(self.response, ProtocolBuffer.ProtocolMessage), 'not isinstance(%r, %r): sys.modules=%r, sys.path=%r' % (
+ assert isinstance(self.response, PROTO_BASE_CLASSES), 'not isinstance(%r, %r): sys.modules=%r, sys.path=%r' % (
self.response.__class__,
- ProtocolBuffer.ProtocolMessage,
+ PROTO_BASE_CLASSES,
sys.modules,
sys.path)
+ if isinstance(self.request, ProtocolBuffer.ProtocolMessage):
e = ProtocolBuffer.Encoder()
self.request.Output(e)
+ request_data = e.buffer()
+ else:
+ request_data = self.request.SerializeToString()
self.__state = RPC.RUNNING
_apphosting_runtime___python__apiproxy.MakeCall(
- self.package, self.call, e.buffer(), self.__result_dict,
+ self.package, self.call, request_data, self.__result_dict,
self.__MakeCallDone, self, deadline=(self.deadline or -1))
def __MakeCallDone(self):
self.__state = RPC.FINISHING
self.cpu_usage_mcycles = self.__result_dict['cpu_usage_mcycles']
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/tools/appcfg.py google-appengine-1.4.0-prerelease/google/appengine/tools/appcfg.py
--- local/google_appengine/google/appengine/tools/appcfg.py 2010-10-14 21:40:43.008837800 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/tools/appcfg.py 2010-11-18 21:44:32.000000000 +0000
@@ -2959,19 +2959,19 @@ def __call__(self, appcfg):
the app, and appcfg.py will create/update the app version referenced
in the app.yaml file at the top level of that directory. appcfg.py
will follow symlinks and recursively upload all files to the server.
Temporary or source control files (e.g. foo~, .svn/*) will be skipped."""),
-
-
-
-
-
-
-
-
-
+ 'download_app': Action(
+ function='DownloadApp',
+ usage='%prog [options] download_app -A app_id [ -V version ] '
+ '<out-dir>',
+ short_desc='Download a previously-uploaded app.',
+ long_desc="""
+Download a previously-uploaded app to the specified directory. The app
+ID is specified by the \"-A\" option. The optional version is specified
+by the \"-V\" option."""),
'update_cron': Action(
function='UpdateCron',
usage='%prog [options] update_cron <directory>',
short_desc='Update application cron definitions.',
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/tools/dev_appserver_blobimage.py google-appengine-1.4.0-prerelease/google/appengine/tools/dev_appserver_blobimage.py
--- local/google_appengine/google/appengine/tools/dev_appserver_blobimage.py 2010-10-14 21:40:43.012837509 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/tools/dev_appserver_blobimage.py 2010-11-18 21:44:32.000000000 +0000
@@ -50,14 +50,11 @@ def CreateBlobImageDispatcher(images_stu
from google.appengine.tools import dev_appserver
class BlobImageDispatcher(dev_appserver.URLDispatcher):
"""Dispatcher that handles image serving requests."""
- _cropped_sizes = [32, 48, 64, 72, 80, 104, 136, 144, 150, 160]
- _uncropped_sizes = [0, 32, 48, 64, 72, 80, 90, 94, 104, 110, 120, 128, 144,
- 150, 160, 200, 220, 288, 320, 400, 512, 576, 640, 720,
- 800, 912, 1024, 1152, 1280, 1440, 1600]
+ _size_limit = 1600
_mime_type_map = {images_service_pb.OutputSettings.JPEG: 'image/jpeg',
images_service_pb.OutputSettings.PNG: 'image/png'}
def __init__(self, images_stub):
"""Constructor.
@@ -92,11 +89,11 @@ def _TransformImage(self, blob_key,
crop_xform.set_crop_left_x(delta)
crop_xform.set_crop_right_x(1.0 - delta)
elif width < height:
crop_xform = images_service_pb.Transform()
delta = (height - width) / (height * 2.0)
- top_delta = max(0, delta - 0.25)
+ top_delta = max(0.0, delta - 0.25)
bottom_delta = 1.0 - (2.0 * delta) + top_delta
crop_xform.set_crop_top_y(top_delta)
crop_xform.set_crop_bottom_y(bottom_delta)
if crop_xform:
image = self._images_stub._Crop(image, crop_xform)
@@ -131,14 +128,12 @@ def _ParseOptions(self, options):
if match.group(1):
resize = int(match.group(1))
if match.group(2):
crop = True
- if resize:
- if crop and resize not in BlobImageDispatcher._cropped_sizes:
- raise ValueError, 'Invalid crop size'
- elif resize not in BlobImageDispatcher._uncropped_sizes:
+ if resize and (resize > BlobImageDispatcher._size_limit or
+ resize < 0):
raise ValueError, 'Invalid resize'
return (resize, crop)
def _ParseUrl(self, url):
"""Parse the URL into the blobkey and option string.
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/tools/dev_appserver_main.py google-appengine-1.4.0-prerelease/google/appengine/tools/dev_appserver_main.py
--- local/google_appengine/google/appengine/tools/dev_appserver_main.py 2010-10-14 21:40:43.015837723 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/tools/dev_appserver_main.py 2010-11-18 21:44:32.000000000 +0000
@@ -116,10 +117,14 @@
ARG_ALLOW_SKIPPED_FILES = 'allow_skipped_files'
ARG_SMTP_HOST = 'smtp_host'
ARG_SMTP_PASSWORD = 'smtp_password'
ARG_SMTP_PORT = 'smtp_port'
ARG_SMTP_USER = 'smtp_user'
+ARG_MYSQL_HOST = 'mysql_host'
+ARG_MYSQL_PORT = 'mysql_port'
+ARG_MYSQL_USER = 'mysql_user'
+ARG_MYSQL_PASSWORD = 'mysql_password'
ARG_STATIC_CACHING = 'static_caching'
ARG_TEMPLATE_DIR = 'template_dir'
ARG_DISABLE_TASK_RUNNING = 'disable_task_running'
ARG_TASK_RETRY_SECONDS = 'task_retry_seconds'
ARG_TRUSTED = 'trusted'
@@ -214,10 +224,14 @@ def ParseArguments(argv):
'enable_sendmail',
'disable_static_caching',
'show_mail_body',
'help',
'history_path=',
+ 'mysql_host=',
+ 'mysql_port=',
+ 'mysql_user=',
+ 'mysql_password=',
'port=',
'require_indexes',
'smtp_host=',
'smtp_password=',
'smtp_port=',
@@ -272,21 +286,27 @@ def ParseArguments(argv):
option_dict[ARG_CLEAR_MATCHER] = True
if option == '--require_indexes':
option_dict[ARG_REQUIRE_INDEXES] = True
+ if option == '--mysql_host':
+ option_dict[ARG_MYSQL_HOST] = value
+
+ if option == '--mysql_port':
+ option_dict[ARG_MYSQL_PORT] = _ParsePort(value, '--mysql_port')
+
+ if option == '--mysql_user':
+ option_dict[ARG_MYSQL_USER] = value
+
+ if option == '--mysql_password':
+ option_dict[ARG_MYSQL_PASSWORD] = value
+
if option == '--smtp_host':
option_dict[ARG_SMTP_HOST] = value
if option == '--smtp_port':
- try:
- option_dict[ARG_SMTP_PORT] = int(value)
- if not (65535 > option_dict[ARG_SMTP_PORT] > 0):
- raise ValueError
- except ValueError:
- print >>sys.stderr, 'Invalid value supplied for SMTP port'
- PrintUsageExit(1)
+ option_dict[ARG_SMTP_PORT] = _ParsePort(value, '--smtp_port')
if option == '--smtp_user':
option_dict[ARG_SMTP_USER] = value
if option == '--smtp_password':
@@ -335,10 +355,32 @@ def ParseArguments(argv):
option_dict[ARG_TRUSTED] = True
return args, option_dict
+def _ParsePort(port, description):
+ """Parses a port number from a string.
+
+ Args:
+ port: string
+ description: string to use in error messages.
+
+ Returns: integer between 0 and 65535
+
+ Raises:
+ ValueError if port is not a valid port number.
+ """
+ try:
+ port = int(port)
+ if not (65535 > port > 0):
+ raise ValueError
+ return port
+ except ValueError:
+ print >>sys.stderr, 'Invalid value %s supplied for %s' % (port, description)
+ PrintUsageExit(1)
+
+
def MakeRpcServer(option_dict):
"""Create a new HttpRpcServer.
Creates a new HttpRpcServer to check for updates to the SDK.
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/tools/dev_appserver.py google-appengine-1.4.0-prerelease/google/appengine/tools/dev_appserver.py
--- local/google_appengine/google/appengine/tools/dev_appserver.py 2010-10-14 21:40:43.010838002 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/tools/dev_appserver.py 2010-11-18 21:44:32.000000000 +0000
@@ -58,15 +58,15 @@
import logging
import mimetools
import mimetypes
import os
import pickle
-import pprint
import random
import select
import shutil
import tempfile
+import yaml
import re
import sre_compile
import sre_constants
import sre_parse
@@ -78,10 +78,12 @@
import types
import urlparse
import urllib
import google
+google._DEV_APPSERVER = True
+
from google.pyglib import gexcept
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import appinfo
from google.appengine.api import appinfo_includes
@@ -96,11 +98,11 @@
from google.appengine.api import yaml_errors
from google.appengine.api.blobstore import blobstore_stub
from google.appengine.api.blobstore import file_blob_storage
from google.appengine.api.capabilities import capability_stub
from google.appengine.api.channel import channel_service_stub
-from google.appengine.api.labs.taskqueue import taskqueue_stub
+from google.appengine.api.taskqueue import taskqueue_stub
from google.appengine.api.matcher import matcher_stub
from google.appengine.api.memcache import memcache_stub
from google.appengine.api.xmpp import xmpp_service_stub
from google.appengine.datastore import datastore_sqlite_stub
@@ -145,10 +147,12 @@
COPY_BLOCK_SIZE = 1 << 20
API_VERSION = '1'
+VERSION_FILE = '../VERSION'
+
SITE_PACKAGES = os.path.normcase(os.path.join(os.path.dirname(os.__file__),
'site-packages'))
DEVEL_PAYLOAD_HEADER = 'HTTP_X_APPENGINE_DEVELOPMENT_PAYLOAD'
DEVEL_PAYLOAD_RAW_HEADER = 'X-AppEngine-Development-Payload'
@@ -866,10 +870,12 @@ def IsPathInSubdirectories(filename,
'wsgiref',
+
+ 'MySQLdb',
])
NOT_SHARED_MODULE_PREFIXES = set([
'google.appengine.ext',
])
@@ -2307,11 +2313,11 @@ def ExecuteCGI(root_path,
__builtin__.open = FakeFile
types.FileType = FakeFile
__builtin__.buffer = NotImplementedFakeClass
- logging.debug('Executing CGI with env:\n%s', pprint.pformat(env))
+ logging.debug('Executing CGI with env:\n%s', repr(env))
try:
reset_modules = exec_script(handler_path, cgi_path, hook)
except SystemExit, e:
logging.debug('CGI exited with status: %s', e)
except:
@@ -3004,10 +3010,36 @@ def ResetModules(self):
apiproxy_stub_map.apiproxy.GetPostCallHooks().Clear()
+def GetVersionObject(isfile=os.path.isfile, open_fn=open):
+ """Gets the version of the SDK by parsing the VERSION file.
+
+ Args:
+ isfile: used for testing.
+ open_fn: Used for testing.
+
+ Returns:
+ A Yaml object or None if the VERSION file does not exist.
+ """
+ version_filename = os.path.join(os.path.dirname(google.__file__),
+ VERSION_FILE)
+ if not isfile(version_filename):
+ logging.error('Could not find version file at %s', version_filename)
+ return None
+
+ version_fh = open_fn(version_filename, 'r')
+ try:
+ version = yaml.safe_load(version_fh)
+ finally:
+ version_fh.close()
+
+ return version
+
+
+
def _ClearTemplateCache(module_dict=sys.modules):
"""Clear template cache in webapp.template module.
Attempts to load template module. Ignores failure. If module loads, the
template cache is cleared.
@@ -3196,10 +3228,12 @@ def _HandleRequest(self):
if config.api_version != API_VERSION:
logging.error(
"API versions cannot be switched dynamically: %r != %r",
config.api_version, API_VERSION)
sys.exit(1)
+ version = GetVersionObject()
+ env_dict['SDK_VERSION'] = version['release']
env_dict['CURRENT_VERSION_ID'] = config.version + ".1"
env_dict['APPLICATION_ID'] = config.application
dispatcher = MatcherDispatcher(login_url,
[implicit_matcher, explicit_matcher])
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/appengine/tools/dev-channel-js.js google-appengine-1.4.0-prerelease/google/appengine/tools/dev-channel-js.js
--- local/google_appengine/google/appengine/tools/dev-channel-js.js 2010-10-14 21:40:43.020837583 +0100
+++ google-appengine-1.4.0-prerelease/google/appengine/tools/dev-channel-js.js 2010-11-18 21:44:32.000000000 +0000
@@ -4,10 +4,16 @@
goog.LOCALE = "en";
goog.evalWorksForGlobals_ = null;
goog.provide = function(name) {
goog.exportPath_(name)
};
+goog.setTestOnly = function(opt_message) {
+ if(!goog.DEBUG) {
+ opt_message = opt_message || "";
+ throw Error("Importing test-only code into non-debug environment" + opt_message ? ": " + opt_message : ".");
+ }
+};
goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
var parts = name.split("."), cur = opt_objectToExportTo || goog.global;
!(parts[0] in cur) && cur.execScript && cur.execScript("var " + parts[0]);
for(var part;parts.length && (part = parts.shift());) {
if(!parts.length && goog.isDef(opt_object)) {
@@ -26,12 +32,12 @@
}
}
return cur
};
goog.globalize = function(obj, opt_global) {
- var global = opt_global || goog.global;
- for(var x in obj) {
+ var global = opt_global || goog.global, x;
+ for(x in obj) {
global[x] = obj[x]
}
};
goog.addDependency = function() {
};
@@ -138,12 +144,12 @@
var type = goog.typeOf(obj);
if(type == "object" || type == "array") {
if(obj.clone) {
return obj.clone()
}
- var clone = type == "array" ? [] : {};
- for(var key in obj) {
+ var clone = type == "array" ? [] : {}, key;
+ for(key in obj) {
clone[key] = goog.cloneObject(obj[key])
}
return clone
}
return obj
@@ -215,12 +221,12 @@
};
goog.setCssNameMapping = function(mapping) {
goog.cssNameMapping_ = mapping
};
goog.getMsg = function(str, opt_values) {
- var values = opt_values || {};
- for(var key in values) {
+ var values = opt_values || {}, key;
+ for(key in values) {
var value = ("" + values[key]).replace(/\$/g, "$$$$");
str = str.replace(RegExp("\\{\\$" + key + "\\}", "gi"), value)
}
return str
};
@@ -441,16 +447,16 @@
return"document" in goog.global && !goog.string.contains(str, "<") ? goog.string.unescapeEntitiesUsingDom_(str) : goog.string.unescapePureXmlEntities_(str)
}
return str
};
goog.string.unescapeEntitiesUsingDom_ = function(str) {
- var el = goog.global.document.createElement("a");
- el.innerHTML = str;
- el[goog.string.NORMALIZE_FN_] && el[goog.string.NORMALIZE_FN_]();
- str = el.firstChild.nodeValue;
+ var el = goog.global.document.createElement("div");
+ el.innerHTML = "<pre>x" + str + "</pre>";
+ el.firstChild[goog.string.NORMALIZE_FN_] && el.firstChild[goog.string.NORMALIZE_FN_]();
+ str = el.firstChild.firstChild.nodeValue.slice(1);
el.innerHTML = "";
- return str
+ return goog.string.canonicalizeNewlines(str)
};
goog.string.unescapePureXmlEntities_ = function(str) {
return str.replace(/&([^;]+);/g, function(s, entity) {
switch(entity) {
case "amp":
@@ -1161,21 +1167,21 @@
for(var key in obj) {
f.call(opt_obj, obj[key], key, obj)
}
};
goog.object.filter = function(obj, f, opt_obj) {
- var res = {};
- for(var key in obj) {
+ var res = {}, key;
+ for(key in obj) {
if(f.call(opt_obj, obj[key], key, obj)) {
res[key] = obj[key]
}
}
return res
};
goog.object.map = function(obj, f, opt_obj) {
- var res = {};
- for(var key in obj) {
+ var res = {}, key;
+ for(key in obj) {
res[key] = f.call(opt_obj, obj[key], key, obj)
}
return res
};
goog.object.some = function(obj, f, opt_obj) {
@@ -1193,12 +1199,12 @@
}
}
return true
};
goog.object.getCount = function(obj) {
- var rv = 0;
- for(var key in obj) {
+ var rv = 0, key;
+ for(key in obj) {
rv++
}
return rv
};
goog.object.getAnyKey = function(obj) {
@@ -1213,19 +1219,19 @@
};
goog.object.contains = function(obj, val) {
return goog.object.containsValue(obj, val)
};
goog.object.getValues = function(obj) {
- var res = [], i = 0;
- for(var key in obj) {
+ var res = [], i = 0, key;
+ for(key in obj) {
res[i++] = obj[key]
}
return res
};
goog.object.getKeys = function(obj) {
- var res = [], i = 0;
- for(var key in obj) {
+ var res = [], i = 0, key;
+ for(key in obj) {
res[i++] = key
}
return res
};
goog.object.containsKey = function(obj, key) {
@@ -1285,19 +1291,19 @@
};
goog.object.setIfUndefined = function(obj, key, value) {
return key in obj ? obj[key] : obj[key] = value
};
goog.object.clone = function(obj) {
- var res = {};
- for(var key in obj) {
+ var res = {}, key;
+ for(key in obj) {
res[key] = obj[key]
}
return res
};
goog.object.transpose = function(obj) {
- var transposed = {};
- for(var key in obj) {
+ var transposed = {}, key;
+ for(key in obj) {
transposed[obj[key]] = key
}
return transposed
};
goog.object.PROTOTYPE_FIELDS_ = ["constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"];
@@ -1507,12 +1513,12 @@
};
goog.dom.classes.toggle = function(element, className) {
var add = !goog.dom.classes.has(element, className);
goog.dom.classes.enable(element, className, add);
return add
-};goog.dom.TagName = {A:"A", ABBR:"ABBR", ACRONYM:"ACRONYM", ADDRESS:"ADDRESS", APPLET:"APPLET", AREA:"AREA", B:"B", BASE:"BASE", BASEFONT:"BASEFONT", BDO:"BDO", BIG:"BIG", BLOCKQUOTE:"BLOCKQUOTE", BODY:"BODY", BR:"BR", BUTTON:"BUTTON", CAPTION:"CAPTION", CENTER:"CENTER", CITE:"CITE", CODE:"CODE", COL:"COL", COLGROUP:"COLGROUP", DD:"DD", DEL:"DEL", DFN:"DFN", DIR:"DIR", DIV:"DIV", DL:"DL", DT:"DT", EM:"EM", FIELDSET:"FIELDSET", FONT:"FONT", FORM:"FORM", FRAME:"FRAME", FRAMESET:"FRAMESET", H1:"H1",
-H2:"H2", H3:"H3", H4:"H4", H5:"H5", H6:"H6", HEAD:"HEAD", HR:"HR", HTML:"HTML", I:"I", IFRAME:"IFRAME", IMG:"IMG", INPUT:"INPUT", INS:"INS", ISINDEX:"ISINDEX", KBD:"KBD", LABEL:"LABEL", LEGEND:"LEGEND", LI:"LI", LINK:"LINK", MAP:"MAP", MENU:"MENU", META:"META", NOFRAMES:"NOFRAMES", NOSCRIPT:"NOSCRIPT", OBJECT:"OBJECT", OL:"OL", OPTGROUP:"OPTGROUP", OPTION:"OPTION", P:"P", PARAM:"PARAM", PRE:"PRE", Q:"Q", S:"S", SAMP:"SAMP", SCRIPT:"SCRIPT", SELECT:"SELECT", SMALL:"SMALL", SPAN:"SPAN", STRIKE:"STRIKE",
+};goog.dom.TagName = {A:"A", ABBR:"ABBR", ACRONYM:"ACRONYM", ADDRESS:"ADDRESS", APPLET:"APPLET", AREA:"AREA", B:"B", BASE:"BASE", BASEFONT:"BASEFONT", BDO:"BDO", BIG:"BIG", BLOCKQUOTE:"BLOCKQUOTE", BODY:"BODY", BR:"BR", BUTTON:"BUTTON", CANVAS:"CANVAS", CAPTION:"CAPTION", CENTER:"CENTER", CITE:"CITE", CODE:"CODE", COL:"COL", COLGROUP:"COLGROUP", DD:"DD", DEL:"DEL", DFN:"DFN", DIR:"DIR", DIV:"DIV", DL:"DL", DT:"DT", EM:"EM", FIELDSET:"FIELDSET", FONT:"FONT", FORM:"FORM", FRAME:"FRAME", FRAMESET:"FRAMESET",
+H1:"H1", H2:"H2", H3:"H3", H4:"H4", H5:"H5", H6:"H6", HEAD:"HEAD", HR:"HR", HTML:"HTML", I:"I", IFRAME:"IFRAME", IMG:"IMG", INPUT:"INPUT", INS:"INS", ISINDEX:"ISINDEX", KBD:"KBD", LABEL:"LABEL", LEGEND:"LEGEND", LI:"LI", LINK:"LINK", MAP:"MAP", MENU:"MENU", META:"META", NOFRAMES:"NOFRAMES", NOSCRIPT:"NOSCRIPT", OBJECT:"OBJECT", OL:"OL", OPTGROUP:"OPTGROUP", OPTION:"OPTION", P:"P", PARAM:"PARAM", PRE:"PRE", Q:"Q", S:"S", SAMP:"SAMP", SCRIPT:"SCRIPT", SELECT:"SELECT", SMALL:"SMALL", SPAN:"SPAN", STRIKE:"STRIKE",
STRONG:"STRONG", STYLE:"STYLE", SUB:"SUB", SUP:"SUP", TABLE:"TABLE", TBODY:"TBODY", TD:"TD", TEXTAREA:"TEXTAREA", TFOOT:"TFOOT", TH:"TH", THEAD:"THEAD", TITLE:"TITLE", TR:"TR", TT:"TT", U:"U", UL:"UL", VAR:"VAR"};goog.dom.ASSUME_QUIRKS_MODE = false;
goog.dom.ASSUME_STANDARDS_MODE = false;
goog.dom.COMPAT_MODE_KNOWN_ = goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;
goog.dom.NodeType = {ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, ENTITY_REFERENCE:5, ENTITY:6, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9, DOCUMENT_TYPE:10, DOCUMENT_FRAGMENT:11, NOTATION:12};
goog.dom.getDomHelper = function(opt_element) {
@@ -2909,12 +2915,12 @@
return"undefined"
}
if(obj == null) {
return"NULL"
}
- var str = [];
- for(var x in obj) {
+ var str = [], x;
+ for(x in obj) {
if(!(!opt_showFn && goog.isFunction(obj[x]))) {
var s = x + " = ";
try {
s += obj[x]
}catch(e) {
@@ -3353,11 +3359,12 @@
goog.events.Event.stopPropagation = function(e) {
e.stopPropagation()
};
goog.events.Event.preventDefault = function(e) {
e.preventDefault()
-};goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
+};goog.events.EventType = {CLICK:"click", DBLCLICK:"dblclick", MOUSEDOWN:"mousedown", MOUSEUP:"mouseup", MOUSEOVER:"mouseover", MOUSEOUT:"mouseout", MOUSEMOVE:"mousemove", SELECTSTART:"selectstart", KEYPRESS:"keypress", KEYDOWN:"keydown", KEYUP:"keyup", BLUR:"blur", FOCUS:"focus", DEACTIVATE:"deactivate", FOCUSIN:goog.userAgent.IE ? "focusin" : "DOMFocusIn", FOCUSOUT:goog.userAgent.IE ? "focusout" : "DOMFocusOut", CHANGE:"change", SELECT:"select", SUBMIT:"submit", INPUT:"input", PROPERTYCHANGE:"propertychange",
+DRAGSTART:"dragstart", DRAGENTER:"dragenter", DRAGOVER:"dragover", DRAGLEAVE:"dragleave", DROP:"drop", TOUCHSTART:"touchstart", TOUCHMOVE:"touchmove", TOUCHEND:"touchend", TOUCHCANCEL:"touchcancel", CONTEXTMENU:"contextmenu", ERROR:"error", HELP:"help", LOAD:"load", LOSECAPTURE:"losecapture", READYSTATECHANGE:"readystatechange", RESIZE:"resize", SCROLL:"scroll", UNLOAD:"unload", HASHCHANGE:"hashchange", POPSTATE:"popstate"};goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
opt_e && this.init(opt_e, opt_currentTarget)
};
goog.inherits(goog.events.BrowserEvent, goog.events.Event);
goog.events.BrowserEvent.MouseButton = {LEFT:0, MIDDLE:1, RIGHT:2};
goog.events.BrowserEvent.IEButtonMap_ = [1, 4, 2];
@@ -3390,14 +3397,14 @@
}catch(err) {
relatedTarget = null
}
}
}else {
- if(type == "mouseover") {
+ if(type == goog.events.EventType.MOUSEOVER) {
relatedTarget = e.fromElement
}else {
- if(type == "mouseout") {
+ if(type == goog.events.EventType.MOUSEOUT) {
relatedTarget = e.toElement
}
}
}
this.relatedTarget = relatedTarget;
@@ -3413,10 +3420,11 @@
this.ctrlKey = e.ctrlKey;
this.altKey = e.altKey;
this.shiftKey = e.shiftKey;
this.metaKey = e.metaKey;
this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
+ this.state = e.state;
this.event_ = e;
delete this.returnValue_;
delete this.propagationStopped_
};
goog.events.BrowserEvent.prototype.stopPropagation = function() {
@@ -3853,18 +3861,16 @@
}
}
return false
};
goog.events.expose = function(e) {
- var str = [];
- for(var key in e) {
+ var str = [], key;
+ for(key in e) {
e[key] && e[key].id ? str.push(key + " = " + e[key] + " (" + e[key].id + ")") : str.push(key + " = " + e[key])
}
return str.join("\n")
};
-goog.events.EventType = {CLICK:"click", DBLCLICK:"dblclick", MOUSEDOWN:"mousedown", MOUSEUP:"mouseup", MOUSEOVER:"mouseover", MOUSEOUT:"mouseout", MOUSEMOVE:"mousemove", SELECTSTART:"selectstart", KEYPRESS:"keypress", KEYDOWN:"keydown", KEYUP:"keyup", BLUR:"blur", FOCUS:"focus", DEACTIVATE:"deactivate", FOCUSIN:goog.userAgent.IE ? "focusin" : "DOMFocusIn", FOCUSOUT:goog.userAgent.IE ? "focusout" : "DOMFocusOut", CHANGE:"change", SELECT:"select", SUBMIT:"submit", INPUT:"input", PROPERTYCHANGE:"propertychange",
-DRAGSTART:"dragstart", DRAGENTER:"dragenter", DRAGOVER:"dragover", DRAGLEAVE:"dragleave", DROP:"drop", CONTEXTMENU:"contextmenu", ERROR:"error", HELP:"help", LOAD:"load", LOSECAPTURE:"losecapture", READYSTATECHANGE:"readystatechange", RESIZE:"resize", SCROLL:"scroll", UNLOAD:"unload", HASHCHANGE:"hashchange", POPSTATE:"popstate"};
goog.events.getOnString_ = function(type) {
if(type in goog.events.onStringMap_) {
return goog.events.onStringMap_[type]
}
return goog.events.onStringMap_[type] = goog.events.onString_ + type
@@ -4173,12 +4179,12 @@
}
sb.push("]")
};
goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
sb.push("{");
- var sep = "";
- for(var key in obj) {
+ var sep = "", key;
+ for(key in obj) {
if(obj.hasOwnProperty(key)) {
var value = obj[key];
if(typeof value != "function") {
sb.push(sep);
this.serializeString_(key, sb);
@@ -4553,11 +4559,11 @@
this.dispatchEvent(goog.net.EventType.COMPLETE);
this.dispatchEvent(goog.net.EventType.ERROR)
}
};
goog.net.XhrIo.prototype.abort = function(opt_failureCode) {
- if(this.xhr_) {
+ if(this.xhr_ && this.active_) {
this.logger_.fine(this.formatMsg_("Aborting"));
this.active_ = false;
this.inAbort_ = true;
this.xhr_.abort();
this.inAbort_ = false;
@@ -4679,10 +4685,13 @@
}
};
goog.net.XhrIo.prototype.getResponseHeader = function(key) {
return this.xhr_ && this.isComplete() ? this.xhr_.getResponseHeader(key) : undefined
};
+goog.net.XhrIo.prototype.getAllResponseHeaders = function() {
+ return this.xhr_ && this.isComplete() ? this.xhr_.getAllResponseHeaders() : ""
+};
goog.net.XhrIo.prototype.formatMsg_ = function(msg) {
return msg + " [" + this.lastMethod_ + " " + this.lastUri_ + " " + this.getStatus() + "]"
};
goog.debug.entryPointRegistry.register(function(transformer) {
goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = transformer(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_)
Only in local/google_appengine/google/appengine/tools: https_wrapper.py
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/google/net/proto/RawMessage.py google-appengine-1.4.0-prerelease/google/net/proto/RawMessage.py
--- local/google_appengine/google/net/proto/RawMessage.py 2009-05-06 00:08:50.000000000 +0100
+++ google-appengine-1.4.0-prerelease/google/net/proto/RawMessage.py 2010-11-18 21:44:32.000000000 +0000
@@ -76,8 +76,8 @@ def __eq__(self, other):
return (other is not None) and (other.__class__ == self.__class__) and self.Equals(other)
def __ne__(self, other):
return not (self == other)
- def ByteSize(self):
+ def ByteSize(self, unused_assume_required_exists=True):
return len(self.__contents)
Only in google-appengine-1.4.0-prerelease/google/net: proto2
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/lib/fancy_urllib/fancy_urllib/__init__.py google-appengine-1.4.0-prerelease/lib/fancy_urllib/fancy_urllib/__init__.py
--- local/google_appengine/lib/fancy_urllib/fancy_urllib/__init__.py 2010-10-14 21:40:41.522837583 +0100
+++ google-appengine-1.4.0-prerelease/lib/fancy_urllib/fancy_urllib/__init__.py 2010-11-18 21:44:32.000000000 +0000
@@ -210,14 +210,10 @@ def __init__(self, *args, **kwargs):
self._ca_certs = None
def set_proxy(self, host, type):
saved_type = None
- # The following is necessary to handle redirects to https connections
- if self.get_type() == "http" and not self._tunnel_host:
- self._tunnel_host = self.get_host()
-
if self.get_type() == "https" and not self._tunnel_host:
self._tunnel_host = self.get_host()
saved_type = self.get_type()
urllib2.Request.set_proxy(self, host, type)
@@ -368,20 +364,35 @@ def redirect_request(self, req, *args,
new_req = urllib2.HTTPRedirectHandler.redirect_request(
self, req, *args, **kwargs)
# Same thing as in our set_proxy implementation, but in this case
# we"ve only got a Request to work with, so it was this or copy
# everything over piecemeal.
+ #
+ # Note that we do not persist tunneling behavior from an http request
+ # to an https request, because an http request does not set _tunnel_host.
+ #
+ # Also note that in Python < 2.6, you will get an error in
+ # FancyHTTPSHandler.do_open() on an https urllib2.Request that uses an http
+ # proxy, since the proxy type will be set to http instead of https.
+ # (FancyRequest, and urllib2.Request in Python >= 2.6 set the proxy type to
+ # https.) Such an urllib2.Request could result from this redirect
+ # if you are redirecting from an http request (since an an http request
+ # does not have _tunnel_host set, and thus you will not set the proxy
+ # in the code below), and if you have defined a proxy for https in, say,
+ # FancyProxyHandler, and that proxy has type http.
if hasattr(req, "_tunnel_host") and isinstance(new_req, urllib2.Request):
if new_req.get_type() == "https":
if req._tunnel_host:
# req is proxied, so copy the proxy info.
new_req._tunnel_host = new_req.get_host()
new_req.set_proxy(req.host, "https")
else:
# req is not proxied, so just make sure _tunnel_host is defined.
new_req._tunnel_host = None
new_req.type = "https"
+ if hasattr(req, "_key_file") and isinstance(new_req, urllib2.Request):
+ # Copy the auxiliary data in case this or any further redirect is https
new_req._key_file = req._key_file
new_req._cert_file = req._cert_file
new_req._ca_certs = req._ca_certs
return new_req
Only in local/google_appengine/lib/webob: LICENSE
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/RELEASE_NOTES google-appengine-1.4.0-prerelease/RELEASE_NOTES
--- local/google_appengine/RELEASE_NOTES 2010-10-14 21:40:44.054837793 +0100
+++ google-appengine-1.4.0-prerelease/RELEASE_NOTES 2010-11-18 21:44:32.000000000 +0000
@@ -1,10 +1,52 @@
Copyright 2008 Google Inc.
All rights reserved.
App Engine Python SDK - Release Notes
+Version 1.4.0
+================================
+- The Always On feature allows applications to pay and keep 3 instances of their
+ application always running, which can significantly reduce application
+ latency.
+- Developers can now enable Warmup Requests. By specifying a handler in an
+ app's app.yaml, App Engine will attempt to to send a Warmup Request to
+ initialize new instances before a user interacts with it. This can reduce the
+ latency an end-user sees for initializing your application.
+- The Channel API is now available for all users.
+- Task Queue has been officially released, and is no longer an experimental
+ feature. The API import paths that use 'labs' have been deprecated. Task queue
+ storage will count towards an application's overall storage quota, and will
+ thus be charged for.
+- The deadline for Task Queue and Cron requests has been raised to 10 minutes.
+ Datastore and API deadlines within those requests remain unchanged.
+- For the Task Queue, developers can specify task retry_parameters in their
+ queue.yaml.
+- Metadata Queries on the datastore for datastore kinds, namespaces, and entity
+ properties are available.
+- URLFetch allowed response size has been increased, up to 32 MB. Request size
+ is still limited to 1 MB.
+- The Admin Console Blacklist page lists the top blacklist rejected visitors.
+- The automatic image thumbnailing service supports arbitrary crop sizes up to
+ 1600px.
+- Overall average instance latency in the Admin Console is now a weighted
+ average over QPS per instance.
+- The developer who uploaded an app version can download that version's code
+ using the appcfg.py download_app command. This feature can be disabled on
+ a per application basis in the admin console, under the 'Permissions' tab.
+ Once disabled, code download for the application CANNOT be re-enabled.
+- Fixed an issue where custom Admin Console pages did not work for Google
+ Apps for your Domain users.
+- Allow Django initialization to be moved to appengine_config.py to avoid
+ Django version conflicts when mixing webapp.template with pure Django.
+ http://code.google.com/p/googleappengine/issues/detail?id=1758
+- Fixed an issue in the dev_appserver where get_serving_url did not work
+ for transparent, cropped PNGs:
+ http://code.google.com/p/googleappengine/issues/detail?id=3887
+- Fixed an issue with the DatastoreFileStub.
+ http://code.google.com/p/googleappengine/issues/detail?id=3895
+
Version 1.3.8
==================================
- Builtin app.yaml handlers are available for common application functions,
such as appstats.
http://code.google.com/appengine/docs/python/config/appconfig.html#Builtin_Handlers
diff -rbBd -u -U 5 -X /home/sd/.exclude -F '^ *def ' local/google_appengine/VERSION google-appengine-1.4.0-prerelease/VERSION
--- local/google_appengine/VERSION 2010-10-14 21:40:44.051837863 +0100
+++ google-appengine-1.4.0-prerelease/VERSION 2010-11-18 22:02:20.000000000 +0000
@@ -1,3 +1,3 @@
-release: "1.3.8"
-timestamp: 1284157741
+release: "prerelease-1.4.0"
+timestamp: 1287687253
api_versions: ['1']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment