reconcile korg/master into goog/master
diff --git a/buildspec.mk.default b/buildspec.mk.default
index 861bb0d..6fd93da 100644
--- a/buildspec.mk.default
+++ b/buildspec.mk.default
@@ -88,10 +88,16 @@
 #NO_FALLBACK_FONT:=true
 endif
 
-# To enabled instrumentation in webcore based apps like gmail and
+# To enable instrumentation in webcore based apps like gmail and
 # the browser, define WEBCORE_INSTRUMENTATION:=true
+ifndef WEBCORE_INSTRUMENTATION
 #WEBCORE_INSTRUMENTATION:=true
-#endif
+endif
+
+# To enable SVG in webcore define ENABLE_SVG:=true
+ifndef ENABLE_SVG
+#ENABLE_SVG:=true
+endif
 
 # when the build system changes such that this file must be updated, this
 # variable will be changed.  After you have modified this file with the new
diff --git a/cleanspec.mk b/cleanspec.mk
index 4cf3f59..58825dc 100644
--- a/cleanspec.mk
+++ b/cleanspec.mk
@@ -18,7 +18,7 @@
 # WHEN DOING SO, DELETE ANY "add-clean-step" ENTRIES THAT HAVE PILED UP.
 # **********************************************************************
 #
-INTERNAL_CLEAN_BUILD_VERSION := 2
+INTERNAL_CLEAN_BUILD_VERSION := 3
 #
 # ***********************************************************************
 # Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step!
@@ -31,7 +31,7 @@
 #
 # E.g.:
 #     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
-#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/external/zlib/)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
 #
 # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
 # files that are missing or have been moved.
@@ -54,35 +54,20 @@
 #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
 #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
-
-$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/etc/NOTICE.html)
-# Remove generated java files after CL 126153
-$(call add-clean-step, find $(OUT_DIR) -type f -name "*.java" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.google.android.datamessaging_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/pvasflocal.cfg)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sholes)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sapphire/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/led)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/mountd)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/mountd.conf)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Browser_intermediates)
-$(call add-clean-step, rm -f vendor/google/apps/Talk/res/drawable/%*)
-$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/android_os_NetStat.o)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libjni_andpyime_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/share)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIME_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.inputmethod.pinyin.lib_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/PinyinIMEGoogleService_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj)
-$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/tcpdump)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location)
-
-$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/lib?camera_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/STATIC_LIBRARIES/lib?camera_intermediates)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libwebcore_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/Silence.ogg)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/ringtones/notifications/Silence.ogg)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/passion/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/core/Makefile b/core/Makefile
index 107d0c1..307a217 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -118,6 +118,7 @@
 			PRODUCT_BRAND="$(PRODUCT_BRAND)" \
 			PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" \
 			PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" \
+			PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \
 			PRODUCT_MODEL="$(PRODUCT_MODEL)" \
 			PRODUCT_MANUFACTURER="$(PRODUCT_MANUFACTURER)" \
 			PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \
@@ -641,10 +642,28 @@
   SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
 endif
 
-$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ACP)
+# The system partition needs room for the recovery image as well.  We
+# now store the recovery image as a binary patch using the boot image
+# as the source (since they are very similar).  Generate the patch so
+# we can see how big it's going to be, and include that in the system
+# image size check calculation.
+ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
+intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)
+RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p
+$(RECOVERY_FROM_BOOT_PATCH): $(INSTALLED_RECOVERYIMAGE_TARGET) \
+                             $(INSTALLED_BOOTIMAGE_TARGET) \
+			     $(HOST_OUT_EXECUTABLES)/imgdiff \
+	                     $(HOST_OUT_EXECUTABLES)/bsdiff
+	@echo "Construct recovery from boot"
+	mkdir -p $(dir $@)
+	PATH=$(HOST_OUT_EXECUTABLES):$$PATH $(HOST_OUT_EXECUTABLES)/imgdiff $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@
+endif
+
+
+$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
 	@echo "Install system fs image: $@"
 	$(copy-file-to-target)
-	$(hide) $(call assert-max-file-size,$@ $(INSTALLED_RECOVERYIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_MAX_SIZE))
+	$(hide) $(call assert-max-file-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_MAX_SIZE))
 
 systemimage: $(INSTALLED_SYSTEMIMAGE)
 
@@ -788,6 +807,7 @@
 
 built_ota_tools := \
 	$(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
+	$(call intermediates-dir-for,EXECUTABLES,applypatch_static)/applypatch_static \
 	$(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \
 	$(call intermediates-dir-for,EXECUTABLES,updater)/updater
 $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
@@ -823,6 +843,9 @@
 ifdef BOARD_KERNEL_CMDLINE
 	$(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline
 endif
+ifdef BOARD_KERNEL_BASE
+	$(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/base
+endif
 	@# Components of the boot image
 	$(hide) mkdir -p $(zip_root)/BOOT
 	$(hide) $(call package_files-copy-root, \
@@ -837,11 +860,12 @@
 ifdef BOARD_KERNEL_CMDLINE
 	$(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
 endif
-ifdef INSTALLED_RADIOIMAGE_TARGET
-	@# The radio image
-	$(hide) mkdir -p $(zip_root)/RADIO
-	$(hide) $(ACP) $(INSTALLED_RADIOIMAGE_TARGET) $(zip_root)/RADIO/image
+ifdef BOARD_KERNEL_BASE
+	$(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base
 endif
+	$(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\
+	            mkdir -p $(zip_root)/RADIO; \
+	            $(ACP) $(t) $(zip_root)/RADIO/$(notdir $(t));)
 	@# Contents of the system image
 	$(hide) $(call package_files-copy-root, \
 		$(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
@@ -873,6 +897,7 @@
 
 ifneq ($(TARGET_SIMULATOR),true)
 ifneq ($(TARGET_PRODUCT),sdk)
+ifneq ($(TARGET_PRODUCT),generic)
 
 name := $(TARGET_PRODUCT)
 ifeq ($(TARGET_BUILD_TYPE),debug)
@@ -884,9 +909,24 @@
 
 $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
 
+ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
+# default to common dir for device vendor
+$(INTERNAL_OTA_PACKAGE_TARGET): extensions := $(TARGET_DEVICE_DIR)/../common
+else
+$(INTERNAL_OTA_PACKAGE_TARGET): extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
+endif
+
+ifeq ($(TARGET_OTA_SCRIPT_MODE),)
+# default to "auto"
+$(INTERNAL_OTA_PACKAGE_TARGET): scriptmode := auto
+else
+$(INTERNAL_OTA_PACKAGE_TARGET): scriptmode := $(TARGET_OTA_SCRIPT_MODE)
+endif
+
 $(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools
 	@echo "Package OTA: $@"
 	$(hide) ./build/tools/releasetools/ota_from_target_files \
+	   -s $(extensions) -m $(scriptmode) \
 	   -p $(HOST_OUT) \
            -k $(KEY_CERT_PAIR) \
            $(BUILT_TARGET_FILES_PACKAGE) $@
@@ -894,6 +934,7 @@
 .PHONY: otapackage
 otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
 
+endif    # TARGET_PRODUCT != generic
 endif    # TARGET_PRODUCT != sdk
 endif    # TARGET_SIMULATOR != true
 
@@ -1009,9 +1050,17 @@
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
+ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
+# default to common dir for device vendor
+$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_DEVICE_DIR)/../common
+else
+$(INTERNAL_UPDATE_PACKAGE_TARGET): extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
+endif
+
 $(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) otatools
 	@echo "Package: $@"
 	$(hide) ./build/tools/releasetools/img_from_target_files \
+	   -s $(extensions) \
 	   -p $(HOST_OUT) \
 	   $(BUILT_TARGET_FILES_PACKAGE) $@
 
diff --git a/core/base_rules.mk b/core/base_rules.mk
index c182a77..82c7dec 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -350,7 +350,6 @@
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_POST_PROCESS_COMMAND:= $(LOCAL_POST_PROCESS_COMMAND)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_AAPT_FLAGS:= $(LOCAL_AAPT_FLAGS)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_JAVA_LIBRARIES:= $(LOCAL_JAVA_LIBRARIES)
-#TODO: add this: $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ADDITIONAL_DEPENDENCIES:= $(LOCAL_ADDITIONAL_DEPENDENCIES)
 
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ALL_JAVA_LIBRARIES:= $(full_java_libs)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE)
@@ -392,6 +391,25 @@
 
 endif # !LOCAL_UNINSTALLABLE_MODULE
 
+
+###########################################################
+## CHECK_BUILD goals
+###########################################################
+
+# If nobody has defined a more specific module for the
+# checked modules, use LOCAL_BUILT_MODULE.  This was old
+# behavior, so it should be a safe default.
+ifndef LOCAL_CHECKED_MODULE
+  LOCAL_CHECKED_MODULE := $(LOCAL_BUILT_MODULE)
+endif
+
+# If they request that this module not be checked, then don't.
+# PLEASE DON'T SET THIS.  ANY PLACES THAT SET THIS WITHOUT
+# GOOD REASON WILL HAVE IT REMOVED.
+ifdef LOCAL_DONT_CHECK_MODULE
+  LOCAL_CHECKED_MODULE :=
+endif
+
 ###########################################################
 ## Register with ALL_MODULES
 ###########################################################
@@ -404,6 +422,8 @@
     $(ALL_MODULES.$(LOCAL_MODULE).PATH) $(LOCAL_PATH)
 ALL_MODULES.$(LOCAL_MODULE).TAGS := \
     $(ALL_MODULES.$(LOCAL_MODULE).TAGS) $(LOCAL_MODULE_TAGS)
+ALL_MODULES.$(LOCAL_MODULE).CHECKED := \
+    $(ALL_MODULES.$(LOCAL_MODULE).CHECKED) $(LOCAL_CHECKED_MODULE)
 ALL_MODULES.$(LOCAL_MODULE).BUILT := \
     $(ALL_MODULES.$(LOCAL_MODULE).BUILT) $(LOCAL_BUILT_MODULE)
 ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
@@ -430,9 +450,6 @@
 $(foreach tag,$(LOCAL_MODULE_TAGS),\
     $(eval ALL_MODULE_NAME_TAGS.$(tag) += $(LOCAL_MODULE)))
 
-# Always build everything, but only install a subset.
-ALL_BUILT_MODULES += $(LOCAL_BUILT_MODULE)
-
 ###########################################################
 ## NOTICE files
 ###########################################################
diff --git a/core/binary.mk b/core/binary.mk
index ddcdc6f..4413d47 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -87,7 +87,7 @@
 ifneq ($(strip $(yacc_cpps)),)
 $(yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \
 		$(TOPDIR)$(LOCAL_PATH)/%.y \
-		$(lex_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+		$(lex_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(call transform-y-to-cpp,$(PRIVATE_CPP_EXTENSION))
 $(yacc_headers): $(intermediates)/%.h: $(intermediates)/%$(LOCAL_CPP_EXTENSION)
 
@@ -115,7 +115,7 @@
 $(lex_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
 $(lex_objects): $(intermediates)/%.o: \
 		$(intermediates)/%$(LOCAL_CPP_EXTENSION) \
-		$(PRIVATE_ADDITIONAL_DEPENDENCIES) \
+		$(LOCAL_ADDITIONAL_DEPENDENCIES) \
 		$(yacc_headers)
 	$(transform-$(PRIVATE_HOST)cpp-to-o)
 endif
@@ -142,7 +142,7 @@
 ifneq ($(strip $(cpp_objects)),)
 $(cpp_objects): $(intermediates)/%.o: \
 		$(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \
-		$(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+		$(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)cpp-to-o)
 -include $(cpp_objects:%.o=%.P)
 endif
@@ -159,7 +159,7 @@
 # TODO: support compiling certain generated files as arm.
 $(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
 $(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
-$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)cpp-to-o)
 -include $(gen_cpp_objects:%.o=%.P)
 endif
@@ -172,7 +172,7 @@
 gen_S_objects := $(gen_S_sources:%.S=%.o)
 
 ifneq ($(strip $(gen_S_sources)),)
-$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o)
 -include $(gen_S_objects:%.o=%.P)
 endif
@@ -181,7 +181,7 @@
 gen_s_objects := $(gen_s_sources:%.s=%.o)
 
 ifneq ($(strip $(gen_s_objects)),)
-$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o-no-deps)
 -include $(gen_s_objects:%.o=%.P)
 endif
@@ -206,12 +206,29 @@
 c_objects        := $(c_arm_objects) $(c_normal_objects)
 
 ifneq ($(strip $(c_objects)),)
-$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)c-to-o)
 -include $(c_objects:%.o=%.P)
 endif
 
 ###########################################################
+## C: Compile generated .c files to .o.
+###########################################################
+
+gen_c_sources := $(filter %.c,$(LOCAL_GENERATED_SOURCES))
+gen_c_objects := $(gen_c_sources:%.c=%.o)
+
+ifneq ($(strip $(gen_c_objects)),)
+# Compile all generated files as thumb.
+# TODO: support compiling certain generated files as arm.
+$(gen_c_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(gen_c_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+$(gen_c_objects): $(intermediates)/%.o: $(intermediates)/%.c $(yacc_cpps) $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(transform-$(PRIVATE_HOST)c-to-o)
+-include $(gen_c_objects:%.o=%.P)
+endif
+
+###########################################################
 ## ObjC: Compile .m files to .o
 ###########################################################
 
@@ -232,7 +249,7 @@
 asm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o))
 
 ifneq ($(strip $(asm_objects_S)),)
-$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o)
 -include $(asm_objects_S:%.o=%.P)
 endif
@@ -241,7 +258,7 @@
 asm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o))
 
 ifneq ($(strip $(asm_objects_s)),)
-$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(LOCAL_ADDITIONAL_DEPENDENCIES)
 	$(transform-$(PRIVATE_HOST)s-to-o-no-deps)
 -include $(asm_objects_s:%.o=%.P)
 endif
@@ -261,6 +278,7 @@
 	$(gen_cpp_objects) \
 	$(gen_asm_objects) \
 	$(c_objects) \
+	$(gen_c_objects) \
 	$(yacc_objects) \
 	$(lex_objects) \
 	$(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))
diff --git a/core/build_id.mk b/core/build_id.mk
index 060c9b5..40bb35d 100644
--- a/core/build_id.mk
+++ b/core/build_id.mk
@@ -23,7 +23,7 @@
 # (like "TC1-RC5").  It must be a single word, and is
 # capitalized by convention.
 #
-BUILD_ID := Donut
+BUILD_ID := MASTER
 
 # DISPLAY_BUILD_NUMBER should only be set for development branches,
 # If set, the BUILD_NUMBER (cl) is appended to the BUILD_ID for
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index b2e95b4..031bc0b 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -5,6 +5,8 @@
 LOCAL_MODULE:=
 LOCAL_MODULE_PATH:=
 LOCAL_MODULE_STEM:=
+LOCAL_DONT_CHECK_MODULE:=
+LOCAL_CHECKED_MODULE:=
 LOCAL_BUILT_MODULE:=
 LOCAL_BUILT_MODULE_STEM:=
 OVERRIDE_BUILT_MODULE_PATH:=
diff --git a/core/combo/arch/arm/armv4t.mk b/core/combo/arch/arm/armv4t.mk
new file mode 100644
index 0000000..abc8fa2
--- /dev/null
+++ b/core/combo/arch/arm/armv4t.mk
@@ -0,0 +1,23 @@
+# Configuration for Linux on ARM.
+# Generating binaries for the ARMv4T architecture and higher
+#
+# Supporting armv4 (without thumb) does not make much sense since
+# it's mostly an obsoleted instruction set architecture (only available
+# in StrongArm and arm8). Supporting armv4 will require a lot of conditional
+# code in assembler source since the bx (branch and exchange) instruction is
+# not supported.
+#
+$(warning ARMv4t support is currently a work in progress. It does not work right now!)
+ARCH_ARM_HAVE_THUMB_SUPPORT := false
+ARCH_ARM_HAVE_THUMB_INTERWORKING := false
+ARCH_ARM_HAVE_64BIT_DATA := false
+ARCH_ARM_HAVE_HALFWORD_MULTIPLY := false
+ARCH_ARM_HAVE_CLZ := false
+ARCH_ARM_HAVE_FFS := false
+
+DEFAULT_TARGET_CPU := arm920t
+
+# Note: Hard coding the 'tune' value here is probably not ideal,
+# and a better solution should be found in the future.
+#
+arch_variant_cflags := -march=armv4t -mtune=arm920t -D__ARM_ARCH_4T__
diff --git a/core/combo/arch/arm/armv5te-vfp.mk b/core/combo/arch/arm/armv5te-vfp.mk
new file mode 100644
index 0000000..75299ac
--- /dev/null
+++ b/core/combo/arch/arm/armv5te-vfp.mk
@@ -0,0 +1,7 @@
+# At the moment, use the same settings than the one
+# for armv5te, since TARGET_ARCH_VARIANT := armv5te-vfp
+# will only be used to select an optimized VFP-capable assembly
+# interpreter loop for Dalvik.
+#
+include $(BUILD_COMBOS)/arch/arm/armv5te.mk
+
diff --git a/core/combo/arch/arm/armv5te.mk b/core/combo/arch/arm/armv5te.mk
new file mode 100644
index 0000000..29aada6
--- /dev/null
+++ b/core/combo/arch/arm/armv5te.mk
@@ -0,0 +1,21 @@
+# Configuration for Linux on ARM.
+# Generating binaries for the ARMv5TE architecture and higher
+#
+ARCH_ARM_HAVE_THUMB_SUPPORT     := true
+ARCH_ARM_HAVE_FAST_INTERWORKING := true
+ARCH_ARM_HAVE_64BIT_DATA        := true
+ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true
+ARCH_ARM_HAVE_CLZ               := true
+ARCH_ARM_HAVE_FFS               := true
+
+# Note: Hard coding the 'tune' value here is probably not ideal,
+# and a better solution should be found in the future.
+#
+arch_variant_cflags := \
+    -march=armv5te \
+    -mtune=xscale  \
+    -D__ARM_ARCH_5__ \
+    -D__ARM_ARCH_5T__ \
+    -D__ARM_ARCH_5E__ \
+    -D__ARM_ARCH_5TE__
+
diff --git a/core/combo/arch/arm/armv7-a.mk b/core/combo/arch/arm/armv7-a.mk
new file mode 100644
index 0000000..7a3ecc4
--- /dev/null
+++ b/core/combo/arch/arm/armv7-a.mk
@@ -0,0 +1,22 @@
+# Configuration for Linux on ARM.
+# Generating binaries for the ARMv7-a architecture and higher
+#
+ARCH_ARM_HAVE_THUMB_SUPPORT     := true
+ARCH_ARM_HAVE_FAST_INTERWORKING := true
+ARCH_ARM_HAVE_64BIT_DATA        := true
+ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true
+ARCH_ARM_HAVE_CLZ               := true
+ARCH_ARM_HAVE_FFS               := true
+ARCH_ARM_HAVE_VFP               := true
+ARCH_ARM_HAVE_NEON              := true
+
+# Note: Hard coding the 'tune' value here is probably not ideal,
+# and a better solution should be found in the future.
+#
+arch_variant_cflags := \
+    -march=armv7-a \
+    -mfloat-abi=softfp \
+    -mfpu=neon
+
+arch_variant_ldflags := \
+	-Wl,--fix-cortex-a8
\ No newline at end of file
diff --git a/core/combo/linux-arm.mk b/core/combo/linux-arm.mk
index 11a8ac7..6011351 100644
--- a/core/combo/linux-arm.mk
+++ b/core/combo/linux-arm.mk
@@ -1,68 +1,44 @@
 # Configuration for Linux on ARM.
 # Included by combo/select.make
 
-# You can set TARGET_ARCH_VERSION to use an arch version other
-# than ARMv5TE
-ifeq ($(strip $(TARGET_ARCH_VERSION)),)
-TARGET_ARCH_VERSION := armv5te
-endif
-
-# This set of if blocks sets makefile variables similar to preprocesser
+# You can set TARGET_ARCH_VARIANT to use an arch version other
+# than ARMv5TE. Each value should correspond to a file named
+# $(BUILD_COMBOS)/arch/<name>.mk which must contain
+# makefile variable definitions similar to the preprocessor
 # defines in system/core/include/arch/<combo>/AndroidConfig.h. Their
-# purpose is to allow module Android.mk files to selctively compile
-# different versions of code based upon the funtionality and 
+# purpose is to allow module Android.mk files to selectively compile
+# different versions of code based upon the funtionality and
 # instructions available in a given architecture version.
 #
-# The blocks also define specific arch_version_cflags, which 
+# The blocks also define specific arch_variant_cflags, which
 # include defines, and compiler settings for the given architecture
 # version.
 #
-# Note: Hard coding the 'tune' value here is probably not ideal,
-# and a better solution should be found in the future.
-#
-# With two or three different versions this if block approach is
-# fine. If/when this becomes large, please change this to include
-# architecture versions specific Makefiles which define these
-# variables.
-#
-# Supporting armv4 (without thumb) does not make much sense since
-# it's mostly an obsoleted instruction set architecture (only available
-# in StrongArm and arm8). Supporting armv4 will require a lot of conditional
-# code in assembler source since the bx (branch and exchange) instruction is
-# not supported.
-#
-ifeq ($(TARGET_ARCH_VERSION),armv5te)
-ARCH_ARM_HAVE_THUMB_SUPPORT := true
-ARCH_ARM_HAVE_FAST_INTERWORKING := true
-ARCH_ARM_HAVE_64BIT_DATA := true
-ARCH_ARM_HAVE_HALFWORD_MULTIPLY := true
-ARCH_ARM_HAVE_CLZ := true
-ARCH_ARM_HAVE_FFS := true
-
-arch_version_cflags := -march=armv5te -mtune=xscale  -D__ARM_ARCH_5__ \
-	-D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__
-else
-ifeq ($(TARGET_ARCH_VERSION),armv4t)
-$(warning ARMv4t support is currently a work in progress. It does not work right now!)
-ARCH_ARM_HAVE_THUMB_SUPPORT := false
-ARCH_ARM_HAVE_THUMB_INTERWORKING := false
-ARCH_ARM_HAVE_64BIT_DATA := false
-ARCH_ARM_HAVE_HALFWORD_MULTIPLY := false
-ARCH_ARM_HAVE_CLZ := false
-ARCH_ARM_HAVE_FFS := false
-
-DEFAULT_TARGET_CPU := arm920t
-
-arch_version_cflags := -march=armv4t -mtune=arm920t -D__ARM_ARCH_4T__
-else
-$(error Unknown ARM architecture version: $(TARGET_ARCH_VERSION))
+ifeq ($(strip $(TARGET_ARCH_VARIANT)),)
+TARGET_ARCH_VARIANT := armv5te
 endif
+
+# TARGET_ARCH_VARIANT used to be called TARGET_ARCH_VERSION
+# to avoid any weirdness, issue an error message if the latter
+# is defined.
+#
+ifneq ($(strip $(TARGET_ARCH_VERSION)),)
+$(info Definition for TARGET_ARCH_VERSION encountered !)
+$(info This variable has been renamed TARGET_ARCH_VARIANT, please update your build files !!)
+$(error Aborting the build.)
 endif
 
+TARGET_ARCH_SPECIFIC_MAKEFILE := $(BUILD_COMBOS)/arch/$(TARGET_ARCH)/$(TARGET_ARCH_VARIANT).mk
+ifeq ($(strip $(wildcard $(TARGET_ARCH_SPECIFIC_MAKEFILE))),)
+$(error Unknown ARM architecture version: $(TARGET_ARCH_VARIANT))
+endif
+
+include $(TARGET_ARCH_SPECIFIC_MAKEFILE)
+
 # You can set TARGET_TOOLS_PREFIX to get gcc from somewhere else
 ifeq ($(strip $($(combo_target)TOOLS_PREFIX)),)
 $(combo_target)TOOLS_PREFIX := \
-	prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin/arm-eabi-
+	prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.4.0/bin/arm-eabi-
 endif
 
 $(combo_target)CC := $($(combo_target)TOOLS_PREFIX)gcc$(HOST_EXECUTABLE_SUFFIX)
@@ -79,7 +55,7 @@
                         -funswitch-loops     \
                         -finline-limit=300
 
-# Modules can choose to compile some source as thumb. As 
+# Modules can choose to compile some source as thumb. As
 # non-thumb enabled targets are supported, this is treated
 # as a 'hint'. If thumb is not enabled, these files are just
 # compiled as ARM.
@@ -103,7 +79,7 @@
 # with -mlong-calls.  When built at -O0, those libraries are
 # too big for a thumb "BL <label>" to go from one end to the other.
 ifeq ($(FORCE_ARM_DEBUGGING),true)
-  TARGET_arm_CFLAGS += -fno-omit-frame-pointer
+  TARGET_arm_CFLAGS += -fno-omit-frame-pointer -fno-strict-aliasing
   TARGET_thumb_CFLAGS += -marm -fno-omit-frame-pointer
 endif
 
@@ -116,10 +92,13 @@
 			-funwind-tables \
 			-fstack-protector \
 			-fno-short-enums \
-			$(arch_version_cflags) \
+			$(arch_variant_cflags) \
 			-include $(android_config_h) \
 			-I $(arch_include_dir)
 
+$(combo_target)GLOBAL_LDFLAGS += \
+			$(arch_variant_ldflags)
+
 # We only need thumb interworking in cases where thumb support
 # is available in the architecture, and just to be sure, (and
 # since sometimes thumb-interwork appears to be default), we
@@ -150,7 +129,7 @@
 
 ## on some hosts, the target cross-compiler is not available so do not run this command
 ifneq ($(wildcard $($(combo_target)CC)),)
-# We compile with the global cflags to ensure that 
+# We compile with the global cflags to ensure that
 # any flags which affect libgcc are correctly taken
 # into account.
 $(combo_target)LIBGCC := $(shell $($(combo_target)CC) $($(combo_target)GLOBAL_CFLAGS) -print-libgcc-file-name)
@@ -201,6 +180,7 @@
 	$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
 	-o $@ \
 	$(PRIVATE_LDFLAGS) \
+	$(TARGET_GLOBAL_LDFLAGS) \
 	$(TARGET_LIBGCC)
 endef
 
@@ -217,6 +197,7 @@
 	$(PRIVATE_ALL_OBJECTS) \
 	$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
 	$(PRIVATE_LDFLAGS) \
+	$(TARGET_GLOBAL_LDFLAGS) \
 	$(TARGET_LIBGCC) \
 	$(TARGET_CRTEND_O)
 endef
@@ -228,6 +209,7 @@
 	$(TARGET_GLOBAL_LD_DIRS) \
 	$(TARGET_CRTBEGIN_STATIC_O) \
 	$(PRIVATE_LDFLAGS) \
+	$(TARGET_GLOBAL_LDFLAGS) \
 	$(PRIVATE_ALL_OBJECTS) \
 	$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
 	$(TARGET_LIBGCC) \
diff --git a/core/combo/linux-x86.mk b/core/combo/linux-x86.mk
index 372c63e..f466147 100644
--- a/core/combo/linux-x86.mk
+++ b/core/combo/linux-x86.mk
@@ -10,7 +10,7 @@
 ifeq ($(combo_target),HOST_)
 # $(1): The file to check
 define get-file-size
-stat --format "%s" "$(1)"
+stat --format "%s" "$(1)" | tr -d '\n'
 endef
 endif
 
diff --git a/core/combo/select.mk b/core/combo/select.mk
index 273b660..793b0a9 100644
--- a/core/combo/select.mk
+++ b/core/combo/select.mk
@@ -39,6 +39,7 @@
 # These flags might (will) be overridden by the target makefiles
 $(combo_target)GLOBAL_CFLAGS := -fno-exceptions -Wno-multichar
 $(combo_target)RELEASE_CFLAGS := -O2 -g -fno-strict-aliasing
+$(combo_target)GLOBAL_LDFLAGS :=
 $(combo_target)GLOBAL_ARFLAGS := crs
 
 $(combo_target)EXECUTABLE_SUFFIX := 
diff --git a/core/config.mk b/core/config.mk
index a5f3720..ec4c3ac 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -75,11 +75,11 @@
 # ###############################################################
 
 # These can be changed to modify both host and device modules.
-COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused
+COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith
 COMMON_RELEASE_CFLAGS:= -DNDEBUG -UDEBUG
 
-COMMON_GLOBAL_CPPFLAGS:=
-COMMON_RELEASE_CPPFLAGS:=
+COMMON_GLOBAL_CPPFLAGS:= $(COMMON_GLOBAL_CFLAGS) -Wsign-promo -Wctor-dtor-privacy 
+COMMON_RELEASE_CPPFLAGS:= $(COMMON_RELEASE_CFLAGS)
 
 # Set the extensions used for various packages
 COMMON_PACKAGE_SUFFIX := .zip
@@ -87,7 +87,13 @@
 COMMON_ANDROID_PACKAGE_SUFFIX := .apk
 
 # list of flags to turn specific warnings in to errors
-TARGET_ERROR_FLAGS := -Werror=return-type
+TARGET_ERROR_FLAGS := -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point
+
+# TODO: do symbol compression
+TARGET_COMPRESS_MODULE_SYMBOLS := false
+
+# Default is to prelink modules.
+TARGET_PRELINK_MODULE := true
 
 # ###############################################################
 # Include sub-configuration files
@@ -105,6 +111,32 @@
 # are specific to the user's build configuration.
 include $(BUILD_SYSTEM)/envsetup.mk
 
+# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
+# or under vendor/*/$(TARGET_DEVICE).  Search in both places, but
+# make sure only one exists.
+# Real boards should always be associated with an OEM vendor.
+board_config_mk := \
+	$(strip $(wildcard \
+		$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
+		vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
+	))
+ifeq ($(board_config_mk),)
+  $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
+endif
+ifneq ($(words $(board_config_mk)),1)
+  $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
+endif
+include $(board_config_mk)
+TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
+board_config_mk :=
+
+# Clean up/verify variables defined by the board config file.
+TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
+TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
+ifeq ($(TARGET_CPU_ABI),)
+  $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
+endif
+
 # $(1): os/arch
 define select-android-config-h
 system/core/include/arch/$(1)/AndroidConfig.h
@@ -259,10 +291,6 @@
 TARGET_GLOBAL_CFLAGS += $(TARGET_RELEASE_CFLAGS)
 TARGET_GLOBAL_CPPFLAGS += $(TARGET_RELEASE_CPPFLAGS)
 
-# TODO: do symbol compression
-TARGET_COMPRESS_MODULE_SYMBOLS := false
-TARGET_PRELINK_MODULE := true
-
 PREBUILT_IS_PRESENT := $(if $(wildcard prebuilt/Android.mk),true)
 
 
diff --git a/core/definitions.mk b/core/definitions.mk
index 485c2ae..b4e4b50 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -41,9 +41,6 @@
 # set of installed targets.
 ALL_DEFAULT_INSTALLED_MODULES:=
 
-# Full paths to all targets that will be built.
-ALL_BUILT_MODULES:=
-
 # The list of tags that have been defined by
 # LOCAL_MODULE_TAGS.  Each word in this variable maps
 # to a corresponding ALL_MODULE_TAGS.<tagname> variable
@@ -1239,21 +1236,21 @@
 $(hide) mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
 $(call unzip-jar-files,$(PRIVATE_STATIC_JAVA_LIBRARIES), \
     $(PRIVATE_CLASS_INTERMEDIATES_DIR))
-$(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_INTERMEDIATES_DIR)/java-source-list)
+$(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list)
 $(hide) if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
-	    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_INTERMEDIATES_DIR)/java-source-list; \
+	    find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list; \
 fi
-$(hide) tr ' ' '\n' < $(PRIVATE_INTERMEDIATES_DIR)/java-source-list \
-    | sort -u > $(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq
+$(hide) tr ' ' '\n' < $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list \
+    | sort -u > $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list-uniq
 $(hide) $(TARGET_JAVAC) -encoding ascii $(PRIVATE_BOOTCLASSPATH) \
     $(addprefix -classpath ,$(strip \
         $(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
     $(strip $(PRIVATE_JAVAC_DEBUG_FLAGS)) $(xlint_unchecked) \
     -extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
-    \@$(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq \
+    \@$(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list-uniq \
     || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
-$(hide) rm -f $(PRIVATE_INTERMEDIATES_DIR)/java-source-list
-$(hide) rm -f $(PRIVATE_INTERMEDIATES_DIR)/java-source-list-uniq
+$(hide) rm -f $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list
+$(hide) rm -f $(dir $(PRIVATE_CLASS_INTERMEDIATES_DIR))/java-source-list-uniq
 $(hide) mkdir -p $(dir $@)
 $(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
     $@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
@@ -1558,8 +1555,8 @@
 # next whole flash block size.
 define assert-max-file-size
 $(if $(2), \
-  size=$$(for i in $(1); do $(call get-file-size,$$i); done); \
-  total=$$(( $$( echo "$$size" | tr '\n' + ; echo 0 ) )); \
+  size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \
+  total=$$(( $$( echo "$$size" ) )); \
   printname=$$(echo -n "$(1)" | tr " " +); \
   echo "$$printname total size is $$total"; \
   img_blocksize=$(call image-size-from-data-size,$(BOARD_FLASH_BLOCK_SIZE)); \
@@ -1570,8 +1567,7 @@
   if [ "$$total" -gt "$$maxsize" ]; then \
     echo "error: $$printname too large ($$total > [$(2) - $$reserve])"; \
     false; \
-  fi; \
-  if [ "$$total" -gt $$((maxsize - 32768)) ]; then \
+  elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \
     echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \
   fi \
  , \
@@ -1579,6 +1575,25 @@
  )
 endef
 
+
+###########################################################
+## Define device-specific radio files
+###########################################################
+
+# Copy a radio image file to the output location, and add it to
+# INSTALLED_RADIOIMAGE_TARGET.
+# $(1): filename
+define add-radio-file
+  $(eval $(call add-radio-file-internal,$(1)))
+endef
+define add-radio-file-internal
+INSTALLED_RADIOIMAGE_TARGET += $$(PRODUCT_OUT)/$(1)
+ALL_PREBUILT += $$(PRODUCT_OUT)/$(1)
+$$(PRODUCT_OUT)/$(1) : $$(LOCAL_PATH)/$(1) | $$(ACP)
+	$$(transform-prebuilt-to-target)
+endef
+
+
 ###########################################################
 ## Other includes
 ###########################################################
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index 30bd918..03ffa55 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -127,7 +127,7 @@
 	$(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX) \
 	$(HOST_OUT_SHARED_LIBRARIES)/libclearsilver-jni$(HOST_JNILIB_SUFFIX)
 
-$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/apicheck$(COMMON_JAVA_PACKAGE_SUFFIX)
 $(full_target): PRIVATE_CURRENT_BUILD := -hdf page.build $(BUILD_ID)-$(BUILD_NUMBER)
 $(full_target): PRIVATE_CURRENT_TIME :=  -hdf page.now "$(shell date "+%d %b %Y %k:%M")"
 $(full_target): PRIVATE_TEMPLATE_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR)
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 31901e9..0ee7cfc 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -270,7 +270,7 @@
 	ABP:=$(ABP):$(TARGET_OUT_EXECUTABLES)
 else
 	# this should be copied to HOST_OUT_EXECUTABLES instead
-	ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin
+	ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.4.0/bin
 endif
 ANDROID_BUILD_PATHS := $(ABP)
 ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG)
@@ -335,5 +335,3 @@
 $(info   BUILD_ID=$(BUILD_ID))
 $(info ============================================)
 endif
-
-
diff --git a/core/java.mk b/core/java.mk
index 5a434c4..658b173 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -37,6 +37,54 @@
 $(error LOCAL_BUILT_MODULE_STEM may not be "$(LOCAL_BUILT_MODULE_STEM)")
 endif
 
+
+##############################################################################
+# Define the intermediate targets before including base_rules so they get
+# the correct environment.
+##############################################################################
+
+intermediates := $(call local-intermediates-dir)
+intermediates.COMMON := $(call local-intermediates-dir,COMMON)
+
+# This is cleared below, and re-set if we really need it.
+full_classes_jar := $(intermediates.COMMON)/classes.jar
+
+# Emma source code coverage
+ifneq ($(EMMA_INSTRUMENT),true)
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+endif
+
+# Choose leaf name for the compiled jar file.
+ifneq ($(LOCAL_NO_EMMA_COMPILE),true)
+full_classes_compiled_jar_leaf := classes-no-debug-var.jar
+else
+full_classes_compiled_jar_leaf := classes-full-debug.jar
+endif
+full_classes_compiled_jar := $(intermediates.COMMON)/$(full_classes_compiled_jar_leaf)
+
+emma_intermediates_dir := $(intermediates.COMMON)/emma_out
+# the 'lib/$(full_classes_compiled_jar_leaf)' portion of this path is fixed in
+# the emma tool
+full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_jar_leaf)
+full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
+full_classes_jarjar_jar := $(full_classes_jar)
+built_dex := $(intermediates.COMMON)/classes.dex
+
+LOCAL_INTERMEDIATE_TARGETS += \
+    $(full_classes_jar) \
+    $(full_classes_compiled_jar) \
+    $(full_classes_emma_jar) \
+    $(full_classes_stubs_jar) \
+    $(full_classes_jarjar_jar) \
+    $(built_dex)
+
+
+# TODO: It looks like the only thing we need from base_rules is
+# all_java_sources.  See if we can get that by adding a
+# common_java.mk, and moving the include of base_rules.mk to
+# after all the declarations.
+
 #######################################
 include $(BUILD_SYSTEM)/base_rules.mk
 #######################################
@@ -65,6 +113,7 @@
 # LOCAL_BUILT_MODULE, so it will inherit the necessary PRIVATE_*
 # variable definitions.
 full_classes_jar := $(intermediates.COMMON)/classes.jar
+built_dex := $(intermediates.COMMON)/classes.dex
 
 # Droiddoc isn't currently able to generate stubs for modules, so we're just
 # allowing it to use the classes.jar as the "stubs" that would be use to link
@@ -74,34 +123,28 @@
 #   it, so it's closest to what's on the device.
 # - This extra copy, with the dependency on LOCAL_BUILT_MODULE allows the
 #   PRIVATE_ vars to be preserved.
-full_classes_stubs_jar := $(intermediates.COMMON)/stubs.jar
 $(full_classes_stubs_jar): PRIVATE_SOURCE_FILE := $(full_classes_jar)
 $(full_classes_stubs_jar) : $(LOCAL_BUILT_MODULE) | $(ACP)
 	@echo Copying $(PRIVATE_SOURCE_FILE)
 	$(hide) $(ACP) -fp $(PRIVATE_SOURCE_FILE) $@
 ALL_MODULES.$(LOCAL_MODULE).STUBS := $(full_classes_stubs_jar)
 
-# Emma source code coverage
-ifneq ($(EMMA_INSTRUMENT),true) 
-LOCAL_NO_EMMA_INSTRUMENT := true
-LOCAL_NO_EMMA_COMPILE := true
-endif
-
-# Choose leaf name for the compiled jar file.
-ifneq ($(LOCAL_NO_EMMA_COMPILE),true) 
-full_classes_compiled_jar_leaf := classes-no-debug-var.jar
-else
-full_classes_compiled_jar_leaf := classes-full-debug.jar
-endif
-
 # Compile the java files to a .jar file.
 # This intentionally depends on java_sources, not all_java_sources.
 # Deps for generated source files must be handled separately,
 # via deps on the target that generates the sources.
-full_classes_compiled_jar := $(intermediates.COMMON)/$(full_classes_compiled_jar_leaf)
 $(full_classes_compiled_jar): $(java_sources) $(full_java_lib_deps)
 	$(transform-java-to-classes.jar)
 
+# All of the rules after full_classes_compiled_jar are very unlikely
+# to fail except for bugs in their respective tools.  If you would
+# like to run these rules, add the "all" modifier goal to the make
+# command line.
+# This overwrites the value defined in base_rules.mk.  That's a little
+# dirty.  It's preferable to set LOCAL_CHECKED_MODULE, but this has to
+# be done after the inclusion of base_rules.mk.
+ALL_MODULES.$(LOCAL_MODULE).CHECKED := $(full_classes_compiled_jar)
+
 ifneq ($(LOCAL_NO_EMMA_COMPILE),true) 
 # If you instrument class files that have local variable debug information in
 # them emma does not correctly maintain the local variable table.
@@ -115,11 +158,6 @@
 $(full_classes_compiled_jar): PRIVATE_JAVAC_DEBUG_FLAGS := -g
 endif
 
-emma_intermediates_dir := $(intermediates.COMMON)/emma_out
-# the 'lib/$(full_classes_compiled_jar_leaf)' portion of this path is fixed in 
-# the emma tool
-full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_jar_leaf)
-
 ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true)
 # Skip adding emma instrumentation to class files if this is a static library,
 # since it will be instrumented by the package that includes it
@@ -142,7 +180,6 @@
 
 # Run jarjar if necessary, otherwise just copy the file.  This is the last
 # part of this step, so the output of this command is full_classes_jar.
-full_classes_jarjar_jar := $(full_classes_jar)
 ifneq ($(strip $(LOCAL_JARJAR_RULES)),)
 $(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)
 $(full_classes_jarjar_jar): $(full_classes_emma_jar) | jarjar
@@ -154,9 +191,6 @@
 	$(hide) $(ACP) $< $@
 endif
 
-
-built_dex := $(intermediates.COMMON)/classes.dex
-
 # Override PRIVATE_INTERMEDIATES_DIR so that install-dex-debug
 # will work even when intermediates != intermediates.COMMON.
 $(built_dex): PRIVATE_INTERMEDIATES_DIR := $(intermediates.COMMON)
diff --git a/core/main.mk b/core/main.mk
index 56232ba..59d216f 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -126,7 +126,7 @@
 # These are the modifier targets that don't do anything themselves, but
 # change the behavior of the build.
 # (must be defined before including definitions.make)
-INTERNAL_MODIFIER_TARGETS := showcommands
+INTERNAL_MODIFIER_TARGETS := showcommands checkbuild
 
 # Bring in standard build system definitions.
 include $(BUILD_SYSTEM)/definitions.mk
@@ -245,6 +245,16 @@
 ADDITIONAL_BUILD_PROPERTIES += ro.config.sync=yes
 endif
 
+## precise GC ##
+
+ifneq ($(filter dalvik.gc.type-precise,$(PRODUCT_TAGS)),)
+  # Enabling type-precise GC results in larger optimized DEX files.  The
+  # additional storage requirements for ".odex" files can cause /system
+  # to overflow on some devices, so this is configured separately for
+  # each product.
+  ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.dexopt-flags=m=y
+endif
+
 # Install an apns-conf.xml file if one's not already being installed.
 ifeq (,$(filter %:system/etc/apns-conf.xml, $(PRODUCT_COPY_FILES)))
   PRODUCT_COPY_FILES += \
@@ -253,18 +263,10 @@
     $(warning implicitly installing apns-conf_sdk.xml)
   endif
 endif
-# Install a vold.conf file is one's not already being installed.
-ifeq (,$(filter %:system/etc/vold.conf, $(PRODUCT_COPY_FILES)))
-  PRODUCT_COPY_FILES += \
-	development/data/etc/vold.conf:system/etc/vold.conf
-  ifeq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),)
-    $(warning implicitly installing vold.conf)
-  endif
-endif
 # If we're on an eng or tests build, but not on the sdk, and we have
 # a better one, use that instead.
 ifneq ($(filter eng tests,$(TARGET_BUILD_VARIANT)),)
-  ifdef is_sdk_build
+  ifndef is_sdk_build
     apns_to_use := $(wildcard vendor/google/etc/apns-conf.xml)
     ifneq ($(strip $(apns_to_use)),)
       PRODUCT_COPY_FILES := \
@@ -300,11 +302,11 @@
 endif
 
 
-# If all they typed was make showcommands, we'll actually build
-# the default target.
-ifeq ($(MAKECMDGOALS),showcommands)
-.PHONY: showcommands
-showcommands: $(DEFAULT_GOAL)
+# If they only used the modifier goals (showcommands, checkbuild), we'll actually
+# build the default target.
+ifeq ($(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS)),)
+.PHONY: $(INTERNAL_MODIFIER_TARGETS)
+$(INTERNAL_MODIFIER_TARGETS): $(DEFAULT_GOAL)
 endif
 
 # These targets are going to delete stuff, don't bother including
@@ -398,8 +400,6 @@
 # TINY_ANDROID is a super-minimal build configuration, handy for board 
 # bringup and very low level debugging
 
-INTERNAL_DEFAULT_DOCS_TARGETS := 
-
 subdirs := \
 	bionic \
 	system/core \
@@ -418,7 +418,6 @@
 #
 # Typical build; include any Android.mk files we can find.
 #
-INTERNAL_DEFAULT_DOCS_TARGETS := offline-sdk-docs
 subdirs := $(TOP)
 
 FULL_BUILD := true
@@ -432,32 +431,6 @@
 subdir_makefiles += \
 	$(shell build/tools/findleaves.sh --prune="./out" $(subdirs) Android.mk)
 
-# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
-# or under vendor/*/$(TARGET_DEVICE).  Search in both places, but
-# make sure only one exists.
-# Real boards should always be associated with an OEM vendor.
-board_config_mk := \
-	$(strip $(wildcard \
-		$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
-		vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
-	))
-ifeq ($(board_config_mk),)
-  $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
-endif
-ifneq ($(words $(board_config_mk)),1)
-  $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
-endif
-include $(board_config_mk)
-TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
-board_config_mk :=
-
-# Clean up/verify variables defined by the board config file.
-TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
-TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
-ifeq ($(TARGET_CPU_ABI),)
-  $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
-endif
-
 #
 # Include all of the makefiles in the system
 #
@@ -473,7 +446,6 @@
 # would have been with a normal make.
 CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS),))
 FULL_BUILD :=
-INTERNAL_DEFAULT_DOCS_TARGETS :=
 # Stub out the notice targets, which probably aren't defined
 # when using ONE_SHOT_MAKEFILE.
 NOTICE-HOST-%: ;
@@ -538,7 +510,6 @@
 # poisons the rest of the tags and shouldn't appear
 # on any list.
 Default_MODULES := $(sort $(ALL_DEFAULT_INSTALLED_MODULES) \
-                          $(ALL_BUILT_MODULES) \
                           $(CUSTOM_MODULES))
 # TODO: Remove the 3 places in the tree that use
 # ALL_DEFAULT_INSTALLED_MODULES and get rid of it from this list.
@@ -604,7 +575,7 @@
 endif
 
 
-# config/Makefile contains extra stuff that we don't want to pollute this
+# build/core/Makefile contains extra stuff that we don't want to pollute this
 # top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES
 # contains everything that's built during the current make, but it also further
 # extends ALL_DEFAULT_INSTALLED_MODULES.
@@ -615,6 +586,20 @@
 
 endif # dont_bother
 
+# These are additional goals that we build, in order to make sure that there
+# is as little code as possible in the tree that doesn't build.
+modules_to_check := $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).CHECKED))
+
+# If you would like to build all goals, and not skip any intermediate
+# steps, you can pass the "all" modifier goal on the commandline.
+ifneq ($(filter all,$(MAKECMDGOALS)),)
+modules_to_check += $(foreach m,$(ALL_MODULES),$(ALL_MODULES.$(m).BUILT))
+endif
+
+# for easier debugging
+modules_to_check := $(sort $(modules_to_check))
+#$(error modules_to_check $(modules_to_check))
+
 # -------------------------------------------------------------------
 # This is used to to get the ordering right, you can also use these,
 # but they're considered undocumented, so don't complain if their
@@ -632,10 +617,16 @@
 
 # All the droid stuff, in directories
 .PHONY: files
-files: prebuilt $(modules_to_install) $(INSTALLED_ANDROID_INFO_TXT_TARGET)
+files: prebuilt \
+        $(modules_to_install) \
+        $(modules_to_check) \
+        $(INSTALLED_ANDROID_INFO_TXT_TARGET)
 
 # -------------------------------------------------------------------
 
+.PHONY: checkbuild
+checkbuild: $(modules_to_check)
+
 .PHONY: ramdisk
 ramdisk: $(INSTALLED_RAMDISK_TARGET)
 
@@ -662,7 +653,6 @@
 	$(INSTALLED_BOOTIMAGE_TARGET) \
 	$(INSTALLED_RECOVERYIMAGE_TARGET) \
 	$(INSTALLED_USERDATAIMAGE_TARGET) \
-	$(INTERNAL_DEFAULT_DOCS_TARGETS) \
 	$(INSTALLED_FILES_FILE)
 
 # The actual files built by the droidcore target changes depending
diff --git a/core/notice_files.mk b/core/notice_files.mk
index 24295c7..fd76d51 100644
--- a/core/notice_files.mk
+++ b/core/notice_files.mk
@@ -20,12 +20,19 @@
     # We can't use xxx_OUT_STATIC_LIBRARIES because it points into
     # device-obj or host-obj.
     module_installed_filename := \
-    	$(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
+        $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
   else
     ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
       # Stick the static java libraries with the regular java libraries.
+      module_leaf := $(notdir $(LOCAL_BUILT_MODULE))
+      # javalib.jar is the default name for the build module (and isn't meaningful)
+      # If that's what we have, substitute the module name instead.  These files
+      # aren't included on the device, so this name is synthetic anyway.
+      ifeq ($(module_leaf),javalib.jar)
+        module_leaf := $(LOCAL_MODULE).jar
+      endif
       module_installed_filename := \
-	  $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
+          $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(module_leaf)
     else
       $(error Cannot determine where to install NOTICE file for $(LOCAL_MODULE))
     endif # JAVA_LIBRARIES
diff --git a/core/pathmap.mk b/core/pathmap.mk
index e281b9d..992e3ef 100644
--- a/core/pathmap.mk
+++ b/core/pathmap.mk
@@ -28,8 +28,8 @@
 #
 pathmap_INCL := \
     bluedroid:system/bluetooth/bluedroid/include \
-    bluez-libs:external/bluez/libs/include \
-    bluez-utils:external/bluez/utils \
+    bluez:external/bluetooth/bluez \
+    glib:external/bluetooth/glib \
     bootloader:bootable/bootloader/legacy/include \
     corecg:external/skia/include/core \
     dbus:external/dbus \
diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map
index 6892885..bd86397 100644
--- a/core/prelink-linux-arm.map
+++ b/core/prelink-linux-arm.map
@@ -27,7 +27,7 @@
 liba2dp.so              0xAEE00000
 audio.so                0xAED00000
 input.so                0xAEC00000
-libhcid.so              0xAEB00000
+libbluetoothd.so        0xAEB00000
 libbluedroid.so         0xAEA00000
 libbluetooth.so         0xAE900000
 libdbus.so              0xAE800000
@@ -50,8 +50,8 @@
 
 # graphics
 libpixelflinger.so      0xACF00000
-libcorecg.so            0xACE00000
 libsurfaceflinger.so    0xACD00000
+libGLES_android.so      0xACC80000
 libagl.so               0xACC00000
 
 libGLESv1_CM.so         0xACB00000
@@ -60,15 +60,22 @@
 libOpenVGU_CM.so        0xAC800000
 libEGL.so               0xAC700000
 
+libacc.so               0xAC600000
+
 libexif.so              0xAC500000
 libui.so                0xAC400000
-libsgl.so               0xAC000000
+libskia.so              0xAC000000
+librs_jni.so            0xABF00000
+libRS.so                0xAB900000
+
 
 # audio
-libspeech.so            0xAB800000
+libaudiopolicy.so       0xAB880000
+libaudiopolicygeneric.so 0xAB800000
+libsoundpool.so         0xAB780000
 libaudio.so             0xAB700000
-libsonivox.so           0xAB600000
-libsoundpool.so         0xAB500000
+libspeech.so            0xAB600000
+libsonivox.so           0xAB500000
 libvorbisidec.so        0xAB400000
 libmedia_jni.so         0xAB300000
 libmediaplayerservice.so 0xAB280000
@@ -80,6 +87,7 @@
 libsqlite.so            0xAAC00000
 libexpat.so             0xAAB00000
 libwebcore.so           0xAA000000
+libbinder.so            0xA9D80000
 libutils.so             0xA9D00000
 libcameraservice.so     0xA9C80000
 libhardware.so          0xA9C70000
@@ -89,13 +97,29 @@
 libime.so               0xA9800000
 libgps.so               0xA9700000
 libcamera.so            0xA9680000
-libqcamera.so           0xA9400000
+liboemcamera.so         0xA9400000
 
-# pv opencore libraries
-libopencore_author.so   0xA7B00000
-libopencore_player.so   0xA7800000
-libopencore_common.so   0xA7300000
-libopencore_2way.so     0xA7000000
+# pv libraries
+libpvasf.so                    0xA7C26000
+libpvasfreg.so                 0xA7C00000
+libomx_sharedlibrary.so        0xA7BA0000
+libopencore_download.so        0xA7B40000
+libopencore_downloadreg.so     0xA7B00000
+libopencore_net_support.so     0xA7A00000
+libopencore_rtsp.so            0xA7900000
+libopencore_rtspreg.so         0xA7890000
+libopencore_author.so          0xA7800000
+libomx_aacdec_sharedlibrary.so 0xA7700000
+libomx_amrdec_sharedlibrary.so 0xA76A0000
+libomx_amrenc_sharedlibrary.so 0xA7680000
+libomx_avcdec_sharedlibrary.so 0xA7660000
+libomx_avcenc_sharedlibrary.so 0xA7610000
+libomx_m4vdec_sharedlibrary.so 0xA75C0000
+libomx_m4venc_sharedlibrary.so 0xA7590000
+libomx_mp3dec_sharedlibrary.so 0xA7450000
+libopencore_mp4local.so        0xA7400000
+libopencore_mp4localreg.so     0xA7300000
+libopencore_player.so          0xA7000000
 
 # opencore hardware support
 libmm-adspsvc.so              0xA6FFD000
@@ -134,3 +158,4 @@
 libtrace_test.so        0x9A300000
 libsrec_jni.so          0x9A200000
 libcerttool_jni.so      0x9A100000
+
diff --git a/core/product.mk b/core/product.mk
index adc81c3..a9a24d2 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -68,7 +68,8 @@
     PRODUCT_SDK_ADDON_NAME \
     PRODUCT_SDK_ADDON_COPY_FILES \
     PRODUCT_SDK_ADDON_COPY_MODULES \
-    PRODUCT_SDK_ADDON_DOC_MODULE
+    PRODUCT_SDK_ADDON_DOC_MODULE \
+    PRODUCT_DEFAULT_WIFI_CHANNELS
 
 define dump-product
 $(info ==== $(1) ====)\
diff --git a/core/product_config.mk b/core/product_config.mk
index 7cfa5f4..3237801 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -214,6 +214,9 @@
   PRODUCT_MANUFACTURER := unknown
 endif
 
+PRODUCT_DEFAULT_WIFI_CHANNELS := \
+	$(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEFAULT_WIFI_CHANNELS))
+
 # Which policy should this product use
 PRODUCT_POLICY := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_POLICY))
 
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
index eed9beb..103a738 100644
--- a/core/tasks/cts.mk
+++ b/core/tasks/cts.mk
@@ -58,6 +58,7 @@
 	CtsGraphicsTestCases \
 	CtsHardwareTestCases \
 	CtsLocationTestCases \
+	CtsMediaTestCases \
 	CtsOsTestCases \
 	CtsPermissionTestCases \
 	CtsProviderTestCases \
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index ca8487f..bbcbb51 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -41,7 +41,7 @@
   # which is the version that we reveal to the end user.
   # Update this value when the platform version changes (rather
   # than overriding it somewhere else).  Can be an arbitrary string.
-  PLATFORM_VERSION := Donut
+  PLATFORM_VERSION := Eclair
 endif
 
 ifeq "" "$(PLATFORM_SDK_VERSION)"
@@ -57,9 +57,9 @@
 endif
 
 ifeq "" "$(PLATFORM_VERSION_CODENAME)"
-  # If the build is not a final release build, then this is the current
-  # development code-name.  If this is a final release build, it is simply "REL".
-  PLATFORM_VERSION_CODENAME := Donut
+  # This is the current development code-name, if the build is not a final
+  # release build.  If this is a final release build, it is simply "REL".
+  PLATFORM_VERSION_CODENAME := Eclair
 endif
 
 ifeq "" "$(DEFAULT_APP_TARGET_SDK)"
diff --git a/envsetup.sh b/envsetup.sh
index 6325c3d..4ef6d77 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -102,7 +102,7 @@
     # and in with the new
     CODE_REVIEWS=
     prebuiltdir=$(getprebuilt)
-    export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.2.1/bin
+    export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.4.0/bin
     export ANDROID_TOOLCHAIN=$ANDROID_EABI_TOOLCHAIN
     export ANDROID_QTOOLS=$T/development/emulator/qtools
     export ANDROID_BUILD_PATHS=:$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_QTOOLS:$ANDROID_TOOLCHAIN:$ANDROID_EABI_TOOLCHAIN$CODE_REVIEWS
@@ -596,6 +596,8 @@
         # Find the closest Android.mk file.
         T=$(gettop)
         local M=$(findmakefile)
+        # Remove the path to top as the makefilepath needs to be relative
+        local M=`echo $M|sed 's:'$T'/::'`
         if [ ! "$T" ]; then
             echo "Couldn't locate the top of the tree.  Try setting TOP."
         elif [ ! "$M" ]; then
@@ -634,6 +636,7 @@
                     ARGS="$ARGS showcommands"
                 else
                     echo "No Android.mk in $DIR."
+                    return 1
                 fi
             fi
         done
diff --git a/target/board/Android.mk b/target/board/Android.mk
index 64e3a74..fc2f651 100644
--- a/target/board/Android.mk
+++ b/target/board/Android.mk
@@ -20,11 +20,8 @@
   INSTALLED_KERNEL_TARGET :=
 endif
 
-ifneq ($(strip $(TARGET_NO_RADIOIMAGE)),true)
-  INSTALLED_RADIOIMAGE_TARGET := $(PRODUCT_OUT)/radio.img
-else
-  INSTALLED_RADIOIMAGE_TARGET :=
-endif
+# Use the add-radio-file function to add values to this variable.
+INSTALLED_RADIOIMAGE_TARGET :=
 
 ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/AndroidBoard.mk))
   ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/Android.mk))
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index 6ec2de3..2b72d01 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -1,12 +1,11 @@
 # config.mk
-# 
+#
 # Product-specific compile-time definitions.
 #
 
 # The generic product target doesn't have any hardware-specific pieces.
 TARGET_NO_BOOTLOADER := true
 TARGET_NO_KERNEL := true
-TARGET_NO_RADIOIMAGE := true
 TARGET_CPU_ABI := armeabi
 HAVE_HTC_AUDIO_DRIVER := true
 BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/product/core.mk b/target/product/core.mk
index 204345e..79372a1 100644
--- a/target/product/core.mk
+++ b/target/product/core.mk
@@ -3,7 +3,8 @@
 PRODUCT_DEVICE :=
 PRODUCT_POLICY := android.policy_phone
 PRODUCT_PROPERTY_OVERRIDES := \
-    ro.config.notification_sound=F1_New_SMS.ogg
+    ro.config.notification_sound=F1_New_SMS.ogg \
+    ro.config.alarm_alert=Alarm_Classic.ogg
 
 PRODUCT_PACKAGES := \
     framework-res \
@@ -26,4 +27,3 @@
     PackageInstaller \
     WebSearchProvider \
     Bugreport
-
diff --git a/target/product/generic.mk b/target/product/generic.mk
index b9bc070..4189b0e 100644
--- a/target/product/generic.mk
+++ b/target/product/generic.mk
@@ -5,9 +5,11 @@
 PRODUCT_PACKAGES := \
     AlarmClock \
     AlarmProvider \
+    Calculator \
     Calendar \
     Camera \
     DrmProvider \
+    Email \
     LatinIME \
     Mms \
     Music \
diff --git a/target/product/generic_with_google.mk b/target/product/generic_with_google.mk
index dddbbb7..57a2b86 100644
--- a/target/product/generic_with_google.mk
+++ b/target/product/generic_with_google.mk
@@ -7,10 +7,12 @@
     GoogleContactsProvider \
     GoogleSubscribedFeedsProvider \
     com.google.android.gtalkservice \
+    com.google.android.datamessaging \
     com.google.android.maps
 
 PRODUCT_COPY_FILES := \
     vendor/google/frameworks/maps/com.google.android.maps.xml:system/etc/permissions/com.google.android.maps.xml \
+    vendor/google/frameworks/datamessaging/com.google.android.datamessaging.xml:system/etc/permissions/com.google.android.datamessaging.xml \
     vendor/google/apps/GTalkService/com.google.android.gtalkservice.xml:system/etc/permissions/com.google.android.gtalkservice.xml
 
 
diff --git a/target/product/min_dev.mk b/target/product/min_dev.mk
index 005af70..056c2b8 100644
--- a/target/product/min_dev.mk
+++ b/target/product/min_dev.mk
@@ -1,7 +1,8 @@
 
 PRODUCT_POLICY := android.policy_phone
 PRODUCT_PROPERTY_OVERRIDES := \
-    ro.config.notification_sound=F1_New_SMS.ogg
+    ro.config.notification_sound=F1_New_SMS.ogg \
+    ro.config.alarm_alert=Alarm_Classic.ogg
 PRODUCT_BRAND := generic
 PRODUCT_NAME := min_dev
 PRODUCT_DEVICE := generic
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
index 46be49f..e9881bf 100644
--- a/target/product/sdk.mk
+++ b/target/product/sdk.mk
@@ -25,6 +25,7 @@
 	libWnnZHCNDic \
 	libwnndict \
 	ApiDemos \
+	GestureBuilder \
 	SoftKeyboard
 
 PRODUCT_COPY_FILES := \
diff --git a/tools/apicheck/src/com/android/apicheck/ApiCheck.java b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
index f78117c..20a98ce 100644
--- a/tools/apicheck/src/com/android/apicheck/ApiCheck.java
+++ b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
@@ -20,7 +20,6 @@
 import org.xml.sax.helpers.*;
 import java.io.*;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Stack;
 
 public class ApiCheck {
@@ -83,62 +82,62 @@
                 }
             }
 
-            String xmlFileName = args.get(0);
-            String xmlFileNameNew = args.get(1);
-            XMLReader xmlreader = null;
-            try {
-                // parse the XML files into our data structures
-                xmlreader = XMLReaderFactory.createXMLReader();
-                ApiCheck acheck = new ApiCheck();
-                MakeHandler handler = acheck.new MakeHandler();
-                xmlreader.setContentHandler(handler);
-                xmlreader.setErrorHandler(handler);
-                FileReader filereader = new FileReader(xmlFileName);
-                xmlreader.parse(new InputSource(filereader));
-                FileReader filereaderNew = new FileReader(xmlFileNameNew);
-                xmlreader.parse(new InputSource(filereaderNew));
+            ApiCheck acheck = new ApiCheck();
 
-                // establish the superclass relationships
-                handler.getOldApi().resolveSuperclasses();
-                handler.getNewApi().resolveSuperclasses();
-                
-                // finally, run the consistency check
-                handler.getOldApi().isConsistent(handler.getNewApi());
+            ApiInfo oldApi = acheck.parseApi(args.get(0));
+            ApiInfo newApi = acheck.parseApi(args.get(1));
 
-            } catch (SAXParseException e) {
-                Errors.error(Errors.PARSE_ERROR,
-                        new SourcePositionInfo(xmlFileName, e.getLineNumber(), 0),
-                        e.getMessage());
-            } catch (Exception e) {
-                e.printStackTrace();
-                Errors.error(Errors.PARSE_ERROR,
-                        new SourcePositionInfo(xmlFileName, 0, 0),
-                        e.getMessage());
-            } 
+            // only run the consistency check if we haven't had XML parse errors
+            if (!Errors.hadError) {
+                oldApi.isConsistent(newApi);
+            }
 
             Errors.printErrors();
             System.exit(Errors.hadError ? 1 : 0);
         }
 
-        private class MakeHandler extends DefaultHandler {
+    public ApiInfo parseApi(String xmlFile) {
+        FileReader fileReader = null;
+        try {
+            XMLReader xmlreader = XMLReaderFactory.createXMLReader();
+            MakeHandler handler = new MakeHandler();
+            xmlreader.setContentHandler(handler);
+            xmlreader.setErrorHandler(handler);
+            fileReader = new FileReader(xmlFile);
+            xmlreader.parse(new InputSource(fileReader));
+            ApiInfo apiInfo = handler.getApi();
+            apiInfo.resolveSuperclasses();
+            return apiInfo;
+        } catch (SAXParseException e) {
+            Errors.error(Errors.PARSE_ERROR,
+                    new SourcePositionInfo(xmlFile, e.getLineNumber(), 0),
+                    e.getMessage());
+        } catch (Exception e) {
+            e.printStackTrace();
+            Errors.error(Errors.PARSE_ERROR,
+                    new SourcePositionInfo(xmlFile, 0, 0), e.getMessage());
+        } finally {
+            if (fileReader != null) {
+                try {
+                    fileReader.close();
+                } catch (IOException ignored) {}
+            }
+        }
+        return null;
+    }
+
+    private static class MakeHandler extends DefaultHandler {
             
-            private Integer mWarningCount;
-            private ApiInfo mOriginalApi;
-            private ApiInfo mNewApi;
-            private boolean mOldApi;
+            private ApiInfo mApi;
             private PackageInfo mCurrentPackage;
             private ClassInfo mCurrentClass;
             private AbstractMethodInfo mCurrentMethod;
-            private ConstructorInfo mCurrentConstructor;
             private Stack<ClassInfo> mClassScope = new Stack<ClassInfo>();
-            
-            
+
+
             public MakeHandler() {
                 super();
-                mOriginalApi = new ApiInfo();
-                mNewApi = new ApiInfo();
-                mOldApi = true;
-                
+                mApi = new ApiInfo();
             }
             
             public void startElement(String uri, String localName, String qName, 
@@ -229,25 +228,11 @@
                     mCurrentPackage.addClass(mCurrentClass);
                     mCurrentClass = mClassScope.pop();
                 } else if (qName.equals("package")){
-                    if (mOldApi) {
-                        mOriginalApi.addPackage(mCurrentPackage);
-                    } else {
-                        mNewApi.addPackage(mCurrentPackage);
-                    }
+                    mApi.addPackage(mCurrentPackage);
                 }
             }
-            public void endDocument() {
-                mOldApi = !mOldApi;
+            public ApiInfo getApi() {
+                return mApi;
             }
-            
-            public ApiInfo getOldApi() {
-                return mOriginalApi;
-            }
-            
-            public ApiInfo getNewApi() {
-                return mNewApi;
-            }
-
-
-            }
+        }
 }
diff --git a/tools/apicheck/src/com/android/apicheck/ClassInfo.java b/tools/apicheck/src/com/android/apicheck/ClassInfo.java
index 4bbf78b..5405ad2 100644
--- a/tools/apicheck/src/com/android/apicheck/ClassInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/ClassInfo.java
@@ -187,8 +187,8 @@
         }
         
         for (FieldInfo mInfo : mFields.values()) {
-          if (cl.mFields.containsKey(mInfo.qualifiedName())) {
-              if (!mInfo.isConsistent(cl.mFields.get(mInfo.qualifiedName()))) {
+          if (cl.mFields.containsKey(mInfo.name())) {
+              if (!mInfo.isConsistent(cl.mFields.get(mInfo.name()))) {
                   consistent = false;
               }
           } else {
@@ -267,7 +267,7 @@
     }
     
     public void addField(FieldInfo fInfo) {
-        mFields.put(fInfo.qualifiedName(), fInfo);
+        mFields.put(fInfo.name(), fInfo);
       
     }
     
@@ -279,4 +279,26 @@
         return mExistsInBoth;
     }
 
+    public Map<String, ConstructorInfo> allConstructors() {
+        return mConstructors;
+    }
+
+    public Map<String, FieldInfo> allFields() {
+        return mFields;
+    }
+
+    public Map<String, MethodInfo> allMethods() {
+        return mMethods;
+    }
+
+    /**
+     * Returns the class hierarchy for this class, starting with this class.
+     */
+    public Iterable<ClassInfo> hierarchy() {
+        List<ClassInfo> result = new ArrayList<ClassInfo>(4);
+        for (ClassInfo c  = this; c != null; c = c.mSuperClass) {
+            result.add(c);
+        }
+        return result;
+    }
 }
diff --git a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
index 57d7617..f36c7cd 100644
--- a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
@@ -55,11 +55,12 @@
     }
     
     public String getHashableName() {
-      String returnString = qualifiedName();
+      StringBuilder result = new StringBuilder();
+      result.append(name());
       for (ParameterInfo pInfo : mParameters) {
-          returnString += ":" + pInfo.getType();
+          result.append(":").append(pInfo.getType());
       }
-      return returnString;
+      return result.toString();
     }
     
     public boolean isInBoth() {
diff --git a/tools/apicheck/src/com/android/apicheck/MethodInfo.java b/tools/apicheck/src/com/android/apicheck/MethodInfo.java
index 86e20de..e4e4537 100644
--- a/tools/apicheck/src/com/android/apicheck/MethodInfo.java
+++ b/tools/apicheck/src/com/android/apicheck/MethodInfo.java
@@ -195,7 +195,7 @@
     }
     
     public String getHashableName() {
-        return qualifiedName() + getParameterHash();
+        return name() + getParameterHash();
     }
     
     public String getSignature() {
diff --git a/tools/applypatch/Android.mk b/tools/applypatch/Android.mk
index fe317ff..5796cef 100644
--- a/tools/applypatch/Android.mk
+++ b/tools/applypatch/Android.mk
@@ -17,7 +17,7 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c
+LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c utils.c
 LOCAL_MODULE := libapplypatch
 LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
@@ -29,17 +29,25 @@
 
 LOCAL_SRC_FILES := main.c
 LOCAL_MODULE := applypatch
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_TAGS := eng
-LOCAL_STATIC_LIBRARIES += libapplypatch
-LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
-LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := imgdiff.c
+LOCAL_SRC_FILES := main.c
+LOCAL_MODULE := applypatch_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := imgdiff.c utils.c
 LOCAL_MODULE := imgdiff
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := eng
diff --git a/tools/applypatch/applypatch.c b/tools/applypatch/applypatch.c
index 185d3de..394c584 100644
--- a/tools/applypatch/applypatch.c
+++ b/tools/applypatch/applypatch.c
@@ -282,9 +282,10 @@
   return 0;
 }
 
-// Copy the contents of source_file to target_mtd partition, a string
-// of the form "MTD:<partition>[:...]".  Return 0 on success.
-int CopyToMTDPartition(const char* source_file, const char* target_mtd) {
+// Write a memory buffer to target_mtd partition, a string of the form
+// "MTD:<partition>[:...]".  Return 0 on success.
+int WriteToMTDPartition(unsigned char* data, size_t len,
+                        const char* target_mtd) {
   char* partition = strchr(target_mtd, ':');
   if (partition == NULL) {
     fprintf(stderr, "bad MTD target name \"%s\"\n", target_mtd);
@@ -298,13 +299,6 @@
   if (end != NULL)
     *end = '\0';
 
-  FILE* f = fopen(source_file, "rb");
-  if (f == NULL) {
-    fprintf(stderr, "failed to open %s for reading: %s\n",
-            source_file, strerror(errno));
-    return -1;
-  }
-
   if (!mtd_partitions_scanned) {
     mtd_scan_partitions();
     mtd_partitions_scanned = 1;
@@ -323,20 +317,14 @@
     return -1;
   }
 
-  const int buffer_size = 4096;
-  char buffer[buffer_size];
-  size_t read;
-  while ((read = fread(buffer, 1, buffer_size, f)) > 0) {
-    size_t written = mtd_write_data(ctx, buffer, read);
-    if (written != read) {
-      fprintf(stderr, "only wrote %d of %d bytes to MTD %s\n",
-              written, read, partition);
-      mtd_write_close(ctx);
-      return -1;
-    }
+  size_t written = mtd_write_data(ctx, (char*)data, len);
+  if (written != len) {
+    fprintf(stderr, "only wrote %d of %d bytes to MTD %s\n",
+            written, len, partition);
+    mtd_write_close(ctx);
+    return -1;
   }
 
-  fclose(f);
   if (mtd_erase_blocks(ctx, -1) < 0) {
     fprintf(stderr, "error finishing mtd write of %s\n", partition);
     mtd_write_close(ctx);
@@ -476,6 +464,26 @@
   return 0;
 }
 
+size_t FileSink(unsigned char* data, size_t len, void* token) {
+  return fwrite(data, 1, len, (FILE*)token);
+}
+
+typedef struct {
+  unsigned char* buffer;
+  size_t size;
+  size_t pos;
+} MemorySinkInfo;
+
+size_t MemorySink(unsigned char* data, size_t len, void* token) {
+  MemorySinkInfo* msi = (MemorySinkInfo*)token;
+  if (msi->size - msi->pos < len) {
+    return -1;
+  }
+  memcpy(msi->buffer + msi->pos, data, len);
+  msi->pos += len;
+  return len;
+}
+
 // Return the amount of free space (in bytes) on the filesystem
 // containing filename.  filename must exist.  Return -1 on error.
 size_t FreeSpaceForFile(const char* filename) {
@@ -720,19 +728,36 @@
   }
 
   char* outname = NULL;
+  FILE* output = NULL;
+  MemorySinkInfo msi;
+  SinkFn sink = NULL;
+  void* token = NULL;
   if (strncmp(target_filename, "MTD:", 4) == 0) {
-    outname = MTD_TARGET_TEMP_FILE;
+    // We store the decoded output in memory.
+    msi.buffer = malloc(target_size);
+    if (msi.buffer == NULL) {
+      fprintf(stderr, "failed to alloc %ld bytes for output\n",
+              (long)target_size);
+      return 1;
+    }
+    msi.pos = 0;
+    msi.size = target_size;
+    sink = MemorySink;
+    token = &msi;
   } else {
     // We write the decoded output to "<tgt-file>.patch".
     outname = (char*)malloc(strlen(target_filename) + 10);
     strcpy(outname, target_filename);
     strcat(outname, ".patch");
-  }
-  FILE* output = fopen(outname, "wb");
-  if (output == NULL) {
-    fprintf(stderr, "failed to open output file %s: %s\n",
-            outname, strerror(errno));
-    return 1;
+
+    output = fopen(outname, "wb");
+    if (output == NULL) {
+      fprintf(stderr, "failed to open output file %s: %s\n",
+              outname, strerror(errno));
+      return 1;
+    }
+    sink = FileSink;
+    token = output;
   }
 
 #define MAX_HEADER_LENGTH 8
@@ -759,27 +784,30 @@
   } else if (header_bytes_read >= 8 &&
              memcmp(header, "BSDIFF40", 8) == 0) {
     int result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
-                                  patch_filename, 0, output, &ctx);
+                                  patch_filename, 0, sink, token, &ctx);
     if (result != 0) {
       fprintf(stderr, "ApplyBSDiffPatch failed\n");
       return result;
     }
   } else if (header_bytes_read >= 8 &&
-             memcmp(header, "IMGDIFF1", 8) == 0) {
+             memcmp(header, "IMGDIFF", 7) == 0 &&
+             (header[7] == '1' || header[7] == '2')) {
     int result = ApplyImagePatch(source_to_use->data, source_to_use->size,
-                                 patch_filename, output, &ctx);
+                                 patch_filename, sink, token, &ctx);
     if (result != 0) {
       fprintf(stderr, "ApplyImagePatch failed\n");
       return result;
     }
   } else {
-    fprintf(stderr, "Unknown patch file format");
+    fprintf(stderr, "Unknown patch file format\n");
     return 1;
   }
 
-  fflush(output);
-  fsync(fileno(output));
-  fclose(output);
+  if (output != NULL) {
+    fflush(output);
+    fsync(fileno(output));
+    fclose(output);
+  }
 
   const uint8_t* current_target_sha1 = SHA_final(&ctx);
   if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
@@ -787,13 +815,13 @@
     return 1;
   }
 
-  if (strcmp(outname, MTD_TARGET_TEMP_FILE) == 0) {
+  if (output == NULL) {
     // Copy the temp file to the MTD partition.
-    if (CopyToMTDPartition(outname, target_filename) != 0) {
-      fprintf(stderr, "copy of %s to %s failed\n", outname, target_filename);
+    if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
+      fprintf(stderr, "write of patched data to %s failed\n", target_filename);
       return 1;
     }
-    unlink(outname);
+    free(msi.buffer);
   } else {
     // Give the .patch file the same owner, group, and mode of the
     // original source file.
diff --git a/tools/applypatch/applypatch.h b/tools/applypatch/applypatch.h
index ccd8424..3701087 100644
--- a/tools/applypatch/applypatch.h
+++ b/tools/applypatch/applypatch.h
@@ -39,10 +39,7 @@
 // and use it as the source instead.
 #define CACHE_TEMP_SOURCE "/cache/saved.file"
 
-// When writing to an MTD partition, we first put the output in this
-// temp file, then copy it to the partition once the patching is
-// finished (and the target sha1 verified).
-#define MTD_TARGET_TEMP_FILE "/tmp/mtd-temp"
+typedef size_t (*SinkFn)(unsigned char*, size_t, void*);
 
 // applypatch.c
 size_t FreeSpaceForFile(const char* filename);
@@ -52,15 +49,15 @@
 void ShowBSDiffLicense();
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                      const char* patch_filename, ssize_t offset,
-                     FILE* output, SHA_CTX* ctx);
+                     SinkFn sink, void* token, SHA_CTX* ctx);
 int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                         const char* patch_filename, ssize_t patch_offset,
                         unsigned char** new_data, ssize_t* new_size);
 
 // imgpatch.c
 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
-                     const char* patch_filename,
-                     FILE* output, SHA_CTX* ctx);
+                    const char* patch_filename,
+                    SinkFn sink, void* token, SHA_CTX* ctx);
 
 // freecache.c
 int MakeFreeSpaceOnCache(size_t bytes_needed);
diff --git a/tools/applypatch/bsdiff.c b/tools/applypatch/bsdiff.c
index 9d55f3b..d5cd617 100644
--- a/tools/applypatch/bsdiff.c
+++ b/tools/applypatch/bsdiff.c
@@ -84,7 +84,7 @@
 
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                      const char* patch_filename, ssize_t patch_offset,
-                     FILE* output, SHA_CTX* ctx) {
+                     SinkFn sink, void* token, SHA_CTX* ctx) {
 
   unsigned char* new_data;
   ssize_t new_size;
@@ -93,7 +93,7 @@
     return -1;
   }
 
-  if (fwrite(new_data, 1, new_size, output) < new_size) {
+  if (sink(new_data, new_size, token) < new_size) {
     fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
     return 1;
   }
diff --git a/tools/applypatch/imgdiff.c b/tools/applypatch/imgdiff.c
index f0b5fea..51835b4 100644
--- a/tools/applypatch/imgdiff.c
+++ b/tools/applypatch/imgdiff.c
@@ -64,11 +64,15 @@
  *    "IMGDIFF1"                  (8)   [magic number and version]
  *    chunk count                 (4)
  *    for each chunk:
- *        chunk type              (4)   [CHUNK_NORMAL or CHUNK_GZIP]
- *        source start            (8)
- *        source len              (8)
- *        bsdiff patch offset     (8)   [from start of patch file]
- *        if chunk type == CHUNK_GZIP:
+ *        chunk type              (4)   [CHUNK_{NORMAL, GZIP, DEFLATE, RAW}]
+ *        if chunk type == CHUNK_NORMAL:
+ *           source start         (8)
+ *           source len           (8)
+ *           bsdiff patch offset  (8)   [from start of patch file]
+ *        if chunk type == CHUNK_GZIP:      (version 1 only)
+ *           source start         (8)
+ *           source len           (8)
+ *           bsdiff patch offset  (8)   [from start of patch file]
  *           source expanded len  (8)   [size of uncompressed source]
  *           target expected len  (8)   [size of uncompressed target]
  *           gzip level           (4)
@@ -79,6 +83,20 @@
  *           gzip header len      (4)
  *           gzip header          (gzip header len)
  *           gzip footer          (8)
+ *        if chunk type == CHUNK_DEFLATE:   (version 2 only)
+ *           source start         (8)
+ *           source len           (8)
+ *           bsdiff patch offset  (8)   [from start of patch file]
+ *           source expanded len  (8)   [size of uncompressed source]
+ *           target expected len  (8)   [size of uncompressed target]
+ *           gzip level           (4)
+ *                method          (4)
+ *                windowBits      (4)
+ *                memLevel        (4)
+ *                strategy        (4)
+ *        if chunk type == RAW:             (version 2 only)
+ *           target len           (4)
+ *           data                 (target len)
  *
  * All integers are little-endian.  "source start" and "source len"
  * specify the section of the input image that comprises this chunk,
@@ -104,29 +122,230 @@
 
 #include "zlib.h"
 #include "imgdiff.h"
+#include "utils.h"
 
 typedef struct {
-  int type;             // CHUNK_NORMAL or CHUNK_GZIP
+  int type;             // CHUNK_NORMAL, CHUNK_DEFLATE
   size_t start;         // offset of chunk in original image file
 
   size_t len;
-  unsigned char* data;  // data to be patched (ie, uncompressed, for
-                        // gzip chunks)
+  unsigned char* data;  // data to be patched (uncompressed, for deflate chunks)
 
-  // everything else is for CHUNK_GZIP chunks only:
+  size_t source_start;
+  size_t source_len;
 
-  size_t gzip_header_len;
-  unsigned char* gzip_header;
-  unsigned char* gzip_footer;
+  // --- for CHUNK_DEFLATE chunks only: ---
 
-  // original (compressed) gzip data, including header and footer
-  size_t gzip_len;
-  unsigned char* gzip_data;
+  // original (compressed) deflate data
+  size_t deflate_len;
+  unsigned char* deflate_data;
+
+  char* filename;       // used for zip entries
 
   // deflate encoder parameters
   int level, method, windowBits, memLevel, strategy;
+
+  size_t source_uncompressed_len;
 } ImageChunk;
 
+typedef struct {
+  int data_offset;
+  int deflate_len;
+  int uncomp_len;
+  char* filename;
+} ZipFileEntry;
+
+static int fileentry_compare(const void* a, const void* b) {
+  int ao = ((ZipFileEntry*)a)->data_offset;
+  int bo = ((ZipFileEntry*)b)->data_offset;
+  if (ao < bo) {
+    return -1;
+  } else if (ao > bo) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+unsigned char* ReadZip(const char* filename,
+                       int* num_chunks, ImageChunk** chunks,
+                       int include_pseudo_chunk) {
+  struct stat st;
+  if (stat(filename, &st) != 0) {
+    fprintf(stderr, "failed to stat \"%s\": %s\n", filename, strerror(errno));
+    return NULL;
+  }
+
+  unsigned char* img = malloc(st.st_size);
+  FILE* f = fopen(filename, "rb");
+  if (fread(img, 1, st.st_size, f) != st.st_size) {
+    fprintf(stderr, "failed to read \"%s\" %s\n", filename, strerror(errno));
+    fclose(f);
+    return NULL;
+  }
+  fclose(f);
+
+  // look for the end-of-central-directory record.
+
+  int i;
+  for (i = st.st_size-20; i >= 0 && i > st.st_size - 65600; --i) {
+    if (img[i] == 0x50 && img[i+1] == 0x4b &&
+        img[i+2] == 0x05 && img[i+3] == 0x06) {
+      break;
+    }
+  }
+  // double-check: this archive consists of a single "disk"
+  if (!(img[i+4] == 0 && img[i+5] == 0 && img[i+6] == 0 && img[i+7] == 0)) {
+    fprintf(stderr, "can't process multi-disk archive\n");
+    return NULL;
+  }
+
+  int cdcount = Read2(img+i+8);
+  int cdoffset = Read4(img+i+16);
+
+  ZipFileEntry* temp_entries = malloc(cdcount * sizeof(ZipFileEntry));
+  int entrycount = 0;
+
+  unsigned char* cd = img+cdoffset;
+  for (i = 0; i < cdcount; ++i) {
+    if (!(cd[0] == 0x50 && cd[1] == 0x4b && cd[2] == 0x01 && cd[3] == 0x02)) {
+      fprintf(stderr, "bad central directory entry %d\n", i);
+      return NULL;
+    }
+
+    int clen = Read4(cd+20);   // compressed len
+    int ulen = Read4(cd+24);   // uncompressed len
+    int nlen = Read2(cd+28);   // filename len
+    int xlen = Read2(cd+30);   // extra field len
+    int mlen = Read2(cd+32);   // file comment len
+    int hoffset = Read4(cd+42);   // local header offset
+
+    char* filename = malloc(nlen+1);
+    memcpy(filename, cd+46, nlen);
+    filename[nlen] = '\0';
+
+    int method = Read2(cd+10);
+
+    cd += 46 + nlen + xlen + mlen;
+
+    if (method != 8) {  // 8 == deflate
+      free(filename);
+      continue;
+    }
+
+    unsigned char* lh = img + hoffset;
+
+    if (!(lh[0] == 0x50 && lh[1] == 0x4b && lh[2] == 0x03 && lh[3] == 0x04)) {
+      fprintf(stderr, "bad local file header entry %d\n", i);
+      return NULL;
+    }
+
+    if (Read2(lh+26) != nlen || memcmp(lh+30, filename, nlen) != 0) {
+      fprintf(stderr, "central dir filename doesn't match local header\n");
+      return NULL;
+    }
+
+    xlen = Read2(lh+28);   // extra field len; might be different from CD entry?
+
+    temp_entries[entrycount].data_offset = hoffset+30+nlen+xlen;
+    temp_entries[entrycount].deflate_len = clen;
+    temp_entries[entrycount].uncomp_len = ulen;
+    temp_entries[entrycount].filename = filename;
+    ++entrycount;
+  }
+
+  qsort(temp_entries, entrycount, sizeof(ZipFileEntry), fileentry_compare);
+
+#if 0
+  printf("found %d deflated entries\n", entrycount);
+  for (i = 0; i < entrycount; ++i) {
+    printf("off %10d  len %10d unlen %10d   %p %s\n",
+           temp_entries[i].data_offset,
+           temp_entries[i].deflate_len,
+           temp_entries[i].uncomp_len,
+           temp_entries[i].filename,
+           temp_entries[i].filename);
+  }
+#endif
+
+  *num_chunks = 0;
+  *chunks = malloc((entrycount*2+2) * sizeof(ImageChunk));
+  ImageChunk* curr = *chunks;
+
+  if (include_pseudo_chunk) {
+    curr->type = CHUNK_NORMAL;
+    curr->start = 0;
+    curr->len = st.st_size;
+    curr->data = img;
+    curr->filename = NULL;
+    ++curr;
+    ++*num_chunks;
+  }
+
+  int pos = 0;
+  int nextentry = 0;
+
+  while (pos < st.st_size) {
+    if (nextentry < entrycount && pos == temp_entries[nextentry].data_offset) {
+      curr->type = CHUNK_DEFLATE;
+      curr->start = pos;
+      curr->deflate_len = temp_entries[nextentry].deflate_len;
+      curr->deflate_data = img + pos;
+      curr->filename = temp_entries[nextentry].filename;
+
+      curr->len = temp_entries[nextentry].uncomp_len;
+      curr->data = malloc(curr->len);
+
+      z_stream strm;
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = curr->deflate_len;
+      strm.next_in = curr->deflate_data;
+
+      // -15 means we are decoding a 'raw' deflate stream; zlib will
+      // not expect zlib headers.
+      int ret = inflateInit2(&strm, -15);
+
+      strm.avail_out = curr->len;
+      strm.next_out = curr->data;
+      ret = inflate(&strm, Z_NO_FLUSH);
+      if (ret != Z_STREAM_END) {
+        fprintf(stderr, "failed to inflate \"%s\"; %d\n", curr->filename, ret);
+        return NULL;
+      }
+
+      inflateEnd(&strm);
+
+      pos += curr->deflate_len;
+      ++nextentry;
+      ++*num_chunks;
+      ++curr;
+      continue;
+    }
+
+    // use a normal chunk to take all the data up to the start of the
+    // next deflate section.
+
+    curr->type = CHUNK_NORMAL;
+    curr->start = pos;
+    if (nextentry < entrycount) {
+      curr->len = temp_entries[nextentry].data_offset - pos;
+    } else {
+      curr->len = st.st_size - pos;
+    }
+    curr->data = img + pos;
+    curr->filename = NULL;
+    pos += curr->len;
+
+    ++*num_chunks;
+    ++curr;
+  }
+
+  free(temp_entries);
+  return img;
+}
+
 /*
  * Read the given file and break it up into chunks, putting the number
  * of chunks and their info in *num_chunks and **chunks,
@@ -166,38 +385,45 @@
   while (pos < st.st_size) {
     unsigned char* p = img+pos;
 
-    // Reallocate the list for every chunk; we expect the number of
-    // chunks to be small (5 for typical boot and recovery images).
-    ++*num_chunks;
-    *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
-    ImageChunk* curr = *chunks + (*num_chunks-1);
-    curr->start = pos;
-
     if (st.st_size - pos >= 4 &&
         p[0] == 0x1f && p[1] == 0x8b &&
         p[2] == 0x08 &&    // deflate compression
         p[3] == 0x00) {    // no header flags
       // 'pos' is the offset of the start of a gzip chunk.
 
-      curr->type = CHUNK_GZIP;
-      curr->gzip_header_len = GZIP_HEADER_LEN;
-      curr->gzip_header = p;
+      *num_chunks += 3;
+      *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
+      ImageChunk* curr = *chunks + (*num_chunks-3);
+
+      // create a normal chunk for the header.
+      curr->start = pos;
+      curr->type = CHUNK_NORMAL;
+      curr->len = GZIP_HEADER_LEN;
+      curr->data = p;
+
+      pos += curr->len;
+      p += curr->len;
+      ++curr;
+
+      curr->type = CHUNK_DEFLATE;
+      curr->filename = NULL;
 
       // We must decompress this chunk in order to discover where it
       // ends, and so we can put the uncompressed data and its length
-      // into curr->data and curr->len;
+      // into curr->data and curr->len.
 
       size_t allocated = 32768;
       curr->len = 0;
       curr->data = malloc(allocated);
-      curr->gzip_data = p;
+      curr->start = pos;
+      curr->deflate_data = p;
 
       z_stream strm;
       strm.zalloc = Z_NULL;
       strm.zfree = Z_NULL;
       strm.opaque = Z_NULL;
-      strm.avail_in = st.st_size - (pos + curr->gzip_header_len);
-      strm.next_in = p + GZIP_HEADER_LEN;
+      strm.avail_in = st.st_size - pos;
+      strm.next_in = p;
 
       // -15 means we are decoding a 'raw' deflate stream; zlib will
       // not expect zlib headers.
@@ -214,27 +440,42 @@
         }
       } while (ret != Z_STREAM_END);
 
-      curr->gzip_len = st.st_size - strm.avail_in - pos + GZIP_FOOTER_LEN;
-      pos = st.st_size - strm.avail_in;
+      curr->deflate_len = st.st_size - strm.avail_in - pos;
       inflateEnd(&strm);
+      pos += curr->deflate_len;
+      p += curr->deflate_len;
+      ++curr;
 
-      // consume the gzip footer.
-      curr->gzip_footer = img+pos;
-      pos += GZIP_FOOTER_LEN;
-      p = img+pos;
+      // create a normal chunk for the footer
+
+      curr->type = CHUNK_NORMAL;
+      curr->start = pos;
+      curr->len = GZIP_FOOTER_LEN;
+      curr->data = img+pos;
+
+      pos += curr->len;
+      p += curr->len;
+      ++curr;
 
       // The footer (that we just skipped over) contains the size of
       // the uncompressed data.  Double-check to make sure that it
       // matches the size of the data we got when we actually did
       // the decompression.
-      size_t footer_size = p[-4] + (p[-3] << 8) + (p[-2] << 16) + (p[-1] << 24);
-      if (footer_size != curr->len) {
+      size_t footer_size = Read4(p-4);
+      if (footer_size != curr[-2].len) {
         fprintf(stderr, "Error: footer size %d != decompressed size %d\n",
-                footer_size, curr->len);
+                footer_size, curr[-2].len);
         free(img);
         return NULL;
       }
     } else {
+      // Reallocate the list for every chunk; we expect the number of
+      // chunks to be small (5 for typical boot and recovery images).
+      ++*num_chunks;
+      *chunks = realloc(*chunks, *num_chunks * sizeof(ImageChunk));
+      ImageChunk* curr = *chunks + (*num_chunks-1);
+      curr->start = pos;
+
       // 'pos' is not the offset of the start of a gzip chunk, so scan
       // forward until we find a gzip header.
       curr->type = CHUNK_NORMAL;
@@ -264,7 +505,13 @@
  * the chunk).  Return 0 on success.
  */
 int TryReconstruction(ImageChunk* chunk, unsigned char* out) {
-  size_t p = chunk->gzip_header_len;
+  size_t p = 0;
+
+#if 0
+  fprintf(stderr, "trying %d %d %d %d %d\n",
+          chunk->level, chunk->method, chunk->windowBits,
+          chunk->memLevel, chunk->strategy);
+#endif
 
   z_stream strm;
   strm.zalloc = Z_NULL;
@@ -281,7 +528,7 @@
     ret = deflate(&strm, Z_FINISH);
     size_t have = BUFFER_SIZE - strm.avail_out;
 
-    if (memcmp(out, chunk->gzip_data+p, have) != 0) {
+    if (memcmp(out, chunk->deflate_data+p, have) != 0) {
       // mismatch; data isn't the same.
       deflateEnd(&strm);
       return -1;
@@ -289,7 +536,7 @@
     p += have;
   } while (ret != Z_STREAM_END);
   deflateEnd(&strm);
-  if (p + GZIP_FOOTER_LEN != chunk->gzip_len) {
+  if (p != chunk->deflate_len) {
     // mismatch; ran out of data before we should have.
     return -1;
   }
@@ -302,9 +549,9 @@
  * strategy fields in the chunk to the encoding parameters needed to
  * produce the right output.  Returns 0 on success.
  */
-int ReconstructGzipChunk(ImageChunk* chunk) {
-  if (chunk->type != CHUNK_GZIP) {
-    fprintf(stderr, "attempt to reconstruct non-gzip chunk\n");
+int ReconstructDeflateChunk(ImageChunk* chunk) {
+  if (chunk->type != CHUNK_DEFLATE) {
+    fprintf(stderr, "attempt to reconstruct non-deflate chunk\n");
     return -1;
   }
 
@@ -329,27 +576,6 @@
   return -1;
 }
 
-/** Write a 4-byte value to f in little-endian order. */
-void Write4(int value, FILE* f) {
-  fputc(value & 0xff, f);
-  fputc((value >> 8) & 0xff, f);
-  fputc((value >> 16) & 0xff, f);
-  fputc((value >> 24) & 0xff, f);
-}
-
-/** Write an 8-byte value to f in little-endian order. */
-void Write8(long long value, FILE* f) {
-  fputc(value & 0xff, f);
-  fputc((value >> 8) & 0xff, f);
-  fputc((value >> 16) & 0xff, f);
-  fputc((value >> 24) & 0xff, f);
-  fputc((value >> 32) & 0xff, f);
-  fputc((value >> 40) & 0xff, f);
-  fputc((value >> 48) & 0xff, f);
-  fputc((value >> 56) & 0xff, f);
-}
-
-
 /*
  * Given source and target chunks, compute a bsdiff patch between them
  * by running bsdiff in a subprocess.  Return the patch data, placing
@@ -357,6 +583,14 @@
  * program to be in the path.
  */
 unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
+  if (tgt->type == CHUNK_NORMAL) {
+    if (tgt->len <= 160) {
+      tgt->type = CHUNK_RAW;
+      *size = tgt->len;
+      return tgt->data;
+    }
+  }
+
   char stemp[] = "/tmp/imgdiff-src-XXXXXX";
   char ttemp[] = "/tmp/imgdiff-tgt-XXXXXX";
   char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
@@ -405,6 +639,17 @@
   }
 
   unsigned char* data = malloc(st.st_size);
+
+  if (tgt->type == CHUNK_NORMAL && tgt->len <= st.st_size) {
+    unlink(stemp);
+    unlink(ttemp);
+    unlink(ptemp);
+
+    tgt->type = CHUNK_RAW;
+    *size = tgt->len;
+    return tgt->data;
+  }
+
   *size = st.st_size;
 
   f = fopen(ptemp, "rb");
@@ -422,6 +667,17 @@
   unlink(ttemp);
   unlink(ptemp);
 
+  tgt->source_start = src->start;
+  switch (tgt->type) {
+    case CHUNK_NORMAL:
+      tgt->source_len = src->len;
+      break;
+    case CHUNK_DEFLATE:
+      tgt->source_len = src->deflate_len;
+      tgt->source_uncompressed_len = src->len;
+      break;
+  }
+
   return data;
 }
 
@@ -432,77 +688,243 @@
  * where some gzip chunks are reconstructible but others aren't (by
  * treating the ones that aren't as normal chunks).
  */
-void ChangeGzipChunkToNormal(ImageChunk* ch) {
+void ChangeDeflateChunkToNormal(ImageChunk* ch) {
+  if (ch->type != CHUNK_DEFLATE) return;
   ch->type = CHUNK_NORMAL;
   free(ch->data);
-  ch->data = ch->gzip_data;
-  ch->len = ch->gzip_len;
+  ch->data = ch->deflate_data;
+  ch->len = ch->deflate_len;
+}
+
+/*
+ * Return true if the data in the chunk is identical (including the
+ * compressed representation, for gzip chunks).
+ */
+int AreChunksEqual(ImageChunk* a, ImageChunk* b) {
+    if (a->type != b->type) return 0;
+
+    switch (a->type) {
+        case CHUNK_NORMAL:
+            return a->len == b->len && memcmp(a->data, b->data, a->len) == 0;
+
+        case CHUNK_DEFLATE:
+            return a->deflate_len == b->deflate_len &&
+                memcmp(a->deflate_data, b->deflate_data, a->deflate_len) == 0;
+
+        default:
+            fprintf(stderr, "unknown chunk type %d\n", a->type);
+            return 0;
+    }
+}
+
+/*
+ * Look for runs of adjacent normal chunks and compress them down into
+ * a single chunk.  (Such runs can be produced when deflate chunks are
+ * changed to normal chunks.)
+ */
+void MergeAdjacentNormalChunks(ImageChunk* chunks, int* num_chunks) {
+  int out = 0;
+  int in_start = 0, in_end;
+  while (in_start < *num_chunks) {
+    if (chunks[in_start].type != CHUNK_NORMAL) {
+      in_end = in_start+1;
+    } else {
+      // in_start is a normal chunk.  Look for a run of normal chunks
+      // that constitute a solid block of data (ie, each chunk begins
+      // where the previous one ended).
+      for (in_end = in_start+1;
+           in_end < *num_chunks && chunks[in_end].type == CHUNK_NORMAL &&
+             (chunks[in_end].start ==
+              chunks[in_end-1].start + chunks[in_end-1].len &&
+              chunks[in_end].data ==
+              chunks[in_end-1].data + chunks[in_end-1].len);
+           ++in_end);
+    }
+
+    if (in_end == in_start+1) {
+#if 0
+      printf("chunk %d is now %d\n", in_start, out);
+#endif
+      if (out != in_start) {
+        memcpy(chunks+out, chunks+in_start, sizeof(ImageChunk));
+      }
+    } else {
+#if 0
+      printf("collapse normal chunks %d-%d into %d\n", in_start, in_end-1, out);
+#endif
+
+      // Merge chunks [in_start, in_end-1] into one chunk.  Since the
+      // data member of each chunk is just a pointer into an in-memory
+      // copy of the file, this can be done without recopying (the
+      // output chunk has the first chunk's start location and data
+      // pointer, and length equal to the sum of the input chunk
+      // lengths).
+      chunks[out].type = CHUNK_NORMAL;
+      chunks[out].start = chunks[in_start].start;
+      chunks[out].data = chunks[in_start].data;
+      chunks[out].len = chunks[in_end-1].len +
+        (chunks[in_end-1].start - chunks[in_start].start);
+    }
+
+    ++out;
+    in_start = in_end;
+  }
+  *num_chunks = out;
+}
+
+ImageChunk* FindChunkByName(const char* name,
+                            ImageChunk* chunks, int num_chunks) {
+  int i;
+  for (i = 0; i < num_chunks; ++i) {
+    if (chunks[i].type == CHUNK_DEFLATE && chunks[i].filename &&
+        strcmp(name, chunks[i].filename) == 0) {
+      return chunks+i;
+    }
+  }
+  return NULL;
 }
 
 int main(int argc, char** argv) {
-  if (argc != 4) {
-    fprintf(stderr, "usage: %s <src-img> <tgt-img> <patch-file>\n", argv[0]);
+  if (argc != 4 && argc != 5) {
+    usage:
+    fprintf(stderr, "usage: %s [-z] <src-img> <tgt-img> <patch-file>\n",
+            argv[0]);
     return 2;
   }
 
+  int zip_mode = 0;
+
+  if (strcmp(argv[1], "-z") == 0) {
+    zip_mode = 1;
+    --argc;
+    ++argv;
+  }
+
+
   int num_src_chunks;
   ImageChunk* src_chunks;
-  if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) {
-    fprintf(stderr, "failed to break apart source image\n");
-    return 1;
-  }
-
   int num_tgt_chunks;
   ImageChunk* tgt_chunks;
-  if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) {
-    fprintf(stderr, "failed to break apart target image\n");
-    return 1;
-  }
-
-  // Verify that the source and target images have the same chunk
-  // structure (ie, the same sequence of gzip and normal chunks).
-
-  if (num_src_chunks != num_tgt_chunks) {
-    fprintf(stderr, "source and target don't have same number of chunks!\n");
-    return 1;
-  }
   int i;
-  for (i = 0; i < num_src_chunks; ++i) {
-    if (src_chunks[i].type != tgt_chunks[i].type) {
-      fprintf(stderr, "source and target don't have same chunk "
-              "structure! (chunk %d)\n", i);
+
+  if (zip_mode) {
+    if (ReadZip(argv[1], &num_src_chunks, &src_chunks, 1) == NULL) {
+      fprintf(stderr, "failed to break apart source zip file\n");
       return 1;
     }
-  }
+    if (ReadZip(argv[2], &num_tgt_chunks, &tgt_chunks, 0) == NULL) {
+      fprintf(stderr, "failed to break apart target zip file\n");
+      return 1;
+    }
+  } else {
+    if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) {
+      fprintf(stderr, "failed to break apart source image\n");
+      return 1;
+    }
+    if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) {
+      fprintf(stderr, "failed to break apart target image\n");
+      return 1;
+    }
 
-  // Confirm that given the uncompressed chunk data in the target, we
-  // can recompress it and get exactly the same bits as are in the
-  // input target image.  If this fails, treat the chunk as a normal
-  // non-gzipped chunk.
+    // Verify that the source and target images have the same chunk
+    // structure (ie, the same sequence of deflate and normal chunks).
 
-  for (i = 0; i < num_tgt_chunks; ++i) {
-    if (tgt_chunks[i].type == CHUNK_GZIP) {
-      if (ReconstructGzipChunk(tgt_chunks+i) < 0) {
-        printf("failed to reconstruct target gzip chunk %d; "
-               "treating as normal chunk\n", i);
-        ChangeGzipChunkToNormal(tgt_chunks+i);
-        ChangeGzipChunkToNormal(src_chunks+i);
-      } else {
-        printf("reconstructed target gzip chunk %d\n", i);
+    if (num_src_chunks != num_tgt_chunks) {
+      fprintf(stderr, "source and target don't have same number of chunks!\n");
+      return 1;
+    }
+    for (i = 0; i < num_src_chunks; ++i) {
+      if (src_chunks[i].type != tgt_chunks[i].type) {
+        fprintf(stderr, "source and target don't have same chunk "
+                "structure! (chunk %d)\n", i);
+        return 1;
       }
     }
   }
 
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    if (tgt_chunks[i].type == CHUNK_DEFLATE) {
+      // Confirm that given the uncompressed chunk data in the target, we
+      // can recompress it and get exactly the same bits as are in the
+      // input target image.  If this fails, treat the chunk as a normal
+      // non-deflated chunk.
+      if (ReconstructDeflateChunk(tgt_chunks+i) < 0) {
+        printf("failed to reconstruct target deflate chunk %d [%s]; "
+               "treating as normal\n", i, tgt_chunks[i].filename);
+        ChangeDeflateChunkToNormal(tgt_chunks+i);
+        if (zip_mode) {
+          ImageChunk* src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
+          if (src) {
+            ChangeDeflateChunkToNormal(src);
+          }
+        } else {
+          ChangeDeflateChunkToNormal(src_chunks+i);
+        }
+        continue;
+      }
+
+      // If two deflate chunks are identical (eg, the kernel has not
+      // changed between two builds), treat them as normal chunks.
+      // This makes applypatch much faster -- it can apply a trivial
+      // patch to the compressed data, rather than uncompressing and
+      // recompressing to apply the trivial patch to the uncompressed
+      // data.
+      ImageChunk* src;
+      if (zip_mode) {
+        src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks);
+      } else {
+        src = src_chunks+i;
+      }
+
+      if (src == NULL || AreChunksEqual(tgt_chunks+i, src)) {
+        ChangeDeflateChunkToNormal(tgt_chunks+i);
+        if (src) {
+          ChangeDeflateChunkToNormal(src);
+        }
+      }
+    }
+  }
+
+  // Merging neighboring normal chunks.
+  if (zip_mode) {
+    // For zips, we only need to do this to the target:  deflated
+    // chunks are matched via filename, and normal chunks are patched
+    // using the entire source file as the source.
+    MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+  } else {
+    // For images, we need to maintain the parallel structure of the
+    // chunk lists, so do the merging in both the source and target
+    // lists.
+    MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+    MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
+    if (num_src_chunks != num_tgt_chunks) {
+      // This shouldn't happen.
+      fprintf(stderr, "merging normal chunks went awry\n");
+      return 1;
+    }
+  }
+
   // Compute bsdiff patches for each chunk's data (the uncompressed
-  // data, in the case of gzip chunks).
+  // data, in the case of deflate chunks).
 
-  unsigned char** patch_data = malloc(num_src_chunks * sizeof(unsigned char*));
-  size_t* patch_size = malloc(num_src_chunks * sizeof(size_t));
-  for (i = 0; i < num_src_chunks; ++i) {
-    patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
-    printf("patch %d is %d bytes (of %d)\n", i, patch_size[i],
-           tgt_chunks[i].type == CHUNK_NORMAL ? tgt_chunks[i].len : tgt_chunks[i].gzip_len);
-
+  printf("Construct patches for %d chunks...\n", num_tgt_chunks);
+  unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*));
+  size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t));
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    if (zip_mode) {
+      ImageChunk* src;
+      if (tgt_chunks[i].type == CHUNK_DEFLATE &&
+          (src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
+                                 num_src_chunks))) {
+        patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
+      } else {
+        patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
+      }
+    } else {
+      patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
+    }
+    printf("patch %3d is %d bytes (of %d)\n",
+           i, patch_size[i], tgt_chunks[i].source_len);
   }
 
   // Figure out how big the imgdiff file header is going to be, so
@@ -510,10 +932,18 @@
   // within the file.
 
   size_t total_header_size = 12;
-  for (i = 0; i < num_src_chunks; ++i) {
-    total_header_size += 4 + 8*3;
-    if (src_chunks[i].type == CHUNK_GZIP) {
-      total_header_size += 8*2 + 4*6 + tgt_chunks[i].gzip_header_len + 8;
+  for (i = 0; i < num_tgt_chunks; ++i) {
+    total_header_size += 4;
+    switch (tgt_chunks[i].type) {
+      case CHUNK_NORMAL:
+        total_header_size += 8*3;
+        break;
+      case CHUNK_DEFLATE:
+        total_header_size += 8*5 + 4*5;
+        break;
+      case CHUNK_RAW:
+        total_header_size += 4 + patch_size[i];
+        break;
     }
   }
 
@@ -523,35 +953,53 @@
 
   // Write out the headers.
 
-  fwrite("IMGDIFF1", 1, 8, f);
-  Write4(num_src_chunks, f);
+  fwrite("IMGDIFF2", 1, 8, f);
+  Write4(num_tgt_chunks, f);
   for (i = 0; i < num_tgt_chunks; ++i) {
     Write4(tgt_chunks[i].type, f);
-    Write8(src_chunks[i].start, f);
-    Write8(src_chunks[i].type == CHUNK_NORMAL ? src_chunks[i].len :
-           (src_chunks[i].gzip_len + src_chunks[i].gzip_header_len + 8), f);
-    Write8(offset, f);
 
-    if (tgt_chunks[i].type == CHUNK_GZIP) {
-      Write8(src_chunks[i].len, f);
-      Write8(tgt_chunks[i].len, f);
-      Write4(tgt_chunks[i].level, f);
-      Write4(tgt_chunks[i].method, f);
-      Write4(tgt_chunks[i].windowBits, f);
-      Write4(tgt_chunks[i].memLevel, f);
-      Write4(tgt_chunks[i].strategy, f);
-      Write4(tgt_chunks[i].gzip_header_len, f);
-      fwrite(tgt_chunks[i].gzip_header, 1, tgt_chunks[i].gzip_header_len, f);
-      fwrite(tgt_chunks[i].gzip_footer, 1, GZIP_FOOTER_LEN, f);
+    switch (tgt_chunks[i].type) {
+      case CHUNK_NORMAL:
+        printf("chunk %3d: normal   (%10d, %10d)  %10d\n", i,
+               tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]);
+        Write8(tgt_chunks[i].source_start, f);
+        Write8(tgt_chunks[i].source_len, f);
+        Write8(offset, f);
+        offset += patch_size[i];
+        break;
+
+      case CHUNK_DEFLATE:
+        printf("chunk %3d: deflate  (%10d, %10d)  %10d  %s\n", i,
+               tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i],
+               tgt_chunks[i].filename);
+        Write8(tgt_chunks[i].source_start, f);
+        Write8(tgt_chunks[i].source_len, f);
+        Write8(offset, f);
+        Write8(tgt_chunks[i].source_uncompressed_len, f);
+        Write8(tgt_chunks[i].len, f);
+        Write4(tgt_chunks[i].level, f);
+        Write4(tgt_chunks[i].method, f);
+        Write4(tgt_chunks[i].windowBits, f);
+        Write4(tgt_chunks[i].memLevel, f);
+        Write4(tgt_chunks[i].strategy, f);
+        offset += patch_size[i];
+        break;
+
+      case CHUNK_RAW:
+        printf("chunk %3d: raw      (%10d, %10d)\n", i,
+               tgt_chunks[i].start, tgt_chunks[i].len);
+        Write4(patch_size[i], f);
+        fwrite(patch_data[i], 1, patch_size[i], f);
+        break;
     }
-
-    offset += patch_size[i];
   }
 
   // Append each chunk's bsdiff patch, in order.
 
   for (i = 0; i < num_tgt_chunks; ++i) {
-    fwrite(patch_data[i], 1, patch_size[i], f);
+    if (tgt_chunks[i].type != CHUNK_RAW) {
+      fwrite(patch_data[i], 1, patch_size[i], f);
+    }
   }
 
   fclose(f);
diff --git a/tools/applypatch/imgdiff.h b/tools/applypatch/imgdiff.h
index 7ec45c5..f2069b4 100644
--- a/tools/applypatch/imgdiff.h
+++ b/tools/applypatch/imgdiff.h
@@ -15,8 +15,10 @@
  */
 
 // Image patch chunk types
-#define CHUNK_NORMAL 0
-#define CHUNK_GZIP   1
+#define CHUNK_NORMAL   0
+#define CHUNK_GZIP     1   // version 1 only
+#define CHUNK_DEFLATE  2   // version 2 only
+#define CHUNK_RAW      3   // version 2 only
 
 // The gzip header size is actually variable, but we currently don't
 // support gzipped data with any of the optional fields, so for now it
diff --git a/tools/applypatch/imgdiff_test.sh b/tools/applypatch/imgdiff_test.sh
new file mode 100755
index 0000000..dcdb922
--- /dev/null
+++ b/tools/applypatch/imgdiff_test.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+#
+# A script for testing imgdiff/applypatch.  It takes two full OTA
+# packages as arguments.  It generates (on the host) patches for all
+# the zip/jar/apk files they have in common, as well as boot and
+# recovery images.  It then applies the patches on the device (or
+# emulator) and checks that the resulting file is correct.
+
+EMULATOR_PORT=5580
+
+# set to 0 to use a device instead
+USE_EMULATOR=0
+
+# where on the device to do all the patching.
+WORK_DIR=/data/local/tmp
+
+START_OTA_PACKAGE=$1
+END_OTA_PACKAGE=$2
+
+# ------------------------
+
+tmpdir=$(mktemp -d)
+
+if [ "$USE_EMULATOR" == 1 ]; then
+  emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
+  pid_emulator=$!
+  ADB="adb -s emulator-$EMULATOR_PORT "
+else
+  ADB="adb -d "
+fi
+
+echo "waiting to connect to device"
+$ADB wait-for-device
+
+# run a command on the device; exit with the exit status of the device
+# command.
+run_command() {
+  $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
+}
+
+testname() {
+  echo
+  echo "$1"...
+  testname="$1"
+}
+
+fail() {
+  echo
+  echo FAIL: $testname
+  echo
+  [ "$open_pid" == "" ] || kill $open_pid
+  [ "$pid_emulator" == "" ] || kill $pid_emulator
+  exit 1
+}
+
+sha1() {
+  sha1sum $1 | awk '{print $1}'
+}
+
+size() {
+  stat -c %s $1 | tr -d '\n'
+}
+
+cleanup() {
+  # not necessary if we're about to kill the emulator, but nice for
+  # running on real devices or already-running emulators.
+  testname "removing test files"
+  run_command rm $WORK_DIR/applypatch
+  run_command rm $WORK_DIR/source
+  run_command rm $WORK_DIR/target
+  run_command rm $WORK_DIR/patch
+
+  [ "$pid_emulator" == "" ] || kill $pid_emulator
+
+  rm -rf $tmpdir
+}
+
+$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
+
+patch_and_apply() {
+  local fn=$1
+  shift
+
+  unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source
+  unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target
+  imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch
+  bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs
+  echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)"
+  echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt
+  $ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed"
+  run_command rm /data/local/tmp/target
+  $ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed"
+  run_command /data/local/tmp/applypatch /data/local/tmp/source \
+    /data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \
+    $(sha1 $tmpdir/source):/data/local/tmp/patch \
+    || fail "applypatch of $fn failed"
+  $ADB pull /data/local/tmp/target $tmpdir/result
+  diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!"
+}
+
+# --------------- basic execution ----------------------
+
+for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \
+           sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do
+  patch_and_apply $i -z
+done
+patch_and_apply boot.img
+patch_and_apply system/recovery.img
+
+
+# --------------- cleanup ----------------------
+
+cleanup
+
+echo
+echo PASS
+echo
+
diff --git a/tools/applypatch/imgpatch.c b/tools/applypatch/imgpatch.c
index 2efe874..74b041f 100644
--- a/tools/applypatch/imgpatch.c
+++ b/tools/applypatch/imgpatch.c
@@ -27,24 +27,7 @@
 #include "mincrypt/sha.h"
 #include "applypatch.h"
 #include "imgdiff.h"
-
-int Read4(unsigned char* p) {
-  return (int)(((unsigned int)p[3] << 24) |
-               ((unsigned int)p[2] << 16) |
-               ((unsigned int)p[1] << 8) |
-               (unsigned int)p[0]);
-}
-
-long long Read8(unsigned char* p) {
-  return (long long)(((unsigned long long)p[7] << 56) |
-                     ((unsigned long long)p[6] << 48) |
-                     ((unsigned long long)p[5] << 40) |
-                     ((unsigned long long)p[4] << 32) |
-                     ((unsigned long long)p[3] << 24) |
-                     ((unsigned long long)p[2] << 16) |
-                     ((unsigned long long)p[1] << 8) |
-                     (unsigned long long)p[0]);
-}
+#include "utils.h"
 
 /*
  * Apply the patch given in 'patch_filename' to the source data given
@@ -54,7 +37,7 @@
  */
 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const char* patch_filename,
-                    FILE* output, SHA_CTX* ctx) {
+                    SinkFn sink, void* token, SHA_CTX* ctx) {
   FILE* f;
   if ((f = fopen(patch_filename, "rb")) == NULL) {
     fprintf(stderr, "failed to open patch file\n");
@@ -67,7 +50,10 @@
     return -1;
   }
 
-  if (memcmp(header, "IMGDIFF1", 8) != 0) {
+  // IMGDIFF1 uses CHUNK_NORMAL and CHUNK_GZIP.
+  // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
+  if (memcmp(header, "IMGDIFF", 7) != 0 ||
+      (header[7] != '1' && header[7] != '2')) {
     fprintf(stderr, "corrupt patch file header (magic number)\n");
     return -1;
   }
@@ -76,48 +62,67 @@
 
   int i;
   for (i = 0; i < num_chunks; ++i) {
-    // each chunk's header record starts with 28 bytes (4 + 8*3).
-    unsigned char chunk[28];
-    if (fread(chunk, 1, 28, f) != 28) {
+    // each chunk's header record starts with 4 bytes.
+    unsigned char chunk[4];
+    if (fread(chunk, 1, 4, f) != 4) {
       fprintf(stderr, "failed to read chunk %d record\n", i);
       return -1;
     }
 
     int type = Read4(chunk);
-    size_t src_start = Read8(chunk+4);
-    size_t src_len = Read8(chunk+12);
-    size_t patch_offset = Read8(chunk+20);
 
     if (type == CHUNK_NORMAL) {
+      unsigned char normal_header[24];
+      if (fread(normal_header, 1, 24, f) != 24) {
+        fprintf(stderr, "failed to read chunk %d normal header data\n", i);
+        return -1;
+      }
+
+      size_t src_start = Read8(normal_header);
+      size_t src_len = Read8(normal_header+8);
+      size_t patch_offset = Read8(normal_header+16);
+
       fprintf(stderr, "CHUNK %d:  normal   patch offset %d\n", i, patch_offset);
 
       ApplyBSDiffPatch(old_data + src_start, src_len,
                        patch_filename, patch_offset,
-                       output, ctx);
+                       sink, token, ctx);
     } else if (type == CHUNK_GZIP) {
-      fprintf(stderr, "CHUNK %d:  gzip     patch offset %d\n", i, patch_offset);
+      // This branch is basically a duplicate of the CHUNK_DEFLATE
+      // branch, with a bit of extra processing for the gzip header
+      // and footer.  I've avoided factoring the common code out since
+      // this branch will just be deleted when we drop support for
+      // IMGDIFF1.
 
-      // gzip chunks have an additional 40 + gzip_header_len + 8 bytes
+      // gzip chunks have an additional 64 + gzip_header_len + 8 bytes
       // in their chunk header.
-      unsigned char* gzip = malloc(40);
-      if (fread(gzip, 1, 40, f) != 40) {
-        fprintf(stderr, "failed to read chunk %d initial gzip data\n", i);
+      unsigned char* gzip = malloc(64);
+      if (fread(gzip, 1, 64, f) != 64) {
+        fprintf(stderr, "failed to read chunk %d initial gzip header data\n",
+                i);
         return -1;
       }
-      size_t gzip_header_len = Read4(gzip+36);
-      gzip = realloc(gzip, 40 + gzip_header_len + 8);
-      if (fread(gzip+40, 1, gzip_header_len+8, f) != gzip_header_len+8) {
-        fprintf(stderr, "failed to read chunk %d remaining gzip data\n", i);
+      size_t gzip_header_len = Read4(gzip+60);
+      gzip = realloc(gzip, 64 + gzip_header_len + 8);
+      if (fread(gzip+64, 1, gzip_header_len+8, f) != gzip_header_len+8) {
+        fprintf(stderr, "failed to read chunk %d remaining gzip header data\n",
+                i);
         return -1;
       }
 
-      size_t expanded_len = Read8(gzip);
-      size_t target_len = Read8(gzip);
-      int gz_level = Read4(gzip+16);
-      int gz_method = Read4(gzip+20);
-      int gz_windowBits = Read4(gzip+24);
-      int gz_memLevel = Read4(gzip+28);
-      int gz_strategy = Read4(gzip+32);
+      size_t src_start = Read8(gzip);
+      size_t src_len = Read8(gzip+8);
+      size_t patch_offset = Read8(gzip+16);
+
+      size_t expanded_len = Read8(gzip+24);
+      size_t target_len = Read8(gzip+32);
+      int gz_level = Read4(gzip+40);
+      int gz_method = Read4(gzip+44);
+      int gz_windowBits = Read4(gzip+48);
+      int gz_memLevel = Read4(gzip+52);
+      int gz_strategy = Read4(gzip+56);
+
+      fprintf(stderr, "CHUNK %d:  gzip     patch offset %d\n", i, patch_offset);
 
       // Decompress the source data; the chunk header tells us exactly
       // how big we expect it to be when decompressed.
@@ -173,8 +178,8 @@
       // Now compress the target data and append it to the output.
 
       // start with the gzip header.
-      fwrite(gzip+40, 1, gzip_header_len, output);
-      SHA_update(ctx, gzip+40, gzip_header_len);
+      sink(gzip+64, gzip_header_len, token);
+      SHA_update(ctx, gzip+64, gzip_header_len);
 
       // we're done with the expanded_source data buffer, so we'll
       // reuse that memory to receive the output of deflate.
@@ -202,7 +207,7 @@
         ret = deflate(&strm, Z_FINISH);
         size_t have = temp_size - strm.avail_out;
 
-        if (fwrite(temp_data, 1, have, output) != have) {
+        if (sink(temp_data, have, token) != have) {
           fprintf(stderr, "failed to write %d compressed bytes to output\n",
                   have);
           return -1;
@@ -212,12 +217,143 @@
       deflateEnd(&strm);
 
       // lastly, the gzip footer.
-      fwrite(gzip+40+gzip_header_len, 1, 8, output);
-      SHA_update(ctx, gzip+40+gzip_header_len, 8);
+      sink(gzip+64+gzip_header_len, 8, token);
+      SHA_update(ctx, gzip+64+gzip_header_len, 8);
 
       free(temp_data);
       free(uncompressed_target_data);
       free(gzip);
+    } else if (type == CHUNK_RAW) {
+      unsigned char raw_header[4];
+      if (fread(raw_header, 1, 4, f) != 4) {
+        fprintf(stderr, "failed to read chunk %d raw header data\n", i);
+        return -1;
+      }
+
+      size_t data_len = Read4(raw_header);
+
+      fprintf(stderr, "CHUNK %d:  raw      data %d\n", i, data_len);
+
+      unsigned char* temp = malloc(data_len);
+      if (fread(temp, 1, data_len, f) != data_len) {
+          fprintf(stderr, "failed to read chunk %d raw data\n", i);
+          return -1;
+      }
+      SHA_update(ctx, temp, data_len);
+      if (sink(temp, data_len, token) != data_len) {
+          fprintf(stderr, "failed to write chunk %d raw data\n", i);
+          return -1;
+      }
+    } else if (type == CHUNK_DEFLATE) {
+      // deflate chunks have an additional 60 bytes in their chunk header.
+      unsigned char deflate_header[60];
+      if (fread(deflate_header, 1, 60, f) != 60) {
+        fprintf(stderr, "failed to read chunk %d deflate header data\n", i);
+        return -1;
+      }
+
+      size_t src_start = Read8(deflate_header);
+      size_t src_len = Read8(deflate_header+8);
+      size_t patch_offset = Read8(deflate_header+16);
+      size_t expanded_len = Read8(deflate_header+24);
+      size_t target_len = Read8(deflate_header+32);
+      int level = Read4(deflate_header+40);
+      int method = Read4(deflate_header+44);
+      int windowBits = Read4(deflate_header+48);
+      int memLevel = Read4(deflate_header+52);
+      int strategy = Read4(deflate_header+56);
+
+      fprintf(stderr, "CHUNK %d:  deflate  patch offset %d\n", i, patch_offset);
+
+      // Decompress the source data; the chunk header tells us exactly
+      // how big we expect it to be when decompressed.
+
+      unsigned char* expanded_source = malloc(expanded_len);
+      if (expanded_source == NULL) {
+        fprintf(stderr, "failed to allocate %d bytes for expanded_source\n",
+                expanded_len);
+        return -1;
+      }
+
+      z_stream strm;
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = src_len;
+      strm.next_in = (unsigned char*)(old_data + src_start);
+      strm.avail_out = expanded_len;
+      strm.next_out = expanded_source;
+
+      int ret;
+      ret = inflateInit2(&strm, -15);
+      if (ret != Z_OK) {
+        fprintf(stderr, "failed to init source inflation: %d\n", ret);
+        return -1;
+      }
+
+      // Because we've provided enough room to accommodate the output
+      // data, we expect one call to inflate() to suffice.
+      ret = inflate(&strm, Z_SYNC_FLUSH);
+      if (ret != Z_STREAM_END) {
+        fprintf(stderr, "source inflation returned %d\n", ret);
+        return -1;
+      }
+      // We should have filled the output buffer exactly.
+      if (strm.avail_out != 0) {
+        fprintf(stderr, "source inflation short by %d bytes\n", strm.avail_out);
+        return -1;
+      }
+      inflateEnd(&strm);
+
+      // Next, apply the bsdiff patch (in memory) to the uncompressed
+      // data.
+      unsigned char* uncompressed_target_data;
+      ssize_t uncompressed_target_size;
+      if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
+                              patch_filename, patch_offset,
+                              &uncompressed_target_data,
+                              &uncompressed_target_size) != 0) {
+        return -1;
+      }
+
+      // Now compress the target data and append it to the output.
+
+      // we're done with the expanded_source data buffer, so we'll
+      // reuse that memory to receive the output of deflate.
+      unsigned char* temp_data = expanded_source;
+      ssize_t temp_size = expanded_len;
+      if (temp_size < 32768) {
+        // ... unless the buffer is too small, in which case we'll
+        // allocate a fresh one.
+        free(temp_data);
+        temp_data = malloc(32768);
+        temp_size = 32768;
+      }
+
+      // now the deflate stream
+      strm.zalloc = Z_NULL;
+      strm.zfree = Z_NULL;
+      strm.opaque = Z_NULL;
+      strm.avail_in = uncompressed_target_size;
+      strm.next_in = uncompressed_target_data;
+      ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
+      do {
+        strm.avail_out = temp_size;
+        strm.next_out = temp_data;
+        ret = deflate(&strm, Z_FINISH);
+        size_t have = temp_size - strm.avail_out;
+
+        if (sink(temp_data, have, token) != have) {
+          fprintf(stderr, "failed to write %d compressed bytes to output\n",
+                  have);
+          return -1;
+        }
+        SHA_update(ctx, temp_data, have);
+      } while (ret != Z_STREAM_END);
+      deflateEnd(&strm);
+
+      free(temp_data);
+      free(uncompressed_target_data);
     } else {
       fprintf(stderr, "patch chunk %d is unknown type %d\n", i, type);
       return -1;
diff --git a/tools/applypatch/utils.c b/tools/applypatch/utils.c
new file mode 100644
index 0000000..912229b
--- /dev/null
+++ b/tools/applypatch/utils.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "utils.h"
+
+/** Write a 4-byte value to f in little-endian order. */
+void Write4(int value, FILE* f) {
+  fputc(value & 0xff, f);
+  fputc((value >> 8) & 0xff, f);
+  fputc((value >> 16) & 0xff, f);
+  fputc((value >> 24) & 0xff, f);
+}
+
+/** Write an 8-byte value to f in little-endian order. */
+void Write8(long long value, FILE* f) {
+  fputc(value & 0xff, f);
+  fputc((value >> 8) & 0xff, f);
+  fputc((value >> 16) & 0xff, f);
+  fputc((value >> 24) & 0xff, f);
+  fputc((value >> 32) & 0xff, f);
+  fputc((value >> 40) & 0xff, f);
+  fputc((value >> 48) & 0xff, f);
+  fputc((value >> 56) & 0xff, f);
+}
+
+int Read2(unsigned char* p) {
+  return (int)(((unsigned int)p[1] << 8) |
+               (unsigned int)p[0]);
+}
+
+int Read4(unsigned char* p) {
+  return (int)(((unsigned int)p[3] << 24) |
+               ((unsigned int)p[2] << 16) |
+               ((unsigned int)p[1] << 8) |
+               (unsigned int)p[0]);
+}
+
+long long Read8(unsigned char* p) {
+  return (long long)(((unsigned long long)p[7] << 56) |
+                     ((unsigned long long)p[6] << 48) |
+                     ((unsigned long long)p[5] << 40) |
+                     ((unsigned long long)p[4] << 32) |
+                     ((unsigned long long)p[3] << 24) |
+                     ((unsigned long long)p[2] << 16) |
+                     ((unsigned long long)p[1] << 8) |
+                     (unsigned long long)p[0]);
+}
diff --git a/tools/applypatch/utils.h b/tools/applypatch/utils.h
new file mode 100644
index 0000000..d6d6f1d
--- /dev/null
+++ b/tools/applypatch/utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H
+#define _BUILD_TOOLS_APPLYPATCH_UTILS_H
+
+#include <stdio.h>
+
+// Read and write little-endian values of various sizes.
+
+void Write4(int value, FILE* f);
+void Write8(long long value, FILE* f);
+int Read2(unsigned char* p);
+int Read4(unsigned char* p);
+long long Read8(unsigned char* p);
+
+#endif //  _BUILD_TOOLS_APPLYPATCH_UTILS_H
diff --git a/tools/apriori/prelinkmap.c b/tools/apriori/prelinkmap.c
index 739c181..9fb00e4 100644
--- a/tools/apriori/prelinkmap.c
+++ b/tools/apriori/prelinkmap.c
@@ -7,11 +7,14 @@
 
 typedef struct mapentry mapentry;
 
+#define MAX_ALIASES 10
+
 struct mapentry
 {
     mapentry *next;
     unsigned base;
-    char name[0];
+    char *names[MAX_ALIASES];
+    int num_names;
 };
 
 static mapentry *maplist = 0;
@@ -22,14 +25,13 @@
 */
 
 #define PRELINK_MIN 0x90000000
-#define PRELINK_MAX 0xB0000000
+#define PRELINK_MAX 0xBFFFFFFF
 
 void pm_init(const char *file)
 {
     unsigned line = 0;
     char buf[256];
     char *x;
-    unsigned n;
     FILE *fp;
     mapentry *me;
     unsigned last = -1UL;
@@ -65,26 +67,52 @@
             continue;
         }
         
-        n = strtoul(x, 0, 16);
-        /* Note that this is not the only bounds check.  If a library's size
-           exceeds its slot as defined in the prelink map, the prelinker will
-           exit with an error.  See pm_report_library_size_in_memory().
-        */
-        FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX),
-               "%s:%d base 0x%08x out of range.\n",
-               file, line, n);
-        
-        me = malloc(sizeof(mapentry) + strlen(buf) + 1);
-        FAILIF(me == NULL, "Out of memory parsing %s\n", file);
+        if (isalpha(*x)) {
+            /* Assume that this is an alias, and look through the list of
+               already-installed libraries.
+            */
+            me = maplist;
+            while(me) {
+                /* The strlen() call ignores the newline at the end of x */
+                if (!strncmp(me->names[0], x, strlen(me->names[0]))) {
+                    PRINT("Aliasing library %s to %s at %08x\n",
+                          buf, x, me->base);
+                    break;
+                }
+                me = me->next;
+            }
+            FAILIF(!me, "Nonexistent alias %s -> %s\n", buf, x);
+        }
+        else {
+            unsigned n = strtoul(x, 0, 16);
+            /* Note that this is not the only bounds check.  If a library's
+               size exceeds its slot as defined in the prelink map, the
+               prelinker will exit with an error.  See
+               pm_report_library_size_in_memory().
+            */
+            FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX),
+                   "%s:%d base 0x%08x out of range.\n",
+                   file, line, n);
 
-        FAILIF(last <= n, "The prelink map is not in descending order "
-               "at entry %s (%08x)!\n", buf, n);
-        last = n;
-        
-        me->base = n;
-        strcpy(me->name, buf);
-        me->next = maplist;
-        maplist = me;        
+            me = malloc(sizeof(mapentry));
+            FAILIF(me == NULL, "Out of memory parsing %s\n", file);
+
+            FAILIF(last <= n, "The prelink map is not in descending order "
+                   "at entry %s (%08x)!\n", buf, n);
+            last = n;
+
+            me->base = n;
+            me->next = maplist;
+            me->num_names = 0;
+            maplist = me;
+        }
+
+        FAILIF(me->num_names >= MAX_ALIASES,
+               "Too many aliases for library %s, maximum is %d.\n",
+               me->names[0],
+               MAX_ALIASES);
+        me->names[me->num_names] = strdup(buf);
+        me->num_names++;
     }
 
     fclose(fp);
@@ -99,41 +127,44 @@
 {
     char *x;
     mapentry *me;
+    int n;
     
     x = strrchr(name,'/');
     if(x) name = x+1;
 
     for(me = maplist; me; me = me->next){
-        if(!strcmp(name, me->name)) {
-            off_t slot = me->next ? me->next->base : PRELINK_MAX;
-            slot -= me->base;
-            FAILIF(fsize > slot,
-                   "prelink map error: library %s@0x%08x is too big "
-                   "at %lld bytes, it runs %lld bytes into "
-                   "library %s@0x%08x!\n",
-                   me->name, me->base, fsize, fsize - slot,
-                   me->next->name, me->next->base);
-            break;
+        for (n = 0; n < me->num_names; n++) {
+            if(!strcmp(name, me->names[n])) {
+                off_t slot = me->next ? me->next->base : PRELINK_MAX;
+                slot -= me->base;
+                FAILIF(fsize > slot,
+                       "prelink map error: library %s@0x%08x is too big "
+                       "at %lld bytes, it runs %lld bytes into "
+                       "library %s@0x%08x!\n",
+                       me->names[0], me->base, fsize, fsize - slot,
+                       me->next->names[0], me->next->base);
+                return;
+            }
         }
     }
     
-    FAILIF(!me,"library '%s' not in prelink map\n", name);
+    FAILIF(1, "library '%s' not in prelink map\n", name);
 }
 
 unsigned pm_get_next_link_address(const char *lookup_name)
 {
     char *x;
     mapentry *me;
+    int n;
     
     x = strrchr(lookup_name,'/');
     if(x) lookup_name = x+1;
     
-    for(me = maplist; me; me = me->next){
-        if(!strcmp(lookup_name, me->name)) {
-            return me->base;
-        }
-    }
-    
-    FAILIF(1==1,"library '%s' not in prelink map\n", lookup_name);
+    for(me = maplist; me; me = me->next)
+        for (n = 0; n < me->num_names; n++)
+            if(!strcmp(lookup_name, me->names[n]))
+                return me->base;
+
+    FAILIF(1, "library '%s' not in prelink map\n", lookup_name);
     return 0;
 }
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 5c738a2..af5aa47 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -24,6 +24,7 @@
 echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER"
 echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
 echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
+echo "ro.wifi.channels=$PRODUCT_DEFAULT_WIFI_CHANNELS"
 echo "ro.board.platform=$TARGET_BOARD_PLATFORM"
 
 echo "# ro.build.product is obsolete; use ro.product.device"
diff --git a/tools/droiddoc/src/Android.mk b/tools/droiddoc/src/Android.mk
index bf404b7..30270b5 100644
--- a/tools/droiddoc/src/Android.mk
+++ b/tools/droiddoc/src/Android.mk
@@ -47,6 +47,7 @@
 	SampleTagInfo.java \
     Scoped.java \
 	SeeTagInfo.java \
+	SinceTagger.java \
 	Sorter.java \
 	SourcePositionInfo.java \
     Stubs.java \
@@ -57,6 +58,7 @@
 	TypeInfo.java
 
 LOCAL_JAVA_LIBRARIES := \
+	apicheck \
 	clearsilver
 
 LOCAL_CLASSPATH := \
diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
index f397a83..869142e 100644
--- a/tools/droiddoc/src/ClassInfo.java
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -907,6 +907,7 @@
         if (kind != null) {
             data.setValue("class.kind", kind);
         }
+        data.setValue("class.since", getSince());
 
         // the containing package -- note that this can be passed to type_link,
         // but it also contains the list of all of the packages
diff --git a/tools/droiddoc/src/ClearPage.java b/tools/droiddoc/src/ClearPage.java
index 2a8fced..184c8b8 100644
--- a/tools/droiddoc/src/ClearPage.java
+++ b/tools/droiddoc/src/ClearPage.java
@@ -126,7 +126,7 @@
         OutputStreamWriter stream = null;
         try {
             stream = new OutputStreamWriter(
-                            new FileOutputStream(file));
+                            new FileOutputStream(file), "UTF-8");
             String rendered = cs.render();
             stream.write(rendered, 0, rendered.length());
         }
diff --git a/tools/droiddoc/src/DocFile.java b/tools/droiddoc/src/DocFile.java
index b71c081..9901330 100644
--- a/tools/droiddoc/src/DocFile.java
+++ b/tools/droiddoc/src/DocFile.java
@@ -33,7 +33,8 @@
         try {
             File f = new File(filename);
             int length = (int)f.length();
-            FileReader reader = new FileReader(f);
+            FileInputStream is = new FileInputStream(f);
+            InputStreamReader reader = new InputStreamReader(is, "UTF-8");
             char[] buf = new char[length];
             int index = 0;
             int amt;
@@ -128,7 +129,7 @@
         } else {
             if (outfile.indexOf("sdk/") != -1) {
                 hdf.setValue("sdk", "true");
-                if (outfile.indexOf("index.html") != -1) {
+                if ((outfile.indexOf("index.html") != -1) || (outfile.indexOf("features.html") != -1)) {
                     ClearPage.write(hdf, "sdkpage.cs", outfile);
                 } else {
                     ClearPage.write(hdf, "docpage.cs", outfile);
diff --git a/tools/droiddoc/src/DocInfo.java b/tools/droiddoc/src/DocInfo.java
index 2530dc2..3abb367 100644
--- a/tools/droiddoc/src/DocInfo.java
+++ b/tools/droiddoc/src/DocInfo.java
@@ -51,8 +51,17 @@
 
     public abstract ContainerInfo parent();
 
+    public void setSince(String since) {
+        mSince = since;
+    }
+
+    public String getSince() {
+        return mSince;
+    }
+
     private String mRawCommentText;
     Comment mComment;
     SourcePositionInfo mPosition;
+    private String mSince;
 }
 
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
index f664c41..63fbc96 100644
--- a/tools/droiddoc/src/DroidDoc.java
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -94,9 +94,11 @@
         String stubsDir = null;
         //Create the dependency graph for the stubs directory
         boolean apiXML = false;
+        boolean noDocs = false;
         String apiFile = null;
         String debugStubsFile = "";
         HashSet<String> stubPackages = null;
+        SinceTagger sinceTagger = new SinceTagger();
 
         root = r;
 
@@ -186,6 +188,12 @@
                 apiXML = true;
                 apiFile = a[1];
             }
+            else if (a[0].equals("-nodocs")) {
+                noDocs = true;
+            }
+            else if (a[0].equals("-since")) {
+                sinceTagger.addVersion(a[1], a[2]);
+            }
         }
 
         // read some prefs from the template
@@ -196,58 +204,69 @@
         // Set up the data structures
         Converter.makeInfo(r);
 
-        // Files for proofreading
-        if (proofreadFile != null) {
-            Proofread.initProofread(proofreadFile);
+        if (!noDocs) {
+            long startTime = System.nanoTime();
+
+            // Apply @since tags from the XML file
+            sinceTagger.tagAll(Converter.rootClasses());
+
+            // Files for proofreading
+            if (proofreadFile != null) {
+                Proofread.initProofread(proofreadFile);
+            }
+            if (todoFile != null) {
+                TodoFile.writeTodoFile(todoFile);
+            }
+
+            // HTML Pages
+            if (ClearPage.htmlDir != null) {
+                writeHTMLPages();
+            }
+
+            // Navigation tree
+            NavTree.writeNavTree(javadocDir);
+
+            // Packages Pages
+            writePackages(javadocDir
+                            + (ClearPage.htmlDir!=null
+                                ? "packages" + htmlExtension
+                                : "index" + htmlExtension));
+
+            // Classes
+            writeClassLists();
+            writeClasses();
+            writeHierarchy();
+     //      writeKeywords();
+
+            // Lists for JavaScript
+            writeLists();
+            if (keepListFile != null) {
+                writeKeepList(keepListFile);
+            }
+
+            // Sample Code
+            for (SampleCode sc: sampleCodes) {
+                sc.write();
+            }
+
+            // Index page
+            writeIndex();
+
+            Proofread.finishProofread(proofreadFile);
+
+            if (sdkValuePath != null) {
+                writeSdkValues(sdkValuePath);
+            }
+
+            long time = System.nanoTime() - startTime;
+            System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
+                    + ClearPage.outputDir);
         }
-        if (todoFile != null) {
-            TodoFile.writeTodoFile(todoFile);
-        }
-
-        // HTML Pages
-        if (ClearPage.htmlDir != null) {
-            writeHTMLPages();
-        }
-
-        // Navigation tree
-        NavTree.writeNavTree(javadocDir);
-
-        // Packages Pages
-        writePackages(javadocDir
-                        + (ClearPage.htmlDir!=null
-                            ? "packages" + htmlExtension
-                            : "index" + htmlExtension));
-
-        // Classes
-        writeClassLists();
-        writeClasses();
-        writeHierarchy();
- //      writeKeywords();
-
-        // Lists for JavaScript
-        writeLists();
-        if (keepListFile != null) {
-            writeKeepList(keepListFile);
-        }
-
-        // Sample Code
-        for (SampleCode sc: sampleCodes) {
-            sc.write();
-        }
-
-        // Index page
-        writeIndex();
-
-        Proofread.finishProofread(proofreadFile);
 
         // Stubs
         if (stubsDir != null) {
             Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
         }
-        
-        if (sdkValuePath != null) {
-            writeSdkValues(sdkValuePath);
-        }
 
         Errors.printErrors();
         return !Errors.hadError;
@@ -394,9 +413,15 @@
         if (option.equals("-apixml")) {
             return 2;
         }
+        if (option.equals("-nodocs")) {
+            return 1;
+        }
+        if (option.equals("-since")) {
+            return 3;
+        }
         return 0;
     }
-    
+
     public static boolean validOptions(String[][] options, DocErrorReporter r)
     {
         for (String[] a: options) {
@@ -762,6 +787,7 @@
         String name = pkg.name();
 
         data.setValue("package.name", name);
+        data.setValue("package.since", pkg.getSince());
         data.setValue("package.descr", "...description...");
 
         makeClassListHDF(data, "package.interfaces", 
diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java
index dfeac88..95439f1 100644
--- a/tools/droiddoc/src/Errors.java
+++ b/tools/droiddoc/src/Errors.java
@@ -114,6 +114,7 @@
     public static Error DEPRECATION_MISMATCH = new Error(13, WARNING);
     public static Error MISSING_COMMENT = new Error(14, WARNING);
     public static Error IO_ERROR = new Error(15, HIDDEN);
+    public static Error NO_SINCE_DATA = new Error(16, WARNING);
 
     public static Error[] ERRORS = {
             UNRESOLVED_LINK,
@@ -129,6 +130,7 @@
             HIDDEN_SUPERCLASS,
             DEPRECATED,
             IO_ERROR,
+            NO_SINCE_DATA,
         };
 
     public static boolean setErrorLevel(int code, int level) {
diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java
index 536d798..1c975e4 100644
--- a/tools/droiddoc/src/FieldInfo.java
+++ b/tools/droiddoc/src/FieldInfo.java
@@ -223,6 +223,7 @@
         TagInfo.makeHDF(data, base + ".descr", inlineTags());
         TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
         TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
+        data.setValue(base + ".since", getSince());
         data.setValue(base + ".final", isFinal() ? "final" : "");
         data.setValue(base + ".static", isStatic() ? "static" : "");
         if (isPublic()) {
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
index ca30665..bded88b 100644
--- a/tools/droiddoc/src/MethodInfo.java
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -15,9 +15,8 @@
  */
 
 import org.clearsilver.HDF;
-import org.clearsilver.CS;
+
 import java.util.*;
-import java.io.*;
 
 public class MethodInfo extends MemberInfo
 {
@@ -357,6 +356,19 @@
         return s;
     }
 
+    /**
+     * Returns a name consistent with the {@link
+     * com.android.apicheck.MethodInfo#getHashableName()}.
+     */
+    public String getHashableName() {
+        StringBuilder result = new StringBuilder();
+        result.append(name());
+        for (ParameterInfo pInfo : mParameters) {
+            result.append(":").append(pInfo.type().fullName());
+        }
+        return result.toString();
+    }
+
     private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
     {
         int len = list.length;
@@ -545,6 +557,7 @@
         TagInfo.makeHDF(data, base + ".descr", inlineTags());
         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
         TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
+        data.setValue(base + ".since", getSince());
         ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
         AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
         ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
diff --git a/tools/droiddoc/src/SinceTagger.java b/tools/droiddoc/src/SinceTagger.java
new file mode 100644
index 0000000..a34814c
--- /dev/null
+++ b/tools/droiddoc/src/SinceTagger.java
@@ -0,0 +1,182 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+import com.android.apicheck.*;
+
+import java.util.*;
+
+/**
+ * Applies version information to the DroidDoc class model from apicheck XML
+ * files. Sample usage:
+ * <pre>
+ *   ClassInfo[] classInfos = ...
+ *
+ *   SinceTagger sinceTagger = new SinceTagger()
+ *   sinceTagger.addVersion("frameworks/base/api/1.xml", "Android 1.0")
+ *   sinceTagger.addVersion("frameworks/base/api/2.xml", "Android 1.5")
+ *   sinceTagger.tagAll(...);
+ * </pre>
+ */
+public class SinceTagger {
+
+    private final Map<String, String> xmlToName
+            = new LinkedHashMap<String, String>();
+
+    /**
+     * Specifies the apicheck XML file and the API version it holds. Calls to
+     * this method should be called in order from oldest version to newest.
+     */
+    public void addVersion(String file, String name) {
+        xmlToName.put(file, name);
+    }
+
+    public void tagAll(ClassInfo[] classDocs) {
+        // read through the XML files in order, applying their since information
+        // to the Javadoc models
+        for (Map.Entry<String, String> versionSpec : xmlToName.entrySet()) {
+            String xmlFile = versionSpec.getKey();
+            String versionName = versionSpec.getValue();
+            ApiInfo specApi = new ApiCheck().parseApi(xmlFile);
+
+            applyVersionsFromSpec(versionName, specApi, classDocs);
+        }
+
+        if (!xmlToName.isEmpty()) {
+            warnForMissingVersions(classDocs);
+        }
+    }
+
+    /**
+     * Applies the version information to {@code classDocs} where not already
+     * present.
+     *
+     * @param versionName the version name
+     * @param specApi the spec for this version. If a symbol is in this spec, it
+     *      was present in the named version
+     * @param classDocs the doc model to update
+     */
+    private void applyVersionsFromSpec(String versionName,
+            ApiInfo specApi, ClassInfo[] classDocs) {
+        for (ClassInfo classDoc : classDocs) {
+            com.android.apicheck.PackageInfo packageSpec
+                    = specApi.getPackages().get(classDoc.containingPackage().name());
+
+            if (packageSpec == null) {
+                continue;
+            }
+
+            com.android.apicheck.ClassInfo classSpec
+                    = packageSpec.allClasses().get(classDoc.name());
+
+            if (classSpec == null) {
+                continue;
+            }
+
+            versionPackage(versionName, classDoc.containingPackage());
+            versionClass(versionName, classDoc);
+            versionConstructors(versionName, classSpec, classDoc);
+            versionFields(versionName, classSpec, classDoc);
+            versionMethods(versionName, classSpec, classDoc);
+        }
+    }
+
+    /**
+     * Applies version information to {@code doc} where not already present.
+     */
+    private void versionPackage(String versionName, PackageInfo doc) {
+        if (doc.getSince() == null) {
+            doc.setSince(versionName);
+        }
+    }
+
+    /**
+     * Applies version information to {@code doc} where not already present.
+     */
+    private void versionClass(String versionName, ClassInfo doc) {
+        if (doc.getSince() == null) {
+            doc.setSince(versionName);
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionConstructors(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (MethodInfo constructor : doc.constructors()) {
+            if (constructor.getSince() == null
+                    && spec.allConstructors().containsKey(constructor.getHashableName())) {
+                constructor.setSince(versionName);
+            }
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionFields(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (FieldInfo field : doc.fields()) {
+            if (field.getSince() == null
+                    && spec.allFields().containsKey(field.name())) {
+                field.setSince(versionName);
+            }
+        }
+    }
+
+    /**
+     * Applies version information from {@code spec} to {@code doc} where not
+     * already present.
+     */
+    private void versionMethods(String versionName,
+            com.android.apicheck.ClassInfo spec, ClassInfo doc) {
+        for (MethodInfo method : doc.methods()) {
+            if (method.getSince() != null) {
+                continue;
+            }
+
+            for (com.android.apicheck.ClassInfo superclass : spec.hierarchy()) {
+                if (superclass.allMethods().containsKey(method.getHashableName())) {
+                    method.setSince(versionName);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Warns if any symbols are missing version information. When configured
+     * properly, this will yield zero warnings because {@code apicheck}
+     * guarantees that all symbols are present in the most recent API.
+     */
+    private void warnForMissingVersions(ClassInfo[] classDocs) {
+        for (ClassInfo claz : classDocs) {
+            if (claz.getSince() == null) {
+                Errors.error(Errors.NO_SINCE_DATA, claz.position(),
+                        "XML missing class " + claz.qualifiedName());
+            }
+            for (FieldInfo field : claz.fields()) {
+                if (field.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, field.position(),
+                            "XML missing field "
+                                    + claz.qualifiedName() + "#" + field .name());
+                }
+            }
+            for (MethodInfo constructor : claz.constructors()) {
+                if (constructor.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, constructor.position(),
+                            "XML missing constructor "
+                                    + claz.qualifiedName() + "#" + constructor.getHashableName());
+                }
+            }
+            for (MethodInfo method : claz.methods()) {
+                if (method.getSince() == null) {
+                    Errors.error(Errors.NO_SINCE_DATA, method.position(),
+                            "XML missing method "
+                                    + claz.qualifiedName() + "#" + method .getHashableName());
+                }
+            }
+        }
+    }
+}
diff --git a/tools/droiddoc/templates-pdk/customization.cs b/tools/droiddoc/templates-pdk/customization.cs
index e2d6682..315b81b 100644
--- a/tools/droiddoc/templates-pdk/customization.cs
+++ b/tools/droiddoc/templates-pdk/customization.cs
@@ -59,7 +59,7 @@
   Except as noted, this content is 
   licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
   Creative Commons Attribution 2.5</a>. For details and 
-  restrictions, see the <a href="<?cs var:toroot ?>license.html">Content 
+  restrictions, see the <a href="http://developer.android.com/license.html">Content 
   License</a>.<?cs 
 /def ?>
 
@@ -67,7 +67,7 @@
 def:custom_copyright() ?>
   Except as noted, this content is licensed under <a
   href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>. 
-  For details and restrictions, see the <a href="<?cs var:toroot ?>license.html">
+  For details and restrictions, see the <a href="http://developer.android.com/license.html">
   Content License</a>.<?cs 
 /def ?>
 
diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs
index 0cb85e8..ba0e0f6 100644
--- a/tools/droiddoc/templates-sdk/customization.cs
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -11,12 +11,12 @@
             <table class="gsc-search-box" cellpadding="0" cellspacing="0"><tbody>
                 <tr>
                   <td class="gsc-input">
-                    <input id="search_autocomplete" class="gsc-input" type="text" size="33" autocomplete="off" 
+                    <input id="search_autocomplete" class="gsc-input" type="text" size="33" autocomplete="off"
                       title="search developer docs" name="q"
-                      value="search developer docs" 
-                      onFocus="search_focus_changed(this, true)" 
-                      onBlur="search_focus_changed(this, false)" 
-                      onkeydown="return search_changed(event, true, '<?cs var:toroot?>')" 
+                      value="search developer docs"
+                      onFocus="search_focus_changed(this, true)"
+                      onBlur="search_focus_changed(this, false)"
+                      onkeydown="return search_changed(event, true, '<?cs var:toroot?>')"
                       onkeyup="return search_changed(event, false, '<?cs var:toroot?>')" />
                   <div id="search_filtered_div" class="no-display">
                       <table id="search_filtered" cellspacing=0>
@@ -46,19 +46,29 @@
       </div>
       <div id="headerRight">
           <div id="headerLinks">
-            <!-- 	<img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" />  -->
+          <?cs if:template.showLanguageMenu ?>
+              <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" /> 
               <span id="language">
-             		<select name="language" onChange="changeLangPref(this.value)">
-    							<option value="en">English</option>
-    				  	<!--  <option value="ja"></option>  -->
-             		</select>	
-             		<script type="text/javascript">
-             		  <!--  
-             		  loadLangPref();  
-             		  //-->
-             		</script>
-             	</span>
-            <a href="http://www.android.com">Android.com</a>
+             	<select name="language" onChange="changeLangPref(this.value, true)">
+    			<option value="en">English&nbsp;&nbsp;&nbsp;</option>
+    			<option value="ja">日本語</option>
+    			<?cs # 
+			<option value="de">Deutsch</option> 
+    			<option value="es">Español</option>
+    			<option value="fr">Français</option>
+    			<option value="it">Italiano</option>
+    			<option value="zh-CN">中文 (简体)</option>
+    			<option value="zh-TW">中文 (繁體)</option>
+			?>
+             	</select>	
+             	<script type="text/javascript">
+             	  <!--  
+                  loadLangPref();  
+             	   //-->
+             	</script>
+             </span>
+          <?cs /if ?>
+          <a href="http://www.android.com">Android.com</a>
           </div><?cs 
           call:default_search_box() ?>
       </div><!-- headerRight -->
diff --git a/tools/droiddoc/templates-sdk/header_tabs.cs b/tools/droiddoc/templates-sdk/header_tabs.cs
index 12b747e..02fc8bf 100644
--- a/tools/droiddoc/templates-sdk/header_tabs.cs
+++ b/tools/droiddoc/templates-sdk/header_tabs.cs
@@ -4,32 +4,74 @@
 	elif:sdk ?>sdk<?cs
 	elif:home ?>home<?cs
 	elif:community ?>community<?cs
-	elif:publish ?>publish<?cs
-	elif:about ?>about<?cs /if ?>">
+	elif:videos ?>videos<?cs /if ?>">
          
 	<li id="home-link"><a href="<?cs var:toroot ?><?cs if:android.whichdoc != "online" ?>offline.html<?cs else ?>index.html<?cs /if ?>">
+	<?cs if:!sdk.redirect ?>
 		<span class="en">Home</span>
-    <span class="ja"></span>
+		<span class="de">Startseite</span>
+		<span class="es"></span>
+		<span class="fr"></span>
+		<span class="it"></span>
+                <span class="ja">ホーム</span>
+		<span class="zh-CN">主页</span>
+		<span class="zh-TW">首頁</span>
+	<?cs /if ?>
 	</a></li>
 	<li id="sdk-link"><a href="<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html">
 		<span class="en">SDK</span>
-    <span class="ja"></span>
 	</a></li>
 	<li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html" onClick="return loadLast('guide')">
+	<?cs if:!sdk.redirect ?>
 		<span class="en">Dev Guide</span>
-    <span class="ja"></span>
+		<span class="de">Handbuch</span>
+		<span class="es">Guía</span>
+		<span class="fr">Guide</span>
+		<span class="it">Guida</span>
+                <span class="ja">開発ガイド</span>
+		<span class="zh-CN">开发人员指南</span>
+		<span class="zh-TW">開發指南</span>
+	<?cs /if ?>
 	</a></li>
 	<li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" onClick="return loadLast('reference')">
+	<?cs if:!sdk.redirect ?>
 		<span class="en">Reference</span>
-    <span class="ja"></span>
+		<span class="de">Referenz</span>
+		<span class="es">Referencia</span>
+		<span class="fr">Référence</span>
+		<span class="it">Riferimento</span>
+                <span class="ja">リファレンス</span>
+		<span class="zh-CN">参考</span>
+		<span class="zh-TW">參考資料</span>
+	<?cs /if ?>
 	</a></li>
-	<li><a href="http://android-developers.blogspot.com">
+	<li><a href="http://android-developers.blogspot.com" onClick="return requestAppendHL(this.href)">
+	<?cs if:!sdk.redirect ?>
 		<span class="en">Blog</span>
-    <span class="ja"></span>
+		<span class="de"></span>
+		<span class="es"></span>
+		<span class="fr"></span>
+		<span class="it"></span>
+                <span class="ja">ブログ</span>
+		<span class="zh-CN">博客</span>
+		<span class="zh-TW">網誌</span>
+	<?cs /if ?>
+	</a></li>
+	<li id="videos-link"><a href="<?cs var:toroot ?>videos/index.html" onClick="return loadLast('videos')">
+		<span class="en">Videos</span>
+    <span class="ja">ビデオ</span>
 	</a></li>
 	<li id="community-link"><a href="<?cs var:toroot ?>community/index.html">
+	<?cs if:!sdk.redirect ?>
 		<span class="en">Community</span>
-    <span class="ja"></span>
+		<span class="de"></span>
+		<span class="es">Comunidad</span>
+		<span class="fr">Communauté</span>
+		<span class="it"></span>
+                <span class="ja">コミュニティ</span>
+		<span class="zh-CN">社区</span>
+		<span class="zh-TW">社群</span>
+	<?cs /if ?>
 	</a></li>
      
-</ul>
\ No newline at end of file
+</ul>
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
index 8bf30d1..d5c9ffc 100644
--- a/tools/droiddoc/templates-sdk/sdkpage.cs
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -4,7 +4,9 @@
 <?cs if:sdk.redirect ?>
   <head>
     <title>Redirecting...</title>
-    <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs var:sdk.current ?>/index.html">
+    <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs 
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?>"<?cs 
+      else ?><?cs var:sdk.current ?>/index.html<?cs /if ?>"
     <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" />
   </head>
 <?cs else ?>
@@ -17,12 +19,14 @@
 <?cs call:sdk_nav() ?>
 
 <?cs if:sdk.redirect ?>
+
 <div class="g-unit">
   <div id="jd-content">
     <p>Redirecting to 
-    <a href="/sdk/<?cs var:sdk.current ?>/index.html">
-    /sdk/<?cs var:sdk.current ?>/index.html
-    </a></p>
+    <a href="<?cs var:toroot ?>sdk/<?cs 
+      if:sdk.redirect.path ?><?cs var:sdk.redirect.path ?>">/sdk/<?cs var:sdk.redirect.path ?><?cs 
+      else ?><?cs var:sdk.current ?>/index.html">/sdk/<?cs var:sdk.current ?>/index.html<?cs /if ?>
+    </a> ...</p>
 <?cs else ?>
 <div class="g-unit" id="doc-content" >
   <div id="jd-header" class="guide-header" >
@@ -45,8 +49,7 @@
     <p><a href="/sdk/<?cs var:sdk.current ?>/index.html">Download the current Android SDK</a></p>
   </div>
 <?cs /if ?>
-  
-  
+
 <?cs if:android.whichdoc != "online" ?>
 
 <p>The sections below provide an overview of the SDK package. </p>
@@ -58,14 +61,13 @@
 application developers build performance-critical portions of their apps in
 native code. It is designed for use <em>only</em> in conjunction with the
 Android SDK, so if you have not already installed the Android 1.5 SDK, please do
-so before downloading the NDK. Also, please read <a href="">What is the Android
-NDK?</a> to get an understanding of what the NDK offers and whether it will be
-useful to you.</p>
+so before downloading the NDK. Also, please read <a href="#overview">What is the 
+Android NDK?</a> to get an understanding of what the NDK offers and whether it 
+will be useful to you.</p>
 
 <p>Select the download package that is appropriate for your development
-computer. Note that separate download packages are provided for 32- and 64-bit
-Linux platforms.</p>
-  
+computer. </p>
+
   <table class="download">
     <tr>
       <th>Platform</th>
@@ -76,7 +78,7 @@
   <tr>
     <td>Windows</td>
     <td>
-  <a href="http://dl.google.com/android/<?cs var:ndk.win_download ?>"><?cs var:ndk.win_download ?></a>
+  <a href="http://dl.google.com/android/ndk/<?cs var:ndk.win_download ?>"><?cs var:ndk.win_download ?></a>
     </td>
     <td><?cs var:ndk.win_bytes ?> bytes</td>
     <td><?cs var:ndk.win_checksum ?></td>
@@ -84,27 +86,19 @@
   <tr class="alt-color">
     <td>Mac OS X (intel)</td>
     <td>
-  <a href="http://dl.google.com/android/<?cs var:ndk.mac_download ?>"><?cs var:ndk.mac_download ?></a>
+  <a href="http://dl.google.com/android/ndk/<?cs var:ndk.mac_download ?>"><?cs var:ndk.mac_download ?></a>
     </td>
     <td><?cs var:ndk.mac_bytes ?> bytes</td>
     <td><?cs var:ndk.mac_checksum ?></td>
   </tr>
   <tr>
-    <td>Linux 32-bit (i386)</td>
+    <td>Linux 32/64-bit (x86)</td>
     <td>
-  <a href="http://dl.google.com/android/<?cs var:ndk.linux_download ?>"><?cs var:ndk.linux_download ?></a>
+  <a href="http://dl.google.com/android/ndk/<?cs var:ndk.linux_download ?>"><?cs var:ndk.linux_download ?></a>
     </td>
     <td><?cs var:ndk.linux_bytes ?> bytes</td>
     <td><?cs var:ndk.linux_checksum ?></td>
   </tr>
-  <tr class="alt-color">
-    <td>Linux 64-bit (x86_64)</td>
-    <td>
-  <a href="http://dl.google.com/android/<?cs var:ndk.linux_64_download ?>"><?cs var:ndk.linux_64_download ?></a>
-    </td>
-    <td><?cs var:ndk.linux_64_bytes ?> bytes</td>
-    <td><?cs var:ndk.linux_64_checksum ?></td>
-  </tr>
   </table>
 
   <?cs else ?>
diff --git a/tools/droiddoc/templates/assets/android-developer-core.css b/tools/droiddoc/templates/assets/android-developer-core.css
index 8a1b9cd..2841398 100644
--- a/tools/droiddoc/templates/assets/android-developer-core.css
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -191,6 +191,11 @@
   padding: 25px 0 0;
 }
 
+#headerLeft img{
+  height:50px;
+  width:180px;
+}
+
 #headerRight {
   position:absolute;
   right:0;
@@ -238,14 +243,17 @@
   top:7px;
 }
 
+#header li a span+span {
+  display:none;
+}
+
 /* TAB HIGHLIGHTING */
 .home #home-link a,
-.publish #publish-link a,
 .guide #guide-link a,
 .reference #reference-link a,
 .sdk #sdk-link a,
 .community #community-link a,
-.about #about-link a {
+.videos #videos-link a {
   background-image: url(images/bg_images_sprite.png);
   background-position: 0 0;
   background-repeat: no-repeat;
@@ -255,12 +263,11 @@
 }
 
 .home #home-link a:hover,
-.publish #publish-link a:hover,
 .guide #guide-link a:hover,
 .reference #reference-link a:hover,
 .sdk #sdk-link a:hover,
 .community #community-link a:hover,
-.about #about-link  a:hover {
+.videos #videos-link  a:hover {
   background-image: url(images/bg_images_sprite.png);
   background-position: 0 0;
 }
@@ -276,8 +283,12 @@
   color: #7FA9B5;
 }
 
+#headerLinks img {
+  vertical-align:middle;
+}
+
 #language {
-  margin:0 10px;
+  margin:0 10px 0 4px;
 }
 
 #search {
@@ -763,65 +774,76 @@
 }
 
 #carouselMain {
+	background: url('/assets/images/home/bg_home_carousel_board.png') 0 0 no-repeat;
+	height:auto;
   padding: 25px 21px 0;
-  height:185px;
-  background-position:top;
   overflow:hidden;
   position:relative;
+  zoom:1; /*IE6*/
 }
 
 #carouselMain img {
   margin:0;
 }
 
-#homeMiddle p {
-  margin:0;
-  padding-bottom: 1em;
+#carouselMain .bulletinDesc h3 {
+	margin:0;
+	padding:0;
+}
+
+#carouselMain .bulletinDesc p {
+	margin:0;
+	padding:0.7em 0 0;
+}
+
+#carouselWheel {
+	background: url('/assets/images/home/bg_home_carousel_wheel.png') 0 0 no-repeat;
+	padding-top:40px;
+	height:150px;
 }
 
 .clearer { clear:both; }
 
-#arrow-left, #arrow-right {
+a#arrow-left, a#arrow-right {
   float:left;
   width:42px;
   height:42px;
   background-image:url(images/home/carousel_buttons_sprite.png);
   background-repeat:no-repeat;
 }
-#arrow-left {
+a#arrow-left {
   margin:35px 3px 0 10px;
 }
-#arrow-right {
+a#arrow-right {
   margin:35px 10px 0 0;
 }
-.arrow-left-off,
-#arrow-left.arrow-left-off:hover { 
+a.arrow-left-off,
+a#arrow-left.arrow-left-off:hover { 
   background-position:0 0;
 }
-.arrow-right-off, 
-#arrow-right.arrow-right-off:hover { 
+a.arrow-right-off, 
+a#arrow-right.arrow-right-off:hover { 
   background-position:-42px 0;
 }
-#arrow-left:hover { 
+a#arrow-left:hover { 
   background-position:0 -42px;
 }
-#arrow-right:hover { 
+a#arrow-right:hover { 
   background-position:-42px -42px;
 }
-.arrow-left-on {
+a.arrow-left-on {
   background-position:0 0;
 }
-.arrow-right-on {
+a.arrow-right-on {
   background-position:-42px 0;
 }
-.arrow-right-off,
-.arrow-left-off {
+a.arrow-right-off,
+a.arrow-left-off {
   cursor:default;
 }
 
 .app-list-container {
-  margin:37px 20px 0;
-  _margin-top:33px;
+  margin:0 20px;
   position:relative;
   width:100%;
 }
@@ -896,13 +918,19 @@
   padding-bottom:.25em;
 }
 
+/*IE6*/
+* html #app-list a { zoom: 1; margin:0 24px 0 15px;}
+
+* html #list-clip { 
+  width:430px !important;
+}
+
 /*carousel bulletin layouts*/
 /*460px width*/
 /*185px height*/
 .img-left {
   float:left;
   width:230px;
-  height:165px;
   overflow:hidden;
   padding:8px 0 8px 8px;
 }
@@ -914,7 +942,6 @@
 .img-right {
   float:right;
   width:220px;
-  height:165px;
   overflow:hidden;
   padding:8px 8px 8px 0;
 }
@@ -925,13 +952,275 @@
   text-align:right;
 }
 .img-top {
-  height:80px;
-  text-align:center;
+  padding:20px 20px 0;
 }
 .desc-bottom {
-  height:100px;
-  margin:10px;
+  padding:10px;
 }
 
 
+/* VIDEO PAGE */
 
+#mainBodyLeft.videoPlayer {
+  width:570px;
+}
+
+#mainBodyRight.videoPlayer {
+  width:330px;
+}
+
+/* player */
+
+#videoPlayerBox {
+  background-color: #DAF3FC;
+  border-radius:7px;
+  -moz-border-radius:7px;
+  -webkit-border-radius:7px;
+  width:530px;
+  padding:20px;
+  border:1px solid #d3ecf5;
+  box-shadow:2px 3px 1px #eee;
+  -moz-box-shadow:2px 3px 1px #eee;
+  -webkit-box-shadow:2px 3px 1px #eee;
+}
+
+#videoBorder {
+  background-color: #FFF;
+  min-height:399px;
+  height:auto !important;
+  border:1px solid #ccdada;
+  border-radius:7px 7px 0 0;
+  -moz-border-radius:7px 7px 0 0;
+  -webkit-border-top-left-radius:7px;
+  -webkit-border-top-right-radius:7px;
+}
+
+#videoPlayerTitle {
+  width:500px;
+  padding:15px 15px 0;
+}
+
+#videoPlayerTitle h2 {
+  font-weight:bold;
+  font-size:1.2em;
+  color:#336666;
+  margin:0;
+  padding:0;
+}
+
+#objectWrapper {
+  padding:15px 15px;
+  height:334px;
+  width:500px;
+}
+
+/* playlist tabs */
+
+ul#videoTabs {
+  list-style-type:none;
+  padding:0;
+  clear:both;
+  margin:0;
+  padding: 20px 0 0 15px;
+  zoom:1; /* IE7/8, otherwise top-padding is double */
+}
+
+ul#videoTabs li {
+  display:inline;
+  padding:0;
+  margin:0 3px 0 0;
+  line-height:2em;
+}
+
+ul#videoTabs li a {
+  border-radius:7px 7px 0 0;
+  -moz-border-radius:7px 7px 0 0;
+  -webkit-border-top-left-radius:7px;
+  -webkit-border-top-right-radius:7px;
+  background:#95c0d0;
+  color:#fff;
+  text-decoration:none;
+  padding:.45em 1.5em;
+  font-weight:bold;
+}
+
+ul#videoTabs li.selected a {
+  font-weight:bold;
+  text-decoration:none;
+  color:#555;
+  background:#daf3fc;
+  border-bottom:1px solid #daf3fc;
+}
+
+ul#videoTabs li:hover a {
+  background:#85acba;
+}
+
+ul#videoTabs li.selected:hover a {
+  background:#daf3fc;
+}
+
+/* playlists */
+
+#videos {
+  background:#daf3fc;
+  margin-bottom:1.5em;
+  padding:15px;
+  border-radius:5px;
+  -moz-border-radius:5px;
+  -webkit-border-radius:5px;
+  box-shadow:2px 3px 1px #eee;
+  -moz-box-shadow:2px 3px 1px #eee;
+  -webkit-box-shadow:2px 3px 1px #eee;
+}
+
+#videos div {
+  display:none;
+}
+
+#videos div.selected {
+  display:block;
+}
+
+ul.videoPreviews {
+  list-style:none;
+  padding:0;
+  margin:0;
+  zoom:1; /* IE, otherwise, layout doesn't update when showing 'more' */
+}
+
+ul.videoPreviews li {
+  margin:0 0 5px;
+  padding:0;
+  overflow:hidden;
+  position:relative;
+}
+
+#mainBodyFixed ul.videoPreviews h3 {
+  font-size: 12px;
+  margin:0 0 1em 130px;
+  padding:0;
+  font-weight:bold;
+  color:inherit;
+}
+
+ul.videoPreviews a {
+  margin:1px;
+  padding:10px;
+  text-decoration:none;
+  height:90px;
+  display:block;
+  border-radius:5px;
+  -moz-border-radius:5px;
+  -webkit-border-radius:5px;
+  background-color:transparent;
+}
+
+ul.videoPreviews a:hover {
+  background-color:#FFF;
+  border:none; /* IE8, otherwise, bg doesn't work */
+}
+
+ul.videoPreviews a.selected {
+  background-color: #FF9900;
+}
+
+ul.videoPreviews img {
+  float:left;
+  clear:left;
+  margin:0;
+}
+
+ul.videoPreviews h3 {
+  font-size:12px;
+  font-weight:bold;
+  text-decoration:none;
+  margin:0 0 1em 130px;
+  padding:0;
+}
+
+ul.videoPreviews p {
+  font-size: 12px;
+  text-decoration:none;
+  margin:0 0 1.2em 130px;
+}
+
+ul.videoPreviews p.full {
+  display:none;
+}
+
+ul.videoPreviews span.more {
+  padding:0 0 0 12px;
+  background:url('/assets/images/arrow_bluelink_down.png') 0 2px no-repeat;
+}
+
+ul.videoPreviews span.less {
+  padding:0 0 0 12px;
+  background:url('/assets/images/arrow_bluelink_up.png') 0 2px no-repeat;
+  display:none;
+}
+
+ul.videoPreviews p.toggle {
+  position:absolute;
+  margin:0;
+  margin-top:-23px; /* instead of bottom:23px, because IE won't do it correctly */
+  left:140px;
+}
+
+ul.videoPreviews p.toggle a {
+  height:auto;
+  margin:0;
+  padding:0;
+  zoom:1; /* IE6, otherwise the margin considers the img on redraws */
+}
+
+ul.videoPreviews p.toggle a:hover {
+  text-decoration:underline;
+  background:transparent; /* IE6, otherwise it inherits white */
+}
+
+/* featured videos */
+
+#mainBodyRight h2 {
+  padding:0 0 5px;
+}
+
+#mainBodyRight ul.videoPreviews {
+  margin:10px 0 0;
+}
+
+#mainBodyRight ul.videoPreviews li {
+  font-size:11px;
+  line-height:13px;
+  margin:0 0 5px;
+  padding:0;
+}
+
+#mainBodyRight ul.videoPreviews h3 {
+  padding:0;
+  margin:0;
+}
+
+#mainBodyRight ul.videoPreviews a {
+  text-decoration:none;
+  height:108px;
+  border:1px solid #FFF;
+}
+
+#mainBodyRight ul.videoPreviews a:hover {
+  border:1px solid #CCDADA;
+}
+
+#mainBodyRight ul.videoPreviews a.selected {
+  border:1px solid #FFF;
+}
+
+#mainBodyRight ul.videoPreviews p {
+	line-height:1.2em;
+  padding:0;
+  margin:4px 0 0 130px;
+}
+
+#mainBodyRight ul.videoPreviews img {
+	margin-top:5px;
+}
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
index f4aa5ef..cf44b26 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.css
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -89,6 +89,10 @@
   zoom:1;
 }
 
+#side-nav li a span+span {
+  display:none;
+}
+
 #side-nav li a:hover {
   text-decoration:underline;
 }
@@ -1130,9 +1134,9 @@
     page-break-inside: avoid;
   }
 
-  #qv,
+/*  #qv,
   #qv-wrapper {
     display:none;
   }
-
+*/
 }
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
index 016fa4e..af1b700 100644
--- a/tools/droiddoc/templates/assets/android-developer-docs.js
+++ b/tools/droiddoc/templates/assets/android-developer-docs.js
@@ -175,7 +175,8 @@
   devdocNav.css({height:sidenav.css("height")});
   $("#nav-tree").css({height:swapperHeight + "px"});
   
-  var section = location.pathname.substring(1,location.pathname.indexOf("/",1));
+  var basePath = getBaseUri(location.pathname);
+  var section = basePath.substring(1,basePath.indexOf("/",1));
   writeCookie("height", resizePackagesNav.css("height"), section, null);
 }
 
@@ -192,7 +193,8 @@
   classesNav.css({width:sidenavWidth});
   $("#packages-nav").css({width:sidenavWidth});
   
-  var section = location.pathname.substring(1,location.pathname.indexOf("/",1));
+  var basePath = getBaseUri(location.pathname);
+  var section = basePath.substring(1,basePath.indexOf("/",1));
   writeCookie("width", sidenavWidth, section, null);
 }
 
@@ -205,6 +207,32 @@
   }
 }
 
+function getBaseUri(uri) {
+  var intlUrl = (uri.substring(0,6) == "/intl/");
+  if (intlUrl) {
+    base = uri.substring(uri.indexOf('intl/')+5,uri.length);
+    base = base.substring(base.indexOf('/')+1, base.length);
+      //alert("intl, returning base url: /" + base);
+    return ("/" + base);
+  } else {
+      //alert("not intl, returning uri as found.");
+    return uri;
+  }
+}
+
+function requestAppendHL(uri) {
+//append "?hl=<lang> to an outgoing request (such as to blog)
+  var lang = getLangPref();
+  if (lang) {
+    var q = 'hl=' + lang;
+    uri += '?' + q;
+    window.location = uri;
+    return false;
+  } else {
+    return true;
+  }
+}
+
 function loadLast(cookiePath) {
   var location = window.location.href;
   if (location.indexOf("/"+cookiePath+"/") != -1) {
@@ -219,7 +247,7 @@
 }
 
 $(window).unload(function(){
-  var path = location.pathname;
+  var path = getBaseUri(location.pathname);
   if (path.indexOf("/reference/") != -1) {
     writeCookie("lastpage", path, "reference", null);
   } else if (path.indexOf("/guide/") != -1) {
@@ -227,8 +255,6 @@
   }
 });
 
-
-
 function toggle(obj, slide) {
   var ul = $("ul", obj);
   var li = ul.parent();
@@ -249,8 +275,6 @@
   }
 }
 
-
-
 function buildToggleLists() {
   $(".toggle-list").each(
     function(i) {
@@ -286,7 +310,7 @@
   }
   var date = new Date();
   date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
-  writeCookie("nav", nav_pref, null, date.toGMTString());
+  writeCookie("nav", nav_pref, "reference", date.toGMTString());
 
   $("#nav-panels").toggle();
   $("#panel-link").toggle();
@@ -387,12 +411,16 @@
   changeNavLang(lang);
 }
 
-function changeLangPref(lang) {
+function changeLangPref(lang, refresh) {
   var date = new Date();
-  date.setTime(date.getTime()+(50*365*24*60*60*1000)); // keep this for 50 years
-  writeCookie("pref_lang", lang, null, date);
-  
-  changeDocLang(lang);
+  expires = date.toGMTString(date.setTime(date.getTime()+(10*365*24*60*60*1000))); // keep this for 50 years
+  //alert("expires: " + expires)
+  writeCookie("pref_lang", lang, null, expires);
+  //changeDocLang(lang);
+  if (refresh) {
+    l = getBaseUri(location.pathname);
+    window.location = l;
+  }
 }
 
 function loadLangPref() {
@@ -403,6 +431,9 @@
 }
 
 function getLangPref() {
-  return $("#language").find(":selected").attr("value");
+  var lang = $("#language").find(":selected").attr("value");
+  if (!lang) {
+    lang = readCookie("pref_lang");
+  }
+  return (lang != 0) ? lang : 'en';
 }
-
diff --git a/tools/droiddoc/templates/assets/carousel.js b/tools/droiddoc/templates/assets/carousel.js
index 4eebd89..57eaff7 100644
--- a/tools/droiddoc/templates/assets/carousel.js
+++ b/tools/droiddoc/templates/assets/carousel.js
@@ -45,23 +45,8 @@
   currentDroid = appName;
 
   var droid = droidList[appName];
-  var layout = droid.layout;
-  var imgDiv = document.getElementById("bulletinImg");
-  var descDiv = document.getElementById("bulletinDesc");
-
-  if (layout == "imgLeft") {
-    imgDiv.className = "img-left";
-    descDiv.className = "desc-right";
-  } else if (layout == "imgTop") {
-    imgDiv.className = "img-top";
-    descDiv.className = "desc-bottom";
-  } else if (layout == "imgRight") {
-    imgDiv.className = "img-right";
-    descDiv.className = "desc-left";
-  }
-
-  imgDiv.innerHTML = "<img src='" + toRoot + assetsRoot + "images/home/" + droid.img + "'>";
-  descDiv.innerHTML = (droid.title != "") ? "<h3>" + droid.title + "</h3>" + droid.desc : droid.desc;
+  
+  $("#"+appName).show().siblings().hide();
 
   if(oldDroid)
     oldDroid.removeClass("selected");
@@ -95,7 +80,41 @@
     a.appendChild(br);
     a.appendChild(span);
     appList.appendChild(a);
+    
+    
+    /* add the bulletins */
+	  var layout = droid.layout;
+    var div = document.createElement("div");
+    var imgDiv = document.createElement("div");
+    var descDiv = document.createElement("div");
+    
+    div.setAttribute("id", x);
+    div.setAttribute("style", "display:none");
+    imgDiv.setAttribute("class", "bulletinImg");
+    descDiv.setAttribute("class", "bulletinDesc");
+	
+	  if (layout == "imgLeft") {
+	    $(imgDiv).addClass("img-left");
+	    $(descDiv).addClass("desc-right");
+	  } else if (layout == "imgTop") {
+	    $(imgDiv).addClass("img-top");
+	    $(descDiv).addClass("desc-bottom");
+	  } else if (layout == "imgRight") {
+	    $(imgDiv).addClass("img-right");
+	    $(descDiv).addClass("desc-left");
+	  }
+	
+	  imgDiv.innerHTML = "<img src='" + toRoot + assetsRoot + "images/home/" + droid.img + "'>";
+	  descDiv.innerHTML = (droid.title != "") ? "<h3>" + droid.title + "</h3>" + droid.desc : droid.desc;
+		$(div).append(imgDiv);
+		$(div).append(descDiv);
+    
+    $("#carouselMain").append(div);
+    
   }
+
+		
+  
 }
 
 // -- * slider * -- //
diff --git a/tools/droiddoc/templates/assets/images/arrow_bluelink_down.png b/tools/droiddoc/templates/assets/images/arrow_bluelink_down.png
new file mode 100755
index 0000000..58c248a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_bluelink_down.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_bluelink_up.png b/tools/droiddoc/templates/assets/images/arrow_bluelink_up.png
new file mode 100755
index 0000000..7d0f38e
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_bluelink_up.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/android_adc.png b/tools/droiddoc/templates/assets/images/home/android_adc.png
new file mode 100644
index 0000000..9fe7f8f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/android_adc.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_carousel_board.png b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_board.png
new file mode 100755
index 0000000..c577e02
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_board.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_carousel_wheel.png b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_wheel.png
new file mode 100755
index 0000000..aa224ad
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_carousel_wheel.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/io-large.png b/tools/droiddoc/templates/assets/images/home/io-large.png
new file mode 100755
index 0000000..986053c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/io-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/io-small.png b/tools/droiddoc/templates/assets/images/home/io-small.png
new file mode 100755
index 0000000..3a22549
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/io-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/jquery-resizable.min.js b/tools/droiddoc/templates/assets/jquery-resizable.min.js
index b3b4aed..67186fe 100755
--- a/tools/droiddoc/templates/assets/jquery-resizable.min.js
+++ b/tools/droiddoc/templates/assets/jquery-resizable.min.js
@@ -1,94 +1,40 @@
 /*
- * jQuery 1.2.6 - New Wave Javascript
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
  *
- * Copyright (c) 2008 John Resig (jquery.com)
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
+/*
+ * jQuery UI 1.7.2
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
  * Dual licensed under the MIT (MIT-LICENSE.txt)
  * and GPL (GPL-LICENSE.txt) licenses.
  *
- * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
- * $Rev: 5685 $
+ * http://docs.jquery.com/UI
  */
-/* Includes jQuery UI core and resizable */ 
-(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
-return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
-return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
-selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
-return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
-this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
-return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
-jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
-script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
-for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
-for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
-jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
-ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
-while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
-while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
-for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
-jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
-xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
-jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
-for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
-s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
-e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
-
-;(function($){var _remove=$.fn.remove;$.fn.remove=function(){$("*",this).add(this).triggerHandler("remove");return _remove.apply(this,arguments);};function isVisible(element){function checkStyles(element){var style=element.style;return(style.display!='none'&&style.visibility!='hidden');}
-var visible=checkStyles(element);(visible&&$.each($.dir(element,'parentNode'),function(){return(visible=checkStyles(this));}));return visible;}
-$.extend($.expr[':'],{data:function(a,i,m){return $.data(a,m[3]);},tabbable:function(a,i,m){var nodeName=a.nodeName.toLowerCase();return(a.tabIndex>=0&&(('a'==nodeName&&a.href)||(/input|select|textarea|button/.test(nodeName)&&'hidden'!=a.type&&!a.disabled))&&isVisible(a));}});$.keyCode={BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38};function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=='string'?methods.split(/,?\s+/):methods);}
-var methods=getMethods('getter');if(args.length==1&&typeof args[0]=='string'){methods=methods.concat(getMethods('getterSetter'));}
-return($.inArray(method,methods)!=-1);}
-$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=='string'),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=='_'){return this;}
-if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined);}
-return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options)));(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args));});};$[namespace][name]=function(element,options){var self=this;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+'-'+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind('setData.'+name,function(e,key,value){return self._setData(key,value);}).bind('getData.'+name,function(e,key){return self._getData(key);}).bind('remove',function(){return self.destroy();});this._init();};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter='option';};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName);},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key);}
-options={};options[key]=value;}
-$.each(options,function(key,value){self._setData(key,value);});},_getData:function(key){return this.options[key];},_setData:function(key,value){this.options[key]=value;if(key=='disabled'){this.element[value?'addClass':'removeClass'](this.widgetBaseClass+'-disabled');}},enable:function(){this._setData('disabled',false);},disable:function(){this._setData('disabled',true);},_trigger:function(type,e,data){var eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);e=e||$.event.fix({type:eventName,target:this.element[0]});return this.element.triggerHandler(eventName,[e,data],this.options[type]);}};$.widget.defaults={disabled:false};$.ui={plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]]);}},call:function(instance,name,args){var set=instance.plugins[name];if(!set){return;}
-for(var i=0;i<set.length;i++){if(instance.options[set[i][0]]){set[i][1].apply(instance.element,args);}}}},cssCache:{},css:function(name){if($.ui.cssCache[name]){return $.ui.cssCache[name];}
-var tmp=$('<div class="ui-gen">').addClass(name).css({position:'absolute',top:'-5000px',left:'-5000px',display:'block'}).appendTo('body');$.ui.cssCache[name]=!!((!(/auto|default/).test(tmp.css('cursor'))||(/^[1-9]/).test(tmp.css('height'))||(/^[1-9]/).test(tmp.css('width'))||!(/none/).test(tmp.css('backgroundImage'))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))));try{$('body').get(0).removeChild(tmp.get(0));}catch(e){}
-return $.ui.cssCache[name];},disableSelection:function(el){return $(el).attr('unselectable','on').css('MozUserSelect','none').bind('selectstart.ui',function(){return false;});},enableSelection:function(el){return $(el).attr('unselectable','off').css('MozUserSelect','').unbind('selectstart.ui');},hasScroll:function(e,a){if($(e).css('overflow')=='hidden'){return false;}
-var scroll=(a&&a=='left')?'scrollLeft':'scrollTop',has=false;if(e[scroll]>0){return true;}
-e[scroll]=1;has=(e[scroll]>0);e[scroll]=0;return has;}};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind('mousedown.'+this.widgetName,function(e){return self._mouseDown(e);});if($.browser.msie){this._mouseUnselectable=this.element.attr('unselectable');this.element.attr('unselectable','on');}
-this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widgetName);($.browser.msie&&this.element.attr('unselectable',this._mouseUnselectable));},_mouseDown:function(e){(this._mouseStarted&&this._mouseUp(e));this._mouseDownEvent=e;var self=this,btnIsLeft=(e.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(e.target).parents().add(e.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(e)){return true;}
-this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}
-if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(e)!==false);if(!this._mouseStarted){e.preventDefault();return true;}}
-this._mouseMoveDelegate=function(e){return self._mouseMove(e);};this._mouseUpDelegate=function(e){return self._mouseUp(e);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);return false;},_mouseMove:function(e){if($.browser.msie&&!e.button){return this._mouseUp(e);}
-if(this._mouseStarted){this._mouseDrag(e);return false;}
-if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,e)!==false);(this._mouseStarted?this._mouseDrag(e):this._mouseUp(e));}
-return!this._mouseStarted;},_mouseUp:function(e){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._mouseStop(e);}
-return false;},_mouseDistanceMet:function(e){return(Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance);},_mouseDelayMet:function(e){return this.mouseDelayMet;},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return true;}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0};})(jQuery);(function($){$.widget("ui.resizable",$.extend({},$.ui.mouse,{_init:function(){var self=this,o=this.options;var elpos=this.element.css('position');this.originalElement=this.element;this.element.addClass("ui-resizable").css({position:/static/.test(elpos)?'relative':elpos});$.extend(o,{_aspectRatio:!!(o.aspectRatio),helper:o.helper||o.ghost||o.animate?o.helper||'proxy':null,knobHandles:o.knobHandles===true?'ui-resizable-knob-handle':o.knobHandles});var aBorder='1px solid #DEDEDE';o.defaultTheme={'ui-resizable':{display:'block'},'ui-resizable-handle':{position:'absolute',background:'#F2F2F2',fontSize:'0.1px'},'ui-resizable-n':{cursor:'n-resize',height:'4px',left:'0px',right:'0px',borderTop:aBorder},'ui-resizable-s':{cursor:'s-resize',height:'4px',left:'0px',right:'0px',borderBottom:aBorder},'ui-resizable-e':{cursor:'e-resize',width:'4px',top:'0px',bottom:'0px',borderRight:aBorder},'ui-resizable-w':{cursor:'w-resize',width:'4px',top:'0px',bottom:'0px',borderLeft:aBorder},'ui-resizable-se':{cursor:'se-resize',width:'4px',height:'4px',borderRight:aBorder,borderBottom:aBorder},'ui-resizable-sw':{cursor:'sw-resize',width:'4px',height:'4px',borderBottom:aBorder,borderLeft:aBorder},'ui-resizable-ne':{cursor:'ne-resize',width:'4px',height:'4px',borderRight:aBorder,borderTop:aBorder},'ui-resizable-nw':{cursor:'nw-resize',width:'4px',height:'4px',borderLeft:aBorder,borderTop:aBorder}};o.knobTheme={'ui-resizable-handle':{background:'#F2F2F2',border:'1px solid #808080',height:'8px',width:'8px'},'ui-resizable-n':{cursor:'n-resize',top:'0px',left:'45%'},'ui-resizable-s':{cursor:'s-resize',bottom:'0px',left:'45%'},'ui-resizable-e':{cursor:'e-resize',right:'0px',top:'45%'},'ui-resizable-w':{cursor:'w-resize',left:'0px',top:'45%'},'ui-resizable-se':{cursor:'se-resize',right:'0px',bottom:'0px'},'ui-resizable-sw':{cursor:'sw-resize',left:'0px',bottom:'0px'},'ui-resizable-nw':{cursor:'nw-resize',left:'0px',top:'0px'},'ui-resizable-ne':{cursor:'ne-resize',right:'0px',top:'0px'}};o._nodeName=this.element[0].nodeName;if(o._nodeName.match(/canvas|textarea|input|select|button|img/i)){var el=this.element;if(/relative/.test(el.css('position'))&&$.browser.opera)
-el.css({position:'relative',top:'auto',left:'auto'});el.wrap($('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')}));var oel=this.element;this.element=this.element.parent();this.element.data('resizable',this);this.element.css({marginLeft:oel.css("marginLeft"),marginTop:oel.css("marginTop"),marginRight:oel.css("marginRight"),marginBottom:oel.css("marginBottom")});oel.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});if($.browser.safari&&o.preventDefault)oel.css('resize','none');o.proportionallyResize=oel.css({position:'static',zoom:1,display:'block'});this.element.css({margin:oel.css('margin')});this._proportionallyResize();}
-if(!o.handles)o.handles=!$('.ui-resizable-handle',this.element).length?"e,s,se":{n:'.ui-resizable-n',e:'.ui-resizable-e',s:'.ui-resizable-s',w:'.ui-resizable-w',se:'.ui-resizable-se',sw:'.ui-resizable-sw',ne:'.ui-resizable-ne',nw:'.ui-resizable-nw'};if(o.handles.constructor==String){o.zIndex=o.zIndex||1000;if(o.handles=='all')o.handles='n,e,s,w,se,sw,ne,nw';var n=o.handles.split(",");o.handles={};var insertionsDefault={handle:'position: absolute; display: none; overflow:hidden;',n:'top: 0pt; width:100%;',e:'right: 0pt; height:100%;',s:'bottom: 0pt; width:100%;',w:'left: 0pt; height:100%;',se:'bottom: 0pt; right: 0px;',sw:'bottom: 0pt; left: 0px;',ne:'top: 0pt; right: 0px;',nw:'top: 0pt; left: 0px;'};for(var i=0;i<n.length;i++){var handle=$.trim(n[i]),dt=o.defaultTheme,hname='ui-resizable-'+handle,loadDefault=!$.ui.css(hname)&&!o.knobHandles,userKnobClass=$.ui.css('ui-resizable-knob-handle'),allDefTheme=$.extend(dt[hname],dt['ui-resizable-handle']),allKnobTheme=$.extend(o.knobTheme[hname],!userKnobClass?o.knobTheme['ui-resizable-handle']:{});var applyZIndex=/sw|se|ne|nw/.test(handle)?{zIndex:++o.zIndex}:{};var defCss=(loadDefault?insertionsDefault[handle]:''),axis=$(['<div class="ui-resizable-handle ',hname,'" style="',defCss,insertionsDefault.handle,'"></div>'].join('')).css(applyZIndex);o.handles[handle]='.ui-resizable-'+handle;this.element.append(axis.css(loadDefault?allDefTheme:{}).css(o.knobHandles?allKnobTheme:{}).addClass(o.knobHandles?'ui-resizable-knob-handle':'').addClass(o.knobHandles));}
-if(o.knobHandles)this.element.addClass('ui-resizable-knob').css(!$.ui.css('ui-resizable-knob')?{}:{});}
-this._renderAxis=function(target){target=target||this.element;for(var i in o.handles){if(o.handles[i].constructor==String)
-o.handles[i]=$(o.handles[i],this.element).show();if(o.transparent)
-o.handles[i].css({opacity:0});if(this.element.is('.ui-wrapper')&&o._nodeName.match(/textarea|input|select|button/i)){var axis=$(o.handles[i],this.element),padWrapper=0;padWrapper=/sw|ne|nw|se|n|s/.test(i)?axis.outerHeight():axis.outerWidth();var padPos=['padding',/ne|nw|n/.test(i)?'Top':/se|sw|s/.test(i)?'Bottom':/^e$/.test(i)?'Right':'Left'].join("");if(!o.transparent)
-target.css(padPos,padWrapper);this._proportionallyResize();}
-if(!$(o.handles[i]).length)continue;}};this._renderAxis(this.element);o._handles=$('.ui-resizable-handle',self.element);if(o.disableSelection)
-o._handles.each(function(i,e){$.ui.disableSelection(e);});o._handles.mouseover(function(){if(!o.resizing){if(this.className)
-var axis=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);self.axis=o.axis=axis&&axis[1]?axis[1]:'se';}});if(o.autoHide){o._handles.hide();$(self.element).addClass("ui-resizable-autohide").hover(function(){$(this).removeClass("ui-resizable-autohide");o._handles.show();},function(){if(!o.resizing){$(this).addClass("ui-resizable-autohide");o._handles.hide();}});}
-this._mouseInit();},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,options:this.options,originalSize:this.originalSize,originalPosition:this.originalPosition};},_propagate:function(n,e){$.ui.plugin.call(this,n,[e,this.ui()]);if(n!="resize")this.element.triggerHandler(["resize",n].join(""),[e,this.ui()],this.options[n]);},destroy:function(){var el=this.element,wrapped=el.children(".ui-resizable").get(0);this._mouseDestroy();var _destroy=function(exp){$(exp).removeClass("ui-resizable ui-resizable-disabled").removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();};_destroy(el);if(el.is('.ui-wrapper')&&wrapped){el.parent().append($(wrapped).css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')})).end().remove();_destroy(wrapped);}},_mouseCapture:function(e){if(this.options.disabled)return false;var handle=false;for(var i in this.options.handles){if($(this.options.handles[i])[0]==e.target)handle=true;}
-if(!handle)return false;return true;},_mouseStart:function(e){var o=this.options,iniPos=this.element.position(),el=this.element,num=function(v){return parseInt(v,10)||0;},ie6=$.browser.msie&&$.browser.version<7;o.resizing=true;o.documentScroll={top:$(document).scrollTop(),left:$(document).scrollLeft()};if(el.is('.ui-draggable')||(/absolute/).test(el.css('position'))){var sOffset=$.browser.msie&&!o.containment&&(/absolute/).test(el.css('position'))&&!(/relative/).test(el.parent().css('position'));var dscrollt=sOffset?o.documentScroll.top:0,dscrolll=sOffset?o.documentScroll.left:0;el.css({position:'absolute',top:(iniPos.top+dscrollt),left:(iniPos.left+dscrolll)});}
-if($.browser.opera&&/relative/.test(el.css('position')))
-el.css({position:'relative',top:'auto',left:'auto'});this._renderProxy();var curleft=num(this.helper.css('left')),curtop=num(this.helper.css('top'));if(o.containment){curleft+=$(o.containment).scrollLeft()||0;curtop+=$(o.containment).scrollTop()||0;}
-this.offset=this.helper.offset();this.position={left:curleft,top:curtop};this.size=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalSize=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalPosition={left:curleft,top:curtop};this.sizeDiff={width:el.outerWidth()-el.width(),height:el.outerHeight()-el.height()};this.originalMousePosition={left:e.pageX,top:e.pageY};o.aspectRatio=(typeof o.aspectRatio=='number')?o.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);if(o.preserveCursor)
-$('body').css('cursor',this.axis+'-resize');this._propagate("start",e);return true;},_mouseDrag:function(e){var el=this.helper,o=this.options,props={},self=this,smp=this.originalMousePosition,a=this.axis;var dx=(e.pageX-smp.left)||0,dy=(e.pageY-smp.top)||0;var trigger=this._change[a];if(!trigger)return false;var data=trigger.apply(this,[e,dx,dy]),ie6=$.browser.msie&&$.browser.version<7,csdif=this.sizeDiff;if(o._aspectRatio||e.shiftKey)
-data=this._updateRatio(data,e);data=this._respectSize(data,e);this._propagate("resize",e);el.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!o.helper&&o.proportionallyResize)
-this._proportionallyResize();this._updateCache(data);this.element.triggerHandler("resize",[e,this.ui()],this.options["resize"]);return false;},_mouseStop:function(e){this.options.resizing=false;var o=this.options,num=function(v){return parseInt(v,10)||0;},self=this;if(o.helper){var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var s={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;if(!o.animate)
-this.element.css($.extend(s,{top:top,left:left}));if(o.helper&&!o.animate)this._proportionallyResize();}
-if(o.preserveCursor)
-$('body').css('cursor','auto');this._propagate("stop",e);if(o.helper)this.helper.remove();return false;},_updateCache:function(data){var o=this.options;this.offset=this.helper.offset();if(data.left)this.position.left=data.left;if(data.top)this.position.top=data.top;if(data.height)this.size.height=data.height;if(data.width)this.size.width=data.width;},_updateRatio:function(data,e){var o=this.options,cpos=this.position,csize=this.size,a=this.axis;if(data.height)data.width=(csize.height*o.aspectRatio);else if(data.width)data.height=(csize.width/o.aspectRatio);if(a=='sw'){data.left=cpos.left+(csize.width-data.width);data.top=null;}
-if(a=='nw'){data.top=cpos.top+(csize.height-data.height);data.left=cpos.left+(csize.width-data.width);}
-return data;},_respectSize:function(data,e){var el=this.helper,o=this.options,pRatio=o._aspectRatio||e.shiftKey,a=this.axis,ismaxw=data.width&&o.maxWidth&&o.maxWidth<data.width,ismaxh=data.height&&o.maxHeight&&o.maxHeight<data.height,isminw=data.width&&o.minWidth&&o.minWidth>data.width,isminh=data.height&&o.minHeight&&o.minHeight>data.height;if(isminw)data.width=o.minWidth;if(isminh)data.height=o.minHeight;if(ismaxw)data.width=o.maxWidth;if(ismaxh)data.height=o.maxHeight;var dw=this.originalPosition.left+this.originalSize.width,dh=this.position.top+this.size.height;var cw=/sw|nw|w/.test(a),ch=/nw|ne|n/.test(a);if(isminw&&cw)data.left=dw-o.minWidth;if(ismaxw&&cw)data.left=dw-o.maxWidth;if(isminh&&ch)data.top=dh-o.minHeight;if(ismaxh&&ch)data.top=dh-o.maxHeight;var isNotwh=!data.width&&!data.height;if(isNotwh&&!data.left&&data.top)data.top=null;else if(isNotwh&&!data.top&&data.left)data.left=null;return data;},_proportionallyResize:function(){var o=this.options;if(!o.proportionallyResize)return;var prel=o.proportionallyResize,el=this.helper||this.element;if(!o.borderDif){var b=[prel.css('borderTopWidth'),prel.css('borderRightWidth'),prel.css('borderBottomWidth'),prel.css('borderLeftWidth')],p=[prel.css('paddingTop'),prel.css('paddingRight'),prel.css('paddingBottom'),prel.css('paddingLeft')];o.borderDif=$.map(b,function(v,i){var border=parseInt(v,10)||0,padding=parseInt(p[i],10)||0;return border+padding;});}
-prel.css({height:(el.height()-o.borderDif[0]-o.borderDif[2])+"px",width:(el.width()-o.borderDif[1]-o.borderDif[3])+"px"});},_renderProxy:function(){var el=this.element,o=this.options;this.elementOffset=el.offset();if(o.helper){this.helper=this.helper||$('<div style="overflow:hidden;"></div>');var ie6=$.browser.msie&&$.browser.version<7,ie6offset=(ie6?1:0),pxyoffset=(ie6?2:-1);this.helper.addClass(o.helper).css({width:el.outerWidth()+pxyoffset,height:el.outerHeight()+pxyoffset,position:'absolute',left:this.elementOffset.left-ie6offset+'px',top:this.elementOffset.top-ie6offset+'px',zIndex:++o.zIndex});this.helper.appendTo("body");if(o.disableSelection)
-$.ui.disableSelection(this.helper.get(0));}else{this.helper=el;}},_change:{e:function(e,dx,dy){return{width:this.originalSize.width+dx};},w:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{left:sp.left+dx,width:cs.width-dx};},n:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{top:sp.top+dy,height:cs.height-dy};},s:function(e,dx,dy){return{height:this.originalSize.height+dy};},se:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},sw:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));},ne:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},nw:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));}}}));$.extend($.ui.resizable,{defaults:{cancel:":input",distance:1,delay:0,preventDefault:true,transparent:false,minWidth:10,minHeight:10,aspectRatio:false,disableSelection:true,preserveCursor:true,autoHide:false,knobHandles:false}});$.ui.plugin.add("resizable","containment",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),el=self.element;var oc=o.containment,ce=(oc instanceof $)?oc.get(0):(/parent/.test(oc))?el.parent().get(0):oc;if(!ce)return;self.containerElement=$(ce);if(/document/.test(oc)||oc==document){self.containerOffset={left:0,top:0};self.containerPosition={left:0,top:0};self.parentData={element:$(document),left:0,top:0,width:$(document).width(),height:$(document).height()||document.body.parentNode.scrollHeight};}
-else{self.containerOffset=$(ce).offset();self.containerPosition=$(ce).position();self.containerSize={height:$(ce).innerHeight(),width:$(ce).innerWidth()};var co=self.containerOffset,ch=self.containerSize.height,cw=self.containerSize.width,width=($.ui.hasScroll(ce,"left")?ce.scrollWidth:cw),height=($.ui.hasScroll(ce)?ce.scrollHeight:ch);self.parentData={element:ce,left:co.left,top:co.top,width:width,height:height};}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),ps=self.containerSize,co=self.containerOffset,cs=self.size,cp=self.position,pRatio=o._aspectRatio||e.shiftKey,cop={top:0,left:0},ce=self.containerElement;if(ce[0]!=document&&/static/.test(ce.css('position')))
-cop=self.containerPosition;if(cp.left<(o.helper?co.left:cop.left)){self.size.width=self.size.width+(o.helper?(self.position.left-co.left):(self.position.left-cop.left));if(pRatio)self.size.height=self.size.width/o.aspectRatio;self.position.left=o.helper?co.left:cop.left;}
-if(cp.top<(o.helper?co.top:0)){self.size.height=self.size.height+(o.helper?(self.position.top-co.top):self.position.top);if(pRatio)self.size.width=self.size.height*o.aspectRatio;self.position.top=o.helper?co.top:0;}
-var woset=(o.helper?self.offset.left-co.left:(self.position.left-cop.left))+self.sizeDiff.width,hoset=(o.helper?self.offset.top-co.top:self.position.top)+self.sizeDiff.height;if(woset+self.size.width>=self.parentData.width){self.size.width=self.parentData.width-woset;if(pRatio)self.size.height=self.size.width/o.aspectRatio;}
-if(hoset+self.size.height>=self.parentData.height){self.size.height=self.parentData.height-hoset;if(pRatio)self.size.width=self.size.height*o.aspectRatio;}},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cp=self.position,co=self.containerOffset,cop=self.containerPosition,ce=self.containerElement;var helper=$(self.helper),ho=helper.offset(),w=helper.innerWidth(),h=helper.innerHeight();if(o.helper&&!o.animate&&/relative/.test(ce.css('position')))
-$(this).css({left:(ho.left-co.left),top:(ho.top-co.top),width:w,height:h});if(o.helper&&!o.animate&&/static/.test(ce.css('position')))
-$(this).css({left:cop.left+(ho.left-co.left),top:cop.top+(ho.top-co.top),width:w,height:h});}});$.ui.plugin.add("resizable","grid",{resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cs=self.size,os=self.originalSize,op=self.originalPosition,a=self.axis,ratio=o._aspectRatio||e.shiftKey;o.grid=typeof o.grid=="number"?[o.grid,o.grid]:o.grid;var ox=Math.round((cs.width-os.width)/(o.grid[0]||1))*(o.grid[0]||1),oy=Math.round((cs.height-os.height)/(o.grid[1]||1))*(o.grid[1]||1);if(/^(se|s|e)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;}
-else if(/^(ne)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;}
-else if(/^(sw)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.left=op.left-ox;}
-else{self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;self.position.left=op.left-ox;}}});$.ui.plugin.add("resizable","animate",{stop:function(e,ui){var o=ui.options,self=$(this).data("resizable");var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var style={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;self.element.animate($.extend(style,top&&left?{top:top,left:left}:{}),{duration:o.animateDuration||"slow",easing:o.animateEasing||"swing",step:function(){var data={width:parseInt(self.element.css('width'),10),height:parseInt(self.element.css('height'),10),top:parseInt(self.element.css('top'),10),left:parseInt(self.element.css('left'),10)};if(pr)pr.css({width:data.width,height:data.height});self._updateCache(data);self._propagate("animate",e);}});}});$.ui.plugin.add("resizable","ghost",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize,cs=self.size;if(!pr)self.ghost=self.element.clone();else self.ghost=pr.clone();self.ghost.css({opacity:.25,display:'block',position:'relative',height:cs.height,width:cs.width,margin:0,left:0,top:0}).addClass('ui-resizable-ghost').addClass(typeof o.ghost=='string'?o.ghost:'');self.ghost.appendTo(self.helper);},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost)self.ghost.css({position:'relative',height:self.size.height,width:self.size.width});},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost&&self.helper)self.helper.get(0).removeChild(self.ghost.get(0));}});$.ui.plugin.add("resizable","alsoResize",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),_store=function(exp){$(exp).each(function(){$(this).data("resizable-alsoresize",{width:parseInt($(this).width(),10),height:parseInt($(this).height(),10),left:parseInt($(this).css('left'),10),top:parseInt($(this).css('top'),10)});});};if(typeof(o.alsoResize)=='object'){if(o.alsoResize.length){o.alsoResize=o.alsoResize[0];_store(o.alsoResize);}
-else{$.each(o.alsoResize,function(exp,c){_store(exp);});}}else{_store(o.alsoResize);}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),os=self.originalSize,op=self.originalPosition;var delta={height:(self.size.height-os.height)||0,width:(self.size.width-os.width)||0,top:(self.position.top-op.top)||0,left:(self.position.left-op.left)||0},_alsoResize=function(exp,c){$(exp).each(function(){var start=$(this).data("resizable-alsoresize"),style={},css=c&&c.length?c:['width','height','top','left'];$.each(css||['width','height','top','left'],function(i,prop){var sum=(start[prop]||0)+(delta[prop]||0);if(sum&&sum>=0)
-style[prop]=sum||null;});$(this).css(style);});};if(typeof(o.alsoResize)=='object'){$.each(o.alsoResize,function(exp,c){_alsoResize(exp,c);});}else{_alsoResize(o.alsoResize);}},stop:function(e,ui){$(this).removeData("resizable-alsoresize-start");}});})(jQuery);
\ No newline at end of file
+jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.2",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/* * jQuery UI Resizable 1.7.2
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ *	ui.core.js
+ */
+(function(c){c.widget("ui.resizable",c.extend({},c.ui.mouse,{_init:function(){var e=this,j=this.options;this.element.addClass("ui-resizable");c.extend(this,{_aspectRatio:!!(j.aspectRatio),aspectRatio:j.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:j.helper||j.ghost||j.animate?j.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){if(/relative/.test(this.element.css("position"))&&c.browser.opera){this.element.css({position:"relative",top:"auto",left:"auto"})}this.element.wrap(c('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=j.handles||(!c(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var k=this.handles.split(",");this.handles={};for(var f=0;f<k.length;f++){var h=c.trim(k[f]),d="ui-resizable-"+h;var g=c('<div class="ui-resizable-handle '+d+'"></div>');if(/sw|se|ne|nw/.test(h)){g.css({zIndex:++j.zIndex})}if("se"==h){g.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[h]=".ui-resizable-"+h;this.element.append(g)}}this._renderAxis=function(p){p=p||this.element;for(var m in this.handles){if(this.handles[m].constructor==String){this.handles[m]=c(this.handles[m],this.element).show()}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var n=c(this.handles[m],this.element),o=0;o=/sw|ne|nw|se|n|s/.test(m)?n.outerHeight():n.outerWidth();var l=["padding",/ne|nw|n/.test(m)?"Top":/se|sw|s/.test(m)?"Bottom":/^e$/.test(m)?"Right":"Left"].join("");p.css(l,o);this._proportionallyResize()}if(!c(this.handles[m]).length){continue}}};this._renderAxis(this.element);this._handles=c(".ui-resizable-handle",this.element).disableSelection();this._handles.mouseover(function(){if(!e.resizing){if(this.className){var i=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}e.axis=i&&i[1]?i[1]:"se"}});if(j.autoHide){this._handles.hide();c(this.element).addClass("ui-resizable-autohide").hover(function(){c(this).removeClass("ui-resizable-autohide");e._handles.show()},function(){if(!e.resizing){c(this).addClass("ui-resizable-autohide");e._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var d=function(f){c(f).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){d(this.element);var e=this.element;e.parent().append(this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")})).end().remove()}this.originalElement.css("resize",this.originalResizeStyle);d(this.originalElement)},_mouseCapture:function(e){var f=false;for(var d in this.handles){if(c(this.handles[d])[0]==e.target){f=true}}return this.options.disabled||!!f},_mouseStart:function(f){var i=this.options,e=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:c(document).scrollTop(),left:c(document).scrollLeft()};if(d.is(".ui-draggable")||(/absolute/).test(d.css("position"))){d.css({position:"absolute",top:e.top,left:e.left})}if(c.browser.opera&&(/relative/).test(d.css("position"))){d.css({position:"relative",top:"auto",left:"auto"})}this._renderProxy();var j=b(this.helper.css("left")),g=b(this.helper.css("top"));if(i.containment){j+=c(i.containment).scrollLeft()||0;g+=c(i.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:j,top:g};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:j,top:g};this.sizeDiff={width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:f.pageX,top:f.pageY};this.aspectRatio=(typeof i.aspectRatio=="number")?i.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);var h=c(".ui-resizable-"+this.axis).css("cursor");c("body").css("cursor",h=="auto"?this.axis+"-resize":h);d.addClass("ui-resizable-resizing");this._propagate("start",f);return true},_mouseDrag:function(d){var g=this.helper,f=this.options,l={},p=this,i=this.originalMousePosition,m=this.axis;var q=(d.pageX-i.left)||0,n=(d.pageY-i.top)||0;var h=this._change[m];if(!h){return false}var k=h.apply(this,[d,q,n]),j=c.browser.msie&&c.browser.version<7,e=this.sizeDiff;if(this._aspectRatio||d.shiftKey){k=this._updateRatio(k,d)}k=this._respectSize(k,d);this._propagate("resize",d);g.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!this._helper&&this._proportionallyResizeElements.length){this._proportionallyResize()}this._updateCache(k);this._trigger("resize",d,this.ui());return false},_mouseStop:function(g){this.resizing=false;var h=this.options,l=this;if(this._helper){var f=this._proportionallyResizeElements,d=f.length&&(/textarea/i).test(f[0].nodeName),e=d&&c.ui.hasScroll(f[0],"left")?0:l.sizeDiff.height,j=d?0:l.sizeDiff.width;var m={width:(l.size.width-j),height:(l.size.height-e)},i=(parseInt(l.element.css("left"),10)+(l.position.left-l.originalPosition.left))||null,k=(parseInt(l.element.css("top"),10)+(l.position.top-l.originalPosition.top))||null;if(!h.animate){this.element.css(c.extend(m,{top:k,left:i}))}l.helper.height(l.size.height);l.helper.width(l.size.width);if(this._helper&&!h.animate){this._proportionallyResize()}}c("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",g);if(this._helper){this.helper.remove()}return false},_updateCache:function(d){var e=this.options;this.offset=this.helper.offset();if(a(d.left)){this.position.left=d.left}if(a(d.top)){this.position.top=d.top}if(a(d.height)){this.size.height=d.height}if(a(d.width)){this.size.width=d.width}},_updateRatio:function(g,f){var h=this.options,i=this.position,e=this.size,d=this.axis;if(g.height){g.width=(e.height*this.aspectRatio)}else{if(g.width){g.height=(e.width/this.aspectRatio)}}if(d=="sw"){g.left=i.left+(e.width-g.width);g.top=null}if(d=="nw"){g.top=i.top+(e.height-g.height);g.left=i.left+(e.width-g.width)}return g},_respectSize:function(k,f){var i=this.helper,h=this.options,q=this._aspectRatio||f.shiftKey,p=this.axis,s=a(k.width)&&h.maxWidth&&(h.maxWidth<k.width),l=a(k.height)&&h.maxHeight&&(h.maxHeight<k.height),g=a(k.width)&&h.minWidth&&(h.minWidth>k.width),r=a(k.height)&&h.minHeight&&(h.minHeight>k.height);if(g){k.width=h.minWidth}if(r){k.height=h.minHeight}if(s){k.width=h.maxWidth}if(l){k.height=h.maxHeight}var e=this.originalPosition.left+this.originalSize.width,n=this.position.top+this.size.height;var j=/sw|nw|w/.test(p),d=/nw|ne|n/.test(p);if(g&&j){k.left=e-h.minWidth}if(s&&j){k.left=e-h.maxWidth}if(r&&d){k.top=n-h.minHeight}if(l&&d){k.top=n-h.maxHeight}var m=!k.width&&!k.height;if(m&&!k.left&&k.top){k.top=null}else{if(m&&!k.top&&k.left){k.left=null}}return k},_proportionallyResize:function(){var j=this.options;if(!this._proportionallyResizeElements.length){return}var f=this.helper||this.element;for(var e=0;e<this._proportionallyResizeElements.length;e++){var g=this._proportionallyResizeElements[e];if(!this.borderDif){var d=[g.css("borderTopWidth"),g.css("borderRightWidth"),g.css("borderBottomWidth"),g.css("borderLeftWidth")],h=[g.css("paddingTop"),g.css("paddingRight"),g.css("paddingBottom"),g.css("paddingLeft")];this.borderDif=c.map(d,function(k,m){var l=parseInt(k,10)||0,n=parseInt(h[m],10)||0;return l+n})}if(c.browser.msie&&!(!(c(f).is(":hidden")||c(f).parents(":hidden").length))){continue}g.css({height:(f.height()-this.borderDif[0]-this.borderDif[2])||0,width:(f.width()-this.borderDif[1]-this.borderDif[3])||0})}},_renderProxy:function(){var e=this.element,h=this.options;this.elementOffset=e.offset();if(this._helper){this.helper=this.helper||c('<div style="overflow:hidden;"></div>');var d=c.browser.msie&&c.browser.version<7,f=(d?1:0),g=(d?2:-1);this.helper.addClass(this._helper).css({width:this.element.outerWidth()+g,height:this.element.outerHeight()+g,position:"absolute",left:this.elementOffset.left-f+"px",top:this.elementOffset.top-f+"px",zIndex:++h.zIndex});this.helper.appendTo("body").disableSelection()}else{this.helper=this.element}},_change:{e:function(f,e,d){return{width:this.originalSize.width+e}},w:function(g,e,d){var i=this.options,f=this.originalSize,h=this.originalPosition;return{left:h.left+e,width:f.width-e}},n:function(g,e,d){var i=this.options,f=this.originalSize,h=this.originalPosition;return{top:h.top+d,height:f.height-d}},s:function(f,e,d){return{height:this.originalSize.height+d}},se:function(f,e,d){return c.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[f,e,d]))},sw:function(f,e,d){return c.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[f,e,d]))},ne:function(f,e,d){return c.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[f,e,d]))},nw:function(f,e,d){return c.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[f,e,d]))}},_propagate:function(e,d){c.ui.plugin.call(this,e,[d,this.ui()]);(e!="resize"&&this._trigger(e,d,this.ui()))},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}));c.extend(c.ui.resizable,{version:"1.7.2",eventPrefix:"resize",defaults:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,cancel:":input,option",containment:false,delay:0,distance:1,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1000}});c.ui.plugin.add("resizable","alsoResize",{start:function(e,f){var d=c(this).data("resizable"),g=d.options;_store=function(h){c(h).each(function(){c(this).data("resizable-alsoresize",{width:parseInt(c(this).width(),10),height:parseInt(c(this).height(),10),left:parseInt(c(this).css("left"),10),top:parseInt(c(this).css("top"),10)})})};if(typeof(g.alsoResize)=="object"&&!g.alsoResize.parentNode){if(g.alsoResize.length){g.alsoResize=g.alsoResize[0];_store(g.alsoResize)}else{c.each(g.alsoResize,function(h,i){_store(h)})}}else{_store(g.alsoResize)}},resize:function(f,h){var e=c(this).data("resizable"),i=e.options,g=e.originalSize,k=e.originalPosition;var j={height:(e.size.height-g.height)||0,width:(e.size.width-g.width)||0,top:(e.position.top-k.top)||0,left:(e.position.left-k.left)||0},d=function(l,m){c(l).each(function(){var p=c(this),q=c(this).data("resizable-alsoresize"),o={},n=m&&m.length?m:["width","height","top","left"];c.each(n||["width","height","top","left"],function(r,t){var s=(q[t]||0)+(j[t]||0);if(s&&s>=0){o[t]=s||null}});if(/relative/.test(p.css("position"))&&c.browser.opera){e._revertToRelativePosition=true;p.css({position:"absolute",top:"auto",left:"auto"})}p.css(o)})};if(typeof(i.alsoResize)=="object"&&!i.alsoResize.nodeType){c.each(i.alsoResize,function(l,m){d(l,m)})}else{d(i.alsoResize)}},stop:function(e,f){var d=c(this).data("resizable");if(d._revertToRelativePosition&&c.browser.opera){d._revertToRelativePosition=false;el.css({position:"relative"})}c(this).removeData("resizable-alsoresize-start")}});c.ui.plugin.add("resizable","animate",{stop:function(h,m){var n=c(this).data("resizable"),i=n.options;var g=n._proportionallyResizeElements,d=g.length&&(/textarea/i).test(g[0].nodeName),e=d&&c.ui.hasScroll(g[0],"left")?0:n.sizeDiff.height,k=d?0:n.sizeDiff.width;var f={width:(n.size.width-k),height:(n.size.height-e)},j=(parseInt(n.element.css("left"),10)+(n.position.left-n.originalPosition.left))||null,l=(parseInt(n.element.css("top"),10)+(n.position.top-n.originalPosition.top))||null;n.element.animate(c.extend(f,l&&j?{top:l,left:j}:{}),{duration:i.animateDuration,easing:i.animateEasing,step:function(){var o={width:parseInt(n.element.css("width"),10),height:parseInt(n.element.css("height"),10),top:parseInt(n.element.css("top"),10),left:parseInt(n.element.css("left"),10)};if(g&&g.length){c(g[0]).css({width:o.width,height:o.height})}n._updateCache(o);n._propagate("resize",h)}})}});c.ui.plugin.add("resizable","containment",{start:function(e,q){var s=c(this).data("resizable"),i=s.options,k=s.element;var f=i.containment,j=(f instanceof c)?f.get(0):(/parent/.test(f))?k.parent().get(0):f;if(!j){return}s.containerElement=c(j);if(/document/.test(f)||f==document){s.containerOffset={left:0,top:0};s.containerPosition={left:0,top:0};s.parentData={element:c(document),left:0,top:0,width:c(document).width(),height:c(document).height()||document.body.parentNode.scrollHeight}}else{var m=c(j),h=[];c(["Top","Right","Left","Bottom"]).each(function(p,o){h[p]=b(m.css("padding"+o))});s.containerOffset=m.offset();s.containerPosition=m.position();s.containerSize={height:(m.innerHeight()-h[3]),width:(m.innerWidth()-h[1])};var n=s.containerOffset,d=s.containerSize.height,l=s.containerSize.width,g=(c.ui.hasScroll(j,"left")?j.scrollWidth:l),r=(c.ui.hasScroll(j)?j.scrollHeight:d);s.parentData={element:j,left:n.left,top:n.top,width:g,height:r}}},resize:function(f,p){var s=c(this).data("resizable"),h=s.options,e=s.containerSize,n=s.containerOffset,l=s.size,m=s.position,q=s._aspectRatio||f.shiftKey,d={top:0,left:0},g=s.containerElement;if(g[0]!=document&&(/static/).test(g.css("position"))){d=n}if(m.left<(s._helper?n.left:0)){s.size.width=s.size.width+(s._helper?(s.position.left-n.left):(s.position.left-d.left));if(q){s.size.height=s.size.width/h.aspectRatio}s.position.left=h.helper?n.left:0}if(m.top<(s._helper?n.top:0)){s.size.height=s.size.height+(s._helper?(s.position.top-n.top):s.position.top);if(q){s.size.width=s.size.height*h.aspectRatio}s.position.top=s._helper?n.top:0}s.offset.left=s.parentData.left+s.position.left;s.offset.top=s.parentData.top+s.position.top;var k=Math.abs((s._helper?s.offset.left-d.left:(s.offset.left-d.left))+s.sizeDiff.width),r=Math.abs((s._helper?s.offset.top-d.top:(s.offset.top-n.top))+s.sizeDiff.height);var j=s.containerElement.get(0)==s.element.parent().get(0),i=/relative|absolute/.test(s.containerElement.css("position"));if(j&&i){k-=s.parentData.left}if(k+s.size.width>=s.parentData.width){s.size.width=s.parentData.width-k;if(q){s.size.height=s.size.width/s.aspectRatio}}if(r+s.size.height>=s.parentData.height){s.size.height=s.parentData.height-r;if(q){s.size.width=s.size.height*s.aspectRatio}}},stop:function(e,m){var p=c(this).data("resizable"),f=p.options,k=p.position,l=p.containerOffset,d=p.containerPosition,g=p.containerElement;var i=c(p.helper),q=i.offset(),n=i.outerWidth()-p.sizeDiff.width,j=i.outerHeight()-p.sizeDiff.height;if(p._helper&&!f.animate&&(/relative/).test(g.css("position"))){c(this).css({left:q.left-d.left-l.left,width:n,height:j})}if(p._helper&&!f.animate&&(/static/).test(g.css("position"))){c(this).css({left:q.left-d.left-l.left,width:n,height:j})}}});c.ui.plugin.add("resizable","ghost",{start:function(f,g){var d=c(this).data("resizable"),h=d.options,e=d.size;d.ghost=d.originalElement.clone();d.ghost.css({opacity:0.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof h.ghost=="string"?h.ghost:"");d.ghost.appendTo(d.helper)},resize:function(e,f){var d=c(this).data("resizable"),g=d.options;if(d.ghost){d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})}},stop:function(e,f){var d=c(this).data("resizable"),g=d.options;if(d.ghost&&d.helper){d.helper.get(0).removeChild(d.ghost.get(0))}}});c.ui.plugin.add("resizable","grid",{resize:function(d,l){var n=c(this).data("resizable"),g=n.options,j=n.size,h=n.originalSize,i=n.originalPosition,m=n.axis,k=g._aspectRatio||d.shiftKey;g.grid=typeof g.grid=="number"?[g.grid,g.grid]:g.grid;var f=Math.round((j.width-h.width)/(g.grid[0]||1))*(g.grid[0]||1),e=Math.round((j.height-h.height)/(g.grid[1]||1))*(g.grid[1]||1);if(/^(se|s|e)$/.test(m)){n.size.width=h.width+f;n.size.height=h.height+e}else{if(/^(ne)$/.test(m)){n.size.width=h.width+f;n.size.height=h.height+e;n.position.top=i.top-e}else{if(/^(sw)$/.test(m)){n.size.width=h.width+f;n.size.height=h.height+e;n.position.left=i.left-f}else{n.size.width=h.width+f;n.size.height=h.height+e;n.position.top=i.top-e;n.position.left=i.left-f}}}}});var b=function(d){return parseInt(d,10)||0};var a=function(d){return !isNaN(parseInt(d,10))}})(jQuery);;
diff --git a/tools/droiddoc/templates/assets/navtree.js b/tools/droiddoc/templates/assets/navtree.js
index f48e1dc..33a4f81 100644
--- a/tools/droiddoc/templates/assets/navtree.js
+++ b/tools/droiddoc/templates/assets/navtree.js
@@ -104,7 +104,8 @@
   var file = "";
   if (toroot.substr(0, 1) == "/") {
     if (full.substr(0, toroot.length) == toroot) {
-      return full.substr(toroot.length);
+      var basePath = getBaseUri(full);
+      return basePath.substring(toroot.length);
     } else {
       // the file isn't under toroot.  Fail.
       return null;
@@ -144,8 +145,20 @@
   return null;
 }
 
+function load_navtree_data(toroot) {
+  var navtreeData = document.createElement("script");
+  navtreeData.setAttribute("type","text/javascript");
+  navtreeData.setAttribute("src", toroot+"navtree_data.js");
+  $("head").append($(navtreeData));
+} 
+
+function init_default_navtree(toroot) {
+  load_navtree_data(toroot);
+  init_navtree("nav-tree", toroot, NAVTREE_DATA);
+}
+
 function init_navtree(navtree_id, toroot, root_nodes)
-{
+{  
   var me = new Object();
   me.toroot = toroot;
   me.node = new Object();
diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs
index 1077886..41d34dd 100644
--- a/tools/droiddoc/templates/class.cs
+++ b/tools/droiddoc/templates/class.cs
@@ -180,6 +180,7 @@
 <?cs /if ?>
 
 <?cs call:see_also_tags(class.seeAlso) ?>
+<?cs call:since_tags(class) ?>
 
 </div><!-- jd-descr -->
 
diff --git a/tools/droiddoc/templates/head_tag.cs b/tools/droiddoc/templates/head_tag.cs
index 61559e1..f721403 100644
--- a/tools/droiddoc/templates/head_tag.cs
+++ b/tools/droiddoc/templates/head_tag.cs
@@ -11,14 +11,12 @@
 <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" /><?cs 
 /if ?>
 <script src="<?cs var:toroot ?>assets/search_autocomplete.js" type="text/javascript"></script>
-<script src="<?cs var:toroot ?>reference/lists.js" type="text/javascript"></script>
 <script src="<?cs var:toroot ?>assets/jquery-resizable.min.js" type="text/javascript"></script>
 <script src="<?cs var:toroot ?>assets/android-developer-docs.js" type="text/javascript"></script>
 <script type="text/javascript">
   setToRoot("<?cs var:toroot ?>");
 </script><?cs 
 if:reference ?>
-<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script>
 <script src="<?cs var:toroot ?>assets/navtree.js" type="text/javascript"></script><?cs 
 /if ?>
 <noscript>
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
index a2306a0..1ca2f8b 100644
--- a/tools/droiddoc/templates/macros.cs
+++ b/tools/droiddoc/templates/macros.cs
@@ -115,9 +115,15 @@
   /if ?>
 <?cs /def ?>
 
+<?cs # print the Since: section ?><?cs
+def:since_tags(obj) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Since <?cs var:obj.since ?></h5>
+  </div>
+<?cs /def ?>
 
 <?cs # Print the long-form description for something.
-       Uses the following fields: deprecated descr seeAlso ?><?cs 
+       Uses the following fields: deprecated descr seeAlso since ?><?cs
 def:description(obj) ?><?cs 
   call:deprecated_warning(obj) ?>
   <div class="jd-tagdata jd-tagdescr"><p><?cs call:tag_list(obj.descr) ?></p></div><?cs 
@@ -165,6 +171,7 @@
   </div><?cs 
   /if ?><?cs 
   call:see_also_tags(obj.seeAlso) ?><?cs 
+  call:since_tags(obj) ?><?cs
 /def ?>
 
 <?cs # A table of links to classes with descriptions, as in a package file or the nested classes ?><?cs
diff --git a/tools/droiddoc/templates/package-descr.cs b/tools/droiddoc/templates/package-descr.cs
index 385ce23..112db4b 100644
--- a/tools/droiddoc/templates/package-descr.cs
+++ b/tools/droiddoc/templates/package-descr.cs
@@ -21,6 +21,7 @@
 <div class="jd-descr">
 <p><?cs call:tag_list(package.descr) ?></p>
 </div>
+<?cs call:since_tags(package) ?>
 
 <?cs include:"footer.cs" ?>
 </div><!-- end jd-content -->
diff --git a/tools/droiddoc/test/stubs/func.sh b/tools/droiddoc/test/stubs/func.sh
index 1ad4bd5..ea4fe75 100644
--- a/tools/droiddoc/test/stubs/func.sh
+++ b/tools/droiddoc/test/stubs/func.sh
@@ -26,21 +26,22 @@
     STUBS_DIR=$3
 
     OBJ_DIR=out/stubs/$ID
+    PLATFORM=${HOST_OS}-${HOST_ARCH}
 
     rm -rf $OBJ_DIR &> /dev/null
     mkdir -p $OBJ_DIR
 
     find $SRC_DIR -name '*.java' > $OBJ_DIR/javadoc-src-list
     ( \
-        LD_LIBRARY_PATH=out/host/darwin-x86/lib \
+        LD_LIBRARY_PATH=out/host/$PLATFORM/lib \
         javadoc \
             \@$OBJ_DIR/javadoc-src-list \
             -J-Xmx512m \
-            -J-Djava.library.path=out/host/darwin-x86/lib \
+            -J-Djava.library.path=out/host/$PLATFORM/lib \
              \
             -quiet \
             -doclet DroidDoc \
-            -docletpath out/host/darwin-x86/framework/clearsilver.jar:out/host/darwin-x86/framework/droiddoc.jar \
+            -docletpath out/host/$PLATFORM/framework/clearsilver.jar:out/host/$PLATFORM/framework/droiddoc.jar:out/host/$PLATFORM/framework/apicheck.jar \
             -templatedir tools/droiddoc/templates \
             -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
             -sourcepath $SRC_DIR:out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
diff --git a/tools/droiddoc/test/stubs/run.sh b/tools/droiddoc/test/stubs/run.sh
index f237a7d..2ea15a6 100755
--- a/tools/droiddoc/test/stubs/run.sh
+++ b/tools/droiddoc/test/stubs/run.sh
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-DIR=tools/droiddoc/test/stubs
+DIR=build/tools/droiddoc/test/stubs
 
 pushd $TOP
 
diff --git a/tools/releasetools/amend_generator.py b/tools/releasetools/amend_generator.py
index 8341599..3e8af13 100644
--- a/tools/releasetools/amend_generator.py
+++ b/tools/releasetools/amend_generator.py
@@ -94,13 +94,13 @@
     for i in sha1:
       out.append(" " + i)
     self.script.append("".join(out))
-    self.included_files.add("applypatch")
+    self.included_files.add(("applypatch_static", "applypatch"))
 
   def CacheFreeSpaceCheck(self, amount):
     """Check that there's at least 'amount' space that can be made
     available on /cache."""
     self.script.append("run_program PACKAGE:applypatch -s %d" % (amount,))
-    self.included_files.add("applypatch")
+    self.included_files.add(("applypatch_static", "applypatch"))
 
   def Mount(self, kind, what, path):
     # no-op; amend uses it's 'roots' system to automatically mount
@@ -155,7 +155,7 @@
          (srcfile, tgtfile, tgtsha1, tgtsize)) +
         " ".join(["%s:%s" % patchpairs[i:i+2]
                   for i in range(0, len(patchpairs), 2)]))
-    self.included_files.add("applypatch")
+    self.included_files.add(("applypatch_static", "applypatch"))
 
   def WriteFirmwareImage(self, kind, fn):
     """Arrange to update the given firmware image (kind must be
@@ -195,11 +195,16 @@
     common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-script",
                        "\n".join(self.script) + "\n")
     for i in self.included_files:
+      if isinstance(i, tuple):
+        sourcefn, targetfn = i
+      else:
+        sourcefn = i
+        targetfn = i
       try:
         if input_path is None:
-          data = input_zip.read(os.path.join("OTA/bin", i))
+          data = input_zip.read(os.path.join("OTA/bin", sourcefn))
         else:
-          data = open(os.path.join(input_path, i)).read()
-        common.ZipWriteStr(output_zip, i, data, perms=0755)
+          data = open(os.path.join(input_path, sourcefn)).read()
+        common.ZipWriteStr(output_zip, targetfn, data, perms=0755)
       except (IOError, KeyError), e:
         raise ExternalError("unable to include binary %s: %s" % (i, e))
diff --git a/tools/releasetools/both_generator.py b/tools/releasetools/both_generator.py
new file mode 100644
index 0000000..df2a659
--- /dev/null
+++ b/tools/releasetools/both_generator.py
@@ -0,0 +1,60 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import edify_generator
+import amend_generator
+
+class BothGenerator(object):
+  def __init__(self, version):
+    self.version = version
+    self.edify = edify_generator.EdifyGenerator(version)
+    self.amend = amend_generator.AmendGenerator()
+
+  def MakeTemporary(self):
+    x = BothGenerator(self.version)
+    x.edify = self.edify.MakeTemporary()
+    x.amend = self.amend.MakeTemporary()
+    return x
+
+  def AppendScript(self, other):
+    self.edify.AppendScript(other.edify)
+    self.amend.AppendScript(other.amend)
+
+  def _DoBoth(self, name, *args):
+    getattr(self.edify, name)(*args)
+    getattr(self.amend, name)(*args)
+
+  def AssertSomeFingerprint(self, *a): self._DoBoth("AssertSomeFingerprint", *a)
+  def AssertOlderBuild(self, *a): self._DoBoth("AssertOlderBuild", *a)
+  def AssertDevice(self, *a): self._DoBoth("AssertDevice", *a)
+  def AssertSomeBootloader(self, *a): self._DoBoth("AssertSomeBootloader", *a)
+  def ShowProgress(self, *a): self._DoBoth("ShowProgress", *a)
+  def PatchCheck(self, *a): self._DoBoth("PatchCheck", *a)
+  def CacheFreeSpaceCheck(self, *a): self._DoBoth("CacheFreeSpaceCheck", *a)
+  def Mount(self, *a): self._DoBoth("Mount", *a)
+  def UnpackPackageDir(self, *a): self._DoBoth("UnpackPackageDir", *a)
+  def Comment(self, *a): self._DoBoth("Comment", *a)
+  def Print(self, *a): self._DoBoth("Print", *a)
+  def FormatPartition(self, *a): self._DoBoth("FormatPartition", *a)
+  def DeleteFiles(self, *a): self._DoBoth("DeleteFiles", *a)
+  def ApplyPatch(self, *a): self._DoBoth("ApplyPatch", *a)
+  def WriteFirmwareImage(self, *a): self._DoBoth("WriteFirmwareImage", *a)
+  def WriteRawImage(self, *a): self._DoBoth("WriteRawImage", *a)
+  def SetPermissions(self, *a): self._DoBoth("SetPermissions", *a)
+  def SetPermissionsRecursive(self, *a): self._DoBoth("SetPermissionsRecursive", *a)
+  def MakeSymlinks(self, *a): self._DoBoth("MakeSymlinks", *a)
+  def AppendExtra(self, *a): self._DoBoth("AppendExtra", *a)
+
+  def AddToZip(self, input_zip, output_zip, input_path=None):
+    self._DoBoth("AddToZip", input_zip, output_zip, input_path)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index a07ff7c..4174db5 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -15,6 +15,7 @@
 import errno
 import getopt
 import getpass
+import imp
 import os
 import re
 import shutil
@@ -33,7 +34,7 @@
 OPTIONS.max_image_size = {}
 OPTIONS.verbose = False
 OPTIONS.tempfiles = []
-
+OPTIONS.device_specific = None
 
 class ExternalError(RuntimeError): pass
 
@@ -245,6 +246,10 @@
       Prepend <dir>/bin to the list of places to search for binaries
       run by this script, and expect to find jars in <dir>/framework.
 
+  -s  (--device_specific) <file>
+      Path to the python module containing device-specific
+      releasetools code.
+
   -v  (--verbose)
       Show command lines being executed.
 
@@ -269,8 +274,9 @@
 
   try:
     opts, args = getopt.getopt(
-        argv, "hvp:" + extra_opts,
-        ["help", "verbose", "path="] + list(extra_long_opts))
+        argv, "hvp:s:" + extra_opts,
+        ["help", "verbose", "path=", "device_specific="] +
+          list(extra_long_opts))
   except getopt.GetoptError, err:
     Usage(docstring)
     print "**", str(err), "**"
@@ -286,6 +292,8 @@
       OPTIONS.verbose = True
     elif o in ("-p", "--path"):
       OPTIONS.search_path = a
+    elif o in ("-s", "--device_specific"):
+      OPTIONS.device_specific = a
     else:
       if extra_option_handler is None or not extra_option_handler(o, a):
         assert False, "unknown option \"%s\"" % (o,)
@@ -410,3 +418,68 @@
   zinfo.compress_type = zip.compression
   zinfo.external_attr = perms << 16
   zip.writestr(zinfo, data)
+
+
+class DeviceSpecificParams(object):
+  module = None
+  def __init__(self, **kwargs):
+    """Keyword arguments to the constructor become attributes of this
+    object, which is passed to all functions in the device-specific
+    module."""
+    for k, v in kwargs.iteritems():
+      setattr(self, k, v)
+
+    if self.module is None:
+      path = OPTIONS.device_specific
+      if path is None: return
+      try:
+        if os.path.isdir(path):
+          info = imp.find_module("releasetools", [path])
+        else:
+          d, f = os.path.split(path)
+          b, x = os.path.splitext(f)
+          if x == ".py":
+            f = b
+          info = imp.find_module(f, [d])
+        self.module = imp.load_module("device_specific", *info)
+      except ImportError:
+        print "unable to load device-specific module; assuming none"
+
+  def _DoCall(self, function_name, *args, **kwargs):
+    """Call the named function in the device-specific module, passing
+    the given args and kwargs.  The first argument to the call will be
+    the DeviceSpecific object itself.  If there is no module, or the
+    module does not define the function, return the value of the
+    'default' kwarg (which itself defaults to None)."""
+    if self.module is None or not hasattr(self.module, function_name):
+      return kwargs.get("default", None)
+    return getattr(self.module, function_name)(*((self,) + args), **kwargs)
+
+  def FullOTA_Assertions(self):
+    """Called after emitting the block of assertions at the top of a
+    full OTA package.  Implementations can add whatever additional
+    assertions they like."""
+    return self._DoCall("FullOTA_Assertions")
+
+  def FullOTA_InstallEnd(self):
+    """Called at the end of full OTA installation; typically this is
+    used to install the image for the device's baseband processor."""
+    return self._DoCall("FullOTA_InstallEnd")
+
+  def IncrementalOTA_Assertions(self):
+    """Called after emitting the block of assertions at the top of an
+    incremental OTA package.  Implementations can add whatever
+    additional assertions they like."""
+    return self._DoCall("IncrementalOTA_Assertions")
+
+  def IncrementalOTA_VerifyEnd(self):
+    """Called at the end of the verification phase of incremental OTA
+    installation; additional checks can be placed here to abort the
+    script before any changes are made."""
+    return self._DoCall("IncrementalOTA_VerifyEnd")
+
+  def IncrementalOTA_InstallEnd(self):
+    """Called at the end of incremental OTA installation; typically
+    this is used to install the image for the device's baseband
+    processor."""
+    return self._DoCall("IncrementalOTA_InstallEnd")
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index ea4c752..58177b4 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -69,6 +69,7 @@
 import common
 import amend_generator
 import edify_generator
+import both_generator
 
 OPTIONS = common.OPTIONS
 OPTIONS.package_key = "build/target/product/security/testkey"
@@ -276,12 +277,6 @@
   common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw)
 
 
-def FixPermissions(script):
-  Item.GetMetadata()
-  root = Item.Get("system")
-  root.SetPermissions(script)
-
-
 def AppendAssertions(script, input_zip):
   device = GetBuildProp("ro.product.device", input_zip)
   script.AssertDevice(device)
@@ -290,31 +285,74 @@
   m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info)
   if m:
     bootloaders = m.group(1).split("|")
-    script.AssertSomeBootloader(*bootloaders)
+    if "*" not in bootloaders:
+      script.AssertSomeBootloader(*bootloaders)
+
+
+def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
+  """Generate a binary patch that creates the recovery image starting
+  with the boot image.  (Most of the space in these images is just the
+  kernel, which is identical for the two, so the resulting patch
+  should be efficient.)  Add it to the output zip, along with a shell
+  script that is run from init.rc on first boot to actually do the
+  patching and install the new recovery image.
+
+  recovery_img and boot_img should be File objects for the
+  corresponding images.
+
+  Returns an Item for the shell script, which must be made
+  executable.
+  """
+
+  patch = Difference(recovery_img, boot_img, "imgdiff")
+  common.ZipWriteStr(output_zip, "system/recovery-from-boot.p", patch)
+  Item.Get("system/recovery-from-boot.p", dir=False)
+
+  # Images with different content will have a different first page, so
+  # we check to see if this recovery has already been installed by
+  # testing just the first 2k.
+  HEADER_SIZE = 2048
+  header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
+  sh = """#!/system/bin/sh
+if ! applypatch -c MTD:recovery:%(header_size)d:%(header_sha1)s; then
+  log -t recovery "Installing new recovery image"
+  applypatch MTD:boot:%(boot_size)d:%(boot_sha1)s MTD:recovery %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
+else
+  log -t recovery "Recovery image already installed"
+fi
+""" % { 'boot_size': boot_img.size,
+        'boot_sha1': boot_img.sha1,
+        'header_size': HEADER_SIZE,
+        'header_sha1': header_sha1,
+        'recovery_size': recovery_img.size,
+        'recovery_sha1': recovery_img.sha1 }
+  common.ZipWriteStr(output_zip, "system/etc/install-recovery.sh", sh)
+  return Item.Get("system/etc/install-recovery.sh", dir=False)
 
 
 def WriteFullOTAPackage(input_zip, output_zip):
-  if OPTIONS.script_mode in ("amend", "auto"):
+  if OPTIONS.script_mode == "auto":
+    script = both_generator.BothGenerator(2)
+  elif OPTIONS.script_mode == "amend":
     script = amend_generator.AmendGenerator()
   else:
     # TODO: how to determine this?  We don't know what version it will
     # be installed on top of.  For now, we expect the API just won't
     # change very often.
-    script = edify_generator.EdifyGenerator(1)
+    script = edify_generator.EdifyGenerator(2)
+
+  device_specific = common.DeviceSpecificParams(
+      input_zip=input_zip,
+      output_zip=output_zip,
+      script=script,
+      input_tmp=OPTIONS.input_tmp)
 
   if not OPTIONS.omit_prereq:
     ts = GetBuildProp("ro.build.date.utc", input_zip)
     script.AssertOlderBuild(ts)
 
   AppendAssertions(script, input_zip)
-
-  script.ShowProgress(0.1, 0)
-
-  try:
-    common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
-    script.WriteFirmwareImage("radio", "radio.img")
-  except KeyError:
-    print "warning: no radio image in input target_files; not flashing radio"
+  device_specific.FullOTA_Assertions()
 
   script.ShowProgress(0.5, 0)
 
@@ -328,18 +366,31 @@
   symlinks = CopySystemFiles(input_zip, output_zip)
   script.MakeSymlinks(symlinks)
 
-  if common.BuildAndAddBootableImage(
-      os.path.join(OPTIONS.input_tmp, "RECOVERY"),
-      "system/recovery.img", output_zip):
-    Item.Get("system/recovery.img", dir=False)
+  boot_img = File("boot.img", common.BuildBootableImage(
+      os.path.join(OPTIONS.input_tmp, "BOOT")))
+  recovery_img = File("recovery.img", common.BuildBootableImage(
+      os.path.join(OPTIONS.input_tmp, "RECOVERY")))
+  i = MakeRecoveryPatch(output_zip, recovery_img, boot_img)
 
-  FixPermissions(script)
+  Item.GetMetadata()
 
-  common.AddBoot(output_zip)
+  # GetMetadata uses the data in android_filesystem_config.h to assign
+  # the uid/gid/mode of all files.  We want to override that for the
+  # recovery patching shell script to make it executable.
+  i.uid = 0
+  i.gid = 0
+  i.mode = 0544
+  Item.Get("system").SetPermissions(script)
+
+  common.CheckSize(boot_img.data, "boot.img")
+  common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
   script.ShowProgress(0.2, 0)
 
-  script.WriteRawImage("boot", "boot.img")
   script.ShowProgress(0.2, 10)
+  script.WriteRawImage("boot", "boot.img")
+
+  script.ShowProgress(0.1, 0)
+  device_specific.FullOTA_InstallEnd()
 
   if OPTIONS.extra_script is not None:
     script.AppendExtra(OPTIONS.extra_script)
@@ -455,6 +506,12 @@
   else:
     raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
 
+  device_specific = common.DeviceSpecificParams(
+      source_zip=source_zip,
+      target_zip=target_zip,
+      output_zip=output_zip,
+      script=script)
+
   print "Loading target..."
   target_data = LoadSystemFiles(target_zip)
   print "Loading source..."
@@ -519,19 +576,15 @@
       os.path.join(OPTIONS.target_tmp, "RECOVERY")))
   updating_recovery = (source_recovery.data != target_recovery.data)
 
-  source_radio = source_zip.read("RADIO/image")
-  target_radio = target_zip.read("RADIO/image")
-  updating_radio = (source_radio != target_radio)
-
-  # The last 0.1 is reserved for creating symlinks, fixing
-  # permissions, and writing the boot image (if necessary).
-  progress_bar_total = 1.0
+  # We reserve the last 0.3 of the progress bar for the
+  # device-specific IncrementalOTA_InstallEnd() call at the end, which
+  # will typically install a radio image.
+  progress_bar_total = 0.7
   if updating_boot:
     progress_bar_total -= 0.1
-  if updating_radio:
-    progress_bar_total -= 0.3
 
   AppendAssertions(script, target_zip)
+  device_specific.IncrementalOTA_Assertions()
 
   script.Print("Verifying current system...")
 
@@ -546,17 +599,6 @@
 
     script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
 
-  if updating_recovery:
-    d = Difference(target_recovery, source_recovery, "imgdiff")
-    print "recovery  target: %d  source: %d  diff: %d" % (
-        target_recovery.size, source_recovery.size, len(d))
-
-    common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
-
-    script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
-                      (source_recovery.size, source_recovery.sha1,
-                       target_recovery.size, target_recovery.sha1))
-
   if updating_boot:
     d = Difference(target_boot, source_boot, "imgdiff")
     print "boot      target: %d  source: %d  diff: %d" % (
@@ -573,6 +615,8 @@
     script.Print("Unpacking patches...")
     script.UnpackPackageDir("patch", "/tmp/patchtmp")
 
+  device_specific.IncrementalOTA_VerifyEnd()
+
   script.Comment("---- start making changes here ----")
 
   if OPTIONS.wipe_user_data:
@@ -600,28 +644,23 @@
     print "boot image unchanged; skipping."
 
   if updating_recovery:
-    # Produce /system/recovery.img by applying a patch to the current
-    # contents of the recovery partition.
-    script.Print("Patching recovery image...")
-    script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
-                      % (source_recovery.size, source_recovery.sha1,
-                         target_recovery.size, target_recovery.sha1),
-                      "/system/recovery.img",
-                      target_recovery.size, target_recovery.sha1,
-                      source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
-    print "recovery image changed; including."
+    # Is it better to generate recovery as a patch from the current
+    # boot image, or from the previous recovery image?  For large
+    # updates with significant kernel changes, probably the former.
+    # For small updates where the kernel hasn't changed, almost
+    # certainly the latter.  We pick the first option.  Future
+    # complicated schemes may let us effectively use both.
+    #
+    # A wacky possibility: as long as there is room in the boot
+    # partition, include the binaries and image files from recovery in
+    # the boot image (though not in the ramdisk) so they can be used
+    # as fodder for constructing the recovery image.
+    recovery_sh_item = MakeRecoveryPatch(output_zip,
+                                         target_recovery, target_boot)
+    print "recovery image changed; including as patch from boot."
   else:
     print "recovery image unchanged; skipping."
 
-  if updating_radio:
-    script.ShowProgress(0.3, 10)
-    script.Print("Writing radio image...")
-    script.WriteFirmwareImage("radio", "radio.img")
-    common.ZipWriteStr(output_zip, "radio.img", target_radio)
-    print "radio image changed; including."
-  else:
-    print "radio image unchanged; skipping."
-
   script.Print("Patching system files...")
   pb_apply = progress_bar_total * 0.7 * \
              (total_patched_size /
@@ -637,7 +676,12 @@
 
   target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
   temp_script = script.MakeTemporary()
-  FixPermissions(temp_script)
+  Item.GetMetadata()
+  if updating_recovery:
+    recovery_sh_item.uid = 0
+    recovery_sh_item.gid = 0
+    recovery_sh_item.mode = 0544
+  Item.Get("system").SetPermissions(temp_script)
 
   # Note that this call will mess up the tree of Items, so make sure
   # we're done with it.
@@ -661,7 +705,7 @@
     script.Print("Unpacking new files...")
     script.UnpackPackageDir("system", "/system")
 
-  script.Print("Finishing up...")
+  script.Print("Symlinks and permissions...")
 
   # Create all the symlinks that don't already exist, or point to
   # somewhere different than what we want.  Delete each symlink before
@@ -680,6 +724,10 @@
   # permissions.
   script.AppendScript(temp_script)
 
+  # Write the radio image, if necessary.
+  script.ShowProgress(0.3, 10)
+  device_specific.IncrementalOTA_InstallEnd()
+
   if OPTIONS.extra_script is not None:
     scirpt.AppendExtra(OPTIONS.extra_script)
 
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index 6dd8ede..5153398 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -20,10 +20,6 @@
 
 Usage:  sign_target_files_apks [flags] input_target_files output_target_files
 
-  -s  (--signapk_jar)  <path>
-      Path of the signapks.jar file used to sign an individual APK
-      file.
-
   -e  (--extra_apks)  <name,name,...=key>
       Add extra APK name/key pairs as though they appeared in
       apkcerts.txt (so mappings specified by -k and -d are applied).
@@ -302,9 +298,7 @@
 def main(argv):
 
   def option_handler(o, a):
-    if o in ("-s", "--signapk_jar"):
-      OPTIONS.signapk_jar = a
-    elif o in ("-e", "--extra_apks"):
+    if o in ("-e", "--extra_apks"):
       names, key = a.split("=")
       names = names.split(",")
       for n in names:
@@ -334,9 +328,8 @@
     return True
 
   args = common.ParseOptions(argv, __doc__,
-                             extra_opts="s:e:d:k:ot:",
-                             extra_long_opts=["signapk_jar=",
-                                              "extra_apks=",
+                             extra_opts="e:d:k:ot:",
+                             extra_long_opts=["extra_apks=",
                                               "default_key_mappings=",
                                               "key_mapping=",
                                               "replace_ota_keys",
diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java
index fb55028..caf7935 100644
--- a/tools/signapk/SignApk.java
+++ b/tools/signapk/SignApk.java
@@ -304,9 +304,14 @@
         pkcs7.encodeSignedData(out);
     }
 
-    /** Copy all the files in a manifest from input to output. */
+    /**
+     * Copy all the files in a manifest from input to output.  We set
+     * the modification times in the output to a fixed time, so as to
+     * reduce variation in the output file and make incremental OTAs
+     * more efficient.
+     */
     private static void copyFiles(Manifest manifest,
-            JarFile in, JarOutputStream out) throws IOException {
+        JarFile in, JarOutputStream out, long timestamp) throws IOException {
         byte[] buffer = new byte[4096];
         int num;
 
@@ -315,15 +320,16 @@
         Collections.sort(names);
         for (String name : names) {
             JarEntry inEntry = in.getJarEntry(name);
+            JarEntry outEntry = null;
             if (inEntry.getMethod() == JarEntry.STORED) {
                 // Preserve the STORED method of the input entry.
-                out.putNextEntry(new JarEntry(inEntry));
+                outEntry = new JarEntry(inEntry);
             } else {
                 // Create a new entry so that the compressed len is recomputed.
-                JarEntry je = new JarEntry(name);
-                je.setTime(inEntry.getTime());
-                out.putNextEntry(je);
+                outEntry = new JarEntry(name);
             }
+            outEntry.setTime(timestamp);
+            out.putNextEntry(outEntry);
 
             InputStream data = in.getInputStream(inEntry);
             while ((num = data.read(buffer)) > 0) {
@@ -380,7 +386,7 @@
             writeSignatureBlock(signature, publicKey, outputJar);
 
             // Everything else
-            copyFiles(manifest, inputJar, outputJar);
+            copyFiles(manifest, inputJar, outputJar, timestamp);
         } catch (Exception e) {
             e.printStackTrace();
             System.exit(1);
diff --git a/tools/warn.py b/tools/warn.py
new file mode 100755
index 0000000..7930299
--- /dev/null
+++ b/tools/warn.py
@@ -0,0 +1,515 @@
+#!/usr/bin/env python
+
+import sys
+import re
+
+if len(sys.argv) == 1:
+    print 'usage: ' + sys.argv[0] + ' <build.log>'
+    sys.exit()
+
+# if you add another level, don't forget to give it a color below
+class severity:
+    UNKNOWN=0
+    SKIP=100
+    FIXMENOW=1
+    HIGH=2
+    MEDIUM=3
+    LOW=4
+    HARMLESS=5
+
+def colorforseverity(sev):
+    if sev == severity.FIXMENOW:
+        return 'fuchsia'
+    if sev == severity.HIGH:
+        return 'red'
+    if sev == severity.MEDIUM:
+        return 'orange'
+    if sev == severity.LOW:
+        return 'yellow'
+    if sev == severity.HARMLESS:
+        return 'limegreen'
+    if sev == severity.UNKNOWN:
+        return 'blue'
+    return 'grey'
+
+warnpatterns = [
+    { 'category':'make',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'make: overriding commands/ignoring old commands',
+        'patterns':[r".*: warning: overriding commands for target .+",
+                    r".*: warning: ignoring old commands for target .+"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wimplicit-function-declaration',
+        'description':'Implicit function declaration',
+        'patterns':[r".*: warning: implicit declaration of function .+"] },
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: conflicting types for '.+'"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wtype-limits',
+        'description':'Expression always evaluates to true or false',
+        'patterns':[r".*: warning: comparison is always false due to limited range of data type",
+                    r".*: warning: comparison of unsigned expression >= 0 is always true",
+                    r".*: warning: comparison of unsigned expression < 0 is always false"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Incompatible pointer types',
+        'patterns':[r".*: warning: assignment from incompatible pointer type",
+                    r".*: warning: passing argument [0-9]+ of '.*' from incompatible pointer type",
+                    r".*: warning: initialization from incompatible pointer type"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-fno-builtin',
+        'description':'Incompatible declaration of built in function',
+        'patterns':[r".*: warning: incompatible implicit declaration of built-in function .+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused-parameter',
+        'description':'Unused parameter',
+        'patterns':[r".*: warning: unused parameter '.*'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused',
+        'description':'Unused function, variable or label',
+        'patterns':[r".*: warning: '.+' defined but not used"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused-value',
+        'description':'Statement with no effect',
+        'patterns':[r".*: warning: statement with no effect"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-field-initializers',
+        'description':'Missing initializer',
+        'patterns':[r".*: warning: missing initializer"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: \(near initialization for '.+'\)"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wformat',
+        'description':'Format string does not match arguments',
+        'patterns':[r".*: warning: format '.+' expects type '.+', but argument [0-9]+ has type '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wformat-extra-args',
+        'description':'Too many arguments for format string',
+        'patterns':[r".*: warning: too many arguments for format"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wsign-compare',
+        'description':'Comparison between signed and unsigned',
+        'patterns':[r".*: warning: comparison between signed and unsigned",
+                    r".*: warning: comparison of promoted \~unsigned with unsigned",
+                    r".*: warning: signed and unsigned type in conditional expression"] },
+    { 'category':'libpng',  'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'libpng: zero area',
+        'patterns':[r".*libpng warning: Ignoring attempt to set cHRM RGB triangle with zero area"] },
+    { 'category':'aapt',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'aapt: no comment for public symbol',
+        'patterns':[r".*: warning: No comment for public symbol .+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-braces',
+        'description':'Missing braces around initializer',
+        'patterns':[r".*: warning: missing braces around initializer.*"] },
+    { 'category':'C/C++',   'severity':severity.HARMLESS, 'members':[], 'option':'',
+        'description':'No newline at end of file',
+        'patterns':[r".*: warning: no newline at end of file"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wcast-qual',
+        'description':'Qualifier discarded',
+        'patterns':[r".*: warning: passing argument [0-9]+ of '.+' discards qualifiers from pointer target type",
+                    r".*: warning: assignment discards qualifiers from pointer target type",
+                    r".*: warning: return discards qualifiers from pointer target type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wattributes',
+        'description':'Attribute ignored',
+        'patterns':[r".*: warning: '_*packed_*' attribute ignored"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wattributes',
+        'description':'Visibility mismatch',
+        'patterns':[r".*: warning: '.+' declared with greater visibility than the type of its field '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Shift count greater than width of type',
+        'patterns':[r".*: warning: (left|right) shift count >= width of type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'extern &lt;foo&gt; is initialized',
+        'patterns':[r".*: warning: '.+' initialized and declared 'extern'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wold-style-declaration',
+        'description':'Old style declaration',
+        'patterns':[r".*: warning: 'static' is not at beginning of declaration"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wuninitialized',
+        'description':'Variable may be used uninitialized',
+        'patterns':[r".*: warning: '.+' may be used uninitialized in this function"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wuninitialized',
+        'description':'Variable is used uninitialized',
+        'patterns':[r".*: warning: '.+' is used uninitialized in this function"] },
+    { 'category':'ld',      'severity':severity.MEDIUM,   'members':[], 'option':'-fshort-enums',
+        'description':'ld: possible enum size mismatch',
+        'patterns':[r".*: warning: .* uses variable-size enums yet the output is to use 32-bit enums; use of enum values across objects may fail"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-sign',
+        'description':'Pointer targets differ in signedness',
+        'patterns':[r".*: warning: pointer targets in initialization differ in signedness",
+                    r".*: warning: pointer targets in assignment differ in signedness",
+                    r".*: warning: pointer targets in return differ in signedness",
+                    r".*: warning: pointer targets in passing argument [0-9]+ of '.+' differ in signedness"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wstrict-overflow',
+        'description':'Assuming overflow does not occur',
+        'patterns':[r".*: warning: assuming signed overflow does not occur when assuming that .* is always (true|false)"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wempty-body',
+        'description':'Suggest adding braces around empty body',
+        'patterns':[r".*: warning: suggest braces around empty body in an 'if' statement",
+                    r".*: warning: empty body in an if-statement",
+                    r".*: warning: suggest braces around empty body in an 'else' statement",
+                    r".*: warning: empty body in an else-statement"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wparentheses',
+        'description':'Suggest adding parentheses',
+        'patterns':[r".*: warning: suggest explicit braces to avoid ambiguous 'else'",
+                    r".*: warning: suggest parentheses around arithmetic in operand of '.+'",
+                    r".*: warning: suggest parentheses around comparison in operand of '.+'",
+                    r".*: warning: suggest parentheses around '.+?' .+ '.+?'",
+                    r".*: warning: suggest parentheses around assignment used as truth value"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Static variable used in non-static inline function',
+        'patterns':[r".*: warning: '.+' is static but used in inline function '.+' which is not static"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wimplicit int',
+        'description':'No type or storage class (will default to int)',
+        'patterns':[r".*: warning: data definition has no type or storage class"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: type defaults to 'int' in declaration of '.+'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: parameter names \(without types\) in function declaration"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wstrict-aliasing',
+        'description':'Dereferencing &lt;foo&gt; breaks strict aliasing rules',
+        'patterns':[r".*: warning: dereferencing .* break strict-aliasing rules"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-to-int-cast',
+        'description':'Cast from pointer to integer of different size',
+        'patterns':[r".*: warning: cast from pointer to integer of different size"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wint-to-pointer-cast',
+        'description':'Cast to pointer from integer of different size',
+        'patterns':[r".*: warning: cast to pointer from integer of different size"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Symbol redefined',
+        'patterns':[r".*: warning: "".+"" redefined"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: this is the location of the previous definition"] },
+    { 'category':'ld',      'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'ld: type and size of dynamic symbol are not defined',
+        'patterns':[r".*: warning: type and size of dynamic symbol `.+' are not defined"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Pointer from integer without cast',
+        'patterns':[r".*: warning: assignment makes pointer from integer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Pointer from integer without cast',
+        'patterns':[r".*: warning: passing argument [0-9]+ of '.+' makes pointer from integer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Integer from pointer without cast',
+        'patterns':[r".*: warning: assignment makes integer from pointer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Integer from pointer without cast',
+        'patterns':[r".*: warning: passing argument [0-9]+ of '.+' makes integer from pointer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Integer from pointer without cast',
+        'patterns':[r".*: warning: return makes integer from pointer without a cast"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunknown-pragmas',
+        'description':'Ignoring pragma',
+        'patterns':[r".*: warning: ignoring #pragma .+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wclobbered',
+        'description':'Variable might be clobbered by longjmp or vfork',
+        'patterns':[r".*: warning: variable '.+' might be clobbered by 'longjmp' or 'vfork'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wclobbered',
+        'description':'Argument might be clobbered by longjmp or vfork',
+        'patterns':[r".*: warning: argument '.+' might be clobbered by 'longjmp' or 'vfork'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wredundant-decls',
+        'description':'Redundant declaration',
+        'patterns':[r".*: warning: redundant redeclaration of '.+'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: previous declaration of '.+' was here"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wswitch-enum',
+        'description':'Enum value not handled in switch',
+        'patterns':[r".*: warning: enumeration value '.+' not handled in switch"] },
+    { 'category':'java',    'severity':severity.MEDIUM,   'members':[], 'option':'-encoding',
+        'description':'Java: Non-ascii characters used, but ascii encoding specified',
+        'patterns':[r".*: warning: unmappable character for encoding ascii"] },
+    { 'category':'java',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Java: Non-varargs call of varargs method with inexact argument type for last parameter',
+        'patterns':[r".*: warning: non-varargs call of varargs method with inexact argument type for last parameter"] },
+    { 'category':'aapt',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'aapt: String marked untranslatable, but translation exists',
+        'patterns':[r".*: warning: string '.+' in .* marked untranslatable but exists in locale '??_??'"] },
+    { 'category':'aapt',    'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'aapt: empty span in string',
+        'patterns':[r".*: warning: empty '.+' span found in text '.+"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Taking address of temporary',
+        'patterns':[r".*: warning: taking address of temporary"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Possible broken line continuation',
+        'patterns':[r".*: warning: backslash and newline separated by space"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Warray-bounds',
+        'description':'Array subscript out of bounds',
+        'patterns':[r".*: warning: array subscript is above array bounds"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Decimal constant is unsigned only in ISO C90',
+        'patterns':[r".*: warning: this decimal constant is unsigned only in ISO C90"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmain',
+        'description':'main is usually a function',
+        'patterns':[r".*: warning: 'main' is usually a function"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Typedef ignored',
+        'patterns':[r".*: warning: 'typedef' was ignored in this declaration"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Waddress',
+        'description':'Address always evaluates to true',
+        'patterns':[r".*: warning: the address of '.+' will always evaluate as 'true'"] },
+    { 'category':'C/C++',   'severity':severity.FIXMENOW, 'members':[], 'option':'',
+        'description':'Freeing a non-heap object',
+        'patterns':[r".*: warning: attempt to free a non-heap object '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wchar-subscripts',
+        'description':'Array subscript has type char',
+        'patterns':[r".*: warning: array subscript has type 'char'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Constant too large for type',
+        'patterns':[r".*: warning: integer constant is too large for '.+' type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Woverflow',
+        'description':'Constant too large for type, truncated',
+        'patterns':[r".*: warning: large integer implicitly truncated to unsigned type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Woverflow',
+        'description':'Overflow in implicit constant conversion',
+        'patterns':[r".*: warning: overflow in implicit constant conversion"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Declaration does not declare anything',
+        'patterns':[r".*: warning: declaration 'class .+' does not declare anything"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wreorder',
+        'description':'Initialization order will be different',
+        'patterns':[r".*: warning: '.+' will be initialized after"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning:   '.+'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning:   when initialized here"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-parameter-type',
+        'description':'Parameter type not specified',
+        'patterns':[r".*: warning: type of '.+' defaults to 'int'"] },
+    { 'category':'gcc',     'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Invalid option for C file',
+        'patterns':[r".*: warning: command line option "".+"" is valid for C\+\+\/ObjC\+\+ but not for C"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'User warning',
+        'patterns':[r".*: warning: #warning "".+"""] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextra',
+        'description':'Dereferencing void*',
+        'patterns':[r".*: warning: dereferencing 'void \*' pointer"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextra',
+        'description':'Comparison of pointer to zero',
+        'patterns':[r".*: warning: ordered comparison of pointer with integer zero"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wwrite-strings',
+        'description':'Conversion of string constant to non-const char*',
+        'patterns':[r".*: warning: deprecated conversion from string constant to '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wstrict-prototypes',
+        'description':'Function declaration isn''t a prototype',
+        'patterns':[r".*: warning: function declaration isn't a prototype"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wignored-qualifiers',
+        'description':'Type qualifiers ignored on function return value',
+        'patterns':[r".*: warning: type qualifiers ignored on function return type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'&lt;foo&gt; declared inside parameter list, scope limited to this definition',
+        'patterns':[r".*: warning: '.+' declared inside parameter list"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: its scope is only this definition or declaration, which is probably not what you want"] },
+    { 'category':'C/C++',   'severity':severity.LOW,      'members':[], 'option':'-Wcomment',
+        'description':'Line continuation inside comment',
+        'patterns':[r".*: warning: multi-line comment"] },
+    { 'category':'C/C++',   'severity':severity.HARMLESS, 'members':[], 'option':'',
+        'description':'Extra tokens after #endif',
+        'patterns':[r".*: warning: extra tokens at end of #endif directive"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wenum-compare',
+        'description':'Comparison between different enums',
+        'patterns':[r".*: warning: comparison between 'enum .+' and 'enum .+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wconversion',
+        'description':'Implicit conversion of negative number to unsigned type',
+        'patterns':[r".*: warning: converting negative value '.+' to '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Passing NULL as non-pointer argument',
+        'patterns':[r".*: warning: passing NULL to non-pointer argument 3 of '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wctor-dtor-privacy',
+        'description':'Class seems unusable because of private ctor/dtor' ,
+        'patterns':[r".*: warning: all member functions in class '.+' are private"] },
+    # skip this next one, because it only points out some RefBase-based classes where having a private destructor is perfectly fine
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'-Wctor-dtor-privacy',
+        'description':'Class seems unusable because of private ctor/dtor' ,
+        'patterns':[r".*: warning: 'class .+' only defines a private destructor and has no friends"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wctor-dtor-privacy',
+        'description':'Class seems unusable because of private ctor/dtor' ,
+        'patterns':[r".*: warning: 'class .+' only defines private constructors and has no friends"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-arith',
+        'description':'void* used in arithmetic' ,
+        'patterns':[r".*: warning: pointer of type 'void \*' used in (arithmetic|subtraction)",
+                    r".*: warning: wrong type argument to increment"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wsign-promo',
+        'description':'Overload resolution chose to promote from unsigned or enum to signed type' ,
+        'patterns':[r".*: warning: passing '.+' chooses 'int' over '.* int'"] },
+    { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning:   in call to '.+'"] },
+
+    # these next ones are to deal with formatting problems resulting from the log being mixed up by 'make -j'
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: ,$"] },
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: $"] },
+    { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
+        'description':'',
+        'patterns':[r".*: warning: In file included from .+,"] },
+
+    # catch-all for warnings this script doesn't know about yet
+    { 'category':'C/C++',   'severity':severity.UNKNOWN,  'members':[], 'option':'',
+        'description':'Unclassified/unrecognized warnings',
+        'patterns':[r".*: warning: .+"] },
+]
+
+anchor = 0
+cur_row_color = 0
+row_colors = [ 'e0e0e0', 'd0d0d0' ]
+
+def output(text):
+    print text,
+
+def htmlbig(param):
+    return '<font size="+2">' + param + '</font>'
+
+def dumphtmlprologue(title):
+    output('<html>\n<head>\n<title>' + title + '</title>\n<body>\n')
+    output(htmlbig(title))
+    output('<p>\n')
+
+def tablerow(text):
+    global cur_row_color
+    output('<tr bgcolor="' + row_colors[cur_row_color] + '"><td colspan="2">',)
+    cur_row_color = 1 - cur_row_color
+    output(text,)
+    output('</td></tr>')
+
+def begintable(text, backgroundcolor):
+    global anchor
+    output('<table border="1" rules="cols" frame="box" width="100%" bgcolor="black"><tr bgcolor="' +
+        backgroundcolor + '"><a name="anchor' + str(anchor) + '"><td>')
+    output(htmlbig(text[0]) + '<br>')
+    for i in text[1:]:
+        output(i + '<br>')
+    output('</td>')
+    output('<td width="100" bgcolor="grey"><a align="right" href="#anchor' + str(anchor-1) +
+        '">previous</a><br><a align="right" href="#anchor' + str(anchor+1) + '">next</a>')
+    output('</td></a></tr>')
+    anchor += 1
+
+def endtable():
+    output('</table><p>')
+
+
+# dump some stats about total number of warnings and such
+def dumpstats():
+    known = 0
+    unknown = 0
+    for i in warnpatterns:
+        if i['severity'] == severity.UNKNOWN:
+            unknown += len(i['members'])
+        elif i['severity'] != severity.SKIP:
+            known += len(i['members'])
+    output('Number of classified warnings: <b>' + str(known) + '</b><br>' )
+    output('Number of unclassified warnings: <b>' + str(unknown) + '</b><br>')
+    total = unknown + known
+    output('Total number of warnings: <b>' + str(total) + '</b>')
+    if total < 1000:
+        output('(low count may indicate incremental build)')
+    output('<p>')
+
+def allpatterns(cat):
+    pats = ''
+    for i in cat['patterns']:
+        pats += i
+        pats += ' / '
+    return pats
+
+def descriptionfor(cat):
+    if cat['description'] != '':
+        return cat['description']
+    return allpatterns(cat)
+
+
+# show which warnings no longer occur
+def dumpfixed():
+    tablestarted = False
+    for i in warnpatterns:
+        if len(i['members']) == 0 and i['severity'] != severity.SKIP:
+            if tablestarted == False:
+                tablestarted = True
+                begintable(['Fixed warnings', 'No more occurences. Please consider turning these in to errors if possible, before they are reintroduced in to the build'], 'blue')
+            tablerow(i['description'] + ' (' + allpatterns(i) + ') ' + i['option'])
+    if tablestarted:
+        endtable()
+
+
+# dump a category, provided it is not marked as 'SKIP' and has more than 0 occurrences
+def dumpcategory(cat):
+    if cat['severity'] != severity.SKIP and len(cat['members']) != 0:
+        header = [descriptionfor(cat),str(len(cat['members'])) + ' occurences:']
+        if cat['option'] != '':
+            header[1:1] = [' (related option: ' + cat['option'] +')']
+        begintable(header, colorforseverity(cat['severity']))
+        for i in cat['members']:
+            tablerow(i)
+        endtable()
+
+
+# dump everything for a given severity
+def dumpseverity(sev):
+    for i in warnpatterns:
+        if i['severity'] == sev:
+            dumpcategory(i)
+
+
+def classifywarning(line):
+    for i in warnpatterns:
+        for pat in i['patterns']:
+            if re.match(pat, line):
+                i['members'].append(line)
+                return
+    else:
+        # If we end up here, there was a problem parsing the log
+        # probably caused by 'make -j' mixing the output from
+        # 2 or more concurrent compiles
+        pass
+
+
+
+infile = open(sys.argv[1], 'r')
+warnings = []
+
+platformversion = 'unknown'
+targetproduct = 'unknown'
+targetvariant = 'unknown'
+linecounter = 0
+
+warningpattern = re.compile('.* warning:.*')
+
+# read the log file and classify all the warnings
+lastmatchedline = ''
+for line in infile:
+    if warningpattern.match(line):
+        if line != lastmatchedline:
+            classifywarning(line)
+            lastmatchedline = line
+    else:
+        # save a little bit of time by only doing this for the first few lines
+        if linecounter < 50:
+            linecounter +=1
+            m = re.search('(?<=^PLATFORM_VERSION=).*', line)
+            if m != None:
+                platformversion = m.group(0)
+            m = re.search('(?<=^TARGET_PRODUCT=).*', line)
+            if m != None:
+                targetproduct = m.group(0)
+            m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
+            if m != None:
+                targetvariant = m.group(0)
+
+
+# dump the html output to stdout
+dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
+dumpstats()
+dumpseverity(severity.FIXMENOW)
+dumpseverity(severity.HIGH)
+dumpseverity(severity.MEDIUM)
+dumpseverity(severity.LOW)
+dumpseverity(severity.HARMLESS)
+dumpseverity(severity.UNKNOWN)
+dumpfixed()
+
+
+
+
+
diff --git a/tools/zipalign/Android.mk b/tools/zipalign/Android.mk
index e23b699..f90a31c 100644
--- a/tools/zipalign/Android.mk
+++ b/tools/zipalign/Android.mk
@@ -8,7 +8,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-	ZipAlign.cpp
+	ZipAlign.cpp \
+	ZipEntry.cpp \
+	ZipFile.cpp
 
 LOCAL_C_INCLUDES += external/zlib
 
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index 058f9ed..eab2f04 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -16,7 +16,7 @@
 /*
  * Zip alignment tool
  */
-#include "utils/ZipFile.h"
+#include "ZipFile.h"
 
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
new file mode 100644
index 0000000..bed0333
--- /dev/null
+++ b/tools/zipalign/ZipEntry.cpp
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Access to entries in a Zip archive.
+//
+
+#define LOG_TAG "zip"
+
+#include "ZipEntry.h"
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Initialize a new ZipEntry structure from a FILE* positioned at a
+ * CentralDirectoryEntry.
+ *
+ * On exit, the file pointer will be at the start of the next CDE or
+ * at the EOCD.
+ */
+status_t ZipEntry::initFromCDE(FILE* fp)
+{
+    status_t result;
+    long posn;
+    bool hasDD;
+
+    //LOGV("initFromCDE ---\n");
+
+    /* read the CDE */
+    result = mCDE.read(fp);
+    if (result != NO_ERROR) {
+        LOGD("mCDE.read failed\n");
+        return result;
+    }
+
+    //mCDE.dump();
+
+    /* using the info in the CDE, go load up the LFH */
+    posn = ftell(fp);
+    if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
+        LOGD("local header seek failed (%ld)\n",
+            mCDE.mLocalHeaderRelOffset);
+        return UNKNOWN_ERROR;
+    }
+
+    result = mLFH.read(fp);
+    if (result != NO_ERROR) {
+        LOGD("mLFH.read failed\n");
+        return result;
+    }
+
+    if (fseek(fp, posn, SEEK_SET) != 0)
+        return UNKNOWN_ERROR;
+
+    //mLFH.dump();
+
+    /*
+     * We *might* need to read the Data Descriptor at this point and
+     * integrate it into the LFH.  If this bit is set, the CRC-32,
+     * compressed size, and uncompressed size will be zero.  In practice
+     * these seem to be rare.
+     */
+    hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
+    if (hasDD) {
+        // do something clever
+        //LOGD("+++ has data descriptor\n");
+    }
+
+    /*
+     * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr"
+     * flag is set, because the LFH is incomplete.  (Not a problem, since we
+     * prefer the CDE values.)
+     */
+    if (!hasDD && !compareHeaders()) {
+        LOGW("WARNING: header mismatch\n");
+        // keep going?
+    }
+
+    /*
+     * If the mVersionToExtract is greater than 20, we may have an
+     * issue unpacking the record -- could be encrypted, compressed
+     * with something we don't support, or use Zip64 extensions.  We
+     * can defer worrying about that to when we're extracting data.
+     */
+
+    return NO_ERROR;
+}
+
+/*
+ * Initialize a new entry.  Pass in the file name and an optional comment.
+ *
+ * Initializes the CDE and the LFH.
+ */
+void ZipEntry::initNew(const char* fileName, const char* comment)
+{
+    assert(fileName != NULL && *fileName != '\0');  // name required
+
+    /* most fields are properly initialized by constructor */
+    mCDE.mVersionMadeBy = kDefaultMadeBy;
+    mCDE.mVersionToExtract = kDefaultVersion;
+    mCDE.mCompressionMethod = kCompressStored;
+    mCDE.mFileNameLength = strlen(fileName);
+    if (comment != NULL)
+        mCDE.mFileCommentLength = strlen(comment);
+    mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does
+
+    if (mCDE.mFileNameLength > 0) {
+        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+        strcpy((char*) mCDE.mFileName, fileName);
+    }
+    if (mCDE.mFileCommentLength > 0) {
+        /* TODO: stop assuming null-terminated ASCII here? */
+        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+        strcpy((char*) mCDE.mFileComment, comment);
+    }
+
+    copyCDEtoLFH();
+}
+
+/*
+ * Initialize a new entry, starting with the ZipEntry from a different
+ * archive.
+ *
+ * Initializes the CDE and the LFH.
+ */
+status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
+    const ZipEntry* pEntry)
+{
+    /*
+     * Copy everything in the CDE over, then fix up the hairy bits.
+     */
+    memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
+
+    if (mCDE.mFileNameLength > 0) {
+        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+        if (mCDE.mFileName == NULL)
+            return NO_MEMORY;
+        strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
+    }
+    if (mCDE.mFileCommentLength > 0) {
+        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+        if (mCDE.mFileComment == NULL)
+            return NO_MEMORY;
+        strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
+    }
+    if (mCDE.mExtraFieldLength > 0) {
+        /* we null-terminate this, though it may not be a string */
+        mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
+        if (mCDE.mExtraField == NULL)
+            return NO_MEMORY;
+        memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
+            mCDE.mExtraFieldLength+1);
+    }
+
+    /* construct the LFH from the CDE */
+    copyCDEtoLFH();
+
+    /*
+     * The LFH "extra" field is independent of the CDE "extra", so we
+     * handle it here.
+     */
+    assert(mLFH.mExtraField == NULL);
+    mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
+    if (mLFH.mExtraFieldLength > 0) {
+        mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
+        if (mLFH.mExtraField == NULL)
+            return NO_MEMORY;
+        memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
+            mLFH.mExtraFieldLength+1);
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
+ * potentially confuse something that put "extra" data in here earlier,
+ * but I can't find an actual problem.
+ */
+status_t ZipEntry::addPadding(int padding)
+{
+    if (padding <= 0)
+        return INVALID_OPERATION;
+
+    //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+    //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
+
+    if (mLFH.mExtraFieldLength > 0) {
+        /* extend existing field */
+        unsigned char* newExtra;
+
+        newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
+        if (newExtra == NULL)
+            return NO_MEMORY;
+        memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
+        memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
+
+        delete[] mLFH.mExtraField;
+        mLFH.mExtraField = newExtra;
+        mLFH.mExtraFieldLength += padding;
+    } else {
+        /* create new field */
+        mLFH.mExtraField = new unsigned char[padding];
+        memset(mLFH.mExtraField, 0, padding);
+        mLFH.mExtraFieldLength = padding;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Set the fields in the LFH equal to the corresponding fields in the CDE.
+ *
+ * This does not touch the LFH "extra" field.
+ */
+void ZipEntry::copyCDEtoLFH(void)
+{
+    mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
+    mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
+    mLFH.mCompressionMethod = mCDE.mCompressionMethod;
+    mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
+    mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
+    mLFH.mCRC32             = mCDE.mCRC32;
+    mLFH.mCompressedSize    = mCDE.mCompressedSize;
+    mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
+    mLFH.mFileNameLength    = mCDE.mFileNameLength;
+    // the "extra field" is independent
+
+    delete[] mLFH.mFileName;
+    if (mLFH.mFileNameLength > 0) {
+        mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
+        strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
+    } else {
+        mLFH.mFileName = NULL;
+    }
+}
+
+/*
+ * Set some information about a file after we add it.
+ */
+void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+    int compressionMethod)
+{
+    mCDE.mCompressionMethod = compressionMethod;
+    mCDE.mCRC32 = crc32;
+    mCDE.mCompressedSize = compLen;
+    mCDE.mUncompressedSize = uncompLen;
+    mCDE.mCompressionMethod = compressionMethod;
+    if (compressionMethod == kCompressDeflated) {
+        mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
+    }
+    copyCDEtoLFH();
+}
+
+/*
+ * See if the data in mCDE and mLFH match up.  This is mostly useful for
+ * debugging these classes, but it can be used to identify damaged
+ * archives.
+ *
+ * Returns "false" if they differ.
+ */
+bool ZipEntry::compareHeaders(void) const
+{
+    if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
+        LOGV("cmp: VersionToExtract\n");
+        return false;
+    }
+    if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
+        LOGV("cmp: GPBitFlag\n");
+        return false;
+    }
+    if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
+        LOGV("cmp: CompressionMethod\n");
+        return false;
+    }
+    if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
+        LOGV("cmp: LastModFileTime\n");
+        return false;
+    }
+    if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
+        LOGV("cmp: LastModFileDate\n");
+        return false;
+    }
+    if (mCDE.mCRC32 != mLFH.mCRC32) {
+        LOGV("cmp: CRC32\n");
+        return false;
+    }
+    if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
+        LOGV("cmp: CompressedSize\n");
+        return false;
+    }
+    if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
+        LOGV("cmp: UncompressedSize\n");
+        return false;
+    }
+    if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
+        LOGV("cmp: FileNameLength\n");
+        return false;
+    }
+#if 0       // this seems to be used for padding, not real data
+    if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
+        LOGV("cmp: ExtraFieldLength\n");
+        return false;
+    }
+#endif
+    if (mCDE.mFileName != NULL) {
+        if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
+            LOGV("cmp: FileName\n");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Convert the DOS date/time stamp into a UNIX time stamp.
+ */
+time_t ZipEntry::getModWhen(void) const
+{
+    struct tm parts;
+
+    parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
+    parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
+    parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
+    parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
+    parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
+    parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
+    parts.tm_wday = parts.tm_yday = 0;
+    parts.tm_isdst = -1;        // DST info "not available"
+
+    return mktime(&parts);
+}
+
+/*
+ * Set the CDE/LFH timestamp from UNIX time.
+ */
+void ZipEntry::setModWhen(time_t when)
+{
+#ifdef HAVE_LOCALTIME_R
+    struct tm tmResult;
+#endif
+    time_t even;
+    unsigned short zdate, ztime;
+
+    struct tm* ptm;
+
+    /* round up to an even number of seconds */
+    even = (time_t)(((unsigned long)(when) + 1) & (~1));
+
+    /* expand */
+#ifdef HAVE_LOCALTIME_R
+    ptm = localtime_r(&even, &tmResult);
+#else
+    ptm = localtime(&even);
+#endif
+
+    int year;
+    year = ptm->tm_year;
+    if (year < 80)
+        year = 80;
+
+    zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
+    ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+
+    mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
+    mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipEntry::LocalFileHeader
+ * ===========================================================================
+ */
+
+/*
+ * Read a local file header.
+ *
+ * On entry, "fp" points to the signature at the start of the header.
+ * On exit, "fp" points to the start of data.
+ */
+status_t ZipEntry::LocalFileHeader::read(FILE* fp)
+{
+    status_t result = NO_ERROR;
+    unsigned char buf[kLFHLen];
+
+    assert(mFileName == NULL);
+    assert(mExtraField == NULL);
+
+    if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+        LOGD("whoops: didn't find expected signature\n");
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
+    mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
+    mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
+    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
+    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
+    mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
+    mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
+    mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
+    mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
+    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
+
+    // TODO: validate sizes
+
+    /* grab filename */
+    if (mFileNameLength != 0) {
+        mFileName = new unsigned char[mFileNameLength+1];
+        if (mFileName == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileName[mFileNameLength] = '\0';
+    }
+
+    /* grab extra field */
+    if (mExtraFieldLength != 0) {
+        mExtraField = new unsigned char[mExtraFieldLength+1];
+        if (mExtraField == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mExtraField[mExtraFieldLength] = '\0';
+    }
+
+bail:
+    return result;
+}
+
+/*
+ * Write a local file header.
+ */
+status_t ZipEntry::LocalFileHeader::write(FILE* fp)
+{
+    unsigned char buf[kLFHLen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
+    ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
+    ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
+    ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
+    ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
+    ZipEntry::putLongLE(&buf[0x0e], mCRC32);
+    ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
+    ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
+    ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
+    ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
+
+    if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
+        return UNKNOWN_ERROR;
+
+    /* write filename */
+    if (mFileNameLength != 0) {
+        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write "extra field" */
+    if (mExtraFieldLength != 0) {
+        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
+/*
+ * Dump the contents of a LocalFileHeader object.
+ */
+void ZipEntry::LocalFileHeader::dump(void) const
+{
+    LOGD(" LocalFileHeader contents:\n");
+    LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
+        mVersionToExtract, mGPBitFlag, mCompressionMethod);
+    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+        mLastModFileTime, mLastModFileDate, mCRC32);
+    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+        mCompressedSize, mUncompressedSize);
+    LOGD("  filenameLen=%u extraLen=%u\n",
+        mFileNameLength, mExtraFieldLength);
+    if (mFileName != NULL)
+        LOGD("  filename: '%s'\n", mFileName);
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipEntry::CentralDirEntry
+ * ===========================================================================
+ */
+
+/*
+ * Read the central dir entry that appears next in the file.
+ *
+ * On entry, "fp" should be positioned on the signature bytes for the
+ * entry.  On exit, "fp" will point at the signature word for the next
+ * entry or for the EOCD.
+ */
+status_t ZipEntry::CentralDirEntry::read(FILE* fp)
+{
+    status_t result = NO_ERROR;
+    unsigned char buf[kCDELen];
+
+    /* no re-use */
+    assert(mFileName == NULL);
+    assert(mExtraField == NULL);
+    assert(mFileComment == NULL);
+
+    if (fread(buf, 1, kCDELen, fp) != kCDELen) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+        LOGD("Whoops: didn't find expected signature\n");
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
+    mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
+    mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
+    mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
+    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
+    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
+    mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
+    mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
+    mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
+    mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
+    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
+    mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
+    mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
+    mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
+    mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
+    mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
+
+    // TODO: validate sizes and offsets
+
+    /* grab filename */
+    if (mFileNameLength != 0) {
+        mFileName = new unsigned char[mFileNameLength+1];
+        if (mFileName == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileName[mFileNameLength] = '\0';
+    }
+
+    /* read "extra field" */
+    if (mExtraFieldLength != 0) {
+        mExtraField = new unsigned char[mExtraFieldLength+1];
+        if (mExtraField == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mExtraField[mExtraFieldLength] = '\0';
+    }
+
+
+    /* grab comment, if any */
+    if (mFileCommentLength != 0) {
+        mFileComment = new unsigned char[mFileCommentLength+1];
+        if (mFileComment == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+        {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileComment[mFileCommentLength] = '\0';
+    }
+
+bail:
+    return result;
+}
+
+/*
+ * Write a central dir entry.
+ */
+status_t ZipEntry::CentralDirEntry::write(FILE* fp)
+{
+    unsigned char buf[kCDELen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
+    ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
+    ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
+    ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
+    ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
+    ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
+    ZipEntry::putLongLE(&buf[0x10], mCRC32);
+    ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
+    ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
+    ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
+    ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
+    ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
+    ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
+    ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
+    ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
+    ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
+
+    if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
+        return UNKNOWN_ERROR;
+
+    /* write filename */
+    if (mFileNameLength != 0) {
+        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write "extra field" */
+    if (mExtraFieldLength != 0) {
+        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write comment */
+    if (mFileCommentLength != 0) {
+        if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Dump the contents of a CentralDirEntry object.
+ */
+void ZipEntry::CentralDirEntry::dump(void) const
+{
+    LOGD(" CentralDirEntry contents:\n");
+    LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+        mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
+    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+        mLastModFileTime, mLastModFileDate, mCRC32);
+    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+        mCompressedSize, mUncompressedSize);
+    LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
+        mFileNameLength, mExtraFieldLength, mFileCommentLength);
+    LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+        mDiskNumberStart, mInternalAttrs, mExternalAttrs,
+        mLocalHeaderRelOffset);
+
+    if (mFileName != NULL)
+        LOGD("  filename: '%s'\n", mFileName);
+    if (mFileComment != NULL)
+        LOGD("  comment: '%s'\n", mFileComment);
+}
+
diff --git a/tools/zipalign/ZipEntry.h b/tools/zipalign/ZipEntry.h
new file mode 100644
index 0000000..7f721b4
--- /dev/null
+++ b/tools/zipalign/ZipEntry.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Zip archive entries.
+//
+// The ZipEntry class is tightly meshed with the ZipFile class.
+//
+#ifndef __LIBS_ZIPENTRY_H
+#define __LIBS_ZIPENTRY_H
+
+#include <utils/Errors.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace android {
+
+class ZipFile;
+
+/*
+ * ZipEntry objects represent a single entry in a Zip archive.
+ *
+ * You can use one of these to get or set information about an entry, but
+ * there are no functions here for accessing the data itself.  (We could
+ * tuck a pointer to the ZipFile in here for convenience, but that raises
+ * the likelihood of using ZipEntry objects after discarding the ZipFile.)
+ *
+ * File information is stored in two places: next to the file data (the Local
+ * File Header, and possibly a Data Descriptor), and at the end of the file
+ * (the Central Directory Entry).  The two must be kept in sync.
+ */
+class ZipEntry {
+public:
+    friend class ZipFile;
+
+    ZipEntry(void)
+        : mDeleted(false), mMarked(false)
+        {}
+    ~ZipEntry(void) {}
+
+    /*
+     * Returns "true" if the data is compressed.
+     */
+    bool isCompressed(void) const {
+        return mCDE.mCompressionMethod != kCompressStored;
+    }
+    int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
+
+    /*
+     * Return the uncompressed length.
+     */
+    off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
+
+    /*
+     * Return the compressed length.  For uncompressed data, this returns
+     * the same thing as getUncompresesdLen().
+     */
+    off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
+
+    /*
+     * Return the absolute file offset of the start of the compressed or
+     * uncompressed data.
+     */
+    off_t getFileOffset(void) const {
+        return mCDE.mLocalHeaderRelOffset +
+                LocalFileHeader::kLFHLen +
+                mLFH.mFileNameLength +
+                mLFH.mExtraFieldLength;
+    }
+
+    /*
+     * Return the data CRC.
+     */
+    unsigned long getCRC32(void) const { return mCDE.mCRC32; }
+
+    /*
+     * Return file modification time in UNIX seconds-since-epoch.
+     */
+    time_t getModWhen(void) const;
+
+    /*
+     * Return the archived file name.
+     */
+    const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
+
+    /*
+     * Application-defined "mark".  Can be useful when synchronizing the
+     * contents of an archive with contents on disk.
+     */
+    bool getMarked(void) const { return mMarked; }
+    void setMarked(bool val) { mMarked = val; }
+
+    /*
+     * Some basic functions for raw data manipulation.  "LE" means
+     * Little Endian.
+     */
+    static inline unsigned short getShortLE(const unsigned char* buf) {
+        return buf[0] | (buf[1] << 8);
+    }
+    static inline unsigned long getLongLE(const unsigned char* buf) {
+        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+    }
+    static inline void putShortLE(unsigned char* buf, short val) {
+        buf[0] = (unsigned char) val;
+        buf[1] = (unsigned char) (val >> 8);
+    }
+    static inline void putLongLE(unsigned char* buf, long val) {
+        buf[0] = (unsigned char) val;
+        buf[1] = (unsigned char) (val >> 8);
+        buf[2] = (unsigned char) (val >> 16);
+        buf[3] = (unsigned char) (val >> 24);
+    }
+
+    /* defined for Zip archives */
+    enum {
+        kCompressStored     = 0,        // no compression
+        // shrunk           = 1,
+        // reduced 1        = 2,
+        // reduced 2        = 3,
+        // reduced 3        = 4,
+        // reduced 4        = 5,
+        // imploded         = 6,
+        // tokenized        = 7,
+        kCompressDeflated   = 8,        // standard deflate
+        // Deflate64        = 9,
+        // lib imploded     = 10,
+        // reserved         = 11,
+        // bzip2            = 12,
+    };
+
+    /*
+     * Deletion flag.  If set, the entry will be removed on the next
+     * call to "flush".
+     */
+    bool getDeleted(void) const { return mDeleted; }
+
+protected:
+    /*
+     * Initialize the structure from the file, which is pointing at
+     * our Central Directory entry.
+     */
+    status_t initFromCDE(FILE* fp);
+
+    /*
+     * Initialize the structure for a new file.  We need the filename
+     * and comment so that we can properly size the LFH area.  The
+     * filename is mandatory, the comment is optional.
+     */
+    void initNew(const char* fileName, const char* comment);
+
+    /*
+     * Initialize the structure with the contents of a ZipEntry from
+     * another file.
+     */
+    status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
+
+    /*
+     * Add some pad bytes to the LFH.  We do this by adding or resizing
+     * the "extra" field.
+     */
+    status_t addPadding(int padding);
+
+    /*
+     * Set information about the data for this entry.
+     */
+    void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+        int compressionMethod);
+
+    /*
+     * Set the modification date.
+     */
+    void setModWhen(time_t when);
+
+    /*
+     * Return the offset of the local file header.
+     */
+    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+    /*
+     * Set the offset of the local file header, relative to the start of
+     * the current file.
+     */
+    void setLFHOffset(off_t offset) {
+        mCDE.mLocalHeaderRelOffset = (long) offset;
+    }
+
+    /* mark for deletion; used by ZipFile::remove() */
+    void setDeleted(void) { mDeleted = true; }
+
+private:
+    /* these are private and not defined */
+    ZipEntry(const ZipEntry& src);
+    ZipEntry& operator=(const ZipEntry& src);
+
+    /* returns "true" if the CDE and the LFH agree */
+    bool compareHeaders(void) const;
+    void copyCDEtoLFH(void);
+
+    bool        mDeleted;       // set if entry is pending deletion
+    bool        mMarked;        // app-defined marker
+
+    /*
+     * Every entry in the Zip archive starts off with one of these.
+     */
+    class LocalFileHeader {
+    public:
+        LocalFileHeader(void) :
+            mVersionToExtract(0),
+            mGPBitFlag(0),
+            mCompressionMethod(0),
+            mLastModFileTime(0),
+            mLastModFileDate(0),
+            mCRC32(0),
+            mCompressedSize(0),
+            mUncompressedSize(0),
+            mFileNameLength(0),
+            mExtraFieldLength(0),
+            mFileName(NULL),
+            mExtraField(NULL)
+        {}
+        virtual ~LocalFileHeader(void) {
+            delete[] mFileName;
+            delete[] mExtraField;
+        }
+
+        status_t read(FILE* fp);
+        status_t write(FILE* fp);
+
+        // unsigned long mSignature;
+        unsigned short  mVersionToExtract;
+        unsigned short  mGPBitFlag;
+        unsigned short  mCompressionMethod;
+        unsigned short  mLastModFileTime;
+        unsigned short  mLastModFileDate;
+        unsigned long   mCRC32;
+        unsigned long   mCompressedSize;
+        unsigned long   mUncompressedSize;
+        unsigned short  mFileNameLength;
+        unsigned short  mExtraFieldLength;
+        unsigned char*  mFileName;
+        unsigned char*  mExtraField;
+
+        enum {
+            kSignature      = 0x04034b50,
+            kLFHLen         = 30,       // LocalFileHdr len, excl. var fields
+        };
+
+        void dump(void) const;
+    };
+
+    /*
+     * Every entry in the Zip archive has one of these in the "central
+     * directory" at the end of the file.
+     */
+    class CentralDirEntry {
+    public:
+        CentralDirEntry(void) :
+            mVersionMadeBy(0),
+            mVersionToExtract(0),
+            mGPBitFlag(0),
+            mCompressionMethod(0),
+            mLastModFileTime(0),
+            mLastModFileDate(0),
+            mCRC32(0),
+            mCompressedSize(0),
+            mUncompressedSize(0),
+            mFileNameLength(0),
+            mExtraFieldLength(0),
+            mFileCommentLength(0),
+            mDiskNumberStart(0),
+            mInternalAttrs(0),
+            mExternalAttrs(0),
+            mLocalHeaderRelOffset(0),
+            mFileName(NULL),
+            mExtraField(NULL),
+            mFileComment(NULL)
+        {}
+        virtual ~CentralDirEntry(void) {
+            delete[] mFileName;
+            delete[] mExtraField;
+            delete[] mFileComment;
+        }
+
+        status_t read(FILE* fp);
+        status_t write(FILE* fp);
+
+        // unsigned long mSignature;
+        unsigned short  mVersionMadeBy;
+        unsigned short  mVersionToExtract;
+        unsigned short  mGPBitFlag;
+        unsigned short  mCompressionMethod;
+        unsigned short  mLastModFileTime;
+        unsigned short  mLastModFileDate;
+        unsigned long   mCRC32;
+        unsigned long   mCompressedSize;
+        unsigned long   mUncompressedSize;
+        unsigned short  mFileNameLength;
+        unsigned short  mExtraFieldLength;
+        unsigned short  mFileCommentLength;
+        unsigned short  mDiskNumberStart;
+        unsigned short  mInternalAttrs;
+        unsigned long   mExternalAttrs;
+        unsigned long   mLocalHeaderRelOffset;
+        unsigned char*  mFileName;
+        unsigned char*  mExtraField;
+        unsigned char*  mFileComment;
+
+        void dump(void) const;
+
+        enum {
+            kSignature      = 0x02014b50,
+            kCDELen         = 46,       // CentralDirEnt len, excl. var fields
+        };
+    };
+
+    enum {
+        //kDataDescriptorSignature  = 0x08074b50,   // currently unused
+        kDataDescriptorLen  = 16,           // four 32-bit fields
+
+        kDefaultVersion     = 20,           // need deflate, nothing much else
+        kDefaultMadeBy      = 0x0317,       // 03=UNIX, 17=spec v2.3
+        kUsesDataDescr      = 0x0008,       // GPBitFlag bit 3
+    };
+
+    LocalFileHeader     mLFH;
+    CentralDirEntry     mCDE;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPENTRY_H
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
new file mode 100644
index 0000000..62c9383
--- /dev/null
+++ b/tools/zipalign/ZipFile.cpp
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Access to Zip archives.
+//
+
+#define LOG_TAG "zip"
+
+#include <utils/ZipUtils.h>
+#include <utils/Log.h>
+
+#include "ZipFile.h"
+
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+
+#include <memory.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Some environments require the "b", some choke on it.
+ */
+#define FILE_OPEN_RO        "rb"
+#define FILE_OPEN_RW        "r+b"
+#define FILE_OPEN_RW_CREATE "w+b"
+
+/* should live somewhere else? */
+static status_t errnoToStatus(int err)
+{
+    if (err == ENOENT)
+        return NAME_NOT_FOUND;
+    else if (err == EACCES)
+        return PERMISSION_DENIED;
+    else
+        return UNKNOWN_ERROR;
+}
+
+/*
+ * Open a file and parse its guts.
+ */
+status_t ZipFile::open(const char* zipFileName, int flags)
+{
+    bool newArchive = false;
+
+    assert(mZipFp == NULL);     // no reopen
+
+    if ((flags & kOpenTruncate))
+        flags |= kOpenCreate;           // trunc implies create
+
+    if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
+        return INVALID_OPERATION;       // not both
+    if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
+        return INVALID_OPERATION;       // not neither
+    if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
+        return INVALID_OPERATION;       // create requires write
+
+    if (flags & kOpenTruncate) {
+        newArchive = true;
+    } else {
+        newArchive = (access(zipFileName, F_OK) != 0);
+        if (!(flags & kOpenCreate) && newArchive) {
+            /* not creating, must already exist */
+            LOGD("File %s does not exist", zipFileName);
+            return NAME_NOT_FOUND;
+        }
+    }
+
+    /* open the file */
+    const char* openflags;
+    if (flags & kOpenReadWrite) {
+        if (newArchive)
+            openflags = FILE_OPEN_RW_CREATE;
+        else
+            openflags = FILE_OPEN_RW;
+    } else {
+        openflags = FILE_OPEN_RO;
+    }
+    mZipFp = fopen(zipFileName, openflags);
+    if (mZipFp == NULL) {
+        int err = errno;
+        LOGD("fopen failed: %d\n", err);
+        return errnoToStatus(err);
+    }
+
+    status_t result;
+    if (!newArchive) {
+        /*
+         * Load the central directory.  If that fails, then this probably
+         * isn't a Zip archive.
+         */
+        result = readCentralDir();
+    } else {
+        /*
+         * Newly-created.  The EndOfCentralDir constructor actually
+         * sets everything to be the way we want it (all zeroes).  We
+         * set mNeedCDRewrite so that we create *something* if the
+         * caller doesn't add any files.  (We could also just unlink
+         * the file if it's brand new and nothing was added, but that's
+         * probably doing more than we really should -- the user might
+         * have a need for empty zip files.)
+         */
+        mNeedCDRewrite = true;
+        result = NO_ERROR;
+    }
+
+    if (flags & kOpenReadOnly)
+        mReadOnly = true;
+    else
+        assert(!mReadOnly);
+
+    return result;
+}
+
+/*
+ * Return the Nth entry in the archive.
+ */
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
+{
+    if (idx < 0 || idx >= (int) mEntries.size())
+        return NULL;
+
+    return mEntries[idx];
+}
+
+/*
+ * Find an entry by name.
+ */
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+{
+    /*
+     * Do a stupid linear string-compare search.
+     *
+     * There are various ways to speed this up, especially since it's rare
+     * to intermingle changes to the archive with "get by name" calls.  We
+     * don't want to sort the mEntries vector itself, however, because
+     * it's used to recreate the Central Directory.
+     *
+     * (Hash table works, parallel list of pointers in sorted order is good.)
+     */
+    int idx;
+
+    for (idx = mEntries.size()-1; idx >= 0; idx--) {
+        ZipEntry* pEntry = mEntries[idx];
+        if (!pEntry->getDeleted() &&
+            strcmp(fileName, pEntry->getFileName()) == 0)
+        {
+            return pEntry;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Empty the mEntries vector.
+ */
+void ZipFile::discardEntries(void)
+{
+    int count = mEntries.size();
+
+    while (--count >= 0)
+        delete mEntries[count];
+
+    mEntries.clear();
+}
+
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end.  In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards.  The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly.  If the wrong value ends up in the EOCD
+ * area, we're hosed.  This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+status_t ZipFile::readCentralDir(void)
+{
+    status_t result = NO_ERROR;
+    unsigned char* buf = NULL;
+    off_t fileLength, seekStart;
+    long readAmount;
+    int i;
+
+    fseek(mZipFp, 0, SEEK_END);
+    fileLength = ftell(mZipFp);
+    rewind(mZipFp);
+
+    /* too small to be a ZIP archive? */
+    if (fileLength < EndOfCentralDir::kEOCDLen) {
+        LOGD("Length is %ld -- too small\n", (long)fileLength);
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
+    if (buf == NULL) {
+        LOGD("Failure allocating %d bytes for EOCD search",
+             EndOfCentralDir::kMaxEOCDSearch);
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
+        seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
+        readAmount = EndOfCentralDir::kMaxEOCDSearch;
+    } else {
+        seekStart = 0;
+        readAmount = (long) fileLength;
+    }
+    if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
+        LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /* read the last part of the file into the buffer */
+    if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
+        LOGD("short file? wanted %ld\n", readAmount);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /* find the end-of-central-dir magic */
+    for (i = readAmount - 4; i >= 0; i--) {
+        if (buf[i] == 0x50 &&
+            ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
+        {
+            LOGV("+++ Found EOCD at buf+%d\n", i);
+            break;
+        }
+    }
+    if (i < 0) {
+        LOGD("EOCD not found, not Zip\n");
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    /* extract eocd values */
+    result = mEOCD.readBuf(buf + i, readAmount - i);
+    if (result != NO_ERROR) {
+        LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+        goto bail;
+    }
+    //mEOCD.dump();
+
+    if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
+        mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
+    {
+        LOGD("Archive spanning not supported\n");
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    /*
+     * So far so good.  "mCentralDirSize" is the size in bytes of the
+     * central directory, so we can just seek back that far to find it.
+     * We can also seek forward mCentralDirOffset bytes from the
+     * start of the file.
+     *
+     * We're not guaranteed to have the rest of the central dir in the
+     * buffer, nor are we guaranteed that the central dir will have any
+     * sort of convenient size.  We need to skip to the start of it and
+     * read the header, then the other goodies.
+     *
+     * The only thing we really need right now is the file comment, which
+     * we're hoping to preserve.
+     */
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        LOGD("Failure seeking to central dir offset %ld\n",
+             mEOCD.mCentralDirOffset);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * Loop through and read the central dir entries.
+     */
+    LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+    int entry;
+    for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
+        ZipEntry* pEntry = new ZipEntry;
+
+        result = pEntry->initFromCDE(mZipFp);
+        if (result != NO_ERROR) {
+            LOGD("initFromCDE failed\n");
+            delete pEntry;
+            goto bail;
+        }
+
+        mEntries.add(pEntry);
+    }
+
+
+    /*
+     * If all went well, we should now be back at the EOCD.
+     */
+    {
+        unsigned char checkBuf[4];
+        if (fread(checkBuf, 1, 4, mZipFp) != 4) {
+            LOGD("EOCD check read failed\n");
+            result = INVALID_OPERATION;
+            goto bail;
+        }
+        if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
+            LOGD("EOCD read check failed\n");
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        LOGV("+++ EOCD read check passed\n");
+    }
+
+bail:
+    delete[] buf;
+    return result;
+}
+
+
+/*
+ * Add a new file to the archive.
+ *
+ * This requires creating and populating a ZipEntry structure, and copying
+ * the data into the file at the appropriate position.  The "appropriate
+ * position" is the current location of the central directory, which we
+ * casually overwrite (we can put it back later).
+ *
+ * If we were concerned about safety, we would want to make all changes
+ * in a temp file and then overwrite the original after everything was
+ * safely written.  Not really a concern for us.
+ */
+status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
+    const char* storageName, int sourceType, int compressionMethod,
+    ZipEntry** ppEntry)
+{
+    ZipEntry* pEntry = NULL;
+    status_t result = NO_ERROR;
+    long lfhPosn, startPosn, endPosn, uncompressedLen;
+    FILE* inputFp = NULL;
+    unsigned long crc;
+    time_t modWhen;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+
+    assert(compressionMethod == ZipEntry::kCompressDeflated ||
+           compressionMethod == ZipEntry::kCompressStored);
+
+    /* make sure we're in a reasonable state */
+    assert(mZipFp != NULL);
+    assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+    /* make sure it doesn't already exist */
+    if (getEntryByName(storageName) != NULL)
+        return ALREADY_EXISTS;
+
+    if (!data) {
+        inputFp = fopen(fileName, FILE_OPEN_RO);
+        if (inputFp == NULL)
+            return errnoToStatus(errno);
+    }
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    pEntry = new ZipEntry;
+    pEntry->initNew(storageName, NULL);
+
+    /*
+     * From here on out, failures are more interesting.
+     */
+    mNeedCDRewrite = true;
+
+    /*
+     * Write the LFH, even though it's still mostly blank.  We need it
+     * as a place-holder.  In theory the LFH isn't necessary, but in
+     * practice some utilities demand it.
+     */
+    lfhPosn = ftell(mZipFp);
+    pEntry->mLFH.write(mZipFp);
+    startPosn = ftell(mZipFp);
+
+    /*
+     * Copy the data in, possibly compressing it as we go.
+     */
+    if (sourceType == ZipEntry::kCompressStored) {
+        if (compressionMethod == ZipEntry::kCompressDeflated) {
+            bool failed = false;
+            result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
+            if (result != NO_ERROR) {
+                LOGD("compression failed, storing\n");
+                failed = true;
+            } else {
+                /*
+                 * Make sure it has compressed "enough".  This probably ought
+                 * to be set through an API call, but I don't expect our
+                 * criteria to change over time.
+                 */
+                long src = inputFp ? ftell(inputFp) : size;
+                long dst = ftell(mZipFp) - startPosn;
+                if (dst + (dst / 10) > src) {
+                    LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+                        src, dst);
+                    failed = true;
+                }
+            }
+
+            if (failed) {
+                compressionMethod = ZipEntry::kCompressStored;
+                if (inputFp) rewind(inputFp);
+                fseek(mZipFp, startPosn, SEEK_SET);
+                /* fall through to kCompressStored case */
+            }
+        }
+        /* handle "no compression" request, or failed compression from above */
+        if (compressionMethod == ZipEntry::kCompressStored) {
+            if (inputFp) {
+                result = copyFpToFp(mZipFp, inputFp, &crc);
+            } else {
+                result = copyDataToFp(mZipFp, data, size, &crc);
+            }
+            if (result != NO_ERROR) {
+                // don't need to truncate; happens in CDE rewrite
+                LOGD("failed copying data in\n");
+                goto bail;
+            }
+        }
+
+        // currently seeked to end of file
+        uncompressedLen = inputFp ? ftell(inputFp) : size;
+    } else if (sourceType == ZipEntry::kCompressDeflated) {
+        /* we should support uncompressed-from-compressed, but it's not
+         * important right now */
+        assert(compressionMethod == ZipEntry::kCompressDeflated);
+
+        bool scanResult;
+        int method;
+        long compressedLen;
+
+        scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
+                        &compressedLen, &crc);
+        if (!scanResult || method != ZipEntry::kCompressDeflated) {
+            LOGD("this isn't a deflated gzip file?");
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+
+        result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
+        if (result != NO_ERROR) {
+            LOGD("failed copying gzip data in\n");
+            goto bail;
+        }
+    } else {
+        assert(false);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * We could write the "Data Descriptor", but there doesn't seem to
+     * be any point since we're going to go back and write the LFH.
+     *
+     * Update file offsets.
+     */
+    endPosn = ftell(mZipFp);            // seeked to end of compressed data
+
+    /*
+     * Success!  Fill out new values.
+     */
+    pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
+        compressionMethod);
+    modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
+    pEntry->setModWhen(modWhen);
+    pEntry->setLFHOffset(lfhPosn);
+    mEOCD.mNumEntries++;
+    mEOCD.mTotalNumEntries++;
+    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
+    mEOCD.mCentralDirOffset = endPosn;
+
+    /*
+     * Go back and write the LFH.
+     */
+    if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+    pEntry->mLFH.write(mZipFp);
+
+    /*
+     * Add pEntry to the list.
+     */
+    mEntries.add(pEntry);
+    if (ppEntry != NULL)
+        *ppEntry = pEntry;
+    pEntry = NULL;
+
+bail:
+    if (inputFp != NULL)
+        fclose(inputFp);
+    delete pEntry;
+    return result;
+}
+
+/*
+ * Add an entry by copying it from another zip file.  If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+    int padding, ZipEntry** ppEntry)
+{
+    ZipEntry* pEntry = NULL;
+    status_t result;
+    long lfhPosn, endPosn;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+
+    /* make sure we're in a reasonable state */
+    assert(mZipFp != NULL);
+    assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    pEntry = new ZipEntry;
+    if (pEntry == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
+    if (result != NO_ERROR)
+        goto bail;
+    if (padding != 0) {
+        result = pEntry->addPadding(padding);
+        if (result != NO_ERROR)
+            goto bail;
+    }
+
+    /*
+     * From here on out, failures are more interesting.
+     */
+    mNeedCDRewrite = true;
+
+    /*
+     * Write the LFH.  Since we're not recompressing the data, we already
+     * have all of the fields filled out.
+     */
+    lfhPosn = ftell(mZipFp);
+    pEntry->mLFH.write(mZipFp);
+
+    /*
+     * Copy the data over.
+     *
+     * If the "has data descriptor" flag is set, we want to copy the DD
+     * fields as well.  This is a fixed-size area immediately following
+     * the data.
+     */
+    if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
+    {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    off_t copyLen;
+    copyLen = pSourceEntry->getCompressedLen();
+    if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
+        copyLen += ZipEntry::kDataDescriptorLen;
+
+    if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
+        != NO_ERROR)
+    {
+        LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * Update file offsets.
+     */
+    endPosn = ftell(mZipFp);
+
+    /*
+     * Success!  Fill out new values.
+     */
+    pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset
+    mEOCD.mNumEntries++;
+    mEOCD.mTotalNumEntries++;
+    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
+    mEOCD.mCentralDirOffset = endPosn;
+
+    /*
+     * Add pEntry to the list.
+     */
+    mEntries.add(pEntry);
+    if (ppEntry != NULL)
+        *ppEntry = pEntry;
+    pEntry = NULL;
+
+    result = NO_ERROR;
+
+bail:
+    delete pEntry;
+    return result;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data.
+ */
+status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
+{
+    unsigned char tmpBuf[32768];
+    size_t count;
+
+    *pCRC32 = crc32(0L, Z_NULL, 0);
+
+    while (1) {
+        count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
+        if (ferror(srcFp) || ferror(dstFp))
+            return errnoToStatus(errno);
+        if (count == 0)
+            break;
+
+        *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+            LOGD("fwrite %d bytes failed\n", (int) count);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "dstFp" will be seeked immediately past the data.
+ */
+status_t ZipFile::copyDataToFp(FILE* dstFp,
+    const void* data, size_t size, unsigned long* pCRC32)
+{
+    size_t count;
+
+    *pCRC32 = crc32(0L, Z_NULL, 0);
+    if (size > 0) {
+        *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
+        if (fwrite(data, 1, size, dstFp) != size) {
+            LOGD("fwrite %d bytes failed\n", (int) size);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Copy some of the bytes in "src" to "dst".
+ *
+ * If "pCRC32" is NULL, the CRC will not be computed.
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data just written.
+ */
+status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+    unsigned long* pCRC32)
+{
+    unsigned char tmpBuf[32768];
+    size_t count;
+
+    if (pCRC32 != NULL)
+        *pCRC32 = crc32(0L, Z_NULL, 0);
+
+    while (length) {
+        long readSize;
+        
+        readSize = sizeof(tmpBuf);
+        if (readSize > length)
+            readSize = length;
+
+        count = fread(tmpBuf, 1, readSize, srcFp);
+        if ((long) count != readSize) {     // error or unexpected EOF
+            LOGD("fread %d bytes failed\n", (int) readSize);
+            return UNKNOWN_ERROR;
+        }
+
+        if (pCRC32 != NULL)
+            *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+            LOGD("fwrite %d bytes failed\n", (int) count);
+            return UNKNOWN_ERROR;
+        }
+
+        length -= readSize;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Compress all of the data in "srcFp" and write it to "dstFp".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the compressed data.
+ */
+status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
+    const void* data, size_t size, unsigned long* pCRC32)
+{
+    status_t result = NO_ERROR;
+    const size_t kBufSize = 32768;
+    unsigned char* inBuf = NULL;
+    unsigned char* outBuf = NULL;
+    z_stream zstream;
+    bool atEof = false;     // no feof() aviailable yet
+    unsigned long crc;
+    int zerr;
+
+    /*
+     * Create an input buffer and an output buffer.
+     */
+    inBuf = new unsigned char[kBufSize];
+    outBuf = new unsigned char[kBufSize];
+    if (inBuf == NULL || outBuf == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    /*
+     * Initialize the zlib stream.
+     */
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = NULL;
+    zstream.avail_in = 0;
+    zstream.next_out = outBuf;
+    zstream.avail_out = kBufSize;
+    zstream.data_type = Z_UNKNOWN;
+
+    zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
+        Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+    if (zerr != Z_OK) {
+        result = UNKNOWN_ERROR;
+        if (zerr == Z_VERSION_ERROR) {
+            LOGE("Installed zlib is not compatible with linked version (%s)\n",
+                ZLIB_VERSION);
+        } else {
+            LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+        }
+        goto bail;
+    }
+
+    crc = crc32(0L, Z_NULL, 0);
+
+    /*
+     * Loop while we have data.
+     */
+    do {
+        size_t getSize;
+        int flush;
+
+        /* only read if the input buffer is empty */
+        if (zstream.avail_in == 0 && !atEof) {
+            LOGV("+++ reading %d bytes\n", (int)kBufSize);
+            if (data) {
+                getSize = size > kBufSize ? kBufSize : size;
+                memcpy(inBuf, data, getSize);
+                data = ((const char*)data) + getSize;
+                size -= getSize;
+            } else {
+                getSize = fread(inBuf, 1, kBufSize, srcFp);
+                if (ferror(srcFp)) {
+                    LOGD("deflate read failed (errno=%d)\n", errno);
+                    goto z_bail;
+                }
+            }
+            if (getSize < kBufSize) {
+                LOGV("+++  got %d bytes, EOF reached\n",
+                    (int)getSize);
+                atEof = true;
+            }
+
+            crc = crc32(crc, inBuf, getSize);
+
+            zstream.next_in = inBuf;
+            zstream.avail_in = getSize;
+        }
+
+        if (atEof)
+            flush = Z_FINISH;       /* tell zlib that we're done */
+        else
+            flush = Z_NO_FLUSH;     /* more to come! */
+
+        zerr = deflate(&zstream, flush);
+        if (zerr != Z_OK && zerr != Z_STREAM_END) {
+            LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+            result = UNKNOWN_ERROR;
+            goto z_bail;
+        }
+
+        /* write when we're full or when we're done */
+        if (zstream.avail_out == 0 ||
+            (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
+        {
+            LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+            if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
+                (size_t)(zstream.next_out - outBuf))
+            {
+                LOGD("write %d failed in deflate\n",
+                    (int) (zstream.next_out - outBuf));
+                goto z_bail;
+            }
+
+            zstream.next_out = outBuf;
+            zstream.avail_out = kBufSize;
+        }
+    } while (zerr == Z_OK);
+
+    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
+
+    *pCRC32 = crc;
+
+z_bail:
+    deflateEnd(&zstream);        /* free up any allocated structures */
+
+bail:
+    delete[] inBuf;
+    delete[] outBuf;
+
+    return result;
+}
+
+/*
+ * Mark an entry as deleted.
+ *
+ * We will eventually need to crunch the file down, but if several files
+ * are being removed (perhaps as part of an "update" process) we can make
+ * things considerably faster by deferring the removal to "flush" time.
+ */
+status_t ZipFile::remove(ZipEntry* pEntry)
+{
+    /*
+     * Should verify that pEntry is actually part of this archive, and
+     * not some stray ZipEntry from a different file.
+     */
+
+    /* mark entry as deleted, and mark archive as dirty */
+    pEntry->setDeleted();
+    mNeedCDRewrite = true;
+    return NO_ERROR;
+}
+
+/*
+ * Flush any pending writes.
+ *
+ * In particular, this will crunch out deleted entries, and write the
+ * Central Directory and EOCD if we have stomped on them.
+ */
+status_t ZipFile::flush(void)
+{
+    status_t result = NO_ERROR;
+    long eocdPosn;
+    int i, count;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+    if (!mNeedCDRewrite)
+        return NO_ERROR;
+
+    assert(mZipFp != NULL);
+
+    result = crunchArchive();
+    if (result != NO_ERROR)
+        return result;
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
+        return UNKNOWN_ERROR;
+
+    count = mEntries.size();
+    for (i = 0; i < count; i++) {
+        ZipEntry* pEntry = mEntries[i];
+        pEntry->mCDE.write(mZipFp);
+    }
+
+    eocdPosn = ftell(mZipFp);
+    mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
+
+    mEOCD.write(mZipFp);
+
+    /*
+     * If we had some stuff bloat up during compression and get replaced
+     * with plain files, or if we deleted some entries, there's a lot
+     * of wasted space at the end of the file.  Remove it now.
+     */
+    if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
+        LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+        // not fatal
+    }
+
+    /* should we clear the "newly added" flag in all entries now? */
+
+    mNeedCDRewrite = false;
+    return NO_ERROR;
+}
+
+/*
+ * Crunch deleted files out of an archive by shifting the later files down.
+ *
+ * Because we're not using a temp file, we do the operation inside the
+ * current file.
+ */
+status_t ZipFile::crunchArchive(void)
+{
+    status_t result = NO_ERROR;
+    int i, count;
+    long delCount, adjust;
+
+#if 0
+    printf("CONTENTS:\n");
+    for (i = 0; i < (int) mEntries.size(); i++) {
+        printf(" %d: lfhOff=%ld del=%d\n",
+            i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
+    }
+    printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset);
+#endif
+
+    /*
+     * Roll through the set of files, shifting them as appropriate.  We
+     * could probably get a slight performance improvement by sliding
+     * multiple files down at once (because we could use larger reads
+     * when operating on batches of small files), but it's not that useful.
+     */
+    count = mEntries.size();
+    delCount = adjust = 0;
+    for (i = 0; i < count; i++) {
+        ZipEntry* pEntry = mEntries[i];
+        long span;
+
+        if (pEntry->getLFHOffset() != 0) {
+            long nextOffset;
+
+            /* Get the length of this entry by finding the offset
+             * of the next entry.  Directory entries don't have
+             * file offsets, so we need to find the next non-directory
+             * entry.
+             */
+            nextOffset = 0;
+            for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
+                nextOffset = mEntries[ii]->getLFHOffset();
+            if (nextOffset == 0)
+                nextOffset = mEOCD.mCentralDirOffset;
+            span = nextOffset - pEntry->getLFHOffset();
+
+            assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
+        } else {
+            /* This is a directory entry.  It doesn't have
+             * any actual file contents, so there's no need to
+             * move anything.
+             */
+            span = 0;
+        }
+
+        //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
+        //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
+
+        if (pEntry->getDeleted()) {
+            adjust += span;
+            delCount++;
+
+            delete pEntry;
+            mEntries.removeAt(i);
+
+            /* adjust loop control */
+            count--;
+            i--;
+        } else if (span != 0 && adjust > 0) {
+            /* shuffle this entry back */
+            //printf("+++ Shuffling '%s' back %ld\n",
+            //    pEntry->getFileName(), adjust);
+            result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
+                        pEntry->getLFHOffset(), span);
+            if (result != NO_ERROR) {
+                /* this is why you use a temp file */
+                LOGE("error during crunch - archive is toast\n");
+                return result;
+            }
+
+            pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
+        }
+    }
+
+    /*
+     * Fix EOCD info.  We have to wait until the end to do some of this
+     * because we use mCentralDirOffset to determine "span" for the
+     * last entry.
+     */
+    mEOCD.mCentralDirOffset -= adjust;
+    mEOCD.mNumEntries -= delCount;
+    mEOCD.mTotalNumEntries -= delCount;
+    mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()
+
+    assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
+    assert(mEOCD.mNumEntries == count);
+
+    return result;
+}
+
+/*
+ * Works like memmove(), but on pieces of a file.
+ */
+status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
+{
+    if (dst == src || n <= 0)
+        return NO_ERROR;
+
+    unsigned char readBuf[32768];
+
+    if (dst < src) {
+        /* shift stuff toward start of file; must read from start */
+        while (n != 0) {
+            size_t getSize = sizeof(readBuf);
+            if (getSize > n)
+                getSize = n;
+
+            if (fseek(fp, (long) src, SEEK_SET) != 0) {
+                LOGD("filemove src seek %ld failed\n", (long) src);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fread(readBuf, 1, getSize, fp) != getSize) {
+                LOGD("filemove read %ld off=%ld failed\n",
+                    (long) getSize, (long) src);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fseek(fp, (long) dst, SEEK_SET) != 0) {
+                LOGD("filemove dst seek %ld failed\n", (long) dst);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fwrite(readBuf, 1, getSize, fp) != getSize) {
+                LOGD("filemove write %ld off=%ld failed\n",
+                    (long) getSize, (long) dst);
+                return UNKNOWN_ERROR;
+            }
+
+            src += getSize;
+            dst += getSize;
+            n -= getSize;
+        }
+    } else {
+        /* shift stuff toward end of file; must read from end */
+        assert(false);      // write this someday, maybe
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
+/*
+ * Get the modification time from a file descriptor.
+ */
+time_t ZipFile::getModTime(int fd)
+{
+    struct stat sb;
+
+    if (fstat(fd, &sb) < 0) {
+        LOGD("HEY: fstat on fd %d failed\n", fd);
+        return (time_t) -1;
+    }
+
+    return sb.st_mtime;
+}
+
+
+#if 0       /* this is a bad idea */
+/*
+ * Get a copy of the Zip file descriptor.
+ *
+ * We don't allow this if the file was opened read-write because we tend
+ * to leave the file contents in an uncertain state between calls to
+ * flush().  The duplicated file descriptor should only be valid for reads.
+ */
+int ZipFile::getZipFd(void) const
+{
+    if (!mReadOnly)
+        return INVALID_OPERATION;
+    assert(mZipFp != NULL);
+
+    int fd;
+    fd = dup(fileno(mZipFp));
+    if (fd < 0) {
+        LOGD("didn't work, errno=%d\n", errno);
+    }
+
+    return fd;
+}
+#endif
+
+
+#if 0
+/*
+ * Expand data.
+ */
+bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
+{
+    return false;
+}
+#endif
+
+// free the memory when you're done
+void* ZipFile::uncompress(const ZipEntry* entry)
+{
+    size_t unlen = entry->getUncompressedLen();
+    size_t clen = entry->getCompressedLen();
+
+    void* buf = malloc(unlen);
+    if (buf == NULL) {
+        return NULL;
+    }
+
+    fseek(mZipFp, 0, SEEK_SET);
+
+    off_t offset = entry->getFileOffset();
+    if (fseek(mZipFp, offset, SEEK_SET) != 0) {
+        goto bail;
+    }
+
+    switch (entry->getCompressionMethod())
+    {
+        case ZipEntry::kCompressStored: {
+            ssize_t amt = fread(buf, 1, unlen, mZipFp);
+            if (amt != (ssize_t)unlen) {
+                goto bail;
+            }
+#if 0
+            printf("data...\n");
+            const unsigned char* p = (unsigned char*)buf;
+            const unsigned char* end = p+unlen;
+            for (int i=0; i<32 && p < end; i++) {
+                printf("0x%08x ", (int)(offset+(i*0x10)));
+                for (int j=0; j<0x10 && p < end; j++) {
+                    printf(" %02x", *p);
+                    p++;
+                }
+                printf("\n");
+            }
+#endif
+
+            }
+            break;
+        case ZipEntry::kCompressDeflated: {
+            if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
+                goto bail;
+            }
+            }
+            break;
+        default:
+            goto bail;
+    }
+    return buf;
+
+bail:
+    free(buf);
+    return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipFile::EndOfCentralDir
+ * ===========================================================================
+ */
+
+/*
+ * Read the end-of-central-dir fields.
+ *
+ * "buf" should be positioned at the EOCD signature, and should contain
+ * the entire EOCD area including the comment.
+ */
+status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
+{
+    /* don't allow re-use */
+    assert(mComment == NULL);
+
+    if (len < kEOCDLen) {
+        /* looks like ZIP file got truncated */
+        LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+            kEOCDLen, len);
+        return INVALID_OPERATION;
+    }
+
+    /* this should probably be an assert() */
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
+        return UNKNOWN_ERROR;
+
+    mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
+    mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
+    mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
+    mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
+    mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
+    mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
+    mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
+
+    // TODO: validate mCentralDirOffset
+
+    if (mCommentLen > 0) {
+        if (kEOCDLen + mCommentLen > len) {
+            LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+                kEOCDLen, mCommentLen, len);
+            return UNKNOWN_ERROR;
+        }
+        mComment = new unsigned char[mCommentLen];
+        memcpy(mComment, buf + kEOCDLen, mCommentLen);
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Write an end-of-central-directory section.
+ */
+status_t ZipFile::EndOfCentralDir::write(FILE* fp)
+{
+    unsigned char buf[kEOCDLen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
+    ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
+    ZipEntry::putShortLE(&buf[0x08], mNumEntries);
+    ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
+    ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
+    ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
+    ZipEntry::putShortLE(&buf[0x14], mCommentLen);
+
+    if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
+        return UNKNOWN_ERROR;
+    if (mCommentLen > 0) {
+        assert(mComment != NULL);
+        if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Dump the contents of an EndOfCentralDir object.
+ */
+void ZipFile::EndOfCentralDir::dump(void) const
+{
+    LOGD(" EndOfCentralDir contents:\n");
+    LOGD("  diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+        mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
+    LOGD("  centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+        mCentralDirSize, mCentralDirOffset, mCommentLen);
+}
+
diff --git a/tools/zipalign/ZipFile.h b/tools/zipalign/ZipFile.h
new file mode 100644
index 0000000..dbbd072
--- /dev/null
+++ b/tools/zipalign/ZipFile.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// General-purpose Zip archive access.  This class allows both reading and
+// writing to Zip archives, including deletion of existing entries.
+//
+#ifndef __LIBS_ZIPFILE_H
+#define __LIBS_ZIPFILE_H
+
+#include <utils/Vector.h>
+#include <utils/Errors.h>
+#include <stdio.h>
+
+#include "ZipEntry.h"
+
+namespace android {
+
+/*
+ * Manipulate a Zip archive.
+ *
+ * Some changes will not be visible in the until until "flush" is called.
+ *
+ * The correct way to update a file archive is to make all changes to a
+ * copy of the archive in a temporary file, and then unlink/rename over
+ * the original after everything completes.  Because we're only interested
+ * in using this for packaging, we don't worry about such things.  Crashing
+ * after making changes and before flush() completes could leave us with
+ * an unusable Zip archive.
+ */
+class ZipFile {
+public:
+    ZipFile(void)
+      : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
+      {}
+    ~ZipFile(void) {
+        if (!mReadOnly)
+            flush();
+        if (mZipFp != NULL)
+            fclose(mZipFp);
+        discardEntries();
+    }
+
+    /*
+     * Open a new or existing archive.
+     */
+    typedef enum {
+        kOpenReadOnly   = 0x01,
+        kOpenReadWrite  = 0x02,
+        kOpenCreate     = 0x04,     // create if it doesn't exist
+        kOpenTruncate   = 0x08,     // if it exists, empty it
+    };
+    status_t open(const char* zipFileName, int flags);
+
+    /*
+     * Add a file to the end of the archive.  Specify whether you want the
+     * library to try to store it compressed.
+     *
+     * If "storageName" is specified, the archive will use that instead
+     * of "fileName".
+     *
+     * If there is already an entry with the same name, the call fails.
+     * Existing entries with the same name must be removed first.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const char* fileName, int compressionMethod,
+        ZipEntry** ppEntry)
+    {
+        return add(fileName, fileName, compressionMethod, ppEntry);
+    }
+    status_t add(const char* fileName, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry)
+    {
+        return addCommon(fileName, NULL, 0, storageName,
+                         ZipEntry::kCompressStored,
+                         compressionMethod, ppEntry);
+    }
+
+    /*
+     * Add a file that is already compressed with gzip.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t addGzip(const char* fileName, const char* storageName,
+        ZipEntry** ppEntry)
+    {
+        return addCommon(fileName, NULL, 0, storageName,
+                         ZipEntry::kCompressDeflated,
+                         ZipEntry::kCompressDeflated, ppEntry);
+    }
+
+    /*
+     * Add a file from an in-memory data buffer.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const void* data, size_t size, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry)
+    {
+        return addCommon(NULL, data, size, storageName,
+                         ZipEntry::kCompressStored,
+                         compressionMethod, ppEntry);
+    }
+
+    /*
+     * Add an entry by copying it from another zip file.  If "padding" is
+     * nonzero, the specified number of bytes will be added to the "extra"
+     * field in the header.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+        int padding, ZipEntry** ppEntry);
+
+    /*
+     * Mark an entry as having been removed.  It is not actually deleted
+     * from the archive or our internal data structures until flush() is
+     * called.
+     */
+    status_t remove(ZipEntry* pEntry);
+
+    /*
+     * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.
+     */
+    status_t flush(void);
+
+    /*
+     * Expand the data into the buffer provided.  The buffer must hold
+     * at least <uncompressed len> bytes.  Variation expands directly
+     * to a file.
+     *
+     * Returns "false" if an error was encountered in the compressed data.
+     */
+    //bool uncompress(const ZipEntry* pEntry, void* buf) const;
+    //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
+    void* uncompress(const ZipEntry* pEntry);
+
+    /*
+     * Get an entry, by name.  Returns NULL if not found.
+     *
+     * Does not return entries pending deletion.
+     */
+    ZipEntry* getEntryByName(const char* fileName) const;
+
+    /*
+     * Get the Nth entry in the archive.
+     *
+     * This will return an entry that is pending deletion.
+     */
+    int getNumEntries(void) const { return mEntries.size(); }
+    ZipEntry* getEntryByIndex(int idx) const;
+
+private:
+    /* these are private and not defined */
+    ZipFile(const ZipFile& src);
+    ZipFile& operator=(const ZipFile& src);
+
+    class EndOfCentralDir {
+    public:
+        EndOfCentralDir(void) :
+            mDiskNumber(0),
+            mDiskWithCentralDir(0),
+            mNumEntries(0),
+            mTotalNumEntries(0),
+            mCentralDirSize(0),
+            mCentralDirOffset(0),
+            mCommentLen(0),
+            mComment(NULL)
+            {}
+        virtual ~EndOfCentralDir(void) {
+            delete[] mComment;
+        }
+
+        status_t readBuf(const unsigned char* buf, int len);
+        status_t write(FILE* fp);
+
+        //unsigned long   mSignature;
+        unsigned short  mDiskNumber;
+        unsigned short  mDiskWithCentralDir;
+        unsigned short  mNumEntries;
+        unsigned short  mTotalNumEntries;
+        unsigned long   mCentralDirSize;
+        unsigned long   mCentralDirOffset;      // offset from first disk
+        unsigned short  mCommentLen;
+        unsigned char*  mComment;
+
+        enum {
+            kSignature      = 0x06054b50,
+            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment
+
+            kMaxCommentLen  = 65535,    // longest possible in ushort
+            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
+
+        };
+
+        void dump(void) const;
+    };
+
+
+    /* read all entries in the central dir */
+    status_t readCentralDir(void);
+
+    /* crunch deleted entries out */
+    status_t crunchArchive(void);
+
+    /* clean up mEntries */
+    void discardEntries(void);
+
+    /* common handler for all "add" functions */
+    status_t addCommon(const char* fileName, const void* data, size_t size,
+        const char* storageName, int sourceType, int compressionMethod,
+        ZipEntry** ppEntry);
+
+    /* copy all of "srcFp" into "dstFp" */
+    status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
+    /* copy all of "data" into "dstFp" */
+    status_t copyDataToFp(FILE* dstFp,
+        const void* data, size_t size, unsigned long* pCRC32);
+    /* copy some of "srcFp" into "dstFp" */
+    status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+        unsigned long* pCRC32);
+    /* like memmove(), but on parts of a single file */
+    status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
+    /* compress all of "srcFp" into "dstFp", using Deflate */
+    status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
+        const void* data, size_t size, unsigned long* pCRC32);
+
+    /* get modification date from a file descriptor */
+    time_t getModTime(int fd);
+
+    /*
+     * We use stdio FILE*, which gives us buffering but makes dealing
+     * with files >2GB awkward.  Until we support Zip64, we're fine.
+     */
+    FILE*           mZipFp;             // Zip file pointer
+
+    /* one of these per file */
+    EndOfCentralDir mEOCD;
+
+    /* did we open this read-only? */
+    bool            mReadOnly;
+
+    /* set this when we trash the central dir */
+    bool            mNeedCDRewrite;
+
+    /*
+     * One ZipEntry per entry in the zip file.  I'm using pointers instead
+     * of objects because it's easier than making operator= work for the
+     * classes and sub-classes.
+     */
+    Vector<ZipEntry*>   mEntries;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPFILE_H