summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/markup
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/inspector/markup')
-rw-r--r--devtools/client/inspector/markup/markup.js1878
-rw-r--r--devtools/client/inspector/markup/markup.xhtml105
-rw-r--r--devtools/client/inspector/markup/moz.build16
-rw-r--r--devtools/client/inspector/markup/test/.eslintrc.js6
-rw-r--r--devtools/client/inspector/markup/test/actor_events_form.js62
-rw-r--r--devtools/client/inspector/markup/test/browser.ini155
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_accessibility_focus_blur.js59
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js277
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_accessibility_navigation_after_edit.js126
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_accessibility_semantics.js100
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_anonymous_01.js44
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_anonymous_02.js31
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_anonymous_03.js34
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_anonymous_04.js37
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_copy_image_data.js67
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_01.js76
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_02.js106
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_03.js54
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js51
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js49
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_distance.js49
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_dragRootNode.js22
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_draggable.js63
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js34
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js48
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_reorder.js109
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_dragdrop_tooltip.js35
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events-overflow.js91
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js61
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events1.js149
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events2.js163
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events3.js161
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_form.js61
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js237
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js271
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.11.1.js196
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.2.js191
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.3.js224
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.4.js287
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.6.js388
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_1.7.js234
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_events_jquery_2.1.1.js196
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_html_edit_01.js84
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_html_edit_02.js119
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_html_edit_03.js200
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_image_tooltip.js60
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_image_tooltip_mutations.js83
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_keybindings_01.js49
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_keybindings_02.js32
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_keybindings_03.js50
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_keybindings_04.js58
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_keybindings_delete_attributes.js63
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_keybindings_scrolltonode.js87
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_01.js128
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_02.js38
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_03.js38
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_04.js116
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_05.js69
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_06.js53
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_links_07.js109
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_load_01.js71
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_mutation_01.js340
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_mutation_02.js159
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_navigation.js147
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_node_names.js28
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_node_names_namespaced.js43
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_node_not_displayed_01.js35
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_node_not_displayed_02.js150
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_pagesize_01.js86
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_pagesize_02.js47
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_remove_xul_attributes.js28
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_search_01.js51
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_01.js68
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_02.js44
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_03.js51
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_04-backspace.js59
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_04-delete.js59
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_05.js77
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_06.js85
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js135
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_08.js132
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_09.js71
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_10.js34
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_11.js38
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_12.js98
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_13-other.js38
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_tag_edit_long-classname.js41
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_textcontent_display.js89
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js84
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js116
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_toggle_01.js58
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_toggle_02.js49
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_toggle_03.js35
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js44
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_void_elements_html.js44
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_void_elements_xhtml.js28
-rw-r--r--devtools/client/inspector/markup/test/browser_markup_whitespace.js66
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_anonymous.html34
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_dragdrop.html23
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_01.html87
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html40
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_edit.html48
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_events-overflow.html19
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_events1.html113
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_events2.html111
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_events3.html115
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_events_form.html11
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_events_jquery.html67
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_flashing.html15
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_html_mixed_case.html12
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_image_and_canvas.html24
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_image_and_canvas_2.html25
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_links.html42
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_mutation.html42
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_navigation.html28
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_not_displayed.html18
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_pagesize_01.html32
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_pagesize_02.html33
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_search.html11
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_svg_attributes.html8
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_toggle.html28
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_tooltip.pngbin0 -> 1095 bytes
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_void_elements.html18
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_void_elements.xhtml21
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_whitespace.html25
-rw-r--r--devtools/client/inspector/markup/test/doc_markup_xul.xul9
-rw-r--r--devtools/client/inspector/markup/test/head.js653
-rw-r--r--devtools/client/inspector/markup/test/helper_attributes_test_runner.js160
-rw-r--r--devtools/client/inspector/markup/test/helper_events_test_runner.js111
-rw-r--r--devtools/client/inspector/markup/test/helper_markup_accessibility_navigation.js70
-rw-r--r--devtools/client/inspector/markup/test/helper_outerhtml_test_runner.js82
-rw-r--r--devtools/client/inspector/markup/test/helper_style_attr_test_runner.js132
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.0.js1814
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.1.js2172
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.11.1_min.js4
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.2_min.js32
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.3_min.js19
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.4_min.js151
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.6_min.js16
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_1.7_min.js4
-rw-r--r--devtools/client/inspector/markup/test/lib_jquery_2.1.1_min.js4
-rw-r--r--devtools/client/inspector/markup/utils.js135
-rw-r--r--devtools/client/inspector/markup/views/element-container.js193
-rw-r--r--devtools/client/inspector/markup/views/element-editor.js560
-rw-r--r--devtools/client/inspector/markup/views/html-editor.js180
-rw-r--r--devtools/client/inspector/markup/views/markup-container.js720
-rw-r--r--devtools/client/inspector/markup/views/moz.build17
-rw-r--r--devtools/client/inspector/markup/views/read-only-container.js33
-rw-r--r--devtools/client/inspector/markup/views/read-only-editor.js43
-rw-r--r--devtools/client/inspector/markup/views/root-container.js55
-rw-r--r--devtools/client/inspector/markup/views/text-container.js40
-rw-r--r--devtools/client/inspector/markup/views/text-editor.js109
152 files changed, 19435 insertions, 0 deletions
diff --git a/devtools/client/inspector/markup/markup.js b/devtools/client/inspector/markup/markup.js
new file mode 100644
index 000000000..d6e9f8c11
--- /dev/null
+++ b/devtools/client/inspector/markup/markup.js
@@ -0,0 +1,1878 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const promise = require("promise");
+const Services = require("Services");
+const defer = require("devtools/shared/defer");
+const {Task} = require("devtools/shared/task");
+const nodeConstants = require("devtools/shared/dom-node-constants");
+const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
+const EventEmitter = require("devtools/shared/event-emitter");
+const {LocalizationHelper} = require("devtools/shared/l10n");
+const {PluralForm} = require("devtools/shared/plural-form");
+const {template} = require("devtools/shared/gcli/templater");
+const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
+const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
+const {UndoStack} = require("devtools/client/shared/undo");
+const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+const {PrefObserver} = require("devtools/client/styleeditor/utils");
+const HTMLEditor = require("devtools/client/inspector/markup/views/html-editor");
+const MarkupElementContainer = require("devtools/client/inspector/markup/views/element-container");
+const MarkupReadOnlyContainer = require("devtools/client/inspector/markup/views/read-only-container");
+const MarkupTextContainer = require("devtools/client/inspector/markup/views/text-container");
+const RootContainer = require("devtools/client/inspector/markup/views/root-container");
+
+const INSPECTOR_L10N =
+ new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+// Page size for pageup/pagedown
+const PAGE_SIZE = 10;
+const DEFAULT_MAX_CHILDREN = 100;
+const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000;
+const DRAG_DROP_AUTOSCROLL_EDGE_MAX_DISTANCE = 50;
+const DRAG_DROP_AUTOSCROLL_EDGE_RATIO = 0.1;
+const DRAG_DROP_MIN_AUTOSCROLL_SPEED = 2;
+const DRAG_DROP_MAX_AUTOSCROLL_SPEED = 8;
+const DRAG_DROP_HEIGHT_TO_SPEED = 500;
+const DRAG_DROP_HEIGHT_TO_SPEED_MIN = 0.5;
+const DRAG_DROP_HEIGHT_TO_SPEED_MAX = 1;
+const ATTR_COLLAPSE_ENABLED_PREF = "devtools.markup.collapseAttributes";
+const ATTR_COLLAPSE_LENGTH_PREF = "devtools.markup.collapseAttributeLength";
+
+/**
+ * Vocabulary for the purposes of this file:
+ *
+ * MarkupContainer - the structure that holds an editor and its
+ * immediate children in the markup panel.
+ * - MarkupElementContainer: markup container for element nodes
+ * - MarkupTextContainer: markup container for text / comment nodes
+ * - MarkupReadonlyContainer: markup container for other nodes
+ * Node - A content node.
+ * object.elt - A UI element in the markup panel.
+ */
+
+/**
+ * The markup tree. Manages the mapping of nodes to MarkupContainers,
+ * updating based on mutations, and the undo/redo bindings.
+ *
+ * @param {Inspector} inspector
+ * The inspector we're watching.
+ * @param {iframe} frame
+ * An iframe in which the caller has kindly loaded markup.xhtml.
+ */
+function MarkupView(inspector, frame, controllerWindow) {
+ this.inspector = inspector;
+ this.walker = this.inspector.walker;
+ this._frame = frame;
+ this.win = this._frame.contentWindow;
+ this.doc = this._frame.contentDocument;
+ this._elt = this.doc.querySelector("#root");
+ this.htmlEditor = new HTMLEditor(this.doc);
+
+ try {
+ this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
+ } catch (ex) {
+ this.maxChildren = DEFAULT_MAX_CHILDREN;
+ }
+
+ this.collapseAttributes =
+ Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
+ this.collapseAttributeLength =
+ Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
+
+ // Creating the popup to be used to show CSS suggestions.
+ // The popup will be attached to the toolbox document.
+ this.popup = new AutocompletePopup(inspector.toolbox.doc, {
+ autoSelect: true,
+ theme: "auto",
+ });
+
+ this.undo = new UndoStack();
+ this.undo.installController(controllerWindow);
+
+ this._containers = new Map();
+
+ // Binding functions that need to be called in scope.
+ this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
+ this._mutationObserver = this._mutationObserver.bind(this);
+ this._onDisplayChange = this._onDisplayChange.bind(this);
+ this._onMouseClick = this._onMouseClick.bind(this);
+ this._onMouseUp = this._onMouseUp.bind(this);
+ this._onNewSelection = this._onNewSelection.bind(this);
+ this._onCopy = this._onCopy.bind(this);
+ this._onFocus = this._onFocus.bind(this);
+ this._onMouseMove = this._onMouseMove.bind(this);
+ this._onMouseOut = this._onMouseOut.bind(this);
+ this._onToolboxPickerCanceled = this._onToolboxPickerCanceled.bind(this);
+ this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
+ this._onCollapseAttributesPrefChange =
+ this._onCollapseAttributesPrefChange.bind(this);
+ this._isImagePreviewTarget = this._isImagePreviewTarget.bind(this);
+ this._onBlur = this._onBlur.bind(this);
+
+ EventEmitter.decorate(this);
+
+ // Listening to various events.
+ this._elt.addEventListener("click", this._onMouseClick, false);
+ this._elt.addEventListener("mousemove", this._onMouseMove, false);
+ this._elt.addEventListener("mouseout", this._onMouseOut, false);
+ this._elt.addEventListener("blur", this._onBlur, true);
+ this.win.addEventListener("mouseup", this._onMouseUp);
+ this.win.addEventListener("copy", this._onCopy);
+ this._frame.addEventListener("focus", this._onFocus, false);
+ this.walker.on("mutations", this._mutationObserver);
+ this.walker.on("display-change", this._onDisplayChange);
+ this.inspector.selection.on("new-node-front", this._onNewSelection);
+ this.toolbox.on("picker-canceled", this._onToolboxPickerCanceled);
+ this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);
+
+ this._onNewSelection();
+ this._initTooltips();
+
+ this._prefObserver = new PrefObserver("devtools.markup");
+ this._prefObserver.on(ATTR_COLLAPSE_ENABLED_PREF,
+ this._onCollapseAttributesPrefChange);
+ this._prefObserver.on(ATTR_COLLAPSE_LENGTH_PREF,
+ this._onCollapseAttributesPrefChange);
+
+ this._initShortcuts();
+}
+
+MarkupView.prototype = {
+ /**
+ * How long does a node flash when it mutates (in ms).
+ */
+ CONTAINER_FLASHING_DURATION: 500,
+
+ _selectedContainer: null,
+
+ get toolbox() {
+ return this.inspector.toolbox;
+ },
+
+ /**
+ * Handle promise rejections for various asynchronous actions, and only log errors if
+ * the markup view still exists.
+ * This is useful to silence useless errors that happen when the markup view is
+ * destroyed while still initializing (and making protocol requests).
+ */
+ _handleRejectionIfNotDestroyed: function (e) {
+ if (!this._destroyer) {
+ console.error(e);
+ }
+ },
+
+ _initTooltips: function () {
+ // The tooltips will be attached to the toolbox document.
+ this.eventDetailsTooltip = new HTMLTooltip(this.toolbox.doc,
+ {type: "arrow"});
+ this.imagePreviewTooltip = new HTMLTooltip(this.toolbox.doc,
+ {type: "arrow", useXulWrapper: "true"});
+ this._enableImagePreviewTooltip();
+ },
+
+ _enableImagePreviewTooltip: function () {
+ this.imagePreviewTooltip.startTogglingOnHover(this._elt,
+ this._isImagePreviewTarget);
+ },
+
+ _disableImagePreviewTooltip: function () {
+ this.imagePreviewTooltip.stopTogglingOnHover();
+ },
+
+ _onToolboxPickerHover: function (event, nodeFront) {
+ this.showNode(nodeFront).then(() => {
+ this._showContainerAsHovered(nodeFront);
+ }, e => console.error(e));
+ },
+
+ /**
+ * If the element picker gets canceled, make sure and re-center the view on the
+ * current selected element.
+ */
+ _onToolboxPickerCanceled: function () {
+ if (this._selectedContainer) {
+ scrollIntoViewIfNeeded(this._selectedContainer.editor.elt);
+ }
+ },
+
+ isDragging: false,
+
+ _onMouseMove: function (event) {
+ let target = event.target;
+
+ // Auto-scroll if we're dragging.
+ if (this.isDragging) {
+ event.preventDefault();
+ this._autoScroll(event);
+ return;
+ }
+
+ // Show the current container as hovered and highlight it.
+ // This requires finding the current MarkupContainer (walking up the DOM).
+ while (!target.container) {
+ if (target.tagName.toLowerCase() === "body") {
+ return;
+ }
+ target = target.parentNode;
+ }
+
+ let container = target.container;
+ if (this._hoveredNode !== container.node) {
+ this._showBoxModel(container.node);
+ }
+ this._showContainerAsHovered(container.node);
+
+ this.emit("node-hover");
+ },
+
+ /**
+ * If focus is moved outside of the markup view document and there is a
+ * selected container, make its contents not focusable by a keyboard.
+ */
+ _onBlur: function (event) {
+ if (!this._selectedContainer) {
+ return;
+ }
+
+ let {relatedTarget} = event;
+ if (relatedTarget && relatedTarget.ownerDocument === this.doc) {
+ return;
+ }
+
+ if (this._selectedContainer) {
+ this._selectedContainer.clearFocus();
+ }
+ },
+
+ /**
+ * Executed on each mouse-move while a node is being dragged in the view.
+ * Auto-scrolls the view to reveal nodes below the fold to drop the dragged
+ * node in.
+ */
+ _autoScroll: function (event) {
+ let docEl = this.doc.documentElement;
+
+ if (this._autoScrollAnimationFrame) {
+ this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
+ }
+
+ // Auto-scroll when the mouse approaches top/bottom edge.
+ let fromBottom = docEl.clientHeight - event.pageY + this.win.scrollY;
+ let fromTop = event.pageY - this.win.scrollY;
+ let edgeDistance = Math.min(DRAG_DROP_AUTOSCROLL_EDGE_MAX_DISTANCE,
+ docEl.clientHeight * DRAG_DROP_AUTOSCROLL_EDGE_RATIO);
+
+ // The smaller the screen, the slower the movement.
+ let heightToSpeedRatio =
+ Math.max(DRAG_DROP_HEIGHT_TO_SPEED_MIN,
+ Math.min(DRAG_DROP_HEIGHT_TO_SPEED_MAX,
+ docEl.clientHeight / DRAG_DROP_HEIGHT_TO_SPEED));
+
+ if (fromBottom <= edgeDistance) {
+ // Map our distance range to a speed range so that the speed is not too
+ // fast or too slow.
+ let speed = map(
+ fromBottom,
+ 0, edgeDistance,
+ DRAG_DROP_MIN_AUTOSCROLL_SPEED, DRAG_DROP_MAX_AUTOSCROLL_SPEED);
+
+ this._runUpdateLoop(() => {
+ docEl.scrollTop -= heightToSpeedRatio *
+ (speed - DRAG_DROP_MAX_AUTOSCROLL_SPEED);
+ });
+ }
+
+ if (fromTop <= edgeDistance) {
+ let speed = map(
+ fromTop,
+ 0, edgeDistance,
+ DRAG_DROP_MIN_AUTOSCROLL_SPEED, DRAG_DROP_MAX_AUTOSCROLL_SPEED);
+
+ this._runUpdateLoop(() => {
+ docEl.scrollTop += heightToSpeedRatio *
+ (speed - DRAG_DROP_MAX_AUTOSCROLL_SPEED);
+ });
+ }
+ },
+
+ /**
+ * Run a loop on the requestAnimationFrame.
+ */
+ _runUpdateLoop: function (update) {
+ let loop = () => {
+ update();
+ this._autoScrollAnimationFrame = this.win.requestAnimationFrame(loop);
+ };
+ loop();
+ },
+
+ _onMouseClick: function (event) {
+ // From the target passed here, let's find the parent MarkupContainer
+ // and ask it if the tooltip should be shown
+ let parentNode = event.target;
+ let container;
+ while (parentNode !== this.doc.body) {
+ if (parentNode.container) {
+ container = parentNode.container;
+ break;
+ }
+ parentNode = parentNode.parentNode;
+ }
+
+ if (container instanceof MarkupElementContainer) {
+ // With the newly found container, delegate the tooltip content creation
+ // and decision to show or not the tooltip
+ container._buildEventTooltipContent(event.target,
+ this.eventDetailsTooltip);
+ }
+ },
+
+ _onMouseUp: function () {
+ this.indicateDropTarget(null);
+ this.indicateDragTarget(null);
+ if (this._autoScrollAnimationFrame) {
+ this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
+ }
+ },
+
+ _onCollapseAttributesPrefChange: function () {
+ this.collapseAttributes =
+ Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
+ this.collapseAttributeLength =
+ Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
+ this.update();
+ },
+
+ cancelDragging: function () {
+ if (!this.isDragging) {
+ return;
+ }
+
+ for (let [, container] of this._containers) {
+ if (container.isDragging) {
+ container.cancelDragging();
+ break;
+ }
+ }
+
+ this.indicateDropTarget(null);
+ this.indicateDragTarget(null);
+ if (this._autoScrollAnimationFrame) {
+ this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
+ }
+ },
+
+ _hoveredNode: null,
+
+ /**
+ * Show a NodeFront's container as being hovered
+ *
+ * @param {NodeFront} nodeFront
+ * The node to show as hovered
+ */
+ _showContainerAsHovered: function (nodeFront) {
+ if (this._hoveredNode === nodeFront) {
+ return;
+ }
+
+ if (this._hoveredNode) {
+ this.getContainer(this._hoveredNode).hovered = false;
+ }
+
+ this.getContainer(nodeFront).hovered = true;
+ this._hoveredNode = nodeFront;
+ // Emit an event that the container view is actually hovered now, as this function
+ // can be called by an asynchronous caller.
+ this.emit("showcontainerhovered");
+ },
+
+ _onMouseOut: function (event) {
+ // Emulate mouseleave by skipping any relatedTarget inside the markup-view.
+ if (this._elt.contains(event.relatedTarget)) {
+ return;
+ }
+
+ if (this._autoScrollAnimationFrame) {
+ this.win.cancelAnimationFrame(this._autoScrollAnimationFrame);
+ }
+ if (this.isDragging) {
+ return;
+ }
+
+ this._hideBoxModel(true);
+ if (this._hoveredNode) {
+ this.getContainer(this._hoveredNode).hovered = false;
+ }
+ this._hoveredNode = null;
+
+ this.emit("leave");
+ },
+
+ /**
+ * Show the box model highlighter on a given node front
+ *
+ * @param {NodeFront} nodeFront
+ * The node to show the highlighter for
+ * @return {Promise} Resolves when the highlighter for this nodeFront is
+ * shown, taking into account that there could already be highlighter
+ * requests queued up
+ */
+ _showBoxModel: function (nodeFront) {
+ return this.toolbox.highlighterUtils.highlightNodeFront(nodeFront);
+ },
+
+ /**
+ * Hide the box model highlighter on a given node front
+ *
+ * @param {Boolean} forceHide
+ * See toolbox-highlighter-utils/unhighlight
+ * @return {Promise} Resolves when the highlighter for this nodeFront is
+ * hidden, taking into account that there could already be highlighter
+ * requests queued up
+ */
+ _hideBoxModel: function (forceHide) {
+ return this.toolbox.highlighterUtils.unhighlight(forceHide);
+ },
+
+ _briefBoxModelTimer: null,
+
+ _clearBriefBoxModelTimer: function () {
+ if (this._briefBoxModelTimer) {
+ clearTimeout(this._briefBoxModelTimer);
+ this._briefBoxModelPromise.resolve();
+ this._briefBoxModelPromise = null;
+ this._briefBoxModelTimer = null;
+ }
+ },
+
+ _brieflyShowBoxModel: function (nodeFront) {
+ this._clearBriefBoxModelTimer();
+ let onShown = this._showBoxModel(nodeFront);
+ this._briefBoxModelPromise = defer();
+
+ this._briefBoxModelTimer = setTimeout(() => {
+ this._hideBoxModel()
+ .then(this._briefBoxModelPromise.resolve,
+ this._briefBoxModelPromise.resolve);
+ }, NEW_SELECTION_HIGHLIGHTER_TIMER);
+
+ return promise.all([onShown, this._briefBoxModelPromise.promise]);
+ },
+
+ template: function (name, dest, options = {stack: "markup.xhtml"}) {
+ let node = this.doc.getElementById("template-" + name).cloneNode(true);
+ node.removeAttribute("id");
+ template(node, dest, options);
+ return node;
+ },
+
+ /**
+ * Get the MarkupContainer object for a given node, or undefined if
+ * none exists.
+ */
+ getContainer: function (node) {
+ return this._containers.get(node);
+ },
+
+ update: function () {
+ let updateChildren = (node) => {
+ this.getContainer(node).update();
+ for (let child of node.treeChildren()) {
+ updateChildren(child);
+ }
+ };
+
+ // Start with the documentElement
+ let documentElement;
+ for (let node of this._rootNode.treeChildren()) {
+ if (node.isDocumentElement === true) {
+ documentElement = node;
+ break;
+ }
+ }
+
+ // Recursively update each node starting with documentElement.
+ updateChildren(documentElement);
+ },
+
+ /**
+ * Executed when the mouse hovers over a target in the markup-view and is used
+ * to decide whether this target should be used to display an image preview
+ * tooltip.
+ * Delegates the actual decision to the corresponding MarkupContainer instance
+ * if one is found.
+ *
+ * @return {Promise} the promise returned by
+ * MarkupElementContainer._isImagePreviewTarget
+ */
+ _isImagePreviewTarget: Task.async(function* (target) {
+ // From the target passed here, let's find the parent MarkupContainer
+ // and ask it if the tooltip should be shown
+ if (this.isDragging) {
+ return false;
+ }
+
+ let parent = target, container;
+ while (parent !== this.doc.body) {
+ if (parent.container) {
+ container = parent.container;
+ break;
+ }
+ parent = parent.parentNode;
+ }
+
+ if (container instanceof MarkupElementContainer) {
+ // With the newly found container, delegate the tooltip content creation
+ // and decision to show or not the tooltip
+ return container.isImagePreviewTarget(target, this.imagePreviewTooltip);
+ }
+
+ return false;
+ }),
+
+ /**
+ * Given the known reason, should the current selection be briefly highlighted
+ * In a few cases, we don't want to highlight the node:
+ * - If the reason is null (used to reset the selection),
+ * - if it's "inspector-open" (when the inspector opens up, let's not
+ * highlight the default node)
+ * - if it's "navigateaway" (since the page is being navigated away from)
+ * - if it's "test" (this is a special case for mochitest. In tests, we often
+ * need to select elements but don't necessarily want the highlighter to come
+ * and go after a delay as this might break test scenarios)
+ * We also do not want to start a brief highlight timeout if the node is
+ * already being hovered over, since in that case it will already be
+ * highlighted.
+ */
+ _shouldNewSelectionBeHighlighted: function () {
+ let reason = this.inspector.selection.reason;
+ let unwantedReasons = [
+ "inspector-open",
+ "navigateaway",
+ "nodeselected",
+ "test"
+ ];
+ let isHighlight = this._hoveredNode === this.inspector.selection.nodeFront;
+ return !isHighlight && reason && unwantedReasons.indexOf(reason) === -1;
+ },
+
+ /**
+ * React to new-node-front selection events.
+ * Highlights the node if needed, and make sure it is shown and selected in
+ * the view.
+ */
+ _onNewSelection: function () {
+ let selection = this.inspector.selection;
+
+ this.htmlEditor.hide();
+ if (this._hoveredNode && this._hoveredNode !== selection.nodeFront) {
+ this.getContainer(this._hoveredNode).hovered = false;
+ this._hoveredNode = null;
+ }
+
+ if (!selection.isNode()) {
+ this.unmarkSelectedNode();
+ return;
+ }
+
+ let done = this.inspector.updating("markup-view");
+ let onShowBoxModel, onShow;
+
+ // Highlight the element briefly if needed.
+ if (this._shouldNewSelectionBeHighlighted()) {
+ onShowBoxModel = this._brieflyShowBoxModel(selection.nodeFront);
+ }
+
+ onShow = this.showNode(selection.nodeFront).then(() => {
+ // We could be destroyed by now.
+ if (this._destroyer) {
+ return promise.reject("markupview destroyed");
+ }
+
+ // Mark the node as selected.
+ this.markNodeAsSelected(selection.nodeFront);
+
+ // Make sure the new selection is navigated to.
+ this.maybeNavigateToNewSelection();
+ return undefined;
+ }).catch(this._handleRejectionIfNotDestroyed);
+
+ promise.all([onShowBoxModel, onShow]).then(done);
+ },
+
+ /**
+ * Maybe make selected the current node selection's MarkupContainer depending
+ * on why the current node got selected.
+ */
+ maybeNavigateToNewSelection: function () {
+ let {reason, nodeFront} = this.inspector.selection;
+
+ // The list of reasons that should lead to navigating to the node.
+ let reasonsToNavigate = [
+ // If the user picked an element with the element picker.
+ "picker-node-picked",
+ // If the user shift-clicked (previewed) an element.
+ "picker-node-previewed",
+ // If the user selected an element with the browser context menu.
+ "browser-context-menu",
+ // If the user added a new node by clicking in the inspector toolbar.
+ "node-inserted"
+ ];
+
+ if (reasonsToNavigate.includes(reason)) {
+ this.getContainer(this._rootNode).elt.focus();
+ this.navigate(this.getContainer(nodeFront));
+ }
+ },
+
+ /**
+ * Create a TreeWalker to find the next/previous
+ * node for selection.
+ */
+ _selectionWalker: function (start) {
+ let walker = this.doc.createTreeWalker(
+ start || this._elt,
+ nodeFilterConstants.SHOW_ELEMENT,
+ function (element) {
+ if (element.container &&
+ element.container.elt === element &&
+ element.container.visible) {
+ return nodeFilterConstants.FILTER_ACCEPT;
+ }
+ return nodeFilterConstants.FILTER_SKIP;
+ }
+ );
+ walker.currentNode = this._selectedContainer.elt;
+ return walker;
+ },
+
+ _onCopy: function (evt) {
+ // Ignore copy events from editors
+ if (this._isInputOrTextarea(evt.target)) {
+ return;
+ }
+
+ let selection = this.inspector.selection;
+ if (selection.isNode()) {
+ this.inspector.copyOuterHTML();
+ }
+ evt.stopPropagation();
+ evt.preventDefault();
+ },
+
+ /**
+ * Register all key shortcuts.
+ */
+ _initShortcuts: function () {
+ let shortcuts = new KeyShortcuts({
+ window: this.win,
+ });
+
+ this._onShortcut = this._onShortcut.bind(this);
+
+ // Process localizable keys
+ ["markupView.hide.key",
+ "markupView.edit.key",
+ "markupView.scrollInto.key"].forEach(name => {
+ let key = INSPECTOR_L10N.getStr(name);
+ shortcuts.on(key, (_, event) => this._onShortcut(name, event));
+ });
+
+ // Process generic keys:
+ ["Delete", "Backspace", "Home", "Left", "Right", "Up", "Down", "PageUp",
+ "PageDown", "Esc", "Enter", "Space"].forEach(key => {
+ shortcuts.on(key, this._onShortcut);
+ });
+ },
+
+ /**
+ * Key shortcut listener.
+ */
+ _onShortcut(name, event) {
+ if (this._isInputOrTextarea(event.target)) {
+ return;
+ }
+ switch (name) {
+ // Localizable keys
+ case "markupView.hide.key": {
+ let node = this._selectedContainer.node;
+ if (node.hidden) {
+ this.walker.unhideNode(node);
+ } else {
+ this.walker.hideNode(node);
+ }
+ break;
+ }
+ case "markupView.edit.key": {
+ this.beginEditingOuterHTML(this._selectedContainer.node);
+ break;
+ }
+ case "markupView.scrollInto.key": {
+ let selection = this._selectedContainer.node;
+ this.inspector.scrollNodeIntoView(selection);
+ break;
+ }
+ // Generic keys
+ case "Delete": {
+ this.deleteNodeOrAttribute();
+ break;
+ }
+ case "Backspace": {
+ this.deleteNodeOrAttribute(true);
+ break;
+ }
+ case "Home": {
+ let rootContainer = this.getContainer(this._rootNode);
+ this.navigate(rootContainer.children.firstChild.container);
+ break;
+ }
+ case "Left": {
+ if (this._selectedContainer.expanded) {
+ this.collapseNode(this._selectedContainer.node);
+ } else {
+ let parent = this._selectionWalker().parentNode();
+ if (parent) {
+ this.navigate(parent.container);
+ }
+ }
+ break;
+ }
+ case "Right": {
+ if (!this._selectedContainer.expanded &&
+ this._selectedContainer.hasChildren) {
+ this._expandContainer(this._selectedContainer);
+ } else {
+ let next = this._selectionWalker().nextNode();
+ if (next) {
+ this.navigate(next.container);
+ }
+ }
+ break;
+ }
+ case "Up": {
+ let previousNode = this._selectionWalker().previousNode();
+ if (previousNode) {
+ this.navigate(previousNode.container);
+ }
+ break;
+ }
+ case "Down": {
+ let nextNode = this._selectionWalker().nextNode();
+ if (nextNode) {
+ this.navigate(nextNode.container);
+ }
+ break;
+ }
+ case "PageUp": {
+ let walker = this._selectionWalker();
+ let selection = this._selectedContainer;
+ for (let i = 0; i < PAGE_SIZE; i++) {
+ let previousNode = walker.previousNode();
+ if (!previousNode) {
+ break;
+ }
+ selection = previousNode.container;
+ }
+ this.navigate(selection);
+ break;
+ }
+ case "PageDown": {
+ let walker = this._selectionWalker();
+ let selection = this._selectedContainer;
+ for (let i = 0; i < PAGE_SIZE; i++) {
+ let nextNode = walker.nextNode();
+ if (!nextNode) {
+ break;
+ }
+ selection = nextNode.container;
+ }
+ this.navigate(selection);
+ break;
+ }
+ case "Enter":
+ case "Space": {
+ if (!this._selectedContainer.canFocus) {
+ this._selectedContainer.canFocus = true;
+ this._selectedContainer.focus();
+ } else {
+ // Return early to prevent cancelling the event.
+ return;
+ }
+ break;
+ }
+ case "Esc": {
+ if (this.isDragging) {
+ this.cancelDragging();
+ } else {
+ // Return early to prevent cancelling the event when not
+ // dragging, to allow the split console to be toggled.
+ return;
+ }
+ break;
+ }
+ default:
+ console.error("Unexpected markup-view key shortcut", name);
+ return;
+ }
+ // Prevent default for this action
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ /**
+ * Check if a node is an input or textarea
+ */
+ _isInputOrTextarea: function (element) {
+ let name = element.tagName.toLowerCase();
+ return name === "input" || name === "textarea";
+ },
+
+ /**
+ * If there's an attribute on the current node that's currently focused, then
+ * delete this attribute, otherwise delete the node itself.
+ *
+ * @param {Boolean} moveBackward
+ * If set to true and if we're deleting the node, focus the previous
+ * sibling after deletion, otherwise the next one.
+ */
+ deleteNodeOrAttribute: function (moveBackward) {
+ let focusedAttribute = this.doc.activeElement
+ ? this.doc.activeElement.closest(".attreditor")
+ : null;
+ if (focusedAttribute) {
+ // The focused attribute might not be in the current selected container.
+ let container = focusedAttribute.closest("li.child").container;
+ container.removeAttribute(focusedAttribute.dataset.attr);
+ } else {
+ this.deleteNode(this._selectedContainer.node, moveBackward);
+ }
+ },
+
+ /**
+ * Delete a node from the DOM.
+ * This is an undoable action.
+ *
+ * @param {NodeFront} node
+ * The node to remove.
+ * @param {Boolean} moveBackward
+ * If set to true, focus the previous sibling, otherwise the next one.
+ */
+ deleteNode: function (node, moveBackward) {
+ if (node.isDocumentElement ||
+ node.nodeType == nodeConstants.DOCUMENT_TYPE_NODE ||
+ node.isAnonymous) {
+ return;
+ }
+
+ let container = this.getContainer(node);
+
+ // Retain the node so we can undo this...
+ this.walker.retainNode(node).then(() => {
+ let parent = node.parentNode();
+ let nextSibling = null;
+ this.undo.do(() => {
+ this.walker.removeNode(node).then(siblings => {
+ nextSibling = siblings.nextSibling;
+ let prevSibling = siblings.previousSibling;
+ let focusNode = moveBackward ? prevSibling : nextSibling;
+
+ // If we can't move as the user wants, we move to the other direction.
+ // If there is no sibling elements anymore, move to the parent node.
+ if (!focusNode) {
+ focusNode = nextSibling || prevSibling || parent;
+ }
+
+ let isNextSiblingText = nextSibling ?
+ nextSibling.nodeType === nodeConstants.TEXT_NODE : false;
+ let isPrevSiblingText = prevSibling ?
+ prevSibling.nodeType === nodeConstants.TEXT_NODE : false;
+
+ // If the parent had two children and the next or previous sibling
+ // is a text node, then it now has only a single text node, is about
+ // to be in-lined; and focus should move to the parent.
+ if (parent.numChildren === 2
+ && (isNextSiblingText || isPrevSiblingText)) {
+ focusNode = parent;
+ }
+
+ if (container.selected) {
+ this.navigate(this.getContainer(focusNode));
+ }
+ });
+ }, () => {
+ let isValidSibling = nextSibling && !nextSibling.isPseudoElement;
+ nextSibling = isValidSibling ? nextSibling : null;
+ this.walker.insertBefore(node, parent, nextSibling);
+ });
+ }).then(null, console.error);
+ },
+
+ /**
+ * If an editable item is focused, select its container.
+ */
+ _onFocus: function (event) {
+ let parent = event.target;
+ while (!parent.container) {
+ parent = parent.parentNode;
+ }
+ if (parent) {
+ this.navigate(parent.container);
+ }
+ },
+
+ /**
+ * Handle a user-requested navigation to a given MarkupContainer,
+ * updating the inspector's currently-selected node.
+ *
+ * @param {MarkupContainer} container
+ * The container we're navigating to.
+ */
+ navigate: function (container) {
+ if (!container) {
+ return;
+ }
+
+ let node = container.node;
+ this.markNodeAsSelected(node, "treepanel");
+ },
+
+ /**
+ * Make sure a node is included in the markup tool.
+ *
+ * @param {NodeFront} node
+ * The node in the content document.
+ * @param {Boolean} flashNode
+ * Whether the newly imported node should be flashed
+ * @return {MarkupContainer} The MarkupContainer object for this element.
+ */
+ importNode: function (node, flashNode) {
+ if (!node) {
+ return null;
+ }
+
+ if (this._containers.has(node)) {
+ return this.getContainer(node);
+ }
+
+ let container;
+ let {nodeType, isPseudoElement} = node;
+ if (node === this.walker.rootNode) {
+ container = new RootContainer(this, node);
+ this._elt.appendChild(container.elt);
+ this._rootNode = node;
+ } else if (nodeType == nodeConstants.ELEMENT_NODE && !isPseudoElement) {
+ container = new MarkupElementContainer(this, node, this.inspector);
+ } else if (nodeType == nodeConstants.COMMENT_NODE ||
+ nodeType == nodeConstants.TEXT_NODE) {
+ container = new MarkupTextContainer(this, node, this.inspector);
+ } else {
+ container = new MarkupReadOnlyContainer(this, node, this.inspector);
+ }
+
+ if (flashNode) {
+ container.flashMutation();
+ }
+
+ this._containers.set(node, container);
+ container.childrenDirty = true;
+
+ this._updateChildren(container);
+
+ this.inspector.emit("container-created", container);
+
+ return container;
+ },
+
+ /**
+ * Mutation observer used for included nodes.
+ */
+ _mutationObserver: function (mutations) {
+ for (let mutation of mutations) {
+ let type = mutation.type;
+ let target = mutation.target;
+
+ if (mutation.type === "documentUnload") {
+ // Treat this as a childList change of the child (maybe the protocol
+ // should do this).
+ type = "childList";
+ target = mutation.targetParent;
+ if (!target) {
+ continue;
+ }
+ }
+
+ let container = this.getContainer(target);
+ if (!container) {
+ // Container might not exist if this came from a load event for a node
+ // we're not viewing.
+ continue;
+ }
+
+ if (type === "attributes" && mutation.attributeName === "class") {
+ container.updateIsDisplayed();
+ }
+ if (type === "attributes" || type === "characterData"
+ || type === "events" || type === "pseudoClassLock") {
+ container.update();
+ } else if (type === "childList" || type === "nativeAnonymousChildList") {
+ container.childrenDirty = true;
+ // Update the children to take care of changes in the markup view DOM
+ // and update container (and its subtree) DOM tree depth level for
+ // accessibility where necessary.
+ this._updateChildren(container, {flash: true}).then(() =>
+ container.updateLevel());
+ } else if (type === "inlineTextChild") {
+ container.childrenDirty = true;
+ this._updateChildren(container, {flash: true});
+ container.update();
+ }
+ }
+
+ this._waitForChildren().then(() => {
+ if (this._destroyer) {
+ // Could not fully update after markup mutations, the markup-view was destroyed
+ // while waiting for children. Bail out silently.
+ return;
+ }
+ this._flashMutatedNodes(mutations);
+ this.inspector.emit("markupmutation", mutations);
+
+ // Since the htmlEditor is absolutely positioned, a mutation may change
+ // the location in which it should be shown.
+ this.htmlEditor.refresh();
+ });
+ },
+
+ /**
+ * React to display-change events from the walker
+ *
+ * @param {Array} nodes
+ * An array of nodeFronts
+ */
+ _onDisplayChange: function (nodes) {
+ for (let node of nodes) {
+ let container = this.getContainer(node);
+ if (container) {
+ container.updateIsDisplayed();
+ }
+ }
+ },
+
+ /**
+ * Given a list of mutations returned by the mutation observer, flash the
+ * corresponding containers to attract attention.
+ */
+ _flashMutatedNodes: function (mutations) {
+ let addedOrEditedContainers = new Set();
+ let removedContainers = new Set();
+
+ for (let {type, target, added, removed, newValue} of mutations) {
+ let container = this.getContainer(target);
+
+ if (container) {
+ if (type === "characterData") {
+ addedOrEditedContainers.add(container);
+ } else if (type === "attributes" && newValue === null) {
+ // Removed attributes should flash the entire node.
+ // New or changed attributes will flash the attribute itself
+ // in ElementEditor.flashAttribute.
+ addedOrEditedContainers.add(container);
+ } else if (type === "childList") {
+ // If there has been removals, flash the parent
+ if (removed.length) {
+ removedContainers.add(container);
+ }
+
+ // If there has been additions, flash the nodes if their associated
+ // container exist (so if their parent is expanded in the inspector).
+ added.forEach(node => {
+ let addedContainer = this.getContainer(node);
+ if (addedContainer) {
+ addedOrEditedContainers.add(addedContainer);
+
+ // The node may be added as a result of an append, in which case
+ // it will have been removed from another container first, but in
+ // these cases we don't want to flash both the removal and the
+ // addition
+ removedContainers.delete(container);
+ }
+ });
+ }
+ }
+ }
+
+ for (let container of removedContainers) {
+ container.flashMutation();
+ }
+ for (let container of addedOrEditedContainers) {
+ container.flashMutation();
+ }
+ },
+
+ /**
+ * Make sure the given node's parents are expanded and the
+ * node is scrolled on to screen.
+ */
+ showNode: function (node, centered = true) {
+ let parent = node;
+
+ this.importNode(node);
+
+ while ((parent = parent.parentNode())) {
+ this.importNode(parent);
+ this.expandNode(parent);
+ }
+
+ return this._waitForChildren().then(() => {
+ if (this._destroyer) {
+ return promise.reject("markupview destroyed");
+ }
+ return this._ensureVisible(node);
+ }).then(() => {
+ scrollIntoViewIfNeeded(this.getContainer(node).editor.elt, centered);
+ }, this._handleRejectionIfNotDestroyed);
+ },
+
+ /**
+ * Expand the container's children.
+ */
+ _expandContainer: function (container) {
+ return this._updateChildren(container, {expand: true}).then(() => {
+ if (this._destroyer) {
+ // Could not expand the node, the markup-view was destroyed in the meantime. Just
+ // silently give up.
+ return;
+ }
+ container.setExpanded(true);
+ });
+ },
+
+ /**
+ * Expand the node's children.
+ */
+ expandNode: function (node) {
+ let container = this.getContainer(node);
+ this._expandContainer(container);
+ },
+
+ /**
+ * Expand the entire tree beneath a container.
+ *
+ * @param {MarkupContainer} container
+ * The container to expand.
+ */
+ _expandAll: function (container) {
+ return this._expandContainer(container).then(() => {
+ let child = container.children.firstChild;
+ let promises = [];
+ while (child) {
+ promises.push(this._expandAll(child.container));
+ child = child.nextSibling;
+ }
+ return promise.all(promises);
+ }).then(null, console.error);
+ },
+
+ /**
+ * Expand the entire tree beneath a node.
+ *
+ * @param {DOMNode} node
+ * The node to expand, or null to start from the top.
+ */
+ expandAll: function (node) {
+ node = node || this._rootNode;
+ return this._expandAll(this.getContainer(node));
+ },
+
+ /**
+ * Collapse the node's children.
+ */
+ collapseNode: function (node) {
+ let container = this.getContainer(node);
+ container.setExpanded(false);
+ },
+
+ /**
+ * Returns either the innerHTML or the outerHTML for a remote node.
+ *
+ * @param {NodeFront} node
+ * The NodeFront to get the outerHTML / innerHTML for.
+ * @param {Boolean} isOuter
+ * If true, makes the function return the outerHTML,
+ * otherwise the innerHTML.
+ * @return {Promise} that will be resolved with the outerHTML / innerHTML.
+ */
+ _getNodeHTML: function (node, isOuter) {
+ let walkerPromise = null;
+
+ if (isOuter) {
+ walkerPromise = this.walker.outerHTML(node);
+ } else {
+ walkerPromise = this.walker.innerHTML(node);
+ }
+
+ return walkerPromise.then(longstr => {
+ return longstr.string().then(html => {
+ longstr.release().then(null, console.error);
+ return html;
+ });
+ });
+ },
+
+ /**
+ * Retrieve the outerHTML for a remote node.
+ *
+ * @param {NodeFront} node
+ * The NodeFront to get the outerHTML for.
+ * @return {Promise} that will be resolved with the outerHTML.
+ */
+ getNodeOuterHTML: function (node) {
+ return this._getNodeHTML(node, true);
+ },
+
+ /**
+ * Retrieve the innerHTML for a remote node.
+ *
+ * @param {NodeFront} node
+ * The NodeFront to get the innerHTML for.
+ * @return {Promise} that will be resolved with the innerHTML.
+ */
+ getNodeInnerHTML: function (node) {
+ return this._getNodeHTML(node);
+ },
+
+ /**
+ * Listen to mutations, expect a given node to be removed and try and select
+ * the node that sits at the same place instead.
+ * This is useful when changing the outerHTML or the tag name so that the
+ * newly inserted node gets selected instead of the one that just got removed.
+ */
+ reselectOnRemoved: function (removedNode, reason) {
+ // Only allow one removed node reselection at a time, so that when there are
+ // more than 1 request in parallel, the last one wins.
+ this.cancelReselectOnRemoved();
+
+ // Get the removedNode index in its parent node to reselect the right node.
+ let isHTMLTag = removedNode.tagName.toLowerCase() === "html";
+ let oldContainer = this.getContainer(removedNode);
+ let parentContainer = this.getContainer(removedNode.parentNode());
+ let childIndex = parentContainer.getChildContainers().indexOf(oldContainer);
+
+ let onMutations = this._removedNodeObserver = (e, mutations) => {
+ let isNodeRemovalMutation = false;
+ for (let mutation of mutations) {
+ let containsRemovedNode = mutation.removed &&
+ mutation.removed.some(n => n === removedNode);
+ if (mutation.type === "childList" &&
+ (containsRemovedNode || isHTMLTag)) {
+ isNodeRemovalMutation = true;
+ break;
+ }
+ }
+ if (!isNodeRemovalMutation) {
+ return;
+ }
+
+ this.inspector.off("markupmutation", onMutations);
+ this._removedNodeObserver = null;
+
+ // Don't select the new node if the user has already changed the current
+ // selection.
+ if (this.inspector.selection.nodeFront === parentContainer.node ||
+ (this.inspector.selection.nodeFront === removedNode && isHTMLTag)) {
+ let childContainers = parentContainer.getChildContainers();
+ if (childContainers && childContainers[childIndex]) {
+ this.markNodeAsSelected(childContainers[childIndex].node, reason);
+ if (childContainers[childIndex].hasChildren) {
+ this.expandNode(childContainers[childIndex].node);
+ }
+ this.emit("reselectedonremoved");
+ }
+ }
+ };
+
+ // Start listening for mutations until we find a childList change that has
+ // removedNode removed.
+ this.inspector.on("markupmutation", onMutations);
+ },
+
+ /**
+ * Make sure to stop listening for node removal markupmutations and not
+ * reselect the corresponding node when that happens.
+ * Useful when the outerHTML/tagname edition failed.
+ */
+ cancelReselectOnRemoved: function () {
+ if (this._removedNodeObserver) {
+ this.inspector.off("markupmutation", this._removedNodeObserver);
+ this._removedNodeObserver = null;
+ this.emit("canceledreselectonremoved");
+ }
+ },
+
+ /**
+ * Replace the outerHTML of any node displayed in the inspector with
+ * some other HTML code
+ *
+ * @param {NodeFront} node
+ * Node which outerHTML will be replaced.
+ * @param {String} newValue
+ * The new outerHTML to set on the node.
+ * @param {String} oldValue
+ * The old outerHTML that will be used if the user undoes the update.
+ * @return {Promise} that will resolve when the outer HTML has been updated.
+ */
+ updateNodeOuterHTML: function (node, newValue) {
+ let container = this.getContainer(node);
+ if (!container) {
+ return promise.reject();
+ }
+
+ // Changing the outerHTML removes the node which outerHTML was changed.
+ // Listen to this removal to reselect the right node afterwards.
+ this.reselectOnRemoved(node, "outerhtml");
+ return this.walker.setOuterHTML(node, newValue).then(null, () => {
+ this.cancelReselectOnRemoved();
+ });
+ },
+
+ /**
+ * Replace the innerHTML of any node displayed in the inspector with
+ * some other HTML code
+ * @param {Node} node
+ * node which innerHTML will be replaced.
+ * @param {String} newValue
+ * The new innerHTML to set on the node.
+ * @param {String} oldValue
+ * The old innerHTML that will be used if the user undoes the update.
+ * @return {Promise} that will resolve when the inner HTML has been updated.
+ */
+ updateNodeInnerHTML: function (node, newValue, oldValue) {
+ let container = this.getContainer(node);
+ if (!container) {
+ return promise.reject();
+ }
+
+ let def = defer();
+
+ container.undo.do(() => {
+ this.walker.setInnerHTML(node, newValue).then(def.resolve, def.reject);
+ }, () => {
+ this.walker.setInnerHTML(node, oldValue);
+ });
+
+ return def.promise;
+ },
+
+ /**
+ * Insert adjacent HTML to any node displayed in the inspector.
+ *
+ * @param {NodeFront} node
+ * The reference node.
+ * @param {String} position
+ * The position as specified for Element.insertAdjacentHTML
+ * (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
+ * @param {String} newValue
+ * The adjacent HTML.
+ * @return {Promise} that will resolve when the adjacent HTML has
+ * been inserted.
+ */
+ insertAdjacentHTMLToNode: function (node, position, value) {
+ let container = this.getContainer(node);
+ if (!container) {
+ return promise.reject();
+ }
+
+ let def = defer();
+
+ let injectedNodes = [];
+ container.undo.do(() => {
+ this.walker.insertAdjacentHTML(node, position, value).then(nodeArray => {
+ injectedNodes = nodeArray.nodes;
+ return nodeArray;
+ }).then(def.resolve, def.reject);
+ }, () => {
+ this.walker.removeNodes(injectedNodes);
+ });
+
+ return def.promise;
+ },
+
+ /**
+ * Open an editor in the UI to allow editing of a node's outerHTML.
+ *
+ * @param {NodeFront} node
+ * The NodeFront to edit.
+ */
+ beginEditingOuterHTML: function (node) {
+ this.getNodeOuterHTML(node).then(oldValue => {
+ let container = this.getContainer(node);
+ if (!container) {
+ return;
+ }
+ this.htmlEditor.show(container.tagLine, oldValue);
+ this.htmlEditor.once("popuphidden", (e, commit, value) => {
+ // Need to focus the <html> element instead of the frame / window
+ // in order to give keyboard focus back to doc (from editor).
+ this.doc.documentElement.focus();
+
+ if (commit) {
+ this.updateNodeOuterHTML(node, value, oldValue);
+ }
+ });
+ });
+ },
+
+ /**
+ * Mark the given node expanded.
+ *
+ * @param {NodeFront} node
+ * The NodeFront to mark as expanded.
+ * @param {Boolean} expanded
+ * Whether the expand or collapse.
+ * @param {Boolean} expandDescendants
+ * Whether to expand all descendants too
+ */
+ setNodeExpanded: function (node, expanded, expandDescendants) {
+ if (expanded) {
+ if (expandDescendants) {
+ this.expandAll(node);
+ } else {
+ this.expandNode(node);
+ }
+ } else {
+ this.collapseNode(node);
+ }
+ },
+
+ /**
+ * Mark the given node selected, and update the inspector.selection
+ * object's NodeFront to keep consistent state between UI and selection.
+ *
+ * @param {NodeFront} aNode
+ * The NodeFront to mark as selected.
+ * @param {String} reason
+ * The reason for marking the node as selected.
+ * @return {Boolean} False if the node is already marked as selected, true
+ * otherwise.
+ */
+ markNodeAsSelected: function (node, reason) {
+ let container = this.getContainer(node);
+
+ if (this._selectedContainer === container) {
+ return false;
+ }
+
+ // Un-select and remove focus from the previous container.
+ if (this._selectedContainer) {
+ this._selectedContainer.selected = false;
+ this._selectedContainer.clearFocus();
+ }
+
+ // Select the new container.
+ this._selectedContainer = container;
+ if (node) {
+ this._selectedContainer.selected = true;
+ }
+
+ // Change the current selection if needed.
+ if (this.inspector.selection.nodeFront !== node) {
+ this.inspector.selection.setNodeFront(node, reason || "nodeselected");
+ }
+
+ return true;
+ },
+
+ /**
+ * Make sure that every ancestor of the selection are updated
+ * and included in the list of visible children.
+ */
+ _ensureVisible: function (node) {
+ while (node) {
+ let container = this.getContainer(node);
+ let parent = node.parentNode();
+ if (!container.elt.parentNode) {
+ let parentContainer = this.getContainer(parent);
+ if (parentContainer) {
+ parentContainer.childrenDirty = true;
+ this._updateChildren(parentContainer, {expand: true});
+ }
+ }
+
+ node = parent;
+ }
+ return this._waitForChildren();
+ },
+
+ /**
+ * Unmark selected node (no node selected).
+ */
+ unmarkSelectedNode: function () {
+ if (this._selectedContainer) {
+ this._selectedContainer.selected = false;
+ this._selectedContainer = null;
+ }
+ },
+
+ /**
+ * Check if the current selection is a descendent of the container.
+ * if so, make sure it's among the visible set for the container,
+ * and set the dirty flag if needed.
+ *
+ * @return The node that should be made visible, if any.
+ */
+ _checkSelectionVisible: function (container) {
+ let centered = null;
+ let node = this.inspector.selection.nodeFront;
+ while (node) {
+ if (node.parentNode() === container.node) {
+ centered = node;
+ break;
+ }
+ node = node.parentNode();
+ }
+
+ return centered;
+ },
+
+ /**
+ * Make sure all children of the given container's node are
+ * imported and attached to the container in the right order.
+ *
+ * Children need to be updated only in the following circumstances:
+ * a) We just imported this node and have never seen its children.
+ * container.childrenDirty will be set by importNode in this case.
+ * b) We received a childList mutation on the node.
+ * container.childrenDirty will be set in that case too.
+ * c) We have changed the selection, and the path to that selection
+ * wasn't loaded in a previous children request (because we only
+ * grab a subset).
+ * container.childrenDirty should be set in that case too!
+ *
+ * @param {MarkupContainer} container
+ * The markup container whose children need updating
+ * @param {Object} options
+ * Options are {expand:boolean,flash:boolean}
+ * @return {Promise} that will be resolved when the children are ready
+ * (which may be immediately).
+ */
+ _updateChildren: function (container, options) {
+ let expand = options && options.expand;
+ let flash = options && options.flash;
+
+ container.hasChildren = container.node.hasChildren;
+ // Accessibility should either ignore empty children or semantically
+ // consider them a group.
+ container.setChildrenRole();
+
+ if (!this._queuedChildUpdates) {
+ this._queuedChildUpdates = new Map();
+ }
+
+ if (this._queuedChildUpdates.has(container)) {
+ return this._queuedChildUpdates.get(container);
+ }
+
+ if (!container.childrenDirty) {
+ return promise.resolve(container);
+ }
+
+ if (container.inlineTextChild
+ && container.inlineTextChild != container.node.inlineTextChild) {
+ // This container was doing double duty as a container for a single
+ // text child, back that out.
+ this._containers.delete(container.inlineTextChild);
+ container.clearInlineTextChild();
+
+ if (container.hasChildren && container.selected) {
+ container.setExpanded(true);
+ }
+ }
+
+ if (container.node.inlineTextChild) {
+ container.setExpanded(false);
+ // this container will do double duty as the container for the single
+ // text child.
+ while (container.children.firstChild) {
+ container.children.removeChild(container.children.firstChild);
+ }
+
+ container.setInlineTextChild(container.node.inlineTextChild);
+
+ this._containers.set(container.node.inlineTextChild, container);
+ container.childrenDirty = false;
+ return promise.resolve(container);
+ }
+
+ if (!container.hasChildren) {
+ while (container.children.firstChild) {
+ container.children.removeChild(container.children.firstChild);
+ }
+ container.childrenDirty = false;
+ container.setExpanded(false);
+ return promise.resolve(container);
+ }
+
+ // If we're not expanded (or asked to update anyway), we're done for
+ // now. Note that this will leave the childrenDirty flag set, so when
+ // expanded we'll refresh the child list.
+ if (!(container.expanded || expand)) {
+ return promise.resolve(container);
+ }
+
+ // We're going to issue a children request, make sure it includes the
+ // centered node.
+ let centered = this._checkSelectionVisible(container);
+
+ // Children aren't updated yet, but clear the childrenDirty flag anyway.
+ // If the dirty flag is re-set while we're fetching we'll need to fetch
+ // again.
+ container.childrenDirty = false;
+ let updatePromise =
+ this._getVisibleChildren(container, centered).then(children => {
+ if (!this._containers) {
+ return promise.reject("markup view destroyed");
+ }
+ this._queuedChildUpdates.delete(container);
+
+ // If children are dirty, we got a change notification for this node
+ // while the request was in progress, we need to do it again.
+ if (container.childrenDirty) {
+ return this._updateChildren(container, {expand: centered});
+ }
+
+ let fragment = this.doc.createDocumentFragment();
+
+ for (let child of children.nodes) {
+ let childContainer = this.importNode(child, flash);
+ fragment.appendChild(childContainer.elt);
+ }
+
+ while (container.children.firstChild) {
+ container.children.removeChild(container.children.firstChild);
+ }
+
+ if (!(children.hasFirst && children.hasLast)) {
+ let nodesCount = container.node.numChildren;
+ let showAllString = PluralForm.get(nodesCount,
+ INSPECTOR_L10N.getStr("markupView.more.showAll2"));
+ let data = {
+ showing: INSPECTOR_L10N.getStr("markupView.more.showing"),
+ showAll: showAllString.replace("#1", nodesCount),
+ allButtonClick: () => {
+ container.maxChildren = -1;
+ container.childrenDirty = true;
+ this._updateChildren(container);
+ }
+ };
+
+ if (!children.hasFirst) {
+ let span = this.template("more-nodes", data);
+ fragment.insertBefore(span, fragment.firstChild);
+ }
+ if (!children.hasLast) {
+ let span = this.template("more-nodes", data);
+ fragment.appendChild(span);
+ }
+ }
+
+ container.children.appendChild(fragment);
+ return container;
+ }).catch(this._handleRejectionIfNotDestroyed);
+ this._queuedChildUpdates.set(container, updatePromise);
+ return updatePromise;
+ },
+
+ _waitForChildren: function () {
+ if (!this._queuedChildUpdates) {
+ return promise.resolve(undefined);
+ }
+
+ return promise.all([...this._queuedChildUpdates.values()]);
+ },
+
+ /**
+ * Return a list of the children to display for this container.
+ */
+ _getVisibleChildren: function (container, centered) {
+ let maxChildren = container.maxChildren || this.maxChildren;
+ if (maxChildren == -1) {
+ maxChildren = undefined;
+ }
+
+ return this.walker.children(container.node, {
+ maxNodes: maxChildren,
+ center: centered
+ });
+ },
+
+ /**
+ * Tear down the markup panel.
+ */
+ destroy: function () {
+ if (this._destroyer) {
+ return this._destroyer;
+ }
+
+ this._destroyer = promise.resolve();
+
+ this._clearBriefBoxModelTimer();
+
+ this._hoveredNode = null;
+
+ this.htmlEditor.destroy();
+ this.htmlEditor = null;
+
+ this.undo.destroy();
+ this.undo = null;
+
+ this.popup.destroy();
+ this.popup = null;
+
+ this._elt.removeEventListener("click", this._onMouseClick, false);
+ this._elt.removeEventListener("mousemove", this._onMouseMove, false);
+ this._elt.removeEventListener("mouseout", this._onMouseOut, false);
+ this._elt.removeEventListener("blur", this._onBlur, true);
+ this.win.removeEventListener("mouseup", this._onMouseUp);
+ this.win.removeEventListener("copy", this._onCopy);
+ this._frame.removeEventListener("focus", this._onFocus, false);
+ this.walker.off("mutations", this._mutationObserver);
+ this.walker.off("display-change", this._onDisplayChange);
+ this.inspector.selection.off("new-node-front", this._onNewSelection);
+ this.toolbox.off("picker-node-hovered",
+ this._onToolboxPickerHover);
+
+ this._prefObserver.off(ATTR_COLLAPSE_ENABLED_PREF,
+ this._onCollapseAttributesPrefChange);
+ this._prefObserver.off(ATTR_COLLAPSE_LENGTH_PREF,
+ this._onCollapseAttributesPrefChange);
+ this._prefObserver.destroy();
+
+ this._elt = null;
+
+ for (let [, container] of this._containers) {
+ container.destroy();
+ }
+ this._containers = null;
+
+ this.eventDetailsTooltip.destroy();
+ this.eventDetailsTooltip = null;
+
+ this.imagePreviewTooltip.destroy();
+ this.imagePreviewTooltip = null;
+
+ this.win = null;
+ this.doc = null;
+
+ this._lastDropTarget = null;
+ this._lastDragTarget = null;
+
+ return this._destroyer;
+ },
+
+ /**
+ * Find the closest element with class tag-line. These are used to indicate
+ * drag and drop targets.
+ *
+ * @param {DOMNode} el
+ * @return {DOMNode}
+ */
+ findClosestDragDropTarget: function (el) {
+ return el.classList.contains("tag-line")
+ ? el
+ : el.querySelector(".tag-line") || el.closest(".tag-line");
+ },
+
+ /**
+ * Takes an element as it's only argument and marks the element
+ * as the drop target
+ */
+ indicateDropTarget: function (el) {
+ if (this._lastDropTarget) {
+ this._lastDropTarget.classList.remove("drop-target");
+ }
+
+ if (!el) {
+ return;
+ }
+
+ let target = this.findClosestDragDropTarget(el);
+ if (target) {
+ target.classList.add("drop-target");
+ this._lastDropTarget = target;
+ }
+ },
+
+ /**
+ * Takes an element to mark it as indicator of dragging target's initial place
+ */
+ indicateDragTarget: function (el) {
+ if (this._lastDragTarget) {
+ this._lastDragTarget.classList.remove("drag-target");
+ }
+
+ if (!el) {
+ return;
+ }
+
+ let target = this.findClosestDragDropTarget(el);
+ if (target) {
+ target.classList.add("drag-target");
+ this._lastDragTarget = target;
+ }
+ },
+
+ /**
+ * Used to get the nodes required to modify the markup after dragging the
+ * element (parent/nextSibling).
+ */
+ get dropTargetNodes() {
+ let target = this._lastDropTarget;
+
+ if (!target) {
+ return null;
+ }
+
+ let parent, nextSibling;
+
+ if (target.previousElementSibling &&
+ target.previousElementSibling.nodeName.toLowerCase() === "ul") {
+ parent = target.parentNode.container.node;
+ nextSibling = null;
+ } else {
+ parent = target.parentNode.container.node.parentNode();
+ nextSibling = target.parentNode.container.node;
+ }
+
+ if (nextSibling && nextSibling.isBeforePseudoElement) {
+ nextSibling = target.parentNode.parentNode.children[1].container.node;
+ }
+ if (nextSibling && nextSibling.isAfterPseudoElement) {
+ parent = target.parentNode.container.node.parentNode();
+ nextSibling = null;
+ }
+
+ if (parent.nodeType !== nodeConstants.ELEMENT_NODE) {
+ return null;
+ }
+
+ return {parent, nextSibling};
+ }
+};
+
+/**
+ * Map a number from one range to another.
+ */
+function map(value, oldMin, oldMax, newMin, newMax) {
+ let ratio = oldMax - oldMin;
+ if (ratio == 0) {
+ return value;
+ }
+ return newMin + (newMax - newMin) * ((value - oldMin) / ratio);
+}
+
+module.exports = MarkupView;
diff --git a/devtools/client/inspector/markup/markup.xhtml b/devtools/client/inspector/markup/markup.xhtml
new file mode 100644
index 000000000..88b06aadd
--- /dev/null
+++ b/devtools/client/inspector/markup/markup.xhtml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" href="chrome://devtools/skin/markup.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>
+
+ <script type="application/javascript;version=1.8"
+ src="chrome://devtools/content/shared/theme-switching.js"></script>
+ <script type="application/javascript;version=1.8"
+ src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
+
+</head>
+<body class="theme-body devtools-monospace" role="application">
+
+<!-- NOTE THAT WE MAKE EXTENSIVE USE OF HTML COMMENTS IN THIS FILE IN ORDER -->
+<!-- TO MAKE SPANS READABLE WHILST AVOIDING SIGNIFICANT WHITESPACE -->
+
+ <div id="root-wrapper" role="presentation">
+ <div id="root" role="presentation"></div>
+ </div>
+ <div id="templates" style="display:none">
+
+ <ul class="children">
+ <li id="template-elementcontainer" save="${elt}" class="child collapsed" role="presentation">
+ <div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><!--
+ --><span save="${tagState}" class="tag-state" role="presentation"></span><!--
+ --><span save="${expander}" class="theme-twisty expander" role="presentation"></span><!--
+ --></div>
+ <ul save="${children}" class="children" role="group"></ul>
+ </li>
+
+ <li id="template-textcontainer" save="${elt}" class="child collapsed" role="presentation">
+ <div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><span save="${tagState}" class="tag-state" role="presentation"></span></div>
+ <ul save="${children}" class="children" role="group"></ul>
+ </li>
+
+ <li id="template-readonlycontainer" save="${elt}" class="child collapsed" role="presentation">
+ <div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><!--
+ --><span save="${tagState}" class="tag-state" role="presentation"></span><!--
+ --><span save="${expander}" class="theme-twisty expander" role="presentation"></span><!--
+ --></div>
+ <ul save="${children}" class="children" role="group"></ul>
+ </li>
+
+ <li id="template-more-nodes"
+ class="more-nodes devtools-class-comment"
+ save="${elt}"><!--
+ --><span>${showing}</span> <!--
+ --><button href="#" onclick="${allButtonClick}">${showAll}</button>
+ </li>
+ </ul>
+
+ <span id="template-generic" save="${elt}" class="editor"><span save="${tag}" class="tag"></span></span>
+
+ <span id="template-element" save="${elt}" class="editor"><!--
+ --><span class="open">&lt;<!--
+ --><span save="${tag}" class="tag theme-fg-color3" tabindex="-1"></span><!--
+ --><span save="${attrList}"></span><!--
+ --><span save="${newAttr}" class="newattr" tabindex="-1"></span><!--
+ --><span class="closing-bracket">&gt;</span><!--
+ --></span><!--
+ --><span class="close">&lt;/<!--
+ --><span save="${closeTag}" class="tag theme-fg-color3"></span><!--
+ -->&gt;<!--
+ --></span><!--
+ --><div save="${eventNode}" class="markupview-events" data-event="true">ev</div><!--
+ --></span>
+
+ <span id="template-attribute"
+ save="${attr}"
+ data-attr="${attrName}"
+ data-value="${attrValue}"
+ class="attreditor"
+ style="display:none"> <!--
+ --><span class="editable" save="${inner}" tabindex="${tabindex}"><!--
+ --><span save="${name}" class="attr-name theme-fg-color2"></span><!--
+ -->=&quot;<!--
+ --><span save="${val}" class="attr-value theme-fg-color6"></span><!--
+ -->&quot;<!--
+ --></span><!--
+ --></span>
+
+ <span id="template-text" save="${elt}" class="editor text"><!--
+ --><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="-1"></pre><!--
+ --></span>
+
+ <span id="template-comment"
+ save="${elt}"
+ class="editor comment theme-comment"><!--
+ --><span>&lt;!--</span><!--
+ --><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="-1"></pre><!--
+ --><span>--&gt;</span><!--
+ --></span>
+
+ </div>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/moz.build b/devtools/client/inspector/markup/moz.build
new file mode 100644
index 000000000..4d721cc3c
--- /dev/null
+++ b/devtools/client/inspector/markup/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'views',
+]
+
+DevToolsModules(
+ 'markup.js',
+ 'utils.js',
+)
+
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
diff --git a/devtools/client/inspector/markup/test/.eslintrc.js b/devtools/client/inspector/markup/test/.eslintrc.js
new file mode 100644
index 000000000..698ae9181
--- /dev/null
+++ b/devtools/client/inspector/markup/test/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the shared list of defined globals for mochitests.
+ "extends": "../../../../.eslintrc.mochitests.js"
+};
diff --git a/devtools/client/inspector/markup/test/actor_events_form.js b/devtools/client/inspector/markup/test/actor_events_form.js
new file mode 100644
index 000000000..bd1b1e91a
--- /dev/null
+++ b/devtools/client/inspector/markup/test/actor_events_form.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test actor is used for testing the addition of custom form data
+// on NodeActor. Custom form property is set when 'form' event is sent
+// by NodeActor actor (see 'onNodeActorForm' method).
+
+const Events = require("sdk/event/core");
+const {ActorClassWithSpec, Actor, FrontClassWithSpec, Front, generateActorSpec} =
+ require("devtools/shared/protocol");
+
+const {NodeActor} = require("devtools/server/actors/inspector");
+
+var eventsSpec = generateActorSpec({
+ typeName: "eventsFormActor",
+
+ methods: {
+ attach: {
+ request: {},
+ response: {}
+ },
+ detach: {
+ request: {},
+ response: {}
+ }
+ }
+});
+
+var EventsFormActor = ActorClassWithSpec(eventsSpec, {
+ initialize: function () {
+ Actor.prototype.initialize.apply(this, arguments);
+ },
+
+ attach: function () {
+ Events.on(NodeActor, "form", this.onNodeActorForm);
+ },
+
+ detach: function () {
+ Events.off(NodeActor, "form", this.onNodeActorForm);
+ },
+
+ onNodeActorForm: function (event) {
+ let nodeActor = event.target;
+ if (nodeActor.rawNode.id == "container") {
+ let form = event.data;
+ form.setFormProperty("test-property", "test-value");
+ }
+ }
+});
+
+var EventsFormFront = FrontClassWithSpec(eventsSpec, {
+ initialize: function (client, form) {
+ Front.prototype.initialize.apply(this, arguments);
+
+ this.actorID = form[EventsFormActor.prototype.typeName];
+ this.manage(this);
+ }
+});
+
+exports.EventsFormFront = EventsFormFront;
diff --git a/devtools/client/inspector/markup/test/browser.ini b/devtools/client/inspector/markup/test/browser.ini
new file mode 100644
index 000000000..3116e4beb
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -0,0 +1,155 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+ actor_events_form.js
+ doc_markup_anonymous.html
+ doc_markup_dragdrop.html
+ doc_markup_dragdrop_autoscroll_01.html
+ doc_markup_dragdrop_autoscroll_02.html
+ doc_markup_edit.html
+ doc_markup_events1.html
+ doc_markup_events2.html
+ doc_markup_events3.html
+ doc_markup_events_form.html
+ doc_markup_events_jquery.html
+ doc_markup_events-overflow.html
+ doc_markup_flashing.html
+ doc_markup_html_mixed_case.html
+ doc_markup_image_and_canvas.html
+ doc_markup_image_and_canvas_2.html
+ doc_markup_links.html
+ doc_markup_mutation.html
+ doc_markup_navigation.html
+ doc_markup_not_displayed.html
+ doc_markup_pagesize_01.html
+ doc_markup_pagesize_02.html
+ doc_markup_search.html
+ doc_markup_svg_attributes.html
+ doc_markup_toggle.html
+ doc_markup_tooltip.png
+ doc_markup_void_elements.html
+ doc_markup_void_elements.xhtml
+ doc_markup_whitespace.html
+ doc_markup_xul.xul
+ head.js
+ helper_attributes_test_runner.js
+ helper_events_test_runner.js
+ helper_markup_accessibility_navigation.js
+ helper_outerhtml_test_runner.js
+ helper_style_attr_test_runner.js
+ lib_jquery_1.0.js
+ lib_jquery_1.1.js
+ lib_jquery_1.2_min.js
+ lib_jquery_1.3_min.js
+ lib_jquery_1.4_min.js
+ lib_jquery_1.6_min.js
+ lib_jquery_1.7_min.js
+ lib_jquery_1.11.1_min.js
+ lib_jquery_2.1.1_min.js
+ !/devtools/client/commandline/test/helpers.js
+ !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/inspector/test/head.js
+ !/devtools/client/inspector/test/shared-head.js
+ !/devtools/client/shared/test/test-actor.js
+ !/devtools/client/shared/test/test-actor-registry.js
+
+[browser_markup_accessibility_focus_blur.js]
+skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
+[browser_markup_accessibility_navigation.js]
+skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
+[browser_markup_accessibility_navigation_after_edit.js]
+skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
+[browser_markup_accessibility_semantics.js]
+[browser_markup_anonymous_01.js]
+[browser_markup_anonymous_02.js]
+skip-if = e10s # scratchpad.xul is not loading in e10s window
+[browser_markup_anonymous_03.js]
+[browser_markup_anonymous_04.js]
+[browser_markup_copy_image_data.js]
+subsuite = clipboard
+[browser_markup_css_completion_style_attribute_01.js]
+[browser_markup_css_completion_style_attribute_02.js]
+[browser_markup_css_completion_style_attribute_03.js]
+[browser_markup_dragdrop_autoscroll_01.js]
+[browser_markup_dragdrop_autoscroll_02.js]
+[browser_markup_dragdrop_distance.js]
+[browser_markup_dragdrop_draggable.js]
+[browser_markup_dragdrop_dragRootNode.js]
+[browser_markup_dragdrop_escapeKeyPress.js]
+[browser_markup_dragdrop_invalidNodes.js]
+[browser_markup_dragdrop_reorder.js]
+[browser_markup_dragdrop_tooltip.js]
+[browser_markup_events1.js]
+[browser_markup_events2.js]
+[browser_markup_events3.js]
+[browser_markup_events_form.js]
+[browser_markup_events_jquery_1.0.js]
+[browser_markup_events_jquery_1.1.js]
+[browser_markup_events_jquery_1.2.js]
+[browser_markup_events_jquery_1.3.js]
+[browser_markup_events_jquery_1.4.js]
+[browser_markup_events_jquery_1.6.js]
+[browser_markup_events_jquery_1.7.js]
+[browser_markup_events_jquery_1.11.1.js]
+[browser_markup_events_jquery_2.1.1.js]
+[browser_markup_events-overflow.js]
+skip-if = true # Bug 1177550
+[browser_markup_events-windowed-host.js]
+[browser_markup_links_01.js]
+[browser_markup_links_02.js]
+[browser_markup_links_03.js]
+[browser_markup_links_04.js]
+subsuite = clipboard
+[browser_markup_links_05.js]
+[browser_markup_links_06.js]
+[browser_markup_links_07.js]
+[browser_markup_load_01.js]
+[browser_markup_html_edit_01.js]
+[browser_markup_html_edit_02.js]
+[browser_markup_html_edit_03.js]
+[browser_markup_image_tooltip.js]
+[browser_markup_image_tooltip_mutations.js]
+[browser_markup_keybindings_01.js]
+[browser_markup_keybindings_02.js]
+[browser_markup_keybindings_03.js]
+[browser_markup_keybindings_04.js]
+[browser_markup_keybindings_delete_attributes.js]
+[browser_markup_keybindings_scrolltonode.js]
+[browser_markup_mutation_01.js]
+[browser_markup_mutation_02.js]
+[browser_markup_navigation.js]
+[browser_markup_node_names.js]
+[browser_markup_node_names_namespaced.js]
+[browser_markup_node_not_displayed_01.js]
+[browser_markup_node_not_displayed_02.js]
+[browser_markup_pagesize_01.js]
+[browser_markup_pagesize_02.js]
+[browser_markup_remove_xul_attributes.js]
+skip-if = e10s # Bug 1036409 - The last selected node isn't reselected
+[browser_markup_search_01.js]
+[browser_markup_tag_edit_01.js]
+[browser_markup_tag_edit_02.js]
+[browser_markup_tag_edit_03.js]
+[browser_markup_tag_edit_04-backspace.js]
+[browser_markup_tag_edit_04-delete.js]
+[browser_markup_tag_edit_05.js]
+[browser_markup_tag_edit_06.js]
+[browser_markup_tag_edit_07.js]
+[browser_markup_tag_edit_08.js]
+[browser_markup_tag_edit_09.js]
+[browser_markup_tag_edit_10.js]
+[browser_markup_tag_edit_11.js]
+[browser_markup_tag_edit_12.js]
+[browser_markup_tag_edit_13-other.js]
+[browser_markup_tag_edit_long-classname.js]
+[browser_markup_textcontent_display.js]
+[browser_markup_textcontent_edit_01.js]
+[browser_markup_textcontent_edit_02.js]
+[browser_markup_toggle_01.js]
+[browser_markup_toggle_02.js]
+[browser_markup_toggle_03.js]
+[browser_markup_update-on-navigtion.js]
+[browser_markup_void_elements_html.js]
+[browser_markup_void_elements_xhtml.js]
+[browser_markup_whitespace.js]
diff --git a/devtools/client/inspector/markup/test/browser_markup_accessibility_focus_blur.js b/devtools/client/inspector/markup/test/browser_markup_accessibility_focus_blur.js
new file mode 100644
index 000000000..7e94669c0
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_focus_blur.js
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Test inspector markup view handling focus and blur when moving between markup
+// view, its root and other containers, and other parts of inspector.
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(
+ "data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
+ let markup = inspector.markup;
+ let doc = markup.doc;
+ let win = doc.defaultView;
+
+ let spanContainer = yield getContainerForSelector("span", inspector);
+ let rootContainer = markup.getContainer(markup._rootNode);
+
+ is(doc.activeElement, doc.body,
+ "Keyboard focus by default is on document body");
+
+ yield selectNode("span", inspector);
+
+ is(doc.activeElement, doc.body,
+ "Keyboard focus is still on document body");
+
+ info("Focusing on the test span node using 'Return' key");
+ // Focus on the tree element.
+ rootContainer.elt.focus();
+ EventUtils.synthesizeKey("VK_RETURN", {}, win);
+
+ is(doc.activeElement, spanContainer.editor.tag,
+ "Keyboard focus should be on tag element of focused container");
+
+ info("Focusing on search box, external to markup view document");
+ yield focusSearchBoxUsingShortcut(inspector.panelWin);
+
+ is(doc.activeElement, doc.body,
+ "Keyboard focus should be removed from focused container");
+
+ info("Selecting the test span node again");
+ yield selectNode("span", inspector);
+
+ is(doc.activeElement, doc.body,
+ "Keyboard focus should again be on document body");
+
+ info("Focusing on the test span node using 'Space' key");
+ // Focus on the tree element.
+ rootContainer.elt.focus();
+ EventUtils.synthesizeKey("VK_SPACE", {}, win);
+
+ is(doc.activeElement, spanContainer.editor.tag,
+ "Keyboard focus should again be on tag element of focused container");
+
+ yield clickOnInspectMenuItem(testActor, "h1");
+ is(doc.activeElement, rootContainer.elt,
+ "When inspect menu item is used keyboard focus should move to tree.");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
new file mode 100644
index 000000000..41e35afef
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
@@ -0,0 +1,277 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from helper_markup_accessibility_navigation.js */
+
+"use strict";
+
+// Test keyboard navigation accessibility of inspector's markup view.
+
+loadHelperScript("helper_markup_accessibility_navigation.js");
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * key {String} key event's key
+ * options {?Object} optional event data such as shiftKey, etc
+ * focused {String} path to expected focused element relative to
+ * its container
+ * activedescendant {String} path to expected aria-activedescendant element
+ * relative to its container
+ * waitFor {String} optional event to wait for if keyboard actions
+ * result in asynchronous updates
+ * }
+ */
+const TESTS = [
+ {
+ desc: "Collapse body container",
+ focused: "root.elt",
+ activedescendant: "body.tagLine",
+ key: "VK_LEFT",
+ options: { },
+ waitFor: "collapsed"
+ },
+ {
+ desc: "Expand body container",
+ focused: "root.elt",
+ activedescendant: "body.tagLine",
+ key: "VK_RIGHT",
+ options: { },
+ waitFor: "expanded"
+ },
+ {
+ desc: "Select header container",
+ focused: "root.elt",
+ activedescendant: "header.tagLine",
+ key: "VK_DOWN",
+ options: { },
+ waitFor: "inspector-updated"
+ },
+ {
+ desc: "Expand header container",
+ focused: "root.elt",
+ activedescendant: "header.tagLine",
+ key: "VK_RIGHT",
+ options: { },
+ waitFor: "expanded"
+ },
+ {
+ desc: "Select text container",
+ focused: "root.elt",
+ activedescendant: "container-0.tagLine",
+ key: "VK_DOWN",
+ options: { },
+ waitFor: "inspector-updated"
+ },
+ {
+ desc: "Select header container again",
+ focused: "root.elt",
+ activedescendant: "header.tagLine",
+ key: "VK_UP",
+ options: { },
+ waitFor: "inspector-updated"
+ },
+ {
+ desc: "Collapse header container",
+ focused: "root.elt",
+ activedescendant: "header.tagLine",
+ key: "VK_LEFT",
+ options: { },
+ waitFor: "collapsed"
+ },
+ {
+ desc: "Focus on header container tag",
+ focused: "header.focusableElms.0",
+ activedescendant: "header.tagLine",
+ key: "VK_RETURN",
+ options: { }
+ },
+ {
+ desc: "Remove focus from header container tag",
+ focused: "root.elt",
+ activedescendant: "header.tagLine",
+ key: "VK_ESCAPE",
+ options: { }
+ },
+ {
+ desc: "Focus on header container tag again",
+ focused: "header.focusableElms.0",
+ activedescendant: "header.tagLine",
+ key: "VK_SPACE",
+ options: { }
+ },
+ {
+ desc: "Focus on header id attribute",
+ focused: "header.focusableElms.1",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Focus on header class attribute",
+ focused: "header.focusableElms.2",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Focus on header new attribute",
+ focused: "header.focusableElms.3",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Circle back and focus on header tag again",
+ focused: "header.focusableElms.0",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Circle back and focus on header new attribute again",
+ focused: "header.focusableElms.3",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { shiftKey: true }
+ },
+ {
+ desc: "Tab back and focus on header class attribute",
+ focused: "header.focusableElms.2",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { shiftKey: true }
+ },
+ {
+ desc: "Tab back and focus on header id attribute",
+ focused: "header.focusableElms.1",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { shiftKey: true }
+ },
+ {
+ desc: "Tab back and focus on header tag",
+ focused: "header.focusableElms.0",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { shiftKey: true }
+ },
+ {
+ desc: "Expand header container, ensure that focus is still on header tag",
+ focused: "header.focusableElms.0",
+ activedescendant: "header.tagLine",
+ key: "VK_RIGHT",
+ options: { },
+ waitFor: "expanded"
+ },
+ {
+ desc: "Activate header tag editor",
+ focused: "header.editor.tag.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_RETURN",
+ options: { }
+ },
+ {
+ desc: "Activate header id attribute editor",
+ focused: "header.editor.attrList.children.0.children.1.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Deselect text in header id attribute editor",
+ focused: "header.editor.attrList.children.0.children.1.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Activate header class attribute editor",
+ focused: "header.editor.attrList.children.1.children.1.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Deselect text in header class attribute editor",
+ focused: "header.editor.attrList.children.1.children.1.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Activate header new attribute editor",
+ focused: "header.editor.newAttr.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Circle back and activate header tag editor again",
+ focused: "header.editor.tag.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Circle back and activate header new attribute editor again",
+ focused: "header.editor.newAttr.inplaceEditor.input",
+ activedescendant: "header.tagLine",
+ key: "VK_TAB",
+ options: { shiftKey: true }
+ },
+ {
+ desc: "Exit edit mode and keep focus on header new attribute",
+ focused: "header.focusableElms.3",
+ activedescendant: "header.tagLine",
+ key: "VK_ESCAPE",
+ options: { }
+ },
+ {
+ desc: "Move the selection to body and reset focus to container tree",
+ focused: "docBody",
+ activedescendant: "body.tagLine",
+ key: "VK_UP",
+ options: { },
+ waitFor: "inspector-updated"
+ },
+];
+
+let containerID = 0;
+let elms = {};
+
+add_task(function* () {
+ let { inspector } = yield openInspectorForURL(`data:text/html;charset=utf-8,
+ <h1 id="some-id" class="some-class">foo<span>Child span<span></h1>`);
+
+ // Record containers that are created after inspector is initialized to be
+ // useful in testing.
+ inspector.on("container-created", memorizeContainer);
+ registerCleanupFunction(() => {
+ inspector.off("container-created", memorizeContainer);
+ });
+
+ elms.docBody = inspector.markup.doc.body;
+ elms.root = inspector.markup.getContainer(inspector.markup._rootNode);
+ elms.header = yield getContainerForSelector("h1", inspector);
+ elms.body = yield getContainerForSelector("body", inspector);
+
+ // Initial focus is on root element and active descendant should be set on
+ // body tag line.
+ testNavigationState(inspector, elms, elms.docBody, elms.body.tagLine);
+
+ // Focus on the tree element.
+ elms.root.elt.focus();
+
+ for (let testData of TESTS) {
+ yield runAccessibilityNavigationTest(inspector, elms, testData);
+ }
+
+ elms = null;
+});
+
+// Record all containers that are created dynamically into elms object.
+function memorizeContainer(event, container) {
+ elms[`container-${containerID++}`] = container;
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation_after_edit.js b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation_after_edit.js
new file mode 100644
index 000000000..ec217db09
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation_after_edit.js
@@ -0,0 +1,126 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from helper_markup_accessibility_navigation.js */
+
+"use strict";
+
+// Test keyboard navigation accessibility is preserved after editing attributes.
+
+loadHelperScript("helper_markup_accessibility_navigation.js");
+
+const TEST_URI = '<div id="some-id" class="some-class"></div>';
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * key {String} key event's key
+ * options {?Object} optional event data such as shiftKey, etc
+ * focused {String} path to expected focused element relative to
+ * its container
+ * activedescendant {String} path to expected aria-activedescendant element
+ * relative to its container
+ * waitFor {String} optional event to wait for if keyboard actions
+ * result in asynchronous updates
+ * }
+ */
+const TESTS = [
+ {
+ desc: "Select header container",
+ focused: "root.elt",
+ activedescendant: "div.tagLine",
+ key: "VK_DOWN",
+ options: { },
+ waitFor: "inspector-updated"
+ },
+ {
+ desc: "Focus on header tag",
+ focused: "div.focusableElms.0",
+ activedescendant: "div.tagLine",
+ key: "VK_RETURN",
+ options: { }
+ },
+ {
+ desc: "Activate header tag editor",
+ focused: "div.editor.tag.inplaceEditor.input",
+ activedescendant: "div.tagLine",
+ key: "VK_RETURN",
+ options: { }
+ },
+ {
+ desc: "Activate header id attribute editor",
+ focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+ activedescendant: "div.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Deselect text in header id attribute editor",
+ focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+ activedescendant: "div.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Move the cursor to the left",
+ focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+ activedescendant: "div.tagLine",
+ key: "VK_LEFT",
+ options: { }
+ },
+ {
+ desc: "Modify the attribute",
+ focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+ activedescendant: "div.tagLine",
+ key: "A",
+ options: { }
+ },
+ {
+ desc: "Commit the attribute change",
+ focused: "div.focusableElms.1",
+ activedescendant: "div.tagLine",
+ key: "VK_RETURN",
+ options: { },
+ waitFor: "inspector-updated"
+ },
+ {
+ desc: "Tab and focus on header class attribute",
+ focused: "div.focusableElms.2",
+ activedescendant: "div.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+ {
+ desc: "Tab and focus on header new attribute node",
+ focused: "div.focusableElms.3",
+ activedescendant: "div.tagLine",
+ key: "VK_TAB",
+ options: { }
+ },
+];
+
+let elms = {};
+
+add_task(function* () {
+ let url = `data:text/html;charset=utf-8,${TEST_URI}`;
+ let { inspector } = yield openInspectorForURL(url);
+
+ elms.docBody = inspector.markup.doc.body;
+ elms.root = inspector.markup.getContainer(inspector.markup._rootNode);
+ elms.div = yield getContainerForSelector("div", inspector);
+ elms.body = yield getContainerForSelector("body", inspector);
+
+ // Initial focus is on root element and active descendant should be set on
+ // body tag line.
+ testNavigationState(inspector, elms, elms.docBody, elms.body.tagLine);
+
+ // Focus on the tree element.
+ elms.root.elt.focus();
+
+ for (let testData of TESTS) {
+ yield runAccessibilityNavigationTest(inspector, elms, testData);
+ }
+
+ elms = null;
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_accessibility_semantics.js b/devtools/client/inspector/markup/test/browser_markup_accessibility_semantics.js
new file mode 100644
index 000000000..b38a68c10
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_semantics.js
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Test that inspector markup view has all expected ARIA properties set and
+// updated.
+
+const TOP_CONTAINER_LEVEL = 3;
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(`
+ data:text/html;charset=utf-8,
+ <h1>foo</h1>
+ <span>bar</span>
+ <ul>
+ <li></li>
+ </ul>`);
+ let markup = inspector.markup;
+ let doc = markup.doc;
+ let win = doc.defaultView;
+
+ let rootElt = markup.getContainer(markup._rootNode).elt;
+ let bodyContainer = yield getContainerForSelector("body", inspector);
+ let spanContainer = yield getContainerForSelector("span", inspector);
+ let headerContainer = yield getContainerForSelector("h1", inspector);
+ let listContainer = yield getContainerForSelector("ul", inspector);
+
+ // Focus on the tree element.
+ rootElt.focus();
+
+ // Test tree related semantics
+ is(rootElt.getAttribute("role"), "tree",
+ "Root container should have tree semantics");
+ is(rootElt.getAttribute("aria-dropeffect"), "none",
+ "By default root container's drop effect should be set to none");
+ is(rootElt.getAttribute("aria-activedescendant"),
+ bodyContainer.tagLine.getAttribute("id"),
+ "Default active descendant should be set to body");
+ is(bodyContainer.tagLine.getAttribute("aria-level"), TOP_CONTAINER_LEVEL - 1,
+ "Body container tagLine should have nested level up to date");
+ [spanContainer, headerContainer, listContainer].forEach(container => {
+ let treeitem = container.tagLine;
+ is(treeitem.getAttribute("role"), "treeitem",
+ "Child container tagLine elements should have tree item semantics");
+ is(treeitem.getAttribute("aria-level"), TOP_CONTAINER_LEVEL,
+ "Child container tagLine should have nested level up to date");
+ is(treeitem.getAttribute("aria-grabbed"), "false",
+ "Child container should be draggable but not grabbed by default");
+ is(container.children.getAttribute("role"), "group",
+ "Container with children should have its children element have group " +
+ "semantics");
+ ok(treeitem.id, "Tree item should have id assigned");
+ if (container.closeTagLine) {
+ is(container.closeTagLine.getAttribute("role"), "presentation",
+ "Ignore closing tag");
+ }
+ if (container.expander) {
+ is(container.expander.getAttribute("role"), "presentation",
+ "Ignore expander");
+ }
+ });
+
+ // Test expanding/expandable semantics
+ ok(!spanContainer.tagLine.hasAttribute("aria-expanded"),
+ "Non expandable tree items should not have aria-expanded attribute");
+ ok(!headerContainer.tagLine.hasAttribute("aria-expanded"),
+ "Non expandable tree items should not have aria-expanded attribute");
+ is(listContainer.tagLine.getAttribute("aria-expanded"), "false",
+ "Closed tree item should have aria-expanded unset");
+
+ info("Selecting and expanding list container");
+ let updated = waitForMultipleChildrenUpdates(inspector);
+ yield selectNode("ul", inspector);
+ EventUtils.synthesizeKey("VK_RIGHT", {}, win);
+ yield updated;
+
+ is(rootElt.getAttribute("aria-activedescendant"),
+ listContainer.tagLine.getAttribute("id"),
+ "Active descendant should not be set to list container tagLine");
+ is(listContainer.tagLine.getAttribute("aria-expanded"), "true",
+ "Open tree item should have aria-expanded set");
+ let listItemContainer = yield getContainerForSelector("li", inspector);
+ is(listItemContainer.tagLine.getAttribute("aria-level"),
+ TOP_CONTAINER_LEVEL + 1,
+ "Grand child container tagLine should have nested level up to date");
+ is(listItemContainer.children.getAttribute("role"), "presentation",
+ "Container with no children should have its children element ignored by " +
+ "accessibility");
+
+ info("Collapsing list container");
+ updated = waitForMultipleChildrenUpdates(inspector);
+ EventUtils.synthesizeKey("VK_LEFT", {}, win);
+ yield updated;
+
+ is(listContainer.tagLine.getAttribute("aria-expanded"), "false",
+ "Closed tree item should have aria-expanded unset");
+});
+
diff --git a/devtools/client/inspector/markup/test/browser_markup_anonymous_01.js b/devtools/client/inspector/markup/test/browser_markup_anonymous_01.js
new file mode 100644
index 000000000..fd32251d0
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_01.js
@@ -0,0 +1,44 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test native anonymous content in the markupview.
+const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ let pseudo = yield getNodeFront("#pseudo", inspector);
+
+ // Markup looks like: <div><::before /><span /><::after /></div>
+ let children = yield inspector.walker.children(pseudo);
+ is(children.nodes.length, 3, "Children returned from walker");
+
+ info("Checking the ::before pseudo element");
+ let before = children.nodes[0];
+ yield isEditingMenuDisabled(before, inspector);
+
+ info("Checking the normal child element");
+ let span = children.nodes[1];
+ yield isEditingMenuEnabled(span, inspector);
+
+ info("Checking the ::after pseudo element");
+ let after = children.nodes[2];
+ yield isEditingMenuDisabled(after, inspector);
+
+ let native = yield getNodeFront("#native", inspector);
+
+ // Markup looks like: <div><video controls /></div>
+ let nativeChildren = yield inspector.walker.children(native);
+ is(nativeChildren.nodes.length, 1, "Children returned from walker");
+
+ info("Checking the video element");
+ let video = nativeChildren.nodes[0];
+ ok(!video.isAnonymous, "<video> is not anonymous");
+
+ let videoChildren = yield inspector.walker.children(video);
+ is(videoChildren.nodes.length, 0,
+ "No native children returned from walker for <video> by default");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_anonymous_02.js b/devtools/client/inspector/markup/test/browser_markup_anonymous_02.js
new file mode 100644
index 000000000..b6221c5c3
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_02.js
@@ -0,0 +1,31 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test XBL anonymous content in the markupview
+const TEST_URL = "chrome://devtools/content/scratchpad/scratchpad.xul";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ let toolbarbutton = yield getNodeFront("toolbarbutton", inspector);
+ let children = yield inspector.walker.children(toolbarbutton);
+
+ is(toolbarbutton.numChildren, 3, "Correct number of children");
+ is(children.nodes.length, 3, "Children returned from walker");
+
+ is(toolbarbutton.isAnonymous, false, "Toolbarbutton is not anonymous");
+ yield isEditingMenuEnabled(toolbarbutton, inspector);
+
+ for (let node of children.nodes) {
+ ok(node.isAnonymous, "Child is anonymous");
+ ok(node._form.isXBLAnonymous, "Child is XBL anonymous");
+ ok(!node._form.isShadowAnonymous, "Child is not shadow anonymous");
+ ok(!node._form.isNativeAnonymous, "Child is not native anonymous");
+ yield isEditingMenuDisabled(node, inspector);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js b/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
new file mode 100644
index 000000000..010ce06e0
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
@@ -0,0 +1,34 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test shadow DOM content in the markupview.
+// Note that many features are not yet enabled, but basic listing
+// of elements should be working.
+const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
+
+add_task(function* () {
+ Services.prefs.setBoolPref("dom.webcomponents.enabled", true);
+
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ let shadow = yield getNodeFront("#shadow", inspector.markup);
+ let children = yield inspector.walker.children(shadow);
+
+ is(shadow.numChildren, 3, "Children of the shadow root are counted");
+ is(children.nodes.length, 3, "Children returned from walker");
+
+ info("Checking the ::before pseudo element");
+ let before = children.nodes[0];
+ yield isEditingMenuDisabled(before, inspector);
+
+ info("Checking the <h3> shadow element");
+ let shadowChild1 = children.nodes[1];
+ yield isEditingMenuDisabled(shadowChild1, inspector);
+
+ info("Checking the <select> shadow element");
+ let shadowChild2 = children.nodes[2];
+ yield isEditingMenuDisabled(shadowChild2, inspector);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_anonymous_04.js b/devtools/client/inspector/markup/test/browser_markup_anonymous_04.js
new file mode 100644
index 000000000..da5e4567d
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_04.js
@@ -0,0 +1,37 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test native anonymous content in the markupview with
+// devtools.inspector.showAllAnonymousContent set to true
+const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
+const PREF = "devtools.inspector.showAllAnonymousContent";
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ let native = yield getNodeFront("#native", inspector);
+
+ // Markup looks like: <div><video controls /></div>
+ let nativeChildren = yield inspector.walker.children(native);
+ is(nativeChildren.nodes.length, 1, "Children returned from walker");
+
+ info("Checking the video element");
+ let video = nativeChildren.nodes[0];
+ ok(!video.isAnonymous, "<video> is not anonymous");
+
+ let videoChildren = yield inspector.walker.children(video);
+ is(videoChildren.nodes.length, 3, "<video> has native anonymous children");
+
+ for (let node of videoChildren.nodes) {
+ ok(node.isAnonymous, "Child is anonymous");
+ ok(!node._form.isXBLAnonymous, "Child is not XBL anonymous");
+ ok(!node._form.isShadowAnonymous, "Child is not shadow anonymous");
+ ok(node._form.isNativeAnonymous, "Child is native anonymous");
+ yield isEditingMenuDisabled(node, inspector);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js b/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js
new file mode 100644
index 000000000..275bff0b7
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js
@@ -0,0 +1,67 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that image nodes have the "copy data-uri" contextual menu item enabled
+// and that clicking it puts the image data into the clipboard
+
+add_task(function* () {
+ yield addTab(URL_ROOT + "doc_markup_image_and_canvas.html");
+ let {inspector, testActor} = yield openInspector();
+
+ yield selectNode("div", inspector);
+ yield assertCopyImageDataNotAvailable(inspector);
+
+ yield selectNode("img", inspector);
+ yield assertCopyImageDataAvailable(inspector);
+ let expectedSrc = yield testActor.getAttribute("img", "src");
+ yield triggerCopyImageUrlAndWaitForClipboard(expectedSrc, inspector);
+
+ yield selectNode("canvas", inspector);
+ yield assertCopyImageDataAvailable(inspector);
+ let expectedURL = yield testActor.eval(`
+ content.document.querySelector(".canvas").toDataURL();`);
+ yield triggerCopyImageUrlAndWaitForClipboard(expectedURL, inspector);
+
+ // Check again that the menu isn't available on the DIV (to make sure our
+ // menu updating mechanism works)
+ yield selectNode("div", inspector);
+ yield assertCopyImageDataNotAvailable(inspector);
+});
+
+function* assertCopyImageDataNotAvailable(inspector) {
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let item = allMenuItems.find(i => i.id === "node-menu-copyimagedatauri");
+
+ ok(item, "The menu item was found in the contextual menu");
+ ok(item.disabled, "The menu item is disabled");
+}
+
+function* assertCopyImageDataAvailable(inspector) {
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let item = allMenuItems.find(i => i.id === "node-menu-copyimagedatauri");
+
+ ok(item, "The menu item was found in the contextual menu");
+ ok(!item.disabled, "The menu item is enabled");
+}
+
+function triggerCopyImageUrlAndWaitForClipboard(expected, inspector) {
+ let def = defer();
+
+ SimpleTest.waitForClipboard(expected, () => {
+ inspector.markup.getContainer(inspector.selection.nodeFront)
+ .copyImageDataUri();
+ }, () => {
+ ok(true, "The clipboard contains the expected value " +
+ expected.substring(0, 50) + "...");
+ def.resolve();
+ }, () => {
+ ok(false, "The clipboard doesn't contain the expected value " +
+ expected.substring(0, 50) + "...");
+ def.resolve();
+ });
+
+ return def.promise;
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_01.js b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_01.js
new file mode 100644
index 000000000..f860456d1
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_01.js
@@ -0,0 +1,76 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_style_attr_test_runner.js */
+
+"use strict";
+
+// Test CSS state is correctly determined and the corresponding suggestions are
+// displayed. i.e. CSS property suggestions are shown when cursor is like:
+// ```style="di|"``` where | is the cursor; And CSS value suggestion is
+// displayed when the cursor is like: ```style="display:n|"``` properly. No
+// suggestions should ever appear when the attribute is not a style attribute.
+// The correctness and cycling of the suggestions is covered in the ruleview
+// tests.
+
+loadHelperScript("helper_style_attr_test_runner.js");
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+
+// test data format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// expected input.selectionStart,
+// expected input.selectionEnd,
+// is popup expected to be open ?
+// ]
+const TEST_DATA = [
+ ["s", "s", 1, 1, false],
+ ["t", "st", 2, 2, false],
+ ["y", "sty", 3, 3, false],
+ ["l", "styl", 4, 4, false],
+ ["e", "style", 5, 5, false],
+ ["=", "style=", 6, 6, false],
+ ["\"", "style=\"", 7, 7, false],
+ ["d", "style=\"display", 8, 14, true],
+ ["VK_TAB", "style=\"display", 14, 14, true],
+ ["VK_TAB", "style=\"dominant-baseline", 24, 24, true],
+ ["VK_TAB", "style=\"direction", 16, 16, true],
+ ["click_1", "style=\"display", 14, 14, false],
+ [":", "style=\"display:block", 15, 20, true],
+ ["n", "style=\"display:none", 16, 19, false],
+ ["VK_BACK_SPACE", "style=\"display:n", 16, 16, false],
+ ["VK_BACK_SPACE", "style=\"display:", 15, 15, false],
+ [" ", "style=\"display: block", 16, 21, true],
+ [" ", "style=\"display: block", 17, 22, true],
+ ["i", "style=\"display: inherit", 18, 24, true],
+ ["VK_RIGHT", "style=\"display: inherit", 24, 24, false],
+ [";", "style=\"display: inherit;", 25, 25, false],
+ [" ", "style=\"display: inherit; ", 26, 26, false],
+ [" ", "style=\"display: inherit; ", 27, 27, false],
+ ["VK_LEFT", "style=\"display: inherit; ", 26, 26, false],
+ ["c", "style=\"display: inherit; color ", 27, 31, true],
+ ["VK_RIGHT", "style=\"display: inherit; color ", 31, 31, false],
+ [" ", "style=\"display: inherit; color ", 32, 32, false],
+ ["c", "style=\"display: inherit; color c ", 33, 33, false],
+ ["VK_BACK_SPACE", "style=\"display: inherit; color ", 32, 32, false],
+ [":", "style=\"display: inherit; color :aliceblue ", 33, 42, true],
+ ["c", "style=\"display: inherit; color :cadetblue ", 34, 42, true],
+ ["VK_DOWN", "style=\"display: inherit; color :chartreuse ", 34, 43, true],
+ ["VK_RIGHT", "style=\"display: inherit; color :chartreuse ", 43, 43, false],
+ [" ", "style=\"display: inherit; color :chartreuse aliceblue ",
+ 44, 53, true],
+ ["!", "style=\"display: inherit; color :chartreuse !important; ",
+ 45, 55, false],
+ ["VK_RIGHT", "style=\"display: inherit; color :chartreuse !important; ",
+ 55, 55, false],
+ ["VK_RETURN", "style=\"display: inherit; color :chartreuse !important;\"",
+ -1, -1, false]
+];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ yield runStyleAttributeAutocompleteTests(inspector, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_02.js b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_02.js
new file mode 100644
index 000000000..345ee4866
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_02.js
@@ -0,0 +1,106 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_style_attr_test_runner.js */
+
+"use strict";
+
+// Test CSS autocompletion of the style attributes stops after closing the
+// attribute using a matching quote.
+
+loadHelperScript("helper_style_attr_test_runner.js");
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+
+// test data format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// expected input.selectionStart,
+// expected input.selectionEnd,
+// is popup expected to be open ?
+// ]
+const TEST_DATA_DOUBLE = [
+ ["s", "s", 1, 1, false],
+ ["t", "st", 2, 2, false],
+ ["y", "sty", 3, 3, false],
+ ["l", "styl", 4, 4, false],
+ ["e", "style", 5, 5, false],
+ ["=", "style=", 6, 6, false],
+ ["\"", "style=\"", 7, 7, false],
+ ["c", "style=\"color", 8, 12, true],
+ ["VK_RIGHT", "style=\"color", 12, 12, false],
+ [":", "style=\"color:aliceblue", 13, 22, true],
+ ["b", "style=\"color:beige", 14, 18, true],
+ ["VK_RIGHT", "style=\"color:beige", 18, 18, false],
+ ["\"", "style=\"color:beige\"", 19, 19, false],
+ [" ", "style=\"color:beige\" ", 20, 20, false],
+ ["d", "style=\"color:beige\" d", 21, 21, false],
+ ["a", "style=\"color:beige\" da", 22, 22, false],
+ ["t", "style=\"color:beige\" dat", 23, 23, false],
+ ["a", "style=\"color:beige\" data", 24, 24, false],
+ ["VK_RETURN", "style=\"color:beige\"",
+ -1, -1, false]
+];
+
+// Check that single quote attribute is also supported
+const TEST_DATA_SINGLE = [
+ ["s", "s", 1, 1, false],
+ ["t", "st", 2, 2, false],
+ ["y", "sty", 3, 3, false],
+ ["l", "styl", 4, 4, false],
+ ["e", "style", 5, 5, false],
+ ["=", "style=", 6, 6, false],
+ ["'", "style='", 7, 7, false],
+ ["c", "style='color", 8, 12, true],
+ ["VK_RIGHT", "style='color", 12, 12, false],
+ [":", "style='color:aliceblue", 13, 22, true],
+ ["b", "style='color:beige", 14, 18, true],
+ ["VK_RIGHT", "style='color:beige", 18, 18, false],
+ ["'", "style='color:beige'", 19, 19, false],
+ [" ", "style='color:beige' ", 20, 20, false],
+ ["d", "style='color:beige' d", 21, 21, false],
+ ["a", "style='color:beige' da", 22, 22, false],
+ ["t", "style='color:beige' dat", 23, 23, false],
+ ["a", "style='color:beige' data", 24, 24, false],
+ ["VK_RETURN", "style=\"color:beige\"",
+ -1, -1, false]
+];
+
+// Check that autocompletion is still enabled after using url('1)
+const TEST_DATA_INNER = [
+ ["s", "s", 1, 1, false],
+ ["t", "st", 2, 2, false],
+ ["y", "sty", 3, 3, false],
+ ["l", "styl", 4, 4, false],
+ ["e", "style", 5, 5, false],
+ ["=", "style=", 6, 6, false],
+ ["\"", "style=\"", 7, 7, false],
+ ["b", "style=\"border", 8, 13, true],
+ ["a", "style=\"background", 9, 17, true],
+ ["VK_RIGHT", "style=\"background", 17, 17, false],
+ [":", "style=\"background:aliceblue", 18, 27, true],
+ ["u", "style=\"background:unset", 19, 23, true],
+ ["r", "style=\"background:url", 20, 21, false],
+ ["l", "style=\"background:url", 21, 21, false],
+ ["(", "style=\"background:url(", 22, 22, false],
+ ["'", "style=\"background:url('", 23, 23, false],
+ ["1", "style=\"background:url('1", 24, 24, false],
+ ["'", "style=\"background:url('1'", 25, 25, false],
+ [")", "style=\"background:url('1')", 26, 26, false],
+ [";", "style=\"background:url('1');", 27, 27, false],
+ [" ", "style=\"background:url('1'); ", 28, 28, false],
+ ["c", "style=\"background:url('1'); color", 29, 33, true],
+ ["VK_RIGHT", "style=\"background:url('1'); color", 33, 33, false],
+ [":", "style=\"background:url('1'); color:aliceblue", 34, 43, true],
+ ["b", "style=\"background:url('1'); color:beige", 35, 39, true],
+ ["VK_RETURN", "style=\"background:url('1'); color:beige\"", -1, -1, false]
+];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ yield runStyleAttributeAutocompleteTests(inspector, TEST_DATA_DOUBLE);
+ yield runStyleAttributeAutocompleteTests(inspector, TEST_DATA_SINGLE);
+ yield runStyleAttributeAutocompleteTests(inspector, TEST_DATA_INNER);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_03.js b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_03.js
new file mode 100644
index 000000000..3dbc3e6b2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_03.js
@@ -0,0 +1,54 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_style_attr_test_runner.js */
+
+"use strict";
+
+// Test CSS autocompletion of the style attribute can be triggered when the
+// caret is before a non-word character.
+
+loadHelperScript("helper_style_attr_test_runner.js");
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+
+// test data format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// expected input.selectionStart,
+// expected input.selectionEnd,
+// is popup expected to be open ?
+// ]
+const TEST_DATA = [
+ ["s", "s", 1, 1, false],
+ ["t", "st", 2, 2, false],
+ ["y", "sty", 3, 3, false],
+ ["l", "styl", 4, 4, false],
+ ["e", "style", 5, 5, false],
+ ["=", "style=", 6, 6, false],
+ ["\"", "style=\"", 7, 7, false],
+ ["\"", "style=\"\"", 8, 8, false],
+ ["VK_LEFT", "style=\"\"", 7, 7, false],
+ ["c", "style=\"color\"", 8, 12, true],
+ ["o", "style=\"color\"", 9, 12, true],
+ ["VK_RIGHT", "style=\"color\"", 12, 12, false],
+ [":", "style=\"color:aliceblue\"", 13, 22, true],
+ ["b", "style=\"color:beige\"", 14, 18, true],
+ ["VK_RIGHT", "style=\"color:beige\"", 18, 18, false],
+ [";", "style=\"color:beige;\"", 19, 19, false],
+ [";", "style=\"color:beige;;\"", 20, 20, false],
+ ["VK_LEFT", "style=\"color:beige;;\"", 19, 19, false],
+ ["p", "style=\"color:beige;padding;\"", 20, 26, true],
+ ["VK_RIGHT", "style=\"color:beige;padding;\"", 26, 26, false],
+ [":", "style=\"color:beige;padding:calc;\"", 27, 31, true],
+ ["0", "style=\"color:beige;padding:0;\"", 28, 28, false],
+ ["VK_RETURN", "style=\"color:beige;padding:0;\"",
+ -1, -1, false]
+];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ yield runStyleAttributeAutocompleteTests(inspector, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js
new file mode 100644
index 000000000..0c25e2fc6
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_01.js
@@ -0,0 +1,51 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that dragging a node near the top or bottom edge of the markup-view
+// auto-scrolls the view on a large toolbox.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop_autoscroll_01.html";
+
+add_task(function* () {
+ // Set the toolbox as large as it would get. The toolbox automatically shrinks
+ // to not overflow to window.
+ yield pushPref("devtools.toolbox.footer.height", 10000);
+
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let markup = inspector.markup;
+ let viewHeight = markup.doc.documentElement.clientHeight;
+
+ info("Pretend the markup-view is dragging");
+ markup.isDragging = true;
+
+ info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
+ let onScrolled = waitForScrollStop(markup.doc);
+
+ markup._onMouseMove({
+ preventDefault: () => {},
+ target: markup.doc.body,
+ pageY: viewHeight + markup.doc.defaultView.scrollY
+ });
+
+ let bottomScrollPos = yield onScrolled;
+ ok(bottomScrollPos > 0, "The view was scrolled down");
+
+ info("Simulate a mousemove at the top and expect more scrolling");
+ onScrolled = waitForScrollStop(markup.doc);
+
+ markup._onMouseMove({
+ preventDefault: () => {},
+ target: markup.doc.body,
+ pageY: markup.doc.defaultView.scrollY
+ });
+
+ let topScrollPos = yield onScrolled;
+ ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
+ is(topScrollPos, 0, "The view was scrolled up to the top");
+
+ info("Simulate a mouseup to stop dragging");
+ markup._onMouseUp();
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js
new file mode 100644
index 000000000..4aca6f424
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_autoscroll_02.js
@@ -0,0 +1,49 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that dragging a node near the top or bottom edge of the markup-view
+// auto-scrolls the view on a small toolbox.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop_autoscroll_02.html";
+
+add_task(function* () {
+ // Set the toolbox to very small in size.
+ yield pushPref("devtools.toolbox.footer.height", 150);
+
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let markup = inspector.markup;
+ let viewHeight = markup.doc.documentElement.clientHeight;
+
+ info("Pretend the markup-view is dragging");
+ markup.isDragging = true;
+
+ info("Simulate a mousemove on the view, at the bottom, and expect scrolling");
+ let onScrolled = waitForScrollStop(markup.doc);
+
+ markup._onMouseMove({
+ preventDefault: () => {},
+ target: markup.doc.body,
+ pageY: viewHeight + markup.doc.defaultView.scrollY
+ });
+
+ let bottomScrollPos = yield onScrolled;
+ ok(bottomScrollPos > 0, "The view was scrolled down");
+ info("Simulate a mousemove at the top and expect more scrolling");
+ onScrolled = waitForScrollStop(markup.doc);
+
+ markup._onMouseMove({
+ preventDefault: () => {},
+ target: markup.doc.body,
+ pageY: markup.doc.defaultView.scrollY
+ });
+
+ let topScrollPos = yield onScrolled;
+ ok(topScrollPos < bottomScrollPos, "The view was scrolled up");
+ is(topScrollPos, 0, "The view was scrolled up to the top");
+
+ info("Simulate a mouseup to stop dragging");
+ markup._onMouseUp();
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_distance.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_distance.js
new file mode 100644
index 000000000..e94b02191
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_distance.js
@@ -0,0 +1,49 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that nodes don't start dragging before the mouse has moved by at least
+// the minimum vertical distance defined in markup-view.js by
+// DRAG_DROP_MIN_INITIAL_DISTANCE.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+const TEST_NODE = "#test";
+
+// Keep this in sync with DRAG_DROP_MIN_INITIAL_DISTANCE in markup-view.js
+const MIN_DISTANCE = 10;
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Drag the test node by half of the minimum distance");
+ yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE / 2);
+ yield checkIsDragging(inspector, TEST_NODE, false);
+
+ info("Drag the test node by exactly the minimum distance");
+ yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE);
+ yield checkIsDragging(inspector, TEST_NODE, true);
+ inspector.markup.cancelDragging();
+
+ info("Drag the test node by more than the minimum distance");
+ yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE * 2);
+ yield checkIsDragging(inspector, TEST_NODE, true);
+ inspector.markup.cancelDragging();
+
+ info("Drag the test node by minus the minimum distance");
+ yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE * -1);
+ yield checkIsDragging(inspector, TEST_NODE, true);
+ inspector.markup.cancelDragging();
+});
+
+function* checkIsDragging(inspector, selector, isDragging) {
+ let container = yield getContainerForSelector(selector, inspector);
+ if (isDragging) {
+ ok(container.isDragging, "The container is being dragged");
+ ok(inspector.markup.isDragging, "And the markup-view knows it");
+ } else {
+ ok(!container.isDragging, "The container hasn't been marked as dragging");
+ ok(!inspector.markup.isDragging, "And the markup-view either");
+ }
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_dragRootNode.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_dragRootNode.js
new file mode 100644
index 000000000..8bb4779d5
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_dragRootNode.js
@@ -0,0 +1,22 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the root node isn't draggable (as well as head and body).
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+const TEST_DATA = ["html", "head", "body"];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ for (let selector of TEST_DATA) {
+ info("Try to drag/drop node " + selector);
+ yield simulateNodeDrag(inspector, selector);
+
+ let container = yield getContainerForSelector(selector, inspector);
+ ok(!container.isDragging, "The container hasn't been marked as dragging");
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_draggable.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_draggable.js
new file mode 100644
index 000000000..1853ab4f7
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_draggable.js
@@ -0,0 +1,63 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test which nodes are consider draggable by the markup-view.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+
+// Test cases should be objects with the following properties:
+// - node {String|Function} A CSS selector that uniquely identifies the node to
+// be tested. Or a generator function called in a Task that should return the
+// corresponding MarkupContainer object to be tested.
+// - draggable {Boolean} Whether or not the node should be draggable.
+const TEST_DATA = [
+ { node: "head", draggable: false },
+ { node: "body", draggable: false },
+ { node: "html", draggable: false },
+ { node: "style", draggable: true },
+ { node: "a", draggable: true },
+ { node: "p", draggable: true },
+ { node: "input", draggable: true },
+ { node: "div", draggable: true },
+ {
+ node: function* (inspector) {
+ let parentFront = yield getNodeFront("#before", inspector);
+ let {nodes} = yield inspector.walker.children(parentFront);
+ // Getting the comment node.
+ return getContainerForNodeFront(nodes[1], inspector);
+ },
+ draggable: true
+ },
+ {
+ node: function* (inspector) {
+ let parentFront = yield getNodeFront("#test", inspector);
+ let {nodes} = yield inspector.walker.children(parentFront);
+ // Getting the ::before pseudo element.
+ return getContainerForNodeFront(nodes[0], inspector);
+ },
+ draggable: false
+ }
+];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ yield inspector.markup.expandAll();
+
+ for (let {node, draggable} of TEST_DATA) {
+ let container;
+ let name;
+ if (typeof node === "string") {
+ container = yield getContainerForSelector(node, inspector);
+ name = node;
+ } else {
+ container = yield node(inspector);
+ name = container.toString();
+ }
+
+ let status = draggable ? "draggable" : "not draggable";
+ info(`Testing ${name}, expecting it to be ${status}`);
+ is(container.isDraggable(), draggable, `The node is ${status}`);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js
new file mode 100644
index 000000000..075d14352
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js
@@ -0,0 +1,34 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test whether ESCAPE keypress cancels dragging of an element.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let {markup} = inspector;
+
+ info("Get a test container");
+ yield selectNode("#test", inspector);
+ let container = yield getContainerForSelector("#test", inspector);
+
+ info("Simulate a drag/drop on this container");
+ yield simulateNodeDrag(inspector, "#test");
+
+ ok(container.isDragging && markup.isDragging,
+ "The container is being dragged");
+ ok(markup.doc.body.classList.contains("dragging"),
+ "The dragging css class was added");
+
+ info("Simulate ESCAPE keypress");
+ EventUtils.sendKey("escape", inspector.panelWin);
+
+ ok(!container.isDragging && !markup.isDragging,
+ "The dragging has stopped");
+ ok(!markup.doc.body.classList.contains("dragging"),
+ "The dragging css class was removed");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
new file mode 100644
index 000000000..9eea6a102
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
@@ -0,0 +1,48 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that pseudo-elements and anonymous nodes are not draggable.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+const PREF = "devtools.inspector.showAllAnonymousContent";
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Expanding nodes below #test");
+ let parentFront = yield getNodeFront("#test", inspector);
+ yield inspector.markup.expandNode(parentFront);
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ info("Getting the ::before pseudo element and selecting it");
+ let parentContainer = yield getContainerForNodeFront(parentFront, inspector);
+ let beforePseudo = parentContainer.elt.children[1].firstChild.container;
+ parentContainer.elt.scrollIntoView(true);
+ yield selectNode(beforePseudo.node, inspector);
+
+ info("Simulate dragging the ::before pseudo element");
+ yield simulateNodeDrag(inspector, beforePseudo);
+
+ ok(!beforePseudo.isDragging, "::before pseudo element isn't dragging");
+
+ info("Expanding nodes below #anonymousParent");
+ let inputFront = yield getNodeFront("#anonymousParent", inspector);
+ yield inspector.markup.expandNode(inputFront);
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ info("Getting the anonymous node and selecting it");
+ let inputContainer = yield getContainerForNodeFront(inputFront, inspector);
+ let anonymousDiv = inputContainer.elt.children[1].firstChild.container;
+ inputContainer.elt.scrollIntoView(true);
+ yield selectNode(anonymousDiv.node, inspector);
+
+ info("Simulate dragging the anonymous node");
+ yield simulateNodeDrag(inspector, anonymousDiv);
+
+ ok(!anonymousDiv.isDragging, "anonymous node isn't dragging");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_reorder.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_reorder.js
new file mode 100644
index 000000000..f74b50147
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_reorder.js
@@ -0,0 +1,109 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test different kinds of drag and drop node re-ordering.
+
+const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let ids;
+
+ info("Expand #test node");
+ let parentFront = yield getNodeFront("#test", inspector);
+ yield inspector.markup.expandNode(parentFront);
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ info("Scroll #test into view");
+ let parentContainer = yield getContainerForNodeFront(parentFront, inspector);
+ parentContainer.elt.scrollIntoView(true);
+
+ info("Test putting an element back at its original place");
+ yield dragElementToOriginalLocation("#firstChild", inspector);
+ ids = yield getChildrenIDsOf(parentFront, inspector);
+ is(ids[0], "firstChild",
+ "#firstChild is still the first child of #test");
+ is(ids[1], "middleChild",
+ "#middleChild is still the second child of #test");
+
+ info("Testing switching elements inside their parent");
+ yield moveElementDown("#firstChild", "#middleChild", inspector);
+ ids = yield getChildrenIDsOf(parentFront, inspector);
+ is(ids[0], "middleChild",
+ "#firstChild is now the second child of #test");
+ is(ids[1], "firstChild",
+ "#middleChild is now the first child of #test");
+
+ info("Testing switching elements with a last child");
+ yield moveElementDown("#firstChild", "#lastChild", inspector);
+ ids = yield getChildrenIDsOf(parentFront, inspector);
+ is(ids[1], "lastChild",
+ "#lastChild is now the second child of #test");
+ is(ids[2], "firstChild",
+ "#firstChild is now the last child of #test");
+
+ info("Testing appending element to a parent");
+ yield moveElementDown("#before", "#test", inspector);
+ ids = yield getChildrenIDsOf(parentFront, inspector);
+ is(ids.length, 4,
+ "New element appended to #test");
+ is(ids[0], "before",
+ "New element is appended at the right place (currently first child)");
+
+ info("Testing moving element to after it's parent");
+ yield moveElementDown("#firstChild", "#test", inspector);
+ ids = yield getChildrenIDsOf(parentFront, inspector);
+ is(ids.length, 3,
+ "#firstChild is no longer #test's child");
+ let siblingFront = yield inspector.walker.nextSibling(parentFront);
+ is(siblingFront.id, "firstChild",
+ "#firstChild is now #test's nextElementSibling");
+});
+
+function* dragElementToOriginalLocation(selector, inspector) {
+ info("Picking up and putting back down " + selector);
+
+ function onMutation() {
+ ok(false, "Mutation received from dragging a node back to its location");
+ }
+ inspector.on("markupmutation", onMutation);
+ yield simulateNodeDragAndDrop(inspector, selector, 0, 0);
+
+ // Wait a bit to make sure the event never fires.
+ // This doesn't need to catch *all* cases, since the mutation
+ // will cause failure later in the test when it checks element ordering.
+ yield wait(500);
+ inspector.off("markupmutation", onMutation);
+}
+
+function* moveElementDown(selector, next, inspector) {
+ info("Switching " + selector + " with " + next);
+
+ let container = yield getContainerForSelector(next, inspector);
+ let height = container.tagLine.getBoundingClientRect().height;
+
+ let onMutated = inspector.once("markupmutation");
+ let uiUpdate = inspector.once("inspector-updated");
+
+ yield simulateNodeDragAndDrop(inspector, selector, 0, Math.round(height) + 2);
+
+ let mutations = yield onMutated;
+ yield uiUpdate;
+
+ is(mutations.length, 2, "2 mutations were received");
+}
+
+function* getChildrenIDsOf(parentFront, {walker}) {
+ let {nodes} = yield walker.children(parentFront);
+ // Filter out non-element nodes since children also returns pseudo-elements.
+ return nodes.filter(node => {
+ return !node.isPseudoElement;
+ }).map(node => {
+ return node.id;
+ });
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_tooltip.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_tooltip.js
new file mode 100644
index 000000000..77472800e
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_tooltip.js
@@ -0,0 +1,35 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that tooltips don't appear when dragging over tooltip targets.
+
+const TEST_URL = "data:text/html;charset=utf8,<img src=\"about:logo\" /><div>";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let {markup} = inspector;
+
+ info("Get the tooltip target element for the image's src attribute");
+ let img = yield getContainerForSelector("img", inspector);
+ let target = img.editor.getAttributeElement("src").querySelector(".link");
+
+ info("Check that the src attribute of the image is a valid tooltip target");
+ let isValid = yield isHoverTooltipTarget(markup.imagePreviewTooltip, target);
+ ok(isValid, "The element is a valid tooltip target");
+
+ info("Start dragging the test div");
+ yield simulateNodeDrag(inspector, "div");
+
+ info("Now check that the src attribute of the image isn't a valid target");
+ isValid = yield isHoverTooltipTarget(markup.imagePreviewTooltip, target);
+ ok(!isValid, "The element is not a valid tooltip target");
+
+ info("Stop dragging the test div");
+ yield simulateNodeDrop(inspector, "div");
+
+ info("Check again the src attribute of the image");
+ isValid = yield isHoverTooltipTarget(markup.imagePreviewTooltip, target);
+ ok(isValid, "The element is a valid tooltip target");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events-overflow.js b/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
new file mode 100644
index 000000000..3e73921f4
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
@@ -0,0 +1,91 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_URL = URL_ROOT + "doc_markup_events-overflow.html";
+const TEST_DATA = [
+ {
+ desc: "editor overflows container",
+ // scroll to bottom
+ initialScrollTop: -1,
+ // last header
+ headerToClick: 49,
+ alignBottom: true,
+ alignTop: false,
+ },
+ {
+ desc: "header overflows the container",
+ initialScrollTop: 2,
+ headerToClick: 0,
+ alignBottom: false,
+ alignTop: true,
+ },
+ {
+ desc: "neither header nor editor overflows the container",
+ initialScrollTop: 2,
+ headerToClick: 5,
+ alignBottom: false,
+ alignTop: false,
+ },
+];
+
+add_task(function* () {
+ let { inspector } = yield openInspectorForURL(TEST_URL);
+
+ let markupContainer = yield getContainerForSelector("#events", inspector);
+ let evHolder = markupContainer.elt.querySelector(".markupview-events");
+ let tooltip = inspector.markup.eventDetailsTooltip;
+
+ info("Clicking to open event tooltip.");
+ EventUtils.synthesizeMouseAtCenter(evHolder, {},
+ inspector.markup.doc.defaultView);
+ yield tooltip.once("shown");
+ info("EventTooltip visible.");
+
+ let container = tooltip.panel;
+ let containerRect = container.getBoundingClientRect();
+ let headers = container.querySelectorAll(".event-header");
+
+ for (let data of TEST_DATA) {
+ info("Testing scrolling when " + data.desc);
+
+ if (data.initialScrollTop < 0) {
+ info("Scrolling container to the bottom.");
+ let newScrollTop = container.scrollHeight - container.clientHeight;
+ data.initialScrollTop = container.scrollTop = newScrollTop;
+ } else {
+ info("Scrolling container by " + data.initialScrollTop + "px");
+ container.scrollTop = data.initialScrollTop;
+ }
+
+ is(container.scrollTop, data.initialScrollTop, "Container scrolled.");
+
+ info("Clicking on header #" + data.headerToClick);
+ let header = headers[data.headerToClick];
+
+ let ready = tooltip.once("event-tooltip-ready");
+ EventUtils.synthesizeMouseAtCenter(header, {}, header.ownerGlobal);
+ yield ready;
+
+ info("Event handler expanded.");
+
+ // Wait for any scrolling to finish.
+ yield promiseNextTick();
+
+ if (data.alignTop) {
+ let headerRect = header.getBoundingClientRect();
+
+ is(Math.round(headerRect.top), Math.round(containerRect.top),
+ "Clicked header is aligned with the container top.");
+ } else if (data.alignBottom) {
+ let editorRect = header.nextElementSibling.getBoundingClientRect();
+
+ is(Math.round(editorRect.bottom), Math.round(containerRect.bottom),
+ "Clicked event handler code is aligned with the container bottom.");
+ } else {
+ is(container.scrollTop, data.initialScrollTop,
+ "Container did not scroll, as expected.");
+ }
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
new file mode 100644
index 000000000..cfcb0a8ab
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
@@ -0,0 +1,61 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/*
+ * Test that the event details tooltip can be hidden by clicking outside of the tooltip
+ * after switching hosts.
+ */
+
+const TEST_URL = URL_ROOT + "doc_markup_events-overflow.html";
+
+registerCleanupFunction(() => {
+ // Restore the default Toolbox host position after the test.
+ Services.prefs.clearUserPref("devtools.toolbox.host");
+});
+
+add_task(function* () {
+ let { inspector, toolbox } = yield openInspectorForURL(TEST_URL);
+ yield runTests(inspector);
+
+ yield toolbox.switchHost("window");
+ yield runTests(inspector);
+
+ yield toolbox.switchHost("bottom");
+ yield runTests(inspector);
+
+ yield toolbox.destroy();
+});
+
+function* runTests(inspector) {
+ let markupContainer = yield getContainerForSelector("#events", inspector);
+ let evHolder = markupContainer.elt.querySelector(".markupview-events");
+ let tooltip = inspector.markup.eventDetailsTooltip;
+
+ info("Clicking to open event tooltip.");
+
+ let onInspectorUpdated = inspector.once("inspector-updated");
+ let onTooltipShown = tooltip.once("shown");
+ EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
+
+ yield onTooltipShown;
+ // New node is selected when clicking on the events bubble, wait for inspector-updated.
+ yield onInspectorUpdated;
+
+ ok(tooltip.isVisible(), "EventTooltip visible.");
+
+ onInspectorUpdated = inspector.once("inspector-updated");
+ let onTooltipHidden = tooltip.once("hidden");
+
+ info("Click on another tag to hide the event tooltip");
+ let h1 = yield getContainerForSelector("h1", inspector);
+ let tag = h1.elt.querySelector(".tag");
+ EventUtils.synthesizeMouseAtCenter(tag, {}, inspector.markup.doc.defaultView);
+
+ yield onTooltipHidden;
+ // New node is selected, wait for inspector-updated.
+ yield onInspectorUpdated;
+
+ ok(!tooltip.isVisible(), "EventTooltip hidden.");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_events1.js b/devtools/client/inspector/markup/test/browser_markup_events1.js
new file mode 100644
index 000000000..dbfd4a5c3
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events1.js
@@ -0,0 +1,149 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for DOM
+// events.
+
+const TEST_URL = URL_ROOT + "doc_markup_events1.html";
+
+loadHelperScript("helper_events_test_runner.js");
+
+const TEST_DATA = [ // eslint-disable-line
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "init();"
+ }
+ ]
+ },
+ {
+ selector: "#container",
+ expected: [
+ {
+ type: "mouseover",
+ filename: TEST_URL + ":45",
+ attributes: [
+ "Capturing",
+ "DOM2"
+ ],
+ handler: "function mouseoverHandler(event) {\n" +
+ " if (event.target.id !== \"container\") {\n" +
+ " let output = document.getElementById(\"output\");\n" +
+ " output.textContent = event.target.textContent;\n" +
+ " }\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#multiple",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":52",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function clickHandler(event) {\n" +
+ " let output = document.getElementById(\"output\");\n" +
+ " output.textContent = \"click\";\n" +
+ "}"
+ },
+ {
+ type: "mouseup",
+ filename: TEST_URL + ":57",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function mouseupHandler(event) {\n" +
+ " let output = document.getElementById(\"output\");\n" +
+ " output.textContent = \"mouseup\";\n" +
+ "}"
+ }
+ ]
+ },
+ // #noevents tests check that dynamically added events are properly displayed
+ // in the markupview
+ {
+ selector: "#noevents",
+ expected: []
+ },
+ {
+ selector: "#noevents",
+ beforeTest: function* (inspector, testActor) {
+ let nodeMutated = inspector.once("markupmutation");
+ yield testActor.eval("window.wrappedJSObject.addNoeventsClickHandler();");
+ yield nodeMutated;
+ },
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":72",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function noeventsClickHandler(event) {\n" +
+ " alert(\"noevents has an event listener\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#noevents",
+ beforeTest: function* (inspector, testActor) {
+ let nodeMutated = inspector.once("markupmutation");
+ yield testActor.eval(
+ "window.wrappedJSObject.removeNoeventsClickHandler();");
+ yield nodeMutated;
+ },
+ expected: []
+ },
+ {
+ selector: "#DOM0",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "alert('DOM0')"
+ }
+ ]
+ },
+ {
+ selector: "#handleevent",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":67",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "handleEvent: function(blah) {\n" +
+ " alert(\"handleEvent\");\n" +
+ "}"
+ }
+ ]
+ }
+];
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events2.js b/devtools/client/inspector/markup/test/browser_markup_events2.js
new file mode 100644
index 000000000..3e741cf1f
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events2.js
@@ -0,0 +1,163 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for DOM
+// events.
+
+const TEST_URL = URL_ROOT + "doc_markup_events2.html";
+
+loadHelperScript("helper_events_test_runner.js");
+
+const TEST_DATA = [ // eslint-disable-line
+ {
+ selector: "#fatarrow",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":39",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " alert(\"Fat arrow without params!\");\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":43",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "event => {\n" +
+ " alert(\"Fat arrow with 1 param!\");\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":47",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "(event, foo, bar) => {\n" +
+ " alert(\"Fat arrow with 3 params!\");\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":51",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "b => b"
+ }
+ ]
+ },
+ {
+ selector: "#bound",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":62",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function boundClickHandler(event) {\n" +
+ " alert(\"Bound event\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#boundhe",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":85",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "handleEvent: function() {\n" +
+ " alert(\"boundHandleEvent\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#comment-inline",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":91",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function functionProceededByInlineComment() {\n" +
+ " alert(\"comment-inline\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#comment-streaming",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":96",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function functionProceededByStreamingComment() {\n" +
+ " alert(\"comment-streaming\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#anon-object-method",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":71",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "anonObjectMethod: function() {\n" +
+ " alert(\"obj.anonObjectMethod\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#object-method",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":75",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "objectMethod: function kay() {\n" +
+ " alert(\"obj.objectMethod\");\n" +
+ "}"
+ }
+ ]
+ }
+];
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events3.js b/devtools/client/inspector/markup/test/browser_markup_events3.js
new file mode 100644
index 000000000..a9dc2a499
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events3.js
@@ -0,0 +1,161 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for DOM
+// events.
+
+const TEST_URL = URL_ROOT + "doc_markup_events3.html";
+
+loadHelperScript("helper_events_test_runner.js");
+
+const TEST_DATA = [ // eslint-disable-line
+ {
+ selector: "#es6-method",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":91",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "es6Method() {\n" +
+ " alert(\"obj.es6Method\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#generator",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":96",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function* generator() {\n" +
+ " alert(\"generator\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#anon-generator",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":55",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function*() {\n" +
+ " alert(\"anonGenerator\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#named-function-expression",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":23",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "let namedFunctionExpression =\n" +
+ " function foo() {\n" +
+ " alert(\"namedFunctionExpression\");\n" +
+ " }"
+ }
+ ]
+ },
+ {
+ selector: "#anon-function-expression",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "let anonFunctionExpression = function() {\n" +
+ " alert(\"anonFunctionExpression\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#returned-function",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":32",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function bar() {\n" +
+ " alert(\"returnedFunction\");\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#constructed-function",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":1",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: ""
+ }
+ ]
+ },
+ {
+ selector: "#constructed-function-with-body-string",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":1",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "alert(\"constructedFuncWithBodyString\");"
+ }
+ ]
+ },
+ {
+ selector: "#multiple-assignment",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":42",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "let multipleAssignment = foo = bar = function multi() {\n" +
+ " alert(\"multipleAssignment\");\n" +
+ "}"
+ }
+ ]
+ },
+];
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_form.js b/devtools/client/inspector/markup/test/browser_markup_events_form.js
new file mode 100644
index 000000000..ab029720c
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_form.js
@@ -0,0 +1,61 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Testing the feature whereby custom registered actors can listen to
+// 'form' events sent by the NodeActor to hook custom data to it.
+// The test registers one backend actor providing custom form data
+// and checks that the value is properly sent to the client (NodeFront).
+
+const TEST_PAGE_URL = URL_ROOT + "doc_markup_events_form.html";
+const TEST_ACTOR_URL = CHROME_URL_ROOT + "actor_events_form.js";
+
+var {EventsFormFront} = require(TEST_ACTOR_URL);
+
+add_task(function* () {
+ info("Opening the Toolbox");
+ let tab = yield addTab(TEST_PAGE_URL);
+ let toolbox = yield openToolboxForTab(tab, "webconsole");
+
+ info("Registering test actor");
+ let {registrar, front} = yield registerTestActor(toolbox);
+
+ info("Selecting the Inspector panel");
+ let inspector = yield toolbox.selectTool("inspector");
+ let container = yield getContainerForSelector("#container", inspector);
+ isnot(container, null, "There must be requested container");
+
+ let nodeFront = container.node;
+ let value = nodeFront.getFormProperty("test-property");
+ is(value, "test-value", "There must be custom property");
+
+ info("Unregistering actor");
+ yield unregisterActor(registrar, front);
+});
+
+function registerTestActor(toolbox) {
+ let deferred = defer();
+
+ let options = {
+ prefix: "eventsFormActor",
+ actorClass: "EventsFormActor",
+ moduleUrl: TEST_ACTOR_URL,
+ };
+
+ // Register as a tab actor
+ let client = toolbox.target.client;
+ registerTabActor(client, options).then(({registrar, form}) => {
+ // Attach to the registered actor
+ let front = EventsFormFront(client, form);
+ front.attach().then(() => {
+ deferred.resolve({
+ front: front,
+ registrar: registrar,
+ });
+ });
+ });
+
+ return deferred.promise;
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js
new file mode 100644
index 000000000..7413ea660
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.0.js
@@ -0,0 +1,237 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.0).
+
+const TEST_LIB = "lib_jquery_1.0.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB,
+ attributes: [
+ "jQuery"
+ ],
+ handler: "ready: function() {\n" +
+ " // Make sure that the DOM is not already loaded\n" +
+ " if (!jQuery.isReady) {\n" +
+ " // Remember that the DOM is ready\n" +
+ " jQuery.isReady = true;\n" +
+ "\n" +
+ " // If there are functions bound, to execute\n" +
+ " if (jQuery.readyList) {\n" +
+ " // Execute all of them\n" +
+ " for (var i = 0; i < jQuery.readyList.length; i++)\n" +
+ " jQuery.readyList[i].apply(document);\n" +
+ "\n" +
+ " // Reset the list of functions\n" +
+ " jQuery.readyList = null;\n" +
+ " }\n" +
+ " }\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: TEST_URL,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return;\n" +
+ "\n" +
+ " event = event || jQuery.event.fix(window.event);\n" +
+ "\n" +
+ " // If no correct event was found, fail\n" +
+ " if (!event) return;\n" +
+ "\n" +
+ " var returnValue = true;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " if (c[j].apply(this, [event]) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":894",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return;\n" +
+ "\n" +
+ " event = event || jQuery.event.fix(window.event);\n" +
+ "\n" +
+ " // If no correct event was found, fail\n" +
+ " if (!event) return;\n" +
+ "\n" +
+ " var returnValue = true;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " if (c[j].apply(this, [event]) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":894",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return;\n" +
+ "\n" +
+ " event = event || jQuery.event.fix(window.event);\n" +
+ "\n" +
+ " // If no correct event was found, fail\n" +
+ " if (!event) return;\n" +
+ "\n" +
+ " var returnValue = true;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " if (c[j].apply(this, [event]) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js
new file mode 100644
index 000000000..e5e995a87
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.1.js
@@ -0,0 +1,271 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.1).
+
+const TEST_LIB = "lib_jquery_1.1.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB,
+ attributes: [
+ "jQuery"
+ ],
+ handler: "ready: function() {\n" +
+ " // Make sure that the DOM is not already loaded\n" +
+ " if (!jQuery.isReady) {\n" +
+ " // Remember that the DOM is ready\n" +
+ " jQuery.isReady = true;\n" +
+ "\n" +
+ " // If there are functions bound, to execute\n" +
+ " if (jQuery.readyList) {\n" +
+ " // Execute all of them\n" +
+ " jQuery.each(jQuery.readyList, function() {\n" +
+ " this.apply(document);\n" +
+ " });\n" +
+ "\n" +
+ " // Reset the list of functions\n" +
+ " jQuery.readyList = null;\n" +
+ " }\n" +
+ " // Remove event lisenter to avoid memory leak\n" +
+ " if (jQuery.browser.mozilla || jQuery.browser.opera)\n" +
+ " document.removeEventListener(\"DOMContentLoaded\", jQuery.ready, false);\n" +
+ " }\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: TEST_URL,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return false;\n" +
+ "\n" +
+ " // Empty object is for triggered events with no data\n" +
+ " event = jQuery.event.fix(event || window.event || {});\n" +
+ "\n" +
+ " // returned undefined or false\n" +
+ " var returnValue;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " var args = [].slice.call(arguments, 1);\n" +
+ " args.unshift(event);\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " // Pass in a reference to the handler function itself\n" +
+ " // So that we can later remove it\n" +
+ " args[0].handler = c[j];\n" +
+ " args[0].data = c[j].data;\n" +
+ "\n" +
+ " if (c[j].apply(this, args) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " // Clean up added properties in IE to prevent memory leak\n" +
+ " if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":1224",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return false;\n" +
+ "\n" +
+ " // Empty object is for triggered events with no data\n" +
+ " event = jQuery.event.fix(event || window.event || {});\n" +
+ "\n" +
+ " // returned undefined or false\n" +
+ " var returnValue;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " var args = [].slice.call(arguments, 1);\n" +
+ " args.unshift(event);\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " // Pass in a reference to the handler function itself\n" +
+ " // So that we can later remove it\n" +
+ " args[0].handler = c[j];\n" +
+ " args[0].data = c[j].data;\n" +
+ "\n" +
+ " if (c[j].apply(this, args) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " // Clean up added properties in IE to prevent memory leak\n" +
+ " if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":1224",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return false;\n" +
+ "\n" +
+ " // Empty object is for triggered events with no data\n" +
+ " event = jQuery.event.fix(event || window.event || {});\n" +
+ "\n" +
+ " // returned undefined or false\n" +
+ " var returnValue;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " var args = [].slice.call(arguments, 1);\n" +
+ " args.unshift(event);\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " // Pass in a reference to the handler function itself\n" +
+ " // So that we can later remove it\n" +
+ " args[0].handler = c[j];\n" +
+ " args[0].data = c[j].data;\n" +
+ "\n" +
+ " if (c[j].apply(this, args) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " // Clean up added properties in IE to prevent memory leak\n" +
+ " if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ }
+ ]
+ }
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.11.1.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.11.1.js
new file mode 100644
index 000000000..17d59a317
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.11.1.js
@@ -0,0 +1,196 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.11.1).
+
+const TEST_LIB = "lib_jquery_1.11.1_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ }
+ ]
+ },
+
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":3",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "k = r.handle = function(a) {\n" +
+ " return typeof m === K || a && m.event.triggered === a.type ? void 0 : m.event.dispatch.apply(k.elem, arguments)\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":3",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "k = r.handle = function(a) {\n" +
+ " return typeof m === K || a && m.event.triggered === a.type ? void 0 : m.event.dispatch.apply(k.elem, arguments)\n" +
+ "}"
+ }
+ ]
+ },
+
+ {
+ selector: "#livediv",
+ expected: [
+ {
+ type: "dragend",
+ filename: TEST_URL + ":31",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ "}"
+ },
+ {
+ type: "dragleave",
+ filename: TEST_URL + ":30",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ "}"
+ },
+ {
+ type: "dragover",
+ filename: TEST_URL + ":33",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ "}"
+ },
+ {
+ type: "drop",
+ filename: TEST_URL + ":32",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.2.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.2.js
new file mode 100644
index 000000000..c26a14d66
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.2.js
@@ -0,0 +1,191 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.2).
+
+const TEST_LIB = "lib_jquery_1.2_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB,
+ attributes: [
+ "Bubbling",
+ "DOM0"
+ ],
+ handler: "handle: function(event) {\n" +
+ " if (typeof jQuery == \"undefined\") return false;\n" +
+ "\n" +
+ " // Empty object is for triggered events with no data\n" +
+ " event = jQuery.event.fix(event || window.event || {});\n" +
+ "\n" +
+ " // returned undefined or false\n" +
+ " var returnValue;\n" +
+ "\n" +
+ " var c = this.events[event.type];\n" +
+ "\n" +
+ " var args = [].slice.call(arguments, 1);\n" +
+ " args.unshift(event);\n" +
+ "\n" +
+ " for (var j in c) {\n" +
+ " // Pass in a reference to the handler function itself\n" +
+ " // So that we can later remove it\n" +
+ " args[0].handler = c[j];\n" +
+ " args[0].data = c[j].data;\n" +
+ "\n" +
+ " if (c[j].apply(this, args) === false) {\n" +
+ " event.preventDefault();\n" +
+ " event.stopPropagation();\n" +
+ " returnValue = false;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " // Clean up added properties in IE to prevent memory leak\n" +
+ " if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;\n" +
+ "\n" +
+ " return returnValue;\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":24",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function() {\n" +
+ " var val;\n" +
+ " if (typeof jQuery == \"undefined\" || jQuery.event.triggered) return val;\n" +
+ " val = jQuery.event.handle.apply(element, arguments);\n" +
+ " return val;\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":24",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function() {\n" +
+ " var val;\n" +
+ " if (typeof jQuery == \"undefined\" || jQuery.event.triggered) return val;\n" +
+ " val = jQuery.event.handle.apply(element, arguments);\n" +
+ " return val;\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.3.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.3.js
new file mode 100644
index 000000000..e0bdab2fd
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.3.js
@@ -0,0 +1,224 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.3).
+
+const TEST_LIB = "lib_jquery_1.3_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB + ":19",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "ready: function() {\n" +
+ " if (!n.isReady) {\n" +
+ " n.isReady = true;\n" +
+ " if (n.readyList) {\n" +
+ " n.each(n.readyList, function() {\n" +
+ " this.call(document, n)\n" +
+ " });\n" +
+ " n.readyList = null\n" +
+ " }\n" +
+ " n(document).triggerHandler(\"ready\")\n" +
+ " }\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB + ":19",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function() {\n" +
+ " return typeof n !== \"undefined\" && !n.event.triggered ? n.event.handle.apply(arguments.callee.elem, arguments) : g\n" +
+ "}"
+ },
+ {
+ type: "unload",
+ filename: URL_ROOT + TEST_LIB + ":19",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "function(H) {\n" +
+ " n(this).unbind(H, D);\n" +
+ " return (E || G).apply(this, arguments)\n" +
+ "}"
+ },
+ {
+ type: "unload",
+ filename: URL_ROOT + TEST_LIB + ":19",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function() {\n" +
+ " return typeof n !== \"undefined\" && !n.event.triggered ? n.event.handle.apply(arguments.callee.elem, arguments) : g\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":19",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function() {\n" +
+ " return typeof n !== \"undefined\" && !n.event.triggered ? n.event.handle.apply(arguments.callee.elem, arguments) : g\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":19",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "function() {\n" +
+ " return typeof n !== \"undefined\" && !n.event.triggered ? n.event.handle.apply(arguments.callee.elem, arguments) : g\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#livediv",
+ expected: [
+ {
+ type: "dblclick",
+ filename: TEST_URL + ":28",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ "}"
+ },
+ {
+ type: "dragstart",
+ filename: TEST_URL + ":29",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.4.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.4.js
new file mode 100644
index 000000000..9f7d9e241
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.4.js
@@ -0,0 +1,287 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.4).
+
+const TEST_LIB = "lib_jquery_1.4_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB + ":26",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "ready: function() {\n" +
+ " if (!c.isReady) {\n" +
+ " if (!s.body) return setTimeout(c.ready, 13);\n" +
+ " c.isReady = true;\n" +
+ " if (Q) {\n" +
+ " for (var a, b = 0; a = Q[b++];) a.call(s, c);\n" +
+ " Q = null\n" +
+ " }\n" +
+ " c.fn.triggerHandler && c(s).triggerHandler(\"ready\")\n" +
+ " }\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":48",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "j = function() {\n" +
+ " return typeof c !== \"undefined\" && !c.event.triggered ? c.event.handle.apply(j.elem, arguments) : w\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":48",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "j = function() {\n" +
+ " return typeof c !== \"undefined\" && !c.event.triggered ? c.event.handle.apply(j.elem, arguments) : w\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#livediv",
+ expected: [
+ {
+ type: "dblclick",
+ filename: TEST_URL + ":28",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ "}"
+ },
+ {
+ type: "dblclick",
+ filename: URL_ROOT + TEST_LIB + ":17",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "function qa(a) {\n" +
+ " var b = true,\n" +
+ " d = [],\n" +
+ " f = [],\n" +
+ " e = arguments,\n" +
+ " i, j, o, p, n, t = c.extend({}, c.data(this, \"events\").live);\n" +
+ " for (p in t) {\n" +
+ " j = t[p];\n" +
+ " if (j.live === a.type || j.altLive && c.inArray(a.type, j.altLive) > -1) {\n" +
+ " i = j.data;\n" +
+ " i.beforeFilter && i.beforeFilter[a.type] && !i.beforeFilter[a.type](a) || f.push(j.selector)\n" +
+ " } else delete t[p]\n" +
+ " }\n" +
+ " i = c(a.target).closest(f, a.currentTarget);\n" +
+ " n = 0;\n" +
+ " for (l = i.length; n < l; n++)\n" +
+ " for (p in t) {\n" +
+ " j = t[p];\n" +
+ " o = i[n].elem;\n" +
+ " f = null;\n" +
+ " if (i[n].selector === j.selector) {\n" +
+ " if (j.live === \"mouseenter\" || j.live === \"mouseleave\") f = c(a.relatedTarget).closest(j.selector)[0];\n" +
+ " if (!f || f !== o) d.push({\n" +
+ " elem: o,\n" +
+ " fn: j\n" +
+ " })\n" +
+ " }\n" +
+ " }\n" +
+ " n = 0;\n" +
+ " for (l = d.length; n < l; n++) {\n" +
+ " i = d[n];\n" +
+ " a.currentTarget = i.elem;\n" +
+ " a.data = i.fn.data;\n" +
+ " if (i.fn.apply(i.elem, e) === false) {\n" +
+ " b = false;\n" +
+ " break\n" +
+ " }\n" +
+ " }\n" +
+ " return b\n" +
+ "}"
+ },
+ {
+ type: "dragstart",
+ filename: TEST_URL + ":29",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ "}"
+ },
+ {
+ type: "dragstart",
+ filename: URL_ROOT + TEST_LIB + ":17",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "function qa(a) {\n" +
+ " var b = true,\n" +
+ " d = [],\n" +
+ " f = [],\n" +
+ " e = arguments,\n" +
+ " i, j, o, p, n, t = c.extend({}, c.data(this, \"events\").live);\n" +
+ " for (p in t) {\n" +
+ " j = t[p];\n" +
+ " if (j.live === a.type || j.altLive && c.inArray(a.type, j.altLive) > -1) {\n" +
+ " i = j.data;\n" +
+ " i.beforeFilter && i.beforeFilter[a.type] && !i.beforeFilter[a.type](a) || f.push(j.selector)\n" +
+ " } else delete t[p]\n" +
+ " }\n" +
+ " i = c(a.target).closest(f, a.currentTarget);\n" +
+ " n = 0;\n" +
+ " for (l = i.length; n < l; n++)\n" +
+ " for (p in t) {\n" +
+ " j = t[p];\n" +
+ " o = i[n].elem;\n" +
+ " f = null;\n" +
+ " if (i[n].selector === j.selector) {\n" +
+ " if (j.live === \"mouseenter\" || j.live === \"mouseleave\") f = c(a.relatedTarget).closest(j.selector)[0];\n" +
+ " if (!f || f !== o) d.push({\n" +
+ " elem: o,\n" +
+ " fn: j\n" +
+ " })\n" +
+ " }\n" +
+ " }\n" +
+ " n = 0;\n" +
+ " for (l = d.length; n < l; n++) {\n" +
+ " i = d[n];\n" +
+ " a.currentTarget = i.elem;\n" +
+ " a.data = i.fn.data;\n" +
+ " if (i.fn.apply(i.elem, e) === false) {\n" +
+ " b = false;\n" +
+ " break\n" +
+ " }\n" +
+ " }\n" +
+ " return b\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.6.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.6.js
new file mode 100644
index 000000000..f89bd0740
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.6.js
@@ -0,0 +1,388 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.6).
+
+const TEST_LIB = "lib_jquery_1.6_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "ready: function(a) {\n" +
+ " if (a === !0 && !--e.readyWait || a !== !0 && !e.isReady) {\n" +
+ " if (!c.body) return setTimeout(e.ready, 1);\n" +
+ " e.isReady = !0;\n" +
+ " if (a !== !0 && --e.readyWait > 0) return;\n" +
+ " y.resolveWith(c, [e]), e.fn.trigger && e(c).trigger(\"ready\").unbind(\"ready\")\n" +
+ " }\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "i.handle = k = function(a) {\n" +
+ " return typeof f != \"undefined\" && (!a || f.event.triggered !== a.type) ? f.event.handle.apply(k.elem, arguments) : b\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "i.handle = k = function(a) {\n" +
+ " return typeof f != \"undefined\" && (!a || f.event.triggered !== a.type) ? f.event.handle.apply(k.elem, arguments) : b\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#livediv",
+ expected: [
+ {
+ type: "dblclick",
+ filename: TEST_URL + ":28",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ "}"
+ },
+ {
+ type: "dblclick",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "function M(a) {\n" +
+ " var b, c, d, e, g, h, i, j, k, l, m, n, o, p = [],\n" +
+ " q = [],\n" +
+ " r = f._data(this, \"events\");\n" +
+ " if (!(a.liveFired === this || !r || !r.live || a.target.disabled || a.button && a.type === \"click\")) {\n" +
+ " a.namespace && (n = new RegExp(\"(^|\\\\.)\" + a.namespace.split(\".\").join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\")), a.liveFired = this;\n" +
+ " var s = r.live.slice(0);\n" +
+ " for (i = 0; i < s.length; i++) g = s[i], g.origType.replace(x, \"\") === a.type ? q.push(g.selector) : s.splice(i--, 1);\n" +
+ " e = f(a.target).closest(q, a.currentTarget);\n" +
+ " for (j = 0, k = e.length; j < k; j++) {\n" +
+ " m = e[j];\n" +
+ " for (i = 0; i < s.length; i++) {\n" +
+ " g = s[i];\n" +
+ " if (m.selector === g.selector && (!n || n.test(g.namespace)) && !m.elem.disabled) {\n" +
+ " h = m.elem, d = null;\n" +
+ " if (g.preType === \"mouseenter\" || g.preType === \"mouseleave\") a.type = g.preType, d = f(a.relatedTarget).closest(g.selector)[0], d && f.contains(h, d) && (d = h);\n" +
+ " (!d || d !== h) && p.push({\n" +
+ " elem: h,\n" +
+ " handleObj: g,\n" +
+ " level: m.level\n" +
+ " })\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " for (j = 0, k = p.length; j < k; j++) {\n" +
+ " e = p[j];\n" +
+ " if (c && e.level > c) break;\n" +
+ " a.currentTarget = e.elem, a.data = e.handleObj.data, a.handleObj = e.handleObj, o = e.handleObj.origHandler.apply(e.elem, arguments);\n" +
+ " if (o === !1 || a.isPropagationStopped()) {\n" +
+ " c = e.level, o === !1 && (b = !1);\n" +
+ " if (a.isImmediatePropagationStopped()) break\n" +
+ " }\n" +
+ " }\n" +
+ " return b\n" +
+ " }\n" +
+ "}"
+ },
+ {
+ type: "dragend",
+ filename: TEST_URL + ":31",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ "}"
+ },
+ {
+ type: "dragend",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "function M(a) {\n" +
+ " var b, c, d, e, g, h, i, j, k, l, m, n, o, p = [],\n" +
+ " q = [],\n" +
+ " r = f._data(this, \"events\");\n" +
+ " if (!(a.liveFired === this || !r || !r.live || a.target.disabled || a.button && a.type === \"click\")) {\n" +
+ " a.namespace && (n = new RegExp(\"(^|\\\\.)\" + a.namespace.split(\".\").join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\")), a.liveFired = this;\n" +
+ " var s = r.live.slice(0);\n" +
+ " for (i = 0; i < s.length; i++) g = s[i], g.origType.replace(x, \"\") === a.type ? q.push(g.selector) : s.splice(i--, 1);\n" +
+ " e = f(a.target).closest(q, a.currentTarget);\n" +
+ " for (j = 0, k = e.length; j < k; j++) {\n" +
+ " m = e[j];\n" +
+ " for (i = 0; i < s.length; i++) {\n" +
+ " g = s[i];\n" +
+ " if (m.selector === g.selector && (!n || n.test(g.namespace)) && !m.elem.disabled) {\n" +
+ " h = m.elem, d = null;\n" +
+ " if (g.preType === \"mouseenter\" || g.preType === \"mouseleave\") a.type = g.preType, d = f(a.relatedTarget).closest(g.selector)[0], d && f.contains(h, d) && (d = h);\n" +
+ " (!d || d !== h) && p.push({\n" +
+ " elem: h,\n" +
+ " handleObj: g,\n" +
+ " level: m.level\n" +
+ " })\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " for (j = 0, k = p.length; j < k; j++) {\n" +
+ " e = p[j];\n" +
+ " if (c && e.level > c) break;\n" +
+ " a.currentTarget = e.elem, a.data = e.handleObj.data, a.handleObj = e.handleObj, o = e.handleObj.origHandler.apply(e.elem, arguments);\n" +
+ " if (o === !1 || a.isPropagationStopped()) {\n" +
+ " c = e.level, o === !1 && (b = !1);\n" +
+ " if (a.isImmediatePropagationStopped()) break\n" +
+ " }\n" +
+ " }\n" +
+ " return b\n" +
+ " }\n" +
+ "}"
+ },
+ {
+ type: "dragleave",
+ filename: TEST_URL + ":30",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ "}"
+ },
+ {
+ type: "dragleave",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "function M(a) {\n" +
+ " var b, c, d, e, g, h, i, j, k, l, m, n, o, p = [],\n" +
+ " q = [],\n" +
+ " r = f._data(this, \"events\");\n" +
+ " if (!(a.liveFired === this || !r || !r.live || a.target.disabled || a.button && a.type === \"click\")) {\n" +
+ " a.namespace && (n = new RegExp(\"(^|\\\\.)\" + a.namespace.split(\".\").join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\")), a.liveFired = this;\n" +
+ " var s = r.live.slice(0);\n" +
+ " for (i = 0; i < s.length; i++) g = s[i], g.origType.replace(x, \"\") === a.type ? q.push(g.selector) : s.splice(i--, 1);\n" +
+ " e = f(a.target).closest(q, a.currentTarget);\n" +
+ " for (j = 0, k = e.length; j < k; j++) {\n" +
+ " m = e[j];\n" +
+ " for (i = 0; i < s.length; i++) {\n" +
+ " g = s[i];\n" +
+ " if (m.selector === g.selector && (!n || n.test(g.namespace)) && !m.elem.disabled) {\n" +
+ " h = m.elem, d = null;\n" +
+ " if (g.preType === \"mouseenter\" || g.preType === \"mouseleave\") a.type = g.preType, d = f(a.relatedTarget).closest(g.selector)[0], d && f.contains(h, d) && (d = h);\n" +
+ " (!d || d !== h) && p.push({\n" +
+ " elem: h,\n" +
+ " handleObj: g,\n" +
+ " level: m.level\n" +
+ " })\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " for (j = 0, k = p.length; j < k; j++) {\n" +
+ " e = p[j];\n" +
+ " if (c && e.level > c) break;\n" +
+ " a.currentTarget = e.elem, a.data = e.handleObj.data, a.handleObj = e.handleObj, o = e.handleObj.origHandler.apply(e.elem, arguments);\n" +
+ " if (o === !1 || a.isPropagationStopped()) {\n" +
+ " c = e.level, o === !1 && (b = !1);\n" +
+ " if (a.isImmediatePropagationStopped()) break\n" +
+ " }\n" +
+ " }\n" +
+ " return b\n" +
+ " }\n" +
+ "}"
+ },
+ {
+ type: "dragstart",
+ filename: TEST_URL + ":29",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ "}"
+ },
+ {
+ type: "dragstart",
+ filename: URL_ROOT + TEST_LIB + ":16",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "function M(a) {\n" +
+ " var b, c, d, e, g, h, i, j, k, l, m, n, o, p = [],\n" +
+ " q = [],\n" +
+ " r = f._data(this, \"events\");\n" +
+ " if (!(a.liveFired === this || !r || !r.live || a.target.disabled || a.button && a.type === \"click\")) {\n" +
+ " a.namespace && (n = new RegExp(\"(^|\\\\.)\" + a.namespace.split(\".\").join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\")), a.liveFired = this;\n" +
+ " var s = r.live.slice(0);\n" +
+ " for (i = 0; i < s.length; i++) g = s[i], g.origType.replace(x, \"\") === a.type ? q.push(g.selector) : s.splice(i--, 1);\n" +
+ " e = f(a.target).closest(q, a.currentTarget);\n" +
+ " for (j = 0, k = e.length; j < k; j++) {\n" +
+ " m = e[j];\n" +
+ " for (i = 0; i < s.length; i++) {\n" +
+ " g = s[i];\n" +
+ " if (m.selector === g.selector && (!n || n.test(g.namespace)) && !m.elem.disabled) {\n" +
+ " h = m.elem, d = null;\n" +
+ " if (g.preType === \"mouseenter\" || g.preType === \"mouseleave\") a.type = g.preType, d = f(a.relatedTarget).closest(g.selector)[0], d && f.contains(h, d) && (d = h);\n" +
+ " (!d || d !== h) && p.push({\n" +
+ " elem: h,\n" +
+ " handleObj: g,\n" +
+ " level: m.level\n" +
+ " })\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " for (j = 0, k = p.length; j < k; j++) {\n" +
+ " e = p[j];\n" +
+ " if (c && e.level > c) break;\n" +
+ " a.currentTarget = e.elem, a.data = e.handleObj.data, a.handleObj = e.handleObj, o = e.handleObj.origHandler.apply(e.elem, arguments);\n" +
+ " if (o === !1 || a.isPropagationStopped()) {\n" +
+ " c = e.level, o === !1 && (b = !1);\n" +
+ " if (a.isImmediatePropagationStopped()) break\n" +
+ " }\n" +
+ " }\n" +
+ " return b\n" +
+ " }\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.7.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.7.js
new file mode 100644
index 000000000..39f1d54e2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_1.7.js
@@ -0,0 +1,234 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 1.7).
+
+const TEST_LIB = "lib_jquery_1.7_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ },
+ {
+ type: "load",
+ filename: URL_ROOT + TEST_LIB + ":2",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "ready: function(a) {\n" +
+ " if (a === !0 && !--e.readyWait || a !== !0 && !e.isReady) {\n" +
+ " if (!c.body) return setTimeout(e.ready, 1);\n" +
+ " e.isReady = !0;\n" +
+ " if (a !== !0 && --e.readyWait > 0) return;\n" +
+ " B.fireWith(c, [e]), e.fn.trigger && e(c).trigger(\"ready\").unbind(\"ready\")\n" +
+ " }\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":3",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "h.handle = i = function(a) {\n" +
+ " return typeof f != \"undefined\" && (!a || f.event.triggered !== a.type) ? f.event.dispatch.apply(i.elem, arguments) : b\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":3",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "h.handle = i = function(a) {\n" +
+ " return typeof f != \"undefined\" && (!a || f.event.triggered !== a.type) ? f.event.dispatch.apply(i.elem, arguments) : b\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#livediv",
+ expected: [
+ {
+ type: "dblclick",
+ filename: TEST_URL + ":28",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ "}"
+ },
+ {
+ type: "dragend",
+ filename: TEST_URL + ":31",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ "}"
+ },
+ {
+ type: "dragleave",
+ filename: TEST_URL + ":30",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ "}"
+ },
+ {
+ type: "dragover",
+ filename: TEST_URL + ":33",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ "}"
+ },
+ {
+ type: "dragstart",
+ filename: TEST_URL + ":29",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ "}"
+ },
+ {
+ type: "drop",
+ filename: TEST_URL + ":32",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_events_jquery_2.1.1.js b/devtools/client/inspector/markup/test/browser_markup_events_jquery_2.1.1.js
new file mode 100644
index 000000000..c6a6642ea
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_jquery_2.1.1.js
@@ -0,0 +1,196 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test that markup view event bubbles show the correct event info for jQuery
+// and jQuery Live events (jQuery version 2.1.1).
+
+const TEST_LIB = "lib_jquery_2.1.1_min.js";
+const TEST_URL = URL_ROOT + "doc_markup_events_jquery.html?" + TEST_LIB;
+
+loadHelperScript("helper_events_test_runner.js");
+
+/*eslint-disable */
+const TEST_DATA = [
+ {
+ selector: "html",
+ expected: [
+ {
+ type: "load",
+ filename: TEST_URL + ":27",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "() => {\n" +
+ " var handler1 = function liveDivDblClick() {\n" +
+ " alert(1);\n" +
+ " };\n" +
+ " var handler2 = function liveDivDragStart() {\n" +
+ " alert(2);\n" +
+ " };\n" +
+ " var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ " };\n" +
+ " var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ " };\n" +
+ " var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ " };\n" +
+ " var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ " };\n" +
+ " var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ " };\n" +
+ " var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ " };\n" +
+ " var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ " };\n" +
+ " var handler10 = function divDragOut() {\n" +
+ " alert(10);\n" +
+ " };\n" +
+ "\n" +
+ " if ($(\"#livediv\").live) {\n" +
+ " $(\"#livediv\").live(\"dblclick\", handler1);\n" +
+ " $(\"#livediv\").live(\"dragstart\", handler2);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").delegate) {\n" +
+ " $(document).delegate(\"#livediv\", \"dragleave\", handler3);\n" +
+ " $(document).delegate(\"#livediv\", \"dragend\", handler4);\n" +
+ " }\n" +
+ "\n" +
+ " if ($(\"#livediv\").on) {\n" +
+ " $(document).on(\"drop\", \"#livediv\", handler5);\n" +
+ " $(document).on(\"dragover\", \"#livediv\", handler6);\n" +
+ " $(document).on(\"dragout\", \"#livediv:xxxxx\", handler10);\n" +
+ " }\n" +
+ "\n" +
+ " var div = $(\"div\")[0];\n" +
+ " $(div).click(handler7);\n" +
+ " $(div).click(handler8);\n" +
+ " $(div).keydown(handler9);\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#testdiv",
+ expected: [
+ {
+ type: "click",
+ filename: TEST_URL + ":34",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler7 = function divClick1() {\n" +
+ " alert(7);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: TEST_URL + ":35",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler8 = function divClick2() {\n" +
+ " alert(8);\n" +
+ "}"
+ },
+ {
+ type: "click",
+ filename: URL_ROOT + TEST_LIB + ":3",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "g = r.handle = function(b) {\n" +
+ " return typeof n !== U && n.event.triggered !== b.type ? n.event.dispatch.apply(a, arguments) : void 0\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: TEST_URL + ":36",
+ attributes: [
+ "jQuery"
+ ],
+ handler: "var handler9 = function divKeyDown() {\n" +
+ " alert(9);\n" +
+ "}"
+ },
+ {
+ type: "keydown",
+ filename: URL_ROOT + TEST_LIB + ":3",
+ attributes: [
+ "Bubbling",
+ "DOM2"
+ ],
+ handler: "g = r.handle = function(b) {\n" +
+ " return typeof n !== U && n.event.triggered !== b.type ? n.event.dispatch.apply(a, arguments) : void 0\n" +
+ "}"
+ }
+ ]
+ },
+ {
+ selector: "#livediv",
+ expected: [
+ {
+ type: "dragend",
+ filename: TEST_URL + ":31",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler4 = function liveDivDragEnd() {\n" +
+ " alert(4);\n" +
+ "}"
+ },
+ {
+ type: "dragleave",
+ filename: TEST_URL + ":30",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler3 = function liveDivDragLeave() {\n" +
+ " alert(3);\n" +
+ "}"
+ },
+ {
+ type: "dragover",
+ filename: TEST_URL + ":33",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler6 = function liveDivDragOver() {\n" +
+ " alert(6);\n" +
+ "}"
+ },
+ {
+ type: "drop",
+ filename: TEST_URL + ":32",
+ attributes: [
+ "jQuery",
+ "Live"
+ ],
+ handler: "var handler5 = function liveDivDrop() {\n" +
+ " alert(5);\n" +
+ "}"
+ }
+ ]
+ },
+];
+/*eslint-enable */
+
+add_task(function* () {
+ yield runEventPopupTests(TEST_URL, TEST_DATA);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_html_edit_01.js b/devtools/client/inspector/markup/test/browser_markup_html_edit_01.js
new file mode 100644
index 000000000..e4c271498
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_html_edit_01.js
@@ -0,0 +1,84 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_outerhtml_test_runner.js */
+"use strict";
+
+// Test outerHTML edition via the markup-view
+
+requestLongerTimeout(2);
+
+loadHelperScript("helper_outerhtml_test_runner.js");
+
+const TEST_DATA = [{
+ selector: "#one",
+ oldHTML: '<div id="one">First <em>Div</em></div>',
+ newHTML: '<div id="one">First Div</div>',
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ let text = yield testActor.getProperty("#one", "textContent");
+ is(text, "First Div", "New div has expected text content");
+ let num = yield testActor.getNumberOfElementMatches("#one em");
+ is(num, 0, "No em remaining");
+ }
+}, {
+ selector: "#removedChildren",
+ oldHTML: "<div id=\"removedChildren\">removedChild " +
+ "<i>Italic <b>Bold <u>Underline</u></b></i> Normal</div>",
+ newHTML: "<div id=\"removedChildren\">removedChild</div>"
+}, {
+ selector: "#addedChildren",
+ oldHTML: '<div id="addedChildren">addedChildren</div>',
+ newHTML: "<div id=\"addedChildren\">addedChildren " +
+ "<i>Italic <b>Bold <u>Underline</u></b></i> Normal</div>"
+}, {
+ selector: "#addedAttribute",
+ oldHTML: '<div id="addedAttribute">addedAttribute</div>',
+ newHTML: "<div id=\"addedAttribute\" class=\"important\" disabled checked>" +
+ "addedAttribute</div>",
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ is(pageNodeFront, selectedNodeFront, "Original element is selected");
+ let html = yield testActor.getProperty("#addedAttribute", "outerHTML");
+ is(html, "<div id=\"addedAttribute\" class=\"important\" disabled=\"\" " +
+ "checked=\"\">addedAttribute</div>", "Attributes have been added");
+ }
+}, {
+ selector: "#changedTag",
+ oldHTML: '<div id="changedTag">changedTag</div>',
+ newHTML: '<p id="changedTag" class="important">changedTag</p>'
+}, {
+ selector: "#siblings",
+ oldHTML: '<div id="siblings">siblings</div>',
+ newHTML: '<div id="siblings-before-sibling">before sibling</div>' +
+ '<div id="siblings">siblings (updated)</div>' +
+ '<div id="siblings-after-sibling">after sibling</div>',
+ validate: function* ({selectedNodeFront, inspector, testActor}) {
+ let beforeSiblingFront = yield getNodeFront("#siblings-before-sibling",
+ inspector);
+ is(beforeSiblingFront, selectedNodeFront, "Sibling has been selected");
+
+ let text = yield testActor.getProperty("#siblings", "textContent");
+ is(text, "siblings (updated)", "New div has expected text content");
+
+ let beforeText = yield testActor.getProperty("#siblings-before-sibling",
+ "textContent");
+ is(beforeText, "before sibling", "Sibling has been inserted");
+
+ let afterText = yield testActor.getProperty("#siblings-after-sibling",
+ "textContent");
+ is(afterText, "after sibling", "Sibling has been inserted");
+ }
+}];
+
+const TEST_URL = "data:text/html," +
+ "<!DOCTYPE html>" +
+ "<head><meta charset='utf-8' /></head>" +
+ "<body>" +
+ TEST_DATA.map(outer => outer.oldHTML).join("\n") +
+ "</body>" +
+ "</html>";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ inspector.markup._frame.focus();
+ yield runEditOuterHTMLTests(TEST_DATA, inspector, testActor);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_html_edit_02.js b/devtools/client/inspector/markup/test/browser_markup_html_edit_02.js
new file mode 100644
index 000000000..8f6d0fd14
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_html_edit_02.js
@@ -0,0 +1,119 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_outerhtml_test_runner.js */
+"use strict";
+
+// Test outerHTML edition via the markup-view
+
+loadHelperScript("helper_outerhtml_test_runner.js");
+requestLongerTimeout(2);
+
+const TEST_DATA = [
+ {
+ selector: "#badMarkup1",
+ oldHTML: "<div id=\"badMarkup1\">badMarkup1</div>",
+ newHTML: "<div id=\"badMarkup1\">badMarkup1</div> hanging</div>",
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ is(pageNodeFront, selectedNodeFront, "Original element is selected");
+
+ let textNodeName = yield testActor.eval(`
+ content.document.querySelector("#badMarkup1").nextSibling.nodeName
+ `);
+ let textNodeData = yield testActor.eval(`
+ content.document.querySelector("#badMarkup1").nextSibling.data
+ `);
+ is(textNodeName, "#text", "Sibling is a text element");
+ is(textNodeData, " hanging", "New text node has expected text content");
+ }
+ },
+ {
+ selector: "#badMarkup2",
+ oldHTML: "<div id=\"badMarkup2\">badMarkup2</div>",
+ newHTML: "<div id=\"badMarkup2\">badMarkup2</div> hanging<div></div>" +
+ "</div></div></body>",
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ is(pageNodeFront, selectedNodeFront, "Original element is selected");
+
+ let textNodeName = yield testActor.eval(`
+ content.document.querySelector("#badMarkup2").nextSibling.nodeName
+ `);
+ let textNodeData = yield testActor.eval(`
+ content.document.querySelector("#badMarkup2").nextSibling.data
+ `);
+ is(textNodeName, "#text", "Sibling is a text element");
+ is(textNodeData, " hanging", "New text node has expected text content");
+ }
+ },
+ {
+ selector: "#badMarkup3",
+ oldHTML: "<div id=\"badMarkup3\">badMarkup3</div>",
+ newHTML: "<div id=\"badMarkup3\">badMarkup3 <em>Emphasized <strong> " +
+ "and strong</div>",
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ is(pageNodeFront, selectedNodeFront, "Original element is selected");
+
+ let emText = yield testActor.getProperty("#badMarkup3 em", "textContent");
+ let strongText = yield testActor.getProperty("#badMarkup3 strong",
+ "textContent");
+ is(emText, "Emphasized and strong", "<em> was auto created");
+ is(strongText, " and strong", "<strong> was auto created");
+ }
+ },
+ {
+ selector: "#badMarkup4",
+ oldHTML: "<div id=\"badMarkup4\">badMarkup4</div>",
+ newHTML: "<div id=\"badMarkup4\">badMarkup4</p>",
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ is(pageNodeFront, selectedNodeFront, "Original element is selected");
+
+ let divText = yield testActor.getProperty("#badMarkup4", "textContent");
+ let divTag = yield testActor.getProperty("#badMarkup4", "tagName");
+
+ let pText = yield testActor.getProperty("#badMarkup4 p", "textContent");
+ let pTag = yield testActor.getProperty("#badMarkup4 p", "tagName");
+
+ is(divText, "badMarkup4", "textContent is correct");
+ is(divTag, "DIV", "did not change to <p> tag");
+ is(pText, "", "The <p> tag has no children");
+ is(pTag, "P", "Created an empty <p> tag");
+ }
+ },
+ {
+ selector: "#badMarkup5",
+ oldHTML: "<p id=\"badMarkup5\">badMarkup5</p>",
+ newHTML: "<p id=\"badMarkup5\">badMarkup5 <div>with a nested div</div></p>",
+ validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
+ is(pageNodeFront, selectedNodeFront, "Original element is selected");
+
+ let num = yield testActor.getNumberOfElementMatches("#badMarkup5 div");
+
+ let pText = yield testActor.getProperty("#badMarkup5", "textContent");
+ let pTag = yield testActor.getProperty("#badMarkup5", "tagName");
+
+ let divText = yield testActor.getProperty("#badMarkup5 ~ div",
+ "textContent");
+ let divTag = yield testActor.getProperty("#badMarkup5 ~ div", "tagName");
+
+ is(num, 0, "The invalid markup got created as a sibling");
+ is(pText, "badMarkup5 ", "The p tag does not take in the div content");
+ is(pTag, "P", "Did not change to a <div> tag");
+ is(divText, "with a nested div", "textContent is correct");
+ is(divTag, "DIV", "Did not change to <p> tag");
+ }
+ }
+];
+
+const TEST_URL = "data:text/html," +
+ "<!DOCTYPE html>" +
+ "<head><meta charset='utf-8' /></head>" +
+ "<body>" +
+ TEST_DATA.map(outer => outer.oldHTML).join("\n") +
+ "</body>" +
+ "</html>";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ inspector.markup._frame.focus();
+ yield runEditOuterHTMLTests(TEST_DATA, inspector, testActor);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_html_edit_03.js b/devtools/client/inspector/markup/test/browser_markup_html_edit_03.js
new file mode 100644
index 000000000..d72bd1f1d
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_html_edit_03.js
@@ -0,0 +1,200 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that outerHTML editing keybindings work as expected and that *special*
+// elements like <html>, <body> and <head> can be edited correctly.
+
+const TEST_URL = "data:text/html," +
+ "<!DOCTYPE html>" +
+ "<head><meta charset='utf-8' /></head>" +
+ "<body>" +
+ "<div id=\"keyboard\"></div>" +
+ "</body>" +
+ "</html>";
+const SELECTOR = "#keyboard";
+const OLD_HTML = '<div id="keyboard"></div>';
+const NEW_HTML = '<div id="keyboard">Edited</div>';
+
+requestLongerTimeout(2);
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ inspector.markup._frame.focus();
+
+ info("Check that pressing escape cancels edits");
+ yield testEscapeCancels(inspector, testActor);
+
+ info("Check that pressing F2 commits edits");
+ yield testF2Commits(inspector, testActor);
+
+ info("Check that editing the <body> element works like other nodes");
+ yield testBody(inspector, testActor);
+
+ info("Check that editing the <head> element works like other nodes");
+ yield testHead(inspector, testActor);
+
+ info("Check that editing the <html> element works like other nodes");
+ yield testDocumentElement(inspector, testActor);
+
+ info("Check (again) that editing the <html> element works like other nodes");
+ yield testDocumentElement2(inspector, testActor);
+});
+
+function* testEscapeCancels(inspector, testActor) {
+ yield selectNode(SELECTOR, inspector);
+
+ let onEditorShown = once(inspector.markup.htmlEditor, "popupshown");
+ EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
+ yield onEditorShown;
+ ok(inspector.markup.htmlEditor._visible, "HTML Editor is visible");
+
+ is((yield testActor.getProperty(SELECTOR, "outerHTML")), OLD_HTML,
+ "The node is starting with old HTML.");
+
+ inspector.markup.htmlEditor.editor.setText(NEW_HTML);
+
+ let onEditorHiddem = once(inspector.markup.htmlEditor, "popuphidden");
+ EventUtils.sendKey("ESCAPE", inspector.markup.htmlEditor.doc.defaultView);
+ yield onEditorHiddem;
+ ok(!inspector.markup.htmlEditor._visible, "HTML Editor is not visible");
+
+ is((yield testActor.getProperty(SELECTOR, "outerHTML")), OLD_HTML,
+ "Escape cancels edits");
+}
+
+function* testF2Commits(inspector, testActor) {
+ let onEditorShown = once(inspector.markup.htmlEditor, "popupshown");
+ inspector.markup._frame.contentDocument.documentElement.focus();
+ EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
+ yield onEditorShown;
+ ok(inspector.markup.htmlEditor._visible, "HTML Editor is visible");
+
+ is((yield testActor.getProperty(SELECTOR, "outerHTML")), OLD_HTML,
+ "The node is starting with old HTML.");
+
+ let onMutations = inspector.once("markupmutation");
+ inspector.markup.htmlEditor.editor.setText(NEW_HTML);
+ EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
+ yield onMutations;
+
+ ok(!inspector.markup.htmlEditor._visible, "HTML Editor is not visible");
+
+ is((yield testActor.getProperty(SELECTOR, "outerHTML")), NEW_HTML,
+ "F2 commits edits - the node has new HTML.");
+}
+
+function* testBody(inspector, testActor) {
+ let currentBodyHTML = yield testActor.getProperty("body", "outerHTML");
+ let bodyHTML = '<body id="updated"><p></p></body>';
+ let bodyFront = yield getNodeFront("body", inspector);
+
+ let onUpdated = inspector.once("inspector-updated");
+ let onReselected = inspector.markup.once("reselectedonremoved");
+ yield inspector.markup.updateNodeOuterHTML(bodyFront, bodyHTML,
+ currentBodyHTML);
+ yield onReselected;
+ yield onUpdated;
+
+ let newBodyHTML = yield testActor.getProperty("body", "outerHTML");
+ is(newBodyHTML, bodyHTML, "<body> HTML has been updated");
+
+ let headsNum = yield testActor.getNumberOfElementMatches("head");
+ is(headsNum, 1, "no extra <head>s have been added");
+}
+
+function* testHead(inspector, testActor) {
+ yield selectNode("head", inspector);
+
+ let currentHeadHTML = yield testActor.getProperty("head", "outerHTML");
+ let headHTML = "<head id=\"updated\"><title>New Title</title>" +
+ "<script>window.foo=\"bar\";</script></head>";
+ let headFront = yield getNodeFront("head", inspector);
+
+ let onUpdated = inspector.once("inspector-updated");
+ let onReselected = inspector.markup.once("reselectedonremoved");
+ yield inspector.markup.updateNodeOuterHTML(headFront, headHTML,
+ currentHeadHTML);
+ yield onReselected;
+ yield onUpdated;
+
+ is((yield testActor.eval("content.document.title")), "New Title",
+ "New title has been added");
+ is((yield testActor.eval("content.foo")), undefined,
+ "Script has not been executed");
+ is((yield testActor.getProperty("head", "outerHTML")), headHTML,
+ "<head> HTML has been updated");
+ is((yield testActor.getNumberOfElementMatches("body")), 1,
+ "no extra <body>s have been added");
+}
+
+function* testDocumentElement(inspector, testActor) {
+ let currentDocElementOuterHMTL = yield testActor.eval(
+ "content.document.documentElement.outerHMTL");
+ let docElementHTML = "<html id=\"updated\" foo=\"bar\"><head>" +
+ "<title>Updated from document element</title>" +
+ "<script>window.foo=\"bar\";</script></head><body>" +
+ "<p>Hello</p></body></html>";
+ let docElementFront = yield inspector.markup.walker.documentElement();
+
+ let onReselected = inspector.markup.once("reselectedonremoved");
+ yield inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML,
+ currentDocElementOuterHMTL);
+ yield onReselected;
+
+ is((yield testActor.eval("content.document.title")),
+ "Updated from document element", "New title has been added");
+ is((yield testActor.eval("content.foo")),
+ undefined, "Script has not been executed");
+ is((yield testActor.getAttribute("html", "id")),
+ "updated", "<html> ID has been updated");
+ is((yield testActor.getAttribute("html", "class")),
+ null, "<html> class has been updated");
+ is((yield testActor.getAttribute("html", "foo")),
+ "bar", "<html> attribute has been updated");
+ is((yield testActor.getProperty("html", "outerHTML")),
+ docElementHTML, "<html> HTML has been updated");
+ is((yield testActor.getNumberOfElementMatches("head")),
+ 1, "no extra <head>s have been added");
+ is((yield testActor.getNumberOfElementMatches("body")),
+ 1, "no extra <body>s have been added");
+ is((yield testActor.getProperty("body", "textContent")),
+ "Hello", "document.body.textContent has been updated");
+}
+
+function* testDocumentElement2(inspector, testActor) {
+ let currentDocElementOuterHMTL = yield testActor.eval(
+ "content.document.documentElement.outerHMTL");
+ let docElementHTML = "<html id=\"somethingelse\" class=\"updated\"><head>" +
+ "<title>Updated again from document element</title>" +
+ "<script>window.foo=\"bar\";</script></head><body>" +
+ "<p>Hello again</p></body></html>";
+ let docElementFront = yield inspector.markup.walker.documentElement();
+
+ let onReselected = inspector.markup.once("reselectedonremoved");
+ inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML,
+ currentDocElementOuterHMTL);
+ yield onReselected;
+
+ is((yield testActor.eval("content.document.title")),
+ "Updated again from document element", "New title has been added");
+ is((yield testActor.eval("content.foo")),
+ undefined, "Script has not been executed");
+ is((yield testActor.getAttribute("html", "id")),
+ "somethingelse", "<html> ID has been updated");
+ is((yield testActor.getAttribute("html", "class")),
+ "updated", "<html> class has been updated");
+ is((yield testActor.getAttribute("html", "foo")),
+ null, "<html> attribute has been removed");
+ is((yield testActor.getProperty("html", "outerHTML")),
+ docElementHTML, "<html> HTML has been updated");
+ is((yield testActor.getNumberOfElementMatches("head")),
+ 1, "no extra <head>s have been added");
+ is((yield testActor.getNumberOfElementMatches("body")),
+ 1, "no extra <body>s have been added");
+ is((yield testActor.getProperty("body", "textContent")),
+ "Hello again", "document.body.textContent has been updated");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_image_tooltip.js b/devtools/client/inspector/markup/test/browser_markup_image_tooltip.js
new file mode 100644
index 000000000..7b1611acd
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_image_tooltip.js
@@ -0,0 +1,60 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that image preview tooltips are shown on img and canvas tags in the
+// markup-view and that the tooltip actually contains an image and shows the
+// right dimension label
+
+const TEST_NODES = [
+ {selector: "img.local", size: "192" + " \u00D7 " + "192"},
+ {selector: "img.data", size: "64" + " \u00D7 " + "64"},
+ {selector: "img.remote", size: "22" + " \u00D7 " + "23"},
+ {selector: ".canvas", size: "600" + " \u00D7 " + "600"}
+];
+
+add_task(function* () {
+ yield addTab(URL_ROOT + "doc_markup_image_and_canvas_2.html");
+ let {inspector} = yield openInspector();
+
+ info("Selecting the first <img> tag");
+ yield selectNode("img", inspector);
+
+ for (let testNode of TEST_NODES) {
+ let target = yield getImageTooltipTarget(testNode, inspector);
+ yield assertTooltipShownOn(target, inspector);
+ checkImageTooltip(testNode, inspector);
+ }
+});
+
+function* getImageTooltipTarget({selector}, inspector) {
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let isImg = nodeFront.tagName.toLowerCase() === "img";
+
+ let container = getContainerForNodeFront(nodeFront, inspector);
+
+ let target = container.editor.tag;
+ if (isImg) {
+ target = container.editor.getAttributeElement("src").querySelector(".link");
+ }
+ return target;
+}
+
+function* assertTooltipShownOn(element, {markup}) {
+ info("Is the element a valid hover target");
+ let isValid = yield isHoverTooltipTarget(markup.imagePreviewTooltip, element);
+ ok(isValid, "The element is a valid hover target for the image tooltip");
+}
+
+function checkImageTooltip({selector, size}, {markup}) {
+ let panel = markup.imagePreviewTooltip.panel;
+ let images = panel.getElementsByTagName("img");
+ is(images.length, 1, "Tooltip for [" + selector + "] contains an image");
+
+ let label = panel.querySelector(".devtools-tooltip-caption");
+ is(label.textContent, size,
+ "Tooltip label for [" + selector + "] displays the right image size");
+
+ markup.imagePreviewTooltip.hide();
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_image_tooltip_mutations.js b/devtools/client/inspector/markup/test/browser_markup_image_tooltip_mutations.js
new file mode 100644
index 000000000..bc1af2c2a
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_image_tooltip_mutations.js
@@ -0,0 +1,83 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that image preview tooltip shows updated content when the image src
+// changes.
+
+/*eslint-disable */
+const INITIAL_SRC = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADI5JREFUeNrsWwuQFNUVPf1m5z87szv7HWSWj8CigBFMEFZKiQsB1PgJwUAZg1HBpIQsKmokEhNjWUnFVPnDWBT+KolJYbRMoqUVq0yCClpqiX8sCchPWFwVlt2db7+X93pez7zu6Vn2NxsVWh8987p7pu+9555z7+tZjTGGY3kjOMa34w447oBjfKsY7i/UNM3Y8eFSAkD50Plgw03K5P9gvGv7U5ieeR3PszeREiPNX3/0DL4hjslzhm8THh+OITfXk3dhiv4GDtGPVzCaeJmPLYzuu5qJuWfuw2QTlcN1X9pwQU7LhdZ/ZAseD45cOh9hHvDkc/yAF/DNhdb5Mrr3PvBMaAYW8fMSIi2G497IMEK/YutGtAYr6+ej+nxu/NN8Ks3N7AR6HgcLz0Eg1Ljg1UcxZzi5qewIkMYLRweTr2Kzp+nmyXAd5pS3XQDd+N/4h4zgu9FI7brlXf90nMEnuwQxlvv+hosE3TuexmWeysmT4W+WxkMaLzf9Y8ATgjcUn7T9H1gqrpFq8eV1gMn6t16NhngjfoX6q4DUP032Rd4LJgpSLwJ1yzFqBG69eRkah0MVyo0Acfe+yy9AG4nMiYCkeM53KKFXncBLAXqEm+wCqZwaueq7WCmuLTcKSJmj737ol2hurA9eq9VdyiO8yWa3NNyog+SB5CZodSsQq/dfu34tJpYbBaTMzvVddDZu16q5smXf4G8zEvqm4cyaAmJPuTJk3oJWdS4WzcVtfMZbThSQckb/pYfRGgo3zNOqZnEHbJPGK4abaDCQIIsT8V/qTaBqHkLh6LzXH8XZQhbLhYKyyCC/WeHYcNdmvOgfe8skzbWL270/T3wf7tSx/lGCbTu8xlzzmCSWLc5iwmgikcCHi3Mga0Ry913vBFvQwg90l6M4ImWKfsWOp7DSWxmfpPlCFuPFfsNfKrCnPYpQKIRgqBK7D0SxYaNHwkEiJMtl0ReDp3Lc5D3PGoTo/sKngCl7a5chFqvBatKwjBd7WwqIlzB/78NcoUcp5VSgGxm+7b8eqQRGnHMO634epO4S1EZww09/iFg5UmGoESDuznP1xVhTUX1WWHPzjpd25wyH0hRxI3LGM75nxmuNEEUVpAN0XgxmPoKralakbQnWlIMQyVBD/w+3orkq4lvualjKyWwzt4MaxqspQHVhPOWG64bxYuhZXSFGWhipbSDVragOu5Y9eAsmDDUKyBA703vemVhHoueD6e9wAzJK1WfmN0Umk5GGM4kEMZcuIECqgjm0nldAqmbjwtm4VxZH5AvlADP6mx9Eqy9Q0+KqW8Ch+47FaMMYmnNGfY1iPMshoC6qFxme4wQ+0p+ARE6H3+9veWEDWgUhDhUKyFARn4jM5BNxT0XsMg7bfymGK1ov3wtjDfhL4w0HVGUVBEjDaaE+QNdrcNWch1PG4W6xrjBUXECGivg++Cva3JUT4iQUz3V2RsSVaKLwOuDT89A3HdBQoxhNC+fnVm74ual2EG893P6G+PuP4SfiO4cCBWQooL9qCWKNXPbcI37Aa/lnlZxXRt4RFONGwSDCPAHqOuqjWct1QiEMw5mChM5X4K47FyNqcd3aK9AwFH0CGYLoe1ctxk2eWi57rg5JfGp9rzC6ggCdFlAgHBDw5Yxlcg6G8SyHCjMlsgmDD9zhSeHlF+JnAgWDTQUy2NxfdwOao1UVV3pi3+bE97YSbWpLAbn6zefHNQkp1PMpIBwwvslKgIYTKM2nEpNzrGcH3FXTEal0L38kJ4uDQgEZbO4vnI173LXf5NHZaiUxtaCxyZuo/rK6LpUg54yg3zTWRAArvDcRIPZ6BqzrQ1REpmL+DNw32OKIDCb3X1qPVn8wNNMT4w2bvs+q4bAZrqBh2skaL3yyhhIIZ4i6oHkUK0RckcB8GigEyRIH4A6Mgc8fatl0/+BkkQxC9gIT4ljna1rIZW9rEdNbjJcNjsnoYj7LHWCUwpITzEgzRQKZ3XAFHbTzA3hrz8TEUUZxFBhoKpABQt/97p+w0hMZG68I8R6FtlsJT3FELndZntjM+VMnylKYq8GJI3UZaRMpquGSGFVOEfv0YZBMNzz+uvjbfzS6xQERIhlI9FcvQWNdFVb7x1zCb+QNK8vb9NsiifmI5hBgVoOCBC1sb0ab5RomqENxLO3eA1/0NDRU47q2RQNbRCUDIb7lF2CNL3ZGxEV4n08TVvZWYG4pZyV0zUdS45tyCBByOHWiyvZmxFXDCyRo1ge5+Sy0TA+8lWMiP/6O0S32exGV9Jf4fr8azdUR3zL/CZz4MtvzdX5uOYs6NDOmpkuj5Huh+7qUQSYl0ThHzw0YQzcGo6bhzEqoYq5rN3yRiYiG3Vfe2Ybm/qKA9NNZ3nNm4F7/yDkg9AN+U1mHiBcXP8zuDN76jj8hg1QyiWQigalj02BJPhK8I0zxijAjhp5zhlpLUDvS+BCy2HMAvvB4XDgL9/SXC0g/ou/5+6/xLX8w0uJrOIkXfPvyhY0F6gr7M8H0KWFYikcqAXakB+xwD9CdREBLoau7Gz3cAdSIdLFxFtJTCqRChSjnutvhDcREtzjz2Tswtz+yeNRFUeXZXtWux7C1fuoVcbd3J//ipDX3uZZDLGrwweS+UBLL5TDliVBnF8P7H+XI8aRRGsIBJg/Zlslt1+W+D1JWoSyi+kD9jfhs78t7mhZhSl+fLfY1Bdyv3I8V/qpY3B1McgN7ZFT5/vNO0I5DPLLdPBIJA8qc4h2I0QplYfDpJwHT+aj0246r5S8rToG8OjCle8wk4OLvvYGa+Ovr84uo2qBSwJS9G5egoZFLTfiEqWDtbwGfHgKOdPHcS+ai7XDzMPW/FJRLGGcxnBbK4YJC2K+h+T6Bdu5CqHqCWERd3bawb7JI+iJ735+LNaHaprBLLHBm08U3XxShEsdt+f3eTh3v7aC95Dct4RCWL5OZWh/oXBZThxAIxyOXLzBk8aiEWJID8rK3CpPOmeHaGpvCS+7EHv5FujVHUSJPLXvIFeHcNc+9xrB2gws9KZdxuLFax/WLM5gzzSm/lTXF/OdAcapyvjxPqxqHjr2v4ckX2bS2dRBrc5lSdpKjEJ9/9tdwX2WMd53ZQ2IVo3RES+UwVSpCPvYepNx4gmTGDUKIMQ4eduPnD7mx9xOn/KZKOlFbStjONxHTtR+BYAPmnoZ1Zp8wkBRwP/EL3u0F/C2hGl7vpz7vW37T3vP7if8wroKuoh8ribknX9BK5rcF+mo1qKaKyRPJTgTDjbzY8szcuLb3bpH00u35T47j7prRpwDJTxzyG0dHgxPp5bPG8VdkpfPbUg3SgoOo2mwVukb98D5EqpswZTTulCggTk4gpYhv0++wIhCJxr0+Hq1sondis0SE2oxQe3qWXwWyO4DSQg9gJ8Iiw1VFcGqXxet0N9xE4ygIxv/9W6wo9WyROEX/R+eiobYSq2vHTOR631Eiv2lRfh9dvxkumkXh92Qsx8XrAJ+7YGbWuhxOi/U+31NQmzyqNYG8N/3wfo6CRtRHcN01FzkvojohwLu0VVvDa56IS/xcj2b7nN+O+m0jqpE1wMPXZxAN9iCVThtDvH7gmiRGRpU8Lspv1Uhq4wIVdQoyuGSLNYPKUCS8+CzNURbzMmjK3i8u0U793lmuV0ef9nWQ5MGC/DiUqEUSaCtXna9RJEspZS1lrXINK/pcq+SpT50t98QKMq1FRmDfx3vxty102k0PM4ssEnvuz5+G26Ij4yDpz6z9fV8bkyIkqBFkhej0Ib+ZQ34XJK9AfozaiimqIoX3Jp3tiISrcfYpuN2+iFph/02P36PNC9fVcCnp6H9jYouKyfaWufz5Tp9tVxcUniw7IohZv4dZz81/ns67z3AYPrc2n0+Ix2q8k0PWjgBy88XaibnfK9A+5LdDY2Ivhy36fbT8Zv3Lb1U1qLqUxorXEEXIs0mjjrtxoTZWtdvigNs2sgPiujTv6DIZLld6b/V5742JZV3fUsUVFy5gdsNtKWFzUCEVbNepD1MkSMVbsb6SZm7jI3/zODtQKgUMsOw8wDZ63t5xcV1TnaEAxoc6wrqY+Fj+N4DsqOnhOIdicrQSm1MPYCPlIqHn5bbHg8/bj2D3QfZnCX3mpAICDZV8jH5kpbZqTD0W+DxaA74CWzLN2nd14OlL72J38Lf7+TjC7dadZFDoZJQPrtaIKL/G0L6ktptPZVJ8fMqHYPZOKYPMyQGadIJfDvdXwAFiZOTvDBPydf5vk4rWA+RfdhBlaF/yDDBRoMu9pfnSjv/p7DG+HXfAcQcc49v/BBgAcFAO4DmB2GQAAAAASUVORK5CYII=";
+/*eslint-enable */
+
+const UPDATED_SRC = URL_ROOT + "doc_markup_tooltip.png";
+
+const INITIAL_SRC_SIZE = "64" + " \u00D7 " + "64";
+const UPDATED_SRC_SIZE = "22" + " \u00D7 " + "23";
+
+add_task(function* () {
+ let { inspector } = yield openInspectorForURL(
+ "data:text/html,<p>markup view tooltip test</p><img>");
+
+ info("Retrieving NodeFront for the <img> element.");
+ let img = yield getNodeFront("img", inspector);
+
+ info("Selecting the <img> element");
+ yield selectNode(img, inspector);
+
+ info("Adding src attribute to the image.");
+ yield updateImageSrc(img, INITIAL_SRC, inspector);
+
+ let container = getContainerForNodeFront(img, inspector);
+ ok(container, "Found markup container for the image.");
+
+ let target = container.editor.getAttributeElement("src")
+ .querySelector(".link");
+ ok(target, "Found the src attribute in the markup view.");
+
+ info("Showing tooltip on the src link.");
+ yield isHoverTooltipTarget(inspector.markup.imagePreviewTooltip, target);
+
+ checkImageTooltip(INITIAL_SRC_SIZE, inspector);
+
+ info("Updating the image src.");
+ yield updateImageSrc(img, UPDATED_SRC, inspector);
+
+ target = container.editor.getAttributeElement("src").querySelector(".link");
+ ok(target, "Found the src attribute in the markup view after mutation.");
+
+ info("Showing tooltip on the src link.");
+ yield isHoverTooltipTarget(inspector.markup.imagePreviewTooltip, target);
+
+ info("Checking that the new image was shown.");
+ checkImageTooltip(UPDATED_SRC_SIZE, inspector);
+});
+
+/**
+ * Updates the src attribute of the image. Return a Promise.
+ */
+function updateImageSrc(img, newSrc, inspector) {
+ let onMutated = inspector.once("markupmutation");
+ let onModified = img.modifyAttributes([{
+ attributeName: "src",
+ newValue: newSrc
+ }]);
+
+ return Promise.all([onMutated, onModified]);
+}
+
+/**
+ * Checks that the markup view tooltip contains an image element with the given
+ * size.
+ */
+function checkImageTooltip(size, {markup}) {
+ let panel = markup.imagePreviewTooltip.panel;
+ let images = panel.getElementsByTagName("img");
+ is(images.length, 1, "Tooltip contains an image");
+
+ let label = panel.querySelector(".devtools-tooltip-caption");
+ is(label.textContent, size, "Tooltip label displays the right image size");
+
+ markup.imagePreviewTooltip.hide();
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_01.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_01.js
new file mode 100644
index 000000000..58eccc173
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_01.js
@@ -0,0 +1,49 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Tests tabbing through attributes on a node
+
+const TEST_URL = "data:text/html;charset=utf8,<div id='test' a b c d e></div>";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Focusing the tag editor of the test element");
+ let {editor} = yield focusNode("div", inspector);
+ editor.tag.focus();
+
+ info("Pressing tab and expecting to focus the ID attribute, always first");
+ EventUtils.sendKey("tab", inspector.panelWin);
+ checkFocusedAttribute("id");
+
+ info("Hit enter to turn the attribute to edit mode");
+ EventUtils.sendKey("return", inspector.panelWin);
+ checkFocusedAttribute("id", true);
+
+ // Check the order of the other attributes in the DOM to the check they appear
+ // correctly in the markup-view
+ let attributes = (yield getAttributesFromEditor("div", inspector)).slice(1);
+
+ info("Tabbing forward through attributes in edit mode");
+ for (let attribute of attributes) {
+ collapseSelectionAndTab(inspector);
+ checkFocusedAttribute(attribute, true);
+ }
+
+ info("Tabbing backward through attributes in edit mode");
+
+ // Just reverse the attributes other than id and remove the first one since
+ // it's already focused now.
+ let reverseAttributes = attributes.reverse();
+ reverseAttributes.shift();
+
+ for (let attribute of reverseAttributes) {
+ collapseSelectionAndShiftTab(inspector);
+ checkFocusedAttribute(attribute, true);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_02.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_02.js
new file mode 100644
index 000000000..0e4b8a802
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_02.js
@@ -0,0 +1,32 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that pressing ESC when a node in the markup-view is focused toggles
+// the split-console (see bug 988278)
+
+const TEST_URL = "data:text/html;charset=utf8,<div></div>";
+
+add_task(function* () {
+ let {inspector, toolbox} = yield openInspectorForURL(TEST_URL);
+
+ info("Focusing the tag editor of the test element");
+ let {editor} = yield getContainerForSelector("div", inspector);
+ editor.tag.focus();
+
+ info("Pressing ESC and wait for the split-console to open");
+ let onSplitConsole = toolbox.once("split-console");
+ let onConsoleReady = toolbox.once("webconsole-ready");
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, inspector.panelWin);
+ yield onSplitConsole;
+ yield onConsoleReady;
+ ok(toolbox.splitConsole, "The split console is shown.");
+
+ info("Pressing ESC again and wait for the split-console to close");
+ onSplitConsole = toolbox.once("split-console");
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, inspector.panelWin);
+ yield onSplitConsole;
+ ok(!toolbox.splitConsole, "The split console is hidden.");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_03.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_03.js
new file mode 100644
index 000000000..1a94c9270
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_03.js
@@ -0,0 +1,50 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that selecting a node with the mouse (by clicking on the line) focuses
+// the first focusable element in the corresponding MarkupContainer so that the
+// keyboard can be used immediately.
+
+const TEST_URL = `data:text/html;charset=utf8,
+ <div class='test-class'></div>Text node`;
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let {walker} = inspector;
+
+ info("Select the test node to have the 2 test containers visible");
+ yield selectNode("div", inspector);
+
+ let divFront = yield walker.querySelector(walker.rootNode, "div");
+ let textFront = yield walker.nextSibling(divFront);
+
+ info("Click on the MarkupContainer element for the text node");
+ yield clickContainer(textFront, inspector);
+ is(inspector.markup.doc.activeElement,
+ getContainerForNodeFront(textFront, inspector).editor.value,
+ "The currently focused element is the node's text content");
+
+ info("Click on the MarkupContainer element for the <div> node");
+ yield clickContainer(divFront, inspector);
+ is(inspector.markup.doc.activeElement,
+ getContainerForNodeFront(divFront, inspector).editor.tag,
+ "The currently focused element is the div's tagname");
+
+ info("Click on the test-class attribute, to make sure it gets focused");
+ let editor = getContainerForNodeFront(divFront, inspector).editor;
+ let attributeEditor = editor.attrElements.get("class")
+ .querySelector(".editable");
+
+ let onFocus = once(attributeEditor, "focus");
+ EventUtils.synthesizeMouseAtCenter(attributeEditor, {type: "mousedown"},
+ inspector.markup.doc.defaultView);
+ EventUtils.synthesizeMouseAtCenter(attributeEditor, {type: "mouseup"},
+ inspector.markup.doc.defaultView);
+ yield onFocus;
+
+ is(inspector.markup.doc.activeElement, attributeEditor,
+ "The currently focused element is the div's class attribute");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js
new file mode 100644
index 000000000..3b6f8bfb3
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js
@@ -0,0 +1,58 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Tests that selecting a node using the browser context menu (inspect element)
+// or the element picker focuses that node so that the keyboard can be used
+// immediately.
+
+const TEST_URL = "data:text/html;charset=utf8,<div>test element</div>";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Select the test node with the browser ctx menu");
+ yield clickOnInspectMenuItem(testActor, "div");
+ assertNodeSelected(inspector, "div");
+
+ info("Press arrowUp to focus <body> " +
+ "(which works if the node was focused properly)");
+ yield selectPreviousNodeWithArrowUp(inspector);
+ assertNodeSelected(inspector, "body");
+
+ info("Select the test node with the element picker");
+ yield selectWithElementPicker(inspector, testActor);
+ assertNodeSelected(inspector, "div");
+
+ info("Press arrowUp to focus <body> " +
+ "(which works if the node was focused properly)");
+ yield selectPreviousNodeWithArrowUp(inspector);
+ assertNodeSelected(inspector, "body");
+});
+
+function assertNodeSelected(inspector, tagName) {
+ is(inspector.selection.nodeFront.tagName.toLowerCase(), tagName,
+ `The <${tagName}> node is selected`);
+}
+
+function selectPreviousNodeWithArrowUp(inspector) {
+ let onNodeHighlighted = inspector.toolbox.once("node-highlight");
+ let onUpdated = inspector.once("inspector-updated");
+ EventUtils.synthesizeKey("VK_UP", {});
+ return Promise.all([onUpdated, onNodeHighlighted]);
+}
+
+function* selectWithElementPicker(inspector, testActor) {
+ yield startPicker(inspector.toolbox);
+
+ yield BrowserTestUtils.synthesizeMouseAtCenter("div", {
+ type: "mousemove",
+ }, gBrowser.selectedBrowser);
+
+ yield testActor.synthesizeKey({key: "VK_RETURN", options: {}});
+ yield inspector.once("inspector-updated");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_delete_attributes.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_delete_attributes.js
new file mode 100644
index 000000000..a4c121360
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_delete_attributes.js
@@ -0,0 +1,63 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that attributes can be deleted from the markup-view with the delete key
+// when they are focused.
+
+const HTML = '<div id="id" class="class" data-id="id"></div>';
+const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
+
+// List of all the test cases. Each item is an object with the following props:
+// - selector: the css selector of the node that should be selected
+// - attribute: the name of the attribute that should be focused. Do not
+// specify an attribute that would make it impossible to find the node using
+// selector.
+// Note that after each test case, undo is called.
+const TEST_DATA = [{
+ selector: "#id",
+ attribute: "class"
+}, {
+ selector: "#id",
+ attribute: "data-id"
+}];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let {walker} = inspector;
+
+ for (let {selector, attribute} of TEST_DATA) {
+ info("Get the container for node " + selector);
+ let {editor} = yield getContainerForSelector(selector, inspector);
+
+ info("Focus attribute " + attribute);
+ let attr = editor.attrElements.get(attribute).querySelector(".editable");
+ attr.focus();
+
+ info("Delete the attribute by pressing delete");
+ let mutated = inspector.once("markupmutation");
+ EventUtils.sendKey("delete", inspector.panelWin);
+ yield mutated;
+
+ info("Check that the node is still here");
+ let node = yield walker.querySelector(walker.rootNode, selector);
+ ok(node, "The node hasn't been deleted");
+
+ info("Check that the attribute has been deleted");
+ node = yield walker.querySelector(walker.rootNode,
+ selector + "[" + attribute + "]");
+ ok(!node, "The attribute does not exist anymore in the DOM");
+ ok(!editor.attrElements.get(attribute),
+ "The attribute has been removed from the container");
+
+ info("Undo the change");
+ yield undoChange(inspector);
+ node = yield walker.querySelector(walker.rootNode,
+ selector + "[" + attribute + "]");
+ ok(node, "The attribute is back in the DOM");
+ ok(editor.attrElements.get(attribute),
+ "The attribute is back on the container");
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_scrolltonode.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_scrolltonode.js
new file mode 100644
index 000000000..7b129fc42
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_scrolltonode.js
@@ -0,0 +1,87 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test the keyboard shortcut "S" used to scroll to the selected node.
+
+const HTML =
+ `<div style="width: 300px; height: 3000px; position:relative;">
+ <div id="scroll-top"
+ style="height: 50px; top: 0; position:absolute;">
+ TOP</div>
+ <div id="scroll-bottom"
+ style="height: 50px; bottom: 0; position:absolute;">
+ BOTTOM</div>
+ </div>`;
+const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
+
+add_task(function* () {
+ let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
+
+ info("Make sure the markup frame has the focus");
+ inspector.markup._frame.focus();
+
+ info("Before test starts, #scroll-top is visible, #scroll-bottom is hidden");
+ yield checkElementIsInViewport("#scroll-top", true, testActor);
+ yield checkElementIsInViewport("#scroll-bottom", false, testActor);
+
+ info("Select the #scroll-bottom node");
+ yield selectNode("#scroll-bottom", inspector);
+ info("Press S to scroll to the bottom node");
+ let waitForScroll = testActor.waitForEventOnNode("scroll");
+ yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
+ yield waitForScroll;
+ ok(true, "Scroll event received");
+
+ info("#scroll-top should be scrolled out, #scroll-bottom should be visible");
+ yield checkElementIsInViewport("#scroll-top", false, testActor);
+ yield checkElementIsInViewport("#scroll-bottom", true, testActor);
+
+ info("Select the #scroll-top node");
+ yield selectNode("#scroll-top", inspector);
+ info("Press S to scroll to the top node");
+ waitForScroll = testActor.waitForEventOnNode("scroll");
+ yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
+ yield waitForScroll;
+ ok(true, "Scroll event received");
+
+ info("#scroll-top should be visible, #scroll-bottom should be scrolled out");
+ yield checkElementIsInViewport("#scroll-top", true, testActor);
+ yield checkElementIsInViewport("#scroll-bottom", false, testActor);
+
+ info("Select #scroll-bottom node");
+ yield selectNode("#scroll-bottom", inspector);
+ info("Press shift + S, nothing should happen due to the modifier");
+ yield EventUtils.synthesizeKey("S", {shiftKey: true}, inspector.panelWin);
+
+ info("Same state, #scroll-top is visible, #scroll-bottom is scrolled out");
+ yield checkElementIsInViewport("#scroll-top", true, testActor);
+ yield checkElementIsInViewport("#scroll-bottom", false, testActor);
+});
+
+/**
+ * Verify that the element matching the provided selector is either in or out
+ * of the viewport, depending on the provided "expected" argument.
+ * Returns a promise that will resolve when the test has been performed.
+ *
+ * @param {String} selector
+ * css selector for the element to test
+ * @param {Boolean} expected
+ * true if the element is expected to be in the viewport, false otherwise
+ * @param {TestActor} testActor
+ * current test actor
+ * @return {Promise} promise
+ */
+function* checkElementIsInViewport(selector, expected, testActor) {
+ let isInViewport = yield testActor.eval(`
+ let node = content.document.querySelector("${selector}");
+ let rect = node.getBoundingClientRect();
+ rect.bottom >= 0 && rect.right >= 0 &&
+ rect.top <= content.innerHeight && rect.left <= content.innerWidth;
+ `);
+
+ is(isInViewport, expected,
+ selector + " in the viewport: expected to be " + expected);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_01.js b/devtools/client/inspector/markup/test/browser_markup_links_01.js
new file mode 100644
index 000000000..4ef3ba4b9
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_01.js
@@ -0,0 +1,128 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that links are shown in attributes when the values (or part of the
+// values) are URIs or pointers to IDs.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+const TEST_DATA = [{
+ selector: "link",
+ attributes: [{
+ attributeName: "href",
+ links: [{type: "cssresource", value: "style.css"}]
+ }]
+}, {
+ selector: "link[rel=icon]",
+ attributes: [{
+ attributeName: "href",
+ links: [{type: "uri",
+ value: "/media/img/firefox/favicon-196.223e1bcaf067.png"}]
+ }]
+}, {
+ selector: "form",
+ attributes: [{
+ attributeName: "action",
+ links: [{type: "uri", value: "/post_message"}]
+ }]
+}, {
+ selector: "label[for=name]",
+ attributes: [{
+ attributeName: "for",
+ links: [{type: "idref", value: "name"}]
+ }]
+}, {
+ selector: "label[for=message]",
+ attributes: [{
+ attributeName: "for",
+ links: [{type: "idref", value: "message"}]
+ }]
+}, {
+ selector: "output",
+ attributes: [{
+ attributeName: "form",
+ links: [{type: "idref", value: "message-form"}]
+ }, {
+ attributeName: "for",
+ links: [
+ {type: "idref", value: "name"},
+ {type: "idref", value: "message"},
+ {type: "idref", value: "invalid"}
+ ]
+ }]
+}, {
+ selector: "a",
+ attributes: [{
+ attributeName: "href",
+ links: [{type: "uri", value: "/go/somewhere/else"}]
+ }, {
+ attributeName: "ping",
+ links: [
+ {type: "uri", value: "/analytics?page=pageA"},
+ {type: "uri", value: "/analytics?user=test"}
+ ]
+ }]
+}, {
+ selector: "li[contextmenu=menu1]",
+ attributes: [{
+ attributeName: "contextmenu",
+ links: [{type: "idref", value: "menu1"}]
+ }]
+}, {
+ selector: "li[contextmenu=menu2]",
+ attributes: [{
+ attributeName: "contextmenu",
+ links: [{type: "idref", value: "menu2"}]
+ }]
+}, {
+ selector: "li[contextmenu=menu3]",
+ attributes: [{
+ attributeName: "contextmenu",
+ links: [{type: "idref", value: "menu3"}]
+ }]
+}, {
+ selector: "video",
+ attributes: [{
+ attributeName: "poster",
+ links: [{type: "uri", value: "doc_markup_tooltip.png"}]
+ }, {
+ attributeName: "src",
+ links: [{type: "uri", value: "code-rush.mp4"}]
+ }]
+}, {
+ selector: "script",
+ attributes: [{
+ attributeName: "src",
+ links: [{type: "jsresource", value: "lib_jquery_1.0.js"}]
+ }]
+}];
+
+requestLongerTimeout(2);
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ for (let {selector, attributes} of TEST_DATA) {
+ info("Testing attributes on node " + selector);
+ yield selectNode(selector, inspector);
+ let {editor} = yield getContainerForSelector(selector, inspector);
+
+ for (let {attributeName, links} of attributes) {
+ info("Testing attribute " + attributeName);
+ let linkEls = editor.attrElements.get(attributeName)
+ .querySelectorAll(".link");
+
+ is(linkEls.length, links.length, "The right number of links were found");
+
+ for (let i = 0; i < links.length; i++) {
+ is(linkEls[i].dataset.type, links[i].type,
+ `Link ${i} has the right type`);
+ is(linkEls[i].textContent, links[i].value,
+ `Link ${i} has the right value`);
+ }
+ }
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_02.js b/devtools/client/inspector/markup/test/browser_markup_links_02.js
new file mode 100644
index 000000000..83893281c
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_02.js
@@ -0,0 +1,38 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that attributes are linkified correctly when attributes are updated
+// and created.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Adding a contextmenu attribute to the body node");
+ yield addNewAttributes("body", "contextmenu=\"menu1\"", inspector);
+
+ info("Checking for links in the new attribute");
+ let {editor} = yield getContainerForSelector("body", inspector);
+ let linkEls = editor.attrElements.get("contextmenu")
+ .querySelectorAll(".link");
+ is(linkEls.length, 1, "There is one link in the contextmenu attribute");
+ is(linkEls[0].dataset.type, "idref", "The link has the right type");
+ is(linkEls[0].textContent, "menu1", "The link has the right value");
+
+ info("Editing the contextmenu attribute on the body node");
+ let nodeMutated = inspector.once("markupmutation");
+ let attr = editor.attrElements.get("contextmenu").querySelector(".editable");
+ setEditableFieldValue(attr, "contextmenu=\"menu2\"", inspector);
+ yield nodeMutated;
+
+ info("Checking for links in the updated attribute");
+ ({editor} = yield getContainerForSelector("body", inspector));
+ linkEls = editor.attrElements.get("contextmenu").querySelectorAll(".link");
+ is(linkEls.length, 1, "There is one link in the contextmenu attribute");
+ is(linkEls[0].dataset.type, "idref", "The link has the right type");
+ is(linkEls[0].textContent, "menu2", "The link has the right value");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_03.js b/devtools/client/inspector/markup/test/browser_markup_links_03.js
new file mode 100644
index 000000000..a54ccb498
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_03.js
@@ -0,0 +1,38 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that links appear correctly in attributes created in content.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Adding a contextmenu attribute to the body node via the content");
+ let onMutated = inspector.once("markupmutation");
+ yield testActor.setAttribute("body", "contextmenu", "menu1");
+ yield onMutated;
+
+ info("Checking for links in the new attribute");
+ let {editor} = yield getContainerForSelector("body", inspector);
+ let linkEls = editor.attrElements.get("contextmenu")
+ .querySelectorAll(".link");
+ is(linkEls.length, 1, "There is one link in the contextmenu attribute");
+ is(linkEls[0].dataset.type, "idref", "The link has the right type");
+ is(linkEls[0].textContent, "menu1", "The link has the right value");
+
+ info("Editing the contextmenu attribute on the body node");
+ onMutated = inspector.once("markupmutation");
+ yield testActor.setAttribute("body", "contextmenu", "menu2");
+ yield onMutated;
+
+ info("Checking for links in the updated attribute");
+ ({editor} = yield getContainerForSelector("body", inspector));
+ linkEls = editor.attrElements.get("contextmenu").querySelectorAll(".link");
+ is(linkEls.length, 1, "There is one link in the contextmenu attribute");
+ is(linkEls[0].dataset.type, "idref", "The link has the right type");
+ is(linkEls[0].textContent, "menu2", "The link has the right value");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_04.js b/devtools/client/inspector/markup/test/browser_markup_links_04.js
new file mode 100644
index 000000000..f21afd8d2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_04.js
@@ -0,0 +1,116 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the contextual menu shows the right items when clicking on a link
+// in an attribute.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+const TOOLBOX_L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+
+// The test case array contains objects with the following properties:
+// - selector: css selector for the node to select in the inspector
+// - attributeName: name of the attribute to test
+// - popupNodeSelector: css selector for the element inside the attribute
+// element to use as the contextual menu anchor
+// - isLinkFollowItemVisible: is the follow-link item expected to be displayed
+// - isLinkCopyItemVisible: is the copy-link item expected to be displayed
+// - linkFollowItemLabel: the expected label of the follow-link item
+// - linkCopyItemLabel: the expected label of the copy-link item
+const TEST_DATA = [{
+ selector: "link",
+ attributeName: "href",
+ popupNodeSelector: ".link",
+ isLinkFollowItemVisible: true,
+ isLinkCopyItemVisible: true,
+ linkFollowItemLabel: TOOLBOX_L10N.getStr(
+ "toolbox.viewCssSourceInStyleEditor.label"),
+ linkCopyItemLabel: INSPECTOR_L10N.getStr(
+ "inspector.menu.copyUrlToClipboard.label")
+}, {
+ selector: "link[rel=icon]",
+ attributeName: "href",
+ popupNodeSelector: ".link",
+ isLinkFollowItemVisible: true,
+ isLinkCopyItemVisible: true,
+ linkFollowItemLabel: INSPECTOR_L10N.getStr(
+ "inspector.menu.openUrlInNewTab.label"),
+ linkCopyItemLabel: INSPECTOR_L10N.getStr(
+ "inspector.menu.copyUrlToClipboard.label")
+}, {
+ selector: "link",
+ attributeName: "rel",
+ popupNodeSelector: ".attr-value",
+ isLinkFollowItemVisible: false,
+ isLinkCopyItemVisible: false
+}, {
+ selector: "output",
+ attributeName: "for",
+ popupNodeSelector: ".link",
+ isLinkFollowItemVisible: true,
+ isLinkCopyItemVisible: false,
+ linkFollowItemLabel: INSPECTOR_L10N.getFormatStr(
+ "inspector.menu.selectElement.label", "name")
+}, {
+ selector: "script",
+ attributeName: "src",
+ popupNodeSelector: ".link",
+ isLinkFollowItemVisible: true,
+ isLinkCopyItemVisible: true,
+ linkFollowItemLabel: TOOLBOX_L10N.getStr(
+ "toolbox.viewJsSourceInDebugger.label"),
+ linkCopyItemLabel: INSPECTOR_L10N.getStr(
+ "inspector.menu.copyUrlToClipboard.label")
+}, {
+ selector: "p[for]",
+ attributeName: "for",
+ popupNodeSelector: ".attr-value",
+ isLinkFollowItemVisible: false,
+ isLinkCopyItemVisible: false
+}];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ for (let test of TEST_DATA) {
+ info("Selecting test node " + test.selector);
+ yield selectNode(test.selector, inspector);
+
+ info("Finding the popupNode to anchor the context-menu to");
+ let {editor} = yield getContainerForSelector(test.selector, inspector);
+ let popupNode = editor.attrElements.get(test.attributeName)
+ .querySelector(test.popupNodeSelector);
+ ok(popupNode, "Found the popupNode in attribute " + test.attributeName);
+
+ info("Simulating a context click on the popupNode");
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: popupNode,
+ });
+
+ let linkFollow = allMenuItems.find(i => i.id === "node-menu-link-follow");
+ let linkCopy = allMenuItems.find(i => i.id === "node-menu-link-copy");
+
+ // The contextual menu setup is async, because it needs to know if the
+ // inspector has the resolveRelativeURL method first. So call actorHasMethod
+ // here too to make sure the first call resolves first and the menu is
+ // properly setup.
+ yield inspector.target.actorHasMethod("inspector", "resolveRelativeURL");
+
+ is(linkFollow.visible, test.isLinkFollowItemVisible,
+ "The follow-link item display is correct");
+ is(linkCopy.visible, test.isLinkCopyItemVisible,
+ "The copy-link item display is correct");
+
+ if (test.isLinkFollowItemVisible) {
+ is(linkFollow.label, test.linkFollowItemLabel,
+ "the follow-link label is correct");
+ }
+ if (test.isLinkCopyItemVisible) {
+ is(linkCopy.label, test.linkCopyItemLabel,
+ "the copy-link label is correct");
+ }
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_05.js b/devtools/client/inspector/markup/test/browser_markup_links_05.js
new file mode 100644
index 000000000..feaf257a8
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_05.js
@@ -0,0 +1,69 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the contextual menu items shown when clicking on links in
+// attributes actually do the right things.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Select a node with a URI attribute");
+ yield selectNode("video", inspector);
+
+ info("Set the popupNode to the node that contains the uri");
+ let {editor} = yield getContainerForSelector("video", inspector);
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("poster").querySelector(".link"),
+ });
+
+ info("Follow the link and wait for the new tab to open");
+ let onTabOpened = once(gBrowser.tabContainer, "TabOpen");
+ inspector.onFollowLink();
+ let {target: tab} = yield onTabOpened;
+ yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ ok(true, "A new tab opened");
+ is(tab.linkedBrowser.currentURI.spec, URL_ROOT + "doc_markup_tooltip.png",
+ "The URL for the new tab is correct");
+ gBrowser.removeTab(tab);
+
+ info("Select a node with a IDREF attribute");
+ yield selectNode("label", inspector);
+
+ info("Set the popupNode to the node that contains the ref");
+ ({editor} = yield getContainerForSelector("label", inspector));
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("for").querySelector(".link"),
+ });
+
+ info("Follow the link and wait for the new node to be selected");
+ let onSelection = inspector.selection.once("new-node-front");
+ inspector.onFollowLink();
+ yield onSelection;
+
+ ok(true, "A new node was selected");
+ is(inspector.selection.nodeFront.id, "name", "The right node was selected");
+
+ info("Select a node with an invalid IDREF attribute");
+ yield selectNode("output", inspector);
+
+ info("Set the popupNode to the node that contains the ref");
+ ({editor} = yield getContainerForSelector("output", inspector));
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("for").querySelectorAll(".link")[2],
+ });
+
+ info("Try to follow the link and check that no new node were selected");
+ let onFailed = inspector.once("idref-attribute-link-failed");
+ inspector.onFollowLink();
+ yield onFailed;
+
+ ok(true, "The node selection failed");
+ is(inspector.selection.nodeFront.tagName.toLowerCase(), "output",
+ "The <output> node is still selected");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_06.js b/devtools/client/inspector/markup/test/browser_markup_links_06.js
new file mode 100644
index 000000000..452fa9eca
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_06.js
@@ -0,0 +1,53 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the contextual menu items shown when clicking on linked attributes
+// for <script> and <link> tags actually open the right tools.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+add_task(function* () {
+ let {toolbox, inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Select a node with a cssresource attribute");
+ yield selectNode("link", inspector);
+
+ info("Set the popupNode to the node that contains the uri");
+ let {editor} = yield getContainerForSelector("link", inspector);
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("href").querySelector(".link"),
+ });
+
+ info("Follow the link and wait for the style-editor to open");
+ let onStyleEditorReady = toolbox.once("styleeditor-ready");
+ inspector.onFollowLink();
+ yield onStyleEditorReady;
+
+ // No real need to test that the editor opened on the right file here as this
+ // is already tested in /framework/test/browser_toolbox_view_source_*
+ ok(true, "The style-editor was open");
+
+ info("Switch back to the inspector");
+ yield toolbox.selectTool("inspector");
+
+ info("Select a node with a jsresource attribute");
+ yield selectNode("script", inspector);
+
+ info("Set the popupNode to the node that contains the uri");
+ ({editor} = yield getContainerForSelector("script", inspector));
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("src").querySelector(".link"),
+ });
+
+ info("Follow the link and wait for the debugger to open");
+ let onDebuggerReady = toolbox.once("jsdebugger-ready");
+ inspector.onFollowLink();
+ yield onDebuggerReady;
+
+ // No real need to test that the debugger opened on the right file here as
+ // this is already tested in /framework/test/browser_toolbox_view_source_*
+ ok(true, "The debugger was open");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_links_07.js b/devtools/client/inspector/markup/test/browser_markup_links_07.js
new file mode 100644
index 000000000..793c1ee90
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_links_07.js
@@ -0,0 +1,109 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a middle-click or meta/ctrl-click on links in attributes actually
+// do follows the link.
+
+const TEST_URL = URL_ROOT + "doc_markup_links.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Select a node with a URI attribute");
+ yield selectNode("video", inspector);
+
+ info("Find the link element from the markup-view");
+ let {editor} = yield getContainerForSelector("video", inspector);
+ let linkEl = editor.attrElements.get("poster").querySelector(".link");
+
+ info("Follow the link with middle-click and wait for the new tab to open");
+ yield followLinkWaitForTab(linkEl, false,
+ URL_ROOT + "doc_markup_tooltip.png");
+
+ info("Follow the link with meta/ctrl-click and wait for the new tab to open");
+ yield followLinkWaitForTab(linkEl, true,
+ URL_ROOT + "doc_markup_tooltip.png");
+
+ info("Select a node with a IDREF attribute");
+ yield selectNode("label", inspector);
+
+ info("Find the link element from the markup-view that contains the ref");
+ ({editor} = yield getContainerForSelector("label", inspector));
+ linkEl = editor.attrElements.get("for").querySelector(".link");
+
+ info("Follow link with middle-click, wait for new node to be selected.");
+ yield followLinkWaitForNewNode(linkEl, false, inspector);
+
+ // We have to re-select the label as the link switched the currently selected
+ // node.
+ yield selectNode("label", inspector);
+
+ info("Follow link with ctrl/meta-click, wait for new node to be selected.");
+ yield followLinkWaitForNewNode(linkEl, true, inspector);
+
+ info("Select a node with an invalid IDREF attribute");
+ yield selectNode("output", inspector);
+
+ info("Find the link element from the markup-view that contains the ref");
+ ({editor} = yield getContainerForSelector("output", inspector));
+ linkEl = editor.attrElements.get("for").querySelectorAll(".link")[2];
+
+ info("Try to follow link wiith middle-click, check no new node selected");
+ yield followLinkNoNewNode(linkEl, false, inspector);
+
+ info("Try to follow link wiith meta/ctrl-click, check no new node selected");
+ yield followLinkNoNewNode(linkEl, true, inspector);
+});
+
+function performMouseDown(linkEl, metactrl) {
+ let evt = linkEl.ownerDocument.createEvent("MouseEvents");
+
+ let button = -1;
+
+ if (metactrl) {
+ info("Performing Meta/Ctrl+Left Click");
+ button = 0;
+ } else {
+ info("Performing Middle Click");
+ button = 1;
+ }
+
+ evt.initMouseEvent("mousedown", true, true,
+ linkEl.ownerDocument.defaultView, 1, 0, 0, 0, 0, metactrl,
+ false, false, metactrl, button, null);
+
+ linkEl.dispatchEvent(evt);
+}
+
+function* followLinkWaitForTab(linkEl, isMetaClick, expectedTabURI) {
+ let onTabOpened = once(gBrowser.tabContainer, "TabOpen");
+ performMouseDown(linkEl, isMetaClick);
+ let {target} = yield onTabOpened;
+ yield BrowserTestUtils.browserLoaded(target.linkedBrowser);
+ ok(true, "A new tab opened");
+ is(target.linkedBrowser.currentURI.spec, expectedTabURI,
+ "The URL for the new tab is correct");
+ gBrowser.removeTab(target);
+}
+
+function* followLinkWaitForNewNode(linkEl, isMetaClick, inspector) {
+ let onSelection = inspector.selection.once("new-node-front");
+ performMouseDown(linkEl, isMetaClick);
+ yield onSelection;
+
+ ok(true, "A new node was selected");
+ is(inspector.selection.nodeFront.id, "name", "The right node was selected");
+}
+
+function* followLinkNoNewNode(linkEl, isMetaClick, inspector) {
+ let onFailed = inspector.once("idref-attribute-link-failed");
+ performMouseDown(linkEl, isMetaClick);
+ yield onFailed;
+
+ ok(true, "The node selection failed");
+ is(inspector.selection.nodeFront.tagName.toLowerCase(), "output",
+ "The <output> node is still selected");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_load_01.js b/devtools/client/inspector/markup/test/browser_markup_load_01.js
new file mode 100644
index 000000000..9c8f4ed2c
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_load_01.js
@@ -0,0 +1,71 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that selecting an element with the 'Inspect Element' context
+// menu during a page reload doesn't cause the markup view to become empty.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1036324
+
+const server = createTestHTTPServer();
+
+// Register a slow image handler so we can simulate a long time between
+// a reload and the load event firing.
+server.registerContentType("gif", "image/gif");
+server.registerPathHandler("/slow.gif", function (metadata, response) {
+ info("Image has been requested");
+ response.processAsync();
+ setTimeout(() => {
+ info("Image is responding");
+ response.finish();
+ }, 500);
+});
+
+// Test page load events.
+const TEST_URL = "data:text/html," +
+ "<!DOCTYPE html>" +
+ "<head><meta charset='utf-8' /></head>" +
+ "<body>" +
+ "<p>Slow script</p>" +
+ "<img src='http://localhost:" + server.identity.primaryPort + "/slow.gif' /></script>" +
+ "</body>" +
+ "</html>";
+
+add_task(function* () {
+ let {inspector, testActor, tab} = yield openInspectorForURL(TEST_URL);
+ let domContentLoaded = waitForLinkedBrowserEvent(tab, "DOMContentLoaded");
+ let pageLoaded = waitForLinkedBrowserEvent(tab, "load");
+
+ ok(inspector.markup, "There is a markup view");
+
+ // Select an element while the tab is in the middle of a slow reload.
+ testActor.eval("location.reload()");
+ yield domContentLoaded;
+ yield chooseWithInspectElementContextMenu("img", testActor);
+ yield pageLoaded;
+
+ yield inspector.once("markuploaded");
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ ok(inspector.markup, "There is a markup view");
+ is(inspector.markup._elt.children.length, 1, "The markup view is rendering");
+});
+
+function* chooseWithInspectElementContextMenu(selector, testActor) {
+ yield BrowserTestUtils.synthesizeMouseAtCenter(selector, {
+ type: "contextmenu",
+ button: 2
+ }, gBrowser.selectedBrowser);
+
+ yield EventUtils.synthesizeKey("Q", {});
+}
+
+function waitForLinkedBrowserEvent(tab, event) {
+ let def = defer();
+ tab.linkedBrowser.addEventListener(event, function cb() {
+ tab.linkedBrowser.removeEventListener(event, cb, true);
+ def.resolve();
+ }, true);
+ return def.promise;
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_mutation_01.js b/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
new file mode 100644
index 000000000..1e4cfb9b0
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
@@ -0,0 +1,340 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that various mutations to the dom update the markup view correctly.
+
+const TEST_URL = URL_ROOT + "doc_markup_mutation.html";
+
+// Mutation tests. Each entry in the array has the following properties:
+// - desc: for logging only
+// - numMutations: how many mutations are expected to come happen due to the
+// test case. Defaults to 1 if not set.
+// - test: a function supposed to mutate the DOM
+// - check: a function supposed to test that the mutation was handled
+const TEST_DATA = [
+ {
+ desc: "Adding an attribute",
+ test: function* (testActor) {
+ yield testActor.setAttribute("#node1", "newattr", "newattrval");
+ },
+ check: function* (inspector) {
+ let {editor} = yield getContainerForSelector("#node1", inspector);
+ ok([...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
+ return attr.textContent.trim() === "newattr=\"newattrval\""
+ && attr.dataset.value === "newattrval"
+ && attr.dataset.attr === "newattr";
+ }), "newattr attribute found");
+ }
+ },
+ {
+ desc: "Removing an attribute",
+ test: function* (testActor) {
+ yield testActor.removeAttribute("#node1", "newattr");
+ },
+ check: function* (inspector) {
+ let {editor} = yield getContainerForSelector("#node1", inspector);
+ ok(![...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
+ return attr.textContent.trim() === "newattr=\"newattrval\"";
+ }), "newattr attribute removed");
+ }
+ },
+ {
+ desc: "Re-adding an attribute",
+ test: function* (testActor) {
+ yield testActor.setAttribute("#node1", "newattr", "newattrval");
+ },
+ check: function* (inspector) {
+ let {editor} = yield getContainerForSelector("#node1", inspector);
+ ok([...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
+ return attr.textContent.trim() === "newattr=\"newattrval\""
+ && attr.dataset.value === "newattrval"
+ && attr.dataset.attr === "newattr";
+ }), "newattr attribute found");
+ }
+ },
+ {
+ desc: "Changing an attribute",
+ test: function* (testActor) {
+ yield testActor.setAttribute("#node1", "newattr", "newattrchanged");
+ },
+ check: function* (inspector) {
+ let {editor} = yield getContainerForSelector("#node1", inspector);
+ ok([...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
+ return attr.textContent.trim() === "newattr=\"newattrchanged\""
+ && attr.dataset.value === "newattrchanged"
+ && attr.dataset.attr === "newattr";
+ }), "newattr attribute found");
+ }
+ },
+ {
+ desc: "Adding another attribute does not rerender unchanged attributes",
+ test: function* (testActor, inspector) {
+ let {editor} = yield getContainerForSelector("#node1", inspector);
+
+ // This test checks the impact on the markup-view nodes after setting attributes on
+ // content nodes.
+ info("Expect attribute-container for 'new-attr' from the previous test");
+ let attributeContainer = editor.attrList.querySelector("[data-attr=newattr]");
+ ok(attributeContainer, "attribute-container for 'newattr' found");
+
+ info("Set a flag on the attribute-container to check after the mutation");
+ attributeContainer.beforeMutationFlag = true;
+
+ info("Add the attribute 'otherattr' on the content node to trigger the mutation");
+ yield testActor.setAttribute("#node1", "otherattr", "othervalue");
+ },
+ check: function* (inspector) {
+ let {editor} = yield getContainerForSelector("#node1", inspector);
+
+ info("Check the attribute-container for the new attribute mutation was created");
+ let otherAttrContainer = editor.attrList.querySelector("[data-attr=otherattr]");
+ ok(otherAttrContainer, "attribute-container for 'otherattr' found");
+
+ info("Check the attribute-container for 'new-attr' is the same node as earlier.");
+ let newAttrContainer = editor.attrList.querySelector("[data-attr=newattr]");
+ ok(newAttrContainer, "attribute-container for 'newattr' found");
+ ok(newAttrContainer.beforeMutationFlag, "attribute-container same as earlier");
+ }
+ },
+ {
+ desc: "Adding ::after element",
+ numMutations: 2,
+ test: function* (testActor) {
+ yield testActor.eval(`
+ let node1 = content.document.querySelector("#node1");
+ node1.classList.add("pseudo");
+ `);
+ },
+ check: function* (inspector) {
+ let {children} = yield getContainerForSelector("#node1", inspector);
+ is(children.childNodes.length, 2,
+ "Node1 now has 2 children (text child and ::after");
+ }
+ },
+ {
+ desc: "Removing ::after element",
+ numMutations: 2,
+ test: function* (testActor) {
+ yield testActor.eval(`
+ let node1 = content.document.querySelector("#node1");
+ node1.classList.remove("pseudo");
+ `);
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node1", inspector);
+ ok(container.inlineTextChild, "Has single text child.");
+ }
+ },
+ {
+ desc: "Updating the text-content",
+ test: function* (testActor) {
+ yield testActor.setProperty("#node1", "textContent", "newtext");
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node1", inspector);
+ ok(container.inlineTextChild, "Has single text child.");
+ ok(!container.canExpand, "Can't expand container with inlineTextChild.");
+ ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
+ is(container.editor.elt.querySelector(".text").textContent.trim(),
+ "newtext", "Single text child editor updated.");
+ }
+ },
+ {
+ desc: "Adding a second text child",
+ test: function* (testActor) {
+ yield testActor.eval(`
+ let node1 = content.document.querySelector("#node1");
+ let newText = node1.ownerDocument.createTextNode("more");
+ node1.appendChild(newText);
+ `);
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node1", inspector);
+ ok(!container.inlineTextChild, "Does not have single text child.");
+ ok(container.canExpand, "Can expand container with child nodes.");
+ ok(container.editor.elt.querySelector(".text") == null,
+ "Single text child editor removed.");
+ },
+ },
+ {
+ desc: "Go from 2 to 1 text child",
+ test: function* (testActor) {
+ yield testActor.setProperty("#node1", "textContent", "newtext");
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node1", inspector);
+ ok(container.inlineTextChild, "Has single text child.");
+ ok(!container.canExpand, "Can't expand container with inlineTextChild.");
+ ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
+ ok(container.editor.elt.querySelector(".text").textContent.trim(),
+ "newtext", "Single text child editor updated.");
+ },
+ },
+ {
+ desc: "Removing an only text child",
+ test: function* (testActor) {
+ yield testActor.setProperty("#node1", "innerHTML", "");
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node1", inspector);
+ ok(!container.inlineTextChild, "Does not have single text child.");
+ ok(!container.canExpand, "Can't expand empty container.");
+ ok(container.editor.elt.querySelector(".text") == null,
+ "Single text child editor removed.");
+ },
+ },
+ {
+ desc: "Go from 0 to 1 text child",
+ test: function* (testActor) {
+ yield testActor.setProperty("#node1", "textContent", "newtext");
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node1", inspector);
+ ok(container.inlineTextChild, "Has single text child.");
+ ok(!container.canExpand, "Can't expand container with inlineTextChild.");
+ ok(!container.inlineTextChild.canExpand, "Can't expand inlineTextChild.");
+ ok(container.editor.elt.querySelector(".text").textContent.trim(),
+ "newtext", "Single text child editor updated.");
+ },
+ },
+
+ {
+ desc: "Updating the innerHTML",
+ test: function* (testActor) {
+ yield testActor.setProperty("#node2", "innerHTML",
+ "<div><span>foo</span></div>");
+ },
+ check: function* (inspector) {
+ let container = yield getContainerForSelector("#node2", inspector);
+
+ let openTags = container.children.querySelectorAll(".open .tag");
+ is(openTags.length, 2, "There are 2 tags in node2");
+ is(openTags[0].textContent.trim(), "div", "The first tag is a div");
+ is(openTags[1].textContent.trim(), "span", "The second tag is a span");
+
+ is(container.children.querySelector(".text").textContent.trim(), "foo",
+ "The span's textcontent is correct");
+ }
+ },
+ {
+ desc: "Removing child nodes",
+ test: function* (testActor) {
+ yield testActor.eval(`
+ let node4 = content.document.querySelector("#node4");
+ while (node4.firstChild) {
+ node4.removeChild(node4.firstChild);
+ }
+ `);
+ },
+ check: function* (inspector) {
+ let {children} = yield getContainerForSelector("#node4", inspector);
+ is(children.innerHTML, "", "Children have been removed");
+ }
+ },
+ {
+ desc: "Appending a child to a different parent",
+ test: function* (testActor) {
+ yield testActor.eval(`
+ let node17 = content.document.querySelector("#node17");
+ let node2 = content.document.querySelector("#node2");
+ node2.appendChild(node17);
+ `);
+ },
+ check: function* (inspector) {
+ let {children} = yield getContainerForSelector("#node16", inspector);
+ is(children.innerHTML, "",
+ "Node17 has been removed from its node16 parent");
+
+ let container = yield getContainerForSelector("#node2", inspector);
+ let openTags = container.children.querySelectorAll(".open .tag");
+ is(openTags.length, 3, "There are now 3 tags in node2");
+ is(openTags[2].textContent.trim(), "p", "The third tag is node17");
+ }
+ },
+ {
+ desc: "Swapping a parent and child element, putting them in the same tree",
+ // body
+ // node1
+ // node18
+ // node19
+ // node20
+ // node21
+ // will become:
+ // body
+ // node1
+ // node20
+ // node21
+ // node18
+ // node19
+ test: function* (testActor) {
+ yield testActor.eval(`
+ let node18 = content.document.querySelector("#node18");
+ let node20 = content.document.querySelector("#node20");
+ let node1 = content.document.querySelector("#node1");
+ node1.appendChild(node20);
+ node20.appendChild(node18);
+ `);
+ },
+ check: function* (inspector) {
+ yield inspector.markup.expandAll();
+
+ let {children} = yield getContainerForSelector("#node1", inspector);
+ is(children.childNodes.length, 2,
+ "Node1 now has 2 children (textnode and node20)");
+
+ let node20 = children.childNodes[1];
+ let node20Children = node20.container.children;
+ is(node20Children.childNodes.length, 2,
+ "Node20 has 2 children (21 and 18)");
+
+ let node21 = node20Children.childNodes[0];
+ is(node21.container.editor.elt.querySelector(".text").textContent.trim(),
+ "line21", "Node21 has a single text child");
+
+ let node18 = node20Children.childNodes[1];
+ is(node18.querySelector(".open .attreditor .attr-value")
+ .textContent.trim(),
+ "node18", "Node20's second child is indeed node18");
+ }
+ }
+];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Expanding all markup-view nodes");
+ yield inspector.markup.expandAll();
+
+ for (let {desc, test, check, numMutations} of TEST_DATA) {
+ info("Starting test: " + desc);
+
+ numMutations = numMutations || 1;
+
+ info("Executing the test markup mutation");
+
+ // If a test expects more than one mutation it may come through in a single
+ // event or possibly in multiples.
+ let def = defer();
+ let seenMutations = 0;
+ inspector.on("markupmutation", function onmutation(e, mutations) {
+ seenMutations += mutations.length;
+ info("Receieved " + seenMutations +
+ " mutations, expecting at least " + numMutations);
+ if (seenMutations >= numMutations) {
+ inspector.off("markupmutation", onmutation);
+ def.resolve();
+ }
+ });
+ yield test(testActor, inspector);
+ yield def.promise;
+
+ info("Expanding all markup-view nodes to make sure new nodes are imported");
+ yield inspector.markup.expandAll();
+
+ info("Checking the markup-view content");
+ yield check(inspector);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_mutation_02.js b/devtools/client/inspector/markup/test/browser_markup_mutation_02.js
new file mode 100644
index 000000000..eb69b4201
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_mutation_02.js
@@ -0,0 +1,159 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that markup-containers in the markup-view do flash when their
+// corresponding DOM nodes mutate
+
+// Have to use the same timer functions used by the inspector.
+const {clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
+
+const TEST_URL = URL_ROOT + "doc_markup_flashing.html";
+
+// The test data contains a list of mutations to test.
+// Each item is an object:
+// - desc: a description of the test step, for better logging
+// - mutate: a generator function that should make changes to the content DOM
+// - attribute: if set, the test will expect the corresponding attribute to
+// flash instead of the whole node
+// - flashedNode: [optional] the css selector of the node that is expected to
+// flash in the markup-view as a result of the mutation.
+// If missing, the rootNode (".list") will be expected to flash
+const TEST_DATA = [{
+ desc: "Adding a new node should flash the new node",
+ mutate: function* (testActor) {
+ yield testActor.eval(`
+ let newLi = content.document.createElement("LI");
+ newLi.textContent = "new list item";
+ content.document.querySelector(".list").appendChild(newLi);
+ `);
+ },
+ flashedNode: ".list li:nth-child(3)"
+}, {
+ desc: "Removing a node should flash its parent",
+ mutate: function* (testActor) {
+ yield testActor.eval(`
+ let root = content.document.querySelector(".list");
+ root.removeChild(root.lastElementChild);
+ `);
+ }
+}, {
+ desc: "Re-appending an existing node should only flash this node",
+ mutate: function* (testActor) {
+ yield testActor.eval(`
+ let root = content.document.querySelector(".list");
+ root.appendChild(root.firstElementChild);
+ `);
+ },
+ flashedNode: ".list .item:last-child"
+}, {
+ desc: "Adding an attribute should flash the attribute",
+ attribute: "test-name",
+ mutate: function* (testActor) {
+ yield testActor.setAttribute(".list", "test-name", "value-" + Date.now());
+ }
+}, {
+ desc: "Adding an attribute with css reserved characters should flash the " +
+ "attribute",
+ attribute: "one:two",
+ mutate: function* (testActor) {
+ yield testActor.setAttribute(".list", "one:two", "value-" + Date.now());
+ }
+}, {
+ desc: "Editing an attribute should flash the attribute",
+ attribute: "class",
+ mutate: function* (testActor) {
+ yield testActor.setAttribute(".list", "class", "list value-" + Date.now());
+ }
+}, {
+ desc: "Multiple changes to an attribute should flash the attribute",
+ attribute: "class",
+ mutate: function* (testActor) {
+ yield testActor.eval(`
+ let root = content.document.querySelector(".list");
+ root.removeAttribute("class");
+ root.setAttribute("class", "list value-" + Date.now());
+ root.setAttribute("class", "list value-" + Date.now());
+ root.removeAttribute("class");
+ root.setAttribute("class", "list value-" + Date.now());
+ root.setAttribute("class", "list value-" + Date.now());
+ `);
+ }
+}, {
+ desc: "Removing an attribute should flash the node",
+ mutate: function* (testActor) {
+ yield testActor.eval(`
+ let root = content.document.querySelector(".list");
+ root.removeAttribute("class");
+ `);
+ }
+}];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ // Make sure mutated nodes flash for a very long time so we can more easily
+ // assert they do
+ inspector.markup.CONTAINER_FLASHING_DURATION = 1000 * 60 * 60;
+
+ info("Getting the <ul.list> root node to test mutations on");
+ let rootNodeFront = yield getNodeFront(".list", inspector);
+
+ info("Selecting the last element of the root node before starting");
+ yield selectNode(".list .item:nth-child(2)", inspector);
+
+ for (let {mutate, flashedNode, desc, attribute} of TEST_DATA) {
+ info("Starting test: " + desc);
+
+ info("Mutating the DOM and listening for markupmutation event");
+ let onMutation = inspector.once("markupmutation");
+ yield mutate(testActor);
+ let mutations = yield onMutation;
+
+ info("Wait for the breadcrumbs widget to update if it needs to");
+ if (inspector.breadcrumbs._hasInterestingMutations(mutations)) {
+ yield inspector.once("breadcrumbs-updated");
+ }
+
+ info("Asserting that the correct markup-container is flashing");
+ let flashingNodeFront = rootNodeFront;
+ if (flashedNode) {
+ flashingNodeFront = yield getNodeFront(flashedNode, inspector);
+ }
+
+ if (attribute) {
+ yield assertAttributeFlashing(flashingNodeFront, attribute, inspector);
+ } else {
+ yield assertNodeFlashing(flashingNodeFront, inspector);
+ }
+ }
+});
+
+function* assertNodeFlashing(nodeFront, inspector) {
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ ok(container, "Markup container for node found");
+ ok(container.tagState.classList.contains("theme-bg-contrast"),
+ "Markup container for node is flashing");
+
+ // Clear the mutation flashing timeout now that we checked the node was
+ // flashing.
+ clearTimeout(container._flashMutationTimer);
+ container._flashMutationTimer = null;
+ container.tagState.classList.remove("theme-bg-contrast");
+}
+
+function* assertAttributeFlashing(nodeFront, attribute, inspector) {
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ ok(container, "Markup container for node found");
+ ok(container.editor.attrElements.get(attribute),
+ "Attribute exists on editor");
+
+ let attributeElement = container.editor.getAttributeElement(attribute);
+
+ ok(attributeElement.classList.contains("theme-bg-contrast"),
+ "Element for " + attribute + " attribute is flashing");
+
+ attributeElement.classList.remove("theme-bg-contrast");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_navigation.js b/devtools/client/inspector/markup/test/browser_markup_navigation.js
new file mode 100644
index 000000000..5bfd9719f
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_navigation.js
@@ -0,0 +1,147 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the markup-view nodes can be navigated to with the keyboard
+
+const TEST_URL = URL_ROOT + "doc_markup_navigation.html";
+const TEST_DATA = [
+ ["pageup", "*doctype*"],
+ ["down", "html"],
+ ["down", "head"],
+ ["down", "body"],
+ ["down", "node0"],
+ ["right", "node0"],
+ ["down", "node1"],
+ ["down", "node2"],
+ ["down", "node3"],
+ ["down", "*comment*"],
+ ["down", "node4"],
+ ["right", "node4"],
+ ["down", "*text*"],
+ ["down", "node5"],
+ ["down", "*text*"],
+ ["down", "node6"],
+ ["down", "*text*"],
+ ["down", "*comment*"],
+ ["down", "node7"],
+ ["right", "node7"],
+ ["down", "*text*"],
+ ["down", "node8"],
+ ["left", "node7"],
+ ["left", "node7"],
+ ["right", "node7"],
+ ["right", "*text*"],
+ ["down", "node8"],
+ ["down", "*text*"],
+ ["down", "node9"],
+ ["down", "*text*"],
+ ["down", "node10"],
+ ["down", "*text*"],
+ ["down", "node11"],
+ ["down", "*text*"],
+ ["down", "node12"],
+ ["right", "node12"],
+ ["down", "*text*"],
+ ["down", "node13"],
+ ["down", "node14"],
+ ["down", "node15"],
+ ["down", "node15"],
+ ["down", "node15"],
+ ["up", "node14"],
+ ["up", "node13"],
+ ["up", "*text*"],
+ ["up", "node12"],
+ ["left", "node12"],
+ ["down", "node14"],
+ ["home", "*doctype*"],
+ ["pagedown", "*text*"],
+ ["down", "node5"],
+ ["down", "*text*"],
+ ["down", "node6"],
+ ["down", "*text*"],
+ ["down", "*comment*"],
+ ["down", "node7"],
+ ["left", "node7"],
+ ["down", "*text*"],
+ ["down", "node9"],
+ ["down", "*text*"],
+ ["down", "node10"],
+ ["pageup", "*text*"],
+ ["pageup", "*doctype*"],
+ ["down", "html"],
+ ["left", "html"],
+ ["down", "head"]
+];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Making sure the markup-view frame is focused");
+ inspector.markup._frame.focus();
+
+ info("Starting to iterate through the test data");
+ for (let [key, className] of TEST_DATA) {
+ info("Testing step: " + key + " to navigate to " + className);
+ pressKey(key);
+
+ info("Making sure markup-view children get updated");
+ yield waitForChildrenUpdated(inspector);
+
+ info("Checking the right node is selected");
+ checkSelectedNode(key, className, inspector);
+ }
+
+ // In theory, we should wait for the inspector-updated event at each iteration
+ // of the previous loop where we expect the current node to change (because
+ // changing the current node ends up refreshing the rule-view, breadcrumbs,
+ // ...), but this would make this test a *lot* slower. Instead, having a final
+ // catch-all event works too.
+ yield inspector.once("inspector-updated");
+});
+
+function pressKey(key) {
+ switch (key) {
+ case "right":
+ EventUtils.synthesizeKey("VK_RIGHT", {});
+ break;
+ case "down":
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ break;
+ case "left":
+ EventUtils.synthesizeKey("VK_LEFT", {});
+ break;
+ case "up":
+ EventUtils.synthesizeKey("VK_UP", {});
+ break;
+ case "pageup":
+ EventUtils.synthesizeKey("VK_PAGE_UP", {});
+ break;
+ case "pagedown":
+ EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
+ break;
+ case "home":
+ EventUtils.synthesizeKey("VK_HOME", {});
+ break;
+ }
+}
+
+function checkSelectedNode(key, className, inspector) {
+ let node = inspector.selection.nodeFront;
+
+ if (className == "*comment*") {
+ is(node.nodeType, Node.COMMENT_NODE,
+ "Found a comment after pressing " + key);
+ } else if (className == "*text*") {
+ is(node.nodeType, Node.TEXT_NODE,
+ "Found text after pressing " + key);
+ } else if (className == "*doctype*") {
+ is(node.nodeType, Node.DOCUMENT_TYPE_NODE,
+ "Found the doctype after pressing " + key);
+ } else {
+ is(node.className, className,
+ "Found node: " + className + " after pressing " + key);
+ }
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_node_names.js b/devtools/client/inspector/markup/test/browser_markup_node_names.js
new file mode 100644
index 000000000..a8afad5e9
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_node_names.js
@@ -0,0 +1,28 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test element node name in the markupview
+const TEST_URL = URL_ROOT + "doc_markup_html_mixed_case.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ // Get and open the svg element to show its children
+ let svgNodeFront = yield getNodeFront("svg", inspector);
+ yield inspector.markup.expandNode(svgNodeFront);
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ let clipPathContainer = yield getContainerForSelector("clipPath", inspector);
+ info("Checking the clipPath element");
+ ok(clipPathContainer.editor.tag.textContent === "clipPath",
+ "clipPath node name is not lowercased");
+
+ let divContainer = yield getContainerForSelector("div", inspector);
+
+ info("Checking the div element");
+ ok(divContainer.editor.tag.textContent === "div",
+ "div node name is lowercased");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_node_names_namespaced.js b/devtools/client/inspector/markup/test/browser_markup_node_names_namespaced.js
new file mode 100644
index 000000000..261176f94
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_node_names_namespaced.js
@@ -0,0 +1,43 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test namespaced element node names in the markupview.
+
+const XHTML = `
+ <!DOCTYPE html>
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <body>
+ <svg:svg width="100" height="100">
+ <svg:clipPath id="clip">
+ <svg:rect id="rectangle" x="0" y="0" width="10" height="5"></svg:rect>
+ </svg:clipPath>
+ <svg:circle cx="0" cy="0" r="5"></svg:circle>
+ </svg:svg>
+ </body>
+ </html>
+`;
+
+const TEST_URI = "data:application/xhtml+xml;charset=utf-8," + encodeURI(XHTML);
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URI);
+
+ // Get and open the svg element to show its children.
+ let svgNodeFront = yield getNodeFront("svg", inspector);
+ yield inspector.markup.expandNode(svgNodeFront);
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ let clipPathContainer = yield getContainerForSelector("clipPath", inspector);
+ info("Checking the clipPath element");
+ ok(clipPathContainer.editor.tag.textContent === "svg:clipPath",
+ "svg:clipPath node is correctly displayed");
+
+ let circlePathContainer = yield getContainerForSelector("circle", inspector);
+ info("Checking the circle element");
+ ok(circlePathContainer.editor.tag.textContent === "svg:circle",
+ "svg:circle node is correctly displayed");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_01.js b/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_01.js
new file mode 100644
index 000000000..ea4ecdfd0
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_01.js
@@ -0,0 +1,35 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that nodes that are not displayed appear differently in the markup-view
+// when these nodes are imported in the view.
+
+// Note that nodes inside a display:none parent are obviously not displayed too
+// but the markup-view uses css inheritance to mark those as hidden instead of
+// having to visit each and every child of a hidden node. So there's no sense
+// testing children nodes.
+
+const TEST_URL = URL_ROOT + "doc_markup_not_displayed.html";
+const TEST_DATA = [
+ {selector: "#normal-div", isDisplayed: true},
+ {selector: "head", isDisplayed: false},
+ {selector: "#display-none", isDisplayed: false},
+ {selector: "#hidden-true", isDisplayed: false},
+ {selector: "#visibility-hidden", isDisplayed: true},
+ {selector: "#hidden-via-hide-shortcut", isDisplayed: false},
+];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ for (let {selector, isDisplayed} of TEST_DATA) {
+ info("Getting node " + selector);
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ is(!container.elt.classList.contains("not-displayed"), isDisplayed,
+ `The container for ${selector} is marked as displayed ${isDisplayed}`);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_02.js b/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_02.js
new file mode 100644
index 000000000..b0423d2e6
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_02.js
@@ -0,0 +1,150 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that nodes are marked as displayed and not-displayed dynamically, when
+// their display changes
+
+const TEST_URL = URL_ROOT + "doc_markup_not_displayed.html";
+const TEST_DATA = [
+ {
+ desc: "Hiding a node by creating a new stylesheet",
+ selector: "#normal-div",
+ before: true,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ let div = content.document.createElement("div");
+ div.id = "new-style";
+ div.innerHTML = "<style>#normal-div {display:none;}</style>";
+ content.document.body.appendChild(div);
+ `);
+ },
+ after: false
+ },
+ {
+ desc: "Showing a node by deleting an existing stylesheet",
+ selector: "#normal-div",
+ before: false,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ content.document.getElementById("new-style").remove();
+ `);
+ },
+ after: true
+ },
+ {
+ desc: "Hiding a node by changing its style property",
+ selector: "#display-none",
+ before: false,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ let node = content.document.querySelector("#display-none");
+ node.style.display = "block";
+ `);
+ },
+ after: true
+ },
+ {
+ desc: "Showing a node by removing its hidden attribute",
+ selector: "#hidden-true",
+ before: false,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ content.document.querySelector("#hidden-true")
+ .removeAttribute("hidden");
+ `);
+ },
+ after: true
+ },
+ {
+ desc: "Hiding a node by adding a hidden attribute",
+ selector: "#hidden-true",
+ before: true,
+ changeStyle: function* (testActor) {
+ yield testActor.setAttribute("#hidden-true", "hidden", "true");
+ },
+ after: false
+ },
+ {
+ desc: "Showing a node by changin a stylesheet's rule",
+ selector: "#hidden-via-stylesheet",
+ before: false,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ content.document.styleSheets[0]
+ .cssRules[0].style
+ .setProperty("display", "inline");
+ `);
+ },
+ after: true
+ },
+ {
+ desc: "Hiding a node by adding a new rule to a stylesheet",
+ selector: "#hidden-via-stylesheet",
+ before: true,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ content.document.styleSheets[0].insertRule(
+ "#hidden-via-stylesheet {display: none;}", 1);
+ `);
+ },
+ after: false
+ },
+ {
+ desc: "Hiding a node by adding a class that matches an existing rule",
+ selector: "#normal-div",
+ before: true,
+ changeStyle: function* (testActor) {
+ yield testActor.eval(`
+ content.document.styleSheets[0].insertRule(
+ ".a-new-class {display: none;}", 2);
+ content.document.querySelector("#normal-div")
+ .classList.add("a-new-class");
+ `);
+ },
+ after: false
+ }
+];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ for (let data of TEST_DATA) {
+ info("Running test case: " + data.desc);
+ yield runTestData(inspector, testActor, data);
+ }
+});
+
+function* runTestData(inspector, testActor,
+ {selector, before, changeStyle, after}) {
+ info("Getting the " + selector + " test node");
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ is(!container.elt.classList.contains("not-displayed"), before,
+ "The container is marked as " + (before ? "shown" : "hidden"));
+
+ info("Listening for the display-change event");
+ let onDisplayChanged = defer();
+ inspector.markup.walker.once("display-change", onDisplayChanged.resolve);
+
+ info("Making style changes");
+ yield changeStyle(testActor);
+ let nodes = yield onDisplayChanged.promise;
+
+ info("Verifying that the list of changed nodes include our container");
+
+ ok(nodes.length, "The display-change event was received with a nodes");
+ let foundContainer = false;
+ for (let node of nodes) {
+ if (getContainerForNodeFront(node, inspector) === container) {
+ foundContainer = true;
+ break;
+ }
+ }
+ ok(foundContainer, "Container is part of the list of changed nodes");
+
+ is(!container.elt.classList.contains("not-displayed"), after,
+ "The container is marked as " + (after ? "shown" : "hidden"));
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_pagesize_01.js b/devtools/client/inspector/markup/test/browser_markup_pagesize_01.js
new file mode 100644
index 000000000..a9ba9fc05
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_pagesize_01.js
@@ -0,0 +1,86 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the markup view loads only as many nodes as specified by the
+// devtools.markup.pagesize preference.
+
+Services.prefs.setIntPref("devtools.markup.pagesize", 5);
+
+const TEST_URL = URL_ROOT + "doc_markup_pagesize_01.html";
+const TEST_DATA = [{
+ desc: "Select the last item",
+ selector: "#z",
+ expected: "*more*vwxyz"
+}, {
+ desc: "Select the first item",
+ selector: "#a",
+ expected: "abcde*more*"
+}, {
+ desc: "Select the last item",
+ selector: "#z",
+ expected: "*more*vwxyz"
+}, {
+ desc: "Select an already-visible item",
+ selector: "#v",
+ // Because "v" was already visible, we shouldn't have loaded
+ // a different page.
+ expected: "*more*vwxyz"
+}, {
+ desc: "Verify childrenDirty reloads the page",
+ selector: "#w",
+ forceReload: true,
+ // But now that we don't already have a loaded page, selecting
+ // w should center around w.
+ expected: "*more*uvwxy*more*"
+}];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Start iterating through the test data");
+ for (let step of TEST_DATA) {
+ info("Start test: " + step.desc);
+
+ if (step.forceReload) {
+ yield forceReload(inspector);
+ }
+ info("Selecting the node that corresponds to " + step.selector);
+ yield selectNode(step.selector, inspector);
+
+ info("Checking that the right nodes are shwon");
+ yield assertChildren(step.expected, inspector);
+ }
+
+ info("Checking that clicking the more button loads everything");
+ yield clickShowMoreNodes(inspector);
+ yield inspector.markup._waitForChildren();
+ yield assertChildren("abcdefghijklmnopqrstuvwxyz", inspector);
+});
+
+function* assertChildren(expected, inspector) {
+ let container = yield getContainerForSelector("body", inspector);
+ let found = "";
+ for (let child of container.children.children) {
+ if (child.classList.contains("more-nodes")) {
+ found += "*more*";
+ } else {
+ found += child.container.node.getAttribute("id");
+ }
+ }
+ is(found, expected, "Got the expected children.");
+}
+
+function* forceReload(inspector) {
+ let container = yield getContainerForSelector("body", inspector);
+ container.childrenDirty = true;
+}
+
+function* clickShowMoreNodes(inspector) {
+ let container = yield getContainerForSelector("body", inspector);
+ let button = container.elt.querySelector("button");
+ let win = button.ownerDocument.defaultView;
+ EventUtils.sendMouseEvent({type: "click"}, button, win);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_pagesize_02.js b/devtools/client/inspector/markup/test/browser_markup_pagesize_02.js
new file mode 100644
index 000000000..549a36b0d
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_pagesize_02.js
@@ -0,0 +1,47 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the markup view loads only as many nodes as specified
+// by the devtools.markup.pagesize preference and that pressing the "show all
+// nodes" actually shows the nodes
+
+const TEST_URL = URL_ROOT + "doc_markup_pagesize_02.html";
+
+// Make sure nodes are hidden when there are more than 5 in a row
+Services.prefs.setIntPref("devtools.markup.pagesize", 5);
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Selecting the UL node");
+ yield clickContainer("ul", inspector);
+ info("Reloading the page with the UL node selected will expand its children");
+ yield reloadPage(inspector, testActor);
+ yield inspector.markup._waitForChildren();
+
+ info("Click on the 'show all nodes' button in the UL's list of children");
+ yield showAllNodes(inspector);
+
+ yield assertAllNodesAreVisible(inspector, testActor);
+});
+
+function* showAllNodes(inspector) {
+ let container = yield getContainerForSelector("ul", inspector);
+ let button = container.elt.querySelector("button");
+ ok(button, "All nodes button is here");
+ let win = button.ownerDocument.defaultView;
+
+ EventUtils.sendMouseEvent({type: "click"}, button, win);
+ yield inspector.markup._waitForChildren();
+}
+
+function* assertAllNodesAreVisible(inspector, testActor) {
+ let container = yield getContainerForSelector("ul", inspector);
+ ok(!container.elt.querySelector("button"),
+ "All nodes button isn't here anymore");
+ let numItems = yield testActor.getNumberOfElementMatches("ul > *");
+ is(container.children.childNodes.length, numItems);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_remove_xul_attributes.js b/devtools/client/inspector/markup/test/browser_markup_remove_xul_attributes.js
new file mode 100644
index 000000000..b7065c683
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_remove_xul_attributes.js
@@ -0,0 +1,28 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test confirms that XUL attributes don't show up as empty
+// attributes after being deleted
+
+const TEST_URL = URL_ROOT + "doc_markup_xul.xul";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ let panelFront = yield getNodeFront("#test", inspector);
+ ok(panelFront.hasAttribute("id"),
+ "panelFront has id attribute in the beginning");
+
+ info("Removing panel's id attribute");
+ let onMutation = inspector.once("markupmutation");
+ yield testActor.removeAttribute("#test", "id");
+
+ info("Waiting for markupmutation");
+ yield onMutation;
+
+ is(panelFront.hasAttribute("id"), false,
+ "panelFront doesn't have id attribute anymore");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_search_01.js b/devtools/client/inspector/markup/test/browser_markup_search_01.js
new file mode 100644
index 000000000..68f0c04db
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_search_01.js
@@ -0,0 +1,51 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that searching for nodes using the selector-search input expands and
+// selects the right nodes in the markup-view, even when those nodes are deeply
+// nested (and therefore not attached yet when the markup-view is initialized).
+
+const TEST_URL = URL_ROOT + "doc_markup_search.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ let container = yield getContainerForSelector("em", inspector);
+ ok(!container, "The <em> tag isn't present yet in the markup-view");
+
+ // Searching for the innermost element first makes sure that the inspector
+ // back-end is able to attach the resulting node to the tree it knows at the
+ // moment. When the inspector is started, the <body> is the default selected
+ // node, and only the parents up to the ROOT are known, and its direct
+ // children.
+ info("searching for the innermost child: <em>");
+ yield searchFor("em", inspector);
+
+ container = yield getContainerForSelector("em", inspector);
+ ok(container, "The <em> tag is now imported in the markup-view");
+
+ let nodeFront = yield getNodeFront("em", inspector);
+ is(inspector.selection.nodeFront, nodeFront,
+ "The <em> tag is the currently selected node");
+
+ info("searching for other nodes too");
+ for (let node of ["span", "li", "ul"]) {
+ yield searchFor(node, inspector);
+
+ nodeFront = yield getNodeFront(node, inspector);
+ is(inspector.selection.nodeFront, nodeFront,
+ "The <" + node + "> tag is the currently selected node");
+ }
+});
+
+function* searchFor(selector, inspector) {
+ let onNewNodeFront = inspector.selection.once("new-node-front");
+
+ searchUsingSelectorSearch(selector, inspector);
+
+ yield onNewNodeFront;
+ yield inspector.once("inspector-updated");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_01.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_01.js
new file mode 100644
index 000000000..b1b4f7115
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_01.js
@@ -0,0 +1,68 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_attributes_test_runner.js */
+"use strict";
+
+// Test editing various markup-containers' attribute fields
+
+loadHelperScript("helper_attributes_test_runner.js");
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+var TEST_DATA = [{
+ desc: "Change an attribute",
+ node: "#node1",
+ originalAttributes: {
+ id: "node1",
+ class: "node1"
+ },
+ name: "class",
+ value: 'class="changednode1"',
+ expectedAttributes: {
+ id: "node1",
+ class: "changednode1"
+ }
+}, {
+ desc: "Try changing an attribute to a quote (\") - this should result " +
+ "in it being set to an empty string",
+ node: "#node22",
+ originalAttributes: {
+ id: "node22",
+ class: "unchanged"
+ },
+ name: "class",
+ value: 'class="""',
+ expectedAttributes: {
+ id: "node22",
+ class: ""
+ }
+}, {
+ desc: "Remove an attribute",
+ node: "#node4",
+ originalAttributes: {
+ id: "node4",
+ class: "node4"
+ },
+ name: "class",
+ value: "",
+ expectedAttributes: {
+ id: "node4"
+ }
+}, {
+ desc: "Try add attributes by adding to an existing attribute's entry",
+ node: "#node24",
+ originalAttributes: {
+ id: "node24"
+ },
+ name: "id",
+ value: 'id="node24" class="""',
+ expectedAttributes: {
+ id: "node24",
+ class: ""
+ }
+}];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ yield runEditAttributesTests(TEST_DATA, inspector, testActor);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_02.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_02.js
new file mode 100644
index 000000000..1e32d783a
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_02.js
@@ -0,0 +1,44 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that an existing attribute can be modified
+
+const TEST_URL = `data:text/html,
+ <div id='test-div'>Test modifying my ID attribute</div>`;
+
+add_task(function* () {
+ info("Opening the inspector on the test page");
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Selecting the test node");
+ yield focusNode("#test-div", inspector);
+
+ info("Verify attributes, only ID should be there for now");
+ yield assertAttributes("#test-div", {
+ id: "test-div"
+ }, testActor);
+
+ info("Focus the ID attribute and change its content");
+ let {editor} = yield getContainerForSelector("#test-div", inspector);
+ let attr = editor.attrElements.get("id").querySelector(".editable");
+ let mutated = inspector.once("markupmutation");
+ setEditableFieldValue(attr,
+ attr.textContent + ' class="newclass" style="color:green"', inspector);
+ yield mutated;
+
+ info("Verify attributes, should have ID, class and style");
+ yield assertAttributes("#test-div", {
+ id: "test-div",
+ class: "newclass",
+ style: "color:green"
+ }, testActor);
+
+ info("Trying to undo the change");
+ yield undoChange(inspector);
+ yield assertAttributes("#test-div", {
+ id: "test-div"
+ }, testActor);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_03.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_03.js
new file mode 100644
index 000000000..cdbdc72b6
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_03.js
@@ -0,0 +1,51 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a node's tagname can be edited in the markup-view
+
+const TEST_URL = `data:text/html;charset=utf-8,
+ <div id='retag-me'><div id='retag-me-2'></div></div>`;
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ yield inspector.markup.expandAll();
+
+ info("Selecting the test node");
+ yield focusNode("#retag-me", inspector);
+
+ info("Getting the markup-container for the test node");
+ let container = yield getContainerForSelector("#retag-me", inspector);
+ ok(container.expanded, "The container is expanded");
+
+ let parentInfo = yield testActor.getNodeInfo("#retag-me");
+ is(parentInfo.tagName.toLowerCase(), "div",
+ "We've got #retag-me element, it's a DIV");
+ is(parentInfo.numChildren, 1, "#retag-me has one child");
+ let childInfo = yield testActor.getNodeInfo("#retag-me > *");
+ is(childInfo.attributes[0].value, "retag-me-2",
+ "#retag-me's only child is #retag-me-2");
+
+ info("Changing #retag-me's tagname in the markup-view");
+ let mutated = inspector.once("markupmutation");
+ let tagEditor = container.editor.tag;
+ setEditableFieldValue(tagEditor, "p", inspector);
+ yield mutated;
+
+ info("Checking that the markup-container exists and is correct");
+ container = yield getContainerForSelector("#retag-me", inspector);
+ ok(container.expanded, "The container is still expanded");
+ ok(container.selected, "The container is still selected");
+
+ info("Checking that the tagname change was done");
+ parentInfo = yield testActor.getNodeInfo("#retag-me");
+ is(parentInfo.tagName.toLowerCase(), "p",
+ "The #retag-me element is now a P");
+ is(parentInfo.numChildren, 1, "#retag-me still has one child");
+ childInfo = yield testActor.getNodeInfo("#retag-me > *");
+ is(childInfo.attributes[0].value, "retag-me-2",
+ "#retag-me's only child is #retag-me-2");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_04-backspace.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_04-backspace.js
new file mode 100644
index 000000000..dbe718f45
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_04-backspace.js
@@ -0,0 +1,59 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a node can be deleted from the markup-view with the backspace key.
+// Also checks that after deletion the correct element is highlighted.
+// The previous sibling is preferred, but the parent is a fallback.
+
+const HTML = `<style type="text/css">
+ #pseudo::before { content: 'before'; }
+ #pseudo::after { content: 'after'; }
+ </style>
+ <div id="parent">
+ <div id="first"></div>
+ <div id="second"></div>
+ <div id="third"></div>
+ </div>
+ <div id="only-child">
+ <div id="fourth"></div>
+ </div>
+ <div id="pseudo">
+ <div id="fifth"></div>
+ </div>`;
+const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
+
+// List of all the test cases. Each item is an object with the following props:
+// - selector: the css selector of the node that should be selected
+// - focusedSelector: the css selector of the node we expect to be selected as
+// a result of the deletion
+// - pseudo: (optional) if the focused node is actually supposed to be a pseudo element
+// of the specified selector.
+// Note that after each test case, undo is called.
+const TEST_DATA = [{
+ selector: "#first",
+ focusedSelector: "#second"
+}, {
+ selector: "#second",
+ focusedSelector: "#first"
+}, {
+ selector: "#third",
+ focusedSelector: "#second"
+}, {
+ selector: "#fourth",
+ focusedSelector: "#only-child"
+}, {
+ selector: "#fifth",
+ focusedSelector: "#pseudo",
+ pseudo: "before"
+}];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ for (let data of TEST_DATA) {
+ yield checkDeleteAndSelection(inspector, "back_space", data);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_04-delete.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_04-delete.js
new file mode 100644
index 000000000..1446eba30
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_04-delete.js
@@ -0,0 +1,59 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a node can be deleted from the markup-view with the delete key.
+// Also checks that after deletion the correct element is highlighted.
+// The next sibling is preferred, but the parent is a fallback.
+
+const HTML = `<style type="text/css">
+ #pseudo::before { content: 'before'; }
+ #pseudo::after { content: 'after'; }
+ </style>
+ <div id="parent">
+ <div id="first"></div>
+ <div id="second"></div>
+ <div id="third"></div>
+ </div>
+ <div id="only-child">
+ <div id="fourth"></div>
+ </div>
+ <div id="pseudo">
+ <div id="fifth"></div>
+ </div>`;
+const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
+
+// List of all the test cases. Each item is an object with the following props:
+// - selector: the css selector of the node that should be selected
+// - focusedSelector: the css selector of the node we expect to be selected as
+// a result of the deletion
+// - pseudo: (optional) if the focused node is actually supposed to be a pseudo element
+// of the specified selector.
+// Note that after each test case, undo is called.
+const TEST_DATA = [{
+ selector: "#first",
+ focusedSelector: "#second"
+}, {
+ selector: "#second",
+ focusedSelector: "#third"
+}, {
+ selector: "#third",
+ focusedSelector: "#second"
+}, {
+ selector: "#fourth",
+ focusedSelector: "#only-child"
+}, {
+ selector: "#fifth",
+ focusedSelector: "#pseudo",
+ pseudo: "after"
+}];
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ for (let data of TEST_DATA) {
+ yield checkDeleteAndSelection(inspector, "delete", data);
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_05.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_05.js
new file mode 100644
index 000000000..54a1dab44
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_05.js
@@ -0,0 +1,77 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_attributes_test_runner.js */
+"use strict";
+
+// Tests that adding various types of attributes to nodes in the markup-view
+// works as expected. Also checks that the changes are properly undoable and
+// redoable. For each step in the test, we:
+// - Create a new DIV
+// - Make the change, check that the change was made as we expect
+// - Undo the change, check that the node is back in its original state
+// - Redo the change, check that the node change was made again correctly.
+
+loadHelperScript("helper_attributes_test_runner.js");
+
+var TEST_URL = "data:text/html,<div>markup-view attributes addition test</div>";
+var TEST_DATA = [{
+ desc: "Add an attribute value without closing \"",
+ text: 'style="display: block;',
+ expectedAttributes: {
+ style: "display: block;"
+ }
+}, {
+ desc: "Add an attribute value without closing '",
+ text: "style='display: inline;",
+ expectedAttributes: {
+ style: "display: inline;"
+ }
+}, {
+ desc: "Add an attribute wrapped with with double quotes double quote in it",
+ text: 'style="display: "inline',
+ expectedAttributes: {
+ style: "display: ",
+ inline: ""
+ }
+}, {
+ desc: "Add an attribute wrapped with single quotes with single quote in it",
+ text: "style='display: 'inline",
+ expectedAttributes: {
+ style: "display: ",
+ inline: ""
+ }
+}, {
+ desc: "Add an attribute with no value",
+ text: "disabled",
+ expectedAttributes: {
+ disabled: ""
+ }
+}, {
+ desc: "Add multiple attributes with no value",
+ text: "disabled autofocus",
+ expectedAttributes: {
+ disabled: "",
+ autofocus: ""
+ }
+}, {
+ desc: "Add multiple attributes with no value, and some with value",
+ text: "disabled name='name' data-test='test' autofocus",
+ expectedAttributes: {
+ disabled: "",
+ autofocus: "",
+ name: "name",
+ "data-test": "test"
+ }
+}, {
+ desc: "Add attribute with xmlns",
+ text: "xmlns:edi='http://ecommerce.example.org/schema'",
+ expectedAttributes: {
+ "xmlns:edi": "http://ecommerce.example.org/schema"
+ }
+}];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ yield runAddAttributesTests(TEST_DATA, "div", inspector, testActor);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_06.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_06.js
new file mode 100644
index 000000000..8202bd0a2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_06.js
@@ -0,0 +1,85 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_attributes_test_runner.js */
+"use strict";
+
+// Tests that adding various types of attributes to nodes in the markup-view
+// works as expected. Also checks that the changes are properly undoable and
+// redoable. For each step in the test, we:
+// - Create a new DIV
+// - Make the change, check that the change was made as we expect
+// - Undo the change, check that the node is back in its original state
+// - Redo the change, check that the node change was made again correctly.
+
+loadHelperScript("helper_attributes_test_runner.js");
+
+var TEST_URL = "data:text/html,<div>markup-view attributes addition test</div>";
+var TEST_DATA = [{
+ desc: "Mixed single and double quotes",
+ text: "name=\"hi\" maxlength='not a number'",
+ expectedAttributes: {
+ maxlength: "not a number",
+ name: "hi"
+ }
+}, {
+ desc: "Invalid attribute name",
+ text: "x='y' <why-would-you-do-this>=\"???\"",
+ expectedAttributes: {
+ x: "y"
+ }
+}, {
+ desc: "Double quote wrapped in single quotes",
+ text: "x='h\"i'",
+ expectedAttributes: {
+ x: "h\"i"
+ }
+}, {
+ desc: "Single quote wrapped in double quotes",
+ text: "x=\"h'i\"",
+ expectedAttributes: {
+ x: "h'i"
+ }
+}, {
+ desc: "No quote wrapping",
+ text: "a=b x=y data-test=Some spaced data",
+ expectedAttributes: {
+ a: "b",
+ x: "y",
+ "data-test": "Some",
+ spaced: "",
+ data: ""
+ }
+}, {
+ desc: "Duplicate Attributes",
+ text: "a=b a='c' a=\"d\"",
+ expectedAttributes: {
+ a: "b"
+ }
+}, {
+ desc: "Inline styles",
+ text: "style=\"font-family: 'Lucida Grande', sans-serif; font-size: 75%;\"",
+ expectedAttributes: {
+ style: "font-family: 'Lucida Grande', sans-serif; font-size: 75%;"
+ }
+}, {
+ desc: "Object attribute names",
+ text: "toString=\"true\" hasOwnProperty=\"false\"",
+ expectedAttributes: {
+ tostring: "true",
+ hasownproperty: "false"
+ }
+}, {
+ desc: "Add event handlers",
+ text: "onclick=\"javascript: throw new Error('wont fire');\" " +
+ "onload=\"alert('here');\"",
+ expectedAttributes: {
+ onclick: "javascript: throw new Error('wont fire');",
+ onload: "alert('here');"
+ }
+}];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ yield runAddAttributesTests(TEST_DATA, "div", inspector, testActor);
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js
new file mode 100644
index 000000000..fffdc99cc
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_07.js
@@ -0,0 +1,135 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_attributes_test_runner.js */
+"use strict";
+
+// One more test testing various add-attributes configurations
+// Some of the test data below asserts that long attributes get collapsed
+
+loadHelperScript("helper_attributes_test_runner.js");
+
+/*eslint-disable */
+const LONG_ATTRIBUTE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const LONG_ATTRIBUTE_COLLAPSED = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEF\u2026UVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const DATA_URL_INLINE_STYLE='color: red; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");';
+const DATA_URL_INLINE_STYLE_COLLAPSED='color: red; background: url("data:image/png;base64,iVBORw0KG\u2026NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");';
+const DATA_URL_ATTRIBUTE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC";
+const DATA_URL_ATTRIBUTE_COLLAPSED = "data:image/png;base64,iVBORw0K\u20269/AFGGFyjOXZtQAAAAAElFTkSuQmCC";
+/*eslint-enable */
+
+var TEST_URL = "data:text/html,<div>markup-view attributes addition test</div>";
+var TEST_DATA = [{
+ desc: "Add an attribute value containing < > &uuml; \" & '",
+ text: 'src="somefile.html?param1=<a>&param2=&uuml;&param3=\'&quot;\'"',
+ expectedAttributes: {
+ src: "somefile.html?param1=<a>&param2=\xfc&param3='\"'"
+ }
+}, {
+ desc: "Add an attribute by clicking the empty space after a node",
+ text: 'class="newclass" style="color:green"',
+ expectedAttributes: {
+ class: "newclass",
+ style: "color:green"
+ }
+}, {
+ desc: "Try add an attribute containing a quote (\") attribute by " +
+ "clicking the empty space after a node - this should result " +
+ "in it being set to an empty string",
+ text: 'class="newclass" style="""',
+ expectedAttributes: {
+ class: "newclass",
+ style: ""
+ }
+}, {
+ desc: "Try to add long data URL to make sure it is collapsed in attribute " +
+ "editor.",
+ text: `style='${DATA_URL_INLINE_STYLE}'`,
+ expectedAttributes: {
+ "style": DATA_URL_INLINE_STYLE
+ },
+ validate: (container, inspector) => {
+ let editor = container.editor;
+ let visibleAttrText = editor.attrElements.get("style")
+ .querySelector(".attr-value")
+ .textContent;
+ is(visibleAttrText, DATA_URL_INLINE_STYLE_COLLAPSED);
+ }
+}, {
+ desc: "Try to add long attribute to make sure it is collapsed in attribute " +
+ "editor.",
+ text: `data-long="${LONG_ATTRIBUTE}"`,
+ expectedAttributes: {
+ "data-long": LONG_ATTRIBUTE
+ },
+ validate: (container, inspector) => {
+ let editor = container.editor;
+ let visibleAttrText = editor.attrElements.get("data-long")
+ .querySelector(".attr-value")
+ .textContent;
+ is(visibleAttrText, LONG_ATTRIBUTE_COLLAPSED);
+ }
+}, {
+ desc: "Try to add long data URL to make sure it is collapsed in attribute " +
+ "editor.",
+ text: `src="${DATA_URL_ATTRIBUTE}"`,
+ expectedAttributes: {
+ "src": DATA_URL_ATTRIBUTE
+ },
+ validate: (container, inspector) => {
+ let editor = container.editor;
+ let visibleAttrText = editor.attrElements.get("src")
+ .querySelector(".attr-value").textContent;
+ is(visibleAttrText, DATA_URL_ATTRIBUTE_COLLAPSED);
+ }
+}, {
+ desc: "Try to add long attribute with collapseAttributes == false" +
+ "to make sure it isn't collapsed in attribute editor.",
+ text: `data-long="${LONG_ATTRIBUTE}"`,
+ expectedAttributes: {
+ "data-long": LONG_ATTRIBUTE
+ },
+ setUp: function (inspector) {
+ Services.prefs.setBoolPref("devtools.markup.collapseAttributes", false);
+ },
+ validate: (container, inspector) => {
+ let editor = container.editor;
+ let visibleAttrText = editor.attrElements
+ .get("data-long")
+ .querySelector(".attr-value")
+ .textContent;
+ is(visibleAttrText, LONG_ATTRIBUTE);
+ },
+ tearDown: function (inspector) {
+ Services.prefs.clearUserPref("devtools.markup.collapseAttributes");
+ }
+}, {
+ desc: "Try to collapse attributes with collapseAttributeLength == 5",
+ text: `data-long="${LONG_ATTRIBUTE}"`,
+ expectedAttributes: {
+ "data-long": LONG_ATTRIBUTE
+ },
+ setUp: function (inspector) {
+ Services.prefs.setIntPref("devtools.markup.collapseAttributeLength", 2);
+ },
+ validate: (container, inspector) => {
+ let firstChar = LONG_ATTRIBUTE[0];
+ let lastChar = LONG_ATTRIBUTE[LONG_ATTRIBUTE.length - 1];
+ let collapsed = firstChar + "\u2026" + lastChar;
+ let editor = container.editor;
+ let visibleAttrText = editor.attrElements
+ .get("data-long")
+ .querySelector(".attr-value")
+ .textContent;
+ is(visibleAttrText, collapsed);
+ },
+ tearDown: function (inspector) {
+ Services.prefs.clearUserPref("devtools.markup.collapseAttributeLength");
+ }
+}];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ yield runAddAttributesTests(TEST_DATA, "div", inspector, testActor);
+});
+
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_08.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_08.js
new file mode 100644
index 000000000..238e59c52
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_08.js
@@ -0,0 +1,132 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test editing various markup-containers' attribute fields, in particular
+// attributes with long values and quotes
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+/*eslint-disable */
+const LONG_ATTRIBUTE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+const LONG_ATTRIBUTE_COLLAPSED = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEF\u2026UVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+/*eslint-enable */
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ yield inspector.markup.expandAll();
+ yield testCollapsedLongAttribute(inspector, testActor);
+ yield testModifyInlineStyleWithQuotes(inspector, testActor);
+ yield testEditingAttributeWithMixedQuotes(inspector, testActor);
+});
+
+function* testCollapsedLongAttribute(inspector, testActor) {
+ info("Try to modify the collapsed long attribute, making sure it expands.");
+
+ info("Adding test attributes to the node");
+ let onMutated = inspector.once("markupmutation");
+ yield testActor.setAttribute("#node24", "class", "");
+ yield testActor.setAttribute("#node24", "data-long", LONG_ATTRIBUTE);
+ yield onMutated;
+
+ yield assertAttributes("#node24", {
+ id: "node24",
+ "class": "",
+ "data-long": LONG_ATTRIBUTE
+ }, testActor);
+
+ let {editor} = yield focusNode("#node24", inspector);
+ let attr = editor.attrElements.get("data-long").querySelector(".editable");
+
+ // Check to make sure it has expanded after focus
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+ let input = inplaceEditor(attr).input;
+ is(input.value, `data-long="${LONG_ATTRIBUTE}"`);
+ EventUtils.sendKey("escape", inspector.panelWin);
+
+ setEditableFieldValue(attr, input.value + ' data-short="ABC"', inspector);
+ yield inspector.once("markupmutation");
+
+ let visibleAttrText = editor.attrElements.get("data-long")
+ .querySelector(".attr-value").textContent;
+ is(visibleAttrText, LONG_ATTRIBUTE_COLLAPSED);
+
+ yield assertAttributes("#node24", {
+ id: "node24",
+ class: "",
+ "data-long": LONG_ATTRIBUTE,
+ "data-short": "ABC"
+ }, testActor);
+}
+
+function* testModifyInlineStyleWithQuotes(inspector, testActor) {
+ info("Modify inline style containing \"");
+
+ yield assertAttributes("#node26", {
+ id: "node26",
+ style: 'background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F");'
+ }, testActor);
+
+ let onMutated = inspector.once("markupmutation");
+ let {editor} = yield focusNode("#node26", inspector);
+ let attr = editor.attrElements.get("style").querySelector(".editable");
+
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ let input = inplaceEditor(attr).input;
+ let value = input.value;
+
+ is(value,
+ "style='background-image: url(\"moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F\");'",
+ "Value contains actual double quotes"
+ );
+
+ value = value.replace(/mozilla\.org/, "mozilla.com");
+ input.value = value;
+
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ yield onMutated;
+
+ yield assertAttributes("#node26", {
+ id: "node26",
+ style: 'background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.com%2F");'
+ }, testActor);
+}
+
+function* testEditingAttributeWithMixedQuotes(inspector, testActor) {
+ info("Modify class containing \" and \'");
+
+ yield assertAttributes("#node27", {
+ "id": "node27",
+ "class": 'Double " and single \''
+ }, testActor);
+
+ let onMutated = inspector.once("markupmutation");
+ let {editor} = yield focusNode("#node27", inspector);
+ let attr = editor.attrElements.get("class").querySelector(".editable");
+
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ let input = inplaceEditor(attr).input;
+ let value = input.value;
+
+ is(value, "class=\"Double &quot; and single '\"", "Value contains &quot;");
+
+ value = value.replace(/Double/, "&quot;").replace(/single/, "'");
+ input.value = value;
+
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ yield onMutated;
+
+ yield assertAttributes("#node27", {
+ id: "node27",
+ class: '" " and \' \''
+ }, testActor);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_09.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_09.js
new file mode 100644
index 000000000..8680ab9f5
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_09.js
@@ -0,0 +1,71 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that editing a mixed-case attribute preserves the case
+
+const TEST_URL = URL_ROOT + "doc_markup_svg_attributes.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ yield inspector.markup.expandAll();
+ yield selectNode("svg", inspector);
+
+ yield testWellformedMixedCase(inspector, testActor);
+ yield testMalformedMixedCase(inspector, testActor);
+});
+
+function* testWellformedMixedCase(inspector, testActor) {
+ info("Modifying a mixed-case attribute, " +
+ "expecting the attribute's case to be preserved");
+
+ info("Listening to markup mutations");
+ let onMutated = inspector.once("markupmutation");
+
+ info("Focusing the viewBox attribute editor");
+ let {editor} = yield focusNode("svg", inspector);
+ let attr = editor.attrElements.get("viewBox").querySelector(".editable");
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ info("Editing the attribute value and waiting for the mutation event");
+ let input = inplaceEditor(attr).input;
+ input.value = "viewBox=\"0 0 1 1\"";
+ EventUtils.sendKey("return", inspector.panelWin);
+ yield onMutated;
+
+ yield assertAttributes("svg", {
+ "viewBox": "0 0 1 1",
+ "width": "200",
+ "height": "200"
+ }, testActor);
+}
+
+function* testMalformedMixedCase(inspector, testActor) {
+ info("Modifying a malformed, mixed-case attribute, " +
+ "expecting the attribute's case to be preserved");
+
+ info("Listening to markup mutations");
+ let onMutated = inspector.once("markupmutation");
+
+ info("Focusing the viewBox attribute editor");
+ let {editor} = yield focusNode("svg", inspector);
+ let attr = editor.attrElements.get("viewBox").querySelector(".editable");
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ info("Editing the attribute value and waiting for the mutation event");
+ let input = inplaceEditor(attr).input;
+ input.value = "viewBox=\"<>\"";
+ EventUtils.sendKey("return", inspector.panelWin);
+ yield onMutated;
+
+ yield assertAttributes("svg", {
+ "viewBox": "<>",
+ "width": "200",
+ "height": "200"
+ }, testActor);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_10.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_10.js
new file mode 100644
index 000000000..8d27c5468
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_10.js
@@ -0,0 +1,34 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that invalid tagname updates are handled correctly
+
+const TEST_URL = "data:text/html;charset=utf-8,<div></div>";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ yield inspector.markup.expandAll();
+
+ info("Updating the DIV tagname to an invalid value");
+ let container = yield focusNode("div", inspector);
+ let onCancelReselect = inspector.markup.once("canceledreselectonremoved");
+ let tagEditor = container.editor.tag;
+ setEditableFieldValue(tagEditor, "<<<", inspector);
+ yield onCancelReselect;
+ ok(true, "The markup-view emitted the canceledreselectonremoved event");
+ is(inspector.selection.nodeFront, container.node,
+ "The test DIV is still selected");
+
+ info("Updating the DIV tagname to a valid value this time");
+ let onReselect = inspector.markup.once("reselectedonremoved");
+ setEditableFieldValue(tagEditor, "span", inspector);
+ yield onReselect;
+ ok(true, "The markup-view emitted the reselectedonremoved event");
+
+ let spanFront = yield getNodeFront("span", inspector);
+ is(inspector.selection.nodeFront, spanFront,
+ "The selected node is now the SPAN");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_11.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_11.js
new file mode 100644
index 000000000..906c4aced
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_11.js
@@ -0,0 +1,38 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Bug 1090874 - Tests that a node is not recreated when it's tagname editor
+// is blurred and no changes were done.
+
+const TEST_URL = "data:text/html;charset=utf-8,<div></div>";
+
+add_task(function* () {
+ let isEditTagNameCalled = false;
+
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ // Overriding the editTagName walkerActor method here to check that it isn't
+ // called when blurring the tagname field.
+ inspector.walker.editTagName = function () {
+ isEditTagNameCalled = true;
+ };
+
+ let container = yield focusNode("div", inspector);
+ let tagEditor = container.editor.tag;
+
+ info("Blurring the tagname field");
+ tagEditor.blur();
+ is(isEditTagNameCalled, false, "The editTagName method wasn't called");
+
+ info("Updating the tagname to uppercase");
+ yield focusNode("div", inspector);
+ setEditableFieldValue(tagEditor, "DIV", inspector);
+ is(isEditTagNameCalled, false, "The editTagName method wasn't called");
+
+ info("Updating the tagname to a different value");
+ setEditableFieldValue(tagEditor, "SPAN", inspector);
+ is(isEditTagNameCalled, true, "The editTagName method was called");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_12.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_12.js
new file mode 100644
index 000000000..4fcf3dd66
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_12.js
@@ -0,0 +1,98 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that focus position is correct when tabbing through and editing
+// attributes.
+
+const TEST_URL = "data:text/html;charset=utf8," +
+ "<div id='attr' a='1' b='2' c='3'></div>" +
+ "<div id='delattr' tobeinvalid='1' last='2'></div>";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ yield testAttributeEditing(inspector);
+ yield testAttributeDeletion(inspector);
+});
+
+function* testAttributeEditing(inspector) {
+ info("Testing focus position after attribute editing");
+
+ info("Setting the first non-id attribute in edit mode");
+ // focuses id
+ yield activateFirstAttribute("#attr", inspector);
+ // focuses the first attr after id
+ collapseSelectionAndTab(inspector);
+
+ let attrs = yield getAttributesFromEditor("#attr", inspector);
+
+ info("Editing this attribute, keeping the same name, " +
+ "and tabbing to the next");
+ yield editAttributeAndTab(attrs[1] + '="99"', inspector);
+ checkFocusedAttribute(attrs[2], true);
+
+ info("Editing the new focused attribute, keeping the name, " +
+ "and tabbing to the previous");
+ yield editAttributeAndTab(attrs[2] + '="99"', inspector, true);
+ checkFocusedAttribute(attrs[1], true);
+
+ info("Editing attribute name, changes attribute order");
+ yield editAttributeAndTab("d='4'", inspector);
+ checkFocusedAttribute("id", true);
+
+ // Escape of the currently focused field for the next test
+ EventUtils.sendKey("escape", inspector.panelWin);
+}
+
+function* testAttributeDeletion(inspector) {
+ info("Testing focus position after attribute deletion");
+
+ info("Setting the first non-id attribute in edit mode");
+ // focuses id
+ yield activateFirstAttribute("#delattr", inspector);
+ // focuses the first attr after id
+ collapseSelectionAndTab(inspector);
+
+ let attrs = yield getAttributesFromEditor("#delattr", inspector);
+
+ info("Entering an invalid attribute to delete the attribute");
+ yield editAttributeAndTab('"', inspector);
+ checkFocusedAttribute(attrs[2], true);
+
+ info("Deleting the last attribute");
+ yield editAttributeAndTab(" ", inspector);
+
+ // Check we're on the newattr element
+ let focusedAttr = Services.focus.focusedElement;
+ ok(focusedAttr.classList.contains("styleinspector-propertyeditor"),
+ "in newattr");
+ is(focusedAttr.tagName, "textarea", "newattr is active");
+}
+
+function* editAttributeAndTab(newValue, inspector, goPrevious) {
+ let onEditMutation = inspector.markup.once("refocusedonedit");
+ inspector.markup.doc.activeElement.value = newValue;
+ if (goPrevious) {
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true },
+ inspector.panelWin);
+ } else {
+ EventUtils.sendKey("tab", inspector.panelWin);
+ }
+ yield onEditMutation;
+}
+
+/**
+ * Given a markup container, focus and turn in edit mode its first attribute
+ * field.
+ */
+function* activateFirstAttribute(container, inspector) {
+ let {editor} = yield focusNode(container, inspector);
+ editor.tag.focus();
+
+ // Go to "id" attribute and trigger edit mode.
+ EventUtils.sendKey("tab", inspector.panelWin);
+ EventUtils.sendKey("return", inspector.panelWin);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_13-other.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_13-other.js
new file mode 100644
index 000000000..188e12cbc
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_13-other.js
@@ -0,0 +1,38 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that doesn't fit into any specific category.
+
+const TEST_URL = `data:text/html;charset=utf8,
+ <div a b id='order' c class></div>`;
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ yield testOriginalAttributesOrder(inspector);
+ yield testOrderAfterAttributeChange(inspector, testActor);
+});
+
+function* testOriginalAttributesOrder(inspector) {
+ info("Testing order of attributes on initial node render");
+
+ let attributes = yield getAttributesFromEditor("#order", inspector);
+ ok(isEqual(attributes, ["id", "class", "a", "b", "c"]), "ordered correctly");
+}
+
+function* testOrderAfterAttributeChange(inspector, testActor) {
+ info("Testing order of attributes after attribute is change by setAttribute");
+
+ yield testActor.setAttribute("#order", "a", "changed");
+
+ let attributes = yield getAttributesFromEditor("#order", inspector);
+ ok(isEqual(attributes, ["id", "class", "a", "b", "c"]),
+ "order isn't changed");
+}
+
+function isEqual(a, b) {
+ return a.toString() === b.toString();
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_tag_edit_long-classname.js b/devtools/client/inspector/markup/test/browser_markup_tag_edit_long-classname.js
new file mode 100644
index 000000000..7615ed691
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_tag_edit_long-classname.js
@@ -0,0 +1,41 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that editing long classnames shows the whole class attribute without scrollbars.
+
+const classname = "this-long-class-attribute-should-be-displayed " +
+ "without-overflow-when-switching-to-edit-mode " +
+ "AAAAAAAAAAAA-BBBBBBBBBBBBB-CCCCCCCCCCCCC-DDDDDDDDDDDDDD-EEEEEEEEEEEEE";
+const TEST_URL = `data:text/html;charset=utf8, <div class="${classname}"></div>`;
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ yield selectNode("div", inspector);
+ yield clickContainer("div", inspector);
+
+ let container = yield focusNode("div", inspector);
+ ok(container && container.editor, "The markup-container was found");
+
+ info("Listening for the markupmutation event");
+ let nodeMutated = inspector.once("markupmutation");
+ let attr = container.editor.attrElements.get("class").querySelector(".editable");
+
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+ let input = inplaceEditor(attr).input;
+ ok(input, "Found editable field for class attribute");
+
+ is(input.scrollHeight, input.clientHeight, "input should not have vertical scrollbars");
+ is(input.scrollWidth, input.clientWidth, "input should not have horizontal scrollbars");
+ input.value = "class=\"other value\"";
+
+ info("Commit the new class value");
+ EventUtils.sendKey("return", inspector.panelWin);
+
+ info("Wait for the markup-mutation event");
+ yield nodeMutated;
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_textcontent_display.js b/devtools/client/inspector/markup/test/browser_markup_textcontent_display.js
new file mode 100644
index 000000000..7513e4e18
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_textcontent_display.js
@@ -0,0 +1,89 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test the rendering of text nodes in the markup view.
+
+const LONG_VALUE = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " +
+ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.";
+const SCHEMA = "data:text/html;charset=UTF-8,";
+const TEST_URL = `${SCHEMA}<!DOCTYPE html>
+ <html>
+ <body>
+ <div id="shorttext">Short text</div>
+ <div id="longtext">${LONG_VALUE}</div>
+ <div id="shortcomment"><!--Short comment--></div>
+ <div id="longcomment"><!--${LONG_VALUE}--></div>
+ <div id="shorttext-and-node">Short text<span>Other element</span></div>
+ <div id="longtext-and-node">${LONG_VALUE}<span>Other element</span></div>
+ </body>
+ </html>`;
+
+const TEST_DATA = [{
+ desc: "Test node containing a short text, short text nodes can be inlined.",
+ selector: "#shorttext",
+ inline: true,
+ value: "Short text",
+}, {
+ desc: "Test node containing a long text, long text nodes are not inlined.",
+ selector: "#longtext",
+ inline: false,
+ value: LONG_VALUE,
+}, {
+ desc: "Test node containing a short comment, comments are not inlined.",
+ selector: "#shortcomment",
+ inline: false,
+ value: "Short comment",
+}, {
+ desc: "Test node containing a long comment, comments are not inlined.",
+ selector: "#longcomment",
+ inline: false,
+ value: LONG_VALUE,
+}, {
+ desc: "Test node containing a short text and a span.",
+ selector: "#shorttext-and-node",
+ inline: false,
+ value: "Short text",
+}, {
+ desc: "Test node containing a long text and a span.",
+ selector: "#longtext-and-node",
+ inline: false,
+ value: LONG_VALUE,
+}];
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ for (let data of TEST_DATA) {
+ yield checkNode(inspector, testActor, data);
+ }
+});
+
+function* checkNode(inspector, testActor, {desc, selector, inline, value}) {
+ info(desc);
+
+ let container = yield getContainerForSelector(selector, inspector);
+ let nodeValue = yield getFirstChildNodeValue(selector, testActor);
+ is(nodeValue, value, "The test node's text content is correct");
+
+ is(!!container.inlineTextChild, inline, "Container inlineTextChild is as expected");
+ is(!container.canExpand, inline, "Container canExpand property is as expected");
+
+ let textContainer;
+ if (inline) {
+ textContainer = container.elt.querySelector("pre");
+ ok(!!textContainer, "Text container is already rendered for inline text elements");
+ } else {
+ textContainer = container.elt.querySelector("pre");
+ ok(!textContainer, "Text container is not rendered for collapsed text nodes");
+ yield inspector.markup.expandNode(container.node);
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ textContainer = container.elt.querySelector("pre");
+ ok(!!textContainer, "Text container is rendered after expanding the container");
+ }
+
+ is(textContainer.textContent, value, "The complete text node is rendered.");
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
new file mode 100644
index 000000000..f27b56647
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
@@ -0,0 +1,84 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test editing a node's text content
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+const {DEFAULT_VALUE_SUMMARY_LENGTH} = require("devtools/server/actors/inspector");
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Expanding all nodes");
+ yield inspector.markup.expandAll();
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ yield editContainer(inspector, testActor, {
+ selector: ".node6",
+ newValue: "New text",
+ oldValue: "line6"
+ });
+
+ yield editContainer(inspector, testActor, {
+ selector: "#node17",
+ newValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
+ "DONEC POSUERE PLACERAT MAGNA ET IMPERDIET.",
+ oldValue: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+ "Donec posuere placerat magna et imperdiet."
+ });
+
+ yield editContainer(inspector, testActor, {
+ selector: "#node17",
+ newValue: "New value",
+ oldValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
+ "DONEC POSUERE PLACERAT MAGNA ET IMPERDIET."
+ });
+
+ yield editContainer(inspector, testActor, {
+ selector: "#node17",
+ newValue: "LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. " +
+ "DONEC POSUERE PLACERAT MAGNA ET IMPERDIET.",
+ oldValue: "New value"
+ });
+});
+
+function* editContainer(inspector, testActor,
+ {selector, newValue, oldValue}) {
+ let nodeValue = yield getFirstChildNodeValue(selector, testActor);
+ is(nodeValue, oldValue, "The test node's text content is correct");
+
+ info("Changing the text content");
+ let onMutated = inspector.once("markupmutation");
+ let container = yield focusNode(selector, inspector);
+
+ let isOldValueInline = oldValue.length <= DEFAULT_VALUE_SUMMARY_LENGTH;
+ is(!!container.inlineTextChild, isOldValueInline, "inlineTextChild is as expected");
+ is(!container.canExpand, isOldValueInline, "canExpand property is as expected");
+
+ let field = container.elt.querySelector("pre");
+ is(field.textContent, oldValue,
+ "The text node has the correct original value after selecting");
+ setEditableFieldValue(field, newValue, inspector);
+
+ info("Listening to the markupmutation event");
+ yield onMutated;
+
+ nodeValue = yield getFirstChildNodeValue(selector, testActor);
+ is(nodeValue, newValue, "The test node's text content has changed");
+
+ let isNewValueInline = newValue.length <= DEFAULT_VALUE_SUMMARY_LENGTH;
+ is(!!container.inlineTextChild, isNewValueInline, "inlineTextChild is as expected");
+ is(!container.canExpand, isNewValueInline, "canExpand property is as expected");
+
+ if (isOldValueInline != isNewValueInline) {
+ is(container.expanded, !isNewValueInline,
+ "Container was automatically expanded/collapsed");
+ }
+
+ info("Selecting the <body> to reset the selection");
+ let bodyContainer = yield getContainerForSelector("body", inspector);
+ inspector.markup.markNodeAsSelected(bodyContainer.node);
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js
new file mode 100644
index 000000000..04825f2d4
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_02.js
@@ -0,0 +1,116 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that using UP/DOWN next to a number when editing a text node does not
+// increment or decrement but simply navigates inside the editable field.
+
+const TEST_URL = URL_ROOT + "doc_markup_edit.html";
+const SELECTOR = ".node6";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Expanding all nodes");
+ yield inspector.markup.expandAll();
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ let nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
+ let expectedValue = "line6";
+ is(nodeValue, expectedValue, "The test node's text content is correct");
+
+ info("Open editable field for .node6");
+ let container = yield focusNode(SELECTOR, inspector);
+ let field = container.elt.querySelector("pre");
+ field.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+ let editor = inplaceEditor(field);
+
+ info("Initially, all the input content should be selected");
+ checkSelectionPositions(editor, 0, expectedValue.length);
+
+ info("Navigate using 'RIGHT': move the caret to the end");
+ yield sendKey("VK_RIGHT", {}, editor, inspector.panelWin);
+ is(editor.input.value, expectedValue, "Value should not have changed");
+ checkSelectionPositions(editor, expectedValue.length, expectedValue.length);
+
+ info("Navigate using 'DOWN': no effect, already at the end");
+ yield sendKey("VK_DOWN", {}, editor, inspector.panelWin);
+ is(editor.input.value, expectedValue, "Value should not have changed");
+ checkSelectionPositions(editor, expectedValue.length, expectedValue.length);
+
+ info("Navigate using 'UP': move to the start");
+ yield sendKey("VK_UP", {}, editor, inspector.panelWin);
+ is(editor.input.value, expectedValue, "Value should not have changed");
+ checkSelectionPositions(editor, 0, 0);
+
+ info("Navigate using 'DOWN': move to the end");
+ yield sendKey("VK_DOWN", {}, editor, inspector.panelWin);
+ is(editor.input.value, expectedValue, "Value should not have changed");
+ checkSelectionPositions(editor, expectedValue.length, expectedValue.length);
+
+ info("Type 'b' in the editable field");
+ yield sendKey("b", {}, editor, inspector.panelWin);
+ expectedValue += "b";
+ is(editor.input.value, expectedValue, "Value should be updated");
+
+ info("Type 'a' in the editable field");
+ yield sendKey("a", {}, editor, inspector.panelWin);
+ expectedValue += "a";
+ is(editor.input.value, expectedValue, "Value should be updated");
+
+ info("Create a new line using shift+RETURN");
+ yield sendKey("VK_RETURN", {shiftKey: true}, editor, inspector.panelWin);
+ expectedValue += "\n";
+ is(editor.input.value, expectedValue, "Value should have a new line");
+ checkSelectionPositions(editor, expectedValue.length, expectedValue.length);
+
+ info("Type '1' in the editable field");
+ yield sendKey("1", {}, editor, inspector.panelWin);
+ expectedValue += "1";
+ is(editor.input.value, expectedValue, "Value should be updated");
+ checkSelectionPositions(editor, expectedValue.length, expectedValue.length);
+
+ info("Navigate using 'UP': move back to the first line");
+ yield sendKey("VK_UP", {}, editor, inspector.panelWin);
+ is(editor.input.value, expectedValue, "Value should not have changed");
+ info("Caret should be back on the first line");
+ checkSelectionPositions(editor, 1, 1);
+
+ info("Commit the new value with RETURN, wait for the markupmutation event");
+ let onMutated = inspector.once("markupmutation");
+ yield sendKey("VK_RETURN", {}, editor, inspector.panelWin);
+ yield onMutated;
+
+ nodeValue = yield getFirstChildNodeValue(SELECTOR, testActor);
+ is(nodeValue, expectedValue, "The test node's text content is correct");
+});
+
+/**
+ * Check that the editor selection is at the expected positions.
+ */
+function checkSelectionPositions(editor, expectedStart, expectedEnd) {
+ is(editor.input.selectionStart, expectedStart,
+ "Selection should start at " + expectedStart);
+ is(editor.input.selectionEnd, expectedEnd,
+ "Selection should end at " + expectedEnd);
+}
+
+/**
+ * Send a key and expect to receive a keypress event on the editor's input.
+ */
+function sendKey(key, options, editor, win) {
+ return new Promise(resolve => {
+ info("Adding event listener for down|left|right|back_space|return keys");
+ editor.input.addEventListener("keypress", function onKeypress() {
+ if (editor.input) {
+ editor.input.removeEventListener("keypress", onKeypress);
+ }
+ executeSoon(resolve);
+ });
+
+ EventUtils.synthesizeKey(key, options, win);
+ });
+}
diff --git a/devtools/client/inspector/markup/test/browser_markup_toggle_01.js b/devtools/client/inspector/markup/test/browser_markup_toggle_01.js
new file mode 100644
index 000000000..a481f685e
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_toggle_01.js
@@ -0,0 +1,58 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling (expand/collapse) elements by clicking on twisties
+
+const TEST_URL = URL_ROOT + "doc_markup_toggle.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Getting the container for the html element");
+ let container = yield getContainerForSelector("html", inspector);
+ ok(container.mustExpand, "HTML element mustExpand");
+ ok(container.canExpand, "HTML element canExpand");
+ is(container.expander.style.visibility, "hidden", "HTML twisty is hidden");
+
+ info("Getting the container for the UL parent element");
+ container = yield getContainerForSelector("ul", inspector);
+ ok(!container.mustExpand, "UL element !mustExpand");
+ ok(container.canExpand, "UL element canExpand");
+ is(container.expander.style.visibility, "visible", "HTML twisty is visible");
+
+ info("Clicking on the UL parent expander, and waiting for children");
+ let onChildren = waitForChildrenUpdated(inspector);
+ let onUpdated = inspector.once("inspector-updated");
+ EventUtils.synthesizeMouseAtCenter(container.expander, {},
+ inspector.markup.doc.defaultView);
+ yield onChildren;
+ yield onUpdated;
+
+ info("Checking that child LI elements have been created");
+ let numLi = yield testActor.getNumberOfElementMatches("li");
+ for (let i = 0; i < numLi; i++) {
+ let liContainer = yield getContainerForSelector(
+ `li:nth-child(${i + 1})`, inspector);
+ ok(liContainer, "A container for the child LI element was created");
+ }
+ ok(container.expanded, "Parent UL container is expanded");
+
+ info("Clicking again on the UL expander");
+ // No need to wait, this is a local, synchronous operation where nodes are
+ // only hidden from the view, not destroyed
+ EventUtils.synthesizeMouseAtCenter(container.expander, {},
+ inspector.markup.doc.defaultView);
+
+ info("Checking that child LI elements have been hidden");
+ numLi = yield testActor.getNumberOfElementMatches("li");
+ for (let i = 0; i < numLi; i++) {
+ let liContainer = yield getContainerForSelector(
+ `li:nth-child(${i + 1})`, inspector);
+ is(liContainer.elt.getClientRects().length, 0,
+ "The container for the child LI element was hidden");
+ }
+ ok(!container.expanded, "Parent UL container is collapsed");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_toggle_02.js b/devtools/client/inspector/markup/test/browser_markup_toggle_02.js
new file mode 100644
index 000000000..481f0bf58
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_toggle_02.js
@@ -0,0 +1,49 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling (expand/collapse) elements by dbl-clicking on tag lines
+
+const TEST_URL = URL_ROOT + "doc_markup_toggle.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+
+ info("Getting the container for the UL parent element");
+ let container = yield getContainerForSelector("ul", inspector);
+
+ info("Dbl-clicking on the UL parent expander, and waiting for children");
+ let onChildren = waitForChildrenUpdated(inspector);
+ let onUpdated = inspector.once("inspector-updated");
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
+ inspector.markup.doc.defaultView);
+ yield onChildren;
+ yield onUpdated;
+
+ info("Checking that child LI elements have been created");
+ let numLi = yield testActor.getNumberOfElementMatches("li");
+ for (let i = 0; i < numLi; i++) {
+ let liContainer = yield getContainerForSelector(
+ "li:nth-child(" + (i + 1) + ")", inspector);
+ ok(liContainer, "A container for the child LI element was created");
+ }
+ ok(container.expanded, "Parent UL container is expanded");
+
+ info("Dbl-clicking again on the UL expander");
+ // No need to wait, this is a local, synchronous operation where nodes are
+ // only hidden from the view, not destroyed
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
+ inspector.markup.doc.defaultView);
+
+ info("Checking that child LI elements have been hidden");
+ numLi = yield testActor.getNumberOfElementMatches("li");
+ for (let i = 0; i < numLi; i++) {
+ let liContainer = yield getContainerForSelector(
+ "li:nth-child(" + (i + 1) + ")", inspector);
+ is(liContainer.elt.getClientRects().length, 0,
+ "The container for the child LI element was hidden");
+ }
+ ok(!container.expanded, "Parent UL container is collapsed");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_toggle_03.js b/devtools/client/inspector/markup/test/browser_markup_toggle_03.js
new file mode 100644
index 000000000..fb3529c8e
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_toggle_03.js
@@ -0,0 +1,35 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling (expand/collapse) elements by alt-clicking on twisties, which
+// should expand all the descendants
+
+const TEST_URL = URL_ROOT + "doc_markup_toggle.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+
+ info("Getting the container for the UL parent element");
+ let container = yield getContainerForSelector("ul", inspector);
+
+ info("Alt-clicking on the UL parent expander, and waiting for children");
+ let onUpdated = inspector.once("inspector-updated");
+ EventUtils.synthesizeMouseAtCenter(container.expander, {altKey: true},
+ inspector.markup.doc.defaultView);
+ yield onUpdated;
+ yield waitForMultipleChildrenUpdates(inspector);
+
+ info("Checking that all nodes exist and are expanded");
+ let nodeList = yield inspector.walker.querySelectorAll(
+ inspector.walker.rootNode, "ul, li, span, em");
+ let nodeFronts = yield nodeList.items();
+ for (let nodeFront of nodeFronts) {
+ let nodeContainer = getContainerForNodeFront(nodeFront, inspector);
+ ok(nodeContainer, "Container for node " + nodeFront.tagName + " exists");
+ ok(nodeContainer.expanded,
+ "Container for node " + nodeFront.tagName + " is expanded");
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js b/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js
new file mode 100644
index 000000000..241cea672
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js
@@ -0,0 +1,44 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that markup view handles page navigation correctly.
+
+const SCHEMA = "data:text/html;charset=UTF-8,";
+const URL_1 = SCHEMA + "<div id='one' style='color:red;'>ONE</div>";
+const URL_2 = SCHEMA + "<div id='two' style='color:green;'>TWO</div>";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(URL_1);
+
+ assertMarkupViewIsLoaded();
+ yield selectNode("#one", inspector);
+
+ let willNavigate = inspector.target.once("will-navigate");
+ yield testActor.eval(`content.location = "${URL_2}"`);
+
+ info("Waiting for will-navigate");
+ yield willNavigate;
+
+ info("Navigation to page 2 has started, the inspector should be empty");
+ assertMarkupViewIsEmpty();
+
+ info("Waiting for new-root");
+ yield inspector.once("new-root");
+
+ info("Navigation to page 2 was done, the inspector should be back up");
+ assertMarkupViewIsLoaded();
+
+ yield selectNode("#two", inspector);
+
+ function assertMarkupViewIsLoaded() {
+ let markupViewBox = inspector.panelDoc.getElementById("markup-box");
+ is(markupViewBox.childNodes.length, 1, "The markup-view is loaded");
+ }
+
+ function assertMarkupViewIsEmpty() {
+ let markupViewBox = inspector.panelDoc.getElementById("markup-box");
+ is(markupViewBox.childNodes.length, 0, "The markup-view is unloaded");
+ }
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_void_elements_html.js b/devtools/client/inspector/markup/test/browser_markup_void_elements_html.js
new file mode 100644
index 000000000..60330a144
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_void_elements_html.js
@@ -0,0 +1,44 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test void element display in the markupview.
+const TEST_URL = URL_ROOT + "doc_markup_void_elements.html";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let {win} = inspector.markup;
+
+ info("check non-void element closing tag is displayed");
+ let {editor} = yield getContainerForSelector("h1", inspector);
+ ok(!editor.elt.classList.contains("void-element"),
+ "h1 element does not have void-element class");
+ ok(!editor.elt.querySelector(".close").style.display !== "none",
+ "h1 element tag is not hidden");
+
+ info("check void element closing tag is hidden in HTML document");
+ let container = yield getContainerForSelector("img", inspector);
+ ok(container.editor.elt.classList.contains("void-element"),
+ "img element has the expected class");
+ let closeElement = container.editor.elt.querySelector(".close");
+ let computedStyle = win.getComputedStyle(closeElement, null);
+ ok(computedStyle.display === "none", "img closing tag is hidden");
+
+ info("check void element with pseudo element");
+ let hrNodeFront = yield getNodeFront("hr.before", inspector);
+ container = getContainerForNodeFront(hrNodeFront, inspector);
+ ok(container.editor.elt.classList.contains("void-element"),
+ "hr element has the expected class");
+ closeElement = container.editor.elt.querySelector(".close");
+ computedStyle = win.getComputedStyle(closeElement, null);
+ ok(computedStyle.display === "none", "hr closing tag is hidden");
+
+ info("check expanded void element closing tag is not hidden");
+ yield inspector.markup.expandNode(hrNodeFront);
+ yield waitForMultipleChildrenUpdates(inspector);
+ ok(container.expanded, "hr container is expanded");
+ computedStyle = win.getComputedStyle(closeElement, null);
+ ok(computedStyle.display === "none", "hr closing tag is not hidden anymore");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_void_elements_xhtml.js b/devtools/client/inspector/markup/test/browser_markup_void_elements_xhtml.js
new file mode 100644
index 000000000..0cccf54d4
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_void_elements_xhtml.js
@@ -0,0 +1,28 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test void element display in the markupview.
+const TEST_URL = URL_ROOT + "doc_markup_void_elements.xhtml";
+
+add_task(function* () {
+ let {inspector} = yield openInspectorForURL(TEST_URL);
+ let {win} = inspector.markup;
+
+ info("check non-void element closing tag is displayed");
+ let {editor} = yield getContainerForSelector("h1", inspector);
+ ok(!editor.elt.classList.contains("void-element"),
+ "h1 element does not have void-element class");
+ ok(!editor.elt.querySelector(".close").style.display !== "none",
+ "h1 element tag is not hidden");
+
+ info("check void element closing tag is not hidden in XHTML document");
+ let container = yield getContainerForSelector("br", inspector);
+ ok(!container.editor.elt.classList.contains("void-element"),
+ "br element does not have void-element class");
+ let closeElement = container.editor.elt.querySelector(".close");
+ let computedStyle = win.getComputedStyle(closeElement, null);
+ ok(computedStyle.display !== "none", "br closing tag is not hidden");
+});
diff --git a/devtools/client/inspector/markup/test/browser_markup_whitespace.js b/devtools/client/inspector/markup/test/browser_markup_whitespace.js
new file mode 100644
index 000000000..63a0d0467
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_whitespace.js
@@ -0,0 +1,66 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that whitespace text nodes do show up in the markup-view when needed.
+
+const TEST_URL = URL_ROOT + "doc_markup_whitespace.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+ let {markup} = inspector;
+
+ yield markup.expandAll();
+
+ info("Verify the number of child nodes and child elements in body");
+
+ // Body has 5 element children, but there are 6 text nodes in there too, they come from
+ // the HTML file formatting (spaces and carriage returns).
+ let {numNodes, numChildren} = yield testActor.getNodeInfo("body");
+ is(numNodes, 11, "The body node has 11 child nodes (includes text nodes)");
+ is(numChildren, 5, "The body node has 5 child elements (only element nodes)");
+
+ // In body, there are only block-level elements, so whitespace text nodes do not have
+ // layout, so they should be skipped in the markup-view.
+ info("Check that the body's whitespace text node children aren't shown");
+ let bodyContainer = markup.getContainer(inspector.selection.nodeFront);
+ let childContainers = bodyContainer.getChildContainers();
+ is(childContainers.length, 5,
+ "Only the element nodes are shown in the markup view");
+
+ // div#inline has 3 element children, but there are 4 text nodes in there too, like in
+ // body, they come from spaces and carriage returns in the HTML file.
+ info("Verify the number of child nodes and child elements in div#inline");
+ ({numNodes, numChildren} = yield testActor.getNodeInfo("#inline"));
+ is(numNodes, 7, "The div#inline node has 7 child nodes (includes text nodes)");
+ is(numChildren, 3, "The div#inline node has 3 child elements (only element nodes)");
+
+ // Within the inline formatting context in div#inline, the whitespace text nodes between
+ // the images have layout, so they should appear in the markup-view.
+ info("Check that the div#inline's whitespace text node children are shown");
+ yield selectNode("#inline", inspector);
+ let divContainer = markup.getContainer(inspector.selection.nodeFront);
+ childContainers = divContainer.getChildContainers();
+ is(childContainers.length, 5,
+ "Both the element nodes and some text nodes are shown in the markup view");
+
+ // div#pre has 2 element children, but there are 3 text nodes in there too, like in
+ // div#inline, they come from spaces and carriage returns in the HTML file.
+ info("Verify the number of child nodes and child elements in div#pre");
+ ({numNodes, numChildren} = yield testActor.getNodeInfo("#pre"));
+ is(numNodes, 5, "The div#pre node has 5 child nodes (includes text nodes)");
+ is(numChildren, 2, "The div#pre node has 2 child elements (only element nodes)");
+
+ // Within the inline formatting context in div#pre, the whitespace text nodes between
+ // the images have layout, so they should appear in the markup-view, but since
+ // white-space is set to pre, then the whitespace text nodes before and after the first
+ // and last image should also appear.
+ info("Check that the div#pre's whitespace text node children are shown");
+ yield selectNode("#pre", inspector);
+ divContainer = markup.getContainer(inspector.selection.nodeFront);
+ childContainers = divContainer.getChildContainers();
+ is(childContainers.length, 5,
+ "Both the element nodes and all text nodes are shown in the markup view");
+});
diff --git a/devtools/client/inspector/markup/test/doc_markup_anonymous.html b/devtools/client/inspector/markup/test/doc_markup_anonymous.html
new file mode 100644
index 000000000..0ede3ca5f
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_anonymous.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Anonymous content test</title>
+ <style type="text/css">
+ #pseudo::before {
+ content: "before";
+ }
+ #pseudo::after {
+ content: "after";
+ }
+ #shadow::before {
+ content: "Testing ::before on a shadow host";
+ }
+ </style>
+</head>
+<body>
+ <div id="pseudo"><span>middle</span></div>
+
+ <div id="shadow">light dom</div>
+
+ <div id="native"><video controls></video></div>
+
+ <script>
+ "use strict";
+ var host = document.querySelector("#shadow");
+ if (host.createShadowRoot) {
+ var root = host.createShadowRoot();
+ root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
+ }
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/inspector/markup/test/doc_markup_dragdrop.html b/devtools/client/inspector/markup/test/doc_markup_dragdrop.html
new file mode 100644
index 000000000..f45c26065
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_dragdrop.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=858038
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 858038</title>
+ <style>
+ #test::before {
+ content: 'This should not be draggable';
+ }
+ </style>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=858038">Mozilla Bug 858038</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <input id="anonymousParent" /><span id="before">Before<!-- Force not-inline --></span>
+ <pre id="test"><span id="firstChild">First</span><span id="middleChild">Middle</span><span id="lastChild">Last</span></pre> <span id="after">After</span>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_01.html b/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_01.html
new file mode 100644
index 000000000..35f3b5f31
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_01.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=858038
+https://bugzilla.mozilla.org/show_bug.cgi?id=1226898
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 858038 and 1226898 - Autoscroll</title>
+</head>
+<body>
+ <div id="first"></div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=858038">Mozilla Bug 858038</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1226898">Mozilla Bug 1226898</a>
+ <p id="display">Test</p>
+ <div id="content" style="display: none">
+
+ </div>
+
+ <!-- Make sure the markup-view has enough nodes shown by default that it has a scrollbar -->
+
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html b/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html
new file mode 100644
index 000000000..9e4d92cf3
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_dragdrop_autoscroll_02.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=858038
+https://bugzilla.mozilla.org/show_bug.cgi?id=1226898
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 858038 and 1226898 - Autoscroll</title>
+</head>
+<body>
+ <div id="first"></div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=858038">Mozilla Bug 858038</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1226898">Mozilla Bug 1226898</a>
+ <p id="display">Test</p>
+ <div id="content" style="display: none">
+
+ </div>
+
+ <!-- Make sure the markup-view has enough nodes shown by default that it has a scrollbar -->
+
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+ <div></div>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_edit.html b/devtools/client/inspector/markup/test/doc_markup_edit.html
new file mode 100644
index 000000000..ddefd1d87
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_edit.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+
+<html class="html">
+
+ <body class="body">
+ <div class="node0">
+ <div id="node1" class="node1">line1</div>
+ <div id="node2" class="node2">line2</div>
+ <p class="node3">line3</p>
+ <!-- A comment -->
+ <p id="node4" class="node4">line4
+ <span class="node5">line5</span>
+ <span class="node6">line6</span>
+ <!-- A comment -->
+ <a class="node7">line7<span class="node8">line8</span></a>
+ <span class="node9">line9</span>
+ <span class="node10">line10</span>
+ <span class="node11">line11</span>
+ <a class="node12">line12<span class="node13">line13</span></a>
+ </p>
+ <p id="node14">line14</p>
+ <p class="node15">line15</p>
+ </div>
+ <div id="node16">
+ <p id="node17">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec posuere placerat magna et imperdiet.</p>
+ </div>
+ <div id="node18">
+ <div id="node19">
+ <div id="node20">
+ <div id="node21">
+ line21
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="node22" class="unchanged"></div>
+ <div id="node23"></div>
+ <div id="node24"></div>
+ <div id="retag-me">
+ <div id="retag-me-2"></div>
+ </div>
+ <div id="node25"></div>
+ <div id="node26" style='background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F");'></div>
+ <div id="node27" class="Double &quot; and single &apos;"></div>
+ <img id="node-data-url" />
+ <div id="node-data-url-style"></div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_events-overflow.html b/devtools/client/inspector/markup/test/doc_markup_events-overflow.html
new file mode 100644
index 000000000..d604245fe
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events-overflow.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>doc_markup_events-overflow.html</title>
+</head>
+<body>
+ <h1>doc_markup_events-overflow.html</h1>
+ <span id="events">Inspect me!</span>
+ <script>
+ "use strict";
+ var el = document.getElementById("events");
+ for (var i = 50; i > 0; i--) {
+ el.addEventListener("click", function onClick() {
+ alert("click");
+ });
+ }
+ </script>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_events1.html b/devtools/client/inspector/markup/test/doc_markup_events1.html
new file mode 100644
index 000000000..0955289e2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events1.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ #container {
+ border: 1px solid #000;
+ width: 200px;
+ height: 85px;
+ }
+
+ #container > div {
+ border: 1px solid #000;
+ display: inline-block;
+ margin: 2px;
+ }
+
+ #output,
+ #noevents,
+ #DOM0,
+ #handleevent,
+ #output,
+ #noevents {
+ cursor: auto;
+ }
+
+ #output {
+ min-height: 1.5em;
+ }
+ </style>
+ <script type="application/javascript;version=1.8">
+ function init() {
+ let container = document.getElementById("container");
+ let multiple = document.getElementById("multiple");
+
+ container.addEventListener("mouseover", mouseoverHandler, true);
+ multiple.addEventListener("click", clickHandler, false);
+ multiple.addEventListener("mouseup", mouseupHandler, false);
+
+ let he = new handleEventClick();
+ let handleevent = document.getElementById("handleevent");
+ handleevent.addEventListener("click", he);
+ }
+
+ function mouseoverHandler(event) {
+ if (event.target.id !== "container") {
+ let output = document.getElementById("output");
+ output.textContent = event.target.textContent;
+ }
+ }
+
+ function clickHandler(event) {
+ let output = document.getElementById("output");
+ output.textContent = "click";
+ }
+
+ function mouseupHandler(event) {
+ let output = document.getElementById("output");
+ output.textContent = "mouseup";
+ }
+
+ function handleEventClick(hehe) {
+
+ }
+
+ handleEventClick.prototype = {
+ handleEvent: function(blah) {
+ alert("handleEvent");
+ }
+ };
+
+ function noeventsClickHandler(event) {
+ alert("noevents has an event listener");
+ }
+
+ function addNoeventsClickHandler() {
+ let noevents = document.getElementById("noevents");
+ noevents.addEventListener("click", noeventsClickHandler);
+ }
+
+ function removeNoeventsClickHandler() {
+ let noevents = document.getElementById("noevents");
+ noevents.removeEventListener("click", noeventsClickHandler);
+ }
+ </script>
+ </head>
+ <body onload="init();">
+ <h1>Events test 1</h1>
+ <div id="container">
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+ <div>10</div>
+ <div>11</div>
+ <div>12</div>
+ <div>13</div>
+ <div>14</div>
+ <div>15</div>
+ <div>16</div>
+ <div id="multiple">multiple</div>
+ </div>
+ <div id="output"></div>
+ <div id="noevents">noevents</div>
+ <div id="DOM0" onclick="alert('DOM0')">DOM0 event here</div>
+ <div id="handleevent">handleEvent</div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_events2.html b/devtools/client/inspector/markup/test/doc_markup_events2.html
new file mode 100644
index 000000000..ddc17537d
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events2.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ #fatarrow,
+ #bound,
+ #boundhe,
+ #comment-inline,
+ #comment-streaming,
+ #anon-object-method,
+ #object-method {
+ border: 1px solid #000;
+ width: 200px;
+ min-height: 1em;
+ cursor: pointer;
+ }
+ </style>
+ <script type="application/javascript;version=1.8">
+ function init() {
+ let fatarrow = document.getElementById("fatarrow");
+
+ let he = new handleEventClick();
+ let anonObjectMethod = document.getElementById("anon-object-method");
+ anonObjectMethod.addEventListener("click", he.anonObjectMethod);
+
+ let objectMethod = document.getElementById("object-method");
+ objectMethod.addEventListener("click", he.objectMethod);
+
+ let bhe = new boundHandleEventClick();
+ let boundheNode = document.getElementById("boundhe");
+ bhe.handleEvent = bhe.handleEvent.bind(bhe);
+ boundheNode.addEventListener("click", bhe);
+
+ let boundNode = document.getElementById("bound");
+ boundClickHandler = boundClickHandler.bind(this);
+ boundNode.addEventListener("click", boundClickHandler);
+
+ fatarrow.addEventListener("click", () => {
+ alert("Fat arrow without params!");
+ });
+
+ fatarrow.addEventListener("click", event => {
+ alert("Fat arrow with 1 param!");
+ });
+
+ fatarrow.addEventListener("click", (event, foo, bar) => {
+ alert("Fat arrow with 3 params!");
+ });
+
+ fatarrow.addEventListener("click", b => b);
+
+ let inlineCommentNode = document.getElementById("comment-inline");
+ inlineCommentNode
+ .addEventListener("click", functionProceededByInlineComment);
+
+ let streamingCommentNode = document.getElementById("comment-streaming");
+ streamingCommentNode
+ .addEventListener("click", functionProceededByStreamingComment);
+ }
+
+ function boundClickHandler(event) {
+ alert("Bound event");
+ }
+
+ function handleEventClick(hehe) {
+
+ }
+
+ handleEventClick.prototype = {
+ anonObjectMethod: function() {
+ alert("obj.anonObjectMethod");
+ },
+
+ objectMethod: function kay() {
+ alert("obj.objectMethod");
+ },
+ };
+
+ function boundHandleEventClick() {
+
+ }
+
+ boundHandleEventClick.prototype = {
+ handleEvent: function() {
+ alert("boundHandleEvent");
+ }
+ };
+
+ // A function proceeded with an inline comment
+ function functionProceededByInlineComment() {
+ alert("comment-inline");
+ }
+
+ /* A function proceeded with a streaming comment */
+ function functionProceededByStreamingComment() {
+ alert("comment-streaming");
+ }
+ </script>
+ </head>
+ <body onload="init();">
+ <h1>Events test 2</h1>
+ <div id="fatarrow">Fat arrows</div>
+ <div id="boundhe">Bound handleEvent</div>
+ <div id="bound">Bound event</div>
+ <div id="comment-inline">Event proceeded by an inline comment</div>
+ <div id="comment-streaming">Event proceeded by a streaming comment</div>
+ <div id="anon-object-method">Anonymous object method</div>
+ <div id="object-method">Object method</div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_events3.html b/devtools/client/inspector/markup/test/doc_markup_events3.html
new file mode 100644
index 000000000..af4decc40
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events3.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ #es6-method,
+ #generator,
+ #anon-generator,
+ #named-function-expression,
+ #anon-function-expression,
+ #returned-function,
+ #constructed-function,
+ #constructed-function-with-body-string,
+ #multiple-assignment {
+ border: 1px solid #000;
+ width: 200px;
+ min-height: 1em;
+ cursor: pointer;
+ }
+ </style>
+ <script type="application/javascript;version=1.8">
+ let namedFunctionExpression =
+ function foo() {
+ alert("namedFunctionExpression");
+ }
+
+ let anonFunctionExpression = function() {
+ alert("anonFunctionExpression");
+ };
+
+ let returnedFunction = (function() {
+ return function bar() {
+ alert("returnedFunction");
+ }
+ })();
+
+ let constructedFunc = new Function();
+
+ let constructedFuncWithBodyString =
+ new Function('a', 'b', 'c', 'alert("constructedFuncWithBodyString");');
+
+ let multipleAssignment = foo = bar = function multi() {
+ alert("multipleAssignment");
+ }
+
+ function init() {
+ let he = new handleEventClick();
+ let es6Method = document.getElementById("es6-method");
+ es6Method.addEventListener("click", he.es6Method);
+
+ let generatorNode = document.getElementById("generator");
+ generatorNode.addEventListener("click", generator);
+
+ let anonGenerator = document.getElementById("anon-generator");
+ anonGenerator.addEventListener("click", function* () {
+ alert("anonGenerator");
+ });
+
+ let namedFunctionExpressionNode =
+ document.getElementById("named-function-expression");
+ namedFunctionExpressionNode.addEventListener("click",
+ namedFunctionExpression);
+
+ let anonFunctionExpressionNode =
+ document.getElementById("anon-function-expression");
+ anonFunctionExpressionNode.addEventListener("click",
+ anonFunctionExpression);
+
+ let returnedFunctionNode = document.getElementById("returned-function");
+ returnedFunctionNode.addEventListener("click", returnedFunction);
+
+ let constructedFunctionNode =
+ document.getElementById("constructed-function");
+ constructedFunctionNode.addEventListener("click", constructedFunc);
+
+ let constructedFunctionWithBodyStringNode =
+ document.getElementById("constructed-function-with-body-string");
+ constructedFunctionWithBodyStringNode
+ .addEventListener("click", constructedFuncWithBodyString);
+
+ let multipleAssignmentNode =
+ document.getElementById("multiple-assignment");
+ multipleAssignmentNode.addEventListener("click", multipleAssignment);
+ }
+
+ function handleEventClick(hehe) {
+
+ }
+
+ handleEventClick.prototype = {
+ es6Method() {
+ alert("obj.es6Method");
+ }
+ };
+
+ function* generator() {
+ alert("generator");
+ }
+ </script>
+ </head>
+ <body onload="init();">
+ <h1>Events test 3</h1>
+ <div id="es6-method">ES6 method</div>
+ <div id="generator">Generator</div>
+ <div id="anon-generator">Anonymous Generator</div>
+ <div id="named-function-expression">Named Function Expression</div>
+ <div id="anon-function-expression">Anonymous Function Expression</div>
+ <div id="returned-function">Returned Function</div>
+ <div id="constructed-function">Constructed Function</div>
+ <div id="constructed-function-with-body-string">
+ Constructed Function with body string
+ </div>
+ <div id="multiple-assignment">Multiple Assignment</div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_events_form.html b/devtools/client/inspector/markup/test/doc_markup_events_form.html
new file mode 100644
index 000000000..b4ddff4aa
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events_form.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ </style>
+ </head>
+ <body>
+ <div id="container">
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_events_jquery.html b/devtools/client/inspector/markup/test/doc_markup_events_jquery.html
new file mode 100644
index 000000000..5f8caff27
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events_jquery.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+
+ <style>
+ input {
+ margin: 5px 3px 10px 10px;
+ }
+
+ div {
+ width: 100px;
+ height: 100px;
+ border: 1px solid #000;
+ }
+ </style>
+
+ <script type="application/javascript;version=1.8">
+ let jq = document.location.search.substr(1);
+
+ let script = document.createElement("script");
+ script.setAttribute("type", "text/javascript");
+ script.setAttribute("src", jq);
+
+ document.head.appendChild(script);
+
+ window.addEventListener("load", () => {
+ var handler1 = function liveDivDblClick() { alert(1); };
+ var handler2 = function liveDivDragStart() { alert(2); };
+ var handler3 = function liveDivDragLeave() { alert(3); };
+ var handler4 = function liveDivDragEnd() { alert(4); };
+ var handler5 = function liveDivDrop() { alert(5); };
+ var handler6 = function liveDivDragOver() { alert(6); };
+ var handler7 = function divClick1() { alert(7); };
+ var handler8 = function divClick2() { alert(8); };
+ var handler9 = function divKeyDown() { alert(9); };
+ var handler10 = function divDragOut() { alert(10); };
+
+ if ($("#livediv").live) {
+ $("#livediv").live( "dblclick", handler1);
+ $("#livediv").live( "dragstart", handler2);
+ }
+
+ if ($("#livediv").delegate) {
+ $(document).delegate( "#livediv", "dragleave", handler3);
+ $(document).delegate( "#livediv", "dragend", handler4);
+ }
+
+ if ($("#livediv").on) {
+ $(document).on( "drop", "#livediv", handler5);
+ $(document).on( "dragover", "#livediv", handler6);
+ $(document).on( "dragout", "#livediv:xxxxx", handler10);
+ }
+
+ var div = $("div")[0];
+ $(div).click(handler7);
+ $(div).click(handler8);
+ $(div).keydown(handler9);
+ });
+ </script>
+ </head>
+ <body>
+ <div id="testdiv"></div>
+ <br>
+ <div id="livediv"></div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_flashing.html b/devtools/client/inspector/markup/test/doc_markup_flashing.html
new file mode 100644
index 000000000..3bb8cf1d2
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_flashing.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>mutation flashing test</title>
+</head>
+<body>
+ <div id="root">
+ <ul class="list">
+ <li class="item">item</li>
+ <li class="item">item</li>
+ </ul>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/inspector/markup/test/doc_markup_html_mixed_case.html b/devtools/client/inspector/markup/test/doc_markup_html_mixed_case.html
new file mode 100644
index 000000000..ab26005e1
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_html_mixed_case.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <svg viewBox="0 0 2 2" width=200 height=200>
+ <clipPath>
+ <rect x=0 y=0 width=1 height=1 />
+ </clipPath>
+ <circle cx=1 cy=1 r=1 fill=lime />
+ </svg>
+ <DIV></DIV>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_image_and_canvas.html b/devtools/client/inspector/markup/test/doc_markup_image_and_canvas.html
new file mode 100644
index 000000000..0b8a8bb80
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_image_and_canvas.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="html">
+ <head class="head">
+ <meta charset=utf-8 />
+ <title>Image and Canvas markup-view test</title>
+ </head>
+ <body>
+ <div></div>
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAdYElEQVRogVWYZ3QV5pWuz6y5694pKzNzZ26SyWQyccpkJo5jx8GOHXcbgzHYgOkgJECIYoQAIQkhoYoaoIKEQEK9HUnnqJxedXpvOk3l6OiogkB0XOLkzpo/z/2BV9a6P971re/ffvbe6/32twVB3QFC+oMEdcmMa/cT0uxnwpBCwnaEZc8J7npPMDO2n8Dop3iHNhIc2URUtoVp1XYS2iSiol3E5UeZ12YRNxSz6G1idXaU5Xk9E5MqEvNO4vMu4gk7iYSVpYSRlYSelVk1t+Mqlue1LM1pWE5ouJPQcG9WxaNZNU/ier6aNfF1wspKUM29yTGeLNq4k9CyEJdxa0HC4vwwgoDmWeBBXTJBXTIRXQqTxgPELanM2Y8QUe4krNhBWLadSfVO5saSWLaksGw5yLLpEM62j/F17SQ6cpQ5UzHLgSZuTw1yZ17L6oqd+TkLc3M25uYsLM2ZuTVnYGVOy524gltxOUuzapa+hVlNqFidkbASEbHg6SZubWfJPcgXcT1P5wysTCm5HVdxO6FkcVbK8oIMQXjsAOGxZCL6Z5mfNB5gynSQKUMKE/r9hBW7mBnbz5L1MCvOYzzwZ/A0coZH/gxuW45gbHwfbe372Dt2sWAu5PFkK6tTvdyeFnN3QcPctJK5mIaFuI7lhI47CQ2rCRX3EjJW43LuJLTcndPxYE7Dw1kJtyO9TBirMfZnMXw9FU3nGR5MDPD1kpqliJg7cQWri3rmZ+QsJlQIosYDhPT7CWn3EdYlEf0WJqzZS1C5i1lLKnc86Tzwn+Zh4AxfTeTyf+OFfBHMYslwGHvLR0grX2O44i08A4e467vC41gHD2N93J8Rczcu4e6snNWE4tk5K+VefJT7iVHuz0pYjal4mNDxJKHg3kQ/cVstNtFpBmu30pj3FvXn3sQnO8+TmX4ezo6yGpdzb2GM5cQYs1MqBCHtXsbVuwmodhHU7GFyLJmY6SAzpoPEjAdYdBznrv80D8fP8iScw59mCvnvuVIe+M8wpUjC2voR0srf0XX+VwxVvoVPdJhV7yW+jnfwzVwff1gU8/XiCE/nh3g0K+Z+TMj9mJBHs4M8mRvlaVzJFzNK7keExM3VeIcz0bXtRVSzlo7S16n8/Gf0XfmIeXcNf1yRc2d6hDszWh4uu0hM6hFMGJKZNKYwbT5IwnGEW9507oUyeRTN5slELg9CWTyOnufrqQL+OFPMfycq+GaqmAXTcdx9W9DUv81Yw7vorr6HqvY9LC1biclPcNtSyC17Kfd81dwPNHA/dIN7oSZWI83cn2zl6Ww3f1gU86c5GV9MiJg31WHvO4mi/jNGqtcjrnobYflr1Gf9B/U5L+KVneWb5WFWJgZZmlDwZMXP0owdwe1AOndDp3gYzebJVC5Pp/P4IpbPV7ELfD1TxNPJfL6YKuQP08V8Eyvhy2gRK85MIrJkHN2bMTV/yHjfZ0wO7SEo3IG/ZxvBvt2M9+/D07OH8FAaE9J0YqpsYrrzzBgKmDEXMeeoYNlTx21nPXFtOY7uEwxXfUpf4bsIC9+iO+8VmjKf50bWC9RkPI+u7QD3ox3cnxnh9qSCe3M2bsWsCJ7O5PFVooA/LZTwx/kSvojlcz+cw11/Jnd8Z7njzWbFk8WqJ4dVTw5L1jNMqtIIDCUREO1mSpJEXJbEzGgS0cGdBPs+I9K7g1D3Nvxdn+Fu34qvdzfjAwfwDR7E2ZeCqXs/hu5kjN0HMLYmoW7YzmDJOjpy3qAr5036z79N26k1XD74U+qOP09t+ouM1O5i3tXI10sq7ie0LExoWJwyIPhDoogvYvk8iGRzJ5DJHd8ZbnvOMG89ybT+CBOawwTlKbjFe3EM7MErSiYqP0pMk05ce4JF/XFmZSnMjO5lQrQDb9sGXE3r8bVuxNvyMYGOLQR7dxAS7iHQv4/AwAG8gwcxd+9B1bSZ0cvv0p3/Mo0n/pO2U2sQ5a2j9cQbFG/5MWfXfpfinT+jIf0N2gs2ccvdzJ9uabgbk3JrSsXqnAnBnUAmi650pg1pjCuScIl3Yuz+BG3bBlTNH6Fs2oD65kZ0bVuwCfcQlR9nwZjDsuU8C4ZMFvWfMzm6j1D/dvzdm3E0rcPRtA5/2yYC7Z/8GSLQvQ1vzw68vbvx9e/F0bcHc8dmdNffo6/wBerS/pXGY88zkv8xA2c3UrrleY68+vec3/RTrqW/R/uFzcwYr/J4ZoT7szLufWu/AlPPZnTtH6Ns/hBp43uIqt+kp/wVhFW/Z7T+QzQ3t+AWHWBae5o5Uw4LljwWrfksWPJYMGUzrzvB+MBObG0bsTStx3DtA6w31hPo2MJ451Y8LZvwdmzB27UVZ8dmHJ1bcXZvw927A3ffZ7h6N6C6+hqd2c/Tdvq3DJ/fRFf6enLX/Zyk5/+G8xv/k9asjfSU7CSivsTdSB8P5xQ8Xh5jZVaNoLvsVTpL19BT/jsGLr/JwOW3GbzyDpqbW/ANpzJvPseKq5i7nlKWrIXMGfNYMF9g0VLEoukccdVxHJ2b0TWuRX9tHWON67Hc3ISneye+rp14O3fg796Bt2cH7q5t2Ns+wdb+Cc6uLbh7P8E3sBFX90eMNa5HUfkRPZlrKdz0Cw6++B32/fKvqNy3hv6i7Ygu7WXSeIXVqQEezct5tKTndlyNoLf8Tfoq3mLw8vtI6jcgb9yEvm07gdFjLFrzeTR+mRVnKdOaLIKSDKY1OdxxlbPqqeKW9QLR0cOYbm5EffVDjDc3Y23fhqNzN57e/bi69+HuScLTm4Svbx/e3t24u7bhaP8UZ+eneHo+wdmzDo9wIz7hLhyt++jN/oBT7/4Te3/5Fxx74x+4fvL3DJR9ysDlLSSc1TydH+L+nJQ7CS0rCQOCgUsfILryISO1G5E1bEbXsgvXwGFi2mxu2UtZtpVw113BirOcOWMB86ZCVpzl3HaUMTeWi1e4D8219agbPsLevQfvYCo+0RE8omM4+9OwdR3A1p2EszcJr3AfPuEuvD3b8PdsIdC/FVffx7iFn+Lr38t4fxr6ut00HH6Zwi0/omr/v9N+7jW6S9+h4+K7xB3lfLE4xOqslNszelbnHQgGK9YxWr0JWd1mVNe2Ye05yLQmhxVn2bMs2y+ybCtlWn0en/gkvuEMprV5TGvzCEoyMLVtR167FnXjJ/jFaUQVmUQUZwkpsghIz2DpOYyxIxlzxx6cvfvwC/fi79tBULiNkGg740Pb8Yl34RXuIzhwmPDgCaxN+xkpW8dA8Rt0Fb1MZ+nLXM//NaGxHB7O9nF3RsbtmJG7CS+Ckas7kTfvR30zBU37IbySLG7763g61cLDyA2eTLZi78+g5vRrFCT/gq6SjwiMZjKlycc1cBRd0xZGrnyA5sZWxkczmNYVMKkvIaorIawpwiLMQN+Vhr4tCWvXPrzCffj7dhEU7iAs3sO4eA+h0RR8/Ul4e/YzLUknLs3A27kLzdUPGCx7icHy33A99z/wyT9nNXqT+zMj3IsbuBO3I1D3ZyPtPcXYUC4uXRlhey1LU518uSLlm1U1y+EeJC0nObPnBfa9+48UpLyAtH4fPvEZxofSsXbuxtmbhLM/jZD0HFO6SsZVldiHi3FKL+JXlSFpPkTf5c3Im7Zj696Lo2snnp5dTAynMC1NIyRKxte3j/GBZKZHj5CQHWV6aD+Rvm0Ya99EXPBLbp5+DkXtem45qngSE7I6KebxvA6BS3+Vcdt1pv3tTI23MR1sZ3VxlG8em/jqgYFEsA9x82kObvwJ7/xCQMZnP8HYcYw5XQkB8edMKdLxDBwgLDuDazCToKKCwfpj1Odv51TSGmovfIq84wRm0SlGr+9AXLMBW/deIsNp+Pr2MTFyhLA4lcDAAcZFKUREB5kaSmF6KIkZ0R58TeuQFf+G5s//lcHi3xNT5fAo0sKDSSGPZyUIJpxdxP19zIWETHrbiAU6eHJbxX89sfD0loqovZmumlRO7vwVJz/7KS0X1uEZPMmMOocJaTqhkTSiss+JyrMZrt1NXdZ6Sk+8T27aW+z44F9Y/+pfk536G7qufMbojd0om3dh7UkhIErDN3gQ/+BhxsVHCAwdwSc6hE+YQqB/P5PiZOKjKUwKd6C/8hbN6c/Rnv0ibuERVgPXeRzr52F8CMFiYJhZ3yAJ/wDz4X7uJST81xMLf3pgYnlyEK+2Bml7JoP1aZj6zjKlLWbOWMCCMZcVWx7zY2eJqU/RX7GB0sO/Zucb36Hy9Hoy9r7CKz8V8G/fEfDRa/+Tyqw30fQewz2agV2YhnvwKFFFJh7RMXzD6fhHTuId/hxnfyouYTLBwRQmRw4wM5KCrWkDfXkv0XHuZYztB1jxNvBlQszjxAiChEvElK2HeZ+I+3EVX67o+eaegYcLSmb83QT0DUxYbjBtbSA6Vsm0oYQlewl3nBdYsmSzZDnHtDaToZrNXDn9Ojvf/nuyU15l59of8fPvCnj9lwLOH/s9yr7TeFT5uGXnCCrPManJJ6o6z7g0h4D8HOOKXMYVufhkZ/BJ0hkfPsr4UCqRoUO4OrejrF6LuPw99C37WbBX82VCxOPEEIJZp5gpWx/zPjF3p+Xcm1XwaEnL09tjPF7UPhucwoPMutuIWa8SM1UQtxSTMOWSMGYxqc4gMHqMsDwHVUsa9ec/ISv5d5xLe4czh96grngX6sF8Io6rRGxXmLBUseC9yoKzhrCmiLDuIuGxMsJjZUQM5USMpUR0+YSVZwlJTzI+dBhPbxKW1u2oGj5F25LEtOEij2NCHsYHEMy6RcRdgyyGRrk7o+LRkp6v7jl4esfK4pQcn7EVnfgS8p5CrKNlhMYuMWmsJGEv5+74ZRZthbhEaXiGzmAWZuJXViHryEHdV4isJx+nthab6hJOXSUTrgaWwm0sh9tIOK8RNVwmarpCxFxD1FJLxFpL2FJN1FxJ1FDM5Nh5IvJT+ESHcfcdwNi2B21LEhFNIQ+mOng0I0RgkdbhUjcy5e7j9rSCp3esPL5jxWHqoK7qBAf3vs3xlLWkJ39AZupaGor3YhoqZtbVyIKnjthYHvGxfILK8/ikBfiVVbiV1ZhHKhkbqcAgKcesrMRjqCbqamTGd5O4t5mY8zqTtgaCxjqClnrCtusEbQ34zTUEzJeZsFQxY6tgxpBPUJKOR3QES3cyutZ9hFR5PJhs5VG8G4F3rBGLsppJfw8L0xLik6PcWTYh7C7jxz/6H/zz9/6CH37vL3nuB/+L//jx3/DRmz/jUn4ybt0NVqJ9rASuEjcWElTmE9JWELM0MmVrIWxqxme4jt/UiM/cQMBylbCjkZi3hYXxThaDPSwGe5gPdBHzthFxNBO03yBov07IXk/EWkPUXEbMXMSMMY+gJB2H8BDOgaN4JZnMOi7z1XwfAr2kFKu2hrmJQW7NK1iaU3P3roP+/su89OIP+NEP/44ffP9v+bu/+Qv++i8FPP+zf+JCVgpOXSfLkRHmnHXEzaVMGcqYNtcQdzQRd3Uw6+lhbryfCVcLEVczEed1JtxNxLwtxP0dzHo7iHvambQ3E7U3E3G0EHG2Eva0EHXfJOqsZ8JeRcx2kVlTAWHFKdyDabhERwlITzHnqOCPCz0I1EMFeK0N3JodZnlOTiIuZ3JSTnNzIW+8+Ut+/vN/4SfP/ZDvffd/8w/f+St+++K/U5yXjlbajlPbzIztKtPGMqJj5UyZa5mxNzPjbCXu7mTG20XM18GUp5VJVxMTzhtM2q8zYbtB1NxE2HidgK6OkOkaEUcLk54Oor42JjwtRF0NTDmqmTSVEjNdIKw4hWvwEM7+VHwjJ5izFPGHRCuCMUkREed1bsXELM4MMxESYTC0UFl5ik2fvMWPf/x9nvvxD/n5z37CmpdeYN+uT7l5rRyjug+T4joR4xX8qgu4ZBfwa8qZsFwn5mghYm7Cq69j2tPKlLuZCWcjE/YGIpYGwsYGxvUNBLX1BDTVhAz1TNqbmXC1EHY2M25rZNxSTdhcybgmn7AmG9/Icay9+7D0JOEcOEhMn8vTyXoE5pEiwuZ6liL9rMyMMhMeQiW9SnnJMVKSNvL273/Db196nt+teZFPN3zA2ZMHab9RhlrajF5ag01aiEOSi1NSgFdZTshQz4TlBiHTNXy6WqLWa0Rt9USsV4mY6wibrhIx1BPWXyM61kBUX8uksZ4Jy3XC5mt4xmpxaKtwaC7i0RThkmbhlWZgF6ZgbN+OqX0ntr4kosoM7geqEFjFxUQM9dwO93MvLmUuNIxe1kBF4VGSdnzA5g1v8e7rv+GDN9ewb8cGcs+kcKMum6Gei8j7CzGIc3HJ8vCqSvEoynDIKnArL+PT1RIYq8OprMClvohXU45XU45fXYlffYmgppaI9ioxQx3TY7VE9LX4NNXYVVVY5Bexygqxy/NwSs7iHD6OqWs3uptbMLZvx967l7DkGLccFxA4RaXMmJt4MCHmXnSEOe8gfkMnzdVZ7N38Fq/9+ie8+IsfsuZX/8bH773E8ZR11JSlIu7OQ9F/HllXOsquE8g70pG2ZqDszsE0VIJDVoFTUY55pACrpACXvAiXvAi3rBSPrJyA4gphZTWT2ktEVOX4FeW45eXY5WVYFaXYFUXYFbm4ZJk4xUfRtW5Ddf1jjK1bsXbvxic+yKz+DAK7uJSEo5VHk6MsBQZIeIXcisrQiGo5vv9D3lvzHK/88ru88JO/5YXn/ooPXvk+pw69w7WKJNpqD9JxeTeNhZu4nPUBV/M3IWo8jm2kFI+iAvNIATZpMU5FCV5VKT51GT5VBT5VFUFNLVHdVYKKMvyyYpySQhySYhyKctyaCtzqUjyqAryybByDR1E3f4a8YQP65k8xt2/D1beXCdlRBDPeDhbDQpajAyxEBliKDjEXHMCuuoqwKYuTyW+x/cOf8eGr/4cNr3+fjW/8Mx+/9k9sf/+HHN3+K5LX/wtJa7/LiW0/p+7cBmQ3T2AduoBjtACHtBC3ogSPshSPugKf9jI+TTVedTVuZTUuxSWc0ou4FSX4VBUE9JcJGqoJj1UzrinHryzGJz2HuecI2pu70d/cjq5pM/obH+Pu3cGs5giCh0sKHt9SsjonYWGin4WJfpYmBog6b2KWVtJ86QAlp9ZzOvlVsg6+TvbBNzm171VOJ/2OC0feoezE+1SdfJemC5tRNB/HMXwBt7QQ23Ae1qHzeJSluBUlOBUXcSkrcKku4VZfwaupw6utwaWqxKOtxK+7RMRQw4S5hgnjFSL6CsKaEkLyPKy9R9E070J/czvG5q0YbmzE1b2VKWkKgq/v6vjDPS2PFuUsRoXMjnexEO4l7u8gbG7ENFKKuOkkHVcOIqw7grjxJH21R+m6nEpfzWFGrh9H3nQMU28WflkxAdVFnKN5mMXZWIZycEgLcMqK/gzgUV3Gq67Gq63Bq615VhVtFaGxS0SMV5gwXmHCUPHsS6rOJyjNxNyVgvb6VsZubMbU/Anm5o24OrcQGdqL4NHCCF+syHmyJOH2VB+J8TYS/hamnU2EjM9szyEpxTiYj22kGLe0AvtwKebBQmziQhyiPOyiXJxDebhH8rAP52IaOINZnI1DkodtJBeHtACPshSftgq/7go+TTUu1SUcikq8mkt4NZUEtBUEtRWEtKWE1AUE5Nn4JRk4+1MxtG5H2/gxumvP9kfGxg9xtW4i0r8LwYPZAb64LeHr21Lux/tZDLWR8DYx7Wggaqpm1tXIhLGaoLaScU0FfsVFnCMF2MR5uIYu4B7JwyY6i0l4CmNfBmN9GRgHMrANZeOS5eFRXMCtKMSjLMGruohHXYFHXYVbVYlb9cxSfaoK/KqL+FSFBBR5+GRn8AwdxTV4AHP7NgzNH6O/thZ9/Xuor7yJ6vIb2BrXEe7ZjuDx3CBf3hriyyUxd6c6SfgaiTuvErPXErPVELPVMGW6TERfQUBVglt6AYf4HHZRDo6hbBxDZ7EOnsbcfxJz/0msg6ewDWfilOTgluUS0pcQ0BTjVhTikBZgkxbiUpbh01YxPnaFce0V/OpKfMpSvIo8vJJMXENHsAuTsHZvx9iyAUPzWgzX3kNT8zry8jXIyn6Lqe49Au2bETyZFfJkXsjj2R5uhZqIOaqZNFcxZakkZq0iMlZKVFfyrKyKPHyyXHzSc7hHsnCIT2EXZWAXZWAbPIllMB2r6CSO4dO4ZefwKs4T1BUT0BTjURXgkhfgVBbjUZcR0FcRNFwmqLvMuKYCn6IIjyQH99Dn2Pr3Y+78DEPbBvQ33kV77Q00Nb9DWfky0osvoSx/BWvDWsbbtyJ4ONPF49keHs50sBxsZMZ+mQlzGZOmZ5oyXGRirIigOo+AModxxTO5Rk5iEqZhEx3HLv4cm+g4xt4jjPWmYRlMxyXNYlyTj0t+Do8ij4CmmHF9KaGxCgJjlfj1FQS05QS1lQTUZXhk+ThHzmAfTMPcvRt960Z0N95H0/B7lNVrkFa+iPTir5GXr0F35S1czRsJ9+xCcH+ylccznTyYamPRV8+0pYKooZSorpiw9gJRfSFhbR5BVTYBxVnGZafxSzLwjqTjHvocu/gYdvExbKKjWAePYRk8hlV0Aos4A+vQKbyK8/jVBYT0F4mYKoiYKgjqy/GpS/AoS/DIi3FK87ENn8UuSsc+mIq1dy/mjs0YW9ahqHmNweL/pPf8TxkqeQH1lTcxNqzF1bKVUO9eBKvRVh5MtXF/spVFXz0xaxUTxotE9YWENPmENOcJqnMYV2bil58mIMvAJ0nHPXwch+gIloHDmPtTMQkPYew7xFhvKoa+wxiFxxkTHieoLSSkKyakv0hIf5FxfSk+TRFuRSFO2QXc8gIcklxsw5k4hk7gFKdh69uHsW0z2utrUda+wXDFbxkpexlt3TvYmjfh6tzOuHA/U5JjCFZCLaxGW1mNtrIcuMas4wpT5nIiY0UEtRcIqnMJKLMIKM4QUJxhXH6KgCwd7+hx3MNHcQ0dwSlOwypK/RbmMKb+I5j6P8c8cAK37BxuWS5ueT5ueT5OeR4O2XlsknNYR3Oe3SU5OEazcA2fwj18HFtfMvqWrSivrkVy5R1GL72Bsu49rK1b8PTtJTBwgNDIEaZVpxEs+2+yEmrhbqSNlVAzi54G4vbLTJrKiIwVEVDm4JWdwSM5iWvkBJ6Rz/+/wJ3iVByiQ1hFqZj7UzH3H8YsPPYMoP8kqs4jaLqPoRdmYBw8g3k4C8tI9p9ll+bglOXgkmbhGj2NY/A4xq5k9Dd3or62GVndRygbNmJs3Y6nP4XA0BECI58TVpxhSn8OwYKvmeXxFu5EOrgbbWcl1MK851krTZrKCChzcEtOYR08gqH3ILqu/Wg7k9B37kXXsQdN2w40bTtQt+5E1bILRctulC37UbUfRNWWymhzCrKWw6i70zEMZGIdPod99FkFbJJszMNncEjP4pJl4hjOwNSdhq41hbHWZMztB7B1peLqO4p/OJ2wPPPZukWdQ1Sfx5SlCMGcu5kFXzMr4Q7uT/Vwf6Kb5UATs/aaZxCGYsZV53AOp2PoPYi6fS+Kll0omrchb9rCSMNGRq5tYKThE0avbWH0+jZkTXuRtySjbE1F232Csb4MTKIsbCO5OKX5uOUFuBT5uBTnMY+cwiY9jVOWiXXoFKa+4xi7jmHvO4l3MJOINI+o4gJT6iIm9UVEdAUE9QWExgqJmIsQxJxNxN1N3Brv4GFMyMNYH7eCN5l11jJtrWLKXE5YX4hPnol9+Dgm4SH03cmoO3ajaH0GIW/+FOXN7ahbd6Pt3I+hJ+3bR+00Lsl5XJJ8XJICXJKCPwN4FIV4VPk4FWdxKTNxK7LwyHPwSXIJSi8QlRcxpSojrqsiPnaJ2NglpgwVhPQl+PRFeHUFuLTnEUzaGpl23GDJ38bDmJBHM/3fAlxl2lpFSFdMQJ2HT56FS5KOXXwMc38q+p4kNJ270HXsQt+1E2PPfqz9qdjFx/BKMgko8gipCwmoSvApinBJCrAPfzsbSfJwywvwqi8QNFzAqz+HX5NLSFdEzFTBnLWaJUs9C6Y65k1XmbPUM2e5SsxSTdR0iYCxFLe2AKs6l/8HXK32/y5m8HIAAAAASUVORK5CYII=" />
+ <canvas class="canvas" width="600" height="600"></canvas>
+ <script type="text/javascript">
+ "use strict";
+
+ let context = document.querySelector(".canvas").getContext("2d");
+ context.beginPath();
+ context.moveTo(300, 0);
+ context.lineTo(600, 600);
+ context.lineTo(0, 600);
+ context.closePath();
+ context.fillStyle = "#ffc821";
+ context.fill();
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_image_and_canvas_2.html b/devtools/client/inspector/markup/test/doc_markup_image_and_canvas_2.html
new file mode 100644
index 000000000..adae9ce21
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_image_and_canvas_2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="html">
+ <head class="head">
+ <meta charset=utf-8 />
+ <title>Image and Canvas markup-view test</title>
+ </head>
+ <body>
+ <img class="local" src="chrome://branding/content/about-logo.png" />
+ <img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADI5JREFUeNrsWwuQFNUVPf1m5z87szv7HWSWj8CigBFMEFZKiQsB1PgJwUAZg1HBpIQsKmokEhNjWUnFVPnDWBT+KolJYbRMoqUVq0yCClpqiX8sCchPWFwVlt2db7+X93pez7zu6Vn2NxsVWh8987p7pu+9555z7+tZjTGGY3kjOMa34w447oBjfKsY7i/UNM3Y8eFSAkD50Plgw03K5P9gvGv7U5ieeR3PszeREiPNX3/0DL4hjslzhm8THh+OITfXk3dhiv4GDtGPVzCaeJmPLYzuu5qJuWfuw2QTlcN1X9pwQU7LhdZ/ZAseD45cOh9hHvDkc/yAF/DNhdb5Mrr3PvBMaAYW8fMSIi2G497IMEK/YutGtAYr6+ej+nxu/NN8Ks3N7AR6HgcLz0Eg1Ljg1UcxZzi5qewIkMYLRweTr2Kzp+nmyXAd5pS3XQDd+N/4h4zgu9FI7brlXf90nMEnuwQxlvv+hosE3TuexmWeysmT4W+WxkMaLzf9Y8ATgjcUn7T9H1gqrpFq8eV1gMn6t16NhngjfoX6q4DUP032Rd4LJgpSLwJ1yzFqBG69eRkah0MVyo0Acfe+yy9AG4nMiYCkeM53KKFXncBLAXqEm+wCqZwaueq7WCmuLTcKSJmj737ol2hurA9eq9VdyiO8yWa3NNyog+SB5CZodSsQq/dfu34tJpYbBaTMzvVddDZu16q5smXf4G8zEvqm4cyaAmJPuTJk3oJWdS4WzcVtfMZbThSQckb/pYfRGgo3zNOqZnEHbJPGK4abaDCQIIsT8V/qTaBqHkLh6LzXH8XZQhbLhYKyyCC/WeHYcNdmvOgfe8skzbWL270/T3wf7tSx/lGCbTu8xlzzmCSWLc5iwmgikcCHi3Mga0Ry913vBFvQwg90l6M4ImWKfsWOp7DSWxmfpPlCFuPFfsNfKrCnPYpQKIRgqBK7D0SxYaNHwkEiJMtl0ReDp3Lc5D3PGoTo/sKngCl7a5chFqvBatKwjBd7WwqIlzB/78NcoUcp5VSgGxm+7b8eqQRGnHMO634epO4S1EZww09/iFg5UmGoESDuznP1xVhTUX1WWHPzjpd25wyH0hRxI3LGM75nxmuNEEUVpAN0XgxmPoKralakbQnWlIMQyVBD/w+3orkq4lvualjKyWwzt4MaxqspQHVhPOWG64bxYuhZXSFGWhipbSDVragOu5Y9eAsmDDUKyBA703vemVhHoueD6e9wAzJK1WfmN0Umk5GGM4kEMZcuIECqgjm0nldAqmbjwtm4VxZH5AvlADP6mx9Eqy9Q0+KqW8Ch+47FaMMYmnNGfY1iPMshoC6qFxme4wQ+0p+ARE6H3+9veWEDWgUhDhUKyFARn4jM5BNxT0XsMg7bfymGK1ov3wtjDfhL4w0HVGUVBEjDaaE+QNdrcNWch1PG4W6xrjBUXECGivg++Cva3JUT4iQUz3V2RsSVaKLwOuDT89A3HdBQoxhNC+fnVm74ual2EG893P6G+PuP4SfiO4cCBWQooL9qCWKNXPbcI37Aa/lnlZxXRt4RFONGwSDCPAHqOuqjWct1QiEMw5mChM5X4K47FyNqcd3aK9AwFH0CGYLoe1ctxk2eWi57rg5JfGp9rzC6ggCdFlAgHBDw5Yxlcg6G8SyHCjMlsgmDD9zhSeHlF+JnAgWDTQUy2NxfdwOao1UVV3pi3+bE97YSbWpLAbn6zefHNQkp1PMpIBwwvslKgIYTKM2nEpNzrGcH3FXTEal0L38kJ4uDQgEZbO4vnI173LXf5NHZaiUxtaCxyZuo/rK6LpUg54yg3zTWRAArvDcRIPZ6BqzrQ1REpmL+DNw32OKIDCb3X1qPVn8wNNMT4w2bvs+q4bAZrqBh2skaL3yyhhIIZ4i6oHkUK0RckcB8GigEyRIH4A6Mgc8fatl0/+BkkQxC9gIT4ljna1rIZW9rEdNbjJcNjsnoYj7LHWCUwpITzEgzRQKZ3XAFHbTzA3hrz8TEUUZxFBhoKpABQt/97p+w0hMZG68I8R6FtlsJT3FELndZntjM+VMnylKYq8GJI3UZaRMpquGSGFVOEfv0YZBMNzz+uvjbfzS6xQERIhlI9FcvQWNdFVb7x1zCb+QNK8vb9NsiifmI5hBgVoOCBC1sb0ab5RomqENxLO3eA1/0NDRU47q2RQNbRCUDIb7lF2CNL3ZGxEV4n08TVvZWYG4pZyV0zUdS45tyCBByOHWiyvZmxFXDCyRo1ge5+Sy0TA+8lWMiP/6O0S32exGV9Jf4fr8azdUR3zL/CZz4MtvzdX5uOYs6NDOmpkuj5Huh+7qUQSYl0ThHzw0YQzcGo6bhzEqoYq5rN3yRiYiG3Vfe2Ybm/qKA9NNZ3nNm4F7/yDkg9AN+U1mHiBcXP8zuDN76jj8hg1QyiWQigalj02BJPhK8I0zxijAjhp5zhlpLUDvS+BCy2HMAvvB4XDgL9/SXC0g/ou/5+6/xLX8w0uJrOIkXfPvyhY0F6gr7M8H0KWFYikcqAXakB+xwD9CdREBLoau7Gz3cAdSIdLFxFtJTCqRChSjnutvhDcREtzjz2Tswtz+yeNRFUeXZXtWux7C1fuoVcbd3J//ipDX3uZZDLGrwweS+UBLL5TDliVBnF8P7H+XI8aRRGsIBJg/Zlslt1+W+D1JWoSyi+kD9jfhs78t7mhZhSl+fLfY1Bdyv3I8V/qpY3B1McgN7ZFT5/vNO0I5DPLLdPBIJA8qc4h2I0QplYfDpJwHT+aj0246r5S8rToG8OjCle8wk4OLvvYGa+Ovr84uo2qBSwJS9G5egoZFLTfiEqWDtbwGfHgKOdPHcS+ai7XDzMPW/FJRLGGcxnBbK4YJC2K+h+T6Bdu5CqHqCWERd3bawb7JI+iJ735+LNaHaprBLLHBm08U3XxShEsdt+f3eTh3v7aC95Dct4RCWL5OZWh/oXBZThxAIxyOXLzBk8aiEWJID8rK3CpPOmeHaGpvCS+7EHv5FujVHUSJPLXvIFeHcNc+9xrB2gws9KZdxuLFax/WLM5gzzSm/lTXF/OdAcapyvjxPqxqHjr2v4ckX2bS2dRBrc5lSdpKjEJ9/9tdwX2WMd53ZQ2IVo3RES+UwVSpCPvYepNx4gmTGDUKIMQ4eduPnD7mx9xOn/KZKOlFbStjONxHTtR+BYAPmnoZ1Zp8wkBRwP/EL3u0F/C2hGl7vpz7vW37T3vP7if8wroKuoh8ribknX9BK5rcF+mo1qKaKyRPJTgTDjbzY8szcuLb3bpH00u35T47j7prRpwDJTxzyG0dHgxPp5bPG8VdkpfPbUg3SgoOo2mwVukb98D5EqpswZTTulCggTk4gpYhv0++wIhCJxr0+Hq1sondis0SE2oxQe3qWXwWyO4DSQg9gJ8Iiw1VFcGqXxet0N9xE4ygIxv/9W6wo9WyROEX/R+eiobYSq2vHTOR631Eiv2lRfh9dvxkumkXh92Qsx8XrAJ+7YGbWuhxOi/U+31NQmzyqNYG8N/3wfo6CRtRHcN01FzkvojohwLu0VVvDa56IS/xcj2b7nN+O+m0jqpE1wMPXZxAN9iCVThtDvH7gmiRGRpU8Lspv1Uhq4wIVdQoyuGSLNYPKUCS8+CzNURbzMmjK3i8u0U793lmuV0ef9nWQ5MGC/DiUqEUSaCtXna9RJEspZS1lrXINK/pcq+SpT50t98QKMq1FRmDfx3vxty102k0PM4ssEnvuz5+G26Ij4yDpz6z9fV8bkyIkqBFkhej0Ib+ZQ34XJK9AfozaiimqIoX3Jp3tiISrcfYpuN2+iFph/02P36PNC9fVcCnp6H9jYouKyfaWufz5Tp9tVxcUniw7IohZv4dZz81/ns67z3AYPrc2n0+Ix2q8k0PWjgBy88XaibnfK9A+5LdDY2Ivhy36fbT8Zv3Lb1U1qLqUxorXEEXIs0mjjrtxoTZWtdvigNs2sgPiujTv6DIZLld6b/V5742JZV3fUsUVFy5gdsNtKWFzUCEVbNepD1MkSMVbsb6SZm7jI3/zODtQKgUMsOw8wDZ63t5xcV1TnaEAxoc6wrqY+Fj+N4DsqOnhOIdicrQSm1MPYCPlIqHn5bbHg8/bj2D3QfZnCX3mpAICDZV8jH5kpbZqTD0W+DxaA74CWzLN2nd14OlL72J38Lf7+TjC7dadZFDoZJQPrtaIKL/G0L6ktptPZVJ8fMqHYPZOKYPMyQGadIJfDvdXwAFiZOTvDBPydf5vk4rWA+RfdhBlaF/yDDBRoMu9pfnSjv/p7DG+HXfAcQcc49v/BBgAcFAO4DmB2GQAAAAASUVORK5CYII=" />
+ <img class="remote" src="http://example.com/browser/devtools/client/inspector/markup/test/doc_markup_tooltip.png" />
+ <canvas class="canvas" width="600" height="600"></canvas>
+ <script type="text/javascript">
+ "use strict";
+
+ let context = document.querySelector(".canvas").getContext("2d");
+ context.beginPath();
+ context.moveTo(300, 0);
+ context.lineTo(600, 600);
+ context.lineTo(0, 600);
+ context.closePath();
+ context.fillStyle = "#ffc821";
+ context.fill();
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_links.html b/devtools/client/inspector/markup/test/doc_markup_links.html
new file mode 100644
index 000000000..f393319f8
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_links.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Markup-view links</title>
+ <link rel="stylesheet" type="text/css" href="style.css">
+ <link rel="icon" type="image/png" sizes="196x196" href="/media/img/firefox/favicon-196.223e1bcaf067.png">
+ </head>
+ <body>
+ <form id="message-form" method="post" action="/post_message">
+ <p for="invalid-idref">
+ <label for="name">Name</label>
+ <input id="name" type="text" />
+ </p>
+ <p>
+ <label for="message">Message</label>
+ <input id="message" type="text" />
+ </p>
+ <p>
+ <button>Send message</button>
+ </p>
+ <output form="message-form" for="name message invalid">Thank you for your message!</output>
+ </form>
+ <a href="/go/somewhere/else" ping="/analytics?page=pageA /analytics?user=test">Click me, I'm a link</a>
+ <ul>
+ <li contextmenu="menu1">Item 1</li>
+ <li contextmenu="menu2">Item 2</li>
+ <li contextmenu="menu3">Item 3</li>
+ </ul>
+ <menu type="context" id="menu1">
+ <menuitem label="custom menu 1"></menuitem>
+ </menu>
+ <menu type="context" id="menu2">
+ <menuitem label="custom menu 2"></menuitem>
+ </menu>
+ <menu type="context" id="menu3">
+ <menuitem label="custom menu 3"></menuitem>
+ </menu>
+ <video controls poster="doc_markup_tooltip.png" src="code-rush.mp4"></video>
+ <script type="text/javascript" src="lib_jquery_1.0.js"></script>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_mutation.html b/devtools/client/inspector/markup/test/doc_markup_mutation.html
new file mode 100644
index 000000000..f021c9fcf
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_mutation.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+<html class="html">
+ <style type="text/css">
+ #node1.pseudo::after {
+ content: "after";
+ }
+ </style>
+
+ <body class="body">
+ <div class="node0">
+ <div id="node1" class="node1">line1</div>
+ <div id="node2" class="node2">line2</div>
+ <p class="node3">line3</p>
+ <!-- A comment -->
+ <p id="node4" class="node4">line4
+ <span class="node5">line5</span>
+ <span class="node6">line6</span>
+ <!-- A comment -->
+ <a class="node7">line7<span class="node8">line8</span></a>
+ <span class="node9">line9</span>
+ <span class="node10">line10</span>
+ <span class="node11">line11</span>
+ <a class="node12">line12<span class="node13">line13</span></a>
+ </p>
+ <p id="node14">line14</p>
+ <p class="node15">line15</p>
+ </div>
+ <div id="node16">
+ <p id="node17">line17</p>
+ </div>
+ <div id="node18">
+ <div id="node19">
+ <div id="node20">
+ <div id="node21">
+ line21
+ </div>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_navigation.html b/devtools/client/inspector/markup/test/doc_markup_navigation.html
new file mode 100644
index 000000000..9633052e1
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_navigation.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+
+<html class="html">
+ <head class="head">
+ <meta charset=utf-8 />
+ </head>
+
+ <body class="body">
+ <div class="node0">
+ <p class="node1">line1</p>
+ <p class="node2">line2</p>
+ <p class="node3">line3</p>
+ <!-- A comment -->
+ <p class="node4">line4
+ <span class="node5">line5</span>
+ <span class="node6">line6</span>
+ <!-- A comment -->
+ <a class="node7">line7<span class="node8">line8</span></a>
+ <span class="node9">line9</span>
+ <span class="node10">line10</span>
+ <span class="node11">line11</span>
+ <a class="node12">line12<span class="node13">line13</span></a>
+ </p>
+ <p class="node14">line14</p>
+ <p class="node15">line15</p>
+ </div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_not_displayed.html b/devtools/client/inspector/markup/test/doc_markup_not_displayed.html
new file mode 100644
index 000000000..20a4b9415
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_not_displayed.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ #hidden-via-stylesheet {
+ display: none;
+ }
+ </style>
+</head>
+<body>
+ <div id="normal-div"></div>
+ <div id="display-none" style="display:none;"></div>
+ <div id="hidden-true" hidden="true"></div>
+ <div id="hidden-via-hide-shortcut" class="__fx-devtools-hide-shortcut__"></div>
+ <div id="visibility-hidden" style="visibility:hidden;"></div>
+ <div id="hidden-via-stylesheet"></div>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_pagesize_01.html b/devtools/client/inspector/markup/test/doc_markup_pagesize_01.html
new file mode 100644
index 000000000..8323f0b2e
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_pagesize_01.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+
+<html class="html">
+ <body class="body">
+ <div id="a"></div>
+ <div id="b"></div>
+ <div id="c"></div>
+ <div id="d"></div>
+ <div id="e"></div>
+ <div id="f"></div>
+ <div id="g"></div>
+ <div id="h"></div>
+ <div id="i"></div>
+ <div id="j"></div>
+ <div id="k"></div>
+ <div id="l"></div>
+ <div id="m"></div>
+ <div id="n"></div>
+ <div id="o"></div>
+ <div id="p"></div>
+ <div id="q"></div>
+ <div id="r"></div>
+ <div id="s"></div>
+ <div id="t"></div>
+ <div id="u"></div>
+ <div id="v"></div>
+ <div id="w"></div>
+ <div id="x"></div>
+ <div id="y"></div>
+ <div id="z"></div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_pagesize_02.html b/devtools/client/inspector/markup/test/doc_markup_pagesize_02.html
new file mode 100644
index 000000000..db2502c89
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_pagesize_02.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+
+<html class="html">
+ <body class="body">
+ <ul>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ <li>some content</li>
+ </ul>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_search.html b/devtools/client/inspector/markup/test/doc_markup_search.html
new file mode 100644
index 000000000..08c047bcc
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_search.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+ <ul>
+ <li>
+ <span>this is an <em>important</em> node</span>
+ </li>
+ </ul>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/inspector/markup/test/doc_markup_svg_attributes.html b/devtools/client/inspector/markup/test/doc_markup_svg_attributes.html
new file mode 100644
index 000000000..04b699be7
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_svg_attributes.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <svg viewBox="0 0 2 2" width=200 height=200>
+ <circle cx=1 cy=1 r=1 fill=lime />
+ </svg>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_toggle.html b/devtools/client/inspector/markup/test/doc_markup_toggle.html
new file mode 100644
index 000000000..521db100c
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_toggle.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Expanding and collapsing markup-view containers</title>
+</head>
+<body>
+ <ul>
+ <li>
+ <span>list <em>item<!-- force expand --></em></span>
+ </li>
+ <li>
+ <span>list <em>item<!-- force expand --></em></span>
+ </li>
+ <li>
+ <span>list <em>item<!-- force expand --></em></span>
+ </li>
+ <li>
+ <span>list <em>item<!-- force expand --></em></span>
+ </li>
+ <li>
+ <span>list <em>item<!-- force expand --></em></span>
+ </li>
+ <li>
+ <span>list <em>item<!-- force expand --></em></span>
+ </li>
+ </ul>
+</body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_tooltip.png b/devtools/client/inspector/markup/test/doc_markup_tooltip.png
new file mode 100644
index 000000000..699ef7940
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_tooltip.png
Binary files differ
diff --git a/devtools/client/inspector/markup/test/doc_markup_void_elements.html b/devtools/client/inspector/markup/test/doc_markup_void_elements.html
new file mode 100644
index 000000000..72a937980
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_void_elements.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="html">
+ <head class="head">
+ <meta charset=utf-8 />
+ <style>
+ .before:before {
+ content: "before";
+ }
+ </style>
+ </head>
+ <body class="body">
+ <h1>Test void elements in HTML document</h1>
+ <img>
+ <hr>
+ <hr class="before">
+ <br>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_void_elements.xhtml b/devtools/client/inspector/markup/test/doc_markup_void_elements.xhtml
new file mode 100644
index 000000000..331346b24
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_void_elements.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head class="head">
+ <meta charset="utf-8" />
+ <style>
+ .before:before {
+ content: "before";
+ }
+ </style>
+ </head>
+ <body class="body">
+ <h1>Test void elements in XHTML document</h1>
+ <hr class="before" />
+ <img />
+ <hr />
+ <br />
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_whitespace.html b/devtools/client/inspector/markup/test/doc_markup_whitespace.html
new file mode 100644
index 000000000..9071c802d
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_whitespace.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ #pre {
+ white-space: pre;
+ }
+ </style>
+ </head>
+ <body>
+ <div>div 1</div>
+ <div>div 2</div>
+ <div>div 3</div>
+ <div id="inline">
+ <img src="chrome://branding/content/about-logo.png" />
+ <img src="chrome://branding/content/about-logo.png" />
+ <img src="chrome://branding/content/about-logo.png" />
+ </div>
+ <div id="pre">
+ <img src="chrome://branding/content/about-logo.png" />
+ <img src="chrome://branding/content/about-logo.png" />
+ </div>
+ </body>
+</html>
diff --git a/devtools/client/inspector/markup/test/doc_markup_xul.xul b/devtools/client/inspector/markup/test/doc_markup_xul.xul
new file mode 100644
index 000000000..34f13dae0
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_xul.xul
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xul:window xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Test Bug 984442">
+
+ <xul:panel id="test"></xul:panel>
+
+</xul:window>
diff --git a/devtools/client/inspector/markup/test/head.js b/devtools/client/inspector/markup/test/head.js
new file mode 100644
index 000000000..f7d55a272
--- /dev/null
+++ b/devtools/client/inspector/markup/test/head.js
@@ -0,0 +1,653 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from ../../test/head.js */
+"use strict";
+
+// Import the inspector's head.js first (which itself imports shared-head.js).
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
+ this);
+
+var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
+var clipboard = require("sdk/clipboard");
+var {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
+
+// If a test times out we want to see the complete log and not just the last few
+// lines.
+SimpleTest.requestCompleteLog();
+
+// Set the testing flag on DevToolsUtils and reset it when the test ends
+flags.testing = true;
+registerCleanupFunction(() => {
+ flags.testing = false;
+});
+
+// Clear preferences that may be set during the course of tests.
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
+ Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
+ Services.prefs.clearUserPref("devtools.markup.pagesize");
+ Services.prefs.clearUserPref("dom.webcomponents.enabled");
+ Services.prefs.clearUserPref("devtools.inspector.showAllAnonymousContent");
+});
+
+/**
+ * Some tests may need to import one or more of the test helper scripts.
+ * A test helper script is simply a js file that contains common test code that
+ * is either not common-enough to be in head.js, or that is located in a
+ * separate directory.
+ * The script will be loaded synchronously and in the test's scope.
+ * @param {String} filePath The file path, relative to the current directory.
+ * Examples:
+ * - "helper_attributes_test_runner.js"
+ * - "../../../commandline/test/helpers.js"
+ */
+function loadHelperScript(filePath) {
+ let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+ Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
+}
+
+/**
+ * Reload the current page
+ * @return a promise that resolves when the inspector has emitted the event
+ * new-root
+ */
+function reloadPage(inspector, testActor) {
+ info("Reloading the page");
+ let newRoot = inspector.once("new-root");
+ testActor.reload();
+ return newRoot;
+}
+
+/**
+ * Get the MarkupContainer object instance that corresponds to the given
+ * NodeFront
+ * @param {NodeFront} nodeFront
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {MarkupContainer}
+ */
+function getContainerForNodeFront(nodeFront, {markup}) {
+ return markup.getContainer(nodeFront);
+}
+
+/**
+ * Get the MarkupContainer object instance that corresponds to the given
+ * selector
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {MarkupContainer}
+ */
+var getContainerForSelector = Task.async(function* (selector, inspector) {
+ info("Getting the markup-container for node " + selector);
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ info("Found markup-container " + container);
+ return container;
+});
+
+/**
+ * Retrieve the nodeValue for the firstChild of a provided selector on the content page.
+ *
+ * @param {String} selector
+ * @param {TestActorFront} testActor The current TestActorFront instance.
+ * @return {String} the nodeValue of the first
+ */
+function* getFirstChildNodeValue(selector, testActor) {
+ let nodeValue = yield testActor.eval(`
+ content.document.querySelector("${selector}").firstChild.nodeValue;
+ `);
+ return nodeValue;
+}
+
+/**
+ * Using the markupview's _waitForChildren function, wait for all queued
+ * children updates to be handled.
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when all queued children updates have been
+ * handled
+ */
+function waitForChildrenUpdated({markup}) {
+ info("Waiting for queued children updates to be handled");
+ let def = defer();
+ markup._waitForChildren().then(() => {
+ executeSoon(def.resolve);
+ });
+ return def.promise;
+}
+
+/**
+ * Simulate a click on the markup-container (a line in the markup-view)
+ * that corresponds to the selector passed.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves when the node has been selected.
+ */
+var clickContainer = Task.async(function* (selector, inspector) {
+ info("Clicking on the markup-container for node " + selector);
+
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+
+ let updated = container.selected
+ ? promise.resolve()
+ : inspector.once("inspector-updated");
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
+ inspector.markup.doc.defaultView);
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mouseup"},
+ inspector.markup.doc.defaultView);
+ return updated;
+});
+
+/**
+ * Focus a given editable element, enter edit mode, set value, and commit
+ * @param {DOMNode} field The element that gets editable after receiving focus
+ * and <ENTER> keypress
+ * @param {String} value The string value to be set into the edited field
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ */
+function setEditableFieldValue(field, value, inspector) {
+ field.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+ let input = inplaceEditor(field).input;
+ ok(input, "Found editable field for setting value: " + value);
+ input.value = value;
+ EventUtils.sendKey("return", inspector.panelWin);
+}
+
+/**
+ * Focus the new-attribute inplace-editor field of a node's markup container
+ * and enters the given text, then wait for it to be applied and the for the
+ * node to mutates (when new attribute(s) is(are) created)
+ * @param {String} selector The selector for the node to edit.
+ * @param {String} text The new attribute text to be entered (e.g. "id='test'")
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when the node has mutated
+ */
+var addNewAttributes = Task.async(function* (selector, text, inspector) {
+ info(`Entering text "${text}" in new attribute field for node ${selector}`);
+
+ let container = yield focusNode(selector, inspector);
+ ok(container, "The container for '" + selector + "' was found");
+
+ info("Listening for the markupmutation event");
+ let nodeMutated = inspector.once("markupmutation");
+ setEditableFieldValue(container.editor.newAttr, text, inspector);
+ yield nodeMutated;
+});
+
+/**
+ * Checks that a node has the given attributes.
+ *
+ * @param {String} selector The selector for the node to check.
+ * @param {Object} expected An object containing the attributes to check.
+ * e.g. {id: "id1", class: "someclass"}
+ * @param {TestActorFront} testActor The current TestActorFront instance.
+ *
+ * Note that node.getAttribute() returns attribute values provided by the HTML
+ * parser. The parser only provides unescaped entities so &amp; will return &.
+ */
+var assertAttributes = Task.async(function* (selector, expected, testActor) {
+ let {attributes: actual} = yield testActor.getNodeInfo(selector);
+
+ is(actual.length, Object.keys(expected).length,
+ "The node " + selector + " has the expected number of attributes.");
+ for (let attr in expected) {
+ let foundAttr = actual.find(({name}) => name === attr);
+ let foundValue = foundAttr ? foundAttr.value : undefined;
+ ok(foundAttr, "The node " + selector + " has the attribute " + attr);
+ is(foundValue, expected[attr],
+ "The node " + selector + " has the correct " + attr + " attribute value");
+ }
+});
+
+/**
+ * Undo the last markup-view action and wait for the corresponding mutation to
+ * occur
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when the markup-mutation has been treated or
+ * rejects if no undo action is possible
+ */
+function undoChange(inspector) {
+ let canUndo = inspector.markup.undo.canUndo();
+ ok(canUndo, "The last change in the markup-view can be undone");
+ if (!canUndo) {
+ return promise.reject();
+ }
+
+ let mutated = inspector.once("markupmutation");
+ inspector.markup.undo.undo();
+ return mutated;
+}
+
+/**
+ * Redo the last markup-view action and wait for the corresponding mutation to
+ * occur
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when the markup-mutation has been treated or
+ * rejects if no redo action is possible
+ */
+function redoChange(inspector) {
+ let canRedo = inspector.markup.undo.canRedo();
+ ok(canRedo, "The last change in the markup-view can be redone");
+ if (!canRedo) {
+ return promise.reject();
+ }
+
+ let mutated = inspector.once("markupmutation");
+ inspector.markup.undo.redo();
+ return mutated;
+}
+
+/**
+ * Get the selector-search input box from the inspector panel
+ * @return {DOMNode}
+ */
+function getSelectorSearchBox(inspector) {
+ return inspector.panelWin.document.getElementById("inspector-searchbox");
+}
+
+/**
+ * Using the inspector panel's selector search box, search for a given selector.
+ * The selector input string will be entered in the input field and the <ENTER>
+ * keypress will be simulated.
+ * This function won't wait for any events and is not async. It's up to callers
+ * to subscribe to events and react accordingly.
+ */
+function searchUsingSelectorSearch(selector, inspector) {
+ info("Entering \"" + selector + "\" into the selector-search input field");
+ let field = getSelectorSearchBox(inspector);
+ field.focus();
+ field.value = selector;
+ EventUtils.sendKey("return", inspector.panelWin);
+}
+
+/**
+ * Check to see if the inspector menu items for editing are disabled.
+ * Things like Edit As HTML, Delete Node, etc.
+ * @param {NodeFront} nodeFront
+ * @param {InspectorPanel} inspector
+ * @param {Boolean} assert Should this function run assertions inline.
+ * @return A promise that resolves with a boolean indicating whether
+ * the menu items are disabled once the menu has been checked.
+ */
+var isEditingMenuDisabled = Task.async(
+function* (nodeFront, inspector, assert = true) {
+ // To ensure clipboard contains something to paste.
+ clipboard.set("<p>test</p>", "html");
+
+ yield selectNode(nodeFront, inspector);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+
+ let deleteMenuItem = allMenuItems.find(i => i.id === "node-menu-delete");
+ let editHTMLMenuItem = allMenuItems.find(i => i.id === "node-menu-edithtml");
+ let pasteHTMLMenuItem = allMenuItems.find(i => i.id === "node-menu-pasteouterhtml");
+
+ if (assert) {
+ ok(deleteMenuItem.disabled, "Delete menu item is disabled");
+ ok(editHTMLMenuItem.disabled, "Edit HTML menu item is disabled");
+ ok(pasteHTMLMenuItem.disabled, "Paste HTML menu item is disabled");
+ }
+
+ return deleteMenuItem.disabled &&
+ editHTMLMenuItem.disabled &&
+ pasteHTMLMenuItem.disabled;
+});
+
+/**
+ * Check to see if the inspector menu items for editing are enabled.
+ * Things like Edit As HTML, Delete Node, etc.
+ * @param {NodeFront} nodeFront
+ * @param {InspectorPanel} inspector
+ * @param {Boolean} assert Should this function run assertions inline.
+ * @return A promise that resolves with a boolean indicating whether
+ * the menu items are enabled once the menu has been checked.
+ */
+var isEditingMenuEnabled = Task.async(
+function* (nodeFront, inspector, assert = true) {
+ // To ensure clipboard contains something to paste.
+ clipboard.set("<p>test</p>", "html");
+
+ yield selectNode(nodeFront, inspector);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+
+ let deleteMenuItem = allMenuItems.find(i => i.id === "node-menu-delete");
+ let editHTMLMenuItem = allMenuItems.find(i => i.id === "node-menu-edithtml");
+ let pasteHTMLMenuItem = allMenuItems.find(i => i.id === "node-menu-pasteouterhtml");
+
+ if (assert) {
+ ok(!deleteMenuItem.disabled, "Delete menu item is enabled");
+ ok(!editHTMLMenuItem.disabled, "Edit HTML menu item is enabled");
+ ok(!pasteHTMLMenuItem.disabled, "Paste HTML menu item is enabled");
+ }
+
+ return !deleteMenuItem.disabled &&
+ !editHTMLMenuItem.disabled &&
+ !pasteHTMLMenuItem.disabled;
+});
+
+/**
+ * Wait for all current promises to be resolved. See this as executeSoon that
+ * can be used with yield.
+ */
+function promiseNextTick() {
+ let deferred = defer();
+ executeSoon(deferred.resolve);
+ return deferred.promise;
+}
+
+/**
+ * Collapses the current text selection in an input field and tabs to the next
+ * field.
+ */
+function collapseSelectionAndTab(inspector) {
+ // collapse selection and move caret to end
+ EventUtils.sendKey("tab", inspector.panelWin);
+ // next element
+ EventUtils.sendKey("tab", inspector.panelWin);
+}
+
+/**
+ * Collapses the current text selection in an input field and tabs to the
+ * previous field.
+ */
+function collapseSelectionAndShiftTab(inspector) {
+ // collapse selection and move caret to end
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true },
+ inspector.panelWin);
+ // previous element
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true },
+ inspector.panelWin);
+}
+
+/**
+ * Check that the current focused element is an attribute element in the markup
+ * view.
+ * @param {String} attrName The attribute name expected to be found
+ * @param {Boolean} editMode Whether or not the attribute should be in edit mode
+ */
+function checkFocusedAttribute(attrName, editMode) {
+ let focusedAttr = Services.focus.focusedElement;
+ ok(focusedAttr, "Has a focused element");
+
+ let dataAttr = focusedAttr.parentNode.dataset.attr;
+ is(dataAttr, attrName, attrName + " attribute editor is currently focused.");
+ if (editMode) {
+ // Using a multiline editor for attributes, the focused element should be a textarea.
+ is(focusedAttr.tagName, "textarea", attrName + "is in edit mode");
+ } else {
+ is(focusedAttr.tagName, "span", attrName + "is not in edit mode");
+ }
+}
+
+/**
+ * Get attributes for node as how they are represented in editor.
+ *
+ * @param {String} selector
+ * @param {InspectorPanel} inspector
+ * @return {Promise}
+ * A promise that resolves with an array of attribute names
+ * (e.g. ["id", "class", "href"])
+ */
+var getAttributesFromEditor = Task.async(function* (selector, inspector) {
+ let nodeList = (yield getContainerForSelector(selector, inspector))
+ .tagLine.querySelectorAll("[data-attr]");
+
+ return [...nodeList].map(node => node.getAttribute("data-attr"));
+});
+
+// The expand all operation of the markup-view calls itself recursively and
+// there's not one event we can wait for to know when it's done so use this
+// helper function to wait until all recursive children updates are done.
+function* waitForMultipleChildrenUpdates(inspector) {
+ // As long as child updates are queued up while we wait for an update already
+ // wait again
+ if (inspector.markup._queuedChildUpdates &&
+ inspector.markup._queuedChildUpdates.size) {
+ yield waitForChildrenUpdated(inspector);
+ return yield waitForMultipleChildrenUpdates(inspector);
+ }
+ return undefined;
+}
+
+/**
+ * Create an HTTP server that can be used to simulate custom requests within
+ * a test. It is automatically cleaned up when the test ends, so no need to
+ * call `destroy`.
+ *
+ * See https://developer.mozilla.org/en-US/docs/Httpd.js/HTTP_server_for_unit_tests
+ * for more information about how to register handlers.
+ *
+ * The server can be accessed like:
+ *
+ * const server = createTestHTTPServer();
+ * let url = "http://localhost: " + server.identity.primaryPort + "/path";
+ *
+ * @returns {HttpServer}
+ */
+function createTestHTTPServer() {
+ const {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+ let server = new HttpServer();
+
+ registerCleanupFunction(function* cleanup() {
+ let destroyed = defer();
+ server.stop(() => {
+ destroyed.resolve();
+ });
+ yield destroyed.promise;
+ });
+
+ server.start(-1);
+ return server;
+}
+
+/**
+ * Registers new backend tab actor.
+ *
+ * @param {DebuggerClient} client RDP client object (toolbox.target.client)
+ * @param {Object} options Configuration object with the following options:
+ *
+ * - moduleUrl {String}: URL of the module that contains actor implementation.
+ * - prefix {String}: prefix of the actor.
+ * - actorClass {ActorClassWithSpec}: Constructor object for the actor.
+ * - frontClass {FrontClassWithSpec}: Constructor object for the front part
+ * of the registered actor.
+ *
+ * @returns {Promise} A promise that is resolved when the actor is registered.
+ * The resolved value has two properties:
+ *
+ * - registrar {ActorActor}: A handle to the registered actor that allows
+ * unregistration.
+ * - form {Object}: The JSON actor form provided by the server.
+ */
+function registerTabActor(client, options) {
+ let moduleUrl = options.moduleUrl;
+
+ return client.listTabs().then(response => {
+ let config = {
+ prefix: options.prefix,
+ constructor: options.actorClass,
+ type: { tab: true },
+ };
+
+ // Register the custom actor on the backend.
+ let registry = ActorRegistryFront(client, response);
+ return registry.registerActor(moduleUrl, config).then(registrar => {
+ return client.getTab().then(tabResponse => ({
+ registrar: registrar,
+ form: tabResponse.tab
+ }));
+ });
+ });
+}
+
+/**
+ * A helper for unregistering an existing backend actor.
+ *
+ * @param {ActorActor} registrar A handle to the registered actor
+ * that has been received after registration.
+ * @param {Front} Corresponding front object.
+ *
+ * @returns A promise that is resolved when the unregistration
+ * has finished.
+ */
+function unregisterActor(registrar, front) {
+ return front.detach().then(() => {
+ return registrar.unregister();
+ });
+}
+
+/**
+ * Simulate dragging a MarkupContainer by calling its mousedown and mousemove
+ * handlers.
+ * @param {InspectorPanel} inspector The current inspector-panel instance.
+ * @param {String|MarkupContainer} selector The selector to identify the node or
+ * the MarkupContainer for this node.
+ * @param {Number} xOffset Optional x offset to drag by.
+ * @param {Number} yOffset Optional y offset to drag by.
+ */
+function* simulateNodeDrag(inspector, selector, xOffset = 10, yOffset = 10) {
+ let container = typeof selector === "string"
+ ? yield getContainerForSelector(selector, inspector)
+ : selector;
+ let rect = container.tagLine.getBoundingClientRect();
+ let scrollX = inspector.markup.doc.documentElement.scrollLeft;
+ let scrollY = inspector.markup.doc.documentElement.scrollTop;
+
+ info("Simulate mouseDown on element " + selector);
+ container._onMouseDown({
+ target: container.tagLine,
+ button: 0,
+ pageX: scrollX + rect.x,
+ pageY: scrollY + rect.y,
+ stopPropagation: () => {},
+ preventDefault: () => {}
+ });
+
+ // _onMouseDown selects the node, so make sure to wait for the
+ // inspector-updated event if the current selection was different.
+ if (inspector.selection.nodeFront !== container.node) {
+ yield inspector.once("inspector-updated");
+ }
+
+ info("Simulate mouseMove on element " + selector);
+ container._onMouseMove({
+ pageX: scrollX + rect.x + xOffset,
+ pageY: scrollY + rect.y + yOffset
+ });
+}
+
+/**
+ * Simulate dropping a MarkupContainer by calling its mouseup handler. This is
+ * meant to be called after simulateNodeDrag has been called.
+ * @param {InspectorPanel} inspector The current inspector-panel instance.
+ * @param {String|MarkupContainer} selector The selector to identify the node or
+ * the MarkupContainer for this node.
+ */
+function* simulateNodeDrop(inspector, selector) {
+ info("Simulate mouseUp on element " + selector);
+ let container = typeof selector === "string"
+ ? yield getContainerForSelector(selector, inspector)
+ : selector;
+ container._onMouseUp();
+ inspector.markup._onMouseUp();
+}
+
+/**
+ * Simulate drag'n'dropping a MarkupContainer by calling its mousedown,
+ * mousemove and mouseup handlers.
+ * @param {InspectorPanel} inspector The current inspector-panel instance.
+ * @param {String|MarkupContainer} selector The selector to identify the node or
+ * the MarkupContainer for this node.
+ * @param {Number} xOffset Optional x offset to drag by.
+ * @param {Number} yOffset Optional y offset to drag by.
+ */
+function* simulateNodeDragAndDrop(inspector, selector, xOffset, yOffset) {
+ yield simulateNodeDrag(inspector, selector, xOffset, yOffset);
+ yield simulateNodeDrop(inspector, selector);
+}
+
+/**
+ * Waits until the element has not scrolled for 30 consecutive frames.
+ */
+function* waitForScrollStop(doc) {
+ let el = doc.documentElement;
+ let win = doc.defaultView;
+ let lastScrollTop = el.scrollTop;
+ let stopFrameCount = 0;
+ while (stopFrameCount < 30) {
+ // Wait for a frame.
+ yield new Promise(resolve => win.requestAnimationFrame(resolve));
+
+ // Check if the element has scrolled.
+ if (lastScrollTop == el.scrollTop) {
+ // No scrolling since the last frame.
+ stopFrameCount++;
+ } else {
+ // The element has scrolled. Reset the frame counter.
+ stopFrameCount = 0;
+ lastScrollTop = el.scrollTop;
+ }
+ }
+
+ return lastScrollTop;
+}
+
+/**
+ * Select a node in the inspector and try to delete it using the provided key. After that,
+ * check that the expected element is focused.
+ *
+ * @param {InspectorPanel} inspector
+ * The current inspector-panel instance.
+ * @param {String} key
+ * The key to simulate to delete the node
+ * @param {Object}
+ * - {String} selector: selector of the element to delete.
+ * - {String} focusedSelector: selector of the element that should be selected
+ * after deleting the node.
+ * - {String} pseudo: optional, "before" or "after" if the element focused after
+ * deleting the node is supposed to be a before/after pseudo-element.
+ */
+function* checkDeleteAndSelection(inspector, key, {selector, focusedSelector, pseudo}) {
+ info("Test deleting node " + selector + " with " + key + ", " +
+ "expecting " + focusedSelector + " to be focused");
+
+ info("Select node " + selector + " and make sure it is focused");
+ yield selectNode(selector, inspector);
+ yield clickContainer(selector, inspector);
+
+ info("Delete the node with: " + key);
+ let mutated = inspector.once("markupmutation");
+ EventUtils.sendKey(key, inspector.panelWin);
+ yield Promise.all([mutated, inspector.once("inspector-updated")]);
+
+ let nodeFront = yield getNodeFront(focusedSelector, inspector);
+ if (pseudo) {
+ // Update the selector for logging in case of failure.
+ focusedSelector = focusedSelector + "::" + pseudo;
+ // Retrieve the :before or :after pseudo element of the nodeFront.
+ let {nodes} = yield inspector.walker.children(nodeFront);
+ nodeFront = pseudo === "before" ? nodes[0] : nodes[nodes.length - 1];
+ }
+
+ is(inspector.selection.nodeFront, nodeFront,
+ focusedSelector + " is selected after deletion");
+
+ info("Check that the node was really removed");
+ let node = yield getNodeFront(selector, inspector);
+ ok(!node, "The node can't be found in the page anymore");
+
+ info("Undo the deletion to restore the original markup");
+ yield undoChange(inspector);
+ node = yield getNodeFront(selector, inspector);
+ ok(node, "The node is back");
+}
diff --git a/devtools/client/inspector/markup/test/helper_attributes_test_runner.js b/devtools/client/inspector/markup/test/helper_attributes_test_runner.js
new file mode 100644
index 000000000..20446d3d1
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_attributes_test_runner.js
@@ -0,0 +1,160 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from head.js */
+"use strict";
+
+/**
+ * Run a series of add-attributes tests.
+ * This function will iterate over the provided tests array and run each test.
+ * Each test's goal is to provide some text to be entered into the test node's
+ * new-attribute field and check that the given attributes have been created.
+ * After each test has run, the markup-view's undo command will be called and
+ * the test runner will check if all the new attributes are gone.
+ * @param {Array} tests See runAddAttributesTest for the structure
+ * @param {DOMNode|String} nodeOrSelector The node or node selector
+ * corresponding to an element on the current test page that has *no attributes*
+ * when the test starts. It will be used to add and remove attributes.
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * opened
+ * @param {TestActorFront} testActor The current TestActorFront instance.
+ * @return a promise that resolves when the tests have run
+ */
+function runAddAttributesTests(tests, nodeOrSelector, inspector, testActor) {
+ info("Running " + tests.length + " add-attributes tests");
+ return Task.spawn(function* () {
+ info("Selecting the test node");
+ yield selectNode("div", inspector);
+
+ for (let test of tests) {
+ yield runAddAttributesTest(test, "div", inspector, testActor);
+ }
+ });
+}
+
+/**
+ * Run a single add-attribute test.
+ * See runAddAttributesTests for a description.
+ * @param {Object} test A test object should contain the following properties:
+ * - desc {String} a textual description for that test, to help when
+ * reading logs
+ * - text {String} the string to be inserted into the new attribute field
+ * - expectedAttributes {Object} a key/value pair object that will be
+ * used to check the attributes on the test element
+ * - validate {Function} optional extra function that will be called
+ * after the attributes have been added and which should be used to
+ * assert some more things this test runner might not be checking. The
+ * function will be called with the following arguments:
+ * - {DOMNode} The element being tested
+ * - {MarkupContainer} The corresponding container in the markup-view
+ * - {InspectorPanel} The instance of the InspectorPanel opened
+ * @param {String} selector The node selector corresponding to the test element
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * @param {TestActorFront} testActor The current TestActorFront instance.
+ * opened
+ */
+function* runAddAttributesTest(test, selector, inspector, testActor) {
+ if (test.setUp) {
+ test.setUp(inspector);
+ }
+
+ info("Starting add-attribute test: " + test.desc);
+ yield addNewAttributes(selector, test.text, inspector);
+
+ info("Assert that the attribute(s) has/have been applied correctly");
+ yield assertAttributes(selector, test.expectedAttributes, testActor);
+
+ if (test.validate) {
+ let container = yield getContainerForSelector(selector, inspector);
+ test.validate(container, inspector);
+ }
+
+ info("Undo the change");
+ yield undoChange(inspector);
+
+ info("Assert that the attribute(s) has/have been removed correctly");
+ yield assertAttributes(selector, {}, testActor);
+ if (test.tearDown) {
+ test.tearDown(inspector);
+ }
+}
+
+/**
+ * Run a series of edit-attributes tests.
+ * This function will iterate over the provided tests array and run each test.
+ * Each test's goal is to locate a given element on the current test page,
+ * assert its current attributes, then provide the name of one of them and a
+ * value to be set into it, and then check if the new attributes are correct.
+ * After each test has run, the markup-view's undo and redo commands will be
+ * called and the test runner will assert again that the attributes are correct.
+ * @param {Array} tests See runEditAttributesTest for the structure
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * opened
+ * @param {TestActorFront} testActor The current TestActorFront instance.
+ * @return a promise that resolves when the tests have run
+ */
+function runEditAttributesTests(tests, inspector, testActor) {
+ info("Running " + tests.length + " edit-attributes tests");
+ return Task.spawn(function* () {
+ info("Expanding all nodes in the markup-view");
+ yield inspector.markup.expandAll();
+
+ for (let test of tests) {
+ yield runEditAttributesTest(test, inspector, testActor);
+ }
+ });
+}
+
+/**
+ * Run a single edit-attribute test.
+ * See runEditAttributesTests for a description.
+ * @param {Object} test A test object should contain the following properties:
+ * - desc {String} a textual description for that test, to help when
+ * reading logs
+ * - node {String} a css selector that will be used to select the node
+ * which will be tested during this iteration
+ * - originalAttributes {Object} a key/value pair object that will be
+ * used to check the attributes of the node before the test runs
+ * - name {String} the name of the attribute to focus the editor for
+ * - value {String} the new value to be typed in the focused editor
+ * - expectedAttributes {Object} a key/value pair object that will be
+ * used to check the attributes on the test element
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * @param {TestActorFront} testActor The current TestActorFront instance.
+ * opened
+ */
+function* runEditAttributesTest(test, inspector, testActor) {
+ info("Starting edit-attribute test: " + test.desc);
+
+ info("Selecting the test node " + test.node);
+ yield selectNode(test.node, inspector);
+
+ info("Asserting that the node has the right attributes to start with");
+ yield assertAttributes(test.node, test.originalAttributes, testActor);
+
+ info("Editing attribute " + test.name + " with value " + test.value);
+
+ let container = yield focusNode(test.node, inspector);
+ ok(container && container.editor, "The markup-container for " + test.node +
+ " was found");
+
+ info("Listening for the markupmutation event");
+ let nodeMutated = inspector.once("markupmutation");
+ let attr = container.editor.attrElements.get(test.name)
+ .querySelector(".editable");
+ setEditableFieldValue(attr, test.value, inspector);
+ yield nodeMutated;
+
+ info("Asserting the new attributes after edition");
+ yield assertAttributes(test.node, test.expectedAttributes, testActor);
+
+ info("Undo the change and assert that the attributes have been changed back");
+ yield undoChange(inspector);
+ yield assertAttributes(test.node, test.originalAttributes, testActor);
+
+ info("Redo the change and assert that the attributes have been changed " +
+ "again");
+ yield redoChange(inspector);
+ yield assertAttributes(test.node, test.expectedAttributes, testActor);
+}
diff --git a/devtools/client/inspector/markup/test/helper_events_test_runner.js b/devtools/client/inspector/markup/test/helper_events_test_runner.js
new file mode 100644
index 000000000..acef334fb
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -0,0 +1,111 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from head.js */
+"use strict";
+
+/**
+ * Generator function that runs checkEventsForNode() for each object in the
+ * TEST_DATA array.
+ */
+function* runEventPopupTests(url, tests) {
+ let {inspector, testActor} = yield openInspectorForURL(url);
+
+ yield inspector.markup.expandAll();
+
+ for (let test of tests) {
+ yield checkEventsForNode(test, inspector, testActor);
+ }
+
+ // Wait for promises to avoid leaks when running this as a single test.
+ // We need to do this because we have opened a bunch of popups and don't them
+ // to affect other test runs when they are GCd.
+ yield promiseNextTick();
+}
+
+/**
+ * Generator function that takes a selector and expected results and returns
+ * the event info.
+ *
+ * @param {Object} test
+ * A test object should contain the following properties:
+ * - selector {String} a css selector targeting the node to edit
+ * - expected {Array} array of expected event objects
+ * - type {String} event type
+ * - filename {String} filename:line where the evt handler is defined
+ * - attributes {Array} array of event attributes ({String})
+ * - handler {String} string representation of the handler
+ * - beforeTest {Function} (optional) a function to execute on the page
+ * before running the test
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * opened
+ * @param {TestActorFront} testActor
+ */
+function* checkEventsForNode(test, inspector, testActor) {
+ let {selector, expected, beforeTest} = test;
+ let container = yield getContainerForSelector(selector, inspector);
+
+ if (typeof beforeTest === "function") {
+ yield beforeTest(inspector, testActor);
+ }
+
+ let evHolder = container.elt.querySelector(".markupview-events");
+
+ if (expected.length === 0) {
+ // if no event is expected, simply check that the event bubble is hidden
+ is(evHolder.style.display, "none", "event bubble should be hidden");
+ return;
+ }
+
+ let tooltip = inspector.markup.eventDetailsTooltip;
+
+ yield selectNode(selector, inspector);
+
+ // Click button to show tooltip
+ info("Clicking evHolder");
+ EventUtils.synthesizeMouseAtCenter(evHolder, {},
+ inspector.markup.doc.defaultView);
+ yield tooltip.once("shown");
+ info("tooltip shown");
+
+ // Check values
+ let headers = tooltip.panel.querySelectorAll(".event-header");
+ let nodeFront = container.node;
+ let cssSelector = nodeFront.nodeName + "#" + nodeFront.id;
+
+ for (let i = 0; i < headers.length; i++) {
+ info("Processing header[" + i + "] for " + cssSelector);
+
+ let header = headers[i];
+ let type = header.querySelector(".event-tooltip-event-type");
+ let filename = header.querySelector(".event-tooltip-filename");
+ let attributes = header.querySelectorAll(".event-tooltip-attributes");
+ let contentBox = header.nextElementSibling;
+
+ is(type.textContent, expected[i].type,
+ "type matches for " + cssSelector);
+ is(filename.textContent, expected[i].filename,
+ "filename matches for " + cssSelector);
+
+ is(attributes.length, expected[i].attributes.length,
+ "we have the correct number of attributes");
+
+ for (let j = 0; j < expected[i].attributes.length; j++) {
+ is(attributes[j].textContent, expected[i].attributes[j],
+ "attribute[" + j + "] matches for " + cssSelector);
+ }
+
+ // Make sure the header is not hidden by scrollbars before clicking.
+ header.scrollIntoView();
+
+ EventUtils.synthesizeMouseAtCenter(header, {}, type.ownerGlobal);
+ yield tooltip.once("event-tooltip-ready");
+
+ let editor = tooltip.eventTooltip._eventEditors.get(contentBox).editor;
+ is(editor.getText(), expected[i].handler,
+ "handler matches for " + cssSelector);
+ }
+
+ tooltip.hide();
+}
diff --git a/devtools/client/inspector/markup/test/helper_markup_accessibility_navigation.js b/devtools/client/inspector/markup/test/helper_markup_accessibility_navigation.js
new file mode 100644
index 000000000..a49f1e7ba
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_markup_accessibility_navigation.js
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from head.js */
+"use strict";
+
+/**
+ * Execute a keyboard event and check that the state is as expected (focused element, aria
+ * attribute etc...).
+ *
+ * @param {InspectorPanel} inspector
+ * Current instance of the inspector being tested.
+ * @param {Object} elms
+ * Map of elements that will be used to retrieve live references to children
+ * elements
+ * @param {Element} focused
+ * Element expected to be focused
+ * @param {Element} activedescendant
+ * Element expected to be the aria activedescendant of the root node
+ */
+function testNavigationState(inspector, elms, focused, activedescendant) {
+ let doc = inspector.markup.doc;
+ let id = activedescendant.getAttribute("id");
+ is(doc.activeElement, focused, `Keyboard focus should be set to ${focused}`);
+ is(elms.root.elt.getAttribute("aria-activedescendant"), id,
+ `Active descendant should be set to ${id}`);
+}
+
+/**
+ * Execute a keyboard event and check that the state is as expected (focused element, aria
+ * attribute etc...).
+ *
+ * @param {InspectorPanel} inspector
+ * Current instance of the inspector being tested.
+ * @param {Object} elms
+ * MarkupContainers/Elements that will be used to retrieve references to other
+ * elements based on objects' paths.
+ * @param {Object} testData
+ * - {String} desc: description for better logging.
+ * - {String} key: keyboard event's key.
+ * - {Object} options, optional: event data such as shiftKey, etc.
+ * - {String} focused: path to expected focused element in elms map.
+ * - {String} activedescendant: path to expected aria-activedescendant element in
+ * elms map.
+ * - {String} waitFor, optional: markupview event to wait for if keyboard actions
+ * result in async updates. Also accepts the inspector event "inspector-updated".
+ */
+function* runAccessibilityNavigationTest(inspector, elms,
+ {desc, key, options, focused, activedescendant, waitFor}) {
+ info(desc);
+
+ let markup = inspector.markup;
+ let doc = markup.doc;
+ let win = doc.defaultView;
+
+ let updated;
+ if (waitFor) {
+ updated = waitFor === "inspector-updated" ?
+ inspector.once(waitFor) : markup.once(waitFor);
+ } else {
+ updated = Promise.resolve();
+ }
+ EventUtils.synthesizeKey(key, options, win);
+ yield updated;
+
+ let focusedElement = lookupPath(elms, focused);
+ let activeDescendantElement = lookupPath(elms, activedescendant);
+ testNavigationState(inspector, elms, focusedElement, activeDescendantElement);
+}
diff --git a/devtools/client/inspector/markup/test/helper_outerhtml_test_runner.js b/devtools/client/inspector/markup/test/helper_outerhtml_test_runner.js
new file mode 100644
index 000000000..f2de0876f
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_outerhtml_test_runner.js
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from head.js */
+"use strict";
+
+/**
+ * Run a series of edit-outer-html tests.
+ * This function will iterate over the provided tests array and run each test.
+ * Each test's goal is to provide a node (a selector) and a new outer-HTML to be
+ * inserted in place of the current one for that node.
+ * This test runner will wait for the mutation event to be fired and will check
+ * a few things. Each test may also provide its own validate function to perform
+ * assertions and verify that the new outer html is correct.
+ * @param {Array} tests See runEditOuterHTMLTest for the structure
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * opened
+ * @param {TestActorFront} testActor The current TestActorFront instance
+ * @return a promise that resolves when the tests have run
+ */
+function runEditOuterHTMLTests(tests, inspector, testActor) {
+ info("Running " + tests.length + " edit-outer-html tests");
+ return Task.spawn(function* () {
+ for (let step of tests) {
+ yield runEditOuterHTMLTest(step, inspector, testActor);
+ }
+ });
+}
+
+/**
+ * Run a single edit-outer-html test.
+ * See runEditOuterHTMLTests for a description.
+ * @param {Object} test A test object should contain the following properties:
+ * - selector {String} a css selector targeting the node to edit
+ * - oldHTML {String}
+ * - newHTML {String}
+ * - validate {Function} will be executed when the edition test is done,
+ * after the new outer-html has been inserted. Should be used to verify
+ * the actual DOM, see if it corresponds to the newHTML string provided
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * @param {TestActorFront} testActor The current TestActorFront instance
+ * opened
+ */
+function* runEditOuterHTMLTest(test, inspector, testActor) {
+ info("Running an edit outerHTML test on '" + test.selector + "'");
+ yield selectNode(test.selector, inspector);
+
+ let onUpdated = inspector.once("inspector-updated");
+
+ info("Listen for reselectedonremoved and edit the outerHTML");
+ let onReselected = inspector.markup.once("reselectedonremoved");
+ yield inspector.markup.updateNodeOuterHTML(inspector.selection.nodeFront,
+ test.newHTML, test.oldHTML);
+ yield onReselected;
+
+ // Typically selectedNode will === pageNode, but if a new element has been
+ // injected in front of it, this will not be the case. If this happens.
+ let selectedNodeFront = inspector.selection.nodeFront;
+ let pageNodeFront = yield inspector.walker.querySelector(
+ inspector.walker.rootNode, test.selector);
+
+ if (test.validate) {
+ yield test.validate({pageNodeFront, selectedNodeFront,
+ inspector, testActor});
+ } else {
+ is(pageNodeFront, selectedNodeFront,
+ "Original node (grabbed by selector) is selected");
+ let {outerHTML} = yield testActor.getNodeInfo(test.selector);
+ is(outerHTML, test.newHTML, "Outer HTML has been updated");
+ }
+
+ // Wait for the inspector to be fully updated to avoid causing errors by
+ // abruptly closing hanging requests when the test ends
+ yield onUpdated;
+
+ let closeTagLine = inspector.markup.getContainer(pageNodeFront).closeTagLine;
+ if (closeTagLine) {
+ is(closeTagLine.querySelectorAll(".theme-fg-contrast").length, 0,
+ "No contrast class");
+ }
+}
diff --git a/devtools/client/inspector/markup/test/helper_style_attr_test_runner.js b/devtools/client/inspector/markup/test/helper_style_attr_test_runner.js
new file mode 100644
index 000000000..f884a8181
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_style_attr_test_runner.js
@@ -0,0 +1,132 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from head.js */
+"use strict";
+
+/**
+ * Perform an style attribute edition and autocompletion test in the test
+ * url, for #node14. Test data should be an
+ * array of arrays structured as follows :
+ * [
+ * what key to press,
+ * expected input box value after keypress,
+ * expected input.selectionStart,
+ * expected input.selectionEnd,
+ * is popup expected to be open ?
+ * ]
+ *
+ * The test will start by adding a new attribute on the node, and then send each
+ * key specified in the testData. The last item of this array should leave the
+ * new attribute editor, either by committing or cancelling the edit.
+ *
+ * @param {InspectorPanel} inspector
+ * @param {Array} testData
+ * Array of arrays representing the characters to type for the new
+ * attribute as well as the expected state at each step
+ */
+function* runStyleAttributeAutocompleteTests(inspector, testData) {
+ info("Expand all markup nodes");
+ yield inspector.markup.expandAll();
+
+ info("Select #node14");
+ let container = yield focusNode("#node14", inspector);
+
+ info("Focus and open the new attribute inplace-editor");
+ let attr = container.editor.newAttr;
+ attr.focus();
+ EventUtils.sendKey("return", inspector.panelWin);
+ let editor = inplaceEditor(attr);
+
+ for (let i = 0; i < testData.length; i++) {
+ let data = testData[i];
+
+ // Expect a markupmutation event at the last iteration since that's when the
+ // attribute is actually created.
+ let onMutation = i === testData.length - 1
+ ? inspector.once("markupmutation") : null;
+
+ info(`Entering test data ${i}: ${data[0]}, expecting: [${data[1]}]`);
+ yield enterData(data, editor, inspector);
+
+ info(`Test data ${i} entered. Checking state.`);
+ yield checkData(data, editor, inspector);
+
+ yield onMutation;
+ }
+
+ // Undoing the action will remove the new attribute, so make sure to wait for
+ // the markupmutation event here again.
+ let onMutation = inspector.once("markupmutation");
+ while (inspector.markup.undo.canUndo()) {
+ yield undoChange(inspector);
+ }
+ yield onMutation;
+}
+
+/**
+ * Process a test data entry.
+ * @param {Array} data
+ * test data - click or key - to enter
+ * @param {InplaceEditor} editor
+ * @param {InspectorPanel} inspector
+ * @return {Promise} promise that will resolve when the test data has been
+ * applied
+ */
+function enterData(data, editor, inspector) {
+ let key = data[0];
+
+ if (/^click_[0-9]+$/.test(key)) {
+ let suggestionIndex = parseInt(key.split("_")[1], 10);
+ return clickOnSuggestion(suggestionIndex, editor);
+ }
+
+ return sendKey(key, editor, inspector);
+}
+
+function clickOnSuggestion(index, editor) {
+ return new Promise(resolve => {
+ info("Clicking on item " + index + " in the list");
+ editor.once("after-suggest", () => executeSoon(resolve));
+ editor.popup._list.childNodes[index].click();
+ });
+}
+
+function sendKey(key, editor, inspector) {
+ return new Promise(resolve => {
+ if (/(down|left|right|back_space|return)/ig.test(key)) {
+ info("Adding event listener for down|left|right|back_space|return keys");
+ editor.input.addEventListener("keypress", function onKeypress() {
+ if (editor.input) {
+ editor.input.removeEventListener("keypress", onKeypress);
+ }
+ executeSoon(resolve);
+ });
+ } else {
+ editor.once("after-suggest", () => executeSoon(resolve));
+ }
+
+ EventUtils.synthesizeKey(key, {}, inspector.panelWin);
+ });
+}
+
+/**
+ * Verify that the inplace editor is in the expected state for the provided
+ * test data.
+ */
+function* checkData(data, editor, inspector) {
+ let [, completion, selStart, selEnd, popupOpen] = data;
+
+ if (selEnd != -1) {
+ is(editor.input.value, completion, "Completed value is correct");
+ is(editor.input.selectionStart, selStart, "Selection start position is correct");
+ is(editor.input.selectionEnd, selEnd, "Selection end position is correct");
+ is(editor.popup.isOpen, popupOpen, "Popup is " + (popupOpen ? "open" : "closed"));
+ } else {
+ let nodeFront = yield getNodeFront("#node14", inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ let attr = container.editor.attrElements.get("style").querySelector(".editable");
+ is(attr.textContent, completion, "Correct value is persisted after pressing Enter");
+ }
+}
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.0.js b/devtools/client/inspector/markup/test/lib_jquery_1.0.js
new file mode 100644
index 000000000..564361282
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.0.js
@@ -0,0 +1,1814 @@
+/*
+ * jQuery - New Wave Javascript
+ *
+ * Copyright (c) 2006 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2006-10-27 23:14:48 -0400 (Fri, 27 Oct 2006) $
+ * $Rev: 509 $
+ */
+
+// Global undefined variable
+window.undefined = window.undefined;
+function jQuery(a,c) {
+
+ // Shortcut for document ready (because $(document).each() is silly)
+ if ( a && a.constructor == Function && jQuery.fn.ready )
+ return jQuery(document).ready(a);
+
+ // Make sure that a selection was provided
+ a = a || jQuery.context || document;
+
+ // Watch for when a jQuery object is passed as the selector
+ if ( a.jquery )
+ return $( jQuery.merge( a, [] ) );
+
+ // Watch for when a jQuery object is passed at the context
+ if ( c && c.jquery )
+ return $( c ).find(a);
+
+ // If the context is global, return a new object
+ if ( window == this )
+ return new jQuery(a,c);
+
+ // Handle HTML strings
+ var m = /^[^<]*(<.+>)[^>]*$/.exec(a);
+ if ( m ) a = jQuery.clean( [ m[1] ] );
+
+ // Watch for when an array is passed in
+ this.get( a.constructor == Array || a.length && !a.nodeType && a[0] != undefined && a[0].nodeType ?
+ // Assume that it is an array of DOM Elements
+ jQuery.merge( a, [] ) :
+
+ // Find the matching elements and save them for later
+ jQuery.find( a, c ) );
+
+ // See if an extra function was provided
+ var fn = arguments[ arguments.length - 1 ];
+
+ // If so, execute it in context
+ if ( fn && fn.constructor == Function )
+ this.each(fn);
+}
+
+// Map over the $ in case of overwrite
+if ( $ )
+ jQuery._$ = $;
+
+// Map the jQuery namespace to the '$' one
+var $ = jQuery;
+
+jQuery.fn = jQuery.prototype = {
+ jquery: "$Rev: 509 $",
+
+ size: function() {
+ return this.length;
+ },
+
+ get: function( num ) {
+ // Watch for when an array (of elements) is passed in
+ if ( num && num.constructor == Array ) {
+
+ // Use a tricky hack to make the jQuery object
+ // look and feel like an array
+ this.length = 0;
+ [].push.apply( this, num );
+
+ return this;
+ } else
+ return num == undefined ?
+
+ // Return a 'clean' array
+ jQuery.map( this, function(a){ return a } ) :
+
+ // Return just the object
+ this[num];
+ },
+ each: function( fn, args ) {
+ return jQuery.each( this, fn, args );
+ },
+
+ index: function( obj ) {
+ var pos = -1;
+ this.each(function(i){
+ if ( this == obj ) pos = i;
+ });
+ return pos;
+ },
+
+ attr: function( key, value, type ) {
+ // Check to see if we're setting style values
+ return key.constructor != String || value != undefined ?
+ this.each(function(){
+ // See if we're setting a hash of styles
+ if ( value == undefined )
+ // Set all the styles
+ for ( var prop in key )
+ jQuery.attr(
+ type ? this.style : this,
+ prop, key[prop]
+ );
+
+ // See if we're setting a single key/value style
+ else
+ jQuery.attr(
+ type ? this.style : this,
+ key, value
+ );
+ }) :
+
+ // Look for the case where we're accessing a style value
+ jQuery[ type || "attr" ]( this[0], key );
+ },
+
+ css: function( key, value ) {
+ return this.attr( key, value, "curCSS" );
+ },
+ text: function(e) {
+ e = e || this;
+ var t = "";
+ for ( var j = 0; j < e.length; j++ ) {
+ var r = e[j].childNodes;
+ for ( var i = 0; i < r.length; i++ )
+ t += r[i].nodeType != 1 ?
+ r[i].nodeValue : jQuery.fn.text([ r[i] ]);
+ }
+ return t;
+ },
+ wrap: function() {
+ // The elements to wrap the target around
+ var a = jQuery.clean(arguments);
+
+ // Wrap each of the matched elements individually
+ return this.each(function(){
+ // Clone the structure that we're using to wrap
+ var b = a[0].cloneNode(true);
+
+ // Insert it before the element to be wrapped
+ this.parentNode.insertBefore( b, this );
+
+ // Find he deepest point in the wrap structure
+ while ( b.firstChild )
+ b = b.firstChild;
+
+ // Move the matched element to within the wrap structure
+ b.appendChild( this );
+ });
+ },
+ append: function() {
+ return this.domManip(arguments, true, 1, function(a){
+ this.appendChild( a );
+ });
+ },
+ prepend: function() {
+ return this.domManip(arguments, true, -1, function(a){
+ this.insertBefore( a, this.firstChild );
+ });
+ },
+ before: function() {
+ return this.domManip(arguments, false, 1, function(a){
+ this.parentNode.insertBefore( a, this );
+ });
+ },
+ after: function() {
+ return this.domManip(arguments, false, -1, function(a){
+ this.parentNode.insertBefore( a, this.nextSibling );
+ });
+ },
+ end: function() {
+ return this.get( this.stack.pop() );
+ },
+ find: function(t) {
+ return this.pushStack( jQuery.map( this, function(a){
+ return jQuery.find(t,a);
+ }), arguments );
+ },
+
+ clone: function(deep) {
+ return this.pushStack( jQuery.map( this, function(a){
+ return a.cloneNode( deep != undefined ? deep : true );
+ }), arguments );
+ },
+
+ filter: function(t) {
+ return this.pushStack(
+ t.constructor == Array &&
+ jQuery.map(this,function(a){
+ for ( var i = 0; i < t.length; i++ )
+ if ( jQuery.filter(t[i],[a]).r.length )
+ return a;
+ }) ||
+
+ t.constructor == Boolean &&
+ ( t ? this.get() : [] ) ||
+
+ t.constructor == Function &&
+ jQuery.grep( this, t ) ||
+
+ jQuery.filter(t,this).r, arguments );
+ },
+
+ not: function(t) {
+ return this.pushStack( t.constructor == String ?
+ jQuery.filter(t,this,false).r :
+ jQuery.grep(this,function(a){ return a != t; }), arguments );
+ },
+
+ add: function(t) {
+ return this.pushStack( jQuery.merge( this, t.constructor == String ?
+ jQuery.find(t) : t.constructor == Array ? t : [t] ), arguments );
+ },
+ is: function(expr) {
+ return expr ? jQuery.filter(expr,this).r.length > 0 : this.length > 0;
+ },
+ domManip: function(args, table, dir, fn){
+ var clone = this.size() > 1;
+ var a = jQuery.clean(args);
+
+ return this.each(function(){
+ var obj = this;
+
+ if ( table && this.nodeName == "TABLE" && a[0].nodeName != "THEAD" ) {
+ var tbody = this.getElementsByTagName("tbody");
+
+ if ( !tbody.length ) {
+ obj = document.createElement("tbody");
+ this.appendChild( obj );
+ } else
+ obj = tbody[0];
+ }
+
+ for ( var i = ( dir < 0 ? a.length - 1 : 0 );
+ i != ( dir < 0 ? dir : a.length ); i += dir ) {
+ fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] );
+ }
+ });
+ },
+ pushStack: function(a,args) {
+ var fn = args && args[args.length-1];
+
+ if ( !fn || fn.constructor != Function ) {
+ if ( !this.stack ) this.stack = [];
+ this.stack.push( this.get() );
+ this.get( a );
+ } else {
+ var old = this.get();
+ this.get( a );
+ if ( fn.constructor == Function )
+ return this.each( fn );
+ this.get( old );
+ }
+
+ return this;
+ }
+};
+
+jQuery.extend = jQuery.fn.extend = function(obj,prop) {
+ if ( !prop ) { prop = obj; obj = this; }
+ for ( var i in prop ) obj[i] = prop[i];
+ return obj;
+};
+
+jQuery.extend({
+ init: function(){
+ jQuery.initDone = true;
+
+ jQuery.each( jQuery.macros.axis, function(i,n){
+ jQuery.fn[ i ] = function(a) {
+ var ret = jQuery.map(this,n);
+ if ( a && a.constructor == String )
+ ret = jQuery.filter(a,ret).r;
+ return this.pushStack( ret, arguments );
+ };
+ });
+
+ jQuery.each( jQuery.macros.to, function(i,n){
+ jQuery.fn[ i ] = function(){
+ var a = arguments;
+ return this.each(function(){
+ for ( var j = 0; j < a.length; j++ )
+ $(a[j])[n]( this );
+ });
+ };
+ });
+
+ jQuery.each( jQuery.macros.each, function(i,n){
+ jQuery.fn[ i ] = function() {
+ return this.each( n, arguments );
+ };
+ });
+
+ jQuery.each( jQuery.macros.filter, function(i,n){
+ jQuery.fn[ n ] = function(num,fn) {
+ return this.filter( ":" + n + "(" + num + ")", fn );
+ };
+ });
+
+ jQuery.each( jQuery.macros.attr, function(i,n){
+ n = n || i;
+ jQuery.fn[ i ] = function(h) {
+ return h == undefined ?
+ this.length ? this[0][n] : null :
+ this.attr( n, h );
+ };
+ });
+
+ jQuery.each( jQuery.macros.css, function(i,n){
+ jQuery.fn[ n ] = function(h) {
+ return h == undefined ?
+ ( this.length ? jQuery.css( this[0], n ) : null ) :
+ this.css( n, h );
+ };
+ });
+
+ },
+ each: function( obj, fn, args ) {
+ if ( obj.length == undefined )
+ for ( var i in obj )
+ fn.apply( obj[i], args || [i, obj[i]] );
+ else
+ for ( var i = 0; i < obj.length; i++ )
+ fn.apply( obj[i], args || [i, obj[i]] );
+ return obj;
+ },
+
+ className: {
+ add: function(o,c){
+ if (jQuery.className.has(o,c)) return;
+ o.className += ( o.className ? " " : "" ) + c;
+ },
+ remove: function(o,c){
+ o.className = !c ? "" :
+ o.className.replace(
+ new RegExp("(^|\\s*\\b[^-])"+c+"($|\\b(?=[^-]))", "g"), "");
+ },
+ has: function(e,a) {
+ if ( e.className != undefined )
+ e = e.className;
+ return new RegExp("(^|\\s)" + a + "(\\s|$)").test(e);
+ }
+ },
+ swap: function(e,o,f) {
+ for ( var i in o ) {
+ e.style["old"+i] = e.style[i];
+ e.style[i] = o[i];
+ }
+ f.apply( e, [] );
+ for ( var i in o )
+ e.style[i] = e.style["old"+i];
+ },
+
+ css: function(e,p) {
+ if ( p == "height" || p == "width" ) {
+ var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"];
+
+ for ( var i in d ) {
+ old["padding" + d[i]] = 0;
+ old["border" + d[i] + "Width"] = 0;
+ }
+
+ jQuery.swap( e, old, function() {
+ if (jQuery.css(e,"display") != "none") {
+ oHeight = e.offsetHeight;
+ oWidth = e.offsetWidth;
+ } else {
+ e = $(e.cloneNode(true)).css({
+ visibility: "hidden", position: "absolute", display: "block"
+ }).prependTo("body")[0];
+
+ oHeight = e.clientHeight;
+ oWidth = e.clientWidth;
+
+ e.parentNode.removeChild(e);
+ }
+ });
+
+ return p == "height" ? oHeight : oWidth;
+ } else if ( p == "opacity" && jQuery.browser.msie )
+ return parseFloat( jQuery.curCSS(e,"filter").replace(/[^0-9.]/,"") ) || 1;
+
+ return jQuery.curCSS( e, p );
+ },
+
+ curCSS: function(elem, prop, force) {
+ var ret;
+
+ if (!force && elem.style[prop]) {
+
+ ret = elem.style[prop];
+
+ } else if (elem.currentStyle) {
+
+ var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase()});
+ ret = elem.currentStyle[prop] || elem.currentStyle[newProp];
+
+ } else if (document.defaultView && document.defaultView.getComputedStyle) {
+
+ prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
+ var cur = document.defaultView.getComputedStyle(elem, null);
+
+ if ( cur )
+ ret = cur.getPropertyValue(prop);
+ else if ( prop == 'display' )
+ ret = 'none';
+ else
+ jQuery.swap(elem, { display: 'block' }, function() {
+ ret = document.defaultView.getComputedStyle(this,null).getPropertyValue(prop);
+ });
+
+ }
+
+ return ret;
+ },
+
+ clean: function(a) {
+ var r = [];
+ for ( var i = 0; i < a.length; i++ ) {
+ if ( a[i].constructor == String ) {
+
+ var table = "";
+
+ if ( !a[i].indexOf("<thead") || !a[i].indexOf("<tbody") ) {
+ table = "thead";
+ a[i] = "<table>" + a[i] + "</table>";
+ } else if ( !a[i].indexOf("<tr") ) {
+ table = "tr";
+ a[i] = "<table>" + a[i] + "</table>";
+ } else if ( !a[i].indexOf("<td") || !a[i].indexOf("<th") ) {
+ table = "td";
+ a[i] = "<table><tbody><tr>" + a[i] + "</tr></tbody></table>";
+ }
+
+ var div = document.createElement("div");
+ div.innerHTML = a[i];
+
+ if ( table ) {
+ div = div.firstChild;
+ if ( table != "thead" ) div = div.firstChild;
+ if ( table == "td" ) div = div.firstChild;
+ }
+
+ for ( var j = 0; j < div.childNodes.length; j++ )
+ r.push( div.childNodes[j] );
+ } else if ( a[i].jquery || a[i].length && !a[i].nodeType )
+ for ( var k = 0; k < a[i].length; k++ )
+ r.push( a[i][k] );
+ else if ( a[i] !== null )
+ r.push( a[i].nodeType ? a[i] : document.createTextNode(a[i].toString()) );
+ }
+ return r;
+ },
+
+ expr: {
+ "": "m[2]== '*'||a.nodeName.toUpperCase()==m[2].toUpperCase()",
+ "#": "a.getAttribute('id')&&a.getAttribute('id')==m[2]",
+ ":": {
+ // Position Checks
+ lt: "i<m[3]-0",
+ gt: "i>m[3]-0",
+ nth: "m[3]-0==i",
+ eq: "m[3]-0==i",
+ first: "i==0",
+ last: "i==r.length-1",
+ even: "i%2==0",
+ odd: "i%2",
+
+ // Child Checks
+ "first-child": "jQuery.sibling(a,0).cur",
+ "last-child": "jQuery.sibling(a,0).last",
+ "only-child": "jQuery.sibling(a).length==1",
+
+ // Parent Checks
+ parent: "a.childNodes.length",
+ empty: "!a.childNodes.length",
+
+ // Text Check
+ contains: "(a.innerText||a.innerHTML).indexOf(m[3])>=0",
+
+ // Visibility
+ visible: "a.type!='hidden'&&jQuery.css(a,'display')!='none'&&jQuery.css(a,'visibility')!='hidden'",
+ hidden: "a.type=='hidden'||jQuery.css(a,'display')=='none'||jQuery.css(a,'visibility')=='hidden'",
+
+ // Form elements
+ enabled: "!a.disabled",
+ disabled: "a.disabled",
+ checked: "a.checked",
+ selected: "a.selected"
+ },
+ ".": "jQuery.className.has(a,m[2])",
+ "@": {
+ "=": "z==m[4]",
+ "!=": "z!=m[4]",
+ "^=": "!z.indexOf(m[4])",
+ "$=": "z.substr(z.length - m[4].length,m[4].length)==m[4]",
+ "*=": "z.indexOf(m[4])>=0",
+ "": "z"
+ },
+ "[": "jQuery.find(m[2],a).length"
+ },
+
+ token: [
+ "\\.\\.|/\\.\\.", "a.parentNode",
+ ">|/", "jQuery.sibling(a.firstChild)",
+ "\\+", "jQuery.sibling(a).next",
+ "~", function(a){
+ var r = [];
+ var s = jQuery.sibling(a);
+ if ( s.n > 0 )
+ for ( var i = s.n; i < s.length; i++ )
+ r.push( s[i] );
+ return r;
+ }
+ ],
+ find: function( t, context ) {
+ // Make sure that the context is a DOM Element
+ if ( context && context.nodeType == undefined )
+ context = null;
+
+ // Set the correct context (if none is provided)
+ context = context || jQuery.context || document;
+
+ if ( t.constructor != String ) return [t];
+
+ if ( !t.indexOf("//") ) {
+ context = context.documentElement;
+ t = t.substr(2,t.length);
+ } else if ( !t.indexOf("/") ) {
+ context = context.documentElement;
+ t = t.substr(1,t.length);
+ // FIX Assume the root element is right :(
+ if ( t.indexOf("/") >= 1 )
+ t = t.substr(t.indexOf("/"),t.length);
+ }
+
+ var ret = [context];
+ var done = [];
+ var last = null;
+
+ while ( t.length > 0 && last != t ) {
+ var r = [];
+ last = t;
+
+ t = jQuery.trim(t).replace( /^\/\//i, "" );
+
+ var foundToken = false;
+
+ for ( var i = 0; i < jQuery.token.length; i += 2 ) {
+ var re = new RegExp("^(" + jQuery.token[i] + ")");
+ var m = re.exec(t);
+
+ if ( m ) {
+ r = ret = jQuery.map( ret, jQuery.token[i+1] );
+ t = jQuery.trim( t.replace( re, "" ) );
+ foundToken = true;
+ }
+ }
+
+ if ( !foundToken ) {
+ if ( !t.indexOf(",") || !t.indexOf("|") ) {
+ if ( ret[0] == context ) ret.shift();
+ done = jQuery.merge( done, ret );
+ r = ret = [context];
+ t = " " + t.substr(1,t.length);
+ } else {
+ var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
+ var m = re2.exec(t);
+
+ if ( m[1] == "#" ) {
+ // Ummm, should make this work in all XML docs
+ var oid = document.getElementById(m[2]);
+ r = ret = oid ? [oid] : [];
+ t = t.replace( re2, "" );
+ } else {
+ if ( !m[2] || m[1] == "." ) m[2] = "*";
+
+ for ( var i = 0; i < ret.length; i++ )
+ r = jQuery.merge( r,
+ m[2] == "*" ?
+ jQuery.getAll(ret[i]) :
+ ret[i].getElementsByTagName(m[2])
+ );
+ }
+ }
+ }
+
+ if ( t ) {
+ var val = jQuery.filter(t,r);
+ ret = r = val.r;
+ t = jQuery.trim(val.t);
+ }
+ }
+
+ if ( ret && ret[0] == context ) ret.shift();
+ done = jQuery.merge( done, ret );
+
+ return done;
+ },
+
+ getAll: function(o,r) {
+ r = r || [];
+ var s = o.childNodes;
+ for ( var i = 0; i < s.length; i++ )
+ if ( s[i].nodeType == 1 ) {
+ r.push( s[i] );
+ jQuery.getAll( s[i], r );
+ }
+ return r;
+ },
+
+ attr: function(elem, name, value){
+ var fix = {
+ "for": "htmlFor",
+ "class": "className",
+ "float": "cssFloat",
+ innerHTML: "innerHTML",
+ className: "className"
+ };
+
+ if ( fix[name] ) {
+ if ( value != undefined ) elem[fix[name]] = value;
+ return elem[fix[name]];
+ } else if ( elem.getAttribute ) {
+ if ( value != undefined ) elem.setAttribute( name, value );
+ return elem.getAttribute( name, 2 );
+ } else {
+ name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
+ if ( value != undefined ) elem[name] = value;
+ return elem[name];
+ }
+ },
+
+ // The regular expressions that power the parsing engine
+ parse: [
+ // Match: [@value='test'], [@foo]
+ [ "\\[ *(@)S *([!*$^=]*) *Q\\]", 1 ],
+
+ // Match: [div], [div p]
+ [ "(\\[)Q\\]", 0 ],
+
+ // Match: :contains('foo')
+ [ "(:)S\\(Q\\)", 0 ],
+
+ // Match: :even, :last-chlid
+ [ "([:.#]*)S", 0 ]
+ ],
+
+ filter: function(t,r,not) {
+ // Figure out if we're doing regular, or inverse, filtering
+ var g = not !== false ? jQuery.grep :
+ function(a,f) {return jQuery.grep(a,f,true);};
+
+ while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
+
+ var p = jQuery.parse;
+
+ for ( var i = 0; i < p.length; i++ ) {
+ var re = new RegExp( "^" + p[i][0]
+
+ // Look for a string-like sequence
+ .replace( 'S', "([a-z*_-][a-z0-9_-]*)" )
+
+ // Look for something (optionally) enclosed with quotes
+ .replace( 'Q', " *'?\"?([^'\"]*?)'?\"? *" ), "i" );
+
+ var m = re.exec( t );
+
+ if ( m ) {
+ // Re-organize the match
+ if ( p[i][1] )
+ m = ["", m[1], m[3], m[2], m[4]];
+
+ // Remove what we just matched
+ t = t.replace( re, "" );
+
+ break;
+ }
+ }
+
+ // :not() is a special case that can be optomized by
+ // keeping it out of the expression list
+ if ( m[1] == ":" && m[2] == "not" )
+ r = jQuery.filter(m[3],r,false).r;
+
+ // Otherwise, find the expression to execute
+ else {
+ var f = jQuery.expr[m[1]];
+ if ( f.constructor != String )
+ f = jQuery.expr[m[1]][m[2]];
+
+ // Build a custom macro to enclose it
+ eval("f = function(a,i){" +
+ ( m[1] == "@" ? "z=jQuery.attr(a,m[3]);" : "" ) +
+ "return " + f + "}");
+
+ // Execute it against the current filter
+ r = g( r, f );
+ }
+ }
+
+ // Return an array of filtered elements (r)
+ // and the modified expression string (t)
+ return { r: r, t: t };
+ },
+ trim: function(t){
+ return t.replace(/^\s+|\s+$/g, "");
+ },
+ parents: function( elem ){
+ var matched = [];
+ var cur = elem.parentNode;
+ while ( cur && cur != document ) {
+ matched.push( cur );
+ cur = cur.parentNode;
+ }
+ return matched;
+ },
+ sibling: function(elem, pos, not) {
+ var elems = [];
+
+ var siblings = elem.parentNode.childNodes;
+ for ( var i = 0; i < siblings.length; i++ ) {
+ if ( not === true && siblings[i] == elem ) continue;
+
+ if ( siblings[i].nodeType == 1 )
+ elems.push( siblings[i] );
+ if ( siblings[i] == elem )
+ elems.n = elems.length - 1;
+ }
+
+ return jQuery.extend( elems, {
+ last: elems.n == elems.length - 1,
+ cur: pos == "even" && elems.n % 2 == 0 || pos == "odd" && elems.n % 2 || elems[pos] == elem,
+ prev: elems[elems.n - 1],
+ next: elems[elems.n + 1]
+ });
+ },
+ merge: function(first, second) {
+ var result = [];
+
+ // Move b over to the new array (this helps to avoid
+ // StaticNodeList instances)
+ for ( var k = 0; k < first.length; k++ )
+ result[k] = first[k];
+
+ // Now check for duplicates between a and b and only
+ // add the unique items
+ for ( var i = 0; i < second.length; i++ ) {
+ var noCollision = true;
+
+ // The collision-checking process
+ for ( var j = 0; j < first.length; j++ )
+ if ( second[i] == first[j] )
+ noCollision = false;
+
+ // If the item is unique, add it
+ if ( noCollision )
+ result.push( second[i] );
+ }
+
+ return result;
+ },
+ grep: function(elems, fn, inv) {
+ // If a string is passed in for the function, make a function
+ // for it (a handy shortcut)
+ if ( fn.constructor == String )
+ fn = new Function("a","i","return " + fn);
+
+ var result = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0; i < elems.length; i++ )
+ if ( !inv && fn(elems[i],i) || inv && !fn(elems[i],i) )
+ result.push( elems[i] );
+
+ return result;
+ },
+ map: function(elems, fn) {
+ // If a string is passed in for the function, make a function
+ // for it (a handy shortcut)
+ if ( fn.constructor == String )
+ fn = new Function("a","return " + fn);
+
+ var result = [];
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0; i < elems.length; i++ ) {
+ var val = fn(elems[i],i);
+
+ if ( val !== null && val != undefined ) {
+ if ( val.constructor != Array ) val = [val];
+ result = jQuery.merge( result, val );
+ }
+ }
+
+ return result;
+ },
+
+ /*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code orignated from Dean Edwards' addEvent library.
+ */
+ event: {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function(element, type, handler) {
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( jQuery.browser.msie && element.setInterval != undefined )
+ element = window;
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid )
+ handler.guid = this.guid++;
+
+ // Init the element's event structure
+ if (!element.events)
+ element.events = {};
+
+ // Get the current list of functions bound to this event
+ var handlers = element.events[type];
+
+ // If it hasn't been initialized yet
+ if (!handlers) {
+ // Init the event handler queue
+ handlers = element.events[type] = {};
+
+ // Remember an existing handler, if it's already there
+ if (element["on" + type])
+ handlers[0] = element["on" + type];
+ }
+
+ // Add the function to the element's handler list
+ handlers[handler.guid] = handler;
+
+ // And bind the global event handler to the element
+ element["on" + type] = this.handle;
+
+ // Remember the function in a global list (for triggering)
+ if (!this.global[type])
+ this.global[type] = [];
+ this.global[type].push( element );
+ },
+
+ guid: 1,
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function(element, type, handler) {
+ if (element.events)
+ if (type && element.events[type])
+ if ( handler )
+ delete element.events[type][handler.guid];
+ else
+ for ( var i in element.events[type] )
+ delete element.events[type][i];
+ else
+ for ( var j in element.events )
+ this.remove( element, j );
+ },
+
+ trigger: function(type,data,element) {
+ // Touch up the incoming data
+ data = data || [];
+
+ // Handle a global trigger
+ if ( !element ) {
+ var g = this.global[type];
+ if ( g )
+ for ( var i = 0; i < g.length; i++ )
+ this.trigger( type, data, g[i] );
+
+ // Handle triggering a single element
+ } else if ( element["on" + type] ) {
+ // Pass along a fake event
+ data.unshift( this.fix({ type: type, target: element }) );
+
+ // Trigger the event
+ element["on" + type].apply( element, data );
+ }
+ },
+
+ handle: function(event) {
+ if ( typeof jQuery == "undefined" ) return;
+
+ event = event || jQuery.event.fix( window.event );
+
+ // If no correct event was found, fail
+ if ( !event ) return;
+
+ var returnValue = true;
+
+ var c = this.events[event.type];
+
+ for ( var j in c ) {
+ if ( c[j].apply( this, [event] ) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ returnValue = false;
+ }
+ }
+
+ return returnValue;
+ },
+
+ fix: function(event) {
+ if ( event ) {
+ event.preventDefault = function() {
+ this.returnValue = false;
+ };
+
+ event.stopPropagation = function() {
+ this.cancelBubble = true;
+ };
+ }
+
+ return event;
+ }
+
+ }
+});
+
+new function() {
+ var b = navigator.userAgent.toLowerCase();
+
+ // Figure out what browser is being used
+ jQuery.browser = {
+ safari: /webkit/.test(b),
+ opera: /opera/.test(b),
+ msie: /msie/.test(b) && !/opera/.test(b),
+ mozilla: /mozilla/.test(b) && !/compatible/.test(b)
+ };
+
+ // Check to see if the W3C box model is being used
+ jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat";
+};
+
+jQuery.macros = {
+ to: {
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after"
+ },
+
+
+ css: "width,height,top,left,position,float,overflow,color,background".split(","),
+
+ filter: [ "eq", "lt", "gt", "contains" ],
+
+ attr: {
+
+ val: "value",
+
+ html: "innerHTML",
+
+ id: null,
+
+ title: null,
+
+ name: null,
+
+ href: null,
+
+ src: null,
+
+ rel: null
+ },
+
+ axis: {
+
+ parent: "a.parentNode",
+
+ ancestors: jQuery.parents,
+
+ parents: jQuery.parents,
+
+ next: "jQuery.sibling(a).next",
+
+ prev: "jQuery.sibling(a).prev",
+
+ siblings: jQuery.sibling,
+
+ children: "a.childNodes"
+ },
+
+ each: {
+
+ removeAttr: function( key ) {
+ this.removeAttribute( key );
+ },
+ show: function(){
+ this.style.display = this.oldblock ? this.oldblock : "";
+ if ( jQuery.css(this,"display") == "none" )
+ this.style.display = "block";
+ },
+ hide: function(){
+ this.oldblock = this.oldblock || jQuery.css(this,"display");
+ if ( this.oldblock == "none" )
+ this.oldblock = "block";
+ this.style.display = "none";
+ },
+ toggle: function(){
+ $(this)[ $(this).is(":hidden") ? "show" : "hide" ].apply( $(this), arguments );
+ },
+ addClass: function(c){
+ jQuery.className.add(this,c);
+ },
+ removeClass: function(c){
+ jQuery.className.remove(this,c);
+ },
+ toggleClass: function( c ){
+ jQuery.className[ jQuery.className.has(this,c) ? "remove" : "add" ](this,c);
+ },
+
+ remove: function(a){
+ if ( !a || jQuery.filter( [this], a ).r )
+ this.parentNode.removeChild( this );
+ },
+ empty: function(){
+ while ( this.firstChild )
+ this.removeChild( this.firstChild );
+ },
+ bind: function( type, fn ) {
+ if ( fn.constructor == String )
+ fn = new Function("e", ( !fn.indexOf(".") ? "$(this)" : "return " ) + fn);
+ jQuery.event.add( this, type, fn );
+ },
+
+ unbind: function( type, fn ) {
+ jQuery.event.remove( this, type, fn );
+ },
+ trigger: function( type, data ) {
+ jQuery.event.trigger( type, data, this );
+ }
+ }
+};
+
+jQuery.init();jQuery.fn.extend({
+
+ // We're overriding the old toggle function, so
+ // remember it for later
+ _toggle: jQuery.fn.toggle,
+ toggle: function(a,b) {
+ // If two functions are passed in, we're
+ // toggling on a click
+ return a && b && a.constructor == Function && b.constructor == Function ? this.click(function(e){
+ // Figure out which function to execute
+ this.last = this.last == a ? b : a;
+
+ // Make sure that clicks stop
+ e.preventDefault();
+
+ // and execute the function
+ return this.last.apply( this, [e] ) || false;
+ }) :
+
+ // Otherwise, execute the old toggle function
+ this._toggle.apply( this, arguments );
+ },
+
+ hover: function(f,g) {
+
+ // A private function for haandling mouse 'hovering'
+ function handleHover(e) {
+ // Check if mouse(over|out) are still within the same parent element
+ var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+
+ // Traverse up the tree
+ while ( p && p != this ) p = p.parentNode;
+
+ // If we actually just moused on to a sub-element, ignore it
+ if ( p == this ) return false;
+
+ // Execute the right function
+ return (e.type == "mouseover" ? f : g).apply(this, [e]);
+ }
+
+ // Bind the function to the two event listeners
+ return this.mouseover(handleHover).mouseout(handleHover);
+ },
+ ready: function(f) {
+ // If the DOM is already ready
+ if ( jQuery.isReady )
+ // Execute the function immediately
+ f.apply( document );
+
+ // Otherwise, remember the function for later
+ else {
+ // Add the function to the wait list
+ jQuery.readyList.push( f );
+ }
+
+ return this;
+ }
+});
+
+jQuery.extend({
+ /*
+ * All the code that makes DOM Ready work nicely.
+ */
+ isReady: false,
+ readyList: [],
+
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( jQuery.readyList ) {
+ // Execute all of them
+ for ( var i = 0; i < jQuery.readyList.length; i++ )
+ jQuery.readyList[i].apply( document );
+
+ // Reset the list of functions
+ jQuery.readyList = null;
+ }
+ }
+ }
+});
+
+new function(){
+
+ var e = ("blur,focus,load,resize,scroll,unload,click,dblclick," +
+ "mousedown,mouseup,mousemove,mouseover,mouseout,change,reset,select," +
+ "submit,keydown,keypress,keyup,error").split(",");
+
+ // Go through all the event names, but make sure that
+ // it is enclosed properly
+ for ( var i = 0; i < e.length; i++ ) new function(){
+
+ var o = e[i];
+
+ // Handle event binding
+ jQuery.fn[o] = function(f){
+ return f ? this.bind(o, f) : this.trigger(o);
+ };
+
+ // Handle event unbinding
+ jQuery.fn["un"+o] = function(f){ return this.unbind(o, f); };
+
+ // Finally, handle events that only fire once
+ jQuery.fn["one"+o] = function(f){
+ // Attach the event listener
+ return this.each(function(){
+
+ var count = 0;
+
+ // Add the event
+ jQuery.event.add( this, o, function(e){
+ // If this function has already been executed, stop
+ if ( count++ ) return;
+
+ // And execute the bound function
+ return f.apply(this, [e]);
+ });
+ });
+ };
+
+ };
+
+ // If Mozilla is used
+ if ( jQuery.browser.mozilla || jQuery.browser.opera ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
+
+ // If IE is used, use the excellent hack by Matthias Miller
+ // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
+ } else if ( jQuery.browser.msie ) {
+
+ // Only works if you document.write() it
+ document.write("<scr" + "ipt id=__ie_init defer=true " +
+ "src=//:><\/script>");
+
+ // Use the defer script hack
+ var script = document.getElementById("__ie_init");
+ script.onreadystatechange = function() {
+ if ( this.readyState == "complete" )
+ jQuery.ready();
+ };
+
+ // Clear from memory
+ script = null;
+
+ // If Safari is used
+ } else if ( jQuery.browser.safari ) {
+ // Continually check to see if the document.readyState is valid
+ jQuery.safariTimer = setInterval(function(){
+ // loaded and complete are both valid states
+ if ( document.readyState == "loaded" ||
+ document.readyState == "complete" ) {
+
+ // If either one are found, remove the timer
+ clearInterval( jQuery.safariTimer );
+ jQuery.safariTimer = null;
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ }, 10);
+ }
+
+ // A fallback to window.onload, that will always work
+ jQuery.event.add( window, "load", jQuery.ready );
+
+};
+jQuery.fn.extend({
+
+ // overwrite the old show method
+ _show: jQuery.fn.show,
+
+ show: function(speed,callback){
+ return speed ? this.animate({
+ height: "show", width: "show", opacity: "show"
+ }, speed, callback) : this._show();
+ },
+
+ // Overwrite the old hide method
+ _hide: jQuery.fn.hide,
+
+ hide: function(speed,callback){
+ return speed ? this.animate({
+ height: "hide", width: "hide", opacity: "hide"
+ }, speed, callback) : this._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.each(function(){
+ var state = $(this).is(":hidden") ? "show" : "hide";
+ $(this).animate({height: state}, 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,callback) {
+ return this.queue(function(){
+
+ this.curAnim = prop;
+
+ for ( var p in prop ) {
+ var e = new jQuery.fx( this, jQuery.speed(speed,callback), p );
+ if ( prop[p].constructor == Number )
+ e.custom( e.cur(), prop[p] );
+ else
+ e[ prop[p] ]( prop );
+ }
+
+ });
+ },
+ queue: function(type,fn){
+ if ( !fn ) {
+ fn = type;
+ type = "fx";
+ }
+
+ return this.each(function(){
+ if ( !this.queue )
+ this.queue = {};
+
+ if ( !this.queue[type] )
+ this.queue[type] = [];
+
+ this.queue[type].push( fn );
+
+ if ( this.queue[type].length == 1 )
+ fn.apply(this);
+ });
+ }
+
+});
+
+jQuery.extend({
+
+ setAuto: function(e,p) {
+ if ( e.notAuto ) return;
+
+ if ( p == "height" && e.scrollHeight != parseInt(jQuery.curCSS(e,p)) ) return;
+ if ( p == "width" && e.scrollWidth != parseInt(jQuery.curCSS(e,p)) ) return;
+
+ // Remember the original height
+ var a = e.style[p];
+
+ // Figure out the size of the height right now
+ var o = jQuery.curCSS(e,p,1);
+
+ if ( p == "height" && e.scrollHeight != o ||
+ p == "width" && e.scrollWidth != o ) return;
+
+ // Set the height to auto
+ e.style[p] = e.currentStyle ? "" : "auto";
+
+ // See what the size of "auto" is
+ var n = jQuery.curCSS(e,p,1);
+
+ // Revert back to the original size
+ if ( o != n && n != "auto" ) {
+ e.style[p] = a;
+ e.notAuto = true;
+ }
+ },
+
+ speed: function(s,o) {
+ o = o || {};
+
+ if ( o.constructor == Function )
+ o = { complete: o };
+
+ var ss = { slow: 600, fast: 200 };
+ o.duration = (s && s.constructor == Number ? s : ss[s]) || 400;
+
+ // Queueing
+ o.oldComplete = o.complete;
+ o.complete = function(){
+ jQuery.dequeue(this, "fx");
+ if ( o.oldComplete && o.oldComplete.constructor == Function )
+ o.oldComplete.apply( this );
+ };
+
+ return o;
+ },
+
+ queue: {},
+
+ dequeue: function(elem,type){
+ type = type || "fx";
+
+ if ( elem.queue && elem.queue[type] ) {
+ // Remove self
+ elem.queue[type].shift();
+
+ // Get next function
+ var f = elem.queue[type][0];
+
+ if ( f ) f.apply( elem );
+ }
+ },
+
+ /*
+ * I originally wrote fx() as a clone of moo.fx and in the process
+ * of making it small in size the code became illegible to sane
+ * people. You've been warned.
+ */
+
+ fx: function( elem, options, prop ){
+
+ var z = this;
+
+ // The users options
+ z.o = {
+ duration: options.duration || 400,
+ complete: options.complete,
+ step: options.step
+ };
+
+ // The element
+ z.el = elem;
+
+ // The styles
+ var y = z.el.style;
+
+ // Simple function for setting a style value
+ z.a = function(){
+ if ( options.step )
+ options.step.apply( elem, [ z.now ] );
+
+ if ( prop == "opacity" ) {
+ if (z.now == 1) z.now = 0.9999;
+ if (window.ActiveXObject)
+ y.filter = "alpha(opacity=" + z.now*100 + ")";
+ else
+ y.opacity = z.now;
+
+ // My hate for IE will never die
+ } else if ( parseInt(z.now) )
+ y[prop] = parseInt(z.now) + "px";
+
+ y.display = "block";
+ };
+
+ // Figure out the maximum number to run to
+ z.max = function(){
+ return parseFloat( jQuery.css(z.el,prop) );
+ };
+
+ // Get the current size
+ z.cur = function(){
+ var r = parseFloat( jQuery.curCSS(z.el, prop) );
+ return r && r > -10000 ? r : z.max();
+ };
+
+ // Start an animation from one number to another
+ z.custom = function(from,to){
+ z.startTime = (new Date()).getTime();
+ z.now = from;
+ z.a();
+
+ z.timer = setInterval(function(){
+ z.step(from, to);
+ }, 13);
+ };
+
+ // Simple 'show' function
+ z.show = function( p ){
+ if ( !z.el.orig ) z.el.orig = {};
+
+ // Remember where we started, so that we can go back to it later
+ z.el.orig[prop] = this.cur();
+
+ z.custom( 0, z.el.orig[prop] );
+
+ // Stupid IE, look what you made me do
+ if ( prop != "opacity" )
+ y[prop] = "1px";
+ };
+
+ // Simple 'hide' function
+ z.hide = function(){
+ if ( !z.el.orig ) z.el.orig = {};
+
+ // Remember where we started, so that we can go back to it later
+ z.el.orig[prop] = this.cur();
+
+ z.o.hide = true;
+
+ // Begin the animation
+ z.custom(z.el.orig[prop], 0);
+ };
+
+ // IE has trouble with opacity if it does not have layout
+ if ( jQuery.browser.msie && !z.el.currentStyle.hasLayout )
+ y.zoom = "1";
+
+ // Remember the overflow of the element
+ if ( !z.el.oldOverlay )
+ z.el.oldOverflow = jQuery.css( z.el, "overflow" );
+
+ // Make sure that nothing sneaks out
+ y.overflow = "hidden";
+
+ // Each step of an animation
+ z.step = function(firstNum, lastNum){
+ var t = (new Date()).getTime();
+
+ if (t > z.o.duration + z.startTime) {
+ // Stop the timer
+ clearInterval(z.timer);
+ z.timer = null;
+
+ z.now = lastNum;
+ z.a();
+
+ z.el.curAnim[ prop ] = true;
+
+ var done = true;
+ for ( var i in z.el.curAnim )
+ if ( z.el.curAnim[i] !== true )
+ done = false;
+
+ if ( done ) {
+ // Reset the overflow
+ y.overflow = z.el.oldOverflow;
+
+ // Hide the element if the "hide" operation was done
+ if ( z.o.hide )
+ y.display = 'none';
+
+ // Reset the property, if the item has been hidden
+ if ( z.o.hide ) {
+ for ( var p in z.el.curAnim ) {
+ y[ p ] = z.el.orig[p] + ( p == "opacity" ? "" : "px" );
+
+ // set its height and/or width to auto
+ if ( p == 'height' || p == 'width' )
+ jQuery.setAuto( z.el, p );
+ }
+ }
+ }
+
+ // If a callback was provided, execute it
+ if( done && z.o.complete && z.o.complete.constructor == Function )
+ // Execute the complete function
+ z.o.complete.apply( z.el );
+ } else {
+ // Figure out where in the animation we are and set the number
+ var p = (t - this.startTime) / z.o.duration;
+ z.now = ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
+
+ // Perform the next step of the animation
+ z.a();
+ }
+ };
+
+ }
+
+});
+// AJAX Plugin
+// Docs Here:
+// http://jquery.com/docs/ajax/
+jQuery.fn.loadIfModified = function( url, params, callback ) {
+ this.load( url, params, callback, 1 );
+};
+
+jQuery.fn.load = function( url, params, callback, ifModified ) {
+ if ( url.constructor == Function )
+ return this.bind("load", url);
+
+ callback = callback || function(){};
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( params.constructor == Function ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else {
+ params = jQuery.param( params );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax( type, url, params,function(res, status){
+
+ if ( status == "success" || !ifModified && status == "notmodified" ) {
+ // Inject the HTML into all the matched elements
+ self.html(res.responseText).each( callback, [res.responseText, status] );
+
+ // Execute all the scripts inside of the newly-injected HTML
+ $("script", self).each(function(){
+ if ( this.src )
+ $.getScript( this.src );
+ else
+ eval.call( window, this.text || this.textContent || this.innerHTML || "" );
+ });
+ } else
+ callback.apply( self, [res.responseText, status] );
+
+ }, ifModified);
+
+ return this;
+};
+
+// If IE is used, create a wrapper for the XMLHttpRequest object
+if ( jQuery.browser.msie )
+ XMLHttpRequest = function(){
+ return new ActiveXObject(
+ navigator.userAgent.indexOf("MSIE 5") >= 0 ?
+ "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"
+ );
+ };
+
+// Attach a bunch of functions for handling common AJAX events
+new function(){
+ var e = "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess".split(',');
+
+ for ( var i = 0; i < e.length; i++ ) new function(){
+ var o = e[i];
+ jQuery.fn[o] = function(f){
+ return this.bind(o, f);
+ };
+ };
+};
+
+jQuery.extend({
+ get: function( url, data, callback, type, ifModified ) {
+ if ( data.constructor == Function ) {
+ type = callback;
+ callback = data;
+ data = null;
+ }
+
+ if ( data ) url += "?" + jQuery.param(data);
+
+ // Build and start the HTTP Request
+ jQuery.ajax( "GET", url, null, function(r, status) {
+ if ( callback ) callback( jQuery.httpData(r,type), status );
+ }, ifModified);
+ },
+
+ getIfModified: function( url, data, callback, type ) {
+ jQuery.get(url, data, callback, type, 1);
+ },
+
+ getScript: function( url, data, callback ) {
+ jQuery.get(url, data, callback, "script");
+ },
+ post: function( url, data, callback, type ) {
+ // Build and start the HTTP Request
+ jQuery.ajax( "POST", url, jQuery.param(data), function(r, status) {
+ if ( callback ) callback( jQuery.httpData(r,type), status );
+ });
+ },
+
+ // timeout (ms)
+ timeout: 0,
+
+ ajaxTimeout: function(timeout) {
+ jQuery.timeout = timeout;
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ ajax: function( type, url, data, ret, ifModified ) {
+ // If only a single argument was passed in,
+ // assume that it is a object of key/value pairs
+ if ( !url ) {
+ ret = type.complete;
+ var success = type.success;
+ var error = type.error;
+ data = type.data;
+ url = type.url;
+ type = type.type;
+ }
+
+ // Watch for a new set of requests
+ if ( ! jQuery.active++ )
+ jQuery.event.trigger( "ajaxStart" );
+
+ var requestDone = false;
+
+ // Create the request object
+ var xml = new XMLHttpRequest();
+
+ // Open the socket
+ xml.open(type || "GET", url, true);
+
+ // Set the correct header, if data is being sent
+ if ( data )
+ xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+
+ // Set the If-Modified-Since header, if ifModified mode.
+ if ( ifModified )
+ xml.setRequestHeader("If-Modified-Since",
+ jQuery.lastModified[url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+ // Set header so calling script knows that it's an XMLHttpRequest
+ xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+ // Make sure the browser sends the right content length
+ if ( xml.overrideMimeType )
+ xml.setRequestHeader("Connection", "close");
+
+ // Wait for a response to come back
+ var onreadystatechange = function(istimeout){
+ // The transfer is complete and the data is available, or the request timed out
+ if ( xml && (xml.readyState == 4 || istimeout == "timeout") ) {
+ requestDone = true;
+
+ var status = jQuery.httpSuccess( xml ) && istimeout != "timeout" ?
+ ifModified && jQuery.httpNotModified( xml, url ) ? "notmodified" : "success" : "error";
+
+ // Make sure that the request was successful or notmodified
+ if ( status != "error" ) {
+ // Cache Last-Modified header, if ifModified mode.
+ var modRes = xml.getResponseHeader("Last-Modified");
+ if ( ifModified && modRes ) jQuery.lastModified[url] = modRes;
+
+ // If a local callback was specified, fire it
+ if ( success ) success( xml, status );
+
+ // Fire the global callback
+ jQuery.event.trigger( "ajaxSuccess" );
+
+ // Otherwise, the request was not successful
+ } else {
+ // If a local callback was specified, fire it
+ if ( error ) error( xml, status );
+
+ // Fire the global callback
+ jQuery.event.trigger( "ajaxError" );
+ }
+
+ // The request was completed
+ jQuery.event.trigger( "ajaxComplete" );
+
+ // Handle the global AJAX counter
+ if ( ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+
+ // Process result
+ if ( ret ) ret(xml, status);
+
+ // Stop memory leaks
+ xml.onreadystatechange = function(){};
+ xml = null;
+
+ }
+ };
+ xml.onreadystatechange = onreadystatechange;
+
+ // Timeout checker
+ if(jQuery.timeout > 0)
+ setTimeout(function(){
+ // Check to see if the request is still happening
+ if (xml) {
+ // Cancel the request
+ xml.abort();
+
+ if ( !requestDone ) onreadystatechange( "timeout" );
+
+ // Clear from memory
+ xml = null;
+ }
+ }, jQuery.timeout);
+
+ // Send the data
+ xml.send(data);
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function(r) {
+ try {
+ return !r.status && location.protocol == "file:" ||
+ ( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
+ jQuery.browser.safari && r.status == undefined;
+ } catch(e){}
+
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function(xml, url) {
+ try {
+ var xmlRes = xml.getResponseHeader("Last-Modified");
+
+ // Firefox always returns 200. check Last-Modified date
+ return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
+ jQuery.browser.safari && xml.status == undefined;
+ } catch(e){}
+
+ return false;
+ },
+
+ // Get the data out of an XMLHttpRequest.
+ // Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
+ // otherwise return plain text.
+ httpData: function(r,type) {
+ var ct = r.getResponseHeader("content-type");
+ var data = !type && ct && ct.indexOf("xml") >= 0;
+ data = type == "xml" || data ? r.responseXML : r.responseText;
+
+ // If the type is "script", eval it
+ if ( type == "script" ) eval.call( window, data );
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function(a) {
+ var s = [];
+
+ // If an array was passed in, assume that it is an array
+ // of form elements
+ if ( a.constructor == Array ) {
+ // Serialize the form elements
+ for ( var i = 0; i < a.length; i++ )
+ s.push( a[i].name + "=" + encodeURIComponent( a[i].value ) );
+
+ // Otherwise, assume that it's an object of key/value pairs
+ } else {
+ // Serialize the key/values
+ for ( var j in a )
+ s.push( j + "=" + encodeURIComponent( a[j] ) );
+ }
+
+ // Return the resulting serialization
+ return s.join("&");
+ }
+
+});
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.1.js b/devtools/client/inspector/markup/test/lib_jquery_1.1.js
new file mode 100644
index 000000000..981a3bdc1
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.1.js
@@ -0,0 +1,2172 @@
+/* prevent execution of jQuery if included more than once */
+if(typeof window.jQuery == "undefined") {
+/*
+ * jQuery 1.1 - New Wave Javascript
+ *
+ * Copyright (c) 2007 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2007-01-14 17:37:33 -0500 (Sun, 14 Jan 2007) $
+ * $Rev: 1073 $
+ */
+
+// Global undefined variable
+window.undefined = window.undefined;
+var jQuery = function(a,c) {
+ // If the context is global, return a new object
+ if ( window == this )
+ return new jQuery(a,c);
+
+ // Make sure that a selection was provided
+ a = a || document;
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ // Safari reports typeof on DOM NodeLists as a function
+ if ( jQuery.isFunction(a) && !a.nodeType && a[0] == undefined )
+ return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
+
+ // Handle HTML strings
+ if ( typeof a == "string" ) {
+ var m = /^[^<]*(<.+>)[^>]*$/.exec(a);
+
+ a = m ?
+ // HANDLE: $(html) -> $(array)
+ jQuery.clean( [ m[1] ] ) :
+
+ // HANDLE: $(expr)
+ jQuery.find( a, c );
+ }
+
+ return this.setArray(
+ // HANDLE: $(array)
+ a.constructor == Array && a ||
+
+ // HANDLE: $(arraylike)
+ // Watch for when an array-like object is passed as the selector
+ (a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||
+
+ // HANDLE: $(*)
+ [ a ] );
+};
+
+// Map over the $ in case of overwrite
+if ( typeof $ != "undefined" )
+ jQuery._$ = $;
+
+// Map the jQuery namespace to the '$' one
+var $ = jQuery;
+
+jQuery.fn = jQuery.prototype = {
+ jquery: "1.1",
+
+ size: function() {
+ return this.length;
+ },
+
+ length: 0,
+
+ get: function( num ) {
+ return num == undefined ?
+
+ // Return a 'clean' array
+ jQuery.makeArray( this ) :
+
+ // Return just the object
+ this[num];
+ },
+ pushStack: function( a ) {
+ var ret = jQuery(this);
+ ret.prevObject = this;
+ return ret.setArray( a );
+ },
+ setArray: function( a ) {
+ this.length = 0;
+ [].push.apply( this, a );
+ return this;
+ },
+ each: function( fn, args ) {
+ return jQuery.each( this, fn, args );
+ },
+ index: function( obj ) {
+ var pos = -1;
+ this.each(function(i){
+ if ( this == obj ) pos = i;
+ });
+ return pos;
+ },
+
+ attr: function( key, value, type ) {
+ var obj = key;
+
+ // Look for the case where we're accessing a style value
+ if ( key.constructor == String )
+ if ( value == undefined )
+ return jQuery[ type || "attr" ]( this[0], key );
+ else {
+ obj = {};
+ obj[ key ] = value;
+ }
+
+ // Check to see if we're setting style values
+ return this.each(function(){
+ // Set all the styles
+ for ( var prop in obj )
+ jQuery.attr(
+ type ? this.style : this,
+ prop, jQuery.prop(this, obj[prop], type)
+ );
+ });
+ },
+
+ css: function( key, value ) {
+ return this.attr( key, value, "curCSS" );
+ },
+
+ text: function(e) {
+ if ( typeof e == "string" )
+ return this.empty().append( document.createTextNode( e ) );
+
+ var t = "";
+ jQuery.each( e || this, function(){
+ jQuery.each( this.childNodes, function(){
+ if ( this.nodeType != 8 )
+ t += this.nodeType != 1 ?
+ this.nodeValue : jQuery.fn.text([ this ]);
+ });
+ });
+ return t;
+ },
+
+ wrap: function() {
+ // The elements to wrap the target around
+ var a = jQuery.clean(arguments);
+
+ // Wrap each of the matched elements individually
+ return this.each(function(){
+ // Clone the structure that we're using to wrap
+ var b = a[0].cloneNode(true);
+
+ // Insert it before the element to be wrapped
+ this.parentNode.insertBefore( b, this );
+
+ // Find the deepest point in the wrap structure
+ while ( b.firstChild )
+ b = b.firstChild;
+
+ // Move the matched element to within the wrap structure
+ b.appendChild( this );
+ });
+ },
+ append: function() {
+ return this.domManip(arguments, true, 1, function(a){
+ this.appendChild( a );
+ });
+ },
+ prepend: function() {
+ return this.domManip(arguments, true, -1, function(a){
+ this.insertBefore( a, this.firstChild );
+ });
+ },
+ before: function() {
+ return this.domManip(arguments, false, 1, function(a){
+ this.parentNode.insertBefore( a, this );
+ });
+ },
+ after: function() {
+ return this.domManip(arguments, false, -1, function(a){
+ this.parentNode.insertBefore( a, this.nextSibling );
+ });
+ },
+ end: function() {
+ return this.prevObject || jQuery([]);
+ },
+ find: function(t) {
+ return this.pushStack( jQuery.map( this, function(a){
+ return jQuery.find(t,a);
+ }) );
+ },
+ clone: function(deep) {
+ return this.pushStack( jQuery.map( this, function(a){
+ return a.cloneNode( deep != undefined ? deep : true );
+ }) );
+ },
+
+ filter: function(t) {
+ return this.pushStack(
+ jQuery.isFunction( t ) &&
+ jQuery.grep(this, function(el, index){
+ return t.apply(el, [index])
+ }) ||
+
+ jQuery.multiFilter(t,this) );
+ },
+
+ not: function(t) {
+ return this.pushStack(
+ t.constructor == String &&
+ jQuery.multiFilter(t,this,true) ||
+
+ jQuery.grep(this,function(a){
+ if ( t.constructor == Array || t.jquery )
+ return jQuery.inArray( t, a ) < 0;
+ else
+ return a != t;
+ }) );
+ },
+
+ add: function(t) {
+ return this.pushStack( jQuery.merge(
+ this.get(),
+ typeof t == "string" ? jQuery(t).get() : t )
+ );
+ },
+ is: function(expr) {
+ return expr ? jQuery.filter(expr,this).r.length > 0 : false;
+ },
+
+ val: function( val ) {
+ return val == undefined ?
+ ( this.length ? this[0].value : null ) :
+ this.attr( "value", val );
+ },
+
+ html: function( val ) {
+ return val == undefined ?
+ ( this.length ? this[0].innerHTML : null ) :
+ this.empty().append( val );
+ },
+ domManip: function(args, table, dir, fn){
+ var clone = this.length > 1;
+ var a = jQuery.clean(args);
+ if ( dir < 0 )
+ a.reverse();
+
+ return this.each(function(){
+ var obj = this;
+
+ if ( table && this.nodeName.toUpperCase() == "TABLE" && a[0].nodeName.toUpperCase() == "TR" )
+ obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody"));
+
+ jQuery.each( a, function(){
+ fn.apply( obj, [ clone ? this.cloneNode(true) : this ] );
+ });
+
+ });
+ }
+};
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0],
+ a = 1;
+
+ // extend jQuery itself if only one argument is passed
+ if ( arguments.length == 1 ) {
+ target = this;
+ a = 0;
+ }
+ var prop;
+ while (prop = arguments[a++])
+ // Extend the base object
+ for ( var i in prop ) target[i] = prop[i];
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function() {
+ if ( jQuery._$ )
+ $ = jQuery._$;
+ },
+
+ isFunction: function( fn ) {
+ return fn && typeof fn == "function";
+ },
+ // args is for internal usage only
+ each: function( obj, fn, args ) {
+ if ( obj.length == undefined )
+ for ( var i in obj )
+ fn.apply( obj[i], args || [i, obj[i]] );
+ else
+ for ( var i = 0, ol = obj.length; i < ol; i++ )
+ if ( fn.apply( obj[i], args || [i, obj[i]] ) === false ) break;
+ return obj;
+ },
+
+ prop: function(elem, value, type){
+ // Handle executable functions
+ if ( jQuery.isFunction( value ) )
+ return value.call( elem );
+
+ // Handle passing in a number to a CSS property
+ if ( value.constructor == Number && type == "curCSS" )
+ return value + "px";
+
+ return value;
+ },
+
+ className: {
+ // internal only, use addClass("class")
+ add: function( elem, c ){
+ jQuery.each( c.split(/\s+/), function(i, cur){
+ if ( !jQuery.className.has( elem.className, cur ) )
+ elem.className += ( elem.className ? " " : "" ) + cur;
+ });
+ },
+
+ // internal only, use removeClass("class")
+ remove: function( elem, c ){
+ elem.className = c ?
+ jQuery.grep( elem.className.split(/\s+/), function(cur){
+ return !jQuery.className.has( c, cur );
+ }).join(" ") : "";
+ },
+
+ // internal only, use is(".class")
+ has: function( t, c ) {
+ t = t.className || t;
+ return t && new RegExp("(^|\\s)" + c + "(\\s|$)").test( t );
+ }
+ },
+ swap: function(e,o,f) {
+ for ( var i in o ) {
+ e.style["old"+i] = e.style[i];
+ e.style[i] = o[i];
+ }
+ f.apply( e, [] );
+ for ( var i in o )
+ e.style[i] = e.style["old"+i];
+ },
+
+ css: function(e,p) {
+ if ( p == "height" || p == "width" ) {
+ var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"];
+
+ jQuery.each( d, function(){
+ old["padding" + this] = 0;
+ old["border" + this + "Width"] = 0;
+ });
+
+ jQuery.swap( e, old, function() {
+ if (jQuery.css(e,"display") != "none") {
+ oHeight = e.offsetHeight;
+ oWidth = e.offsetWidth;
+ } else {
+ e = jQuery(e.cloneNode(true))
+ .find(":radio").removeAttr("checked").end()
+ .css({
+ visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0"
+ }).appendTo(e.parentNode)[0];
+
+ var parPos = jQuery.css(e.parentNode,"position");
+ if ( parPos == "" || parPos == "static" )
+ e.parentNode.style.position = "relative";
+
+ oHeight = e.clientHeight;
+ oWidth = e.clientWidth;
+
+ if ( parPos == "" || parPos == "static" )
+ e.parentNode.style.position = "static";
+
+ e.parentNode.removeChild(e);
+ }
+ });
+
+ return p == "height" ? oHeight : oWidth;
+ }
+
+ return jQuery.curCSS( e, p );
+ },
+
+ curCSS: function(elem, prop, force) {
+ var ret;
+
+ if (prop == "opacity" && jQuery.browser.msie)
+ return jQuery.attr(elem.style, "opacity");
+
+ if (prop == "float" || prop == "cssFloat")
+ prop = jQuery.browser.msie ? "styleFloat" : "cssFloat";
+
+ if (!force && elem.style[prop])
+ ret = elem.style[prop];
+
+ else if (document.defaultView && document.defaultView.getComputedStyle) {
+
+ if (prop == "cssFloat" || prop == "styleFloat")
+ prop = "float";
+
+ prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
+ var cur = document.defaultView.getComputedStyle(elem, null);
+
+ if ( cur )
+ ret = cur.getPropertyValue(prop);
+ else if ( prop == "display" )
+ ret = "none";
+ else
+ jQuery.swap(elem, { display: "block" }, function() {
+ var c = document.defaultView.getComputedStyle(this, "");
+ ret = c && c.getPropertyValue(prop) || "";
+ });
+
+ } else if (elem.currentStyle) {
+
+ var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});
+ ret = elem.currentStyle[prop] || elem.currentStyle[newProp];
+
+ }
+
+ return ret;
+ },
+
+ clean: function(a) {
+ var r = [];
+
+ jQuery.each( a, function(i,arg){
+ if ( !arg ) return;
+
+ if ( arg.constructor == Number )
+ arg = arg.toString();
+
+ // Convert html string into DOM nodes
+ if ( typeof arg == "string" ) {
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var s = jQuery.trim(arg), div = document.createElement("div"), tb = [];
+
+ var wrap =
+ // option or optgroup
+ !s.indexOf("<opt") &&
+ [1, "<select>", "</select>"] ||
+
+ (!s.indexOf("<thead") || !s.indexOf("<tbody") || !s.indexOf("<tfoot")) &&
+ [1, "<table>", "</table>"] ||
+
+ !s.indexOf("<tr") &&
+ [2, "<table><tbody>", "</tbody></table>"] ||
+
+ // <thead> matched above
+ (!s.indexOf("<td") || !s.indexOf("<th")) &&
+ [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
+
+ [0,"",""];
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + s + wrap[2];
+
+ // Move to the right depth
+ while ( wrap[0]-- )
+ div = div.firstChild;
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( jQuery.browser.msie ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ if ( !s.indexOf("<table") && s.indexOf("<tbody") < 0 )
+ tb = div.firstChild && div.firstChild.childNodes;
+
+ // String was a bare <thead> or <tfoot>
+ else if ( wrap[1] == "<table>" && s.indexOf("<tbody") < 0 )
+ tb = div.childNodes;
+
+ for ( var n = tb.length-1; n >= 0 ; --n )
+ if ( tb[n].nodeName.toUpperCase() == "TBODY" && !tb[n].childNodes.length )
+ tb[n].parentNode.removeChild(tb[n]);
+
+ }
+
+ arg = div.childNodes;
+ }
+
+ if ( arg.length === 0 )
+ return;
+
+ if ( arg[0] == undefined )
+ r.push( arg );
+ else
+ r = jQuery.merge( r, arg );
+
+ });
+
+ return r;
+ },
+
+ attr: function(elem, name, value){
+ var fix = {
+ "for": "htmlFor",
+ "class": "className",
+ "float": jQuery.browser.msie ? "styleFloat" : "cssFloat",
+ cssFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat",
+ innerHTML: "innerHTML",
+ className: "className",
+ value: "value",
+ disabled: "disabled",
+ checked: "checked",
+ readonly: "readOnly",
+ selected: "selected"
+ };
+
+ // IE actually uses filters for opacity ... elem is actually elem.style
+ if ( name == "opacity" && jQuery.browser.msie && value != undefined ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ elem.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ return elem.filter = elem.filter.replace(/alpha\([^\)]*\)/gi,"") +
+ ( value == 1 ? "" : "alpha(opacity=" + value * 100 + ")" );
+
+ } else if ( name == "opacity" && jQuery.browser.msie )
+ return elem.filter ?
+ parseFloat( elem.filter.match(/alpha\(opacity=(.*)\)/)[1] ) / 100 : 1;
+
+ // Mozilla doesn't play well with opacity 1
+ if ( name == "opacity" && jQuery.browser.mozilla && value == 1 )
+ value = 0.9999;
+
+ // Certain attributes only work when accessed via the old DOM 0 way
+ if ( fix[name] ) {
+ if ( value != undefined ) elem[fix[name]] = value;
+ return elem[fix[name]];
+
+ } else if ( value == undefined && jQuery.browser.msie && elem.nodeName && elem.nodeName.toUpperCase() == "FORM" && (name == "action" || name == "method") )
+ return elem.getAttributeNode(name).nodeValue;
+
+ // IE elem.getAttribute passes even for style
+ else if ( elem.tagName ) {
+ if ( value != undefined ) elem.setAttribute( name, value );
+ return elem.getAttribute( name );
+
+ } else {
+ name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
+ if ( value != undefined ) elem[name] = value;
+ return elem[name];
+ }
+ },
+ trim: function(t){
+ return t.replace(/^\s+|\s+$/g, "");
+ },
+
+ makeArray: function( a ) {
+ var r = [];
+
+ if ( a.constructor != Array )
+ for ( var i = 0, al = a.length; i < al; i++ )
+ r.push( a[i] );
+ else
+ r = a.slice( 0 );
+
+ return r;
+ },
+
+ inArray: function( b, a ) {
+ for ( var i = 0, al = a.length; i < al; i++ )
+ if ( a[i] == b )
+ return i;
+ return -1;
+ },
+ merge: function(first, second) {
+ var r = [].slice.call( first, 0 );
+
+ // Now check for duplicates between the two arrays
+ // and only add the unique items
+ for ( var i = 0, sl = second.length; i < sl; i++ )
+ // Check for duplicates
+ if ( jQuery.inArray( second[i], r ) == -1 )
+ // The item is unique, add it
+ first.push( second[i] );
+
+ return first;
+ },
+ grep: function(elems, fn, inv) {
+ // If a string is passed in for the function, make a function
+ // for it (a handy shortcut)
+ if ( typeof fn == "string" )
+ fn = new Function("a","i","return " + fn);
+
+ var result = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, el = elems.length; i < el; i++ )
+ if ( !inv && fn(elems[i],i) || inv && !fn(elems[i],i) )
+ result.push( elems[i] );
+
+ return result;
+ },
+ map: function(elems, fn) {
+ // If a string is passed in for the function, make a function
+ // for it (a handy shortcut)
+ if ( typeof fn == "string" )
+ fn = new Function("a","return " + fn);
+
+ var result = [], r = [];
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, el = elems.length; i < el; i++ ) {
+ var val = fn(elems[i],i);
+
+ if ( val !== null && val != undefined ) {
+ if ( val.constructor != Array ) val = [val];
+ result = result.concat( val );
+ }
+ }
+
+ var r = result.length ? [ result[0] ] : [];
+
+ check: for ( var i = 1, rl = result.length; i < rl; i++ ) {
+ for ( var j = 0; j < i; j++ )
+ if ( result[i] == r[j] )
+ continue check;
+
+ r.push( result[i] );
+ }
+
+ return r;
+ }
+});
+
+/*
+ * Whether the W3C compliant box model is being used.
+ *
+ * @property
+ * @name $.boxModel
+ * @type Boolean
+ * @cat JavaScript
+ */
+new function() {
+ var b = navigator.userAgent.toLowerCase();
+
+ // Figure out what browser is being used
+ jQuery.browser = {
+ safari: /webkit/.test(b),
+ opera: /opera/.test(b),
+ msie: /msie/.test(b) && !/opera/.test(b),
+ mozilla: /mozilla/.test(b) && !/(compatible|webkit)/.test(b)
+ };
+
+ // Check to see if the W3C box model is being used
+ jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat";
+};
+
+jQuery.each({
+ parent: "a.parentNode",
+ parents: "jQuery.parents(a)",
+ next: "jQuery.nth(a,2,'nextSibling')",
+ prev: "jQuery.nth(a,2,'previousSibling')",
+ siblings: "jQuery.sibling(a.parentNode.firstChild,a)",
+ children: "jQuery.sibling(a.firstChild)"
+}, function(i,n){
+ jQuery.fn[ i ] = function(a) {
+ var ret = jQuery.map(this,n);
+ if ( a && typeof a == "string" )
+ ret = jQuery.multiFilter(a,ret);
+ return this.pushStack( ret );
+ };
+});
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after"
+}, function(i,n){
+ jQuery.fn[ i ] = function(){
+ var a = arguments;
+ return this.each(function(){
+ for ( var j = 0, al = a.length; j < al; j++ )
+ jQuery(a[j])[n]( this );
+ });
+ };
+});
+
+jQuery.each( {
+ removeAttr: function( key ) {
+ jQuery.attr( this, key, "" );
+ this.removeAttribute( key );
+ },
+ addClass: function(c){
+ jQuery.className.add(this,c);
+ },
+ removeClass: function(c){
+ jQuery.className.remove(this,c);
+ },
+ toggleClass: function( c ){
+ jQuery.className[ jQuery.className.has(this,c) ? "remove" : "add" ](this, c);
+ },
+ remove: function(a){
+ if ( !a || jQuery.filter( a, [this] ).r.length )
+ this.parentNode.removeChild( this );
+ },
+ empty: function() {
+ while ( this.firstChild )
+ this.removeChild( this.firstChild );
+ }
+}, function(i,n){
+ jQuery.fn[ i ] = function() {
+ return this.each( n, arguments );
+ };
+});
+
+jQuery.each( [ "eq", "lt", "gt", "contains" ], function(i,n){
+ jQuery.fn[ n ] = function(num,fn) {
+ return this.filter( ":" + n + "(" + num + ")", fn );
+ };
+});
+
+jQuery.each( [ "height", "width" ], function(i,n){
+ jQuery.fn[ n ] = function(h) {
+ return h == undefined ?
+ ( this.length ? jQuery.css( this[0], n ) : null ) :
+ this.css( n, h.constructor == String ? h : h + "px" );
+ };
+});
+jQuery.extend({
+ expr: {
+ "": "m[2]=='*'||a.nodeName.toUpperCase()==m[2].toUpperCase()",
+ "#": "a.getAttribute('id')==m[2]",
+ ":": {
+ // Position Checks
+ lt: "i<m[3]-0",
+ gt: "i>m[3]-0",
+ nth: "m[3]-0==i",
+ eq: "m[3]-0==i",
+ first: "i==0",
+ last: "i==r.length-1",
+ even: "i%2==0",
+ odd: "i%2",
+
+ // Child Checks
+ "nth-child": "jQuery.nth(a.parentNode.firstChild,m[3],'nextSibling',a)==a",
+ "first-child": "jQuery.nth(a.parentNode.firstChild,1,'nextSibling')==a",
+ "last-child": "jQuery.nth(a.parentNode.lastChild,1,'previousSibling')==a",
+ "only-child": "jQuery.sibling(a.parentNode.firstChild).length==1",
+
+ // Parent Checks
+ parent: "a.firstChild",
+ empty: "!a.firstChild",
+
+ // Text Check
+ contains: "jQuery.fn.text.apply([a]).indexOf(m[3])>=0",
+
+ // Visibility
+ visible: 'a.type!="hidden"&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden"',
+ hidden: 'a.type=="hidden"||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden"',
+
+ // Form attributes
+ enabled: "!a.disabled",
+ disabled: "a.disabled",
+ checked: "a.checked",
+ selected: "a.selected||jQuery.attr(a,'selected')",
+
+ // Form elements
+ text: "a.type=='text'",
+ radio: "a.type=='radio'",
+ checkbox: "a.type=='checkbox'",
+ file: "a.type=='file'",
+ password: "a.type=='password'",
+ submit: "a.type=='submit'",
+ image: "a.type=='image'",
+ reset: "a.type=='reset'",
+ button: 'a.type=="button"||a.nodeName=="BUTTON"',
+ input: "/input|select|textarea|button/i.test(a.nodeName)"
+ },
+ ".": "jQuery.className.has(a,m[2])",
+ "@": {
+ "=": "z==m[4]",
+ "!=": "z!=m[4]",
+ "^=": "z&&!z.indexOf(m[4])",
+ "$=": "z&&z.substr(z.length - m[4].length,m[4].length)==m[4]",
+ "*=": "z&&z.indexOf(m[4])>=0",
+ "": "z",
+ _resort: function(m){
+ return ["", m[1], m[3], m[2], m[5]];
+ },
+ _prefix: "z=a[m[3]]||jQuery.attr(a,m[3]);"
+ },
+ "[": "jQuery.find(m[2],a).length"
+ },
+
+ // The regular expressions that power the parsing engine
+ parse: [
+ // Match: [@value='test'], [@foo]
+ /^\[ *(@)([a-z0-9_-]*) *([!*$^=]*) *('?"?)(.*?)\4 *\]/i,
+
+ // Match: [div], [div p]
+ /^(\[)\s*(.*?(\[.*?\])?[^[]*?)\s*\]/,
+
+ // Match: :contains('foo')
+ /^(:)([a-z0-9_-]*)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/i,
+
+ // Match: :even, :last-chlid
+ /^([:.#]*)([a-z0-9_*-]*)/i
+ ],
+
+ token: [
+ /^(\/?\.\.)/, "a.parentNode",
+ /^(>|\/)/, "jQuery.sibling(a.firstChild)",
+ /^(\+)/, "jQuery.nth(a,2,'nextSibling')",
+ /^(~)/, function(a){
+ var s = jQuery.sibling(a.parentNode.firstChild);
+ return s.slice(0, jQuery.inArray(a,s));
+ }
+ ],
+
+ 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 ) {
+ // Quickly handle non-string expressions
+ if ( typeof t != "string" )
+ return [ t ];
+
+ // Make sure that the context is a DOM Element
+ if ( context && !context.nodeType )
+ context = null;
+
+ // Set the correct context (if none is provided)
+ context = context || document;
+
+ // Handle the common XPath // expression
+ if ( !t.indexOf("//") ) {
+ context = context.documentElement;
+ t = t.substr(2,t.length);
+
+ // And the / root expression
+ } else if ( !t.indexOf("/") ) {
+ context = context.documentElement;
+ t = t.substr(1,t.length);
+ if ( t.indexOf("/") >= 1 )
+ t = t.substr(t.indexOf("/"),t.length);
+ }
+
+ // Initialize the search
+ var ret = [context], done = [], last = null;
+
+ // Continue while a selector expression exists, and while
+ // we're no longer looping upon ourselves
+ while ( t && last != t ) {
+ var r = [];
+ last = t;
+
+ t = jQuery.trim(t).replace( /^\/\//i, "" );
+
+ var foundToken = false;
+
+ // An attempt at speeding up child selectors that
+ // point to a specific element tag
+ var re = /^[\/>]\s*([a-z0-9*-]+)/i;
+ var m = re.exec(t);
+
+ if ( m ) {
+ // Perform our own iteration and filter
+ jQuery.each( ret, function(){
+ for ( var c = this.firstChild; c; c = c.nextSibling )
+ if ( c.nodeType == 1 && ( c.nodeName == m[1].toUpperCase() || m[1] == "*" ) )
+ r.push( c );
+ });
+
+ ret = r;
+ t = jQuery.trim( t.replace( re, "" ) );
+ foundToken = true;
+ } else {
+ // Look for pre-defined expression tokens
+ for ( var i = 0; i < jQuery.token.length; i += 2 ) {
+ // Attempt to match each, individual, token in
+ // the specified order
+ var re = jQuery.token[i];
+ var m = re.exec(t);
+
+ // If the token match was found
+ if ( m ) {
+ // Map it against the token's handler
+ r = ret = jQuery.map( ret, jQuery.isFunction( jQuery.token[i+1] ) ?
+ jQuery.token[i+1] :
+ function(a){ return eval(jQuery.token[i+1]); });
+
+ // And remove the token
+ t = jQuery.trim( t.replace( re, "" ) );
+ foundToken = true;
+ break;
+ }
+ }
+ }
+
+ // See if there's still an expression, and that we haven't already
+ // matched a token
+ if ( t && !foundToken ) {
+ // Handle multiple expressions
+ if ( !t.indexOf(",") ) {
+ // Clean the result set
+ if ( ret[0] == context ) ret.shift();
+
+ // Merge the result sets
+ jQuery.merge( done, ret );
+
+ // Reset the context
+ r = ret = [context];
+
+ // Touch up the selector string
+ t = " " + t.substr(1,t.length);
+
+ } else {
+ // Optomize for the case nodeName#idName
+ var re2 = /^([a-z0-9_-]+)(#)([a-z0-9\\*_-]*)/i;
+ var m = re2.exec(t);
+
+ // Re-organize the results, so that they're consistent
+ if ( m ) {
+ m = [ 0, m[2], m[3], m[1] ];
+
+ } else {
+ // Otherwise, do a traditional filter check for
+ // ID, class, and element selectors
+ re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
+ m = re2.exec(t);
+ }
+
+ // Try to do a global search by ID, where we can
+ if ( m[1] == "#" && ret[ret.length-1].getElementById ) {
+ // Optimization for HTML document case
+ var oid = ret[ret.length-1].getElementById(m[2]);
+
+ // Do a quick check for node name (where applicable) so
+ // that div#foo searches will be really fast
+ ret = r = oid &&
+ (!m[3] || oid.nodeName == m[3].toUpperCase()) ? [oid] : [];
+
+ } else {
+ // Pre-compile a regular expression to handle class searches
+ if ( m[1] == "." )
+ var rec = new RegExp("(^|\\s)" + m[2] + "(\\s|$)");
+
+ // We need to find all descendant elements, it is more
+ // efficient to use getAll() when we are already further down
+ // the tree - we try to recognize that here
+ jQuery.each( ret, function(){
+ // Grab the tag name being searched for
+ var tag = m[1] != "" || m[0] == "" ? "*" : m[2];
+
+ // Handle IE7 being really dumb about <object>s
+ if ( this.nodeName.toUpperCase() == "OBJECT" && tag == "*" )
+ tag = "param";
+
+ jQuery.merge( r,
+ m[1] != "" && ret.length != 1 ?
+ jQuery.getAll( this, [], m[1], m[2], rec ) :
+ this.getElementsByTagName( tag )
+ );
+ });
+
+ // It's faster to filter by class and be done with it
+ if ( m[1] == "." && ret.length == 1 )
+ r = jQuery.grep( r, function(e) {
+ return rec.test(e.className);
+ });
+
+ // Same with ID filtering
+ if ( m[1] == "#" && ret.length == 1 ) {
+ // Remember, then wipe out, the result set
+ var tmp = r;
+ r = [];
+
+ // Then try to find the element with the ID
+ jQuery.each( tmp, function(){
+ if ( this.getAttribute("id") == m[2] ) {
+ r = [ this ];
+ return false;
+ }
+ });
+ }
+
+ ret = r;
+ }
+
+ t = t.replace( re2, "" );
+ }
+
+ }
+
+ // If a selector string still exists
+ if ( t ) {
+ // Attempt to filter it
+ var val = jQuery.filter(t,r);
+ ret = r = val.r;
+ t = jQuery.trim(val.t);
+ }
+ }
+
+ // Remove the root context
+ if ( ret && ret[0] == context ) ret.shift();
+
+ // And combine the results
+ jQuery.merge( done, ret );
+
+ return done;
+ },
+
+ filter: function(t,r,not) {
+ // Look for common filter expressions
+ while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
+
+ var p = jQuery.parse, m;
+
+ jQuery.each( p, function(i,re){
+
+ // Look for, and replace, string-like sequences
+ // and finally build a regexp out of it
+ m = re.exec( t );
+
+ if ( m ) {
+ // Remove what we just matched
+ t = t.substring( m[0].length );
+
+ // Re-organize the first match
+ if ( jQuery.expr[ m[1] ]._resort )
+ m = jQuery.expr[ m[1] ]._resort( m );
+
+ return false;
+ }
+ });
+
+ // :not() is a special case that can be optimized by
+ // keeping it out of the expression list
+ if ( m[1] == ":" && m[2] == "not" )
+ r = jQuery.filter(m[3], r, true).r;
+
+ // Handle classes as a special case (this will help to
+ // improve the speed, as the regexp will only be compiled once)
+ else if ( m[1] == "." ) {
+
+ var re = new RegExp("(^|\\s)" + m[2] + "(\\s|$)");
+ r = jQuery.grep( r, function(e){
+ return re.test(e.className || "");
+ }, not);
+
+ // Otherwise, find the expression to execute
+ } else {
+ var f = jQuery.expr[m[1]];
+ if ( typeof f != "string" )
+ f = jQuery.expr[m[1]][m[2]];
+
+ // Build a custom macro to enclose it
+ eval("f = function(a,i){" +
+ ( jQuery.expr[ m[1] ]._prefix || "" ) +
+ "return " + f + "}");
+
+ // Execute it against the current filter
+ r = jQuery.grep( r, f, not );
+ }
+ }
+
+ // Return an array of filtered elements (r)
+ // and the modified expression string (t)
+ return { r: r, t: t };
+ },
+
+ getAll: function( o, r, token, name, re ) {
+ for ( var s = o.firstChild; s; s = s.nextSibling )
+ if ( s.nodeType == 1 ) {
+ var add = true;
+
+ if ( token == "." )
+ add = s.className && re.test(s.className);
+ else if ( token == "#" )
+ add = s.getAttribute("id") == name;
+
+ if ( add )
+ r.push( s );
+
+ if ( token == "#" && r.length ) break;
+
+ if ( s.firstChild )
+ jQuery.getAll( s, r, token, name, re );
+ }
+
+ return r;
+ },
+ parents: function( elem ){
+ var matched = [];
+ var cur = elem.parentNode;
+ while ( cur && cur != document ) {
+ matched.push( cur );
+ cur = cur.parentNode;
+ }
+ return matched;
+ },
+ nth: function(cur,result,dir,elem){
+ result = result || 1;
+ var num = 0;
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType == 1 ) num++;
+ if ( num == result || result == "even" && num % 2 == 0 && num > 1 && cur == elem ||
+ result == "odd" && num % 2 == 1 && cur == elem ) return cur;
+ }
+ },
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType == 1 && (!elem || n != elem) )
+ r.push( n );
+ }
+
+ return r;
+ }
+});
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code orignated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function(element, type, handler, data) {
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( jQuery.browser.msie && element.setInterval != undefined )
+ element = window;
+
+ // if data is passed, bind to handler
+ if( data )
+ handler.data = data;
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid )
+ handler.guid = this.guid++;
+
+ // Init the element's event structure
+ if (!element.events)
+ element.events = {};
+
+ // Get the current list of functions bound to this event
+ var handlers = element.events[type];
+
+ // If it hasn't been initialized yet
+ if (!handlers) {
+ // Init the event handler queue
+ handlers = element.events[type] = {};
+
+ // Remember an existing handler, if it's already there
+ if (element["on" + type])
+ handlers[0] = element["on" + type];
+ }
+
+ // Add the function to the element's handler list
+ handlers[handler.guid] = handler;
+
+ // And bind the global event handler to the element
+ element["on" + type] = this.handle;
+
+ // Remember the function in a global list (for triggering)
+ if (!this.global[type])
+ this.global[type] = [];
+ this.global[type].push( element );
+ },
+
+ guid: 1,
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function(element, type, handler) {
+ if (element.events)
+ if ( type && type.type )
+ delete element.events[ type.type ][ type.handler.guid ];
+ else if (type && element.events[type])
+ if ( handler )
+ delete element.events[type][handler.guid];
+ else
+ for ( var i in element.events[type] )
+ delete element.events[type][i];
+ else
+ for ( var j in element.events )
+ this.remove( element, j );
+ },
+
+ trigger: function(type,data,element) {
+ // Clone the incoming data, if any
+ data = jQuery.makeArray(data || []);
+
+ // Handle a global trigger
+ if ( !element ) {
+ var g = this.global[type];
+ if ( g )
+ jQuery.each( g, function(){
+ jQuery.event.trigger( type, data, this );
+ });
+
+ // Handle triggering a single element
+ } else if ( element["on" + type] ) {
+ // Pass along a fake event
+ data.unshift( this.fix({ type: type, target: element }) );
+
+ // Trigger the event
+ var val = element["on" + type].apply( element, data );
+
+ if ( val !== false && jQuery.isFunction( element[ type ] ) )
+ element[ type ]();
+ }
+ },
+
+ handle: function(event) {
+ if ( typeof jQuery == "undefined" ) return false;
+
+ // Empty object is for triggered events with no data
+ event = jQuery.event.fix( event || window.event || {} );
+
+ // returned undefined or false
+ var returnValue;
+
+ var c = this.events[event.type];
+
+ var args = [].slice.call( arguments, 1 );
+ args.unshift( event );
+
+ for ( var j in c ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ args[0].handler = c[j];
+ args[0].data = c[j].data;
+
+ if ( c[j].apply( this, args ) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ returnValue = false;
+ }
+ }
+
+ // Clean up added properties in IE to prevent memory leak
+ if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;
+
+ return returnValue;
+ },
+
+ fix: function(event) {
+ // Fix target property, if necessary
+ if ( !event.target && event.srcElement )
+ event.target = event.srcElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == undefined && event.clientX != undefined ) {
+ var e = document.documentElement, b = document.body;
+ event.pageX = event.clientX + (e.scrollLeft || b.scrollLeft);
+ event.pageY = event.clientY + (e.scrollTop || b.scrollTop);
+ }
+
+ // check if target is a textnode (safari)
+ if (jQuery.browser.safari && event.target.nodeType == 3) {
+ // store a copy of the original event object
+ // and clone because target is read only
+ var originalEvent = event;
+ event = jQuery.extend({}, originalEvent);
+
+ // get parentnode from textnode
+ event.target = originalEvent.target.parentNode;
+
+ // add preventDefault and stopPropagation since
+ // they will not work on the clone
+ event.preventDefault = function() {
+ return originalEvent.preventDefault();
+ };
+ event.stopPropagation = function() {
+ return originalEvent.stopPropagation();
+ };
+ }
+
+ // fix preventDefault and stopPropagation
+ if (!event.preventDefault)
+ event.preventDefault = function() {
+ this.returnValue = false;
+ };
+
+ if (!event.stopPropagation)
+ event.stopPropagation = function() {
+ this.cancelBubble = true;
+ };
+
+ return event;
+ }
+};
+
+jQuery.fn.extend({
+ bind: function( type, data, fn ) {
+ return this.each(function(){
+ jQuery.event.add( this, type, fn || data, data );
+ });
+ },
+ one: function( type, data, fn ) {
+ return this.each(function(){
+ jQuery.event.add( this, type, function(event) {
+ jQuery(this).unbind(event);
+ return (fn || data).apply( this, arguments);
+ }, data);
+ });
+ },
+ unbind: function( type, fn ) {
+ return this.each(function(){
+ jQuery.event.remove( this, type, fn );
+ });
+ },
+ trigger: function( type, data ) {
+ return this.each(function(){
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ toggle: function() {
+ // Save reference to arguments for access in closure
+ var a = arguments;
+
+ return this.click(function(e) {
+ // Figure out which function to execute
+ this.lastToggle = this.lastToggle == 0 ? 1 : 0;
+
+ // Make sure that clicks stop
+ e.preventDefault();
+
+ // and execute the function
+ return a[this.lastToggle].apply( this, [e] ) || false;
+ });
+ },
+ hover: function(f,g) {
+
+ // A private function for handling mouse 'hovering'
+ function handleHover(e) {
+ // Check if mouse(over|out) are still within the same parent element
+ var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
+
+ // Traverse up the tree
+ while ( p && p != this ) try { p = p.parentNode } catch(e) { p = this; };
+
+ // If we actually just moused on to a sub-element, ignore it
+ if ( p == this ) return false;
+
+ // Execute the right function
+ return (e.type == "mouseover" ? f : g).apply(this, [e]);
+ }
+
+ // Bind the function to the two event listeners
+ return this.mouseover(handleHover).mouseout(handleHover);
+ },
+ ready: function(f) {
+ // If the DOM is already ready
+ if ( jQuery.isReady )
+ // Execute the function immediately
+ f.apply( document, [jQuery] );
+
+ // Otherwise, remember the function for later
+ else {
+ // Add the function to the wait list
+ jQuery.readyList.push( function() { return f.apply(this, [jQuery]) } );
+ }
+
+ return this;
+ }
+});
+
+jQuery.extend({
+ /*
+ * All the code that makes DOM Ready work nicely.
+ */
+ isReady: false,
+ readyList: [],
+
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( jQuery.readyList ) {
+ // Execute all of them
+ jQuery.each( jQuery.readyList, function(){
+ this.apply( document );
+ });
+
+ // Reset the list of functions
+ jQuery.readyList = null;
+ }
+ // Remove event lisenter to avoid memory leak
+ if ( jQuery.browser.mozilla || jQuery.browser.opera )
+ document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
+ }
+ }
+});
+
+new function(){
+
+ 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,o){
+
+ // Handle event binding
+ jQuery.fn[o] = function(f){
+ return f ? this.bind(o, f) : this.trigger(o);
+ };
+
+ });
+
+ // If Mozilla is used
+ if ( jQuery.browser.mozilla || jQuery.browser.opera )
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
+
+ // If IE is used, use the excellent hack by Matthias Miller
+ // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
+ else if ( jQuery.browser.msie ) {
+
+ // Only works if you document.write() it
+ document.write("<scr" + "ipt id=__ie_init defer=true " +
+ "src=//:><\/script>");
+
+ // Use the defer script hack
+ var script = document.getElementById("__ie_init");
+
+ // script does not exist if jQuery is loaded dynamically
+ if ( script )
+ script.onreadystatechange = function() {
+ if ( this.readyState != "complete" ) return;
+ this.parentNode.removeChild( this );
+ jQuery.ready();
+ };
+
+ // Clear from memory
+ script = null;
+
+ // If Safari is used
+ } else if ( jQuery.browser.safari )
+ // Continually check to see if the document.readyState is valid
+ jQuery.safariTimer = setInterval(function(){
+ // loaded and complete are both valid states
+ if ( document.readyState == "loaded" ||
+ document.readyState == "complete" ) {
+
+ // If either one are found, remove the timer
+ clearInterval( jQuery.safariTimer );
+ jQuery.safariTimer = null;
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ }, 10);
+
+ // A fallback to window.onload, that will always work
+ jQuery.event.add( window, "load", jQuery.ready );
+
+};
+
+// Clean up after IE to avoid memory leaks
+if (jQuery.browser.msie)
+ jQuery(window).one("unload", function() {
+ var global = jQuery.event.global;
+ for ( var type in global ) {
+ var els = global[type], i = els.length;
+ if ( i && type != 'unload' )
+ do
+ jQuery.event.remove(els[i-1], type);
+ while (--i);
+ }
+ });
+jQuery.fn.extend({
+
+ show: function(speed,callback){
+ var hidden = this.filter(":hidden");
+ return speed ?
+ hidden.animate({
+ height: "show", width: "show", opacity: "show"
+ }, speed, callback) :
+
+ hidden.each(function(){
+ this.style.display = this.oldblock ? this.oldblock : "";
+ if ( jQuery.css(this,"display") == "none" )
+ this.style.display = "block";
+ });
+ },
+
+ hide: function(speed,callback){
+ var visible = this.filter(":visible");
+ return speed ?
+ visible.animate({
+ height: "hide", width: "hide", opacity: "hide"
+ }, speed, callback) :
+
+ visible.each(function(){
+ this.oldblock = this.oldblock || jQuery.css(this,"display");
+ if ( this.oldblock == "none" )
+ this.oldblock = "block";
+ this.style.display = "none";
+ });
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+ toggle: function( fn, fn2 ){
+ var args = arguments;
+ return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
+ this._toggle( fn, fn2 ) :
+ this.each(function(){
+ jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]
+ .apply( jQuery(this), args );
+ });
+ },
+ 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.each(function(){
+ var state = jQuery(this).is(":hidden") ? "show" : "hide";
+ jQuery(this).animate({height: state}, 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 ) {
+ return this.queue(function(){
+
+ this.curAnim = jQuery.extend({}, prop);
+ var opt = jQuery.speed(speed, easing, callback);
+
+ for ( var p in prop ) {
+ var e = new jQuery.fx( this, opt, p );
+ if ( prop[p].constructor == Number )
+ e.custom( e.cur(), prop[p] );
+ else
+ e[ prop[p] ]( prop );
+ }
+
+ });
+ },
+ queue: function(type,fn){
+ if ( !fn ) {
+ fn = type;
+ type = "fx";
+ }
+
+ return this.each(function(){
+ if ( !this.queue )
+ this.queue = {};
+
+ if ( !this.queue[type] )
+ this.queue[type] = [];
+
+ this.queue[type].push( fn );
+
+ if ( this.queue[type].length == 1 )
+ fn.apply(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 :
+ { slow: 600, fast: 200 }[opt.duration]) || 400;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function(){
+ jQuery.dequeue(this, "fx");
+ if ( jQuery.isFunction( opt.old ) )
+ opt.old.apply( this );
+ };
+
+ return opt;
+ },
+
+ easing: {},
+
+ queue: {},
+
+ dequeue: function(elem,type){
+ type = type || "fx";
+
+ if ( elem.queue && elem.queue[type] ) {
+ // Remove self
+ elem.queue[type].shift();
+
+ // Get next function
+ var f = elem.queue[type][0];
+
+ if ( f ) f.apply( elem );
+ }
+ },
+
+ /*
+ * I originally wrote fx() as a clone of moo.fx and in the process
+ * of making it small in size the code became illegible to sane
+ * people. You've been warned.
+ */
+
+ fx: function( elem, options, prop ){
+
+ var z = this;
+
+ // The styles
+ var y = elem.style;
+
+ // Store display property
+ var oldDisplay = jQuery.css(elem, "display");
+
+ // Set display property to block for animation
+ y.display = "block";
+
+ // Make sure that nothing sneaks out
+ y.overflow = "hidden";
+
+ // Simple function for setting a style value
+ z.a = function(){
+ if ( options.step )
+ options.step.apply( elem, [ z.now ] );
+
+ if ( prop == "opacity" )
+ jQuery.attr(y, "opacity", z.now); // Let attr handle opacity
+ else if ( parseInt(z.now) ) // My hate for IE will never die
+ y[prop] = parseInt(z.now) + "px";
+ };
+
+ // Figure out the maximum number to run to
+ z.max = function(){
+ return parseFloat( jQuery.css(elem,prop) );
+ };
+
+ // Get the current size
+ z.cur = function(){
+ var r = parseFloat( jQuery.curCSS(elem, prop) );
+ return r && r > -10000 ? r : z.max();
+ };
+
+ // Start an animation from one number to another
+ z.custom = function(from,to){
+ z.startTime = (new Date()).getTime();
+ z.now = from;
+ z.a();
+
+ z.timer = setInterval(function(){
+ z.step(from, to);
+ }, 13);
+ };
+
+ // Simple 'show' function
+ z.show = function(){
+ if ( !elem.orig ) elem.orig = {};
+
+ // Remember where we started, so that we can go back to it later
+ elem.orig[prop] = this.cur();
+
+ options.show = true;
+
+ // Begin the animation
+ z.custom(0, elem.orig[prop]);
+
+ // Stupid IE, look what you made me do
+ if ( prop != "opacity" )
+ y[prop] = "1px";
+ };
+
+ // Simple 'hide' function
+ z.hide = function(){
+ if ( !elem.orig ) elem.orig = {};
+
+ // Remember where we started, so that we can go back to it later
+ elem.orig[prop] = this.cur();
+
+ options.hide = true;
+
+ // Begin the animation
+ z.custom(elem.orig[prop], 0);
+ };
+
+ //Simple 'toggle' function
+ z.toggle = function() {
+ if ( !elem.orig ) elem.orig = {};
+
+ // Remember where we started, so that we can go back to it later
+ elem.orig[prop] = this.cur();
+
+ if(oldDisplay == "none") {
+ options.show = true;
+
+ // Stupid IE, look what you made me do
+ if ( prop != "opacity" )
+ y[prop] = "1px";
+
+ // Begin the animation
+ z.custom(0, elem.orig[prop]);
+ } else {
+ options.hide = true;
+
+ // Begin the animation
+ z.custom(elem.orig[prop], 0);
+ }
+ };
+
+ // Each step of an animation
+ z.step = function(firstNum, lastNum){
+ var t = (new Date()).getTime();
+
+ if (t > options.duration + z.startTime) {
+ // Stop the timer
+ clearInterval(z.timer);
+ z.timer = null;
+
+ z.now = lastNum;
+ z.a();
+
+ if (elem.curAnim) elem.curAnim[ prop ] = true;
+
+ var done = true;
+ for ( var i in elem.curAnim )
+ if ( elem.curAnim[i] !== true )
+ done = false;
+
+ if ( done ) {
+ // Reset the overflow
+ y.overflow = "";
+
+ // Reset the display
+ y.display = oldDisplay;
+ if (jQuery.css(elem, "display") == "none")
+ y.display = "block";
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide )
+ y.display = "none";
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show )
+ for ( var p in elem.curAnim )
+ if (p == "opacity")
+ jQuery.attr(y, p, elem.orig[p]);
+ else
+ y[p] = "";
+ }
+
+ // If a callback was provided, execute it
+ if ( done && jQuery.isFunction( options.complete ) )
+ // Execute the complete function
+ options.complete.apply( elem );
+ } else {
+ var n = t - this.startTime;
+ // Figure out where in the animation we are and set the number
+ var p = n / options.duration;
+
+ // If the easing function exists, then use it
+ z.now = options.easing && jQuery.easing[options.easing] ?
+ jQuery.easing[options.easing](p, n, firstNum, (lastNum-firstNum), options.duration) :
+ // else use default linear easing
+ ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
+
+ // Perform the next step of the animation
+ z.a();
+ }
+ };
+
+ }
+});
+jQuery.fn.extend({
+ loadIfModified: function( url, params, callback ) {
+ this.load( url, params, callback, 1 );
+ },
+ load: function( url, params, callback, ifModified ) {
+ if ( jQuery.isFunction( url ) )
+ return this.bind("load", url);
+
+ callback = callback || function(){};
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params )
+ // If it's a function
+ if ( jQuery.isFunction( params.constructor ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else {
+ params = jQuery.param( params );
+ type = "POST";
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ data: params,
+ ifModified: ifModified,
+ complete: function(res, status){
+ if ( status == "success" || !ifModified && status == "notmodified" )
+ // Inject the HTML into all the matched elements
+ self.attr("innerHTML", res.responseText)
+ // Execute all the scripts inside of the newly-injected HTML
+ .evalScripts()
+ // Execute callback
+ .each( callback, [res.responseText, status, res] );
+ else
+ callback.apply( self, [res.responseText, status, res] );
+ }
+ });
+ return this;
+ },
+ serialize: function() {
+ return jQuery.param( this );
+ },
+ evalScripts: function() {
+ return this.find("script").each(function(){
+ if ( this.src )
+ jQuery.getScript( this.src );
+ else
+ jQuery.globalEval( this.text || this.textContent || this.innerHTML || "" );
+ }).end();
+ }
+
+});
+
+// If IE is used, create a wrapper for the XMLHttpRequest object
+if ( jQuery.browser.msie && typeof XMLHttpRequest == "undefined" )
+ XMLHttpRequest = function(){
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ };
+
+// Attach a bunch of functions for handling common AJAX events
+
+jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
+ jQuery.fn[o] = function(f){
+ return this.bind(o, f);
+ };
+});
+
+jQuery.extend({
+ get: function( url, data, callback, type, ifModified ) {
+ // shift arguments if data argument was ommited
+ if ( jQuery.isFunction( data ) ) {
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type,
+ ifModified: ifModified
+ });
+ },
+ getIfModified: function( url, data, callback, type ) {
+ return jQuery.get(url, data, callback, type, 1);
+ },
+ 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 ) {
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ // timeout (ms)
+ //timeout: 0,
+ ajaxTimeout: function( timeout ) {
+ jQuery.ajaxSettings.timeout = timeout;
+ },
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ global: true,
+ type: "GET",
+ timeout: 0,
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ data: null
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ ajax: function( s ) {
+ // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
+ s = jQuery.extend({}, jQuery.ajaxSettings, s);
+
+ // if data available
+ if ( s.data ) {
+ // convert data if not already a string
+ if (s.processData && typeof s.data != "string")
+ s.data = jQuery.param(s.data);
+ // append data to url for get requests
+ if( s.type.toLowerCase() == "get" )
+ // "?" + data or "&" + data (in case there are already params)
+ s.url += ((s.url.indexOf("?") > -1) ? "&" : "?") + s.data;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && ! jQuery.active++ )
+ jQuery.event.trigger( "ajaxStart" );
+
+ var requestDone = false;
+
+ // Create the request object
+ var xml = new XMLHttpRequest();
+
+ // Open the socket
+ xml.open(s.type, s.url, s.async);
+
+ // Set the correct header, if data is being sent
+ if ( s.data )
+ xml.setRequestHeader("Content-Type", s.contentType);
+
+ // Set the If-Modified-Since header, if ifModified mode.
+ if ( s.ifModified )
+ xml.setRequestHeader("If-Modified-Since",
+ jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+
+ // Make sure the browser sends the right content length
+ if ( xml.overrideMimeType )
+ xml.setRequestHeader("Connection", "close");
+
+ // Allow custom headers/mimetypes
+ if( s.beforeSend )
+ s.beforeSend(xml);
+
+ if ( s.global )
+ jQuery.event.trigger("ajaxSend", [xml, s]);
+
+ // Wait for a response to come back
+ var onreadystatechange = function(isTimeout){
+ // The transfer is complete and the data is available, or the request timed out
+ if ( xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
+ requestDone = true;
+ var status;
+ try {
+ status = jQuery.httpSuccess( xml ) && isTimeout != "timeout" ?
+ s.ifModified && jQuery.httpNotModified( xml, s.url ) ? "notmodified" : "success" : "error";
+ // Make sure that the request was successful or notmodified
+ if ( status != "error" ) {
+ // Cache Last-Modified header, if ifModified mode.
+ var modRes;
+ try {
+ modRes = xml.getResponseHeader("Last-Modified");
+ } catch(e) {} // swallow exception thrown by FF if header is not available
+
+ if ( s.ifModified && modRes )
+ jQuery.lastModified[s.url] = modRes;
+
+ // process the data (runs the xml through httpData regardless of callback)
+ var data = jQuery.httpData( xml, s.dataType );
+
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success )
+ s.success( data, status );
+
+ // Fire the global callback
+ if( s.global )
+ jQuery.event.trigger( "ajaxSuccess", [xml, s] );
+ } else
+ jQuery.handleError(s, xml, status);
+ } catch(e) {
+ status = "error";
+ jQuery.handleError(s, xml, status, e);
+ }
+
+ // The request was completed
+ if( s.global )
+ jQuery.event.trigger( "ajaxComplete", [xml, s] );
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active )
+ jQuery.event.trigger( "ajaxStop" );
+
+ // Process result
+ if ( s.complete )
+ s.complete(xml, status);
+
+ // Stop memory leaks
+ xml.onreadystatechange = function(){};
+ xml = null;
+ }
+ };
+ xml.onreadystatechange = onreadystatechange;
+
+ // Timeout checker
+ if ( s.timeout > 0 )
+ setTimeout(function(){
+ // Check to see if the request is still happening
+ if ( xml ) {
+ // Cancel the request
+ xml.abort();
+
+ if( !requestDone )
+ onreadystatechange( "timeout" );
+ }
+ }, s.timeout);
+
+ // save non-leaking reference
+ var xml2 = xml;
+
+ // Send the data
+ try {
+ xml2.send(s.data);
+ } catch(e) {
+ jQuery.handleError(s, xml, null, e);
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async )
+ onreadystatechange();
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xml2;
+ },
+
+ handleError: function( s, xml, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) s.error( xml, status, e );
+
+ // Fire the global callback
+ if ( s.global )
+ jQuery.event.trigger( "ajaxError", [xml, s, e] );
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( r ) {
+ try {
+ return !r.status && location.protocol == "file:" ||
+ ( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
+ jQuery.browser.safari && r.status == undefined;
+ } catch(e){}
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xml, url ) {
+ try {
+ var xmlRes = xml.getResponseHeader("Last-Modified");
+
+ // Firefox always returns 200. check Last-Modified date
+ return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
+ jQuery.browser.safari && xml.status == undefined;
+ } catch(e){}
+ return false;
+ },
+
+ /* Get the data out of an XMLHttpRequest.
+ * Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
+ * otherwise return plain text.
+ * (String) data - The type of data that you're expecting back,
+ * (e.g. "xml", "html", "script")
+ */
+ httpData: function( r, type ) {
+ var ct = r.getResponseHeader("content-type");
+ var data = !type && ct && ct.indexOf("xml") >= 0;
+ data = type == "xml" || data ? r.responseXML : r.responseText;
+
+ // If the type is "script", eval it in global context
+ if ( type == "script" )
+ jQuery.globalEval( data );
+
+ // Get the JavaScript object, if JSON is used.
+ if ( type == "json" )
+ eval( "data = " + data );
+
+ // evaluate scripts within html
+ if ( type == "html" )
+ jQuery("<div>").html(data).evalScripts();
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a ) {
+ var s = [];
+
+ // If an array was passed in, assume that it is an array
+ // of form elements
+ if ( a.constructor == Array || a.jquery )
+ // Serialize the form elements
+ jQuery.each( a, function(){
+ s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
+ });
+
+ // Otherwise, assume that it's an object of key/value pairs
+ else
+ // Serialize the key/values
+ for ( var j in a )
+ // If the value is an array then the key names need to be repeated
+ if ( a[j].constructor == Array )
+ jQuery.each( a[j], function(){
+ s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
+ });
+ else
+ s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
+
+ // Return the resulting serialization
+ return s.join("&");
+ },
+
+ // evalulates a script in global context
+ // not reliable for safari
+ globalEval: function( data ) {
+ if ( window.execScript )
+ window.execScript( data );
+ else if ( jQuery.browser.safari )
+ // safari doesn't provide a synchronous global eval
+ window.setTimeout( data, 0 );
+ else
+ eval.call( window, data );
+ }
+
+});
+}
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.11.1_min.js b/devtools/client/inspector/markup/test/lib_jquery_1.11.1_min.js
new file mode 100644
index 000000000..ab28a2472
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.11.1_min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
+if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
+},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.2_min.js b/devtools/client/inspector/markup/test/lib_jquery_1.2_min.js
new file mode 100644
index 000000000..f10d4943f
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.2_min.js
@@ -0,0 +1,32 @@
+/*
+ * jQuery 1.2 - New Wave Javascript
+ *
+ * Copyright (c) 2007 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2007-09-10 15:45:49 -0400 (Mon, 10 Sep 2007) $
+ * $Rev: 3219 $
+ */
+(function(){if(typeof jQuery!="undefined")var _jQuery=jQuery;var jQuery=window.jQuery=function(a,c){if(window==this||!this.init)return new jQuery(a,c);return this.init(a,c);};if(typeof $!="undefined")var _$=$;window.$=jQuery;var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;jQuery.fn=jQuery.prototype={init:function(a,c){a=a||document;if(typeof a=="string"){var m=quickExpr.exec(a);if(m&&(m[1]||!c)){if(m[1])a=jQuery.clean([m[1]],c);else{var tmp=document.getElementById(m[3]);if(tmp)if(tmp.id!=m[3])return jQuery().find(a);else{this[0]=tmp;this.length=1;return this;}else
+a=[];}}else
+return new jQuery(c).find(a);}else if(jQuery.isFunction(a))return new jQuery(document)[jQuery.fn.ready?"ready":"load"](a);return this.setArray(a.constructor==Array&&a||(a.jquery||a.length&&a!=window&&!a.nodeType&&a[0]!=undefined&&a[0].nodeType)&&jQuery.makeArray(a)||[a]);},jquery:"1.2",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(a){var ret=jQuery(a);ret.prevObject=this;return ret;},setArray:function(a){this.length=0;Array.prototype.push.apply(this,a);return this;},each:function(fn,args){return jQuery.each(this,fn,args);},index:function(obj){var pos=-1;this.each(function(i){if(this==obj)pos=i;});return pos;},attr:function(key,value,type){var obj=key;if(key.constructor==String)if(value==undefined)return this.length&&jQuery[type||"attr"](this[0],key)||undefined;else{obj={};obj[key]=value;}return this.each(function(index){for(var prop in obj)jQuery.attr(type?this.style:this,prop,jQuery.prop(this,obj[prop],type,index,prop));});},css:function(key,value){return this.attr(key,value,"curCSS");},text:function(e){if(typeof e!="object"&&e!=null)return this.empty().append(document.createTextNode(e));var t="";jQuery.each(e||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)t+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return t;},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,1,function(a){this.appendChild(a);});},prepend:function(){return this.domManip(arguments,true,-1,function(a){this.insertBefore(a,this.firstChild);});},before:function(){return this.domManip(arguments,false,1,function(a){this.parentNode.insertBefore(a,this);});},after:function(){return this.domManip(arguments,false,-1,function(a){this.parentNode.insertBefore(a,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(t){var data=jQuery.map(this,function(a){return jQuery.find(t,a);});return this.pushStack(/[^+>] [^+>]/.test(t)||t.indexOf("..")>-1?jQuery.unique(data):data);},clone:function(events){var ret=this.map(function(){return this.outerHTML?jQuery(this.outerHTML)[0]:this.cloneNode(true);});if(events===true){var clone=ret.find("*").andSelf();this.find("*").andSelf().each(function(i){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(t){return this.pushStack(jQuery.isFunction(t)&&jQuery.grep(this,function(el,index){return t.apply(el,[index]);})||jQuery.multiFilter(t,this));},not:function(t){return this.pushStack(t.constructor==String&&jQuery.multiFilter(t,this,true)||jQuery.grep(this,function(a){return(t.constructor==Array||t.jquery)?jQuery.inArray(a,t)<0:a!=t;}));},add:function(t){return this.pushStack(jQuery.merge(this.get(),t.constructor==String?jQuery(t).get():t.length!=undefined&&(!t.nodeName||t.nodeName=="FORM")?t:[t]));},is:function(expr){return expr?jQuery.multiFilter(expr,this).length>0:false;},hasClass:function(expr){return this.is("."+expr);},val:function(val){if(val==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,a=[],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){var val=jQuery.browser.msie&&!option.attributes["value"].specified?option.text:option.value;if(one)return val;a.push(val);}}return a;}else
+return this[0].value.replace(/\r/g,"");}}else
+return this.each(function(){if(val.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,val)>=0||jQuery.inArray(this.name,val)>=0);else if(jQuery.nodeName(this,"select")){var tmp=val.constructor==Array?val:[val];jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,tmp)>=0||jQuery.inArray(this.text,tmp)>=0);});if(!tmp.length)this.selectedIndex=-1;}else
+this.value=val;});},html:function(val){return val==undefined?(this.length?this[0].innerHTML:null):this.empty().append(val);},replaceWith:function(val){return this.after(val).remove();},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(fn){return this.pushStack(jQuery.map(this,function(elem,i){return fn.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},domManip:function(args,table,dir,fn){var clone=this.length>1,a;return this.each(function(){if(!a){a=jQuery.clean(args,this.ownerDocument);if(dir<0)a.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(a[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(document.createElement("tbody"));jQuery.each(a,function(){if(jQuery.nodeName(this,"script")){if(this.src)jQuery.ajax({url:this.src,async:false,dataType:"script"});else
+jQuery.globalEval(this.text||this.textContent||this.innerHTML||"");}else
+fn.apply(obj,[clone?this.cloneNode(true):this]);});});}};jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},a=1,al=arguments.length,deep=false;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};}if(al==1){target=this;a=0;}var prop;for(;a<al;a++)if((prop=arguments[a])!=null)for(var i in prop){if(target==prop[i])continue;if(deep&&typeof prop[i]=='object'&&target[i])jQuery.extend(target[i],prop[i]);else if(prop[i]!=undefined)target[i]=prop[i];}return target;};var expando="jQuery"+(new Date()).getTime(),uuid=0,win={};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&&/function/i.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){if(window.execScript)window.execScript(data);else if(jQuery.browser.safari)window.setTimeout(data,0);else
+eval.call(window,data);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?win: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?win: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(obj,fn,args){if(args){if(obj.length==undefined)for(var i in obj)fn.apply(obj[i],args);else
+for(var i=0,ol=obj.length;i<ol;i++)if(fn.apply(obj[i],args)===false)break;}else{if(obj.length==undefined)for(var i in obj)fn.call(obj[i],i,obj[i]);else
+for(var i=0,ol=obj.length,val=obj[0];i<ol&&fn.call(val,i,val)!==false;val=obj[++i]){}}return obj;},prop:function(elem,value,type,index,prop){if(jQuery.isFunction(value))value=value.call(elem,[index]);var exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i;return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(prop)?value+"px":value;},className:{add:function(elem,c){jQuery.each((c||"").split(/\s+/),function(i,cur){if(!jQuery.className.has(elem.className,cur))elem.className+=(elem.className?" ":"")+cur;});},remove:function(elem,c){elem.className=c!=undefined?jQuery.grep(elem.className.split(/\s+/),function(cur){return!jQuery.className.has(c,cur);}).join(" "):"";},has:function(t,c){return jQuery.inArray(c,(t.className||t).toString().split(/\s+/))>-1;}},swap:function(e,o,f){for(var i in o){e.style["old"+i]=e.style[i];e.style[i]=o[i];}f.apply(e,[]);for(var i in o)e.style[i]=e.style["old"+i];},css:function(e,p){if(p=="height"||p=="width"){var old={},oHeight,oWidth,d=["Top","Bottom","Right","Left"];jQuery.each(d,function(){old["padding"+this]=0;old["border"+this+"Width"]=0;});jQuery.swap(e,old,function(){if(jQuery(e).is(':visible')){oHeight=e.offsetHeight;oWidth=e.offsetWidth;}else{e=jQuery(e.cloneNode(true)).find(":radio").removeAttr("checked").end().css({visibility:"hidden",position:"absolute",display:"block",right:"0",left:"0"}).appendTo(e.parentNode)[0];var parPos=jQuery.css(e.parentNode,"position")||"static";if(parPos=="static")e.parentNode.style.position="relative";oHeight=e.clientHeight;oWidth=e.clientWidth;if(parPos=="static")e.parentNode.style.position="static";e.parentNode.removeChild(e);}});return p=="height"?oHeight:oWidth;}return jQuery.curCSS(e,p);},curCSS:function(elem,prop,force){var ret,stack=[],swap=[];function color(a){if(!jQuery.browser.safari)return false;var ret=document.defaultView.getComputedStyle(a,null);return!ret||ret.getPropertyValue("color")=="";}if(prop=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(elem.style,"opacity");return ret==""?"1":ret;}if(prop.match(/float/i))prop=styleFloat;if(!force&&elem.style[prop])ret=elem.style[prop];else if(document.defaultView&&document.defaultView.getComputedStyle){if(prop.match(/float/i))prop="float";prop=prop.replace(/([A-Z])/g,"-$1").toLowerCase();var cur=document.defaultView.getComputedStyle(elem,null);if(cur&&!color(elem))ret=cur.getPropertyValue(prop);else{for(var a=elem;a&&color(a);a=a.parentNode)stack.unshift(a);for(a=0;a<stack.length;a++)if(color(stack[a])){swap[a]=stack[a].style.display;stack[a].style.display="block";}ret=prop=="display"&&swap[stack.length-1]!=null?"none":document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop)||"";for(a=0;a<swap.length;a++)if(swap[a]!=null)stack[a].style.display=swap[a];}if(prop=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var newProp=prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});ret=elem.currentStyle[prop]||elem.currentStyle[newProp];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var style=elem.style.left;var runtimeStyle=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;elem.style.left=ret||0;ret=elem.style.pixelLeft+"px";elem.style.left=style;elem.runtimeStyle.left=runtimeStyle;}}return ret;},clean:function(a,doc){var r=[];doc=doc||document;jQuery.each(a,function(i,arg){if(!arg)return;if(arg.constructor==Number)arg=arg.toString();if(typeof arg=="string"){arg=arg.replace(/(<(\w+)[^>]*?)\/>/g,function(m,all,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i)?m:all+"></"+tag+">";});var s=jQuery.trim(arg).toLowerCase(),div=doc.createElement("div"),tb=[];var wrap=!s.indexOf("<opt")&&[1,"<select>","</select>"]||!s.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||s.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!s.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!s.indexOf("<td")||!s.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!s.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+arg+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){if(!s.indexOf("<table")&&s.indexOf("<tbody")<0)tb=div.firstChild&&div.firstChild.childNodes;else if(wrap[1]=="<table>"&&s.indexOf("<tbody")<0)tb=div.childNodes;for(var n=tb.length-1;n>=0;--n)if(jQuery.nodeName(tb[n],"tbody")&&!tb[n].childNodes.length)tb[n].parentNode.removeChild(tb[n]);if(/^\s/.test(arg))div.insertBefore(doc.createTextNode(arg.match(/^\s*/)[0]),div.firstChild);}arg=jQuery.makeArray(div.childNodes);}if(0===arg.length&&(!jQuery.nodeName(arg,"form")&&!jQuery.nodeName(arg,"select")))return;if(arg[0]==undefined||jQuery.nodeName(arg,"form")||arg.options)r.push(arg);else
+r=jQuery.merge(r,arg);});return r;},attr:function(elem,name,value){var fix=jQuery.isXMLDoc(elem)?{}:jQuery.props;if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(fix[name]){if(value!=undefined)elem[fix[name]]=value;return elem[fix[name]];}else if(jQuery.browser.msie&&name=="style")return jQuery.attr(elem.style,"cssText",value);else if(value==undefined&&jQuery.browser.msie&&jQuery.nodeName(elem,"form")&&(name=="action"||name=="method"))return elem.getAttributeNode(name).nodeValue;else if(elem.tagName){if(value!=undefined){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem.setAttribute(name,value);}if(jQuery.browser.msie&&/href|src/.test(name)&&!jQuery.isXMLDoc(elem))return elem.getAttribute(name,2);return elem.getAttribute(name);}else{if(name=="opacity"&&jQuery.browser.msie){if(value!=undefined){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseFloat(value).toString()=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100).toString():"";}name=name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});if(value!=undefined)elem[name]=value;return elem[name];}},trim:function(t){return(t||"").replace(/^\s+|\s+$/g,"");},makeArray:function(a){var r=[];if(typeof a!="array")for(var i=0,al=a.length;i<al;i++)r.push(a[i]);else
+r=a.slice(0);return r;},inArray:function(b,a){for(var i=0,al=a.length;i<al;i++)if(a[i]==b)return i;return-1;},merge:function(first,second){if(jQuery.browser.msie){for(var i=0;second[i];i++)if(second[i].nodeType!=8)first.push(second[i]);}else
+for(var i=0;second[i];i++)first.push(second[i]);return first;},unique:function(first){var r=[],done={};try{for(var i=0,fl=first.length;i<fl;i++){var id=jQuery.data(first[i]);if(!done[id]){done[id]=true;r.push(first[i]);}}}catch(e){r=first;}return r;},grep:function(elems,fn,inv){if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+"}");var result=[];for(var i=0,el=elems.length;i<el;i++)if(!inv&&fn(elems[i],i)||inv&&!fn(elems[i],i))result.push(elems[i]);return result;},map:function(elems,fn){if(typeof fn=="string")fn=eval("false||function(a){return "+fn+"}");var result=[];for(var i=0,el=elems.length;i<el;i++){var val=fn(elems[i],i);if(val!==null&&val!=undefined){if(val.constructor!=Array)val=[val];result=result.concat(val);}}return result;}});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",styleFloat:jQuery.browser.msie?"styleFloat":"cssFloat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,innerHTML:"innerHTML",className:"className",value:"value",disabled:"disabled",checked:"checked",readonly:"readOnly",selected:"selected",maxlength:"maxLength"}});jQuery.each({parent:"a.parentNode",parents:"jQuery.dir(a,'parentNode')",next:"jQuery.nth(a,2,'nextSibling')",prev:"jQuery.nth(a,2,'previousSibling')",nextAll:"jQuery.dir(a,'nextSibling')",prevAll:"jQuery.dir(a,'previousSibling')",siblings:"jQuery.sibling(a.parentNode.firstChild,a)",children:"jQuery.sibling(a.firstChild)",contents:"jQuery.nodeName(a,'iframe')?a.contentDocument||a.contentWindow.document:jQuery.makeArray(a.childNodes)"},function(i,n){jQuery.fn[i]=function(a){var ret=jQuery.map(this,n);if(a&&typeof a=="string")ret=jQuery.multiFilter(a,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(i,n){jQuery.fn[i]=function(){var a=arguments;return this.each(function(){for(var j=0,al=a.length;j<al;j++)jQuery(a[j])[n](this);});};});jQuery.each({removeAttr:function(key){jQuery.attr(this,key,"");this.removeAttribute(key);},addClass:function(c){jQuery.className.add(this,c);},removeClass:function(c){jQuery.className.remove(this,c);},toggleClass:function(c){jQuery.className[jQuery.className.has(this,c)?"remove":"add"](this,c);},remove:function(a){if(!a||jQuery.filter(a,[this]).r.length){jQuery.removeData(this);this.parentNode.removeChild(this);}},empty:function(){jQuery("*",this).each(function(){jQuery.removeData(this);});while(this.firstChild)this.removeChild(this.firstChild);}},function(i,n){jQuery.fn[i]=function(){return this.each(n,arguments);};});jQuery.each(["Height","Width"],function(i,name){var n=name.toLowerCase();jQuery.fn[n]=function(h){return this[0]==window?jQuery.browser.safari&&self["inner"+name]||jQuery.boxModel&&Math.max(document.documentElement["client"+name],document.body["client"+name])||document.body["client"+name]:this[0]==document?Math.max(document.body["scroll"+name],document.body["offset"+name]):h==undefined?(this.length?jQuery.css(this[0],n):null):this.css(n,h.constructor==String?h:h+"px");};});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:{"":"m[2]=='*'||jQuery.nodeName(a,m[2])","#":"a.getAttribute('id')==m[2]",":":{lt:"i<m[3]-0",gt:"i>m[3]-0",nth:"m[3]-0==i",eq:"m[3]-0==i",first:"i==0",last:"i==r.length-1",even:"i%2==0",odd:"i%2","first-child":"a.parentNode.getElementsByTagName('*')[0]==a","last-child":"jQuery.nth(a.parentNode.lastChild,1,'previousSibling')==a","only-child":"!jQuery.nth(a.parentNode.lastChild,2,'previousSibling')",parent:"a.firstChild",empty:"!a.firstChild",contains:"(a.textContent||a.innerText||'').indexOf(m[3])>=0",visible:'"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden"',hidden:'"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden"',enabled:"!a.disabled",disabled:"a.disabled",checked:"a.checked",selected:"a.selected||jQuery.attr(a,'selected')",text:"'text'==a.type",radio:"'radio'==a.type",checkbox:"'checkbox'==a.type",file:"'file'==a.type",password:"'password'==a.type",submit:"'submit'==a.type",image:"'image'==a.type",reset:"'reset'==a.type",button:'"button"==a.type||jQuery.nodeName(a,"button")',input:"/input|select|textarea|button/i.test(a.nodeName)",has:"jQuery.find(m[3],a).length",header:"/h\\d/i.test(a.nodeName)",animated:"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)context=null;context=context||document;var ret=[context],done=[],last;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false;var re=quickChild;var m=re.exec(t);if(m){var 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.toUpperCase()))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 nodeName=m[2],merge={};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.toUpperCase()){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=jQuery.filter(m[3],r,true).r;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])&&"n+"+m[3]||m[3]),first=(test[1]||1)-0,last=test[2]-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==1){if(last==0||node.nodeIndex==last)add=true;}else if((node.nodeIndex+last)%first==0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var f=jQuery.expr[m[1]];if(typeof f!="string")f=jQuery.expr[m[1]][m[2]];f=eval("false||function(a,i){return "+f+"}");r=jQuery.grep(r,f,not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[];var 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&&(!elem||n!=elem))r.push(n);}return r;}});jQuery.event={add:function(element,type,handler,data){if(jQuery.browser.msie&&element.setInterval!=undefined)element=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=function(){return fn.apply(this,arguments);};handler.data=data;handler.guid=fn.guid;}var parts=type.split(".");type=parts[0];handler.type=parts[1];var events=jQuery.data(element,"events")||jQuery.data(element,"events",{});var handle=jQuery.data(element,"handle",function(){var val;if(typeof jQuery=="undefined"||jQuery.event.triggered)return val;val=jQuery.event.handle.apply(element,arguments);return val;});var handlers=events[type];if(!handlers){handlers=events[type]={};if(element.addEventListener)element.addEventListener(type,handle,false);else
+element.attachEvent("on"+type,handle);}handlers[handler.guid]=handler;this.global[type]=true;},guid:1,global:{},remove:function(element,type,handler){var events=jQuery.data(element,"events"),ret,index;if(typeof type=="string"){var parts=type.split(".");type=parts[0];}if(events){if(type&&type.type){handler=type.handler;type=type.type;}if(!type){for(type in events)this.remove(element,type);}else 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(element.removeEventListener)element.removeEventListener(type,jQuery.data(element,"handle"),false);else
+element.detachEvent("on"+type,jQuery.data(element,"handle"));ret=null;delete events[type];}}for(ret in events)break;if(!ret){jQuery.removeData(element,"events");jQuery.removeData(element,"handle");}}},trigger:function(type,data,element,donative,extra){data=jQuery.makeArray(data||[]);if(!element){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{var val,ret,fn=jQuery.isFunction(element[type]||null),evt=!data[0]||!data[0].preventDefault;if(evt)data.unshift(this.fix({type:type,target:element}));if(jQuery.isFunction(jQuery.data(element,"handle")))val=jQuery.data(element,"handle").apply(element,data);if(!fn&&element["on"+type]&&element["on"+type].apply(element,data)===false)val=false;if(evt)data.shift();if(extra&&extra.apply(element,data)===false)val=false;if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(element,'a')&&type=="click")){this.triggered=true;element[type]();}this.triggered=false;}return val;},handle:function(event){var val;event=jQuery.event.fix(event||window.event||{});var parts=event.type.split(".");event.type=parts[0];var c=jQuery.data(this,"events")&&jQuery.data(this,"events")[event.type],args=Array.prototype.slice.call(arguments,1);args.unshift(event);for(var j in c){args[0].handler=c[j];args[0].data=c[j].data;if(!parts[1]||c[j].type==parts[1]){var tmp=c[j].apply(this,args);if(val!==false)val=tmp;if(tmp===false){event.preventDefault();event.stopPropagation();}}}if(jQuery.browser.msie)event.target=event.preventDefault=event.stopPropagation=event.handler=event.data=null;return val;},fix:function(event){var originalEvent=event;event=jQuery.extend({},originalEvent);event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};if(!event.target&&event.srcElement)event.target=event.srcElement;if(jQuery.browser.safari&&event.target.nodeType==3)event.target=originalEvent.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 e=document.documentElement,b=document.body;event.pageX=event.clientX+(e&&e.scrollLeft||b.scrollLeft||0);event.pageY=event.clientY+(e&&e.scrollTop||b.scrollTop||0);}if(!event.which&&(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;}};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){return this.each(function(){jQuery.event.add(this,type,function(event){jQuery(this).unbind(event);return(fn||data).apply(this,arguments);},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){if(this[0])return jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(){var a=arguments;return this.click(function(e){this.lastToggle=0==this.lastToggle?1:0;e.preventDefault();return a[this.lastToggle].apply(this,[e])||false;});},hover:function(f,g){function handleHover(e){var p=e.relatedTarget;while(p&&p!=this)try{p=p.parentNode;}catch(e){p=this;};if(p==this)return false;return(e.type=="mouseover"?f:g).apply(this,[e]);}return this.mouseover(handleHover).mouseout(handleHover);},ready:function(f){bindReady();if(jQuery.isReady)f.apply(document,[jQuery]);else
+jQuery.readyList.push(function(){return f.apply(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.apply(document);});jQuery.readyList=null;}if(jQuery.browser.mozilla||jQuery.browser.opera)document.removeEventListener("DOMContentLoaded",jQuery.ready,false);if(!window.frames.length)jQuery(window).load(function(){jQuery("#__ie_init").remove();});}}});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,o){jQuery.fn[o]=function(f){return f?this.bind(o,f):this.trigger(o);};});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(jQuery.browser.mozilla||jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);else if(jQuery.browser.msie){document.write("<scr"+"ipt id=__ie_init defer=true "+"src=//:><\/script>");var script=document.getElementById("__ie_init");if(script)script.onreadystatechange=function(){if(this.readyState!="complete")return;jQuery.ready();};script=null;}else if(jQuery.browser.safari)jQuery.safariTimer=setInterval(function(){if(document.readyState=="loaded"||document.readyState=="complete"){clearInterval(jQuery.safariTimer);jQuery.safariTimer=null;jQuery.ready();}},10);jQuery.event.add(window,"load",jQuery.ready);}jQuery.fn.extend({load:function(url,params,callback){if(jQuery.isFunction(url))return this.bind("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,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);setTimeout(function(){self.each(callback,[res.responseText,status,res]);},13);}});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(i,val){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=(new Date).getTime();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:{global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null},lastModified:{},ajax:function(s){var jsonp,jsre=/=(\?|%3F)/g,status,data;s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);var q=s.url.indexOf("?");if(q>-1){s.data=(s.data?s.data+"&":"")+s.url.slice(q+1);s.url=s.url.slice(0,q);}if(s.dataType=="jsonp"){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)){jsonp="jsonp"+jsc++;s.data=s.data.replace(jsre,"="+jsonp);s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&s.type.toLowerCase()=="get")s.data=(s.data?s.data+"&":"")+"_="+(new Date()).getTime();if(s.data&&s.type.toLowerCase()=="get"){s.url+="?"+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");if(!s.url.indexOf("http")&&s.dataType=="script"){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(!jsonp&&(s.success||s.complete)){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;}var requestDone=false;var xml=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();xml.open(s.type,s.url,s.async);if(s.data)xml.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xml.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xml.setRequestHeader("X-Requested-With","XMLHttpRequest");if(s.beforeSend)s.beforeSend(xml);if(s.global)jQuery.event.trigger("ajaxSend",[xml,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xml&&(xml.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xml)&&"error"||s.ifModified&&jQuery.httpNotModified(xml,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xml,s.dataType);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xml.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
+jQuery.handleError(s,xml,status);complete();if(s.async)xml=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xml){xml.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xml.send(s.data);}catch(e){jQuery.handleError(s,xml,null,e);}if(!s.async)onreadystatechange();return xml;function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xml,s]);}function complete(){if(s.complete)s.complete(xml,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xml,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}},handleError:function(s,xml,status,e){if(s.error)s.error(xml,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xml,s,e]);},active:0,httpSuccess:function(r){try{return!r.status&&location.protocol=="file:"||(r.status>=200&&r.status<300)||r.status==304||jQuery.browser.safari&&r.status==undefined;}catch(e){}return false;},httpNotModified:function(xml,url){try{var xmlRes=xml.getResponseHeader("Last-Modified");return xml.status==304||xmlRes==jQuery.lastModified[url]||jQuery.browser.safari&&xml.status==undefined;}catch(e){}return false;},httpData:function(r,type){var ct=r.getResponseHeader("content-type");var xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0;var data=xml?r.responseXML:r.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";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(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?this.oldblock:"";if(jQuery.css(this,"display")=="none")this.style.display="block";}).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");if(this.oldblock=="none")this.oldblock="block";this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle(fn,fn2):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 opt=jQuery.speed(speed,easing,callback);return this[opt.queue===false?"each":"queue"](function(){opt=jQuery.extend({},opt);var hidden=jQuery(this).is(":hidden"),self=this;for(var p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return jQuery.isFunction(opt.complete)&&opt.complete.apply(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){end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=end+unit;start=(end/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(!fn){fn=type;type="fx";}if(!arguments.length)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.apply(this);}});},stop:function(){var timers=jQuery.timers;return this.each(function(){for(var i=0;i<timers.length;i++)if(timers[i].elem==this)timers.splice(i--,1);}).dequeue();}});var queue=function(elem,type,array){if(!elem)return;var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",array?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].apply(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:{slow:600,fast:200}[opt.duration])||400;opt.old=opt.complete;opt.complete=function(){jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.apply(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:[],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.apply(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.curCSS(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.css(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=(new Date()).getTime();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(){return self.step();}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timers.length==1){var timer=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(timer);},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(){var t=(new Date()).getTime();if(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&&jQuery.isFunction(this.options.complete))this.options.complete.apply(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.fx.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 absolute=jQuery.css(elem,"position")=="absolute",parent=elem.parentNode,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&!absolute&&parseInt(version)<522;if(elem.getBoundingClientRect){box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));if(msie){var border=jQuery("html").css("borderWidth");border=(border=="medium"||jQuery.boxModel&&parseInt(version)>=7)&&2||border;add(-border,-border);}}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&/^t[d|h]$/i.test(parent.tagName)||!safari2)border(offsetParent);if(safari2&&!absolute&&jQuery.css(offsetParent,"position")=="absolute")absolute=true;offsetParent=offsetParent.offsetParent;}while(parent.tagName&&/^body|html$/i.test(parent.tagName)){if(/^inline|table-row.*$/i.test(jQuery.css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&jQuery.css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if(safari&&absolute)add(-doc.body.offsetLeft,-doc.body.offsetTop);}results={top:top,left:left};}return results;function border(elem){add(jQuery.css(elem,"borderLeftWidth"),jQuery.css(elem,"borderTopWidth"));}function add(l,t){left+=parseInt(l)||0;top+=parseInt(t)||0;}};})(); \ No newline at end of file
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.3_min.js b/devtools/client/inspector/markup/test/lib_jquery_1.3_min.js
new file mode 100644
index 000000000..378f94376
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.3_min.js
@@ -0,0 +1,19 @@
+/*
+ * jQuery JavaScript Library v1.3
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-01-13 12:50:31 -0500 (Tue, 13 Jan 2009)
+ * Revision: 6104
+ */
+(function(){var l=this,g,x=l.jQuery,o=l.$,n=l.jQuery=l.$=function(D,E){return new n.fn.init(D,E)},C=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;n.fn=n.prototype={init:function(D,G){D=D||document;if(D.nodeType){this[0]=D;this.length=1;this.context=D;return this}if(typeof D==="string"){var F=C.exec(D);if(F&&(F[1]||!G)){if(F[1]){D=n.clean([F[1]],G)}else{var H=document.getElementById(F[3]);if(H){if(H.id!=F[3]){return n().find(D)}var E=n(H);E.context=document;E.selector=D;return E}D=[]}}else{return n(G).find(D)}}else{if(n.isFunction(D)){return n(document).ready(D)}}if(D.selector&&D.context){this.selector=D.selector;this.context=D.context}return this.setArray(n.makeArray(D))},selector:"",jquery:"1.3",size:function(){return this.length},get:function(D){return D===g?n.makeArray(this):this[D]},pushStack:function(E,G,D){var F=n(E);F.prevObject=this;F.context=this.context;if(G==="find"){F.selector=this.selector+(this.selector?" ":"")+D}else{if(G){F.selector=this.selector+"."+G+"("+D+")"}}return F},setArray:function(D){this.length=0;Array.prototype.push.apply(this,D);return this},each:function(E,D){return n.each(this,E,D)},index:function(D){return n.inArray(D&&D.jquery?D[0]:D,this)},attr:function(E,G,F){var D=E;if(typeof E==="string"){if(G===g){return this[0]&&n[F||"attr"](this[0],E)}else{D={};D[E]=G}}return this.each(function(H){for(E in D){n.attr(F?this.style:this,E,n.prop(this,D[E],F,H,E))}})},css:function(D,E){if((D=="width"||D=="height")&&parseFloat(E)<0){E=g}return this.attr(D,E,"curCSS")},text:function(E){if(typeof E!=="object"&&E!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(E))}var D="";n.each(E||this,function(){n.each(this.childNodes,function(){if(this.nodeType!=8){D+=this.nodeType!=1?this.nodeValue:n.fn.text([this])}})});return D},wrapAll:function(D){if(this[0]){var E=n(D,this[0].ownerDocument).clone();if(this[0].parentNode){E.insertBefore(this[0])}E.map(function(){var F=this;while(F.firstChild){F=F.firstChild}return F}).append(this)}return this},wrapInner:function(D){return this.each(function(){n(this).contents().wrapAll(D)})},wrap:function(D){return this.each(function(){n(this).wrapAll(D)})},append:function(){return this.domManip(arguments,true,function(D){if(this.nodeType==1){this.appendChild(D)}})},prepend:function(){return this.domManip(arguments,true,function(D){if(this.nodeType==1){this.insertBefore(D,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(D){this.parentNode.insertBefore(D,this)})},after:function(){return this.domManip(arguments,false,function(D){this.parentNode.insertBefore(D,this.nextSibling)})},end:function(){return this.prevObject||n([])},push:[].push,find:function(D){if(this.length===1&&!/,/.test(D)){var F=this.pushStack([],"find",D);F.length=0;n.find(D,this[0],F);return F}else{var E=n.map(this,function(G){return n.find(D,G)});return this.pushStack(/[^+>] [^+>]/.test(D)?n.unique(E):E,"find",D)}},clone:function(E){var D=this.map(function(){if(!n.support.noCloneEvent&&!n.isXMLDoc(this)){var H=this.cloneNode(true),G=document.createElement("div");G.appendChild(H);return n.clean([G.innerHTML])[0]}else{return this.cloneNode(true)}});var F=D.find("*").andSelf().each(function(){if(this[h]!==g){this[h]=null}});if(E===true){this.find("*").andSelf().each(function(H){if(this.nodeType==3){return}var G=n.data(this,"events");for(var J in G){for(var I in G[J]){n.event.add(F[H],J,G[J][I],G[J][I].data)}}})}return D},filter:function(D){return this.pushStack(n.isFunction(D)&&n.grep(this,function(F,E){return D.call(F,E)})||n.multiFilter(D,n.grep(this,function(E){return E.nodeType===1})),"filter",D)},closest:function(D){var E=n.expr.match.POS.test(D)?n(D):null;return this.map(function(){var F=this;while(F&&F.ownerDocument){if(E?E.index(F)>-1:n(F).is(D)){return F}F=F.parentNode}})},not:function(D){if(typeof D==="string"){if(f.test(D)){return this.pushStack(n.multiFilter(D,this,true),"not",D)}else{D=n.multiFilter(D,this)}}var E=D.length&&D[D.length-1]!==g&&!D.nodeType;return this.filter(function(){return E?n.inArray(this,D)<0:this!=D})},add:function(D){return this.pushStack(n.unique(n.merge(this.get(),typeof D==="string"?n(D):n.makeArray(D))))},is:function(D){return !!D&&n.multiFilter(D,this).length>0},hasClass:function(D){return !!D&&this.is("."+D)},val:function(J){if(J===g){var D=this[0];if(D){if(n.nodeName(D,"option")){return(D.attributes.value||{}).specified?D.value:D.text}if(n.nodeName(D,"select")){var H=D.selectedIndex,K=[],L=D.options,G=D.type=="select-one";if(H<0){return null}for(var E=G?H:0,I=G?H+1:L.length;E<I;E++){var F=L[E];if(F.selected){J=n(F).val();if(G){return J}K.push(J)}}return K}return(D.value||"").replace(/\r/g,"")}return g}if(typeof J==="number"){J+=""}return this.each(function(){if(this.nodeType!=1){return}if(n.isArray(J)&&/radio|checkbox/.test(this.type)){this.checked=(n.inArray(this.value,J)>=0||n.inArray(this.name,J)>=0)}else{if(n.nodeName(this,"select")){var M=n.makeArray(J);n("option",this).each(function(){this.selected=(n.inArray(this.value,M)>=0||n.inArray(this.text,M)>=0)});if(!M.length){this.selectedIndex=-1}}else{this.value=J}}})},html:function(D){return D===g?(this[0]?this[0].innerHTML:null):this.empty().append(D)},replaceWith:function(D){return this.after(D).remove()},eq:function(D){return this.slice(D,+D+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(D){return this.pushStack(n.map(this,function(F,E){return D.call(F,E,F)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=n.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild,D=this.length>1?I.cloneNode(true):I;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),G>0?D.cloneNode(true):I)}}if(F){n.each(F,y)}}return this;function K(N,O){return M&&n.nodeName(N,"table")&&n.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};n.fn.init.prototype=n.fn;function y(D,E){if(E.src){n.ajax({url:E.src,async:false,dataType:"script"})}else{n.globalEval(E.text||E.textContent||E.innerHTML||"")}if(E.parentNode){E.parentNode.removeChild(E)}}function e(){return +new Date}n.extend=n.fn.extend=function(){var I=arguments[0]||{},G=1,H=arguments.length,D=false,F;if(typeof I==="boolean"){D=I;I=arguments[1]||{};G=2}if(typeof I!=="object"&&!n.isFunction(I)){I={}}if(H==G){I=this;--G}for(;G<H;G++){if((F=arguments[G])!=null){for(var E in F){var J=I[E],K=F[E];if(I===K){continue}if(D&&K&&typeof K==="object"&&!K.nodeType){I[E]=n.extend(D,J||(K.length!=null?[]:{}),K)}else{if(K!==g){I[E]=K}}}}}return I};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,p=document.defaultView||{},r=Object.prototype.toString;n.extend({noConflict:function(D){l.$=o;if(D){l.jQuery=x}return n},isFunction:function(D){return r.call(D)==="[object Function]"},isArray:function(D){return r.call(D)==="[object Array]"},isXMLDoc:function(D){return D.documentElement&&!D.body||D.tagName&&D.ownerDocument&&!D.ownerDocument.body},globalEval:function(F){F=n.trim(F);if(F){var E=document.getElementsByTagName("head")[0]||document.documentElement,D=document.createElement("script");D.type="text/javascript";if(n.support.scriptEval){D.appendChild(document.createTextNode(F))}else{D.text=F}E.insertBefore(D,E.firstChild);E.removeChild(D)}},nodeName:function(E,D){return E.nodeName&&E.nodeName.toUpperCase()==D.toUpperCase()},each:function(F,J,E){var D,G=0,H=F.length;if(E){if(H===g){for(D in F){if(J.apply(F[D],E)===false){break}}}else{for(;G<H;){if(J.apply(F[G++],E)===false){break}}}}else{if(H===g){for(D in F){if(J.call(F[D],D,F[D])===false){break}}}else{for(var I=F[0];G<H&&J.call(I,G,I)!==false;I=F[++G]){}}}return F},prop:function(G,H,F,E,D){if(n.isFunction(H)){H=H.call(G,E)}return typeof H==="number"&&F=="curCSS"&&!b.test(D)?H+"px":H},className:{add:function(D,E){n.each((E||"").split(/\s+/),function(F,G){if(D.nodeType==1&&!n.className.has(D.className,G)){D.className+=(D.className?" ":"")+G}})},remove:function(D,E){if(D.nodeType==1){D.className=E!==g?n.grep(D.className.split(/\s+/),function(F){return !n.className.has(E,F)}).join(" "):""}},has:function(E,D){return n.inArray(D,(E.className||E).toString().split(/\s+/))>-1}},swap:function(G,F,H){var D={};for(var E in F){D[E]=G.style[E];G.style[E]=F[E]}H.call(G);for(var E in F){G.style[E]=D[E]}},css:function(F,D,H){if(D=="width"||D=="height"){var J,E={position:"absolute",visibility:"hidden",display:"block"},I=D=="width"?["Left","Right"]:["Top","Bottom"];function G(){J=D=="width"?F.offsetWidth:F.offsetHeight;var L=0,K=0;n.each(I,function(){L+=parseFloat(n.curCSS(F,"padding"+this,true))||0;K+=parseFloat(n.curCSS(F,"border"+this+"Width",true))||0});J-=Math.round(L+K)}if(n(F).is(":visible")){G()}else{n.swap(F,E,G)}return Math.max(0,J)}return n.curCSS(F,D,H)},curCSS:function(H,E,F){var K,D=H.style;if(E=="opacity"&&!n.support.opacity){K=n.attr(D,"opacity");return K==""?"1":K}if(E.match(/float/i)){E=v}if(!F&&D&&D[E]){K=D[E]}else{if(p.getComputedStyle){if(E.match(/float/i)){E="float"}E=E.replace(/([A-Z])/g,"-$1").toLowerCase();var L=p.getComputedStyle(H,null);if(L){K=L.getPropertyValue(E)}if(E=="opacity"&&K==""){K="1"}}else{if(H.currentStyle){var I=E.replace(/\-(\w)/g,function(M,N){return N.toUpperCase()});K=H.currentStyle[E]||H.currentStyle[I];if(!/^\d+(px)?$/i.test(K)&&/^\d/.test(K)){var G=D.left,J=H.runtimeStyle.left;H.runtimeStyle.left=H.currentStyle.left;D.left=K||0;K=D.pixelLeft+"px";D.left=G;H.runtimeStyle.left=J}}}}return K},clean:function(E,J,H){J=J||document;if(typeof J.createElement==="undefined"){J=J.ownerDocument||J[0]&&J[0].ownerDocument||document}if(!H&&E.length===1&&typeof E[0]==="string"){var G=/^<(\w+)\s*\/?>$/.exec(E[0]);if(G){return[J.createElement(G[1])]}}var F=[],D=[],K=J.createElement("div");n.each(E,function(O,Q){if(typeof Q==="number"){Q+=""}if(!Q){return}if(typeof Q==="string"){Q=Q.replace(/(<(\w+)[^>]*?)\/>/g,function(S,T,R){return R.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?S:T+"></"+R+">"});var N=n.trim(Q).toLowerCase();var P=!N.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!N.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||N.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!N.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!N.indexOf("<td")||!N.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!N.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!n.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];K.innerHTML=P[1]+Q+P[2];while(P[0]--){K=K.lastChild}if(!n.support.tbody){var M=!N.indexOf("<table")&&N.indexOf("<tbody")<0?K.firstChild&&K.firstChild.childNodes:P[1]=="<table>"&&N.indexOf("<tbody")<0?K.childNodes:[];for(var L=M.length-1;L>=0;--L){if(n.nodeName(M[L],"tbody")&&!M[L].childNodes.length){M[L].parentNode.removeChild(M[L])}}}if(!n.support.leadingWhitespace&&/^\s/.test(Q)){K.insertBefore(J.createTextNode(Q.match(/^\s*/)[0]),K.firstChild)}Q=n.makeArray(K.childNodes)}if(Q.nodeType){F.push(Q)}else{F=n.merge(F,Q)}});if(H){for(var I=0;F[I];I++){if(n.nodeName(F[I],"script")&&(!F[I].type||F[I].type.toLowerCase()==="text/javascript")){D.push(F[I].parentNode?F[I].parentNode.removeChild(F[I]):F[I])}else{if(F[I].nodeType===1){F.splice.apply(F,[I+1,0].concat(n.makeArray(F[I].getElementsByTagName("script"))))}H.appendChild(F[I])}}return D}return F},attr:function(I,F,J){if(!I||I.nodeType==3||I.nodeType==8){return g}var G=!n.isXMLDoc(I),K=J!==g;F=G&&n.props[F]||F;if(I.tagName){var E=/href|src|style/.test(F);if(F=="selected"&&I.parentNode){I.parentNode.selectedIndex}if(F in I&&G&&!E){if(K){if(F=="type"&&n.nodeName(I,"input")&&I.parentNode){throw"type property can't be changed"}I[F]=J}if(n.nodeName(I,"form")&&I.getAttributeNode(F)){return I.getAttributeNode(F).nodeValue}if(F=="tabIndex"){var H=I.getAttributeNode("tabIndex");return H&&H.specified?H.value:I.nodeName.match(/^(a|area|button|input|object|select|textarea)$/i)?0:g}return I[F]}if(!n.support.style&&G&&F=="style"){return n.attr(I.style,"cssText",J)}if(K){I.setAttribute(F,""+J)}var D=!n.support.hrefNormalized&&G&&E?I.getAttribute(F,2):I.getAttribute(F);return D===null?g:D}if(!n.support.opacity&&F=="opacity"){if(K){I.zoom=1;I.filter=(I.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(J)+""=="NaN"?"":"alpha(opacity="+J*100+")")}return I.filter&&I.filter.indexOf("opacity=")>=0?(parseFloat(I.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}F=F.replace(/-([a-z])/ig,function(L,M){return M.toUpperCase()});if(K){I[F]=J}return I[F]},trim:function(D){return(D||"").replace(/^\s+|\s+$/g,"")},makeArray:function(F){var D=[];if(F!=null){var E=F.length;if(E==null||typeof F==="string"||n.isFunction(F)||F.setInterval){D[0]=F}else{while(E){D[--E]=F[E]}}}return D},inArray:function(F,G){for(var D=0,E=G.length;D<E;D++){if(G[D]===F){return D}}return -1},merge:function(G,D){var E=0,F,H=G.length;if(!n.support.getAll){while((F=D[E++])!=null){if(F.nodeType!=8){G[H++]=F}}}else{while((F=D[E++])!=null){G[H++]=F}}return G},unique:function(J){var E=[],D={};try{for(var F=0,G=J.length;F<G;F++){var I=n.data(J[F]);if(!D[I]){D[I]=true;E.push(J[F])}}}catch(H){E=J}return E},grep:function(E,I,D){var F=[];for(var G=0,H=E.length;G<H;G++){if(!D!=!I(E[G],G)){F.push(E[G])}}return F},map:function(D,I){var E=[];for(var F=0,G=D.length;F<G;F++){var H=I(D[F],F);if(H!=null){E[E.length]=H}}return E.concat.apply([],E)}});var B=navigator.userAgent.toLowerCase();n.browser={version:(B.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(B),opera:/opera/.test(B),msie:/msie/.test(B)&&!/opera/.test(B),mozilla:/mozilla/.test(B)&&!/(compatible|webkit)/.test(B)};n.each({parent:function(D){return D.parentNode},parents:function(D){return n.dir(D,"parentNode")},next:function(D){return n.nth(D,2,"nextSibling")},prev:function(D){return n.nth(D,2,"previousSibling")},nextAll:function(D){return n.dir(D,"nextSibling")},prevAll:function(D){return n.dir(D,"previousSibling")},siblings:function(D){return n.sibling(D.parentNode.firstChild,D)},children:function(D){return n.sibling(D.firstChild)},contents:function(D){return n.nodeName(D,"iframe")?D.contentDocument||D.contentWindow.document:n.makeArray(D.childNodes)}},function(D,E){n.fn[D]=function(F){var G=n.map(this,E);if(F&&typeof F=="string"){G=n.multiFilter(F,G)}return this.pushStack(n.unique(G),D,F)}});n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(D,E){n.fn[D]=function(){var F=arguments;return this.each(function(){for(var G=0,H=F.length;G<H;G++){n(F[G])[E](this)}})}});n.each({removeAttr:function(D){n.attr(this,D,"");if(this.nodeType==1){this.removeAttribute(D)}},addClass:function(D){n.className.add(this,D)},removeClass:function(D){n.className.remove(this,D)},toggleClass:function(E,D){if(typeof D!=="boolean"){D=!n.className.has(this,E)}n.className[D?"add":"remove"](this,E)},remove:function(D){if(!D||n.filter(D,[this]).length){n("*",this).add([this]).each(function(){n.event.remove(this);n.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){n(">*",this).remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(D,E){n.fn[D]=function(){return this.each(E,arguments)}});function j(D,E){return D[0]&&parseInt(n.curCSS(D[0],E,true),10)||0}var h="jQuery"+e(),u=0,z={};n.extend({cache:{},data:function(E,D,F){E=E==l?z:E;var G=E[h];if(!G){G=E[h]=++u}if(D&&!n.cache[G]){n.cache[G]={}}if(F!==g){n.cache[G][D]=F}return D?n.cache[G][D]:G},removeData:function(E,D){E=E==l?z:E;var G=E[h];if(D){if(n.cache[G]){delete n.cache[G][D];D="";for(D in n.cache[G]){break}if(!D){n.removeData(E)}}}else{try{delete E[h]}catch(F){if(E.removeAttribute){E.removeAttribute(h)}}delete n.cache[G]}},queue:function(E,D,G){if(E){D=(D||"fx")+"queue";var F=n.data(E,D);if(!F||n.isArray(G)){F=n.data(E,D,n.makeArray(G))}else{if(G){F.push(G)}}}return F},dequeue:function(G,F){var D=n.queue(G,F),E=D.shift();if(!F||F==="fx"){E=D[0]}if(E!==g){E.call(G)}}});n.fn.extend({data:function(D,F){var G=D.split(".");G[1]=G[1]?"."+G[1]:"";if(F===g){var E=this.triggerHandler("getData"+G[1]+"!",[G[0]]);if(E===g&&this.length){E=n.data(this[0],D)}return E===g&&G[1]?this.data(G[0]):E}else{return this.trigger("setData"+G[1]+"!",[G[0],F]).each(function(){n.data(this,D,F)})}},removeData:function(D){return this.each(function(){n.removeData(this,D)})},queue:function(D,E){if(typeof D!=="string"){E=D;D="fx"}if(E===g){return n.queue(this[0],D)}return this.each(function(){var F=n.queue(this,D,E);if(D=="fx"&&F.length==1){F[0].call(this)}})},dequeue:function(D){return this.each(function(){n.dequeue(this,D)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.1
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){var N=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,I=0,F=Object.prototype.toString;var E=function(ae,S,aa,V){aa=aa||[];S=S||document;if(S.nodeType!==1&&S.nodeType!==9){return[]}if(!ae||typeof ae!=="string"){return aa}var ab=[],ac,Y,ah,ag,Z,R,Q=true;N.lastIndex=0;while((ac=N.exec(ae))!==null){ab.push(ac[1]);if(ac[2]){R=RegExp.rightContext;break}}if(ab.length>1&&G.match.POS.exec(ae)){if(ab.length===2&&G.relative[ab[0]]){var U="",X;while((X=G.match.POS.exec(ae))){U+=X[0];ae=ae.replace(G.match.POS,"")}Y=E.filter(U,E(/\s$/.test(ae)?ae+"*":ae,S))}else{Y=G.relative[ab[0]]?[S]:E(ab.shift(),S);while(ab.length){var P=[];ae=ab.shift();if(G.relative[ae]){ae+=ab.shift()}for(var af=0,ad=Y.length;af<ad;af++){E(ae,Y[af],P)}Y=P}}}else{var ai=V?{expr:ab.pop(),set:D(V)}:E.find(ab.pop(),ab.length===1&&S.parentNode?S.parentNode:S);Y=E.filter(ai.expr,ai.set);if(ab.length>0){ah=D(Y)}else{Q=false}while(ab.length){var T=ab.pop(),W=T;if(!G.relative[T]){T=""}else{W=ab.pop()}if(W==null){W=S}G.relative[T](ah,W,M(S))}}if(!ah){ah=Y}if(!ah){throw"Syntax error, unrecognized expression: "+(T||ae)}if(F.call(ah)==="[object Array]"){if(!Q){aa.push.apply(aa,ah)}else{if(S.nodeType===1){for(var af=0;ah[af]!=null;af++){if(ah[af]&&(ah[af]===true||ah[af].nodeType===1&&H(S,ah[af]))){aa.push(Y[af])}}}else{for(var af=0;ah[af]!=null;af++){if(ah[af]&&ah[af].nodeType===1){aa.push(Y[af])}}}}}else{D(ah,aa)}if(R){E(R,S,aa,V)}return aa};E.matches=function(P,Q){return E(P,null,null,Q)};E.find=function(V,S){var W,Q;if(!V){return[]}for(var R=0,P=G.order.length;R<P;R++){var T=G.order[R],Q;if((Q=G.match[T].exec(V))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){Q[1]=(Q[1]||"").replace(/\\/g,"");W=G.find[T](Q,S);if(W!=null){V=V.replace(G.match[T],"");break}}}}if(!W){W=S.getElementsByTagName("*")}return{set:W,expr:V}};E.filter=function(S,ac,ad,T){var Q=S,Y=[],ah=ac,V,ab;while(S&&ac.length){for(var U in G.filter){if((V=G.match[U].exec(S))!=null){var Z=G.filter[U],R=null,X=0,aa,ag;ab=false;if(ah==Y){Y=[]}if(G.preFilter[U]){V=G.preFilter[U](V,ah,ad,Y,T);if(!V){ab=aa=true}else{if(V===true){continue}else{if(V[0]===true){R=[];var W=null,af;for(var ae=0;(af=ah[ae])!==g;ae++){if(af&&W!==af){R.push(af);W=af}}}}}}if(V){for(var ae=0;(ag=ah[ae])!==g;ae++){if(ag){if(R&&ag!=R[X]){X++}aa=Z(ag,V,X,R);var P=T^!!aa;if(ad&&aa!=null){if(P){ab=true}else{ah[ae]=false}}else{if(P){Y.push(ag);ab=true}}}}}if(aa!==g){if(!ad){ah=Y}S=S.replace(G.match[U],"");if(!ab){return[]}break}}}S=S.replace(/\s*,\s*/,"");if(S==Q){if(ab==null){throw"Syntax error, unrecognized expression: "+S}else{break}}Q=S}return ah};var G=E.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(P){return P.getAttribute("href")}},relative:{"+":function(T,Q){for(var R=0,P=T.length;R<P;R++){var S=T[R];if(S){var U=S.previousSibling;while(U&&U.nodeType!==1){U=U.previousSibling}T[R]=typeof Q==="string"?U||false:U===Q}}if(typeof Q==="string"){E.filter(Q,T,true)}},">":function(U,Q,V){if(typeof Q==="string"&&!/\W/.test(Q)){Q=V?Q:Q.toUpperCase();for(var R=0,P=U.length;R<P;R++){var T=U[R];if(T){var S=T.parentNode;U[R]=S.nodeName===Q?S:false}}}else{for(var R=0,P=U.length;R<P;R++){var T=U[R];if(T){U[R]=typeof Q==="string"?T.parentNode:T.parentNode===Q}}if(typeof Q==="string"){E.filter(Q,U,true)}}},"":function(S,Q,U){var R="done"+(I++),P=O;if(!Q.match(/\W/)){var T=Q=U?Q:Q.toUpperCase();P=L}P("parentNode",Q,R,S,T,U)},"~":function(S,Q,U){var R="done"+(I++),P=O;if(typeof Q==="string"&&!Q.match(/\W/)){var T=Q=U?Q:Q.toUpperCase();P=L}P("previousSibling",Q,R,S,T,U)}},find:{ID:function(Q,R){if(R.getElementById){var P=R.getElementById(Q[1]);return P?[P]:[]}},NAME:function(P,Q){return Q.getElementsByName?Q.getElementsByName(P[1]):null},TAG:function(P,Q){return Q.getElementsByTagName(P[1])}},preFilter:{CLASS:function(S,Q,R,P,U){S=" "+S[1].replace(/\\/g,"")+" ";for(var T=0;Q[T];T++){if(U^(" "+Q[T].className+" ").indexOf(S)>=0){if(!R){P.push(Q[T])}}else{if(R){Q[T]=false}}}return false},ID:function(P){return P[1].replace(/\\/g,"")},TAG:function(Q,P){for(var R=0;!P[R];R++){}return M(P[R])?Q[1]:Q[1].toUpperCase()},CHILD:function(P){if(P[1]=="nth"){var Q=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(P[2]=="even"&&"2n"||P[2]=="odd"&&"2n+1"||!/\D/.test(P[2])&&"0n+"+P[2]||P[2]);P[2]=(Q[1]+(Q[2]||1))-0;P[3]=Q[3]-0}P[0]="done"+(I++);return P},ATTR:function(Q){var P=Q[1];if(G.attrMap[P]){Q[1]=G.attrMap[P]}if(Q[2]==="~="){Q[4]=" "+Q[4]+" "}return Q},PSEUDO:function(T,Q,R,P,U){if(T[1]==="not"){if(T[3].match(N).length>1){T[3]=E(T[3],null,null,Q)}else{var S=E.filter(T[3],Q,R,true^U);if(!R){P.push.apply(P,S)}return false}}else{if(G.match.POS.test(T[0])){return true}}return T},POS:function(P){P.unshift(true);return P}},filters:{enabled:function(P){return P.disabled===false&&P.type!=="hidden"},disabled:function(P){return P.disabled===true},checked:function(P){return P.checked===true},selected:function(P){P.parentNode.selectedIndex;return P.selected===true},parent:function(P){return !!P.firstChild},empty:function(P){return !P.firstChild},has:function(R,Q,P){return !!E(P[3],R).length},header:function(P){return/h\d/i.test(P.nodeName)},text:function(P){return"text"===P.type},radio:function(P){return"radio"===P.type},checkbox:function(P){return"checkbox"===P.type},file:function(P){return"file"===P.type},password:function(P){return"password"===P.type},submit:function(P){return"submit"===P.type},image:function(P){return"image"===P.type},reset:function(P){return"reset"===P.type},button:function(P){return"button"===P.type||P.nodeName.toUpperCase()==="BUTTON"},input:function(P){return/input|select|textarea|button/i.test(P.nodeName)}},setFilters:{first:function(Q,P){return P===0},last:function(R,Q,P,S){return Q===S.length-1},even:function(Q,P){return P%2===0},odd:function(Q,P){return P%2===1},lt:function(R,Q,P){return Q<P[3]-0},gt:function(R,Q,P){return Q>P[3]-0},nth:function(R,Q,P){return P[3]-0==Q},eq:function(R,Q,P){return P[3]-0==Q}},filter:{CHILD:function(P,S){var V=S[1],W=P.parentNode;var U="child"+W.childNodes.length;if(W&&(!W[U]||!P.nodeIndex)){var T=1;for(var Q=W.firstChild;Q;Q=Q.nextSibling){if(Q.nodeType==1){Q.nodeIndex=T++}}W[U]=T-1}if(V=="first"){return P.nodeIndex==1}else{if(V=="last"){return P.nodeIndex==W[U]}else{if(V=="only"){return W[U]==1}else{if(V=="nth"){var Y=false,R=S[2],X=S[3];if(R==1&&X==0){return true}if(R==0){if(P.nodeIndex==X){Y=true}}else{if((P.nodeIndex-X)%R==0&&(P.nodeIndex-X)/R>=0){Y=true}}return Y}}}}},PSEUDO:function(V,R,S,W){var Q=R[1],T=G.filters[Q];if(T){return T(V,S,R,W)}else{if(Q==="contains"){return(V.textContent||V.innerText||"").indexOf(R[3])>=0}else{if(Q==="not"){var U=R[3];for(var S=0,P=U.length;S<P;S++){if(U[S]===V){return false}}return true}}}},ID:function(Q,P){return Q.nodeType===1&&Q.getAttribute("id")===P},TAG:function(Q,P){return(P==="*"&&Q.nodeType===1)||Q.nodeName===P},CLASS:function(Q,P){return P.test(Q.className)},ATTR:function(T,R){var P=G.attrHandle[R[1]]?G.attrHandle[R[1]](T):T[R[1]]||T.getAttribute(R[1]),U=P+"",S=R[2],Q=R[4];return P==null?false:S==="="?U===Q:S==="*="?U.indexOf(Q)>=0:S==="~="?(" "+U+" ").indexOf(Q)>=0:!R[4]?P:S==="!="?U!=Q:S==="^="?U.indexOf(Q)===0:S==="$="?U.substr(U.length-Q.length)===Q:S==="|="?U===Q||U.substr(0,Q.length+1)===Q+"-":false},POS:function(T,Q,R,U){var P=Q[2],S=G.setFilters[P];if(S){return S(T,R,Q,U)}}}};for(var K in G.match){G.match[K]=RegExp(G.match[K].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var D=function(Q,P){Q=Array.prototype.slice.call(Q);if(P){P.push.apply(P,Q);return P}return Q};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(J){D=function(T,S){var Q=S||[];if(F.call(T)==="[object Array]"){Array.prototype.push.apply(Q,T)}else{if(typeof T.length==="number"){for(var R=0,P=T.length;R<P;R++){Q.push(T[R])}}else{for(var R=0;T[R];R++){Q.push(T[R])}}}return Q}}(function(){var Q=document.createElement("form"),R="script"+(new Date).getTime();Q.innerHTML="<input name='"+R+"'/>";var P=document.documentElement;P.insertBefore(Q,P.firstChild);if(!!document.getElementById(R)){G.find.ID=function(T,U){if(U.getElementById){var S=U.getElementById(T[1]);return S?S.id===T[1]||S.getAttributeNode&&S.getAttributeNode("id").nodeValue===T[1]?[S]:g:[]}};G.filter.ID=function(U,S){var T=U.getAttributeNode&&U.getAttributeNode("id");return U.nodeType===1&&T&&T.nodeValue===S}}P.removeChild(Q)})();(function(){var P=document.createElement("div");P.appendChild(document.createComment(""));if(P.getElementsByTagName("*").length>0){G.find.TAG=function(Q,U){var T=U.getElementsByTagName(Q[1]);if(Q[1]==="*"){var S=[];for(var R=0;T[R];R++){if(T[R].nodeType===1){S.push(T[R])}}T=S}return T}}P.innerHTML="<a href='#'></a>";if(P.firstChild.getAttribute("href")!=="#"){G.attrHandle.href=function(Q){return Q.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var P=E;E=function(T,S,Q,R){S=S||document;if(!R&&S.nodeType===9){try{return D(S.querySelectorAll(T),Q)}catch(U){}}return P(T,S,Q,R)};E.find=P.find;E.filter=P.filter;E.selectors=P.selectors;E.matches=P.matches})()}if(document.documentElement.getElementsByClassName){G.order.splice(1,0,"CLASS");G.find.CLASS=function(P,Q){return Q.getElementsByClassName(P[1])}}function L(Q,W,V,Z,X,Y){for(var T=0,R=Z.length;T<R;T++){var P=Z[T];if(P){P=P[Q];var U=false;while(P&&P.nodeType){var S=P[V];if(S){U=Z[S];break}if(P.nodeType===1&&!Y){P[V]=T}if(P.nodeName===W){U=P;break}P=P[Q]}Z[T]=U}}}function O(Q,V,U,Y,W,X){for(var S=0,R=Y.length;S<R;S++){var P=Y[S];if(P){P=P[Q];var T=false;while(P&&P.nodeType){if(P[U]){T=Y[P[U]];break}if(P.nodeType===1){if(!X){P[U]=S}if(typeof V!=="string"){if(P===V){T=true;break}}else{if(E.filter(V,[P]).length>0){T=P;break}}}P=P[Q]}Y[S]=T}}}var H=document.compareDocumentPosition?function(Q,P){return Q.compareDocumentPosition(P)&16}:function(Q,P){return Q!==P&&(Q.contains?Q.contains(P):true)};var M=function(P){return P.documentElement&&!P.body||P.tagName&&P.ownerDocument&&!P.ownerDocument.body};n.find=E;n.filter=E.filter;n.expr=E.selectors;n.expr[":"]=n.expr.filters;E.selectors.filters.hidden=function(P){return"hidden"===P.type||n.css(P,"display")==="none"||n.css(P,"visibility")==="hidden"};E.selectors.filters.visible=function(P){return"hidden"!==P.type&&n.css(P,"display")!=="none"&&n.css(P,"visibility")!=="hidden"};E.selectors.filters.animated=function(P){return n.grep(n.timers,function(Q){return P===Q.elem}).length};n.multiFilter=function(R,P,Q){if(Q){R=":not("+R+")"}return E.matches(R,P)};n.dir=function(R,Q){var P=[],S=R[Q];while(S&&S!=document){if(S.nodeType==1){P.push(S)}S=S[Q]}return P};n.nth=function(T,P,R,S){P=P||1;var Q=0;for(;T;T=T[R]){if(T.nodeType==1&&++Q==P){break}}return T};n.sibling=function(R,Q){var P=[];for(;R;R=R.nextSibling){if(R.nodeType==1&&R!=Q){P.push(R)}}return P};return;l.Sizzle=E})();n.event={add:function(H,E,G,J){if(H.nodeType==3||H.nodeType==8){return}if(H.setInterval&&H!=l){H=l}if(!G.guid){G.guid=this.guid++}if(J!==g){var F=G;G=this.proxy(F);G.data=J}var D=n.data(H,"events")||n.data(H,"events",{}),I=n.data(H,"handle")||n.data(H,"handle",function(){return typeof n!=="undefined"&&!n.event.triggered?n.event.handle.apply(arguments.callee.elem,arguments):g});I.elem=H;n.each(E.split(/\s+/),function(L,M){var N=M.split(".");M=N.shift();G.type=N.slice().sort().join(".");var K=D[M];if(n.event.specialAll[M]){n.event.specialAll[M].setup.call(H,J,N)}if(!K){K=D[M]={};if(!n.event.special[M]||n.event.special[M].setup.call(H,J,N)===false){if(H.addEventListener){H.addEventListener(M,I,false)}else{if(H.attachEvent){H.attachEvent("on"+M,I)}}}}K[G.guid]=G;n.event.global[M]=true});H=null},guid:1,global:{},remove:function(J,G,I){if(J.nodeType==3||J.nodeType==8){return}var F=n.data(J,"events"),E,D;if(F){if(G===g||(typeof G==="string"&&G.charAt(0)==".")){for(var H in F){this.remove(J,H+(G||""))}}else{if(G.type){I=G.handler;G=G.type}n.each(G.split(/\s+/),function(L,N){var P=N.split(".");N=P.shift();var M=RegExp("(^|\\.)"+P.slice().sort().join(".*\\.")+"(\\.|$)");if(F[N]){if(I){delete F[N][I.guid]}else{for(var O in F[N]){if(M.test(F[N][O].type)){delete F[N][O]}}}if(n.event.specialAll[N]){n.event.specialAll[N].teardown.call(J,P)}for(E in F[N]){break}if(!E){if(!n.event.special[N]||n.event.special[N].teardown.call(J,P)===false){if(J.removeEventListener){J.removeEventListener(N,n.data(J,"handle"),false)}else{if(J.detachEvent){J.detachEvent("on"+N,n.data(J,"handle"))}}}E=null;delete F[N]}}})}for(E in F){break}if(!E){var K=n.data(J,"handle");if(K){K.elem=null}n.removeData(J,"events");n.removeData(J,"handle")}}},trigger:function(H,J,G,D){var F=H.type||H;if(!D){H=typeof H==="object"?H[h]?H:n.extend(n.Event(F),H):n.Event(F);if(F.indexOf("!")>=0){H.type=F=F.slice(0,-1);H.exclusive=true}if(!G){H.stopPropagation();if(this.global[F]){n.each(n.cache,function(){if(this.events&&this.events[F]){n.event.trigger(H,J,this.handle.elem)}})}}if(!G||G.nodeType==3||G.nodeType==8){return g}H.result=g;H.target=G;J=n.makeArray(J);J.unshift(H)}H.currentTarget=G;var I=n.data(G,"handle");if(I){I.apply(G,J)}if((!G[F]||(n.nodeName(G,"a")&&F=="click"))&&G["on"+F]&&G["on"+F].apply(G,J)===false){H.result=false}if(!D&&G[F]&&!H.isDefaultPrevented()&&!(n.nodeName(G,"a")&&F=="click")){this.triggered=true;try{G[F]()}catch(K){}}this.triggered=false;if(!H.isPropagationStopped()){var E=G.parentNode||G.ownerDocument;if(E){n.event.trigger(H,J,E,true)}}},handle:function(J){var I,D;J=arguments[0]=n.event.fix(J||l.event);var K=J.type.split(".");J.type=K.shift();I=!K.length&&!J.exclusive;var H=RegExp("(^|\\.)"+K.slice().sort().join(".*\\.")+"(\\.|$)");D=(n.data(this,"events")||{})[J.type];for(var F in D){var G=D[F];if(I||H.test(G.type)){J.handler=G;J.data=G.data;var E=G.apply(this,arguments);if(E!==g){J.result=E;if(E===false){J.preventDefault();J.stopPropagation()}}if(J.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(G){if(G[h]){return G}var E=G;G=n.Event(E);for(var F=this.props.length,I;F;){I=this.props[--F];G[I]=E[I]}if(!G.target){G.target=G.srcElement||document}if(G.target.nodeType==3){G.target=G.target.parentNode}if(!G.relatedTarget&&G.fromElement){G.relatedTarget=G.fromElement==G.target?G.toElement:G.fromElement}if(G.pageX==null&&G.clientX!=null){var H=document.documentElement,D=document.body;G.pageX=G.clientX+(H&&H.scrollLeft||D&&D.scrollLeft||0)-(H.clientLeft||0);G.pageY=G.clientY+(H&&H.scrollTop||D&&D.scrollTop||0)-(H.clientTop||0)}if(!G.which&&((G.charCode||G.charCode===0)?G.charCode:G.keyCode)){G.which=G.charCode||G.keyCode}if(!G.metaKey&&G.ctrlKey){G.metaKey=G.ctrlKey}if(!G.which&&G.button){G.which=(G.button&1?1:(G.button&2?3:(G.button&4?2:0)))}return G},proxy:function(E,D){D=D||function(){return E.apply(this,arguments)};D.guid=E.guid=E.guid||D.guid||this.guid++;return D},special:{ready:{setup:A,teardown:function(){}}},specialAll:{live:{setup:function(D,E){n.event.add(this,E[0],c)},teardown:function(F){if(F.length){var D=0,E=RegExp("(^|\\.)"+F[0]+"(\\.|$)");n.each((n.data(this,"events").live||{}),function(){if(E.test(this.type)){D++}});if(D<1){n.event.remove(this,F[0],c)}}}}}};n.Event=function(D){if(!this.preventDefault){return new n.Event(D)}if(D&&D.type){this.originalEvent=D;this.type=D.type;this.timeStamp=D.timeStamp}else{this.type=D}if(!this.timeStamp){this.timeStamp=e()}this[h]=true};function k(){return false}function t(){return true}n.Event.prototype={preventDefault:function(){this.isDefaultPrevented=t;var D=this.originalEvent;if(!D){return}if(D.preventDefault){D.preventDefault()}D.returnValue=false},stopPropagation:function(){this.isPropagationStopped=t;var D=this.originalEvent;if(!D){return}if(D.stopPropagation){D.stopPropagation()}D.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=t;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(E){var D=E.relatedTarget;while(D&&D!=this){try{D=D.parentNode}catch(F){D=this}}if(D!=this){E.type=E.data;n.event.handle.apply(this,arguments)}};n.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(E,D){n.event.special[D]={setup:function(){n.event.add(this,E,a,D)},teardown:function(){n.event.remove(this,E,a)}}});n.fn.extend({bind:function(E,F,D){return E=="unload"?this.one(E,F,D):this.each(function(){n.event.add(this,E,D||F,D&&F)})},one:function(F,G,E){var D=n.event.proxy(E||G,function(H){n(this).unbind(H,D);return(E||G).apply(this,arguments)});return this.each(function(){n.event.add(this,F,D,E&&G)})},unbind:function(E,D){return this.each(function(){n.event.remove(this,E,D)})},trigger:function(D,E){return this.each(function(){n.event.trigger(D,E,this)})},triggerHandler:function(D,F){if(this[0]){var E=n.Event(D);E.preventDefault();E.stopPropagation();n.event.trigger(E,F,this[0]);return E.result}},toggle:function(F){var D=arguments,E=1;while(E<D.length){n.event.proxy(F,D[E++])}return this.click(n.event.proxy(F,function(G){this.lastToggle=(this.lastToggle||0)%E;G.preventDefault();return D[this.lastToggle++].apply(this,arguments)||false}))},hover:function(D,E){return this.mouseenter(D).mouseleave(E)},ready:function(D){A();if(n.isReady){D.call(document,n)}else{n.readyList.push(D)}return this},live:function(F,E){var D=n.event.proxy(E);D.guid+=this.selector+F;n(document).bind(i(F,this.selector),this.selector,D);return this},die:function(E,D){n(document).unbind(i(E,this.selector),D?{guid:D.guid+this.selector+E}:null);return this}});function c(G){var D=RegExp("(^|\\.)"+G.type+"(\\.|$)"),F=true,E=[];n.each(n.data(this,"events").live||[],function(H,I){if(D.test(I.type)){var J=n(G.target).closest(I.data)[0];if(J){E.push({elem:J,fn:I})}}});n.each(E,function(){if(!G.isImmediatePropagationStopped()&&this.fn.call(this.elem,G,this.fn.data)===false){F=false}});return F}function i(E,D){return["live",E,D.replace(/\./g,"`").replace(/ /g,"|")].join(".")}n.extend({isReady:false,readyList:[],ready:function(){if(!n.isReady){n.isReady=true;if(n.readyList){n.each(n.readyList,function(){this.call(document,n)});n.readyList=null}n(document).triggerHandler("ready")}}});var w=false;function A(){if(w){return}w=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);n.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);n.ready()}});if(document.documentElement.doScroll&&!l.frameElement){(function(){if(n.isReady){return}try{document.documentElement.doScroll("left")}catch(D){setTimeout(arguments.callee,0);return}n.ready()})()}}}n.event.add(l,"load",n.ready)}n.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(E,D){n.fn[D]=function(F){return F?this.bind(D,F):this.trigger(D)}});n(l).bind("unload",function(){for(var D in n.cache){if(D!=1&&n.cache[D].handle){n.event.remove(n.cache[D].handle.elem)}}});(function(){n.support={};var E=document.documentElement,F=document.createElement("script"),J=document.createElement("div"),I="script"+(new Date).getTime();J.style.display="none";J.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 G=J.getElementsByTagName("*"),D=J.getElementsByTagName("a")[0];if(!G||!G.length||!D){return}n.support={leadingWhitespace:J.firstChild.nodeType==3,tbody:!J.getElementsByTagName("tbody").length,objectAll:!!J.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!J.getElementsByTagName("link").length,style:/red/.test(D.getAttribute("style")),hrefNormalized:D.getAttribute("href")==="/a",opacity:D.style.opacity==="0.5",cssFloat:!!D.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};F.type="text/javascript";try{F.appendChild(document.createTextNode("window."+I+"=1;"))}catch(H){}E.insertBefore(F,E.firstChild);if(l[I]){n.support.scriptEval=true;delete l[I]}E.removeChild(F);if(J.attachEvent&&J.fireEvent){J.attachEvent("onclick",function(){n.support.noCloneEvent=false;J.detachEvent("onclick",arguments.callee)});J.cloneNode(true).fireEvent("onclick")}n(function(){var K=document.createElement("div");K.style.width="1px";K.style.paddingLeft="1px";document.body.appendChild(K);n.boxModel=n.support.boxModel=K.offsetWidth===2;document.body.removeChild(K)})})();var v=n.support.cssFloat?"cssFloat":"styleFloat";n.props={"for":"htmlFor","class":"className","float":v,cssFloat:v,styleFloat:v,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};n.fn.extend({_load:n.fn.load,load:function(F,I,J){if(typeof F!=="string"){return this._load(F)}var H=F.indexOf(" ");if(H>=0){var D=F.slice(H,F.length);F=F.slice(0,H)}var G="GET";if(I){if(n.isFunction(I)){J=I;I=null}else{if(typeof I==="object"){I=n.param(I);G="POST"}}}var E=this;n.ajax({url:F,type:G,dataType:"html",data:I,complete:function(L,K){if(K=="success"||K=="notmodified"){E.html(D?n("<div/>").append(L.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(D):L.responseText)}if(J){E.each(J,[L.responseText,K,L])}}});return this},serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?n.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(D,E){var F=n(this).val();return F==null?null:n.isArray(F)?n.map(F,function(H,G){return{name:E.name,value:H}}):{name:E.name,value:F}}).get()}});n.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(D,E){n.fn[E]=function(F){return this.bind(E,F)}});var q=e();n.extend({get:function(D,F,G,E){if(n.isFunction(F)){G=F;F=null}return n.ajax({type:"GET",url:D,data:F,success:G,dataType:E})},getScript:function(D,E){return n.get(D,null,E,"script")},getJSON:function(D,E,F){return n.get(D,E,F,"json")},post:function(D,F,G,E){if(n.isFunction(F)){G=F;F={}}return n.ajax({type:"POST",url:D,data:F,success:G,dataType:E})},ajaxSetup:function(D){n.extend(n.ajaxSettings,D)},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(L){L=n.extend(true,L,n.extend(true,{},n.ajaxSettings,L));var V,E=/=\?(&|$)/g,Q,U,F=L.type.toUpperCase();if(L.data&&L.processData&&typeof L.data!=="string"){L.data=n.param(L.data)}if(L.dataType=="jsonp"){if(F=="GET"){if(!L.url.match(E)){L.url+=(L.url.match(/\?/)?"&":"?")+(L.jsonp||"callback")+"=?"}}else{if(!L.data||!L.data.match(E)){L.data=(L.data?L.data+"&":"")+(L.jsonp||"callback")+"=?"}}L.dataType="json"}if(L.dataType=="json"&&(L.data&&L.data.match(E)||L.url.match(E))){V="jsonp"+q++;if(L.data){L.data=(L.data+"").replace(E,"="+V+"$1")}L.url=L.url.replace(E,"="+V+"$1");L.dataType="script";l[V]=function(W){U=W;H();K();l[V]=g;try{delete l[V]}catch(X){}if(G){G.removeChild(S)}}}if(L.dataType=="script"&&L.cache==null){L.cache=false}if(L.cache===false&&F=="GET"){var D=e();var T=L.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+D+"$2");L.url=T+((T==L.url)?(L.url.match(/\?/)?"&":"?")+"_="+D:"")}if(L.data&&F=="GET"){L.url+=(L.url.match(/\?/)?"&":"?")+L.data;L.data=null}if(L.global&&!n.active++){n.event.trigger("ajaxStart")}var P=/^(\w+:)?\/\/([^\/?#]+)/.exec(L.url);if(L.dataType=="script"&&F=="GET"&&P&&(P[1]&&P[1]!=location.protocol||P[2]!=location.host)){var G=document.getElementsByTagName("head")[0];var S=document.createElement("script");S.src=L.url;if(L.scriptCharset){S.charset=L.scriptCharset}if(!V){var N=false;S.onload=S.onreadystatechange=function(){if(!N&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){N=true;H();K();G.removeChild(S)}}}G.appendChild(S);return g}var J=false;var I=L.xhr();if(L.username){I.open(F,L.url,L.async,L.username,L.password)}else{I.open(F,L.url,L.async)}try{if(L.data){I.setRequestHeader("Content-Type",L.contentType)}if(L.ifModified){I.setRequestHeader("If-Modified-Since",n.lastModified[L.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}I.setRequestHeader("X-Requested-With","XMLHttpRequest");I.setRequestHeader("Accept",L.dataType&&L.accepts[L.dataType]?L.accepts[L.dataType]+", */*":L.accepts._default)}catch(R){}if(L.beforeSend&&L.beforeSend(I,L)===false){if(L.global&&!--n.active){n.event.trigger("ajaxStop")}I.abort();return false}if(L.global){n.event.trigger("ajaxSend",[I,L])}var M=function(W){if(I.readyState==0){if(O){clearInterval(O);O=null;if(L.global&&!--n.active){n.event.trigger("ajaxStop")}}}else{if(!J&&I&&(I.readyState==4||W=="timeout")){J=true;if(O){clearInterval(O);O=null}Q=W=="timeout"?"timeout":!n.httpSuccess(I)?"error":L.ifModified&&n.httpNotModified(I,L.url)?"notmodified":"success";if(Q=="success"){try{U=n.httpData(I,L.dataType,L)}catch(Y){Q="parsererror"}}if(Q=="success"){var X;try{X=I.getResponseHeader("Last-Modified")}catch(Y){}if(L.ifModified&&X){n.lastModified[L.url]=X}if(!V){H()}}else{n.handleError(L,I,Q)}K();if(L.async){I=null}}}};if(L.async){var O=setInterval(M,13);if(L.timeout>0){setTimeout(function(){if(I){if(!J){M("timeout")}if(I){I.abort()}}},L.timeout)}}try{I.send(L.data)}catch(R){n.handleError(L,I,null,R)}if(!L.async){M()}function H(){if(L.success){L.success(U,Q)}if(L.global){n.event.trigger("ajaxSuccess",[I,L])}}function K(){if(L.complete){L.complete(I,Q)}if(L.global){n.event.trigger("ajaxComplete",[I,L])}if(L.global&&!--n.active){n.event.trigger("ajaxStop")}}return I},handleError:function(E,G,D,F){if(E.error){E.error(G,D,F)}if(E.global){n.event.trigger("ajaxError",[G,E,F])}},active:0,httpSuccess:function(E){try{return !E.status&&location.protocol=="file:"||(E.status>=200&&E.status<300)||E.status==304||E.status==1223}catch(D){}return false},httpNotModified:function(F,D){try{var G=F.getResponseHeader("Last-Modified");return F.status==304||G==n.lastModified[D]}catch(E){}return false},httpData:function(I,G,F){var E=I.getResponseHeader("content-type"),D=G=="xml"||!G&&E&&E.indexOf("xml")>=0,H=D?I.responseXML:I.responseText;if(D&&H.documentElement.tagName=="parsererror"){throw"parsererror"}if(F&&F.dataFilter){H=F.dataFilter(H,G)}if(typeof H==="string"){if(G=="script"){n.globalEval(H)}if(G=="json"){H=l["eval"]("("+H+")")}}return H},param:function(D){var F=[];function G(H,I){F[F.length]=encodeURIComponent(H)+"="+encodeURIComponent(I)}if(n.isArray(D)||D.jquery){n.each(D,function(){G(this.name,this.value)})}else{for(var E in D){if(n.isArray(D[E])){n.each(D[E],function(){G(E,this)})}else{G(E,n.isFunction(D[E])?D[E]():D[E])}}}return F.join("&").replace(/%20/g,"+")}});var m={},d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function s(E,D){var F={};n.each(d.concat.apply([],d.slice(0,D)),function(){F[this]=E});return F}n.fn.extend({show:function(I,K){if(I){return this.animate(s("show",3),I,K)}else{for(var G=0,E=this.length;G<E;G++){var D=n.data(this[G],"olddisplay");this[G].style.display=D||"";if(n.css(this[G],"display")==="none"){var F=this[G].tagName,J;if(m[F]){J=m[F]}else{var H=n("<"+F+" />").appendTo("body");J=H.css("display");if(J==="none"){J="block"}H.remove();m[F]=J}this[G].style.display=n.data(this[G],"olddisplay",J)}}return this}},hide:function(G,H){if(G){return this.animate(s("hide",3),G,H)}else{for(var F=0,E=this.length;F<E;F++){var D=n.data(this[F],"olddisplay");if(!D&&D!=="none"){n.data(this[F],"olddisplay",n.css(this[F],"display"))}this[F].style.display="none"}return this}},_toggle:n.fn.toggle,toggle:function(F,E){var D=typeof F==="boolean";return n.isFunction(F)&&n.isFunction(E)?this._toggle.apply(this,arguments):F==null||D?this.each(function(){var G=D?F:n(this).is(":hidden");n(this)[G?"show":"hide"]()}):this.animate(s("toggle",3),F,E)},fadeTo:function(D,F,E){return this.animate({opacity:F},D,E)},animate:function(H,E,G,F){var D=n.speed(E,G,F);return this[D.queue===false?"each":"queue"](function(){var J=n.extend({},D),L,K=this.nodeType==1&&n(this).is(":hidden"),I=this;for(L in H){if(H[L]=="hide"&&K||H[L]=="show"&&!K){return J.complete.call(this)}if((L=="height"||L=="width")&&this.style){J.display=n.css(this,"display");J.overflow=this.style.overflow}}if(J.overflow!=null){this.style.overflow="hidden"}J.curAnim=n.extend({},H);n.each(H,function(N,R){var Q=new n.fx(I,J,N);if(/toggle|show|hide/.test(R)){Q[R=="toggle"?K?"show":"hide":R](H)}else{var P=R.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),S=Q.cur(true)||0;if(P){var M=parseFloat(P[2]),O=P[3]||"px";if(O!="px"){I.style[N]=(M||1)+O;S=((M||1)/Q.cur(true))*S;I.style[N]=S+O}if(P[1]){M=((P[1]=="-="?-1:1)*M)+S}Q.custom(S,M,O)}else{Q.custom(S,R,"")}}});return true})},stop:function(E,D){var F=n.timers;if(E){this.queue([])}this.each(function(){for(var G=F.length-1;G>=0;G--){if(F[G].elem==this){if(D){F[G](true)}F.splice(G,1)}}});if(!D){this.dequeue()}return this}});n.each({slideDown:s("show",1),slideUp:s("hide",1),slideToggle:s("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(D,E){n.fn[D]=function(F,G){return this.animate(E,F,G)}});n.extend({speed:function(F,G,E){var D=typeof F==="object"?F:{complete:E||!E&&G||n.isFunction(F)&&F,duration:F,easing:E&&G||G&&!n.isFunction(G)&&G};D.duration=n.fx.off?0:typeof D.duration==="number"?D.duration:n.fx.speeds[D.duration]||n.fx.speeds._default;D.old=D.complete;D.complete=function(){if(D.queue!==false){n(this).dequeue()}if(n.isFunction(D.old)){D.old.call(this)}};return D},easing:{linear:function(F,G,D,E){return D+E*F},swing:function(F,G,D,E){return((-Math.cos(F*Math.PI)/2)+0.5)*E+D}},timers:[],timerId:null,fx:function(E,D,F){this.options=D;this.elem=E;this.prop=F;if(!D.orig){D.orig={}}}});n.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(n.fx.step[this.prop]||n.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(E){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var D=parseFloat(n.css(this.elem,this.prop,E));return D&&D>-10000?D:parseFloat(n.curCSS(this.elem,this.prop))||0},custom:function(H,G,F){this.startTime=e();this.start=H;this.end=G;this.unit=F||this.unit||"px";this.now=this.start;this.pos=this.state=0;var D=this;function E(I){return D.step(I)}E.elem=this.elem;n.timers.push(E);if(E()&&n.timerId==null){n.timerId=setInterval(function(){var J=n.timers;for(var I=0;I<J.length;I++){if(!J[I]()){J.splice(I--,1)}}if(!J.length){clearInterval(n.timerId);n.timerId=null}},13)}},show:function(){this.options.orig[this.prop]=n.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());n(this.elem).show()},hide:function(){this.options.orig[this.prop]=n.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(G){var F=e();if(G||F>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var D=true;for(var E in this.options.curAnim){if(this.options.curAnim[E]!==true){D=false}}if(D){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(n.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){n(this.elem).hide()}if(this.options.hide||this.options.show){for(var H in this.options.curAnim){n.attr(this.elem.style,H,this.options.orig[H])}}}if(D){this.options.complete.call(this.elem)}return false}else{var I=F-this.startTime;this.state=I/this.options.duration;this.pos=n.easing[this.options.easing||(n.easing.swing?"swing":"linear")](this.state,I,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};n.extend(n.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(D){n.attr(D.elem.style,"opacity",D.now)},_default:function(D){if(D.elem.style&&D.elem.style[D.prop]!=null){D.elem.style[D.prop]=D.now+D.unit}else{D.elem[D.prop]=D.now}}}});if(document.documentElement.getBoundingClientRect){n.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return n.offset.bodyOffset(this[0])}var F=this[0].getBoundingClientRect(),I=this[0].ownerDocument,E=I.body,D=I.documentElement,K=D.clientTop||E.clientTop||0,J=D.clientLeft||E.clientLeft||0,H=F.top+(self.pageYOffset||n.boxModel&&D.scrollTop||E.scrollTop)-K,G=F.left+(self.pageXOffset||n.boxModel&&D.scrollLeft||E.scrollLeft)-J;return{top:H,left:G}}}else{n.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return n.offset.bodyOffset(this[0])}n.offset.initialized||n.offset.initialize();var I=this[0],F=I.offsetParent,E=I,N=I.ownerDocument,L,G=N.documentElement,J=N.body,K=N.defaultView,D=K.getComputedStyle(I,null),M=I.offsetTop,H=I.offsetLeft;while((I=I.parentNode)&&I!==J&&I!==G){L=K.getComputedStyle(I,null);M-=I.scrollTop,H-=I.scrollLeft;if(I===F){M+=I.offsetTop,H+=I.offsetLeft;if(n.offset.doesNotAddBorder&&!(n.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(I.tagName))){M+=parseInt(L.borderTopWidth,10)||0,H+=parseInt(L.borderLeftWidth,10)||0}E=F,F=I.offsetParent}if(n.offset.subtractsBorderForOverflowNotVisible&&L.overflow!=="visible"){M+=parseInt(L.borderTopWidth,10)||0,H+=parseInt(L.borderLeftWidth,10)||0}D=L}if(D.position==="relative"||D.position==="static"){M+=J.offsetTop,H+=J.offsetLeft}if(D.position==="fixed"){M+=Math.max(G.scrollTop,J.scrollTop),H+=Math.max(G.scrollLeft,J.scrollLeft)}return{top:M,left:H}}}n.offset={initialize:function(){if(this.initialized){return}var K=document.body,E=document.createElement("div"),G,F,M,H,L,D,I=K.style.marginTop,J='<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>';L={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(D in L){E.style[D]=L[D]}E.innerHTML=J;K.insertBefore(E,K.firstChild);G=E.firstChild,F=G.firstChild,H=G.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(F.offsetTop!==5);this.doesAddBorderForTableAndCells=(H.offsetTop===5);G.style.overflow="hidden",G.style.position="relative";this.subtractsBorderForOverflowNotVisible=(F.offsetTop===-5);K.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(K.offsetTop===0);K.style.marginTop=I;K.removeChild(E);this.initialized=true},bodyOffset:function(D){n.offset.initialized||n.offset.initialize();var F=D.offsetTop,E=D.offsetLeft;if(n.offset.doesNotIncludeMarginInBodyOffset){F+=parseInt(n.curCSS(D,"marginTop",true),10)||0,E+=parseInt(n.curCSS(D,"marginLeft",true),10)||0}return{top:F,left:E}}};n.fn.extend({position:function(){var H=0,G=0,E;if(this[0]){var F=this.offsetParent(),I=this.offset(),D=/^body|html$/i.test(F[0].tagName)?{top:0,left:0}:F.offset();I.top-=j(this,"marginTop");I.left-=j(this,"marginLeft");D.top+=j(F,"borderTopWidth");D.left+=j(F,"borderLeftWidth");E={top:I.top-D.top,left:I.left-D.left}}return E},offsetParent:function(){var D=this[0].offsetParent||document.body;while(D&&(!/^body|html$/i.test(D.tagName)&&n.css(D,"position")=="static")){D=D.offsetParent}return n(D)}});n.each(["Left","Top"],function(E,D){var F="scroll"+D;n.fn[F]=function(G){if(!this[0]){return null}return G!==g?this.each(function(){this==l||this==document?l.scrollTo(!E?G:n(l).scrollLeft(),E?G:n(l).scrollTop()):this[F]=G}):this[0]==l||this[0]==document?self[E?"pageYOffset":"pageXOffset"]||n.boxModel&&document.documentElement[F]||document.body[F]:this[0][F]}});n.each(["Height","Width"],function(G,E){var D=G?"Left":"Top",F=G?"Right":"Bottom";n.fn["inner"+E]=function(){return this[E.toLowerCase()]()+j(this,"padding"+D)+j(this,"padding"+F)};n.fn["outer"+E]=function(I){return this["inner"+E]()+j(this,"border"+D+"Width")+j(this,"border"+F+"Width")+(I?j(this,"margin"+D)+j(this,"margin"+F):0)};var H=E.toLowerCase();n.fn[H]=function(I){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+E]||document.body["client"+E]:this[0]==document?Math.max(document.documentElement["client"+E],document.body["scroll"+E],document.documentElement["scroll"+E],document.body["offset"+E],document.documentElement["offset"+E]):I===g?(this.length?n.css(this[0],H):null):this.css(H,typeof I==="string"?I:I+"px")}})})();
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.4_min.js b/devtools/client/inspector/markup/test/lib_jquery_1.4_min.js
new file mode 100644
index 000000000..5c70e4c5f
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.4_min.js
@@ -0,0 +1,151 @@
+/*!
+ * jQuery JavaScript Library v1.4
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://docs.jquery.com/License
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Jan 13 15:23:05 2010 -0500
+ */
+(function(A,w){function oa(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(oa,1);return}c.ready()}}function La(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function $(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var o in b)$(a,o,b[o],f,e,d);return a}if(d!==w){f=!i&&f&&c.isFunction(d);for(o=0;o<j;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,i);return a}return j?
+e(a[0],b):null}function K(){return(new Date).getTime()}function aa(){return false}function ba(){return true}function pa(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function qa(a){var b=true,d=[],f=[],e=arguments,i,j,o,p,n,t=c.extend({},c.data(this,"events").live);for(p in t){j=t[p];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete t[p]}i=c(a.target).closest(f,a.currentTarget);
+n=0;for(l=i.length;n<l;n++)for(p in t){j=t[p];o=i[n].elem;f=null;if(i[n].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==o)d.push({elem:o,fn:j})}}n=0;for(l=d.length;n<l;n++){i=d[n];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}function ra(a,b){return["live",a,b.replace(/\./g,"`").replace(/ /g,"&")].join(".")}function sa(a){return!a||!a.parentNode||a.parentNode.nodeType===
+11}function ta(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ua(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:s;f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=
+i?f:1;return{fragment:f,cacheable:e}}function T(a){for(var b=0,d,f;(d=a[b])!=null;b++)if(!c.noData[d.nodeName.toLowerCase()]&&(f=d[H]))delete c.cache[f]}function L(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ma=A.jQuery,Na=A.$,s=A.document,U,Oa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Pa=/^.[^:#\[\.,]*$/,Qa=/\S/,
+Ra=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Sa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],M,ca=Object.prototype.toString,da=Object.prototype.hasOwnProperty,ea=Array.prototype.push,R=Array.prototype.slice,V=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Oa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Sa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];
+c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ua([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return U.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a)}else return!b||b.jquery?(b||U).find(a):c(b).find(a);else if(c.isFunction(a))return U.ready(a);if(a.selector!==w){this.selector=a.selector;
+this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,this)},selector:"",jquery:"1.4",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=
+0;ea.apply(this,a);return this},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||
+c(null)},push:ea,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];o=e[i];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(o)?[]:{};a[i]=c.extend(f,j,o)}else if(o!==w)a[i]=
+o}return a};c.extend({noConflict:function(a){A.$=Na;if(a)A.jQuery=Ma;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",M,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",
+M);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&oa()}}},isFunction:function(a){return ca.call(a)==="[object Function]"},isArray:function(a){return ca.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||ca.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!da.call(a,"constructor")&&!da.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===w||da.call(a,b)},
+isEmptyObject:function(a){for(var b in a)return false;return true},noop:function(){},globalEval:function(a){if(a&&Qa.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===w||c.isFunction(a);
+if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Ra,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ea.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=
+0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b===
+"string"){d=a;a=d[b];b=w}else if(b&&!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){var b={browser:""};a=a.toLowerCase();if(/webkit/.test(a))b={browser:"webkit",version:/webkit[\/ ]([\w.]+)/};else if(/opera/.test(a))b={browser:"opera",version:/version/.test(a)?/version[\/ ]([\w.]+)/:/opera[\/ ]([\w.]+)/};else if(/msie/.test(a))b={browser:"msie",version:/msie ([\w.]+)/};else if(/mozilla/.test(a)&&
+!/compatible/.test(a))b={browser:"mozilla",version:/rv:([\w.]+)/};b.version=(b.version&&b.version.exec(a)||[0,"0"])[1];return b},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=true;if(V)c.inArray=function(a,b){return V.call(b,a)};U=c(s);if(s.addEventListener)M=function(){s.removeEventListener("DOMContentLoaded",M,false);c.ready()};else if(s.attachEvent)M=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",
+M);c.ready()}};if(V)c.inArray=function(a,b){return V.call(b,a)};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+K();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,
+htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,
+a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function o(){c.support.noCloneEvent=false;d.detachEvent("onclick",o)});d.cloneNode(true).fireEvent("onclick")}c(function(){var o=s.createElement("div");o.style.width=o.style.paddingLeft="1px";s.body.appendChild(o);c.boxModel=c.support.boxModel=o.offsetWidth===2;s.body.removeChild(o).style.display="none"});a=function(o){var p=s.createElement("div");o="on"+o;var n=o in
+p;if(!n){p.setAttribute(o,"return;");n=typeof p[o]==="function"}return n};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var H="jQuery"+K(),Ta=0,ya={},Ua={};c.extend({cache:{},expando:H,noData:{embed:true,object:true,applet:true},data:function(a,
+b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?ya:a;var f=a[H],e=c.cache;if(!b&&!f)return null;f||(f=++Ta);if(typeof b==="object"){a[H]=f;e=e[f]=c.extend(true,{},b)}else e=e[f]?e[f]:typeof d==="undefined"?Ua:(e[f]={});if(d!==w){a[H]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?ya:a;var d=a[H],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[H]}catch(i){a.removeAttribute&&
+a.removeAttribute(H)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,
+a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,
+a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var za=/[\n\t]/g,fa=/\s+/,Va=/\r/g,Wa=/href|src|style/,Xa=/(button|input)/i,Ya=/(button|input|object|select|textarea)/i,Za=/^(a|area)$/i,Aa=/radio|checkbox/;c.fn.extend({attr:function(a,
+b){return $(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(p){var n=c(this);n.addClass(a.call(this,p,n.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(fa),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className)for(var i=" "+e.className+" ",j=0,o=b.length;j<o;j++){if(i.indexOf(" "+b[j]+" ")<0)e.className+=
+" "+b[j]}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(p){var n=c(this);n.removeClass(a.call(this,p,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(fa),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(za," "),j=0,o=b.length;j<o;j++)i=i.replace(" "+b[j]+" "," ");e.className=i.substring(1,i.length-1)}else e.className=""}return this},toggleClass:function(a,
+b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),o=b,p=a.split(fa);e=p[i++];){o=f?o:!j.hasClass(e);j[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=
+" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(za," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(Aa.test(b.type)&&
+!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Va,"")}return w}var o=c.isFunction(a);return this.each(function(p){var n=c(this),t=a;if(this.nodeType===1){if(o)t=a.call(this,p,n.val());if(typeof t==="number")t+="";if(c.isArray(t)&&Aa.test(this.type))this.checked=c.inArray(n.val(),t)>=0;else if(c.nodeName(this,"select")){var z=c.makeArray(t);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),z)>=0});if(!z.length)this.selectedIndex=
+-1}else this.value=t}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Wa.test(b);if(b in a&&f&&!i){if(e){if(b==="type"&&Xa.test(a.nodeName)&&a.parentNode)throw"type property can't be changed";a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;
+if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Ya.test(a.nodeName)||Za.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var $a=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===
+3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;if(!d.guid)d.guid=c.guid++;if(f!==w){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):w};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var o,p=0;o=b[p++];){var n=o.split(".");o=n.shift();d.type=n.slice(0).sort().join(".");var t=e[o],z=this.special[o]||{};if(!t){t=e[o]={};
+if(!z.setup||z.setup.call(a,f,n,d)===false)if(a.addEventListener)a.addEventListener(o,i,false);else a.attachEvent&&a.attachEvent("on"+o,i)}if(z.add)if((n=z.add.call(a,d,f,n,t))&&c.isFunction(n)){n.guid=n.guid||d.guid;d=n}t[d.guid]=d;this.global[o]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===w||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);
+for(var o=0;i=b[o++];){var p=i.split(".");i=p.shift();var n=!p.length,t=c.map(p.slice(0).sort(),$a);t=new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.)?")+"(\\.|$)");var z=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var B in f[i])if(n||t.test(f[i][B].type))delete f[i][B];z.remove&&z.remove.call(a,p,j);for(e in f[i])break;if(!e){if(!z.teardown||z.teardown.call(a,p)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+
+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(B=c.data(a,"handle"))B.elem=null;c.removeData(a,"events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[H]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
+8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;var i=c.data(d,"handle");i&&i.apply(d,b);var j,o;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){j=d[e];o=d["on"+e]}}catch(p){}i=c.nodeName(d,"a")&&e==="click";if(!f&&j&&!a.isDefaultPrevented()&&!i){this.triggered=true;try{d[e]()}catch(n){}}else if(o&&d["on"+e].apply(d,b)===false)a.result=false;this.triggered=false;if(!a.isPropagationStopped())(d=d.parentNode||d.ownerDocument)&&c.event.trigger(a,b,d,true)},
+handle:function(a){var b,d;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},
+props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[H])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||
+s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&
+a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;c.event.add(this,b.live,qa,b)},remove:function(a){if(a.length){var b=0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],qa)}},special:{}},beforeunload:{setup:function(a,
+b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=K();this[H]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ba;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=
+ba;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ba;this.stopPropagation()},isDefaultPrevented:aa,isPropagationStopped:aa,isImmediatePropagationStopped:aa};var Ba=function(a){for(var b=a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ca=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",
+mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ca:Ba,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ca:Ba)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return pa("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+
+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return pa("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this,"keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var ga=/textarea|input|select/i;function Da(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>
+-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ha(a,b){var d=a.target,f,e;if(!(!ga.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Da(d);if(e!==f){if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",e);if(d.type!=="select"&&(f!=null||e)){a.type="change";return c.event.trigger(a,b,this)}}}}c.event.special.change={filters:{focusout:ha,click:function(a){var b=a.target,d=b.type;if(d===
+"radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ha.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ha.call(this,a)},beforeactivate:function(a){a=a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Da(a))}},setup:function(a,b,d){for(var f in W)c.event.add(this,f+".specialChange."+d.guid,W[f]);return ga.test(this.nodeName)},
+remove:function(a,b){for(var d in W)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),W[d]);return ga.test(this.nodeName)}};var W=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,
+f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){thisObject=e;e=f;f=w}var j=b==="one"?c.proxy(e,function(o){c(this).unbind(o,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e,thisObject):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,
+b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||
+a)},live:function(a,b,d){if(c.isFunction(b)){d=b;b=w}c(this.context).bind(ra(a,this.selector),{data:b,selector:this.selector,live:a},d);return this},die:function(a,b){c(this.context).unbind(ra(a,this.selector),b?{guid:b.guid+this.selector+a}:null);return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d){return d?
+this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",k,m=0;g[m];m++){k=g[m];if(k.nodeType===3||k.nodeType===4)h+=k.nodeValue;else if(k.nodeType!==8)h+=a(k.childNodes)}return h}function b(g,h,k,m,r,q){r=0;for(var v=m.length;r<v;r++){var u=m[r];if(u){u=u[g];for(var y=false;u;){if(u.sizcache===
+k){y=m[u.sizset];break}if(u.nodeType===1&&!q){u.sizcache=k;u.sizset=r}if(u.nodeName.toLowerCase()===h){y=u;break}u=u[g]}m[r]=y}}}function d(g,h,k,m,r,q){r=0;for(var v=m.length;r<v;r++){var u=m[r];if(u){u=u[g];for(var y=false;u;){if(u.sizcache===k){y=m[u.sizset];break}if(u.nodeType===1){if(!q){u.sizcache=k;u.sizset=r}if(typeof h!=="string"){if(u===h){y=true;break}}else if(p.filter(h,[u]).length>0){y=u;break}}u=u[g]}m[r]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,i=Object.prototype.toString,j=false,o=true;[0,0].sort(function(){o=false;return 0});var p=function(g,h,k,m){k=k||[];var r=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return k;for(var q=[],v,u,y,S,I=true,N=x(h),J=g;(f.exec(""),v=f.exec(J))!==null;){J=v[3];q.push(v[1]);if(v[2]){S=v[3];break}}if(q.length>1&&t.exec(g))if(q.length===2&&n.relative[q[0]])u=ia(q[0]+q[1],h);else for(u=n.relative[q[0]]?[h]:p(q.shift(),h);q.length;){g=q.shift();if(n.relative[g])g+=q.shift();
+u=ia(g,u)}else{if(!m&&q.length>1&&h.nodeType===9&&!N&&n.match.ID.test(q[0])&&!n.match.ID.test(q[q.length-1])){v=p.find(q.shift(),h,N);h=v.expr?p.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:q.pop(),set:B(m)}:p.find(q.pop(),q.length===1&&(q[0]==="~"||q[0]==="+")&&h.parentNode?h.parentNode:h,N);u=v.expr?p.filter(v.expr,v.set):v.set;if(q.length>0)y=B(u);else I=false;for(;q.length;){var E=q.pop();v=E;if(n.relative[E])v=q.pop();else E="";if(v==null)v=h;n.relative[E](y,v,N)}}else y=[]}y||(y=u);if(!y)throw"Syntax error, unrecognized expression: "+
+(E||g);if(i.call(y)==="[object Array]")if(I)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&F(h,y[g])))k.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&k.push(u[g]);else k.push.apply(k,y);else B(y,k);if(S){p(S,r,k,m);p.uniqueSort(k)}return k};p.uniqueSort=function(g){if(D){j=o;g.sort(D);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};p.matches=function(g,h){return p(g,null,null,h)};p.find=function(g,h,k){var m,r;if(!g)return[];
+for(var q=0,v=n.order.length;q<v;q++){var u=n.order[q];if(r=n.leftMatch[u].exec(g)){var y=r[1];r.splice(1,1);if(y.substr(y.length-1)!=="\\"){r[1]=(r[1]||"").replace(/\\/g,"");m=n.find[u](r,h,k);if(m!=null){g=g.replace(n.match[u],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};p.filter=function(g,h,k,m){for(var r=g,q=[],v=h,u,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var I in n.filter)if((u=n.leftMatch[I].exec(g))!=null&&u[2]){var N=n.filter[I],J,E;E=u[1];y=false;u.splice(1,1);if(E.substr(E.length-
+1)!=="\\"){if(v===q)q=[];if(n.preFilter[I])if(u=n.preFilter[I](u,v,k,q,m,S)){if(u===true)continue}else y=J=true;if(u)for(var X=0;(E=v[X])!=null;X++)if(E){J=N(E,u,X,v);var Ea=m^!!J;if(k&&J!=null)if(Ea)y=true;else v[X]=false;else if(Ea){q.push(E);y=true}}if(J!==w){k||(v=q);g=g.replace(n.match[I],"");if(!y)return[];break}}}if(g===r)if(y==null)throw"Syntax error, unrecognized expression: "+g;else break;r=g}return v};var n=p.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var k=typeof h==="string",m=k&&!/\W/.test(h);k=k&&!m;if(m)h=h.toLowerCase();m=0;for(var r=g.length,q;m<r;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=k||q&&q.nodeName.toLowerCase()===h?q||false:q===h}k&&p.filter(h,g,true)},">":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,r=g.length;m<r;m++){var q=g[m];if(q){k=q.parentNode;g[m]=k.nodeName.toLowerCase()===h?k:false}}}else{m=0;for(r=g.length;m<r;m++)if(q=g[m])g[m]=
+k?q.parentNode:q.parentNode===h;k&&p.filter(h,g,true)}},"":function(g,h,k){var m=e++,r=d;if(typeof h==="string"&&!/\W/.test(h)){var q=h=h.toLowerCase();r=b}r("parentNode",h,m,g,q,k)},"~":function(g,h,k){var m=e++,r=d;if(typeof h==="string"&&!/\W/.test(h)){var q=h=h.toLowerCase();r=b}r("previousSibling",h,m,g,q,k)}},find:{ID:function(g,h,k){if(typeof h.getElementById!=="undefined"&&!k)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var k=[];
+h=h.getElementsByName(g[1]);for(var m=0,r=h.length;m<r;m++)h[m].getAttribute("name")===g[1]&&k.push(h[m]);return k.length===0?null:k}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,k,m,r,q){g=" "+g[1].replace(/\\/g,"")+" ";if(q)return g;q=0;for(var v;(v=h[q])!=null;q++)if(v)if(r^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))k||m.push(v);else if(k)h[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,m,r,q){h=g[1].replace(/\\/g,"");if(!q&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,m,r){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=p(g[3],null,null,h);else{g=p.filter(g[3],h,k,true^r);k||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!p(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,k,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return h<k[3]-0},gt:function(g,h,k){return h>k[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,m){var r=h[1],q=n.filters[r];if(q)return q(g,k,h,m);else if(r==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(r==="not"){h=
+h[3];k=0;for(m=h.length;k<m;k++)if(h[k]===g)return false;return true}else throw"Syntax error, unrecognized expression: "+r;},CHILD:function(g,h){var k=h[1],m=g;switch(k){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(k==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":k=h[2];var r=h[3];if(k===1&&r===0)return true;h=h[0];var q=g.parentNode;if(q&&(q.sizcache!==h||!g.nodeIndex)){var v=0;for(m=q.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;q.sizcache=h}g=g.nodeIndex-r;return k===0?g===0:g%k===0&&g/k>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=n.attrHandle[k]?n.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?k===h:m==="*="?k.indexOf(h)>=0:m==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:m==="!="?k!==h:m==="^="?k.indexOf(h)===0:m==="$="?k.substr(k.length-h.length)===h:m==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,m){var r=n.setFilters[h[2]];if(r)return r(g,k,h,m)}}},t=n.match.POS;for(var z in n.match){n.match[z]=new RegExp(n.match[z].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[z]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[z].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var B=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){B=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,m=g.length;k<m;k++)h.push(g[k]);else for(k=0;g[k];k++)h.push(g[k]);return h}}var D;if(s.documentElement.compareDocumentPosition)D=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in s.documentElement)D=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(s.createRange)D=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)j=true;return g.ownerDocument?-1:1}var k=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();k.setStart(g,0);k.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=k.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)j=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var k=s.documentElement;k.insertBefore(g,k.firstChild);if(s.getElementById(h)){n.find.ID=function(m,r,q){if(typeof r.getElementById!=="undefined"&&!q)return(r=r.getElementById(m[1]))?r.id===m[1]||typeof r.getAttributeNode!=="undefined"&&
+r.getAttributeNode("id").nodeValue===m[1]?[r]:w:[]};n.filter.ID=function(m,r){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===r}}k.removeChild(g);k=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;k[m];m++)k[m].nodeType===1&&h.push(k[m]);k=h}return k};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=p,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){p=function(m,r,q,v){r=r||s;if(!v&&r.nodeType===9&&!x(r))try{return B(r.querySelectorAll(m),q)}catch(u){}return g(m,r,q,v)};for(var k in g)p[k]=g[k];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,k,m){if(typeof k.getElementsByClassName!=="undefined"&&!m)return k.getElementsByClassName(h[1])};g=null}}})();var F=s.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,
+h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ia=function(g,h){var k=[],m="",r;for(h=h.nodeType?[h]:h;r=n.match.PSEUDO.exec(g);){m+=r[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;r=0;for(var q=h.length;r<q;r++)p(g,h[r],k);return p.filter(m,k)};c.find=p;c.expr=p.selectors;c.expr[":"]=c.expr.filters;c.unique=p.uniqueSort;c.getText=a;c.isXMLDoc=x;c.contains=F})();var ab=/Until$/,bb=/^(?:parents|prevUntil|prevAll)/,
+cb=/,/;R=Array.prototype.slice;var Fa=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Pa.test(b))return c.filter(b,f,!d);else b=c.filter(b,a)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Fa(this,a,false),"not",a)},filter:function(a){return this.pushStack(Fa(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i=
+{},j;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var p=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,t){for(;t&&t.ownerDocument&&t!==b;){if(p?p.index(t)>-1:c(t).is(a))return t;t=t.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(sa(a[0])||sa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);ab.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||cb.test(f))&&bb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ga=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,db=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/<tbody/i,gb=/<|&\w+;/,hb=function(a,b,d){return eb.test(d)?a:b+"></"+d+">"},G={option:[1,"<select multiple='multiple'>","</select>"],
+legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};G.optgroup=G.option;G.tbody=G.tfoot=G.colgroup=G.caption=G.thead;G.th=G.td;if(!c.support.htmlSerialize)G._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);
+return d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.getText(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&
+this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,
+"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ga,"").replace(Y,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ta(this,b);ta(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===
+1?this[0].innerHTML.replace(Ga,""):null;else if(typeof a==="string"&&!/<script/i.test(a)&&(c.support.leadingWhitespace||!Y.test(a))&&!G[(Ha.exec(a)||["",""])[1].toLowerCase()])try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){T(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}else c.isFunction(a)?this.each(function(e){var i=c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){c.isFunction(a)||(a=c(a).detach());return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(t){return c.nodeName(t,"table")?t.getElementsByTagName("tbody")[0]||t.appendChild(t.ownerDocument.createElement("tbody")):t}var e,i,j=a[0],o=[];if(c.isFunction(j))return this.each(function(t){var z=
+c(this);a[0]=j.call(this,t,b?z.html():w);return z.domManip(a,b,d)});if(this[0]){e=a[0]&&a[0].parentNode&&a[0].parentNode.nodeType===11?{fragment:a[0].parentNode}:ua(a,this,o);if(i=e.fragment.firstChild){b=b&&c.nodeName(i,"tr");for(var p=0,n=this.length;p<n;p++)d.call(b?f(this[p],i):this[p],e.cacheable||this.length>1||p>0?e.fragment.cloneNode(true):e.fragment)}o&&c.each(o,La)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},
+function(a,b){c.fn[a]=function(d){var f=[];d=c(d);for(var e=0,i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}});c.each({remove:function(a,b){if(!a||c.filter(a,[this]).length){if(!b&&this.nodeType===1){T(this.getElementsByTagName("*"));T([this])}this.parentNode&&this.parentNode.removeChild(this)}},empty:function(){for(this.nodeType===1&&T(this.getElementsByTagName("*"));this.firstChild;)this.removeChild(this.firstChild)}},
+function(a,b){c.fn[a]=function(){return this.each(b,arguments)}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;var e=[];c.each(a,function(i,j){if(typeof j==="number")j+="";if(j){if(typeof j==="string"&&!gb.test(j))j=b.createTextNode(j);else if(typeof j==="string"){j=j.replace(db,hb);var o=(Ha.exec(j)||["",""])[1].toLowerCase(),p=G[o]||G._default,n=p[0];i=b.createElement("div");for(i.innerHTML=p[1]+j+p[2];n--;)i=i.lastChild;
+if(!c.support.tbody){n=fb.test(j);o=o==="table"&&!n?i.firstChild&&i.firstChild.childNodes:p[1]==="<table>"&&!n?i.childNodes:[];for(p=o.length-1;p>=0;--p)c.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!c.support.leadingWhitespace&&Y.test(j)&&i.insertBefore(b.createTextNode(Y.exec(j)[0]),i.firstChild);j=c.makeArray(i.childNodes)}if(j.nodeType)e.push(j);else e=c.merge(e,j)}});if(d)for(a=0;e[a];a++)if(f&&c.nodeName(e[a],"script")&&(!e[a].type||e[a].type.toLowerCase()===
+"text/javascript"))f.push(e[a].parentNode?e[a].parentNode.removeChild(e[a]):e[a]);else{e[a].nodeType===1&&e.splice.apply(e,[a+1,0].concat(c.makeArray(e[a].getElementsByTagName("script"))));d.appendChild(e[a])}return e}});var ib=/z-?index|font-?weight|opacity|zoom|line-?height/i,Ia=/alpha\([^)]*\)/,Ja=/opacity=([^)]*)/,ja=/float/i,ka=/-([a-z])/ig,jb=/([A-Z])/g,kb=/^-?\d+(?:px)?$/i,lb=/^-?\d/,mb={position:"absolute",visibility:"hidden",display:"block"},nb=["Left","Right"],ob=["Top","Bottom"],pb=s.defaultView&&
+s.defaultView.getComputedStyle,Ka=c.support.cssFloat?"cssFloat":"styleFloat",la=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return $(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!ib.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""===
+"NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Ia.test(a)?a.replace(Ia,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Ja.exec(f.filter)[1])/100+"":""}if(ja.test(b))b=Ka;b=b.replace(ka,la);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?nb:ob;function j(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=
+parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})}a.offsetWidth!==0?j():c.swap(a,mb,j);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Ja.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ja.test(b))b=Ka;if(!d&&e&&e[b])f=e[b];else if(pb){if(ja.test(b))b="float";b=b.replace(jb,"-$1").toLowerCase();e=
+a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ka,la);f=a.currentStyle[b]||a.currentStyle[d];if(!kb.test(f)&&lb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=
+f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var qb=K(),rb=/<script(.|\s)*?\/script>/gi,sb=/select|textarea/i,tb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,O=/=\?(&|$)/,ma=/\?/,ub=/(\?|&)_=.*?(&|$)/,vb=/^(\w+:)?\/\/([^\/?#]+)/,
+wb=/%20/g;c.fn.extend({_load:c.fn.load,load:function(a,b,d){if(typeof a!=="string")return this._load(a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}c.ajax({url:a,type:f,dataType:"html",data:b,context:this,complete:function(i,j){if(j==="success"||j==="notmodified")this.html(e?c("<div />").append(i.responseText.replace(rb,
+"")).find(e):i.responseText);d&&this.each(d,[i.responseText,j,i])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||sb.test(this.nodeName)||tb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});
+c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},
+ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",
+text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(p,o,j,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(p,x,j);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(r,q){(e.context?c(e.context):c.event).trigger(r,q)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,o,p=e.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,
+e.traditional);if(e.dataType==="jsonp"){if(n==="GET")O.test(e.url)||(e.url+=(ma.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!O.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&O.test(e.data)||O.test(e.url))){i=e.jsonpCallback||"jsonp"+qb++;if(e.data)e.data=(e.data+"").replace(O,"="+i+"$1");e.url=e.url.replace(O,"="+i+"$1");e.dataType="script";A[i]=A[i]||function(r){o=r;b();d();A[i]=w;try{delete A[i]}catch(q){}B&&
+B.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&n==="GET"){var t=K(),z=e.url.replace(ub,"$1_="+t+"$2");e.url=z+(z===e.url?(ma.test(e.url)?"&":"?")+"_="+t:"")}if(e.data&&n==="GET")e.url+=(ma.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");t=(t=vb.exec(e.url))&&(t[1]&&t[1]!==location.protocol||t[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&t){var B=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");
+C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!i){var D=false;C.onload=C.onreadystatechange=function(){if(!D&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){D=true;b();d();C.onload=C.onreadystatechange=null;B&&C.parentNode&&B.removeChild(C)}}}B.insertBefore(C,B.firstChild);return w}var F=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",
+e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}t||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ia){}if(e.beforeSend&&e.beforeSend.call(p,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",
+[x,e]);var g=x.onreadystatechange=function(r){if(!x||x.readyState===0){F||d();F=true;if(x)x.onreadystatechange=c.noop}else if(!F&&x&&(x.readyState===4||r==="timeout")){F=true;x.onreadystatechange=c.noop;j=r==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";if(j==="success")try{o=c.httpData(x,e.dataType,e)}catch(q){j="parsererror"}if(j==="success"||j==="notmodified")i||b();else c.handleError(e,x,j);d();r==="timeout"&&x.abort();if(e.async)x=
+null}};try{var h=x.abort;x.abort=function(){if(x){h.call(x);if(x)x.readyState=0}g()}}catch(k){}e.async&&e.timeout>0&&setTimeout(function(){x&&!F&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||A,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol===
+"file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;if(e&&a.documentElement.nodeName==="parsererror")throw"parsererror";if(d&&
+d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))a=A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+a))();else throw"Invalid JSON: "+a;else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(e,i){i=
+c.isFunction(i)?i():i;f[f.length]=encodeURIComponent(e)+"="+encodeURIComponent(i)}var f=[];if(b===w)b=c.ajaxSettings.traditional;c.isArray(a)||a.jquery?c.each(a,function(){d(this.name,this.value)}):c.each(a,function e(i,j){if(c.isArray(j))c.each(j,function(o,p){b?d(i,p):e(i+"["+(typeof p==="object"||c.isArray(p)?o:"")+"]",p)});else!b&&j!=null&&typeof j==="object"?c.each(j,function(o,p){e(i+"["+o+"]",p)}):d(i,j)});return f.join("&").replace(wb,"+")}});var na={},xb=/toggle|show|hide/,yb=/^([+-]=)?([\d+-.]+)(.*)$/,
+Z,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a!=null)return this.animate(L("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(na[d])f=na[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();
+na[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a!=null)return this.animate(L("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&
+c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(L("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,o=this.nodeType===1&&c(this).is(":hidden"),
+p=this;for(j in a){var n=j.replace(ka,la);if(j!==n){a[n]=a[j];delete a[j];j=n}if(a[j]==="hide"&&o||a[j]==="show"&&!o)return i.complete.call(this);if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(t,z){var B=new c.fx(p,i,t);if(xb.test(z))B[z==="toggle"?o?"show":"hide":z](a);
+else{var C=yb.exec(z),D=B.cur(true)||0;if(C){z=parseFloat(C[2]);var F=C[3]||"px";if(F!=="px"){p.style[t]=(z||1)+F;D=(z||1)/B.cur(true)*D;p.style[t]=D+F}if(C[1])z=(C[1]==="-="?-1:1)*z+D;B.custom(D,z,F)}else B.custom(D,z,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:L("show",1),slideUp:L("hide",1),slideToggle:L("toggle",
+1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,
+b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==
+null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=K();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!Z)Z=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop===
+"width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=K(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=
+this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=
+c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(Z);Z=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=
+null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?function(a){var b=this[0];if(!b||!b.ownerDocument)return null;if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),
+f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(!b||!b.ownerDocument)return null;if(a)return this.each(function(t){c.offset.setOffset(this,a,t)});if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=
+b,e=b.ownerDocument,i,j=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var p=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;p-=b.scrollTop;n-=b.scrollLeft;if(b===d){p+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){p+=parseFloat(i.borderTopWidth)||
+0;n+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!=="visible"){p+=parseFloat(i.borderTopWidth)||0;n+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){p+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){p+=Math.max(j.scrollTop,o.scrollTop);n+=Math.max(j.scrollLeft,o.scrollLeft)}return{top:p,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),
+d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);
+d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop},
+bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left-
+e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=
+this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==w)return this.each(function(){if(i=wa(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=wa(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}});
+c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+
+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.6_min.js b/devtools/client/inspector/markup/test/lib_jquery_1.6_min.js
new file mode 100644
index 000000000..c72011dfa
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.6_min.js
@@ -0,0 +1,16 @@
+/*!
+ * jQuery JavaScript Library v1.6
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Mon May 2 13:50:00 2011 -0400
+ */
+(function(a,b){function cw(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function ct(a){if(!ch[a]){var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d===""){ci||(ci=c.createElement("iframe"),ci.frameBorder=ci.width=ci.height=0),c.body.appendChild(ci);if(!cj||!ci.createElement)cj=(ci.contentWindow||ci.contentDocument).document,cj.write("<!doctype><html><body></body></html>");b=cj.createElement(a),cj.body.appendChild(b),d=f.css(b,"display"),c.body.removeChild(ci)}ch[a]=d}return ch[a]}function cs(a,b){var c={};f.each(cn.concat.apply([],cn.slice(0,b)),function(){c[this]=a});return c}function cr(){co=b}function cq(){setTimeout(cr,0);return co=f.now()}function cg(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cf(){try{return new a.XMLHttpRequest}catch(b){}}function b_(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function b$(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bZ(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):bZ(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bZ(a+"["+e+"]",b[e],c,d);else d(a,b)}function bY(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bY(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bY(a,c,d,e,"*",g));return l}function bX(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?bv:bw,e=b==="width"?a.offsetWidth:a.offsetHeight;if(c==="border")return e;f.each(d,function(){c||(e-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?e+=parseFloat(f.css(a,"margin"+this))||0:e-=parseFloat(f.css(a,"border"+this+"Width"))||0});return e}function bl(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval(b.text||b.textContent||b.innerHTML||""),b.parentNode&&b.parentNode.removeChild(b)}function bk(a){f.nodeName(a,"input")?bj(a):a.getElementsByTagName&&f.grep(a.getElementsByTagName("input"),bj)}function bj(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bi(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bh(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bg(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bf(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function W(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(R.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(x,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){name="data-"+c.replace(j,"$1-$2").toLowerCase(),d=a.getAttribute(name);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(e){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function H(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(H,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=d.userAgent,x,y,z,A=Object.prototype.toString,B=Object.prototype.hasOwnProperty,C=Array.prototype.push,D=Array.prototype.slice,E=String.prototype.trim,F=Array.prototype.indexOf,G={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?g=[null,a,null]:g=i.exec(a);if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6",length:0,size:function(){return this.length},toArray:function(){return D.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?C.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(D.apply(this,arguments),"slice",D.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:C,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;y.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!y){y=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",z,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",z),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&H()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):G[A.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!B.call(a,"constructor")&&!B.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||B.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:E?function(a){return a==null?"":E.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?C.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(F)return F.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=D.call(arguments,2),g=function(){return a.apply(c,f.concat(D.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(c,d){d&&d instanceof e&&!(d instanceof a)&&(d=a(d));return e.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){G["[object "+b+"]"]=b.toLowerCase()}),x=e.uaMatch(w),x.browser&&(e.browser[x.browser]=!0,e.browser.version=x.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?z=function(){c.removeEventListener("DOMContentLoaded",z,!1),e.ready()}:c.attachEvent&&(z=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",z),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g](h)}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b,d,e,f,g,h,i,j,k,l,m,n,o,p,q;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",b=a.getElementsByTagName("*"),d=a.getElementsByTagName("a")[0];if(!b||!b.length||!d)return{};e=c.createElement("select"),f=e.appendChild(c.createElement("option")),g=a.getElementsByTagName("input")[0],i={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.55$/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:g.value==="on",optSelected:f.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},g.checked=!0,i.noCloneChecked=g.cloneNode(!0).checked,e.disabled=!0,i.optDisabled=!f.disabled;try{delete a.test}catch(r){i.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function click(){i.noCloneEvent=!1,a.detachEvent("onclick",click)}),a.cloneNode(!0).fireEvent("onclick")),g=c.createElement("input"),g.value="t",g.setAttribute("type","radio"),i.radioValue=g.value==="t",g.setAttribute("checked","checked"),a.appendChild(g),j=c.createDocumentFragment(),j.appendChild(a.firstChild),i.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",k=c.createElement("body"),l={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"};for(p in l)k.style[p]=l[p];k.appendChild(a),c.documentElement.appendChild(k),i.appendChecked=g.checked,i.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,i.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",i.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",m=a.getElementsByTagName("td"),q=m[0].offsetHeight===0,m[0].style.display="",m[1].style.display="none",i.reliableHiddenOffsets=q&&m[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(h=c.createElement("div"),h.style.width="0",h.style.marginRight="0",a.appendChild(h),i.reliableMarginRight=(parseInt(c.defaultView.getComputedStyle(h,null).marginRight,10)||0)===0),k.innerHTML="",c.documentElement.removeChild(k);if(a.attachEvent)for(p in{submit:1,change:1,focusin:1})o="on"+p,q=o in a,q||(a.setAttribute(o,"return;"),q=typeof a[o]=="function"),i[p+"Bubbles"]=q;return i}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[c]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function l(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark";while(g--)if(tmp=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,tmp.done(l);l();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:data-|aria-)/,u=/\:/,v;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.addClass(a.call(this,b,c.attr("class")||""))});if(a&&typeof a=="string"){var b=(a||"").split(o);for(var c=0,d=this.length;c<d;c++){var e=this[c];if(e.nodeType===1)if(!e.className)e.className=a;else{var g=" "+e.className+" ",h=e.className;for(var i=0,j=b.length;i<j;i++)g.indexOf(" "+b[i]+" ")<0&&(h+=" "+b[i]);e.className=f.trim(h)}}}return this},removeClass:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.removeClass(a.call(this,b,c.attr("class")))});if(a&&typeof a=="string"||a===b){var c=(a||"").split(o);for(var d=0,e=this.length;d<e;d++){var g=this[d];if(g.nodeType===1&&g.className)if(a){var h=(" "+g.className+" ").replace(n," ");for(var i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){var d=f(this);d.toggleClass(a.call(this,c,d.attr("class"),b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;return(e.value||"").replace(p,"")}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||"set"in c&&c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b=a.selectedIndex,c=[],d=a.options,e=a.type==="select-one";if(b<0)return null;for(var g=e?b:0,h=e?b+1:d.length;g<h;g++){var i=d[g];if(i.selected&&(f.support.optDisabled?!i.disabled:i.getAttribute("disabled")===null)&&(!i.parentNode.disabled||!f.nodeName(i.parentNode,"optgroup"))){value=f(i).val();if(e)return value;c.push(value)}}if(e&&!c.length&&d.length)return f(d[b]).val();return c},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex",readonly:"readOnly"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);var h,i,j=g!==1||!f.isXMLDoc(a);c=j&&f.attrFix[c]||c,i=f.attrHooks[c]||(v&&(f.nodeName(a,"form")||u.test(c))?v:b);if(d!==b){if(d===null||d===!1&&!t.test(c)){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;d===!0&&!t.test(c)&&(d=c),a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j)return i.get(a,c);h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.getAttribute("value");a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}},propFix:{},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);c=i&&f.propFix[c]||c,h=f.propHooks[c];return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),f.support.getSetAttribute||(f.attrFix=f.extend(f.attrFix,{"for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder"}),v=f.attrHooks.name=f.attrHooks.value=f.valHooks.button={get:function(a,c){var d;if(c==="value"&&!f.nodeName(a,"button"))return a.getAttribute(c);d=a.getAttributeNode(c);return d&&d.specified?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var w=Object.prototype.hasOwnProperty,x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,N(a.origType,a.selector),f.extend({},a,{handler:M,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,N(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?E:D):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=E;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=E;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=E,this.stopPropagation()},isDefaultPrevented:D,isPropagationStopped:D,isImmediatePropagationStopped:D};var F=function(a){var b=a.relatedTarget;try{if(b&&b!==c&&!b.parentNode)return;while(b&&b!==this)b=b.parentNode;b!==this&&(a.type=a.data,f.event.handle.apply(this,arguments))}catch(d){}},G=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?G:F,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?G:F)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&K("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&K("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var H,I=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function J(a){var c=a.target,d,e;if(!!y.test(c.nodeName)&&!c.readOnly){d=f._data(c,"_change_data"),e=I(c),(a.type!=="focusout"||c.type!=="radio")&&f._data(c,"_change_data",e);if(d===b||e===d)return;if(d!=null||e)a.type="change",a.liveFired=b,f.event.trigger(a,arguments[1],c)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var L={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||D,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=x.exec(h),k="",j&&(k=j[0],h=h.replace(x,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,L[h]?(a.push(L[h]+k),h=h+k):h=(L[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+N(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+N(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.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|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){return a.nodeName.toLowerCase()==="input"&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(a===b){g=!0;return 0}if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var O=/Until$/,P=/^(?:parents|prevUntil|prevAll)/,Q=/,/,R=/^.[^:#\[\.,]*$/,S=Array.prototype.slice,T=f.expr.match.POS,U={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(W(this,a,!1),"not",a)},filter:function(a){return this.pushStack(W(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=T.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/<tbody/i,ba=/<|&#?\w+;/,bb=/<(?:script|object|embed|option|style)/i,bc=/checked\s*(?:[^=]|=\s*.checked.)/i,bd=/\/(java|ecma)script/i,be={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};be.optgroup=be.option,be.tbody=be.tfoot=be.colgroup=be.caption=be.thead,be.th=be.td,f.support.htmlSerialize||(be._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!be[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bc.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bf(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bl)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i=b&&b[0]?b[0].ownerDocument||b[0]:c;a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!bb.test(a[0])&&(f.support.checkClone||!bc.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bh(a,d),e=bi(a),g=bi(d);for(h=0;e[h];++h)bh(e[h],g[h])}if(b){bg(a,d);if(c){e=bi(a),g=bi(d);for(h=0;e[h];++h)bg(e[h],g[h])}}return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[];for(var i=0,j;(j=a[i])!=null;i++){typeof j=="number"&&(j+="");if(!j)continue;if(typeof j=="string")if(!ba.test(j))j=b.createTextNode(j);else{j=j.replace(Z,"<$1></$2>");var k=($.exec(j)||["",""])[1].toLowerCase(),l=be[k]||be._default,m=l[0],n=b.createElement("div");n.innerHTML=l[1]+j+l[2];while(m--)n=n.lastChild;if(!f.support.tbody){var o=_.test(j),p=k==="table"&&!o?n.firstChild&&n.firstChild.childNodes:l[1]==="<table>"&&!o?n.childNodes:[];for(var q=p.length-1;q>=0;--q)f.nodeName(p[q],"tbody")&&!p[q].childNodes.length&&p[q].parentNode.removeChild(p[q])}!f.support.leadingWhitespace&&Y.test(j)&&n.insertBefore(b.createTextNode(Y.exec(j)[0]),n.firstChild),j=n.childNodes}var r;if(!f.support.appendChecked)if(j[0]&&typeof (r=j.length)=="number")for(i=0;i<r;i++)bk(j[i]);else bk(j);j.nodeType?h.push(j):h=f.merge(h,j)}if(d){g=function(a){return!a.type||bd.test(a.type)};for(i=0;h[i];i++)if(e&&f.nodeName(h[i],"script")&&(!h[i].type||h[i].type.toLowerCase()==="text/javascript"))e.push(h[i].parentNode?h[i].parentNode.removeChild(h[i]):h[i]);else{if(h[i].nodeType===1){var s=f.grep(h[i].getElementsByTagName("script"),g);h.splice.apply(h,[i+1,0].concat(s))}d.appendChild(h[i])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bm=/alpha\([^)]*\)/i,bn=/opacity=([^)]*)/,bo=/-([a-z])/ig,bp=/([A-Z]|^ms)/g,bq=/^-?\d+(?:px)?$/i,br=/^-?\d/,bs=/^[+\-]=/,bt=/[^+\-\.\de]+/g,bu={position:"absolute",visibility:"hidden",display:"block"},bv=["Left","Right"],bw=["Top","Bottom"],bx,by,bz,bA=function(a,b){return b.toUpperCase()};f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bx(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0,widows:!0,orphans:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bs.test(d)&&(d=+d.replace(bt,"")+parseFloat(f.css(a,c))),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bx)return bx(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bo,bA)}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){a.offsetWidth!==0?e=bB(a,b,d):f.swap(a,bu,function(){e=bB(a,b,d)});if(e<=0){e=bx(a,b,b),e==="0px"&&bz&&(e=bz(a,b,b));if(e!=null)return e===""||e==="auto"?"0px":e}if(e<0||e==null){e=a.style[b];return e===""||e==="auto"?"0px":e}return typeof e=="string"?e:e+"px"}},set:function(a,b){if(!bq.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bn.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bm.test(g)?g.replace(bm,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV;try{bU=e.href}catch(bW){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bX(bS),ajaxTransport:bX(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?b$(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b_(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bY(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bY(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bZ(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var ca=f.now(),cb=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+ca++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cb.test(b.url)||e&&cb.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cb,l),b.url===j&&(e&&(k=k.replace(cb,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cc=a.ActiveXObject?function(){for(var a in ce)ce[a](0,1)}:!1,cd=0,ce;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cf()||cg()}:cf,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cc&&delete ce[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cd,cc&&(ce||(ce={},f(a).unload(cc)),ce[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ch={},ci,cj,ck=/^(?:toggle|show|hide)$/,cl=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cm,cn=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],co,cp=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cs("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",ct(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cs("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cs("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g];if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=ct(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block")),b.animatedProperties[g]=f.isArray(h)?h[1]:b.specialEasing&&b.specialEasing[g]||b.easing||"swing"}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],ck.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=cl.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[g]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cs("show",1),slideUp:cs("hide",1),slideToggle:cs("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this),f.isFunction(d.old)&&d.old.call(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function h(a){return d.step(a)}var d=this,e=f.fx,g;this.startTime=co||cq(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,h.elem=this.elem,h()&&f.timers.push(h)&&!cm&&(cp?(cm=1,g=function(){cm&&(cp(g),e.tick())},cp(g)):cm=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=co||cq(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a=f.timers,b=a.length;while(b--)a[b]()||a.splice(b,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cm),cm=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cu=/^t(?:able|d|h)$/i,cv=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cw(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!cu.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<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>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cv.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cv.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cw(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cw(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){return this[0]?parseFloat(f.css(this[0],d,"padding")):null},f.fn["outer"+c]=function(a){return this[0]?parseFloat(f.css(this[0],d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file
diff --git a/devtools/client/inspector/markup/test/lib_jquery_1.7_min.js b/devtools/client/inspector/markup/test/lib_jquery_1.7_min.js
new file mode 100644
index 000000000..3ca5e0f5d
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_1.7_min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7 jquery.com | jquery.org/license */
+(function(a,b){function cA(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cx(a){if(!cm[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cn||(cn=c.createElement("iframe"),cn.frameBorder=cn.width=cn.height=0),b.appendChild(cn);if(!co||!cn.createElement)co=(cn.contentWindow||cn.contentDocument).document,co.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),co.close();d=co.createElement(a),co.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cn)}cm[a]=e}return cm[a]}function cw(a,b){var c={};f.each(cs.concat.apply([],cs.slice(0,b)),function(){c[this]=a});return c}function cv(){ct=b}function cu(){setTimeout(cv,0);return ct=f.now()}function cl(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ck(){try{return new a.XMLHttpRequest}catch(b){}}function ce(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cd(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function cc(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bG.test(a)?d(a,e):cc(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)cc(a+"["+e+"]",b[e],c,d);else d(a,b)}function cb(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function ca(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bV,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=ca(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=ca(a,c,d,e,"*",g));return l}function b_(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bR),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bE(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bz:bA;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bB(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function br(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bi,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bq(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bp(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bp)}function bp(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bo(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bn(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bm(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bl(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function X(a){var b=Y.split(" "),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function W(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(R.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(){return!0}function M(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function K(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(K,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.add(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;B.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return a!=null&&m.test(a)&&!isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:H?function(a){return a==null?"":H.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?F.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(I)return I.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=G.call(arguments,2),g=function(){return a.apply(c,f.concat(G.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){J["[object "+b+"]"]=b.toLowerCase()}),A=e.uaMatch(z),A.browser&&(e.browser[A.browser]=!0,e.browser.version=A.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?C=function(){c.removeEventListener("DOMContentLoaded",C,!1),e.ready()}:c.attachEvent&&(C=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",C),e.ready())}),typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return e});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){return i.done.apply(i,arguments).fail.apply(i,arguments)},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/><nav></nav>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,unknownElems:!!a.getElementsByTagName("nav").length,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",enctype:!!c.createElement("form").enctype,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.lastChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-999px",top:"-999px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;f(function(){var a,b,d,e,g,h,i=1,j="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",l="visibility:hidden;border:0;",n="style='"+j+"border:5px solid #000;padding:0;'",p="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>";m=c.getElementsByTagName("body")[0];!m||(a=c.createElement("div"),a.style.cssText=l+"width:0;height:0;position:static;top:0;margin-top:"+i+"px",m.insertBefore(a,m.firstChild),o=c.createElement("div"),o.style.cssText=j+l,o.innerHTML=p,a.appendChild(o),b=o.firstChild,d=b.firstChild,g=b.nextSibling.firstChild.firstChild,h={doesNotAddBorder:d.offsetTop!==5,doesAddBorderForTableAndCells:g.offsetTop===5},d.style.position="fixed",d.style.top="20px",h.fixedPosition=d.offsetTop===20||d.offsetTop===15,d.style.position=d.style.top="",b.style.overflow="hidden",b.style.position="relative",h.subtractsBorderForOverflowNotVisible=d.offsetTop===-5,h.doesNotIncludeMarginInBodyOffset=m.offsetTop!==i,m.removeChild(a),o=a=null,f.extend(k,h))}),o.innerHTML="",n.removeChild(o),o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[f.expando]:a[f.expando]&&f.expando,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[f.expando]=n=++f.uuid:n=f.expando),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[f.expando]:f.expando;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)?b=b:b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" "));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[f.expando]:a.removeAttribute?a.removeAttribute(f.expando):a[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];if(!arguments.length){if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}return b}e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!a||j===3||j===8||j===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g},removeAttr:function(a,b){var c,d,e,g,h=0;if(a.nodeType===1){d=(b||"").split(p),g=d.length;for(;h<g;h++)e=d[h].toLowerCase(),c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1)}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return b;h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/\.(.*)$/,A=/^(?:textarea|input|select)$/i,B=/\./g,C=/ /g,D=/[^\w\s.|`]/g,E=/^([^\.]*)?(?:\.(.+))?$/,F=/\bhover(\.\S+)?/,G=/^key/,H=/^(?:mouse|contextmenu)|click/,I=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,J=function(a){var b=I.exec(a);b&&
+(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},K=function(a,b){return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||a.id===b[2])&&(!b[3]||b[3].test(a.className))},L=function(a){return f.event.special.hover?a:a.replace(F,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=L(c).split(" ");for(k=0;k<c.length;k++){l=E.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,namespace:n.join(".")},p),g&&(o.quick=J(g),!o.quick&&f.expr.match.POS.test(g)&&(o.isPositional=!0)),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d){var e=f.hasData(a)&&f._data(a),g,h,i,j,k,l,m,n,o,p,q;if(!!e&&!!(m=e.events)){b=L(b||"").split(" ");for(g=0;g<b.length;g++){h=E.exec(b[g])||[],i=h[1],j=h[2];if(!i){j=j?"."+j:"";for(l in m)f.event.remove(a,l+j,c,d);return}n=f.event.special[i]||{},i=(d?n.delegateType:n.bindType)||i,p=m[i]||[],k=p.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;if(c||j||d||n.remove)for(l=0;l<p.length;l++){q=p[l];if(!c||c.guid===q.guid)if(!j||j.test(q.namespace))if(!d||d===q.selector||d==="**"&&q.selector)p.splice(l--,1),q.selector&&p.delegateCount--,n.remove&&n.remove.call(a,q)}else p.length=0;p.length===0&&k!==p.length&&((!n.teardown||n.teardown.call(a,j)===!1)&&f.removeEvent(a,i,e.handle),delete m[i])}f.isEmptyObject(m)&&(o=e.handle,o&&(o.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"",(g||!e)&&c.preventDefault();if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,n=null;for(m=e.parentNode;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length;l++){m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d);if(c.isPropagationStopped())break}c.type=h,c.isDefaultPrevented()||(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=(f.event.special[c.type]||{}).handle,j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click"))for(m=c.target;m!=this;m=m.parentNode||this){o={},q=[];for(k=0;k<e;k++)r=d[k],s=r.selector,t=o[s],r.isPositional?t=(t||(o[s]=f(s))).index(m)>=0:t===b&&(t=o[s]=r.quick?K(m,r.quick):f(m).is(s)),t&&q.push(r);q.length&&j.push({elem:m,matches:q})}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){p=j[k],c.currentTarget=p.elem;for(l=0;l<p.matches.length&&!c.isImmediatePropagationStopped();l++){r=p.matches[l];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=(i||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement wheelDelta".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},focus:{delegateType:"focusin",noBubble:!0},blur:{delegateType:"focusout",noBubble:!0},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?N:M):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=N;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=N;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=N,this.stopPropagation()},isDefaultPrevented:M,isPropagationStopped:M,isImmediatePropagationStopped:M},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]=f.event.special[b]={delegateType:b,bindType:b,handle:function(a){var b=this,c=a.relatedTarget,d=a.handleObj,e=d.selector,g,h;if(!c||d.origType===a.type||c!==b&&!f.contains(b,c))g=a.type,a.type=d.origType,h=d.handler.apply(this,arguments),a.type=g;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(A.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;A.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return A.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=M;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=M);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),G.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),H.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw"Syntax error, unrecognized expression: "+a};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var O=/Until$/,P=/^(?:parents|prevUntil|prevAll)/,Q=/,/,R=/^.[^:#\[\.,]*$/,S=Array.prototype.slice,T=f.expr.match.POS,U={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(W(this,a,!1),"not",a)},filter:function(a){return this.pushStack(W(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?T.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",Z=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,_=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,ba=/<([\w:]+)/,bb=/<tbody/i,bc=/<|&#?\w+;/,bd=/<(?:script|style)/i,be=/<(?:script|object|embed|option|style)/i,bf=new RegExp("<(?:"+Y.replace(" ","|")+")","i"),bg=/checked\s*(?:[^=]|=\s*.checked.)/i,bh=/\/(java|ecma)script/i,bi=/^\s*<!(?:\[CDATA\[|\-\-)/,bj={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bk=X(c);bj.optgroup=bj.option,bj.tbody=bj.tfoot=bj.colgroup=bj.caption=bj.thead,bj.th=bj.td,f.support.htmlSerialize||(bj._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after"
+,arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Z,""):null;if(typeof a=="string"&&!bd.test(a)&&(f.support.leadingWhitespace||!$.test(a))&&!bj[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(_,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bg.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bl(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,br)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!be.test(j)&&(f.support.checkClone||!bg.test(j))&&!f.support.unknownElems&&bf.test(j)&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bn(a,d),e=bo(a),g=bo(d);for(h=0;e[h];++h)g[h]&&bn(e[h],g[h])}if(b){bm(a,d);if(c){e=bo(a),g=bo(d);for(h=0;e[h];++h)bm(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bc.test(k))k=b.createTextNode(k);else{k=k.replace(_,"<$1></$2>");var l=(ba.exec(k)||["",""])[1].toLowerCase(),m=bj[l]||bj._default,n=m[0],o=b.createElement("div");b===c?bk.appendChild(o):X(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=bb.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&$.test(k)&&o.insertBefore(b.createTextNode($.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bq(k[i]);else bq(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bh.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bs=/alpha\([^)]*\)/i,bt=/opacity=([^)]*)/,bu=/([A-Z]|^ms)/g,bv=/^-?\d+(?:px)?$/i,bw=/^-?\d/,bx=/^([\-+])=([\-+.\de]+)/,by={position:"absolute",visibility:"hidden",display:"block"},bz=["Left","Right"],bA=["Top","Bottom"],bB,bC,bD;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bB(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bx.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bB)return bB(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bE(a,b,d);f.swap(a,by,function(){e=bE(a,b,d)});return e}},set:function(a,b){if(!bv.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bt.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bs,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bs.test(g)?g.replace(bs,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bB(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bC=function(a,c){var d,e,g;c=c.replace(bu,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bD=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bv.test(f)&&bw.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bB=bC||bD,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bF=/%20/g,bG=/\[\]$/,bH=/\r?\n/g,bI=/#.*$/,bJ=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bK=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bL=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bM=/^(?:GET|HEAD)$/,bN=/^\/\//,bO=/\?/,bP=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bQ=/^(?:select|textarea)/i,bR=/\s+/,bS=/([?&])_=[^&]*/,bT=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bU=f.fn.load,bV={},bW={},bX,bY,bZ=["*/"]+["*"];try{bX=e.href}catch(b$){bX=c.createElement("a"),bX.href="",bX=bX.href}bY=bT.exec(bX.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bU)return bU.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bP,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bQ.test(this.nodeName)||bK.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bH,"\r\n")}}):{name:b.name,value:c.replace(bH,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?cb(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),cb(a,b);return a},ajaxSettings:{url:bX,isLocal:bL.test(bY[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bZ},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:b_(bV),ajaxTransport:b_(bW),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cd(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=ce(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bJ.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bI,"").replace(bN,bY[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bR),d.crossDomain==null&&(r=bT.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bY[1]&&r[2]==bY[2]&&(r[3]||(r[1]==="http:"?80:443))==(bY[3]||(bY[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),ca(bV,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bM.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bO.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bS,"$1_="+x);d.url=y+(y===d.url?(bO.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bZ+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=ca(bW,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)cc(g,a[g],c,e);return d.join("&").replace(bF,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cf=f.now(),cg=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cf++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cg.test(b.url)||e&&cg.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cg,l),b.url===j&&(e&&(k=k.replace(cg,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ch=a.ActiveXObject?function(){for(var a in cj)cj[a](0,1)}:!1,ci=0,cj;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ck()||cl()}:ck,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ch&&delete cj[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++ci,ch&&(cj||(cj={},f(a).unload(ch)),cj[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cm={},cn,co,cp=/^(?:toggle|show|hide)$/,cq=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cr,cs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],ct;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cw("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cx(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cw("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cw("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cx(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cp.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=cq.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cw("show",1),slideUp:cw("hide",1),slideToggle:cw("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=ct||cu(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cr&&(cr=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=ct||cu(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cr),cr=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now))}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cy=/^t(?:able|d|h)$/i,cz=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cA(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cy.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cz.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cz.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cA(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cA(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file
diff --git a/devtools/client/inspector/markup/test/lib_jquery_2.1.1_min.js b/devtools/client/inspector/markup/test/lib_jquery_2.1.1_min.js
new file mode 100644
index 000000000..e5ace116b
--- /dev/null
+++ b/devtools/client/inspector/markup/test/lib_jquery_2.1.1_min.js
@@ -0,0 +1,4 @@
+/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)
+},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ab=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ib={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),Lb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Mb||(Mb=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Mb),Mb=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Yb,Zb,$b=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
+},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec,fc,gc=/#.*$/,hc=/([?&])_=[^&]*/,ic=/^(.*?):[ \t]*([^\r\n]*)$/gm,jc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,kc=/^(?:GET|HEAD)$/,lc=/^\/\//,mc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,nc={},oc={},pc="*/".concat("*");try{fc=location.href}catch(qc){fc=l.createElement("a"),fc.href="",fc=fc.href}ec=mc.exec(fc.toLowerCase())||[];function rc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function sc(a,b,c,d){var e={},f=a===oc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function tc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function uc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function vc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:fc,type:"GET",isLocal:jc.test(ec[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":pc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?tc(tc(a,n.ajaxSettings),b):tc(n.ajaxSettings,a)},ajaxPrefilter:rc(nc),ajaxTransport:rc(oc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=ic.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||fc)+"").replace(gc,"").replace(lc,ec[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=mc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===ec[1]&&h[2]===ec[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(ec[3]||("http:"===ec[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),sc(nc,k,b,v),2===t)return v;i=k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!kc.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=hc.test(d)?d.replace(hc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+pc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=sc(oc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=uc(k,v,f)),u=vc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var wc=/%20/g,xc=/\[\]$/,yc=/\r?\n/g,zc=/^(?:submit|button|image|reset|file)$/i,Ac=/^(?:input|select|textarea|keygen)/i;function Bc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||xc.test(a)?d(a,e):Bc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Bc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Bc(c,a[c],b,e);return d.join("&").replace(wc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Ac.test(this.nodeName)&&!zc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(yc,"\r\n")}}):{name:b.name,value:c.replace(yc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Cc=0,Dc={},Ec={0:200,1223:204},Fc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Dc)Dc[a]()}),k.cors=!!Fc&&"withCredentials"in Fc,k.ajax=Fc=!!Fc,n.ajaxTransport(function(a){var b;return k.cors||Fc&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Cc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Dc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Ec[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Dc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Gc=[],Hc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Gc.pop()||n.expando+"_"+cc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Hc.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Hc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Hc,"$1"+e):b.jsonp!==!1&&(b.url+=(dc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Gc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Ic=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Ic)return Ic.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Jc=a.document.documentElement;function Kc(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Kc(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Jc;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Jc})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Kc(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=yb(k.pixelPosition,function(a,c){return c?(c=xb(a,b),vb.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Lc=a.jQuery,Mc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Mc),b&&a.jQuery===n&&(a.jQuery=Lc),n},typeof b===U&&(a.jQuery=a.$=n),n});
diff --git a/devtools/client/inspector/markup/utils.js b/devtools/client/inspector/markup/utils.js
new file mode 100644
index 000000000..8fab9d963
--- /dev/null
+++ b/devtools/client/inspector/markup/utils.js
@@ -0,0 +1,135 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Apply a 'flashed' background and foreground color to elements. Intended
+ * to be used with flashElementOff as a way of drawing attention to an element.
+ *
+ * @param {Node} backgroundElt
+ * The element to set the highlighted background color on.
+ * @param {Node} foregroundElt
+ * The element to set the matching foreground color on.
+ * Optional. This will equal backgroundElt if not set.
+ */
+function flashElementOn(backgroundElt, foregroundElt = backgroundElt) {
+ if (!backgroundElt || !foregroundElt) {
+ return;
+ }
+
+ // Make sure the animation class is not here
+ backgroundElt.classList.remove("flash-out");
+
+ // Change the background
+ backgroundElt.classList.add("theme-bg-contrast");
+
+ foregroundElt.classList.add("theme-fg-contrast");
+ [].forEach.call(
+ foregroundElt.querySelectorAll("[class*=theme-fg-color]"),
+ span => span.classList.add("theme-fg-contrast")
+ );
+}
+
+/**
+ * Remove a 'flashed' background and foreground color to elements.
+ * See flashElementOn.
+ *
+ * @param {Node} backgroundElt
+ * The element to reomve the highlighted background color on.
+ * @param {Node} foregroundElt
+ * The element to remove the matching foreground color on.
+ * Optional. This will equal backgroundElt if not set.
+ */
+function flashElementOff(backgroundElt, foregroundElt = backgroundElt) {
+ if (!backgroundElt || !foregroundElt) {
+ return;
+ }
+
+ // Add the animation class to smoothly remove the background
+ backgroundElt.classList.add("flash-out");
+
+ // Remove the background
+ backgroundElt.classList.remove("theme-bg-contrast");
+
+ foregroundElt.classList.remove("theme-fg-contrast");
+ [].forEach.call(
+ foregroundElt.querySelectorAll("[class*=theme-fg-color]"),
+ span => span.classList.remove("theme-fg-contrast")
+ );
+}
+
+/**
+ * Retrieve the available width between a provided element left edge and a container right
+ * edge. This used can be used as a max-width for inplace-editor (autocomplete) widgets
+ * replacing Editor elements of the the markup-view;
+ */
+function getAutocompleteMaxWidth(element, container) {
+ let elementRect = element.getBoundingClientRect();
+ let containerRect = container.getBoundingClientRect();
+ return containerRect.right - elementRect.left - 2;
+}
+
+/**
+ * Parse attribute names and values from a string.
+ *
+ * @param {String} attr
+ * The input string for which names/values are to be parsed.
+ * @param {HTMLDocument} doc
+ * A document that can be used to test valid attributes.
+ * @return {Array}
+ * An array of attribute names and their values.
+ */
+function parseAttributeValues(attr, doc) {
+ attr = attr.trim();
+
+ let parseAndGetNode = str => {
+ return new DOMParser().parseFromString(str, "text/html").body.childNodes[0];
+ };
+
+ // Handle bad user inputs by appending a " or ' if it fails to parse without
+ // them. Also note that a SVG tag is used to make sure the HTML parser
+ // preserves mixed-case attributes
+ let el = parseAndGetNode("<svg " + attr + "></svg>") ||
+ parseAndGetNode("<svg " + attr + "\"></svg>") ||
+ parseAndGetNode("<svg " + attr + "'></svg>");
+
+ let div = doc.createElement("div");
+ let attributes = [];
+ for (let {name, value} of el.attributes) {
+ // Try to set on an element in the document, throws exception on bad input.
+ // Prevents InvalidCharacterError - "String contains an invalid character".
+ try {
+ div.setAttribute(name, value);
+ attributes.push({ name, value });
+ } catch (e) {
+ // This may throw exceptions on bad input.
+ // Prevents InvalidCharacterError - "String contains an invalid
+ // character".
+ }
+ }
+
+ return attributes;
+}
+
+/**
+ * Truncate the string and add ellipsis to the middle of the string.
+ */
+function truncateString(str, maxLength) {
+ if (!str || str.length <= maxLength) {
+ return str;
+ }
+
+ return str.substring(0, Math.ceil(maxLength / 2)) +
+ "…" +
+ str.substring(str.length - Math.floor(maxLength / 2));
+}
+
+module.exports = {
+ flashElementOn,
+ flashElementOff,
+ getAutocompleteMaxWidth,
+ parseAttributeValues,
+ truncateString,
+};
diff --git a/devtools/client/inspector/markup/views/element-container.js b/devtools/client/inspector/markup/views/element-container.js
new file mode 100644
index 000000000..851a803cb
--- /dev/null
+++ b/devtools/client/inspector/markup/views/element-container.js
@@ -0,0 +1,193 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const PREVIEW_MAX_DIM_PREF = "devtools.inspector.imagePreviewTooltipSize";
+
+const promise = require("promise");
+const Services = require("Services");
+const Heritage = require("sdk/core/heritage");
+const {Task} = require("devtools/shared/task");
+const nodeConstants = require("devtools/shared/dom-node-constants");
+const clipboardHelper = require("devtools/shared/platform/clipboard");
+const {setImageTooltip, setBrokenImageTooltip} =
+ require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
+const {setEventTooltip} = require("devtools/client/shared/widgets/tooltip/EventTooltipHelper");
+const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
+const ElementEditor = require("devtools/client/inspector/markup/views/element-editor");
+
+/**
+ * An implementation of MarkupContainer for Elements that can contain
+ * child nodes.
+ * Allows editing of tag name, attributes, expanding / collapsing.
+ *
+ * @param {MarkupView} markupView
+ * The markup view that owns this container.
+ * @param {NodeFront} node
+ * The node to display.
+ */
+function MarkupElementContainer(markupView, node) {
+ MarkupContainer.prototype.initialize.call(this, markupView, node,
+ "elementcontainer");
+
+ if (node.nodeType === nodeConstants.ELEMENT_NODE) {
+ this.editor = new ElementEditor(this, node);
+ } else {
+ throw new Error("Invalid node for MarkupElementContainer");
+ }
+
+ this.tagLine.appendChild(this.editor.elt);
+}
+
+MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
+ _buildEventTooltipContent: Task.async(function* (target, tooltip) {
+ if (target.hasAttribute("data-event")) {
+ yield tooltip.hide();
+
+ let listenerInfo = yield this.node.getEventListenerInfo();
+
+ let toolbox = this.markup.toolbox;
+ setEventTooltip(tooltip, listenerInfo, toolbox);
+ // Disable the image preview tooltip while we display the event details
+ this.markup._disableImagePreviewTooltip();
+ tooltip.once("hidden", () => {
+ // Enable the image preview tooltip after closing the event details
+ this.markup._enableImagePreviewTooltip();
+ });
+ tooltip.show(target);
+ }
+ }),
+
+ /**
+ * Generates the an image preview for this Element. The element must be an
+ * image or canvas (@see isPreviewable).
+ *
+ * @return {Promise} that is resolved with an object of form
+ * { data, size: { naturalWidth, naturalHeight, resizeRatio } } where
+ * - data is the data-uri for the image preview.
+ * - size contains information about the original image size and if
+ * the preview has been resized.
+ *
+ * If this element is not previewable or the preview cannot be generated for
+ * some reason, the Promise is rejected.
+ */
+ _getPreview: function () {
+ if (!this.isPreviewable()) {
+ return promise.reject("_getPreview called on a non-previewable element.");
+ }
+
+ if (this.tooltipDataPromise) {
+ // A preview request is already pending. Re-use that request.
+ return this.tooltipDataPromise;
+ }
+
+ // Fetch the preview from the server.
+ this.tooltipDataPromise = Task.spawn(function* () {
+ let maxDim = Services.prefs.getIntPref(PREVIEW_MAX_DIM_PREF);
+ let preview = yield this.node.getImageData(maxDim);
+ let data = yield preview.data.string();
+
+ // Clear the pending preview request. We can't reuse the results later as
+ // the preview contents might have changed.
+ this.tooltipDataPromise = null;
+ return { data, size: preview.size };
+ }.bind(this));
+
+ return this.tooltipDataPromise;
+ },
+
+ /**
+ * Executed by MarkupView._isImagePreviewTarget which is itself called when
+ * the mouse hovers over a target in the markup-view.
+ * Checks if the target is indeed something we want to have an image tooltip
+ * preview over and, if so, inserts content into the tooltip.
+ *
+ * @return {Promise} that resolves when the tooltip content is ready. Resolves
+ * true if the tooltip should be displayed, false otherwise.
+ */
+ isImagePreviewTarget: Task.async(function* (target, tooltip) {
+ // Is this Element previewable.
+ if (!this.isPreviewable()) {
+ return false;
+ }
+
+ // If the Element has an src attribute, the tooltip is shown when hovering
+ // over the src url. If not, the tooltip is shown when hovering over the tag
+ // name.
+ let src = this.editor.getAttributeElement("src");
+ let expectedTarget = src ? src.querySelector(".link") : this.editor.tag;
+ if (target !== expectedTarget) {
+ return false;
+ }
+
+ try {
+ let { data, size } = yield this._getPreview();
+ // The preview is ready.
+ let options = {
+ naturalWidth: size.naturalWidth,
+ naturalHeight: size.naturalHeight,
+ maxDim: Services.prefs.getIntPref(PREVIEW_MAX_DIM_PREF)
+ };
+
+ setImageTooltip(tooltip, this.markup.doc, data, options);
+ } catch (e) {
+ // Indicate the failure but show the tooltip anyway.
+ setBrokenImageTooltip(tooltip, this.markup.doc);
+ }
+ return true;
+ }),
+
+ copyImageDataUri: function () {
+ // We need to send again a request to gettooltipData even if one was sent
+ // for the tooltip, because we want the full-size image
+ this.node.getImageData().then(data => {
+ data.data.string().then(str => {
+ clipboardHelper.copyString(str);
+ });
+ });
+ },
+
+ setInlineTextChild: function (inlineTextChild) {
+ this.inlineTextChild = inlineTextChild;
+ this.editor.updateTextEditor();
+ },
+
+ clearInlineTextChild: function () {
+ this.inlineTextChild = undefined;
+ this.editor.updateTextEditor();
+ },
+
+ /**
+ * Trigger new attribute field for input.
+ */
+ addAttribute: function () {
+ this.editor.newAttr.editMode();
+ },
+
+ /**
+ * Trigger attribute field for editing.
+ */
+ editAttribute: function (attrName) {
+ this.editor.attrElements.get(attrName).editMode();
+ },
+
+ /**
+ * Remove attribute from container.
+ * This is an undoable action.
+ */
+ removeAttribute: function (attrName) {
+ let doMods = this.editor._startModifyingAttributes();
+ let undoMods = this.editor._startModifyingAttributes();
+ this.editor._saveAttribute(attrName, undoMods);
+ doMods.removeAttribute(attrName);
+ this.undo.do(() => {
+ doMods.apply();
+ }, () => {
+ undoMods.apply();
+ });
+ }
+});
+
+module.exports = MarkupElementContainer;
diff --git a/devtools/client/inspector/markup/views/element-editor.js b/devtools/client/inspector/markup/views/element-editor.js
new file mode 100644
index 000000000..3149086eb
--- /dev/null
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -0,0 +1,560 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Services = require("Services");
+const TextEditor = require("devtools/client/inspector/markup/views/text-editor");
+const {
+ getAutocompleteMaxWidth,
+ flashElementOn,
+ flashElementOff,
+ parseAttributeValues,
+ truncateString,
+} = require("devtools/client/inspector/markup/utils");
+const {editableField, InplaceEditor} =
+ require("devtools/client/shared/inplace-editor");
+const {parseAttribute} =
+ require("devtools/client/shared/node-attribute-parser");
+const {getCssProperties} = require("devtools/shared/fronts/css-properties");
+
+// Page size for pageup/pagedown
+const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
+const COLLAPSE_DATA_URL_LENGTH = 60;
+
+// Contains only void (without end tag) HTML elements
+const HTML_VOID_ELEMENTS = [ "area", "base", "br", "col", "command", "embed",
+ "hr", "img", "input", "keygen", "link", "meta", "param", "source",
+ "track", "wbr" ];
+
+/**
+ * Creates an editor for an Element node.
+ *
+ * @param {MarkupContainer} container
+ * The container owning this editor.
+ * @param {Element} node
+ * The node being edited.
+ */
+function ElementEditor(container, node) {
+ this.container = container;
+ this.node = node;
+ this.markup = this.container.markup;
+ this.template = this.markup.template.bind(this.markup);
+ this.doc = this.markup.doc;
+ this._cssProperties = getCssProperties(this.markup.toolbox);
+
+ this.attrElements = new Map();
+ this.animationTimers = {};
+
+ // The templates will fill the following properties
+ this.elt = null;
+ this.tag = null;
+ this.closeTag = null;
+ this.attrList = null;
+ this.newAttr = null;
+ this.closeElt = null;
+
+ // Create the main editor
+ this.template("element", this);
+
+ // Make the tag name editable (unless this is a remote node or
+ // a document element)
+ if (!node.isDocumentElement) {
+ // Make the tag optionally tabbable but not by default.
+ this.tag.setAttribute("tabindex", "-1");
+ editableField({
+ element: this.tag,
+ multiline: true,
+ maxWidth: () => getAutocompleteMaxWidth(this.tag, this.container.elt),
+ trigger: "dblclick",
+ stopOnReturn: true,
+ done: this.onTagEdit.bind(this),
+ contextMenu: this.markup.inspector.onTextBoxContextMenu,
+ cssProperties: this._cssProperties
+ });
+ }
+
+ // Make the new attribute space editable.
+ this.newAttr.editMode = editableField({
+ element: this.newAttr,
+ multiline: true,
+ maxWidth: () => getAutocompleteMaxWidth(this.newAttr, this.container.elt),
+ trigger: "dblclick",
+ stopOnReturn: true,
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
+ popup: this.markup.popup,
+ done: (val, commit) => {
+ if (!commit) {
+ return;
+ }
+
+ let doMods = this._startModifyingAttributes();
+ let undoMods = this._startModifyingAttributes();
+ this._applyAttributes(val, null, doMods, undoMods);
+ this.container.undo.do(() => {
+ doMods.apply();
+ }, function () {
+ undoMods.apply();
+ });
+ },
+ contextMenu: this.markup.inspector.onTextBoxContextMenu,
+ cssProperties: this._cssProperties
+ });
+
+ let displayName = this.node.displayName;
+ this.tag.textContent = displayName;
+ this.closeTag.textContent = displayName;
+
+ let isVoidElement = HTML_VOID_ELEMENTS.includes(displayName);
+ if (node.isInHTMLDocument && isVoidElement) {
+ this.elt.classList.add("void-element");
+ }
+
+ this.update();
+ this.initialized = true;
+}
+
+ElementEditor.prototype = {
+ set selected(value) {
+ if (this.textEditor) {
+ this.textEditor.selected = value;
+ }
+ },
+
+ flashAttribute: function (attrName) {
+ if (this.animationTimers[attrName]) {
+ clearTimeout(this.animationTimers[attrName]);
+ }
+
+ flashElementOn(this.getAttributeElement(attrName));
+
+ this.animationTimers[attrName] = setTimeout(() => {
+ flashElementOff(this.getAttributeElement(attrName));
+ }, this.markup.CONTAINER_FLASHING_DURATION);
+ },
+
+ /**
+ * Returns information about node in the editor.
+ *
+ * @param {DOMNode} node
+ * The node to get information from.
+ * @return {Object} An object literal with the following information:
+ * {type: "attribute", name: "rel", value: "index", el: node}
+ */
+ getInfoAtNode: function (node) {
+ if (!node) {
+ return null;
+ }
+
+ let type = null;
+ let name = null;
+ let value = null;
+
+ // Attribute
+ let attribute = node.closest(".attreditor");
+ if (attribute) {
+ type = "attribute";
+ name = attribute.querySelector(".attr-name").textContent;
+ value = attribute.querySelector(".attr-value").textContent;
+ }
+
+ return {type, name, value, el: node};
+ },
+
+ /**
+ * Update the state of the editor from the node.
+ */
+ update: function () {
+ let nodeAttributes = this.node.attributes || [];
+
+ // Keep the data model in sync with attributes on the node.
+ let currentAttributes = new Set(nodeAttributes.map(a => a.name));
+ for (let name of this.attrElements.keys()) {
+ if (!currentAttributes.has(name)) {
+ this.removeAttribute(name);
+ }
+ }
+
+ // Only loop through the current attributes on the node. Missing
+ // attributes have already been removed at this point.
+ for (let attr of nodeAttributes) {
+ let el = this.attrElements.get(attr.name);
+ let valueChanged = el &&
+ el.dataset.value !== attr.value;
+ let isEditing = el && el.querySelector(".editable").inplaceEditor;
+ let canSimplyShowEditor = el && (!valueChanged || isEditing);
+
+ if (canSimplyShowEditor) {
+ // Element already exists and doesn't need to be recreated.
+ // Just show it (it's hidden by default due to the template).
+ el.style.removeProperty("display");
+ } else {
+ // Create a new editor, because the value of an existing attribute
+ // has changed.
+ let attribute = this._createAttribute(attr, el);
+ attribute.style.removeProperty("display");
+
+ // Temporarily flash the attribute to highlight the change.
+ // But not if this is the first time the editor instance has
+ // been created.
+ if (this.initialized) {
+ this.flashAttribute(attr.name);
+ }
+ }
+ }
+
+ // Update the event bubble display
+ this.eventNode.style.display = this.node.hasEventListeners ?
+ "inline-block" : "none";
+
+ this.updateTextEditor();
+ },
+
+ /**
+ * Update the inline text editor in case of a single text child node.
+ */
+ updateTextEditor: function () {
+ let node = this.node.inlineTextChild;
+
+ if (this.textEditor && this.textEditor.node != node) {
+ this.elt.removeChild(this.textEditor.elt);
+ this.textEditor = null;
+ }
+
+ if (node && !this.textEditor) {
+ // Create a text editor added to this editor.
+ // This editor won't receive an update automatically, so we rely on
+ // child text editors to let us know that we need updating.
+ this.textEditor = new TextEditor(this.container, node, "text");
+ this.elt.insertBefore(this.textEditor.elt,
+ this.elt.firstChild.nextSibling.nextSibling);
+ }
+
+ if (this.textEditor) {
+ this.textEditor.update();
+ }
+ },
+
+ _startModifyingAttributes: function () {
+ return this.node.startModifyingAttributes();
+ },
+
+ /**
+ * Get the element used for one of the attributes of this element.
+ *
+ * @param {String} attrName
+ * The name of the attribute to get the element for
+ * @return {DOMNode}
+ */
+ getAttributeElement: function (attrName) {
+ return this.attrList.querySelector(
+ ".attreditor[data-attr=" + CSS.escape(attrName) + "] .attr-value");
+ },
+
+ /**
+ * Remove an attribute from the attrElements object and the DOM.
+ *
+ * @param {String} attrName
+ * The name of the attribute to remove
+ */
+ removeAttribute: function (attrName) {
+ let attr = this.attrElements.get(attrName);
+ if (attr) {
+ this.attrElements.delete(attrName);
+ attr.remove();
+ }
+ },
+
+ _createAttribute: function (attribute, before = null) {
+ // Create the template editor, which will save some variables here.
+ let data = {
+ attrName: attribute.name,
+ attrValue: attribute.value,
+ tabindex: this.container.canFocus ? "0" : "-1",
+ };
+ this.template("attribute", data);
+ let {attr, inner, name, val} = data;
+
+ // Double quotes need to be handled specially to prevent DOMParser failing.
+ // name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
+ // name="v'a"l'u"e" when editing -> name="v'a&quot;l'u&quot;e"
+ let editValueDisplayed = attribute.value || "";
+ let hasDoubleQuote = editValueDisplayed.includes('"');
+ let hasSingleQuote = editValueDisplayed.includes("'");
+ let initial = attribute.name + '="' + editValueDisplayed + '"';
+
+ // Can't just wrap value with ' since the value contains both " and '.
+ if (hasDoubleQuote && hasSingleQuote) {
+ editValueDisplayed = editValueDisplayed.replace(/\"/g, "&quot;");
+ initial = attribute.name + '="' + editValueDisplayed + '"';
+ }
+
+ // Wrap with ' since there are no single quotes in the attribute value.
+ if (hasDoubleQuote && !hasSingleQuote) {
+ initial = attribute.name + "='" + editValueDisplayed + "'";
+ }
+
+ // Make the attribute editable.
+ attr.editMode = editableField({
+ element: inner,
+ trigger: "dblclick",
+ stopOnReturn: true,
+ selectAll: false,
+ initial: initial,
+ multiline: true,
+ maxWidth: () => getAutocompleteMaxWidth(inner, this.container.elt),
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
+ popup: this.markup.popup,
+ start: (editor, event) => {
+ // If the editing was started inside the name or value areas,
+ // select accordingly.
+ if (event && event.target === name) {
+ editor.input.setSelectionRange(0, name.textContent.length);
+ } else if (event && event.target.closest(".attr-value") === val) {
+ let length = editValueDisplayed.length;
+ let editorLength = editor.input.value.length;
+ let start = editorLength - (length + 1);
+ editor.input.setSelectionRange(start, start + length);
+ } else {
+ editor.input.select();
+ }
+ },
+ done: (newValue, commit, direction) => {
+ if (!commit || newValue === initial) {
+ return;
+ }
+
+ let doMods = this._startModifyingAttributes();
+ let undoMods = this._startModifyingAttributes();
+
+ // Remove the attribute stored in this editor and re-add any attributes
+ // parsed out of the input element. Restore original attribute if
+ // parsing fails.
+ this.refocusOnEdit(attribute.name, attr, direction);
+ this._saveAttribute(attribute.name, undoMods);
+ doMods.removeAttribute(attribute.name);
+ this._applyAttributes(newValue, attr, doMods, undoMods);
+ this.container.undo.do(() => {
+ doMods.apply();
+ }, () => {
+ undoMods.apply();
+ });
+ },
+ contextMenu: this.markup.inspector.onTextBoxContextMenu,
+ cssProperties: this._cssProperties
+ });
+
+ // Figure out where we should place the attribute.
+ if (attribute.name == "id") {
+ before = this.attrList.firstChild;
+ } else if (attribute.name == "class") {
+ let idNode = this.attrElements.get("id");
+ before = idNode ? idNode.nextSibling : this.attrList.firstChild;
+ }
+ this.attrList.insertBefore(attr, before);
+
+ this.removeAttribute(attribute.name);
+ this.attrElements.set(attribute.name, attr);
+
+ // Parse the attribute value to detect whether there are linkable parts in
+ // it (make sure to pass a complete list of existing attributes to the
+ // parseAttribute function, by concatenating attribute, because this could
+ // be a newly added attribute not yet on this.node).
+ let attributes = this.node.attributes.filter(existingAttribute => {
+ return existingAttribute.name !== attribute.name;
+ });
+ attributes.push(attribute);
+ let parsedLinksData = parseAttribute(this.node.namespaceURI,
+ this.node.tagName, attributes, attribute.name);
+
+ // Create links in the attribute value, and collapse long attributes if
+ // needed.
+ let collapse = value => {
+ if (value && value.match(COLLAPSE_DATA_URL_REGEX)) {
+ return truncateString(value, COLLAPSE_DATA_URL_LENGTH);
+ }
+ return this.markup.collapseAttributes
+ ? truncateString(value, this.markup.collapseAttributeLength)
+ : value;
+ };
+
+ val.innerHTML = "";
+ for (let token of parsedLinksData) {
+ if (token.type === "string") {
+ val.appendChild(this.doc.createTextNode(collapse(token.value)));
+ } else {
+ let link = this.doc.createElement("span");
+ link.classList.add("link");
+ link.setAttribute("data-type", token.type);
+ link.setAttribute("data-link", token.value);
+ link.textContent = collapse(token.value);
+ val.appendChild(link);
+ }
+ }
+
+ name.textContent = attribute.name;
+
+ return attr;
+ },
+
+ /**
+ * Parse a user-entered attribute string and apply the resulting
+ * attributes to the node. This operation is undoable.
+ *
+ * @param {String} value
+ * The user-entered value.
+ * @param {DOMNode} attrNode
+ * The attribute editor that created this
+ * set of attributes, used to place new attributes where the
+ * user put them.
+ */
+ _applyAttributes: function (value, attrNode, doMods, undoMods) {
+ let attrs = parseAttributeValues(value, this.doc);
+ for (let attr of attrs) {
+ // Create an attribute editor next to the current attribute if needed.
+ this._createAttribute(attr, attrNode ? attrNode.nextSibling : null);
+ this._saveAttribute(attr.name, undoMods);
+ doMods.setAttribute(attr.name, attr.value);
+ }
+ },
+
+ /**
+ * Saves the current state of the given attribute into an attribute
+ * modification list.
+ */
+ _saveAttribute: function (name, undoMods) {
+ let node = this.node;
+ if (node.hasAttribute(name)) {
+ let oldValue = node.getAttribute(name);
+ undoMods.setAttribute(name, oldValue);
+ } else {
+ undoMods.removeAttribute(name);
+ }
+ },
+
+ /**
+ * Listen to mutations, and when the attribute list is regenerated
+ * try to focus on the attribute after the one that's being edited now.
+ * If the attribute order changes, go to the beginning of the attribute list.
+ */
+ refocusOnEdit: function (attrName, attrNode, direction) {
+ // Only allow one refocus on attribute change at a time, so when there's
+ // more than 1 request in parallel, the last one wins.
+ if (this._editedAttributeObserver) {
+ this.markup.inspector.off("markupmutation", this._editedAttributeObserver);
+ this._editedAttributeObserver = null;
+ }
+
+ let container = this.markup.getContainer(this.node);
+
+ let activeAttrs = [...this.attrList.childNodes]
+ .filter(el => el.style.display != "none");
+ let attributeIndex = activeAttrs.indexOf(attrNode);
+
+ let onMutations = this._editedAttributeObserver = (e, mutations) => {
+ let isDeletedAttribute = false;
+ let isNewAttribute = false;
+
+ for (let mutation of mutations) {
+ let inContainer =
+ this.markup.getContainer(mutation.target) === container;
+ if (!inContainer) {
+ continue;
+ }
+
+ let isOriginalAttribute = mutation.attributeName === attrName;
+
+ isDeletedAttribute = isDeletedAttribute || isOriginalAttribute &&
+ mutation.newValue === null;
+ isNewAttribute = isNewAttribute || mutation.attributeName !== attrName;
+ }
+
+ let isModifiedOrder = isDeletedAttribute && isNewAttribute;
+ this._editedAttributeObserver = null;
+
+ // "Deleted" attributes are merely hidden, so filter them out.
+ let visibleAttrs = [...this.attrList.childNodes]
+ .filter(el => el.style.display != "none");
+ let activeEditor;
+ if (visibleAttrs.length > 0) {
+ if (!direction) {
+ // No direction was given; stay on current attribute.
+ activeEditor = visibleAttrs[attributeIndex];
+ } else if (isModifiedOrder) {
+ // The attribute was renamed, reordering the existing attributes.
+ // So let's go to the beginning of the attribute list for consistency.
+ activeEditor = visibleAttrs[0];
+ } else {
+ let newAttributeIndex;
+ if (isDeletedAttribute) {
+ newAttributeIndex = attributeIndex;
+ } else if (direction == Services.focus.MOVEFOCUS_FORWARD) {
+ newAttributeIndex = attributeIndex + 1;
+ } else if (direction == Services.focus.MOVEFOCUS_BACKWARD) {
+ newAttributeIndex = attributeIndex - 1;
+ }
+
+ // The number of attributes changed (deleted), or we moved through
+ // the array so check we're still within bounds.
+ if (newAttributeIndex >= 0 &&
+ newAttributeIndex <= visibleAttrs.length - 1) {
+ activeEditor = visibleAttrs[newAttributeIndex];
+ }
+ }
+ }
+
+ // Either we have no attributes left,
+ // or we just edited the last attribute and want to move on.
+ if (!activeEditor) {
+ activeEditor = this.newAttr;
+ }
+
+ // Refocus was triggered by tab or shift-tab.
+ // Continue in edit mode.
+ if (direction) {
+ activeEditor.editMode();
+ } else {
+ // Refocus was triggered by enter.
+ // Exit edit mode (but restore focus).
+ let editable = activeEditor === this.newAttr ?
+ activeEditor : activeEditor.querySelector(".editable");
+ editable.focus();
+ }
+
+ this.markup.emit("refocusedonedit");
+ };
+
+ // Start listening for mutations until we find an attributes change
+ // that modifies this attribute.
+ this.markup.inspector.once("markupmutation", onMutations);
+ },
+
+ /**
+ * Called when the tag name editor has is done editing.
+ */
+ onTagEdit: function (newTagName, isCommit) {
+ if (!isCommit ||
+ newTagName.toLowerCase() === this.node.tagName.toLowerCase() ||
+ !("editTagName" in this.markup.walker)) {
+ return;
+ }
+
+ // Changing the tagName removes the node. Make sure the replacing node gets
+ // selected afterwards.
+ this.markup.reselectOnRemoved(this.node, "edittagname");
+ this.markup.walker.editTagName(this.node, newTagName).then(null, () => {
+ // Failed to edit the tag name, cancel the reselection.
+ this.markup.cancelReselectOnRemoved();
+ });
+ },
+
+ destroy: function () {
+ for (let key in this.animationTimers) {
+ clearTimeout(this.animationTimers[key]);
+ }
+ this.animationTimers = null;
+ }
+};
+
+module.exports = ElementEditor;
diff --git a/devtools/client/inspector/markup/views/html-editor.js b/devtools/client/inspector/markup/views/html-editor.js
new file mode 100644
index 000000000..6f99391b6
--- /dev/null
+++ b/devtools/client/inspector/markup/views/html-editor.js
@@ -0,0 +1,180 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Editor = require("devtools/client/sourceeditor/editor");
+const Services = require("Services");
+const EventEmitter = require("devtools/shared/event-emitter");
+
+/**
+ * A wrapper around the Editor component, that allows editing of HTML.
+ *
+ * The main functionality this provides around the Editor is the ability
+ * to show/hide/position an editor inplace. It only appends once to the
+ * body, and uses CSS to position the editor. The reason it is done this
+ * way is that the editor is loaded in an iframe, and calling appendChild
+ * causes it to reload.
+ *
+ * Meant to be embedded inside of an HTML page, as in markup.xhtml.
+ *
+ * @param {HTMLDocument} htmlDocument
+ * The document to attach the editor to. Will also use this
+ * document as a basis for listening resize events.
+ */
+function HTMLEditor(htmlDocument) {
+ this.doc = htmlDocument;
+ this.container = this.doc.createElement("div");
+ this.container.className = "html-editor theme-body";
+ this.container.style.display = "none";
+ this.editorInner = this.doc.createElement("div");
+ this.editorInner.className = "html-editor-inner";
+ this.container.appendChild(this.editorInner);
+
+ this.doc.body.appendChild(this.container);
+ this.hide = this.hide.bind(this);
+ this.refresh = this.refresh.bind(this);
+
+ EventEmitter.decorate(this);
+
+ this.doc.defaultView.addEventListener("resize",
+ this.refresh, true);
+
+ let config = {
+ mode: Editor.modes.html,
+ lineWrapping: true,
+ styleActiveLine: false,
+ extraKeys: {},
+ theme: "mozilla markup-view"
+ };
+
+ config.extraKeys[ctrl("Enter")] = this.hide;
+ config.extraKeys.F2 = this.hide;
+ config.extraKeys.Esc = this.hide.bind(this, false);
+
+ this.container.addEventListener("click", this.hide, false);
+ this.editorInner.addEventListener("click", stopPropagation, false);
+ this.editor = new Editor(config);
+
+ this.editor.appendToLocalElement(this.editorInner);
+ this.hide(false);
+}
+
+HTMLEditor.prototype = {
+
+ /**
+ * Need to refresh position by manually setting CSS values, so this will
+ * need to be called on resizes and other sizing changes.
+ */
+ refresh: function () {
+ let element = this._attachedElement;
+
+ if (element) {
+ this.container.style.top = element.offsetTop + "px";
+ this.container.style.left = element.offsetLeft + "px";
+ this.container.style.width = element.offsetWidth + "px";
+ this.container.style.height = element.parentNode.offsetHeight + "px";
+ this.editor.refresh();
+ }
+ },
+
+ /**
+ * Anchor the editor to a particular element.
+ *
+ * @param {DOMNode} element
+ * The element that the editor will be anchored to.
+ * Should belong to the HTMLDocument passed into the constructor.
+ */
+ _attach: function (element) {
+ this._detach();
+ this._attachedElement = element;
+ element.classList.add("html-editor-container");
+ this.refresh();
+ },
+
+ /**
+ * Unanchor the editor from an element.
+ */
+ _detach: function () {
+ if (this._attachedElement) {
+ this._attachedElement.classList.remove("html-editor-container");
+ this._attachedElement = undefined;
+ }
+ },
+
+ /**
+ * Anchor the editor to a particular element, and show the editor.
+ *
+ * @param {DOMNode} element
+ * The element that the editor will be anchored to.
+ * Should belong to the HTMLDocument passed into the constructor.
+ * @param {String} text
+ * Value to set the contents of the editor to
+ * @param {Function} cb
+ * The function to call when hiding
+ */
+ show: function (element, text) {
+ if (this._visible) {
+ return;
+ }
+
+ this._originalValue = text;
+ this.editor.setText(text);
+ this._attach(element);
+ this.container.style.display = "flex";
+ this._visible = true;
+
+ this.editor.refresh();
+ this.editor.focus();
+
+ this.emit("popupshown");
+ },
+
+ /**
+ * Hide the editor, optionally committing the changes
+ *
+ * @param {Boolean} shouldCommit
+ * A change will be committed by default. If this param
+ * strictly equals false, no change will occur.
+ */
+ hide: function (shouldCommit) {
+ if (!this._visible) {
+ return;
+ }
+
+ this.container.style.display = "none";
+ this._detach();
+
+ let newValue = this.editor.getText();
+ let valueHasChanged = this._originalValue !== newValue;
+ let preventCommit = shouldCommit === false || !valueHasChanged;
+ this._originalValue = undefined;
+ this._visible = undefined;
+ this.emit("popuphidden", !preventCommit, newValue);
+ },
+
+ /**
+ * Destroy this object and unbind all event handlers
+ */
+ destroy: function () {
+ this.doc.defaultView.removeEventListener("resize",
+ this.refresh, true);
+ this.container.removeEventListener("click", this.hide, false);
+ this.editorInner.removeEventListener("click", stopPropagation, false);
+
+ this.hide(false);
+ this.container.remove();
+ this.editor.destroy();
+ }
+};
+
+function ctrl(k) {
+ return (Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + k;
+}
+
+function stopPropagation(e) {
+ e.stopPropagation();
+}
+
+module.exports = HTMLEditor;
diff --git a/devtools/client/inspector/markup/views/markup-container.js b/devtools/client/inspector/markup/views/markup-container.js
new file mode 100644
index 000000000..b54157242
--- /dev/null
+++ b/devtools/client/inspector/markup/views/markup-container.js
@@ -0,0 +1,720 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Task} = require("devtools/shared/task");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
+const {flashElementOn, flashElementOff} =
+ require("devtools/client/inspector/markup/utils");
+
+const DRAG_DROP_MIN_INITIAL_DISTANCE = 10;
+
+/**
+ * The main structure for storing a document node in the markup
+ * tree. Manages creation of the editor for the node and
+ * a <ul> for placing child elements, and expansion/collapsing
+ * of the element.
+ *
+ * This should not be instantiated directly, instead use one of:
+ * MarkupReadOnlyContainer
+ * MarkupTextContainer
+ * MarkupElementContainer
+ */
+function MarkupContainer() { }
+
+/**
+ * Unique identifier used to set markup container node id.
+ * @type {Number}
+ */
+let markupContainerID = 0;
+
+MarkupContainer.prototype = {
+ /*
+ * Initialize the MarkupContainer. Should be called while one
+ * of the other contain classes is instantiated.
+ *
+ * @param {MarkupView} markupView
+ * The markup view that owns this container.
+ * @param {NodeFront} node
+ * The node to display.
+ * @param {String} templateID
+ * Which template to render for this container
+ */
+ initialize: function (markupView, node, templateID) {
+ this.markup = markupView;
+ this.node = node;
+ this.undo = this.markup.undo;
+ this.win = this.markup._frame.contentWindow;
+ this.id = "treeitem-" + markupContainerID++;
+ this.htmlElt = this.win.document.documentElement;
+
+ // The template will fill the following properties
+ this.elt = null;
+ this.expander = null;
+ this.tagState = null;
+ this.tagLine = null;
+ this.children = null;
+ this.markup.template(templateID, this);
+ this.elt.container = this;
+
+ this._onMouseDown = this._onMouseDown.bind(this);
+ this._onToggle = this._onToggle.bind(this);
+ this._onMouseUp = this._onMouseUp.bind(this);
+ this._onMouseMove = this._onMouseMove.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+
+ // Binding event listeners
+ this.elt.addEventListener("mousedown", this._onMouseDown, false);
+ this.win.addEventListener("mouseup", this._onMouseUp, true);
+ this.win.addEventListener("mousemove", this._onMouseMove, true);
+ this.elt.addEventListener("dblclick", this._onToggle, false);
+ if (this.expander) {
+ this.expander.addEventListener("click", this._onToggle, false);
+ }
+
+ // Marking the node as shown or hidden
+ this.updateIsDisplayed();
+ },
+
+ toString: function () {
+ return "[MarkupContainer for " + this.node + "]";
+ },
+
+ isPreviewable: function () {
+ if (this.node.tagName && !this.node.isPseudoElement) {
+ let tagName = this.node.tagName.toLowerCase();
+ let srcAttr = this.editor.getAttributeElement("src");
+ let isImage = tagName === "img" && srcAttr;
+ let isCanvas = tagName === "canvas";
+
+ return isImage || isCanvas;
+ }
+
+ return false;
+ },
+
+ /**
+ * Show whether the element is displayed or not
+ * If an element has the attribute `display: none` or has been hidden with
+ * the H key, it is not displayed (faded in markup view).
+ * Otherwise, it is displayed.
+ */
+ updateIsDisplayed: function () {
+ this.elt.classList.remove("not-displayed");
+ if (!this.node.isDisplayed || this.node.hidden) {
+ this.elt.classList.add("not-displayed");
+ }
+ },
+
+ /**
+ * True if the current node has children. The MarkupView
+ * will set this attribute for the MarkupContainer.
+ */
+ _hasChildren: false,
+
+ get hasChildren() {
+ return this._hasChildren;
+ },
+
+ set hasChildren(value) {
+ this._hasChildren = value;
+ this.updateExpander();
+ },
+
+ /**
+ * A list of all elements with tabindex that are not in container's children.
+ */
+ get focusableElms() {
+ return [...this.tagLine.querySelectorAll("[tabindex]")];
+ },
+
+ /**
+ * An indicator that the container internals are focusable.
+ */
+ get canFocus() {
+ return this._canFocus;
+ },
+
+ /**
+ * Toggle focusable state for container internals.
+ */
+ set canFocus(value) {
+ if (this._canFocus === value) {
+ return;
+ }
+
+ this._canFocus = value;
+
+ if (value) {
+ this.tagLine.addEventListener("keydown", this._onKeyDown, true);
+ this.focusableElms.forEach(elm => elm.setAttribute("tabindex", "0"));
+ } else {
+ this.tagLine.removeEventListener("keydown", this._onKeyDown, true);
+ // Exclude from tab order.
+ this.focusableElms.forEach(elm => elm.setAttribute("tabindex", "-1"));
+ }
+ },
+
+ /**
+ * If conatiner and its contents are focusable, exclude them from tab order,
+ * and, if necessary, remove focus.
+ */
+ clearFocus: function () {
+ if (!this.canFocus) {
+ return;
+ }
+
+ this.canFocus = false;
+ let doc = this.markup.doc;
+
+ if (!doc.activeElement || doc.activeElement === doc.body) {
+ return;
+ }
+
+ let parent = doc.activeElement;
+
+ while (parent && parent !== this.elt) {
+ parent = parent.parentNode;
+ }
+
+ if (parent) {
+ doc.activeElement.blur();
+ }
+ },
+
+ /**
+ * True if the current node can be expanded.
+ */
+ get canExpand() {
+ return this._hasChildren && !this.node.inlineTextChild;
+ },
+
+ /**
+ * True if this is the root <html> element and can't be collapsed.
+ */
+ get mustExpand() {
+ return this.node._parent === this.markup.walker.rootNode;
+ },
+
+ /**
+ * True if current node can be expanded and collapsed.
+ */
+ get showExpander() {
+ return this.canExpand && !this.mustExpand;
+ },
+
+ updateExpander: function () {
+ if (!this.expander) {
+ return;
+ }
+
+ if (this.showExpander) {
+ this.expander.style.visibility = "visible";
+ // Update accessibility expanded state.
+ this.tagLine.setAttribute("aria-expanded", this.expanded);
+ } else {
+ this.expander.style.visibility = "hidden";
+ // No need for accessible expanded state indicator when expander is not
+ // shown.
+ this.tagLine.removeAttribute("aria-expanded");
+ }
+ },
+
+ /**
+ * If current node has no children, ignore them. Otherwise, consider them a
+ * group from the accessibility point of view.
+ */
+ setChildrenRole: function () {
+ this.children.setAttribute("role",
+ this.hasChildren ? "group" : "presentation");
+ },
+
+ /**
+ * Set an appropriate DOM tree depth level for a node and its subtree.
+ */
+ updateLevel: function () {
+ // ARIA level should already be set when container template is rendered.
+ let currentLevel = this.tagLine.getAttribute("aria-level");
+ let newLevel = this.level;
+ if (currentLevel === newLevel) {
+ // If level did not change, ignore this node and its subtree.
+ return;
+ }
+
+ this.tagLine.setAttribute("aria-level", newLevel);
+ let childContainers = this.getChildContainers();
+ if (childContainers) {
+ childContainers.forEach(container => container.updateLevel());
+ }
+ },
+
+ /**
+ * If the node has children, return the list of containers for all these
+ * children.
+ */
+ getChildContainers: function () {
+ if (!this.hasChildren) {
+ return null;
+ }
+
+ return [...this.children.children].filter(node => node.container)
+ .map(node => node.container);
+ },
+
+ /**
+ * True if the node has been visually expanded in the tree.
+ */
+ get expanded() {
+ return !this.elt.classList.contains("collapsed");
+ },
+
+ setExpanded: function (value) {
+ if (!this.expander) {
+ return;
+ }
+
+ if (!this.canExpand) {
+ value = false;
+ }
+ if (this.mustExpand) {
+ value = true;
+ }
+
+ if (value && this.elt.classList.contains("collapsed")) {
+ // Expanding a node means cloning its "inline" closing tag into a new
+ // tag-line that the user can interact with and showing the children.
+ let closingTag = this.elt.querySelector(".close");
+ if (closingTag) {
+ if (!this.closeTagLine) {
+ let line = this.markup.doc.createElement("div");
+ line.classList.add("tag-line");
+ // Closing tag is not important for accessibility.
+ line.setAttribute("role", "presentation");
+
+ let tagState = this.markup.doc.createElement("div");
+ tagState.classList.add("tag-state");
+ line.appendChild(tagState);
+
+ line.appendChild(closingTag.cloneNode(true));
+
+ flashElementOff(line);
+ this.closeTagLine = line;
+ }
+ this.elt.appendChild(this.closeTagLine);
+ }
+
+ this.elt.classList.remove("collapsed");
+ this.expander.setAttribute("open", "");
+ this.hovered = false;
+ this.markup.emit("expanded");
+ } else if (!value) {
+ if (this.closeTagLine) {
+ this.elt.removeChild(this.closeTagLine);
+ this.closeTagLine = undefined;
+ }
+ this.elt.classList.add("collapsed");
+ this.expander.removeAttribute("open");
+ this.markup.emit("collapsed");
+ }
+ if (this.showExpander) {
+ this.tagLine.setAttribute("aria-expanded", this.expanded);
+ }
+ },
+
+ parentContainer: function () {
+ return this.elt.parentNode ? this.elt.parentNode.container : null;
+ },
+
+ /**
+ * Determine tree depth level of a given node. This is used to specify ARIA
+ * level for node tree items and to give them better semantic context.
+ */
+ get level() {
+ let level = 1;
+ let parent = this.node.parentNode();
+ while (parent && parent !== this.markup.walker.rootNode) {
+ level++;
+ parent = parent.parentNode();
+ }
+ return level;
+ },
+
+ _isDragging: false,
+ _dragStartY: 0,
+
+ set isDragging(isDragging) {
+ let rootElt = this.markup.getContainer(this.markup._rootNode).elt;
+ this._isDragging = isDragging;
+ this.markup.isDragging = isDragging;
+ this.tagLine.setAttribute("aria-grabbed", isDragging);
+
+ if (isDragging) {
+ this.htmlElt.classList.add("dragging");
+ this.elt.classList.add("dragging");
+ this.markup.doc.body.classList.add("dragging");
+ rootElt.setAttribute("aria-dropeffect", "move");
+ } else {
+ this.htmlElt.classList.remove("dragging");
+ this.elt.classList.remove("dragging");
+ this.markup.doc.body.classList.remove("dragging");
+ rootElt.setAttribute("aria-dropeffect", "none");
+ }
+ },
+
+ get isDragging() {
+ return this._isDragging;
+ },
+
+ /**
+ * Check if element is draggable.
+ */
+ isDraggable: function () {
+ let tagName = this.node.tagName && this.node.tagName.toLowerCase();
+
+ return !this.node.isPseudoElement &&
+ !this.node.isAnonymous &&
+ !this.node.isDocumentElement &&
+ tagName !== "body" &&
+ tagName !== "head" &&
+ this.win.getSelection().isCollapsed &&
+ this.node.parentNode().tagName !== null;
+ },
+
+ /**
+ * Move keyboard focus to a next/previous focusable element inside container
+ * that is not part of its children (only if current focus is on first or last
+ * element).
+ *
+ * @param {DOMNode} current currently focused element
+ * @param {Boolean} back direction
+ * @return {DOMNode} newly focused element if any
+ */
+ _wrapMoveFocus: function (current, back) {
+ let elms = this.focusableElms;
+ let next;
+ if (back) {
+ if (elms.indexOf(current) === 0) {
+ next = elms[elms.length - 1];
+ next.focus();
+ }
+ } else if (elms.indexOf(current) === elms.length - 1) {
+ next = elms[0];
+ next.focus();
+ }
+ return next;
+ },
+
+ _onKeyDown: function (event) {
+ let {target, keyCode, shiftKey} = event;
+ let isInput = this.markup._isInputOrTextarea(target);
+
+ // Ignore all keystrokes that originated in editors except for when 'Tab' is
+ // pressed.
+ if (isInput && keyCode !== KeyCodes.DOM_VK_TAB) {
+ return;
+ }
+
+ switch (keyCode) {
+ case KeyCodes.DOM_VK_TAB:
+ // Only handle 'Tab' if tabbable element is on the edge (first or last).
+ if (isInput) {
+ // Corresponding tabbable element is editor's next sibling.
+ let next = this._wrapMoveFocus(target.nextSibling, shiftKey);
+ if (next) {
+ event.preventDefault();
+ // Keep the editing state if possible.
+ if (next._editable) {
+ let e = this.markup.doc.createEvent("Event");
+ e.initEvent(next._trigger, true, true);
+ next.dispatchEvent(e);
+ }
+ }
+ } else {
+ let next = this._wrapMoveFocus(target, shiftKey);
+ if (next) {
+ event.preventDefault();
+ }
+ }
+ break;
+ case KeyCodes.DOM_VK_ESCAPE:
+ this.clearFocus();
+ this.markup.getContainer(this.markup._rootNode).elt.focus();
+ if (this.isDragging) {
+ // Escape when dragging is handled by markup view itself.
+ return;
+ }
+ event.preventDefault();
+ break;
+ default:
+ return;
+ }
+ event.stopPropagation();
+ },
+
+ _onMouseDown: function (event) {
+ let {target, button, metaKey, ctrlKey} = event;
+ let isLeftClick = button === 0;
+ let isMiddleClick = button === 1;
+ let isMetaClick = isLeftClick && (metaKey || ctrlKey);
+
+ // The "show more nodes" button already has its onclick, so early return.
+ if (target.nodeName === "button") {
+ return;
+ }
+
+ // target is the MarkupContainer itself.
+ this.hovered = false;
+ this.markup.navigate(this);
+ // Make container tabbable descendants tabbable and focus in.
+ this.canFocus = true;
+ this.focus();
+ event.stopPropagation();
+
+ // Preventing the default behavior will avoid the body to gain focus on
+ // mouseup (through bubbling) when clicking on a non focusable node in the
+ // line. So, if the click happened outside of a focusable element, do
+ // prevent the default behavior, so that the tagname or textcontent gains
+ // focus.
+ if (!target.closest(".editor [tabindex]")) {
+ event.preventDefault();
+ }
+
+ // Follow attribute links if middle or meta click.
+ if (isMiddleClick || isMetaClick) {
+ let link = target.dataset.link;
+ let type = target.dataset.type;
+ // Make container tabbable descendants not tabbable (by default).
+ this.canFocus = false;
+ this.markup.inspector.followAttributeLink(type, link);
+ return;
+ }
+
+ // Start node drag & drop (if the mouse moved, see _onMouseMove).
+ if (isLeftClick && this.isDraggable()) {
+ this._isPreDragging = true;
+ this._dragStartY = event.pageY;
+ }
+ },
+
+ /**
+ * On mouse up, stop dragging.
+ */
+ _onMouseUp: Task.async(function* () {
+ this._isPreDragging = false;
+
+ if (this.isDragging) {
+ this.cancelDragging();
+
+ let dropTargetNodes = this.markup.dropTargetNodes;
+
+ if (!dropTargetNodes) {
+ return;
+ }
+
+ yield this.markup.walker.insertBefore(this.node, dropTargetNodes.parent,
+ dropTargetNodes.nextSibling);
+ this.markup.emit("drop-completed");
+ }
+ }),
+
+ /**
+ * On mouse move, move the dragged element and indicate the drop target.
+ */
+ _onMouseMove: function (event) {
+ // If this is the first move after mousedown, only start dragging after the
+ // mouse has travelled a few pixels and then indicate the start position.
+ let initialDiff = Math.abs(event.pageY - this._dragStartY);
+ if (this._isPreDragging && initialDiff >= DRAG_DROP_MIN_INITIAL_DISTANCE) {
+ this._isPreDragging = false;
+ this.isDragging = true;
+
+ // If this is the last child, use the closing <div.tag-line> of parent as
+ // indicator.
+ let position = this.elt.nextElementSibling ||
+ this.markup.getContainer(this.node.parentNode())
+ .closeTagLine;
+ this.markup.indicateDragTarget(position);
+ }
+
+ if (this.isDragging) {
+ let x = 0;
+ let y = event.pageY - this.win.scrollY;
+
+ // Ensure we keep the dragged element within the markup view.
+ if (y < 0) {
+ y = 0;
+ } else if (y >= this.markup.doc.body.offsetHeight - this.win.scrollY) {
+ y = this.markup.doc.body.offsetHeight - this.win.scrollY - 1;
+ }
+
+ let diff = y - this._dragStartY + this.win.scrollY;
+ this.elt.style.top = diff + "px";
+
+ let el = this.markup.doc.elementFromPoint(x, y);
+ this.markup.indicateDropTarget(el);
+ }
+ },
+
+ cancelDragging: function () {
+ if (!this.isDragging) {
+ return;
+ }
+
+ this._isPreDragging = false;
+ this.isDragging = false;
+ this.elt.style.removeProperty("top");
+ },
+
+ /**
+ * Temporarily flash the container to attract attention.
+ * Used for markup mutations.
+ */
+ flashMutation: function () {
+ if (!this.selected) {
+ flashElementOn(this.tagState, this.editor.elt);
+ if (this._flashMutationTimer) {
+ clearTimeout(this._flashMutationTimer);
+ this._flashMutationTimer = null;
+ }
+ this._flashMutationTimer = setTimeout(() => {
+ flashElementOff(this.tagState, this.editor.elt);
+ }, this.markup.CONTAINER_FLASHING_DURATION);
+ }
+ },
+
+ _hovered: false,
+
+ /**
+ * Highlight the currently hovered tag + its closing tag if necessary
+ * (that is if the tag is expanded)
+ */
+ set hovered(value) {
+ this.tagState.classList.remove("flash-out");
+ this._hovered = value;
+ if (value) {
+ if (!this.selected) {
+ this.tagState.classList.add("theme-bg-darker");
+ }
+ if (this.closeTagLine) {
+ this.closeTagLine.querySelector(".tag-state").classList.add(
+ "theme-bg-darker");
+ }
+ } else {
+ this.tagState.classList.remove("theme-bg-darker");
+ if (this.closeTagLine) {
+ this.closeTagLine.querySelector(".tag-state").classList.remove(
+ "theme-bg-darker");
+ }
+ }
+ },
+
+ /**
+ * True if the container is visible in the markup tree.
+ */
+ get visible() {
+ return this.elt.getBoundingClientRect().height > 0;
+ },
+
+ /**
+ * True if the container is currently selected.
+ */
+ _selected: false,
+
+ get selected() {
+ return this._selected;
+ },
+
+ set selected(value) {
+ this.tagState.classList.remove("flash-out");
+ this._selected = value;
+ this.editor.selected = value;
+ // Markup tree item should have accessible selected state.
+ this.tagLine.setAttribute("aria-selected", value);
+ if (this._selected) {
+ let container = this.markup.getContainer(this.markup._rootNode);
+ if (container) {
+ container.elt.setAttribute("aria-activedescendant", this.id);
+ }
+ this.tagLine.setAttribute("selected", "");
+ this.tagState.classList.add("theme-selected");
+ } else {
+ this.tagLine.removeAttribute("selected");
+ this.tagState.classList.remove("theme-selected");
+ }
+ },
+
+ /**
+ * Update the container's editor to the current state of the
+ * viewed node.
+ */
+ update: function () {
+ if (this.node.pseudoClassLocks.length) {
+ this.elt.classList.add("pseudoclass-locked");
+ } else {
+ this.elt.classList.remove("pseudoclass-locked");
+ }
+
+ if (this.editor.update) {
+ this.editor.update();
+ }
+ },
+
+ /**
+ * Try to put keyboard focus on the current editor.
+ */
+ focus: function () {
+ // Elements with tabindex of -1 are not focusable.
+ let focusable = this.editor.elt.querySelector("[tabindex='0']");
+ if (focusable) {
+ focusable.focus();
+ }
+ },
+
+ _onToggle: function (event) {
+ this.markup.navigate(this);
+ if (this.hasChildren) {
+ this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
+ }
+ event.stopPropagation();
+ },
+
+ /**
+ * Get rid of event listeners and references, when the container is no longer
+ * needed
+ */
+ destroy: function () {
+ // Remove event listeners
+ this.elt.removeEventListener("mousedown", this._onMouseDown, false);
+ this.elt.removeEventListener("dblclick", this._onToggle, false);
+ this.tagLine.removeEventListener("keydown", this._onKeyDown, true);
+ if (this.win) {
+ this.win.removeEventListener("mouseup", this._onMouseUp, true);
+ this.win.removeEventListener("mousemove", this._onMouseMove, true);
+ }
+
+ this.win = null;
+ this.htmlElt = null;
+
+ if (this.expander) {
+ this.expander.removeEventListener("click", this._onToggle, false);
+ }
+
+ // Recursively destroy children containers
+ let firstChild = this.children.firstChild;
+ while (firstChild) {
+ // Not all children of a container are containers themselves
+ // ("show more nodes" button is one example)
+ if (firstChild.container) {
+ firstChild.container.destroy();
+ }
+ this.children.removeChild(firstChild);
+ firstChild = this.children.firstChild;
+ }
+
+ this.editor.destroy();
+ }
+};
+
+module.exports = MarkupContainer;
diff --git a/devtools/client/inspector/markup/views/moz.build b/devtools/client/inspector/markup/views/moz.build
new file mode 100644
index 000000000..846bc6a84
--- /dev/null
+++ b/devtools/client/inspector/markup/views/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+ 'element-container.js',
+ 'element-editor.js',
+ 'html-editor.js',
+ 'markup-container.js',
+ 'read-only-container.js',
+ 'read-only-editor.js',
+ 'root-container.js',
+ 'text-container.js',
+ 'text-editor.js',
+)
diff --git a/devtools/client/inspector/markup/views/read-only-container.js b/devtools/client/inspector/markup/views/read-only-container.js
new file mode 100644
index 000000000..fd645baac
--- /dev/null
+++ b/devtools/client/inspector/markup/views/read-only-container.js
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Heritage = require("sdk/core/heritage");
+const ReadOnlyEditor = require("devtools/client/inspector/markup/views/read-only-editor");
+const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
+
+/**
+ * An implementation of MarkupContainer for Pseudo Elements,
+ * Doctype nodes, or any other type generic node that doesn't
+ * fit for other editors.
+ * Does not allow any editing, just viewing / selecting.
+ *
+ * @param {MarkupView} markupView
+ * The markup view that owns this container.
+ * @param {NodeFront} node
+ * The node to display.
+ */
+function MarkupReadOnlyContainer(markupView, node) {
+ MarkupContainer.prototype.initialize.call(this, markupView, node,
+ "readonlycontainer");
+
+ this.editor = new ReadOnlyEditor(this, node);
+ this.tagLine.appendChild(this.editor.elt);
+}
+
+MarkupReadOnlyContainer.prototype =
+ Heritage.extend(MarkupContainer.prototype, {});
+
+module.exports = MarkupReadOnlyContainer;
diff --git a/devtools/client/inspector/markup/views/read-only-editor.js b/devtools/client/inspector/markup/views/read-only-editor.js
new file mode 100644
index 000000000..dbc39eeb7
--- /dev/null
+++ b/devtools/client/inspector/markup/views/read-only-editor.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const nodeConstants = require("devtools/shared/dom-node-constants");
+
+/**
+ * Creates an editor for non-editable nodes.
+ */
+function ReadOnlyEditor(container, node) {
+ this.container = container;
+ this.markup = this.container.markup;
+ this.template = this.markup.template.bind(this.markup);
+ this.elt = null;
+ this.template("generic", this);
+
+ if (node.isPseudoElement) {
+ this.tag.classList.add("theme-fg-color5");
+ this.tag.textContent = node.isBeforePseudoElement ? "::before" : "::after";
+ } else if (node.nodeType == nodeConstants.DOCUMENT_TYPE_NODE) {
+ this.elt.classList.add("comment");
+ this.tag.textContent = node.doctypeString;
+ } else {
+ this.tag.textContent = node.nodeName;
+ }
+}
+
+ReadOnlyEditor.prototype = {
+ destroy: function () {
+ this.elt.remove();
+ },
+
+ /**
+ * Stub method for consistency with ElementEditor.
+ */
+ getInfoAtNode: function () {
+ return null;
+ }
+};
+
+module.exports = ReadOnlyEditor;
diff --git a/devtools/client/inspector/markup/views/root-container.js b/devtools/client/inspector/markup/views/root-container.js
new file mode 100644
index 000000000..ccc918fca
--- /dev/null
+++ b/devtools/client/inspector/markup/views/root-container.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Dummy container node used for the root document element.
+ */
+function RootContainer(markupView, node) {
+ this.doc = markupView.doc;
+ this.elt = this.doc.createElement("ul");
+ // Root container has tree semantics for accessibility.
+ this.elt.setAttribute("role", "tree");
+ this.elt.setAttribute("tabindex", "0");
+ this.elt.setAttribute("aria-dropeffect", "none");
+ this.elt.container = this;
+ this.children = this.elt;
+ this.node = node;
+ this.toString = () => "[root container]";
+}
+
+RootContainer.prototype = {
+ hasChildren: true,
+ expanded: true,
+ update: function () {},
+ destroy: function () {},
+
+ /**
+ * If the node has children, return the list of containers for all these children.
+ * @return {Array} An array of child containers or null.
+ */
+ getChildContainers: function () {
+ return [...this.children.children].filter(node => node.container)
+ .map(node => node.container);
+ },
+
+ /**
+ * Set the expanded state of the container node.
+ * @param {Boolean} value
+ */
+ setExpanded: function () {},
+
+ /**
+ * Set an appropriate role of the container's children node.
+ */
+ setChildrenRole: function () {},
+
+ /**
+ * Set an appropriate DOM tree depth level for a node and its subtree.
+ */
+ updateLevel: function () {}
+};
+
+module.exports = RootContainer;
diff --git a/devtools/client/inspector/markup/views/text-container.js b/devtools/client/inspector/markup/views/text-container.js
new file mode 100644
index 000000000..357f17778
--- /dev/null
+++ b/devtools/client/inspector/markup/views/text-container.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Heritage = require("sdk/core/heritage");
+const nodeConstants = require("devtools/shared/dom-node-constants");
+const TextEditor = require("devtools/client/inspector/markup/views/text-editor");
+const MarkupContainer = require("devtools/client/inspector/markup/views/markup-container");
+
+/**
+ * An implementation of MarkupContainer for text node and comment nodes.
+ * Allows basic text editing in a textarea.
+ *
+ * @param {MarkupView} markupView
+ * The markup view that owns this container.
+ * @param {NodeFront} node
+ * The node to display.
+ * @param {Inspector} inspector
+ * The inspector tool container the markup-view
+ */
+function MarkupTextContainer(markupView, node) {
+ MarkupContainer.prototype.initialize.call(this, markupView, node,
+ "textcontainer");
+
+ if (node.nodeType == nodeConstants.TEXT_NODE) {
+ this.editor = new TextEditor(this, node, "text");
+ } else if (node.nodeType == nodeConstants.COMMENT_NODE) {
+ this.editor = new TextEditor(this, node, "comment");
+ } else {
+ throw new Error("Invalid node for MarkupTextContainer");
+ }
+
+ this.tagLine.appendChild(this.editor.elt);
+}
+
+MarkupTextContainer.prototype = Heritage.extend(MarkupContainer.prototype, {});
+
+module.exports = MarkupTextContainer;
diff --git a/devtools/client/inspector/markup/views/text-editor.js b/devtools/client/inspector/markup/views/text-editor.js
new file mode 100644
index 000000000..f3c83ca87
--- /dev/null
+++ b/devtools/client/inspector/markup/views/text-editor.js
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {getAutocompleteMaxWidth} = require("devtools/client/inspector/markup/utils");
+const {editableField} = require("devtools/client/shared/inplace-editor");
+const {getCssProperties} = require("devtools/shared/fronts/css-properties");
+const {LocalizationHelper} = require("devtools/shared/l10n");
+
+const INSPECTOR_L10N =
+ new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+/**
+ * Creates a simple text editor node, used for TEXT and COMMENT
+ * nodes.
+ *
+ * @param {MarkupContainer} container
+ * The container owning this editor.
+ * @param {DOMNode} node
+ * The node being edited.
+ * @param {String} templateId
+ * The template id to use to build the editor.
+ */
+function TextEditor(container, node, templateId) {
+ this.container = container;
+ this.markup = this.container.markup;
+ this.node = node;
+ this.template = this.markup.template.bind(templateId);
+ this._selected = false;
+
+ this.markup.template(templateId, this);
+
+ editableField({
+ element: this.value,
+ stopOnReturn: true,
+ trigger: "dblclick",
+ multiline: true,
+ maxWidth: () => getAutocompleteMaxWidth(this.value, this.container.elt),
+ trimOutput: false,
+ done: (val, commit) => {
+ if (!commit) {
+ return;
+ }
+ this.node.getNodeValue().then(longstr => {
+ longstr.string().then(oldValue => {
+ longstr.release().then(null, console.error);
+
+ this.container.undo.do(() => {
+ this.node.setNodeValue(val);
+ }, () => {
+ this.node.setNodeValue(oldValue);
+ });
+ });
+ });
+ },
+ cssProperties: getCssProperties(this.markup.toolbox),
+ contextMenu: this.markup.inspector.onTextBoxContextMenu
+ });
+
+ this.update();
+}
+
+TextEditor.prototype = {
+ get selected() {
+ return this._selected;
+ },
+
+ set selected(value) {
+ if (value === this._selected) {
+ return;
+ }
+ this._selected = value;
+ this.update();
+ },
+
+ update: function () {
+ let longstr = null;
+ this.node.getNodeValue().then(ret => {
+ longstr = ret;
+ return longstr.string();
+ }).then(str => {
+ longstr.release().then(null, console.error);
+ this.value.textContent = str;
+
+ let isWhitespace = !/[^\s]/.exec(str);
+ this.value.classList.toggle("whitespace", isWhitespace);
+
+ let chars = str.replace(/\n/g, "⏎")
+ .replace(/\t/g, "⇥")
+ .replace(/ /g, "◦");
+ this.value.setAttribute("title", isWhitespace
+ ? INSPECTOR_L10N.getFormatStr("markupView.whitespaceOnly", chars)
+ : "");
+ }).then(null, console.error);
+ },
+
+ destroy: function () {},
+
+ /**
+ * Stub method for consistency with ElementEditor.
+ */
+ getInfoAtNode: function () {
+ return null;
+ }
+};
+
+module.exports = TextEditor;