diff --git a/components/mjs/core/config.json b/components/mjs/core/config.json
index 7f032dbc4..87e95dc00 100644
--- a/components/mjs/core/config.json
+++ b/components/mjs/core/config.json
@@ -4,6 +4,8 @@
"targets": [
"mathjax.ts",
"core", "util", "handlers",
+ "ui/dialog/DraggableDialog.ts",
+ "ui/dialog/InfoDialog.ts",
"adaptors/HTMLAdaptor.ts",
"adaptors/browserAdaptor.ts",
"components/global.ts"
diff --git a/ts/a11y/explorer.ts b/ts/a11y/explorer.ts
index 7964807c0..8df549bc6 100644
--- a/ts/a11y/explorer.ts
+++ b/ts/a11y/explorer.ts
@@ -321,6 +321,7 @@ export function ExplorerMathDocumentMixin<
public static OPTIONS: OptionList = {
...BaseDocument.OPTIONS,
enableExplorer: hasWindow, // only activate in interactive contexts
+ enableExplorerHelp: true, // help dialog is enabled
renderActions: expandable({
...BaseDocument.OPTIONS.renderActions,
explorable: [STATE.EXPLORER]
@@ -417,78 +418,6 @@ export function ExplorerMathDocumentMixin<
display: 'inline-flex',
'align-items': 'center',
},
-
- 'mjx-help-sizer': {
- position: 'fixed',
- width: '40%',
- 'max-width': '30em',
- top: '3em',
- left: '50%',
- },
- 'mjx-help-dialog': {
- position: 'absolute',
- width: '200%',
- left: '-100%',
- border: '3px outset',
- 'border-radius': '15px',
- color: 'black',
- 'background-color': '#DDDDDD',
- 'z-index': '301',
- 'text-align': 'right',
- 'font-style': 'normal',
- 'text-indent': 0,
- 'text-transform': 'none',
- 'line-height': 'normal',
- 'letter-spacing': 'normal',
- 'word-spacing': 'normal',
- 'word-wrap': 'normal',
- float: 'none',
- 'box-shadow': '0px 10px 20px #808080',
- outline: 'none',
- },
- 'mjx-help-dialog > h1': {
- 'font-size': '24px',
- 'text-align': 'center',
- margin: '.5em 0',
- },
- 'mjx-help-dialog > div': {
- margin: '0 1em',
- padding: '3px',
- overflow: 'auto',
- height: '20em',
- border: '2px inset black',
- 'background-color': 'white',
- 'text-align': 'left',
- },
- 'mjx-help-dialog > input': {
- margin: '.5em 2em',
- },
- 'mjx-help-dialog kbd': {
- display: 'inline-block',
- padding: '3px 5px',
- 'font-size': '11px',
- 'line-height': '10px',
- color: '#444d56',
- 'vertical-align': 'middle',
- 'background-color': '#fafbfc',
- border: 'solid 1.5px #c6cbd1',
- 'border-bottom-color': '#959da5',
- 'border-radius': '3px',
- 'box-shadow': 'inset -.5px -1px 0 #959da5',
- },
- 'mjx-help-dialog ul': {
- 'list-style-type': 'none',
- },
- 'mjx-help-dialog li': {
- 'margin-bottom': '.5em',
- },
- 'mjx-help-background': {
- position: 'fixed',
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
};
/**
@@ -556,7 +485,7 @@ export function ExplorerMathDocumentMixin<
SVGNS
),
]);
- this.tmpFocus = this.adaptor.node('mjx-focus', {
+ this.tmpFocus = adaptor.node('mjx-focus', {
tabIndex: 0,
style: {
outline: 'none',
diff --git a/ts/a11y/explorer/KeyExplorer.ts b/ts/a11y/explorer/KeyExplorer.ts
index 943def94d..e333db800 100644
--- a/ts/a11y/explorer/KeyExplorer.ts
+++ b/ts/a11y/explorer/KeyExplorer.ts
@@ -30,6 +30,9 @@ import { MmlNode } from '../../core/MmlTree/MmlNode.js';
import { honk, SemAttr } from '../speech/SpeechUtil.js';
import { GeneratorPool } from '../speech/GeneratorPool.js';
import { context } from '../../util/context.js';
+import { InfoDialog } from '../../ui/dialog/InfoDialog.js';
+
+/**********************************************************************/
/**
* Interface for keyboard explorers. Adds the necessary keyboard events.
@@ -70,7 +73,7 @@ export interface KeyExplorer extends Explorer {
/**
* Type of function that implements a key press action
*/
-type keyMapping = (
+export type keyMapping = (
explorer: SpeechExplorer,
event: KeyboardEvent
) => boolean | void;
@@ -106,149 +109,162 @@ export function hasModifiers(
);
}
+/**********************************************************************/
/**********************************************************************/
/**
- * Creates a customized help dialog
+ * @class
+ * @augments {AbstractExplorer}
*
- * @param {string} title The title to use for the message
- * @param {string} select Additional ways to select the typeset math
- * @returns {string} The customized message
+ * @template T The type that is consumed by the Region of this explorer.
*/
-function helpMessage(title: string, select: string): string {
- return `
-
Exploring expressions ${title}
+export class SpeechExplorer
+ extends AbstractExplorer
+ implements KeyExplorer
+{
+ /**
+ * Creates a customized help dialog
+ *
+ * @param {string} title The title to use for the message
+ * @param {string} select Additional ways to select the typeset math
+ * @returns {string} The customized message
+ */
+ protected static helpMessage(title: string, select: string): string {
+ return `
+ Exploring expressions ${title}
-The mathematics on this page is being rendered by MathJax, which
-generates both the text spoken by screen readers, as well as the
-visual layout for sighted users.
+ The mathematics on this page is being rendered by MathJax, which
+ generates both the text spoken by screen readers, as well as the
+ visual layout for sighted users.
-Expressions typeset by MathJax can be explored interactively, and
-are focusable. You can use the Tab key to move to a typeset
-expression${select}. Initially, the expression will be read in full,
-but you can use the following keys to explore the expression
-further:
+
Expressions typeset by MathJax can be explored interactively, and
+ are focusable. You can use the Tab key to move to a typeset
+ expression${select}. Initially, the expression will be read in full,
+ but you can use the following keys to explore the expression
+ further:
-
+
-- Down Arrow moves one level deeper into the expression to
-allow you to explore the current subexpression term by term.
+ - Down Arrow moves one level deeper into the
+ expression to allow you to explore the current subexpression term by
+ term.
-- Up Arrow moves back up a level within the expression.
+ - Up Arrow moves back up a level within the
+ expression.
-- Right Arrow moves to the next term in the current
-subexpression.
+ - Right Arrow moves to the next term in the
+ current subexpression.
-- Left Arrow moves to the next term in the current
-subexpression.
+ - Left Arrow moves to the next term in the
+ current subexpression.
-- Shift+Arrow moves to a neighboring cell within a table.
+
- Shift+Arrow moves to a
+ neighboring cell within a table.
-- 0-9+0-9 jumps to a cell by its index in the table, where 0 = 10.
+
- 0-9+0-9 jumps to a cell
+ by its index in the table, where 0 = 10.
-- Home takes you to the top of the expression.
+ - Home takes you to the top of the
+ expression.
-- Enter or Return clicks a link or activates an active
-subexpression.
+ - Enter or Return clicks a
+ link or activates an active subexpression.
-- Space opens the MathJax contextual menu where you can view
-or copy the source format of the expression, or modify MathJax's
-settings.
+ - Space opens the MathJax contextual menu
+ where you can view or copy the source format of the expression, or
+ modify MathJax's settings.
-- Escape exits the expression explorer.
+ - Escape exits the expression
+ explorer.
-- x gives a summary of the current subexpression.
+ - x gives a summary of the current
+ subexpression.
-- z gives the full text of a collapsed expression.
+ - z gives the full text of a collapsed
+ expression.
-- d gives the current depth within the expression.
+ - d gives the current depth within the
+ expression.
-- s starts or stops auto-voicing with synchronized highlighting.
+ - s starts or stops auto-voicing with
+ synchronized highlighting.
-- v marks the current position in the expression.
+ - v marks the current position in the
+ expression.
-- p cycles through the marked positions in the expression.
+ - p cycles through the marked positions in
+ the expression.
-- u clears all marked positions and returns to the starting position.
+ - u clears all marked positions and returns
+ to the starting position.
-- > cycles through the available speech rule sets
-(MathSpeak, ClearSpeak).
+ - > cycles through the available speech
+ rule sets (MathSpeak, ClearSpeak).
-- < cycles through the verbosity levels for the current
-rule set.
+ - < cycles through the verbosity levels
+ for the current rule set.
-- h produces this help listing.
-
+ - h produces this help listing.
+
-The MathJax contextual menu allows you to enable or disable speech
-or Braille generation for mathematical expressions, the language to
-use for the spoken mathematics, and other features of MathJax. In
-particular, the Explorer submenu allows you to specify how the
-mathematics should be identified in the page (e.g., by saying "math"
-when the expression is spoken), and whether or not to include a
-message about the letter "h" bringing up this dialog box.
+ The MathJax contextual menu allows you to enable or disable speech
+ or Braille generation for mathematical expressions, the language to
+ use for the spoken mathematics, and other features of MathJax. In
+ particular, the Explorer submenu allows you to specify how the
+ mathematics should be identified in the page (e.g., by saying "math"
+ when the expression is spoken), and whether or not to include a
+ message about the letter "h" bringing up this dialog box. Turning off
+ speech and Braille will disable the expression explorer, its
+ highlighting, and its help icon.
-The contextual menu also provides options for viewing or copying a
-MathML version of the expression or its original source format,
-creating an SVG version of the expression, and viewing various other
-information.
+ The contextual menu also provides options for viewing or copying a
+ MathML version of the expression or its original source format,
+ creating an SVG version of the expression, and viewing various other
+ information.
-For more help, see the MathJax accessibility documentation.
-`;
-}
+ For more help, see the MathJax accessibility documentation.
+ `;
+ }
-/**
- * Help for the different OS versions
- */
-const helpData: Map = new Map([
- [
- 'MacOS',
+ /**
+ * Help for the different OS versions
+ */
+ protected static helpData: Map = new Map([
[
- 'on MacOS and iOS using VoiceOver',
- ', or the VoiceOver arrow keys to select an expression',
+ 'MacOS',
+ [
+ 'on MacOS and iOS using VoiceOver',
+ ', or the VoiceOver arrow keys to select an expression',
+ ],
],
- ],
- [
- 'Windows',
[
- 'in Windows using NVDA or JAWS',
- `. The screen reader should enter focus or forms mode automatically
-when the expression gets the browser focus, but if not, you can toggle
-focus mode using NVDA+space in NVDA; for JAWS, Enter should start
-forms mode while Numpad Plus leaves it. Also note that you can use
-the NVDA or JAWS key plus the arrow keys to explore the expression
-even in browse mode, and you can use NVDA+shift+arrow keys to
-navigate out of an expression that has the focus in NVDA`,
+ 'Windows',
+ [
+ 'in Windows using NVDA or JAWS',
+ `. The screen reader should enter focus or forms mode automatically
+ when the expression gets the browser focus, but if not, you can toggle
+ focus mode using NVDA+space in NVDA; for JAWS, Enter should start
+ forms mode while Numpad Plus leaves it. Also note that you can use
+ the NVDA or JAWS key plus the arrow keys to explore the expression
+ even in browse mode, and you can use NVDA+shift+arrow keys to
+ navigate out of an expression that has the focus in NVDA`,
+ ],
],
- ],
- [
- 'Unix',
[
- 'in Unix using Orca',
- `, and Orca should enter focus mode automatically. If not, use the
-Orca+a key to toggle focus mode on or off. Also note that you can use
-Orca+arrow keys to explore expressions even in browse mode`,
+ 'Unix',
+ [
+ 'in Unix using Orca',
+ `, and Orca should enter focus mode automatically. If not, use the
+ Orca+a key to toggle focus mode on or off. Also note that you can use
+ Orca+arrow keys to explore expressions even in browse mode`,
+ ],
],
- ],
- ['unknown', ['with a Screen Reader.', '']],
-]);
-
-/**********************************************************************/
-/**********************************************************************/
+ ['unknown', ['with a Screen Reader.', '']],
+ ]);
-/**
- * @class
- * @augments {AbstractExplorer}
- *
- * @template T The type that is consumed by the Region of this explorer.
- */
-export class SpeechExplorer
- extends AbstractExplorer
- implements KeyExplorer
-{
/*
* The explorer key mapping
*/
@@ -605,8 +621,13 @@ export class SpeechExplorer
/**
* Open the help dialog, and refocus when it closes.
+ *
+ * @returns {boolean | void} True cancels the event
*/
- protected hKey() {
+ protected hKey(): boolean | void {
+ if (!this.document.options.enableExplorerHelp) {
+ return true;
+ }
this.refocus = this.current;
this.help();
}
@@ -933,40 +954,36 @@ export class SpeechExplorer
* Displays the help dialog.
*/
protected help() {
- const adaptor = this.document.adaptor;
- const helpBackground = adaptor.node('mjx-help-background');
- const close = (event: Event) => {
- helpBackground.remove();
- this.node.focus();
- this.stopEvent(event);
- };
- helpBackground.addEventListener('click', close);
- const helpSizer = adaptor.node('mjx-help-sizer', {}, [
- adaptor.node(
- 'mjx-help-dialog',
- { tabindex: 0, role: 'dialog', 'aria-labeledby': 'mjx-help-label' },
- [
- adaptor.node('h1', { id: 'mjx-help-label' }, [
- adaptor.text('MathJax Expression Explorer Help'),
- ]),
- adaptor.node('div'),
- adaptor.node('input', { type: 'button', value: 'Close' }),
- ]
- ),
- ]);
- helpBackground.append(helpSizer);
- const help = helpSizer.firstChild as HTMLElement;
- help.addEventListener('click', (event) => this.stopEvent(event));
- help.lastChild.addEventListener('click', close);
- help.addEventListener('keydown', (event: KeyboardEvent) => {
- if (event.code === 'Escape') {
- close(event);
- }
+ if (!this.document.options.enableExplorerHelp) {
+ return;
+ }
+ const CLASS = this.constructor as typeof SpeechExplorer;
+ const [title, select] = CLASS.helpData.get(context.os);
+ InfoDialog.post({
+ title: 'MathJax Expression Explorer Help',
+ message: CLASS.helpMessage(title, select),
+ node: this.node,
+ adaptor: this.document.adaptor,
+ styles: {
+ '.mjx-dialog': {
+ 'max-height': 'calc(min(35em, 90%))',
+ },
+ 'mjx-dialog mjx-title': {
+ 'font-size': '133%',
+ margin: '.5em 1.75em',
+ },
+ 'mjx-dialog h2': {
+ 'font-size': '20px',
+ margin: '.5em 0',
+ },
+ 'mjx-dialog ul': {
+ 'list-style-type': 'none',
+ },
+ 'mjx-dialog li': {
+ 'margin-bottom': '.5em',
+ },
+ },
});
- const [title, select] = helpData.get(context.os);
- (help.childNodes[1] as HTMLElement).innerHTML = helpMessage(title, select);
- document.body.append(helpBackground);
- help.focus();
}
/********************************************************************/
@@ -1062,7 +1079,10 @@ export class SpeechExplorer
if (describe) {
let description =
this.description === this.none ? '' : ', ' + this.description;
- if (this.document.options.a11y.help) {
+ if (
+ this.document.options.a11y.help &&
+ this.document.options.enableExplorerHelp
+ ) {
description += ', press h for help';
}
speech += description;
@@ -1538,7 +1558,9 @@ export class SpeechExplorer
// and add the info icon.
//
this.node.classList.add('mjx-explorer-active');
- this.node.append(this.document.infoIcon);
+ if (this.document.options.enableExplorerHelp) {
+ this.node.append(this.document.infoIcon);
+ }
//
// Get the node to make current, and determine if we need to add a
// speech node (or just use the top-level node), then set the
@@ -1574,7 +1596,9 @@ export class SpeechExplorer
this.node.setAttribute('aria-roledescription', description);
}
this.node.classList.remove('mjx-explorer-active');
- this.document.infoIcon.remove();
+ if (this.document.options.enableExplorerHelp) {
+ this.document.infoIcon.remove();
+ }
this.pool.unhighlight();
this.magnifyRegion.Hide();
this.region.Hide();
diff --git a/ts/a11y/speech/SpeechMenu.ts b/ts/a11y/speech/SpeechMenu.ts
index 97663197d..e76a927cc 100644
--- a/ts/a11y/speech/SpeechMenu.ts
+++ b/ts/a11y/speech/SpeechMenu.ts
@@ -22,7 +22,12 @@
*/
import { ExplorerMathItem } from '../explorer.js';
-import { MJContextMenu } from '../../ui/menu/MJContextMenu.js';
+import { MJContextMenu, SubmenuCallback } from '../../ui/menu/MJContextMenu.js';
+import {
+ SelectionDialog,
+ SelectionOrder,
+ SelectionGrid,
+} from '../../ui/dialog/SelectionDialog.js';
import { SubMenu, Submenu } from '../../ui/menu/mj-context-menu.js';
import * as Sre from '../sre.js';
@@ -113,7 +118,7 @@ let counter = 0;
function csSelectionBox(menu: MJContextMenu, locale: string): object {
const props = localePreferences.get(locale);
csPrefsVariables(menu, Object.keys(props));
- const items = [];
+ const items: any[] = [];
for (const prop of Object.getOwnPropertyNames(props)) {
items.push({
title: prop,
@@ -121,22 +126,19 @@ function csSelectionBox(menu: MJContextMenu, locale: string): object {
variable: 'csprf_' + prop,
});
}
- const sb = menu.factory.get('selectionBox')(
- menu.factory,
- {
- title: 'Clearspeak Preferences',
- signature: '',
- order: 'alphabetic',
- grid: 'square',
- selections: items,
- },
+ const sb = new SelectionDialog(
+ 'Clearspeak Preferences',
+ '',
+ items,
+ SelectionOrder.ALPHABETICAL,
+ SelectionGrid.SQUARE,
menu
);
return {
type: 'command',
id: 'ClearspeakPreferences',
content: 'Select Preferences',
- action: () => sb.post(0, 0),
+ action: () => sb.post(),
};
}
@@ -223,13 +225,13 @@ function smartPreferences(
*
* @param {MJContextMenu} menu The context menu.
* @param {Submenu} sub The submenu to attach elements to.
- * @param {(sub: SubMenu) => void} callback Callback to apply on the constructed
+ * @param {SubmenuCallback} callback Callback to apply on the constructed
* submenu.
*/
export async function clearspeakMenu(
menu: MJContextMenu,
sub: Submenu,
- callback: (sub: SubMenu) => void
+ callback: SubmenuCallback
) {
const exit = (items: object[]) => {
callback(
@@ -288,13 +290,13 @@ let LOCALE_MENU: SubMenu = null;
*
* @param {MJContextMenu} menu The context menu.
* @param {Submenu} sub The submenu to attach elements to.
- * @param {(sub: SubMenu) => void} callback Callback to apply on the constructed
+ * @param {SubmenuCallback} callback Callback to apply on the constructed
* submenu.
*/
export function localeMenu(
menu: MJContextMenu,
sub: Submenu,
- callback: (sub: SubMenu) => void
+ callback: SubmenuCallback
) {
if (LOCALE_MENU) {
callback(LOCALE_MENU);
diff --git a/ts/ui/dialog/CopyDialog.ts b/ts/ui/dialog/CopyDialog.ts
new file mode 100644
index 000000000..d31caebc3
--- /dev/null
+++ b/ts/ui/dialog/CopyDialog.ts
@@ -0,0 +1,77 @@
+/*************************************************************
+ *
+ * Copyright (c) 2025 The MathJax Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Implements the CopyDialog class (InfoDialog with copy button).
+ *
+ * @author dpvc@mathjax.org (Davide Cervone)
+ */
+
+import { InfoDialog, InfoDialogArgs } from './InfoDialog.js';
+
+/**
+ * The args for a CopyDialog
+ */
+export type CopyDialogArgs = InfoDialogArgs & { code?: boolean };
+
+/**
+ * The CopyDialog subclass of InfoDialog
+ */
+export class CopyDialog extends InfoDialog {
+ /**
+ * @override
+ */
+ public static post(args: CopyDialogArgs) {
+ return super.post(args);
+ }
+
+ /**
+ * @override
+ */
+ protected html(args: CopyDialogArgs) {
+ //
+ // Add a copy-to-clipboard button
+ //
+ args.extraNodes ??= [];
+ const copy = args.adaptor.node('input', {
+ type: 'button',
+ value: 'Copy to Clipboard',
+ 'data-drag': 'none',
+ });
+ copy.addEventListener('click', this.copyToClipboard.bind(this));
+ args.extraNodes.push(copy);
+ //
+ // If this is a code dialog, format the source and set in a pre element
+ //
+ if (args.code) {
+ args.message = '' + this.formatSource(args.message) + '
';
+ }
+ return super.html(args);
+ }
+
+ /**
+ * @param {string} text The text to be displayed in the Info box
+ * @returns {string} The text with HTML specials being escaped
+ */
+ protected formatSource(text: string): string {
+ return text
+ .trim()
+ .replace(/&/g, '&')
+ .replace(//g, '>');
+ }
+}
diff --git a/ts/ui/dialog/DraggableDialog.ts b/ts/ui/dialog/DraggableDialog.ts
new file mode 100644
index 000000000..6575376e9
--- /dev/null
+++ b/ts/ui/dialog/DraggableDialog.ts
@@ -0,0 +1,1114 @@
+/*************************************************************
+ *
+ * Copyright (c) 2025 The MathJax Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Implements a draggable dialog class.
+ *
+ * @author dpvc@mathjax.org (Davide Cervone)
+ */
+
+import { DOMAdaptor } from '../../core/DOMAdaptor.js';
+import { StyleJson, StyleJsonSheet } from '../../util/StyleJson.js';
+import { context } from '../../util/context.js';
+
+export type ADAPTOR = DOMAdaptor;
+
+export type Action = (
+ dialog: DraggableDialog,
+ event: MouseEvent
+) => void | number[];
+export type ActionList = { [action: string]: Action };
+export type ActionMap = { [type: string]: ActionList };
+
+/**
+ * The arguments that control the dialog contents
+ */
+export type DialogArgs = {
+ title?: string; // // the dialog title HTML
+ message: string; // // the dialog message HTML
+ adaptor: ADAPTOR; // // the adaptor to use to create the dialog
+ node?: HTMLElement; // // the node to focus when the dialog closes, if any
+ styles?: StyleJson; // // extra styles to use for the dialog
+ extraNodes?: HTMLElement[]; // // extra HTML nodes to put at the bottom of the dialog
+ className?: string; // // optional class to apply to the dialog
+};
+
+/**
+ * Type of function that implements a key press action
+ */
+export type keyMapping = (
+ dialog: DraggableDialog,
+ event: KeyboardEvent
+) => void;
+
+/**
+ * True if we can rely on an HTML dialog element.
+ */
+export const isDialog: boolean = !!context.window?.HTMLDialogElement;
+
+/*========================================================================*/
+
+/**
+ * The draggable dialog class
+ */
+export class DraggableDialog {
+ /**
+ * The minimum width of the dialog
+ */
+ protected minW = 200;
+ /**
+ * The maximum width of the dialog
+ */
+ protected minH = 80;
+
+ /**
+ * The current x translation of the dialog
+ */
+ protected tx: number = 0;
+ /**
+ * The current y translation of the dialog
+ */
+ protected ty: number = 0;
+
+ /**
+ * The current mouse x position
+ */
+ protected x: number;
+ /**
+ * The current mouse y position
+ */
+ protected y: number;
+ /**
+ * The current dialog width
+ */
+ protected w: number;
+ /**
+ * The current dialog height
+ */
+ protected h: number;
+ /**
+ * True when the dialog is being dragged or sized
+ */
+ protected dragging: boolean = false;
+ /**
+ * The drag acction being taken (move, left, right, top, bottom, etc.)
+ */
+ protected action: string;
+
+ /**
+ * Elements where clicking doesn't cause dragging
+ */
+ protected noDrag: HTMLElement[];
+ /**
+ * The title element
+ */
+ protected title: HTMLElement;
+ /**
+ * The content div element
+ */
+ protected content: HTMLElement;
+
+ /**
+ * The node to focus when dialog closes
+ */
+ protected node: HTMLElement;
+ /**
+ * The background element when