text:
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview ChromeVox commands.
 */
import {AutomationPredicate} from '../../common/automation_predicate.js';
import {AutomationUtil} from '../../common/automation_util.js';
import {constants} from '../../common/constants.js';
import {Cursor, CursorUnit} from '../../common/cursors/cursor.js';
import {CursorRange} from '../../common/cursors/range.js';
import {EventGenerator} from '../../common/event_generator.js';
import {KeyCode} from '../../common/key_code.js';
import {RectUtil} from '../../common/rect_util.js';
import {Earcon} from '../common/abstract_earcons.js';
import {AbstractTts} from '../common/abstract_tts.js';
import {NavBraille} from '../common/braille/nav_braille.js';
import {BridgeConstants} from '../common/bridge_constants.js';
import {BridgeHelper} from '../common/bridge_helper.js';
import {CommandStore} from '../common/command_store.js';
import {ChromeVoxEvent, CustomAutomationEvent} from '../common/custom_automation_event.js';
import {EventSourceType} from '../common/event_source_type.js';
import {GestureGranularity} from '../common/gesture_command_data.js';
import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
import {LogType} from '../common/log_types.js';
import {Msgs} from '../common/msgs.js';
import {PanelCommand, PanelCommandType} from '../common/panel_command.js';
import {TreeDumper} from '../common/tree_dumper.js';
import {QueueMode, TtsSpeechProperties} from '../common/tts_interface.js';

import {AutoScrollHandler} from './auto_scroll_handler.js';
import {BrailleBackground} from './braille/braille_background.js';
import {BrailleCaptionsBackground} from './braille/braille_captions_background.js';
import {ChromeVox} from './chromevox.js';
import {ChromeVoxState} from './chromevox_state.js';
import {ChromeVoxBackground} from './classic_background.js';
import {Color} from './color.js';
import {CommandHandlerInterface} from './command_handler_interface.js';
import {DesktopAutomationInterface} from './desktop_automation_interface.js';
import {TypingEcho} from './editing/editable_text_base.js';
import {EventSourceState} from './event_source.js';
import {GestureInterface} from './gesture_interface.js';
import {LogStore} from './logging/log_store.js';
import {Output} from './output/output.js';
import {OutputEventType} from './output/output_types.js';
import {PhoneticData} from './phonetic_data.js';
import {ChromeVoxPrefs} from './prefs.js';
import {SmartStickyMode} from './smart_sticky_mode.js';

const AutomationNode = chrome.automation.AutomationNode;
const Dir = constants.Dir;
const EventType = chrome.automation.EventType;
const RoleType = chrome.automation.RoleType;

/**
 * @typedef {{
 *   node: (AutomationNode|undefined),
 *   range: !CursorRange}}
 */
let NewRangeData;

export class CommandHandler extends CommandHandlerInterface {
  /** @private */
  constructor() {
    super();

    /**
     * To support viewGraphicAsBraille_(), the current image node.
     * @type {?AutomationNode}
     */
    this.imageNode_;

    /** @private {boolean} */
    this.isIncognito_ = Boolean(chrome.runtime.getManifest()['incognito']);

    /** @private {boolean} */
    this.isKioskSession_ = false;

    /** @private {boolean} */
    this.languageLoggingEnabled_ = false;

    /**
     * Handles toggling sticky mode when encountering editables.
     * @private {!SmartStickyMode}
     */
    this.smartStickyMode_ = new SmartStickyMode();

    this.init();
  }

  /** @override */
  onCommand(command) {
    // Check for a command denied in incognito contexts and kiosk.
    if (!this.isAllowed_(command)) {
      return true;
    }

    // Check for loss of focus which results in us invalidating our current
    // range. Note this call is synchronous.
    chrome.automation.getFocus(focus => this.checkForLossOfFocus_(focus));

    // These commands don't require a current range.
    switch (command) {
      case 'speakTimeAndDate':
        this.speakTimeAndDate_();
        return false;
      case 'showOptionsPage':
        chrome.runtime.openOptionsPage();
        break;
      case 'toggleStickyMode':
        this.toggleStickyMode_();
        return false;
      case 'passThroughMode':
        ChromeVox.passThroughMode = true;
        ChromeVox.tts.speak(Msgs.getMsg('pass_through_key'), QueueMode.QUEUE);
        return true;
      case 'showLearnModePage':
        this.showLearnModePage_();
        break;
      case 'showLogPage':
        const logPage = {url: 'chromevox/log_page/log.html'};
        chrome.tabs.create(logPage);
        break;
      case 'enableLogging':
        this.enableLogging_();
        break;
      case 'disableLogging':
        this.disableLogging_();
        break;
      case 'dumpTree':
        chrome.automation.getDesktop(
            root => LogStore.getInstance().writeTreeLog(new TreeDumper(root)));
        break;
      case 'decreaseTtsRate':
        this.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, false);
        return false;
      case 'increaseTtsRate':
        this.increaseOrDecreaseSpeechProperty_(AbstractTts.RATE, true);
        return false;
      case 'decreaseTtsPitch':
        this.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, false);
        return false;
      case 'increaseTtsPitch':
        this.increaseOrDecreaseSpeechProperty_(AbstractTts.PITCH, true);
        return false;
      case 'decreaseTtsVolume':
        this.increaseOrDecreaseSpeechProperty_(AbstractTts.VOLUME, false);
        return false;
      case 'increaseTtsVolume':
        this.increaseOrDecreaseSpeechProperty_(AbstractTts.VOLUME, true);
        return false;
      case 'stopSpeech':
        ChromeVox.tts.stop();
        ChromeVoxState.instance.isReadingContinuously = false;
        return false;
      case 'toggleEarcons':
        this.toggleEarcons_();
        return false;
      case 'cycleTypingEcho':
        this.cycleTypingEcho_();
        return false;
      case 'cyclePunctuationEcho':
        ChromeVox.tts.speak(
            Msgs.getMsg(
                ChromeVoxState.instance.backgroundTts.cyclePunctuationEcho()),
            QueueMode.FLUSH);
        return false;
      case 'reportIssue':
        this.reportIssue_();
        return false;
      case 'toggleBrailleCaptions':
        BrailleCaptionsBackground.setActive(
            !BrailleCaptionsBackground.isEnabled());
        return false;
      case 'toggleBrailleTable':
        this.toggleBrailleTable_();
        return false;
      case 'help':
        (new PanelCommand(PanelCommandType.TUTORIAL)).send();
        return false;
      case 'toggleScreen':
        this.toggleScreen_();
        return false;
      case 'toggleSpeechOnOrOff':
        const state = ChromeVox.tts.toggleSpeechOnOrOff();
        new Output().format(state ? '@speech_on' : '@speech_off').go();
        return false;
      case 'enableChromeVoxArcSupportForCurrentApp':
        this.enableChromeVoxArcSupportForCurrentApp_();
        break;
      case 'disableChromeVoxArcSupportForCurrentApp':
        this.disableChromeVoxArcSupportForCurrentApp_();
        break;
      case 'showTalkBackKeyboardShortcuts':
        this.showTalkBackKeyboardShortcuts_();
        return false;
      case 'showTtsSettings':
        chrome.accessibilityPrivate.openSettingsSubpage(
            'manageAccessibility/tts');
        break;
      default:
        break;
      case 'toggleKeyboardHelp':
        (new PanelCommand(PanelCommandType.OPEN_MENUS)).send();
        return false;
      case 'showPanelMenuMostRecent':
        (new PanelCommand(PanelCommandType.OPEN_MENUS_MOST_RECENT)).send();
        return false;
      case 'nextGranularity':
      case 'previousGranularity':
        this.nextOrPreviousGranularity_(command === 'previousGranularity');
        return false;
      case 'announceBatteryDescription':
        this.announceBatteryDescription_();
        break;
      case 'resetTextToSpeechSettings':
        ChromeVox.tts.resetTextToSpeechSettings();
        return false;
      case 'copy':
        EventGenerator.sendKeyPress(KeyCode.C, {ctrl: true});

        // The above command doesn't trigger document clipboard events, so we
        // need to set this manually.
        ChromeVoxState.instance.readNextClipboardDataChange();
        return false;
      case 'toggleDictation':
        EventGenerator.sendKeyPress(KeyCode.D, {search: true});
        return false;
    }

    // The remaining commands require a current range.
    if (!ChromeVoxState.instance.currentRange) {
      if (!ChromeVoxState.instance.talkBackEnabled) {
        this.announceNoCurrentRange_();
      }
      return true;
    }

    // Allow edit commands first.
    if (!this.onEditCommand_(command)) {
      return false;
    }

    let currentRange = ChromeVoxState.instance.currentRange;
    let node = currentRange.start.node;

    // If true, will check if the predicate matches the current node.
    let matchCurrent = false;

    let dir = Dir.FORWARD;
    let newRangeData;
    let pred = null;
    let predErrorMsg = undefined;
    let rootPred = AutomationPredicate.rootOrEditableRoot;
    let unit = null;
    let shouldWrap = true;
    const speechProps = new TtsSpeechProperties();
    let skipSync = false;
    let didNavigate = false;
    let tryScrolling = true;
    let skipSettingSelection = false;
    let skipInitialAncestry = true;
    switch (command) {
      case 'nextCharacter':
        didNavigate = true;
        speechProps.phoneticCharacters = true;
        unit = CursorUnit.CHARACTER;
        currentRange = currentRange.move(CursorUnit.CHARACTER, Dir.FORWARD);
        break;
      case 'previousCharacter':
        dir = Dir.BACKWARD;
        didNavigate = true;
        speechProps.phoneticCharacters = true;
        unit = CursorUnit.CHARACTER;
        currentRange = currentRange.move(CursorUnit.CHARACTER, dir);
        break;
      case 'nativeNextCharacter':
      case 'nativePreviousCharacter':
        DesktopAutomationInterface.instance.onNativeNextOrPreviousCharacter();
        return true;
      case 'nextWord':
        didNavigate = true;
        unit = CursorUnit.WORD;
        currentRange = currentRange.move(CursorUnit.WORD, Dir.FORWARD);
        break;
      case 'previousWord':
        dir = Dir.BACKWARD;
        didNavigate = true;
        unit = CursorUnit.WORD;
        currentRange = currentRange.move(CursorUnit.WORD, dir);
        break;
      case 'nativeNextWord':
      case 'nativePreviousWord':
        DesktopAutomationInterface.instance.onNativeNextOrPreviousWord(
            command === 'nativeNextWord');
        return true;
      case 'forward':
      case 'nextLine':
        didNavigate = true;
        unit = CursorUnit.LINE;
        currentRange = currentRange.move(CursorUnit.LINE, Dir.FORWARD);
        break;
      case 'backward':
      case 'previousLine':
        dir = Dir.BACKWARD;
        didNavigate = true;
        unit = CursorUnit.LINE;
        currentRange = currentRange.move(CursorUnit.LINE, dir);
        break;
      case 'nextButton':
        dir = Dir.FORWARD;
        pred = AutomationPredicate.button;
        predErrorMsg = 'no_next_button';
        break;
      case 'previousButton':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.button;
        predErrorMsg = 'no_previous_button';
        break;
      case 'nextCheckbox':
        pred = AutomationPredicate.checkBox;
        predErrorMsg = 'no_next_checkbox';
        break;
      case 'previousCheckbox':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.checkBox;
        predErrorMsg = 'no_previous_checkbox';
        break;
      case 'nextComboBox':
        pred = AutomationPredicate.comboBox;
        predErrorMsg = 'no_next_combo_box';
        break;
      case 'previousComboBox':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.comboBox;
        predErrorMsg = 'no_previous_combo_box';
        break;
      case 'nextEditText':
        skipSettingSelection = true;
        pred = AutomationPredicate.editText;
        predErrorMsg = 'no_next_edit_text';
        this.smartStickyMode_.startIgnoringRangeChanges();
        break;
      case 'previousEditText':
        skipSettingSelection = true;
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.editText;
        predErrorMsg = 'no_previous_edit_text';
        this.smartStickyMode_.startIgnoringRangeChanges();
        break;
      case 'nextFormField':
        skipSettingSelection = true;
        pred = AutomationPredicate.formField;
        predErrorMsg = 'no_next_form_field';
        this.smartStickyMode_.startIgnoringRangeChanges();
        break;
      case 'previousFormField':
        skipSettingSelection = true;
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.formField;
        predErrorMsg = 'no_previous_form_field';
        this.smartStickyMode_.startIgnoringRangeChanges();
        break;
      case 'previousGraphic':
        skipSettingSelection = true;
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.image;
        predErrorMsg = 'no_previous_graphic';
        break;
      case 'nextGraphic':
        skipSettingSelection = true;
        pred = AutomationPredicate.image;
        predErrorMsg = 'no_next_graphic';
        break;
      case 'nextHeading':
        pred = AutomationPredicate.heading;
        predErrorMsg = 'no_next_heading';
        break;
      case 'nextHeading1':
        pred = AutomationPredicate.makeHeadingPredicate(1);
        predErrorMsg = 'no_next_heading_1';
        break;
      case 'nextHeading2':
        pred = AutomationPredicate.makeHeadingPredicate(2);
        predErrorMsg = 'no_next_heading_2';
        break;
      case 'nextHeading3':
        pred = AutomationPredicate.makeHeadingPredicate(3);
        predErrorMsg = 'no_next_heading_3';
        break;
      case 'nextHeading4':
        pred = AutomationPredicate.makeHeadingPredicate(4);
        predErrorMsg = 'no_next_heading_4';
        break;
      case 'nextHeading5':
        pred = AutomationPredicate.makeHeadingPredicate(5);
        predErrorMsg = 'no_next_heading_5';
        break;
      case 'nextHeading6':
        pred = AutomationPredicate.makeHeadingPredicate(6);
        predErrorMsg = 'no_next_heading_6';
        break;
      case 'previousHeading':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.heading;
        predErrorMsg = 'no_previous_heading';
        break;
      case 'previousHeading1':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeHeadingPredicate(1);
        predErrorMsg = 'no_previous_heading_1';
        break;
      case 'previousHeading2':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeHeadingPredicate(2);
        predErrorMsg = 'no_previous_heading_2';
        break;
      case 'previousHeading3':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeHeadingPredicate(3);
        predErrorMsg = 'no_previous_heading_3';
        break;
      case 'previousHeading4':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeHeadingPredicate(4);
        predErrorMsg = 'no_previous_heading_4';
        break;
      case 'previousHeading5':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeHeadingPredicate(5);
        predErrorMsg = 'no_previous_heading_5';
        break;
      case 'previousHeading6':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeHeadingPredicate(6);
        predErrorMsg = 'no_previous_heading_6';
        break;
      case 'nextLink':
        pred = AutomationPredicate.link;
        predErrorMsg = 'no_next_link';
        break;
      case 'previousLink':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.link;
        predErrorMsg = 'no_previous_link';
        break;
      case 'nextTable':
        pred = AutomationPredicate.table;
        predErrorMsg = 'no_next_table';
        break;
      case 'previousTable':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.table;
        predErrorMsg = 'no_previous_table';
        break;
      case 'nextVisitedLink':
        pred = AutomationPredicate.visitedLink;
        predErrorMsg = 'no_next_visited_link';
        break;
      case 'previousVisitedLink':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.visitedLink;
        predErrorMsg = 'no_previous_visited_link';
        break;
      case 'nextLandmark':
        pred = AutomationPredicate.landmark;
        predErrorMsg = 'no_next_landmark';
        break;
      case 'previousLandmark':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.landmark;
        predErrorMsg = 'no_previous_landmark';
        break;
      case 'left':
      case 'previousObject':
        skipSettingSelection = true;
        dir = Dir.BACKWARD;
        // Falls through.
      case 'right':
      case 'nextObject':
        skipSettingSelection = true;
        didNavigate = true;
        unit = (EventSourceState.get() === EventSourceType.TOUCH_GESTURE) ?
            CursorUnit.GESTURE_NODE :
            CursorUnit.NODE;
        currentRange = currentRange.move(unit, dir);
        currentRange = this.skipLabelOrDescriptionFor(currentRange, dir);
        break;
      case 'previousGroup':
        skipSync = true;
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.group;
        break;
      case 'nextGroup':
        skipSync = true;
        pred = AutomationPredicate.group;
        break;
      case 'previousPage':
      case 'nextPage':
        this.nextOrPreviousPage_(command, currentRange);
        return false;
      case 'previousSimilarItem':
        dir = Dir.BACKWARD;
        // Falls through.
      case 'nextSimilarItem':
        skipSync = true;
        pred = this.getPredicateForNextOrPreviousSimilarItem_(node);
        break;
      case 'previousInvalidItem':
        dir = Dir.BACKWARD;
        // Falls through.
      case 'nextInvalidItem':
        pred = AutomationPredicate.isInvalid;
        rootPred = AutomationPredicate.root;
        predErrorMsg = 'no_invalid_item';
        break;
      case 'nextList':
        pred = AutomationPredicate.makeListPredicate(node);
        predErrorMsg = 'no_next_list';
        break;
      case 'previousList':
        dir = Dir.BACKWARD;
        pred = AutomationPredicate.makeListPredicate(node);
        predErrorMsg = 'no_previous_list';
        skipInitialAncestry = false;
        break;
      case 'jumpToTop':
        newRangeData = this.getNewRangeForJumpToTop_(node, currentRange);
        currentRange = newRangeData.range;
        tryScrolling = false;
        break;
      case 'jumpToBottom':
        newRangeData = this.getNewRangeForJumpToBottom_(node, currentRange);
        currentRange = newRangeData.range;
        tryScrolling = false;
        break;
      case 'forceClickOnCurrentItem':
        this.forceClickOnCurrentItem_();
        // Skip all other processing; if focus changes, we should get an event
        // for that.
        return false;
      case 'forceLongClickOnCurrentItem':
        node.longClick();
        // Skip all other processing; if focus changes, we should get an event
        // for that.
        return false;
      case 'jumpToDetails':
        newRangeData = this.getNewRangeForJumpToDetails_(node, currentRange);
        node = newRangeData.node;
        currentRange = newRangeData.range;
        break;
      case 'readFromHere':
        this.readFromHere_();
        return false;
      case 'contextMenu':
        EventGenerator.sendKeyPress(KeyCode.APPS);
        break;
      case 'showHeadingsList':
        (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_heading')).send();
        return false;
      case 'showFormsList':
        (new PanelCommand(
             PanelCommandType.OPEN_MENUS, 'panel_menu_form_controls'))
            .send();
        return false;
      case 'showLandmarksList':
        (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_landmark')).send();
        return false;
      case 'showLinksList':
        (new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_link')).send();
        return false;
      case 'showActionsMenu':
        (new PanelCommand(PanelCommandType.OPEN_MENUS, 'panel_menu_actions'))
            .send();
        return false;
      case 'showTablesList':
        (new PanelCommand(PanelCommandType.OPEN_MENUS, 'table_strategy'))
            .send();
        return false;
      case 'toggleSearchWidget':
        (new PanelCommand(PanelCommandType.SEARCH)).send();
        return false;
      case 'readCurrentTitle':
        this.readCurrentTitle_();
        return false;
      case 'readCurrentURL':
        new Output().withString(node.root.docUrl || '').go();
        return false;
      case 'toggleSelection':
        if (!this.toggleSelection_()) {
          return false;
        }
        break;
      case 'fullyDescribe':
        const o = new Output();
        o.withContextFirst()
            .withRichSpeechAndBraille(
                currentRange, null, OutputEventType.NAVIGATE)
            .go();
        return false;
      case 'viewGraphicAsBraille':
        this.viewGraphicAsBraille_(currentRange);
        return false;
      // Table commands.
      case 'previousRow':
        skipSync = true;
        dir = Dir.BACKWARD;
        pred = this.getPredicateForPreviousRow_(currentRange, dir);
        predErrorMsg = 'no_cell_above';
        rootPred = AutomationPredicate.table;
        shouldWrap = false;
        break;
      case 'previousCol':
        skipSync = true;
        dir = Dir.BACKWARD;
        pred = this.getPredicateForPreviousCol_(currentRange, dir);
        predErrorMsg = 'no_cell_left';
        rootPred = AutomationPredicate.row;
        shouldWrap = false;
        break;
      case 'nextRow':
        skipSync = true;
        pred = this.getPredicateForNextRow_(currentRange, dir);
        predErrorMsg = 'no_cell_below';
        rootPred = AutomationPredicate.table;
        shouldWrap = false;
        break;
      case 'nextCol':
        skipSync = true;
        pred = this.getPredicateForNextCol_(currentRange, dir);
        predErrorMsg = 'no_cell_right';
        rootPred = AutomationPredicate.row;
        shouldWrap = false;
        break;
      case 'goToRowFirstCell':
      case 'goToRowLastCell':
        skipSync = true;
        newRangeData = this.getNewRangeForGoToRowFirstOrLastCell_(
            node, currentRange, command);
        node = newRangeData.node;
        currentRange = newRangeData.range;
        break;
      case 'goToColFirstCell':
        skipSync = true;
        node = this.getTableNode_(node);
        if (!node || !node.firstChild) {
          return false;
        }
        pred = this.getPredicateForGoToColFirstOrLastCell_(currentRange, dir);
        currentRange = CursorRange.fromNode(node.firstChild);
        // Should not be outputted.
        predErrorMsg = 'no_cell_above';
        rootPred = AutomationPredicate.table;
        shouldWrap = false;
        break;
      case 'goToColLastCell':
        skipSync = true;
        dir = Dir.BACKWARD;
        node = this.getTableNode_(node);
        if (!node || !node.lastChild) {
          return false;
        }
        pred = this.getPredicateForGoToColFirstOrLastCell_(currentRange, dir);

        newRangeData = this.getNewRangeForGoToColLastCell_(node);
        currentRange = newRangeData.range;
        matchCurrent = true;

        // Should not be outputted.
        predErrorMsg = 'no_cell_below';
        rootPred = AutomationPredicate.table;
        shouldWrap = false;
        break;
      case 'goToFirstCell':
      case 'goToLastCell':
        skipSync = true;
        node = this.getTableNode_(node);
        if (!node) {
          break;
        }
        newRangeData = this.getNewRangeForGoToFirstOrLastCell_(
            node, currentRange, command);
        currentRange = newRangeData.range;
        break;

      // These commands are only available when invoked from touch.
      case 'nextAtGranularity':
      case 'previousAtGranularity':
        this.nextOrPreviousAtGranularity_(command === 'previousAtGranularity');
        return false;
      case 'announceRichTextDescription':
        this.announceRichTextDescription_(node);
        return false;
      case 'readPhoneticPronunciation':
        this.readPhoneticPronunciation_(node);
        return false;
      case 'readLinkURL':
        this.readLinkURL_(node);
        return false;
      default:
        return true;
    }

    if (didNavigate) {
      chrome.metricsPrivate.recordUserAction(
          'Accessibility.ChromeVox.Navigate');
    }

    // TODO(accessibility): extract this block and remove explicit type casts
    // after re-writing.
    if (pred) {
      chrome.metricsPrivate.recordUserAction('Accessibility.ChromeVox.Jump');

      let bound = currentRange.getBound(dir).node;
      if (bound) {
        let node = null;

        if (matchCurrent && pred(bound)) {
          node = bound;
        }

        if (!node) {
          node = AutomationUtil.findNextNode(
              bound, dir, pred, {skipInitialAncestry, root: rootPred});
        }

        if (node && !skipSync) {
          node = AutomationUtil.findNodePre(
                     node, Dir.FORWARD, AutomationPredicate.object) ||
              node;
        }

        if (node) {
          currentRange = CursorRange.fromNode(node);
        } else {
          ChromeVox.earcons.playEarcon(Earcon.WRAP);
          if (!shouldWrap) {
            if (predErrorMsg) {
              new Output()
                  .withString(Msgs.getMsg(predErrorMsg))
                  .withQueueMode(QueueMode.FLUSH)
                  .go();
            }
            this.onFinishCommand();
            return false;
          }

          let root = bound;
          while (root && !AutomationPredicate.rootOrEditableRoot(root)) {
            root = root.parent;
          }

          if (!root) {
            root = bound.root;
          }

          if (dir === Dir.FORWARD) {
            bound = root;
          } else {
            bound = AutomationUtil.findNodePost(
                        /** @type {!AutomationNode} */ (root), dir,
                        AutomationPredicate.leaf) ||
                bound;
          }
          node = AutomationUtil.findNextNode(
              /** @type {!AutomationNode} */ (bound), dir, pred,
              {root: rootPred});

          if (node && !skipSync) {
            node = AutomationUtil.findNodePre(
                       node, Dir.FORWARD, AutomationPredicate.object) ||
                node;
          }

          if (node) {
            currentRange = CursorRange.fromNode(node);
          } else if (predErrorMsg) {
            new Output()
                .withString(Msgs.getMsg(predErrorMsg))
                .withQueueMode(QueueMode.FLUSH)
                .go();
            this.onFinishCommand();
            return false;
          }
        }
      }
    }

    // TODO(accessibility): extract into function.
    if (tryScrolling && currentRange &&
        !AutoScrollHandler.getInstance().onCommandNavigation(
            currentRange, dir, pred, unit, speechProps, rootPred, () => {
              this.onCommand(command);
              this.onFinishCommand();
            })) {
      this.onFinishCommand();
      return false;
    }

    if (currentRange) {
      if (currentRange.wrapped) {
        ChromeVox.earcons.playEarcon(Earcon.WRAP);
      }

      ChromeVoxState.instance.navigateToRange(
          currentRange, undefined, speechProps, skipSettingSelection);
    }

    this.onFinishCommand();
    return false;
  }

  /**
   * Finishes processing of a command.
   */
  onFinishCommand() {
    this.smartStickyMode_.stopIgnoringRangeChanges();
  }

  /**
   * Increase or decrease a speech property and make an announcement.
   * @param {string} propertyName The name of the property to change.
   * @param {boolean} increase If true, increases the property value by one
   *     step size, otherwise decreases.
   * @private
   */
  increaseOrDecreaseSpeechProperty_(propertyName, increase) {
    ChromeVox.tts.increaseOrDecreaseProperty(propertyName, increase);
  }

  /**
   * Called when an image frame is received on a node.
   * @param {!ChromeVoxEvent} event The event.
   * @private
   */
  onImageFrameUpdated_(event) {
    const target = event.target;
    if (target !== this.imageNode_) {
      return;
    }

    if (!AutomationUtil.isDescendantOf(
            ChromeVoxState.instance.currentRange.start.node, this.imageNode_)) {
      this.imageNode_.removeEventListener(
          EventType.IMAGE_FRAME_UPDATED, this.onImageFrameUpdated_, false);
      this.imageNode_ = null;
      return;
    }

    if (target.imageDataUrl) {
      ChromeVox.braille.writeRawImage(target.imageDataUrl);
      ChromeVox.braille.freeze();
    }
  }

  /**
   * Handle the command to view the first graphic within the current range
   * as braille.
   * @param {!CursorRange} currentRange The current range.
   * @private
   */
  viewGraphicAsBraille_(currentRange) {
    if (this.imageNode_) {
      this.imageNode_.removeEventListener(
          EventType.IMAGE_FRAME_UPDATED, this.onImageFrameUpdated_, false);
      this.imageNode_ = null;
    }

    // Find the first node within the current range that supports image data.
    const imageNode = AutomationUtil.findNodePost(
        currentRange.start.node, Dir.FORWARD,
        AutomationPredicate.supportsImageData);
    if (!imageNode) {
      return;
    }

    imageNode.addEventListener(
        EventType.IMAGE_FRAME_UPDATED, this.onImageFrameUpdated_, false);
    this.imageNode_ = imageNode;
    if (imageNode.imageDataUrl) {
      const event = new CustomAutomationEvent(
          EventType.IMAGE_FRAME_UPDATED, imageNode, {eventFrom: 'page'});
      this.onImageFrameUpdated_(event);
    } else {
      imageNode.getImageData(0, 0);
    }
  }

  /**
   * Provides a partial mapping from ChromeVox key combinations to
   * Search-as-a-function key as seen in Chrome OS documentation.
   * @param {string} command
   * @return {boolean} True if the command should propagate.
   * @private
   */
  onEditCommand_(command) {
    if (ChromeVox.isStickyModeOn()) {
      return true;
    }

    const textEditHandler = DesktopAutomationInterface.instance.textEditHandler;
    if (!textEditHandler ||
        !AutomationUtil.isDescendantOf(
            ChromeVoxState.instance.currentRange.start.node,
            textEditHandler.node)) {
      return true;
    }

    // Skip customized keys for read only text fields.
    if (textEditHandler.node.restriction ===
        chrome.automation.Restriction.READ_ONLY) {
      return true;
    }

    // Skips customized keys if they get suppressed in speech.
    if (AutomationPredicate.shouldOnlyOutputSelectionChangeInBraille(
            textEditHandler.node)) {
      return true;
    }

    const isMultiline = AutomationPredicate.multiline(textEditHandler.node);
    switch (command) {
      case 'previousCharacter':
        EventGenerator.sendKeyPress(KeyCode.HOME, {shift: true});
        break;
      case 'nextCharacter':
        EventGenerator.sendKeyPress(KeyCode.END, {shift: true});
        break;
      case 'previousWord':
        EventGenerator.sendKeyPress(KeyCode.HOME, {shift: true, ctrl: true});
        break;
      case 'nextWord':
        EventGenerator.sendKeyPress(KeyCode.END, {shift: true, ctrl: true});
        break;
      case 'previousObject':
        if (!isMultiline) {
          return true;
        }

        if (textEditHandler.isSelectionOnFirstLine()) {
          ChromeVoxState.instance.setCurrentRange(
              CursorRange.fromNode(textEditHandler.node));
          return true;
        }
        EventGenerator.sendKeyPress(KeyCode.HOME);
        break;
      case 'nextObject':
        if (!isMultiline) {
          return true;
        }

        if (textEditHandler.isSelectionOnLastLine()) {
          textEditHandler.moveToAfterEditText();
          return false;
        }

        EventGenerator.sendKeyPress(KeyCode.END);
        break;
      case 'previousLine':
        if (!isMultiline) {
          return true;
        }
        if (textEditHandler.isSelectionOnFirstLine()) {
          ChromeVoxState.instance.setCurrentRange(
              CursorRange.fromNode(textEditHandler.node));
          return true;
        }
        EventGenerator.sendKeyPress(KeyCode.PRIOR);
        break;
      case 'nextLine':
        if (!isMultiline) {
          return true;
        }

        if (textEditHandler.isSelectionOnLastLine()) {
          textEditHandler.moveToAfterEditText();
          return false;
        }
        EventGenerator.sendKeyPress(KeyCode.NEXT);
        break;
      case 'jumpToTop':
        EventGenerator.sendKeyPress(KeyCode.HOME, {ctrl: true});
        break;
      case 'jumpToBottom':
        EventGenerator.sendKeyPress(KeyCode.END, {ctrl: true});
        break;
      default:
        return true;
    }
    return false;
  }

  /** @override */
  skipLabelOrDescriptionFor(currentRange, dir) {
    if (!currentRange) {
      return null;
    }

    // Keep moving past all nodes acting as labels or descriptions.
    while (currentRange && currentRange.start && currentRange.start.node &&
           currentRange.start.node.role === RoleType.STATIC_TEXT) {
      // We must scan upwards as any ancestor might have a label or description.
      let ancestor = currentRange.start.node;
      while (ancestor) {
        if ((ancestor.labelFor && ancestor.labelFor.length > 0) ||
            (ancestor.descriptionFor && ancestor.descriptionFor.length > 0)) {
          break;
        }
        ancestor = ancestor.parent;
      }
      if (ancestor) {
        currentRange = currentRange.move(CursorUnit.NODE, dir);
      } else {
        break;
      }
    }

    return currentRange;
  }

  /** @private */
  announceBatteryDescription_() {
    chrome.accessibilityPrivate.getBatteryDescription(batteryDescription => {
      new Output()
          .withString(batteryDescription)
          .withQueueMode(QueueMode.FLUSH)
          .go();
    });
  }

  /** @private */
  announceNoCurrentRange_() {
    new Output()
        .withString(Msgs.getMsg(
            EventSourceState.get() === EventSourceType.TOUCH_GESTURE ?
                'no_focus_touch' :
                'no_focus'))
        .withQueueMode(QueueMode.FLUSH)
        .go();
  }

  /**
   * @param {!AutomationNode} node
   * @private
   */
  announceRichTextDescription_(node) {
    const optSubs = [];
    node.fontSize ? optSubs.push('font size: ' + node.fontSize) :
                    optSubs.push('');
    node.color ? optSubs.push(Color.getColorDescription(node.color)) :
                 optSubs.push('');
    node.bold ? optSubs.push(Msgs.getMsg('bold')) : optSubs.push('');
    node.italic ? optSubs.push(Msgs.getMsg('italic')) : optSubs.push('');
    node.underline ? optSubs.push(Msgs.getMsg('underline')) : optSubs.push('');
    node.lineThrough ? optSubs.push(Msgs.getMsg('linethrough')) :
                       optSubs.push('');
    node.fontFamily ? optSubs.push('font family: ' + node.fontFamily) :
                      optSubs.push('');

    const richTextDescription = Msgs.getMsg('rich_text_attributes', optSubs);
    new Output()
        .withString(richTextDescription)
        .withQueueMode(QueueMode.CATEGORY_FLUSH)
        .go();
  }

  /**
   * @param {AutomationNode} focusedNode
   * @private
   */
  checkForLossOfFocus_(focusedNode) {
    const cur = ChromeVoxState.instance.currentRange;
    if (cur && !cur.isValid() && focusedNode) {
      ChromeVoxState.instance.setCurrentRange(
          CursorRange.fromNode(focusedNode));
    }

    if (!focusedNode) {
      ChromeVoxState.instance.setCurrentRange(null);
      return;
    }

    // This case detects when TalkBack (in ARC++) is enabled (which also
    // covers when the ARC++ window is active). Clear the ChromeVox range
    // so keys get passed through for ChromeVox commands.
    if (ChromeVoxState.instance.talkBackEnabled &&
        // This additional check is not strictly necessary, but we use it to
        // ensure we are never inadvertently losing focus. ARC++ windows set
        // "focus" on a root view.
        focusedNode.role === RoleType.CLIENT) {
      ChromeVoxState.instance.setCurrentRange(null);
    }
  }

  /** @private */
  cycleTypingEcho_() {
    ChromeVoxState.instance.typingEcho =
        TypingEcho.cycle(ChromeVoxState.instance.typingEcho);
    let announce = '';
    switch (ChromeVoxState.instance.typingEcho) {
      case TypingEcho.CHARACTER:
        announce = Msgs.getMsg('character_echo');
        break;
      case TypingEcho.WORD:
        announce = Msgs.getMsg('word_echo');
        break;
      case TypingEcho.CHARACTER_AND_WORD:
        announce = Msgs.getMsg('character_and_word_echo');
        break;
      case TypingEcho.NONE:
        announce = Msgs.getMsg('none_echo');
        break;
    }
    ChromeVox.tts.speak(
        announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION);
  }

  /** @private */
  disableChromeVoxArcSupportForCurrentApp_() {
    chrome.accessibilityPrivate.setNativeChromeVoxArcSupportForCurrentApp(
        false, response => {
          if (response ===
              chrome.accessibilityPrivate.SetNativeChromeVoxResponse
                  .TALKBACK_NOT_INSTALLED) {
            ChromeVox.braille.write(
                NavBraille.fromText(Msgs.getMsg('announce_install_talkback')));
            ChromeVox.tts.speak(
                Msgs.getMsg('announce_install_talkback'), QueueMode.FLUSH);
          } else if (
              response ===
              chrome.accessibilityPrivate.SetNativeChromeVoxResponse
                  .NEED_DEPRECATION_CONFIRMATION) {
            ChromeVox.braille.write(NavBraille.fromText(
                Msgs.getMsg('announce_talkback_deprecation')));
            ChromeVox.tts.speak(
                Msgs.getMsg('announce_talkback_deprecation'), QueueMode.FLUSH);
          }
        });
  }

  /** @private */
  disableLogging_() {
    for (const type in ChromeVoxPrefs.loggingPrefs) {
      ChromeVoxPrefs.instance.setLoggingPrefs(
          ChromeVoxPrefs.loggingPrefs[type], false);
    }
  }

  /** @private */
  enableChromeVoxArcSupportForCurrentApp_() {
    chrome.accessibilityPrivate.setNativeChromeVoxArcSupportForCurrentApp(
        true, response => {});
  }

  /** @private */
  enableLogging_() {
    for (const type in ChromeVoxPrefs.loggingPrefs) {
      ChromeVoxPrefs.instance.setLoggingPrefs(
          ChromeVoxPrefs.loggingPrefs[type], true);
    }
  }

  /** @private */
  forceClickOnCurrentItem_() {
    if (!ChromeVoxState.instance.currentRange) {
      return;
    }
    let actionNode = ChromeVoxState.instance.currentRange.start.node;
    // Scan for a clickable, which overrides the |actionNode|.
    let clickable = actionNode;
    while (clickable && !clickable.clickable &&
           actionNode.root === clickable.root) {
      clickable = clickable.parent;
    }
    if (clickable && actionNode.root === clickable.root) {
      clickable.doDefault();
      return;
    }

    if (EventSourceState.get() === EventSourceType.TOUCH_GESTURE &&
        actionNode.state.editable) {
      // Dispatch a click to ensure the VK gets shown.
      const center = RectUtil.center(actionNode.location);
      EventGenerator.sendMouseClick(center.x, center.y);
      return;
    }

    while (actionNode.role === RoleType.INLINE_TEXT_BOX ||
           actionNode.role === RoleType.STATIC_TEXT) {
      actionNode = actionNode.parent;
    }
    if (actionNode.inPageLinkTarget) {
      ChromeVoxState.instance.navigateToRange(
          CursorRange.fromNode(actionNode.inPageLinkTarget));
      return;
    }
    actionNode.doDefault();
  }

  /**
   * @param {!AutomationNode} node
   * @return {!NewRangeData}
   * @private
   */
  getNewRangeForGoToColLastCell_(node) {
    // Try to start on the last cell of the table and allow
    // matching that node.
    let startNode = node.lastChild;
    while (startNode.lastChild && !AutomationPredicate.cellLike(startNode)) {
      startNode = startNode.lastChild;
    }
    return {node: startNode, range: CursorRange.fromNode(startNode)};
  }

  /**
   * @param {!AutomationNode} node
   * @param {!CursorRange} currentRange
   * @param {string} command
   * @return {!NewRangeData}
   * @private
   */
  getNewRangeForGoToFirstOrLastCell_(node, currentRange, command) {
    const end = AutomationUtil.findNodePost(
        node, command === 'goToLastCell' ? Dir.BACKWARD : Dir.FORWARD,
        AutomationPredicate.leaf);
    if (end) {
      return {node: end, range: CursorRange.fromNode(end)};
    }
    return {node, range: currentRange};
  }

  /**
   * @param {!CursorRange} currentRange
   * @return {!NewRangeData}
   * @private
   */
  getNewRangeForJumpToBottom_(node, currentRange) {
    if (!currentRange.start.node || !currentRange.start.node.root) {
      return {node, range: currentRange};
    }
    const newNode = AutomationUtil.findLastNode(
        currentRange.start.node.root, AutomationPredicate.object);
    if (newNode) {
      return {node: newNode, range: CursorRange.fromNode(newNode)};
    }
    return {node, range: currentRange};
  }

  /**
   * @param {!CursorRange} currentRange
   * @return {!NewRangeData}
   * @private
   */
  getNewRangeForJumpToTop_(node, currentRange) {
    if (!currentRange.start.node || !currentRange.start.node.root) {
      return {node, range: currentRange};
    }
    const newNode = AutomationUtil.findNodePost(
        currentRange.start.node.root, Dir.FORWARD, AutomationPredicate.object);
    if (newNode) {
      return {node: newNode, range: CursorRange.fromNode(newNode)};
    }
    return {node, range: currentRange};
  }

  /**
   * @param {!AutomationNode} node
   * @param {!CursorRange} currentRange
   * @param {string} command
   * @return {!NewRangeData}
   * @private
   */
  getNewRangeForGoToRowFirstOrLastCell_(node, currentRange, command) {
    let current = node;
    while (current && current.role !== RoleType.ROW) {
      current = current.parent;
    }
    if (!current) {
      return {node: current, range: currentRange};
    }
    const end = AutomationUtil.findNodePost(
        current, command === 'goToRowLastCell' ? Dir.BACKWARD : Dir.FORWARD,
        AutomationPredicate.leaf);
    if (end) {
      currentRange = CursorRange.fromNode(end);
    }
    return {node: current, range: currentRange};
  }

  /**
   * @param {AutomationNode} node
   * @param {!CursorRange} currentRange
   * @return {!NewRangeData}
   * @private
   */
  getNewRangeForJumpToDetails_(node, currentRange) {
    let current = node;
    while (current && !current.details) {
      current = current.parent;
    }
    if (current && current.details.length) {
      // TODO currently can only jump to first detail.
      currentRange = CursorRange.fromNode(current.details[0]);
    }
    return {node: current, range: currentRange};
  }

  /**
   * @param {!CursorRange} currentRange
   * @param {constants.Dir} dir
   * @return {?AutomationPredicate.Unary}
   * @private
   */
  getPredicateForGoToColFirstOrLastCell_(currentRange, dir) {
    const tableOpts = {col: true, dir, end: true};
    return AutomationPredicate.makeTableCellPredicate(
        currentRange.start.node, tableOpts);
  }

  /**
   * @param {!CursorRange} currentRange
   * @param {constants.Dir} dir
   * @return {?AutomationPredicate.Unary}
   * @private
   */
  getPredicateForNextCol_(currentRange, dir) {
    const tableOpts = {col: true, dir};
    return AutomationPredicate.makeTableCellPredicate(
        currentRange.start.node, tableOpts);
  }

  /**
   * @param {!CursorRange} currentRange
   * @param {constants.Dir} dir
   * @return {?AutomationPredicate.Unary}
   * @private
   */
  getPredicateForNextRow_(currentRange, dir) {
    const tableOpts = {row: true, dir};
    return AutomationPredicate.makeTableCellPredicate(
        currentRange.start.node, tableOpts);
  }

  /**
   * @param {!AutomationNode} node
   * @return {?AutomationPredicate.Unary}
   * @private
   */
  getPredicateForNextOrPreviousSimilarItem_(node) {
    const originalNode = node;
    let current = node;

    // Scan upwards until we get a role we don't want to ignore.
    while (current && AutomationPredicate.ignoreDuringJump(current)) {
      current = current.parent;
    }

    const useNode = current || originalNode;
    return AutomationPredicate.roles([current.role]);
  }

  /**
   * @param {!CursorRange} currentRange
   * @param {!constants.Dir} dir
   * @return {?AutomationPredicate.Unary}
   * @private
   */
  getPredicateForPreviousCol_(currentRange, dir) {
    const tableOpts = {col: true, dir};
    return AutomationPredicate.makeTableCellPredicate(
        currentRange.start.node, tableOpts);
  }

  /**
   * @param {!CursorRange} currentRange
   * @param {!constants.Dir} dir
   * @return {?AutomationPredicate.Unary}
   * @private
   */
  getPredicateForPreviousRow_(currentRange, dir) {
    const tableOpts = {row: true, dir};
    return AutomationPredicate.makeTableCellPredicate(
        currentRange.start.node, tableOpts);
  }

  /**
   * @param {AutomationNode} node
   * @return {AutomationNode|undefined} node
   * @private
   */
  getTableNode_(node) {
    let current = node;
    while (current && current.role !== RoleType.TABLE) {
      current = current.parent;
    }
    return current;
  }

  /**
   * @param {string} command
   * @return {boolean}
   * @private
   */
  isAllowed_(command) {
    if (!this.isIncognito_ && !this.isKioskSession_) {
      return true;
    }

    return !CommandStore.CMD_ALLOWLIST[command] ||
        !CommandStore.CMD_ALLOWLIST[command].denySignedOut;
  }

  /**
   * @param {boolean} isPrevious
   * @private
   */
  nextOrPreviousAtGranularity_(isPrevious) {
    let command;
    switch (GestureInterface.getGranularity()) {
      case GestureGranularity.CHARACTER:
        command = isPrevious ? 'previousCharacter' : 'nextCharacter';
        break;
      case GestureGranularity.WORD:
        command = isPrevious ? 'previousWord' : 'nextWord';
        break;
      case GestureGranularity.LINE:
        command = isPrevious ? 'previousLine' : 'nextLine';
        break;
      case GestureGranularity.HEADING:
        command = isPrevious ? 'previousHeading' : 'nextHeading';
        break;
      case GestureGranularity.LINK:
        command = isPrevious ? 'previousLink' : 'nextLink';
        break;
      case GestureGranularity.FORM_FIELD_CONTROL:
        command = isPrevious ? 'previousFormField' : 'nextFormField';
        break;
    }
    if (command) {
      this.onCommand(command);
    }
  }

  /**
   * @param {string} command
   * @param {!CursorRange} currentRange
   * @private
   */
  nextOrPreviousPage_(command, currentRange) {
    const root = AutomationUtil.getTopLevelRoot(currentRange.start.node);
    if (root && root.scrollY !== undefined) {
      let page = Math.ceil(root.scrollY / root.location.height) || 1;
      page = command === 'nextPage' ? page + 1 : page - 1;
      ChromeVox.tts.stop();
      root.setScrollOffset(0, page * root.location.height);
    }
  }

  /** @private */
  readCurrentTitle_() {
    let target = ChromeVoxState.instance.currentRange.start.node;
    const output = new Output();

    if (!target) {
      return false;
    }

    let firstWindow;
    let rootViewWindow;
    if (target.root && target.root.role === RoleType.DESKTOP) {
      // Search for the first container with a name.
      while (target && (!target.name || !AutomationPredicate.root(target))) {
        target = target.parent;
      }
    } else {
      // Search for a root window with a title.
      while (target) {
        const isNamedWindow =
            Boolean(target.name) && target.role === RoleType.WINDOW;
        const isRootView = target.className === 'RootView';
        if (isNamedWindow && !firstWindow) {
          firstWindow = target;
        }

        if (isNamedWindow && isRootView) {
          rootViewWindow = target;
          break;
        }
        target = target.parent;
      }
    }

    // Re-target with preference for the root.
    target = rootViewWindow || firstWindow || target;

    if (!target) {
      output.format('@no_title');
    } else if (target.name) {
      output.withString(target.name);
    }

    output.go();
  }

  /** @private */
  readFromHere_() {
    ChromeVoxState.instance.isReadingContinuously = true;
    const continueReading = () => {
      if (!ChromeVoxState.instance.isReadingContinuously ||
          !ChromeVoxState.instance.currentRange) {
        return;
      }

      const prevRange = ChromeVoxState.instance.currentRange;
      const newRange = ChromeVoxState.instance.currentRange.move(
          CursorUnit.NODE, Dir.FORWARD);

      // Stop if we've wrapped back to the document.
      const maybeDoc = newRange.start.node;
      if (AutomationPredicate.root(maybeDoc)) {
        ChromeVoxState.instance.isReadingContinuously = false;
        return;
      }

      ChromeVoxState.instance.setCurrentRange(newRange);
      newRange.select();

      const o = new Output()
                    .withoutHints()
                    .withRichSpeechAndBraille(
                        ChromeVoxState.instance.currentRange, prevRange,
                        OutputEventType.NAVIGATE)
                    .onSpeechEnd(continueReading);

      if (!o.hasSpeech) {
        continueReading();
        return;
      }

      o.go();
    };

    {
      const startNode = ChromeVoxState.instance.currentRange.start.node;
      const collapsedRange = CursorRange.fromNode(startNode);
      const o =
          new Output()
              .withoutHints()
              .withRichSpeechAndBraille(
                  collapsedRange, collapsedRange, OutputEventType.NAVIGATE)
              .onSpeechEnd(continueReading);

      if (o.hasSpeech) {
        o.go();
      } else {
        continueReading();
      }
    }
  }

  /**
   * @param {!AutomationNode} node
   * @private
   */
  readLinkURL_(node) {
    const rootNode = node.root;
    let current = node;
    while (current && !current.url) {
      // URL could be an ancestor of current range.
      current = current.parent;
    }
    // Announce node's URL if it's not the root node; we don't want to
    // announce the URL of the current page.
    const url = (current && current !== rootNode) ? current.url : '';
    new Output()
        .withString(
            url ? Msgs.getMsg('url_behind_link', [url]) :
                  Msgs.getMsg('no_url_found'))
        .withQueueMode(QueueMode.CATEGORY_FLUSH)
        .go();
  }

  /**
   * @param {!AutomationNode} node
   * @private
   */
  readPhoneticPronunciation_(node) {
    // Get node info.
    const index = ChromeVoxState.instance.currentRange.start.index;
    const name = node.name;
    // If there is no text to speak, inform the user and return early.
    if (!name) {
      new Output()
          .withString(Msgs.getMsg('empty_name'))
          .withQueueMode(QueueMode.CATEGORY_FLUSH)
          .go();
      return;
    }

    // Get word start and end indices.
    let wordStarts;
    let wordEnds;
    if (node.role === RoleType.INLINE_TEXT_BOX) {
      wordStarts = node.wordStarts;
      wordEnds = node.wordEnds;
    } else {
      wordStarts = node.nonInlineTextWordStarts;
      wordEnds = node.nonInlineTextWordEnds;
    }
    // Find the word we want to speak phonetically. If index === -1, then
    // the index represents an entire node.
    let text = '';
    if (index === -1) {
      text = name;
    } else {
      for (let z = 0; z < wordStarts.length; ++z) {
        if (wordStarts[z] <= index && wordEnds[z] > index) {
          text = name.substring(wordStarts[z], wordEnds[z]);
          break;
        }
      }
    }

    const language = chrome.i18n.getUILanguage();
    const phoneticText = PhoneticData.forText(text, language);
    if (phoneticText) {
      new Output()
          .withString(phoneticText)
          .withQueueMode(QueueMode.CATEGORY_FLUSH)
          .go();
    }
  }

  /** @private */
  reportIssue_() {
    let url = 'https://code.google.com/p/chromium/issues/entry?' +
        'labels=Type-Bug,Pri-2,OS-Chrome&' +
        'components=OS>Accessibility>ChromeVox&' +
        'description=';

    const description = {};
    description['Version'] = chrome.runtime.getManifest().version;
    description['Reproduction Steps'] = '%0a1.%0a2.%0a3.';
    for (const key in description) {
      url += key + ':%20' + description[key] + '%0a';
    }
    chrome.tabs.create({url});
  }

  /** @private */
  showLearnModePage_() {
    const explorerPage = {
      url: 'chromevox/learn_mode/learn_mode.html',
      type: 'panel',
    };
    chrome.windows.create(explorerPage);
  }

  /** @private */
  showTalkBackKeyboardShortcuts_() {
    chrome.tabs.create({
      url: 'https://support.google.com/accessibility/android/answer/6110948',
    });
  }

  /** @private */
  speakTimeAndDate_() {
    chrome.automation.getDesktop(d => {
      // First, try speaking the on-screen time.
      const allTime = d.findAll({role: RoleType.TIME});
      allTime.filter(time => time.root.role === RoleType.DESKTOP);

      let timeString = '';
      allTime.forEach(time => {
        if (time.name) {
          timeString = time.name;
        }
      });
      if (timeString) {
        ChromeVox.tts.speak(timeString, QueueMode.FLUSH);
        ChromeVox.braille.write(NavBraille.fromText(timeString));
      } else {
        // Fallback to the old way of speaking time.
        const output = new Output();
        const dateTime = new Date();
        output
            .withString(
                dateTime.toLocaleTimeString() + ', ' +
                dateTime.toLocaleDateString())
            .go();
      }
    });
  }

  /**
   * @param {boolean} isPrevious
   * @private
   */
  nextOrPreviousGranularity_(isPrevious) {
    let gran = GestureInterface.getGranularity();
    const next = isPrevious ?
        (--gran >= 0 ? gran : GestureGranularity.COUNT - 1) :
        ++gran % GestureGranularity.COUNT;
    GestureInterface.setGranularity(
        /** @type {GestureGranularity} */ (next));

    let announce = '';
    switch (GestureInterface.getGranularity()) {
      case GestureGranularity.CHARACTER:
        announce = Msgs.getMsg('character_granularity');
        break;
      case GestureGranularity.WORD:
        announce = Msgs.getMsg('word_granularity');
        break;
      case GestureGranularity.LINE:
        announce = Msgs.getMsg('line_granularity');
        break;
      case GestureGranularity.HEADING:
        announce = Msgs.getMsg('heading_granularity');
        break;
      case GestureGranularity.LINK:
        announce = Msgs.getMsg('link_granularity');
        break;
      case GestureGranularity.FORM_FIELD_CONTROL:
        announce = Msgs.getMsg('form_field_control_granularity');
        break;
    }
    ChromeVox.tts.speak(announce, QueueMode.FLUSH);
  }

  /** @private */
  toggleBrailleTable_() {
    let brailleTableType = localStorage['brailleTableType'];
    let output = '';
    if (brailleTableType === 'brailleTable6') {
      brailleTableType = 'brailleTable8';

      // This label reads "switch to 8 dot braille".
      output = '@OPTIONS_BRAILLE_TABLE_TYPE_6';
    } else {
      brailleTableType = 'brailleTable6';

      // This label reads "switch to 6 dot braille".
      output = '@OPTIONS_BRAILLE_TABLE_TYPE_8';
    }

    localStorage['brailleTable'] = localStorage[brailleTableType];
    localStorage['brailleTableType'] = brailleTableType;
    BrailleBackground.instance.getTranslatorManager().refresh(
        localStorage[brailleTableType]);
    new Output().format(output).go();
  }

  /** @private */
  toggleEarcons_() {
    ChromeVox.earcons.enabled = !ChromeVox.earcons.enabled;
    const announce = ChromeVox.earcons.enabled ? Msgs.getMsg('earcons_on') :
                                                 Msgs.getMsg('earcons_off');
    ChromeVox.tts.speak(
        announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION);
  }

  /** @private */
  toggleScreen_() {
    const oldState = sessionStorage.getItem('darkScreen');
    const newState = (oldState === 'true') ? false : true;
    if (newState && localStorage['acceptToggleScreen'] !== 'true') {
      // If this is the first time, show a confirmation dialog.
      chrome.accessibilityPrivate.showConfirmationDialog(
          Msgs.getMsg('toggle_screen_title'),
          Msgs.getMsg('toggle_screen_description'), confirmed => {
            if (confirmed) {
              sessionStorage.setItem('darkScreen', 'true');
              localStorage['acceptToggleScreen'] = true;
              chrome.accessibilityPrivate.darkenScreen(true);
              new Output().format('@toggle_screen_off').go();
            }
          });
    } else {
      sessionStorage.setItem('darkScreen', (newState) ? 'true' : 'false');
      chrome.accessibilityPrivate.darkenScreen(newState);
      new Output()
          .format((newState) ? '@toggle_screen_off' : '@toggle_screen_on')
          .go();
    }
  }

  /**
   * @return {boolean} whether execution should continue.
   * @private
   */
  toggleSelection_() {
    if (!ChromeVoxState.instance.pageSel) {
      ChromeVoxState.instance.pageSel = ChromeVoxState.instance.currentRange;
      DesktopAutomationInterface.instance.ignoreDocumentSelectionFromAction(
          true);
    } else {
      const root = ChromeVoxState.instance.currentRange.start.node.root;
      if (root && root.selectionStartObject && root.selectionEndObject &&
          !isNaN(Number(root.selectionStartOffset)) &&
          !isNaN(Number(root.selectionEndOffset))) {
        const sel = new CursorRange(
            new Cursor(
                root.selectionStartObject,
                /** @type {number} */ (root.selectionStartOffset)),
            new Cursor(
                root.selectionEndObject,
                /** @type {number} */ (root.selectionEndOffset)));
        const o = new Output()
                      .format('@end_selection')
                      .withSpeechAndBraille(sel, sel, OutputEventType.NAVIGATE)
                      .go();
        DesktopAutomationInterface.instance.ignoreDocumentSelectionFromAction(
            false);
      }
      ChromeVoxState.instance.pageSel = null;
      return false;
    }
    return true;
  }

  /** @private */
  toggleStickyMode_() {
    ChromeVoxBackground.setPref('sticky', !ChromeVox.isStickyPrefOn, true);
    if (ChromeVoxState.instance.currentRange) {
      this.smartStickyMode_.onStickyModeCommand(
          ChromeVoxState.instance.currentRange);
    }
  }

  /**
   * Performs global initialization.
   */
  init() {
    ChromeVoxKbHandler.commandHandler = command => this.onCommand(command);

    chrome.commandLinePrivate.hasSwitch(
        'enable-experimental-accessibility-language-detection', enabled => {
          if (enabled) {
            this.languageLoggingEnabled_ = true;
          }
        });
    chrome.commandLinePrivate.hasSwitch(
        'enable-experimental-accessibility-language-detection-dynamic',
        enabled => {
          if (enabled) {
            this.languageLoggingEnabled_ = true;
          }
        });

    chrome.chromeosInfoPrivate.get(['sessionType'], result => {
      /** @type {boolean} */
      this.isKioskSession_ = result['sessionType'] ===
          chrome.chromeosInfoPrivate.SessionType.KIOSK;
    });
  }
}

CommandHandlerInterface.instance = new CommandHandler();

BridgeHelper.registerHandler(
    BridgeConstants.CommandHandler.TARGET,
    BridgeConstants.CommandHandler.Action.ON_COMMAND,
    command => CommandHandlerInterface.instance.onCommand(command));


text:
/*                                                                  */
/* This is an automatically generated copyright prolog.             */
/* After initializing,  DO NOT MODIFY OR MOVE                       */
/* **************************************************************** */
/* (C) Copyright IBM Corp.  2016, 2016                              */
/* All Rights Reserved.                                             */
/* **************************************************************** */
/* end_generated_IBM_copyright_prolog                               */
package com.ibm.streamsx.smartsprinkler.edgent;

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

// This class implements Supplier<Reading> to provide Reading object for Topology
public class SprinklerSimulatorWithSensor extends SprinklerSimulator {
	/* begin_generated_IBM_copyright_code                               */
	public static final String IBM_COPYRIGHT =
		" (C) Copyright IBM Corp.  2016, 2016    All Rights Reserved.     " + //$NON-NLS-1$
		"                                                                 " ; //$NON-NLS-1$
	/* end_generated_IBM_copyright_code                                 */

	private static final long serialVersionUID = 1L;

	GpioPinDigitalOutput buzzer;
	GpioPinDigitalInput moistureSensor;

	final GpioController gpio = GpioFactory.getInstance();

	public SprinklerSimulatorWithSensor() {

		moistureSensor = gpio.provisionDigitalInputPin(RaspiPin.GPIO_00);

		buzzer = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_02, PinState.LOW);
		buzzer.setShutdownOptions(true, PinState.LOW);
	}

	public double getMoisture() {

		boolean isMoistureDetected = moistureSensor.isLow();

		System.out.println("Lots of moisture from sensor: " + isMoistureDetected);

		// if sensor detects that moisture is high
		if (isMoistureDetected)
		{
			moisture = 950;
		}

		return super.getMoisture();
	}

	@Override
	public void setSprinkler(boolean on) {
		super.setSprinkler(on);

		if (on)
		{
			buzzer.setState(PinState.HIGH);
		}
		else {
			buzzer.setState(PinState.LOW);
		}
	}

	@Override
	public Reading get() {
		double moisture = getMoisture();
		return new Reading(moisture, isSprinklerOn());
	}

}

redpajama_set_name: RedPajamaGithub


text:
from PyQt5 import QtGui
from PyQt5.QtWidgets import QFileDialog, QCheckBox

class H5Dialog(QFileDialog):
    def __init__(self, *args, **kwargs):
        super(H5Dialog, self).__init__(*args, **kwargs)

        self.setNameFilter("hdf5 Files (*.h5 *.hdf *.hdf5)")
        if "directory" not in list(kwargs.keys()):
            self.setDirectory('.')

        # get the grid layout of the dialog
        # TODO: make nice sub layout
        layout = self.layout()

        self.chk_branches = QCheckBox("Branch Masks")
        self.chk_freehand = QCheckBox("Freehand Masks")
        self.chk_pixel = QCheckBox("Pixel Masks")
        self.chk_circles = QCheckBox("Circle Masks")

        self.chk_traces = QCheckBox("Traces")
        self.chk_data = QCheckBox("Data")
        self.chk_mask = QCheckBox("Threshold Mask")
        self.chk_segmentation = QCheckBox("Segmentation")

        layout.addWidget(self.chk_branches)
        layout.addWidget(self.chk_freehand)
        layout.addWidget(self.chk_circles)
        layout.addWidget(self.chk_pixel)
        layout.addWidget(self.chk_traces)
        layout.addWidget(self.chk_mask)
        layout.addWidget(self.chk_data)
        layout.addWidget(self.chk_segmentation)

class H5SaveDialog(H5Dialog):
    def __init__(self, *args, **kwargs):
        super(H5SaveDialog, self).__init__(*args, **kwargs)

        self.setAcceptMode(QFileDialog.AcceptSave)
        self.setFileMode(QFileDialog.AnyFile)

        self.chk_branches.setChecked(True)
        self.chk_freehand.setChecked(True)
        self.chk_pixel.setChecked(True)
        self.chk_circles.setChecked(True)
        self.chk_traces.setChecked(True)
        self.chk_data.setChecked(False)
        self.chk_mask.setChecked(True)
        self.chk_segmentation.setChecked(True)

        self.chk_branches.setToolTip("Store the branch rois in the hdf5 file.")
        self.chk_freehand.setToolTip("Store the freehand rois in the hdf5 file.")
        self.chk_pixel.setToolTip("Store the pixel rois in the hdf5 file.")
        self.chk_circles.setToolTip("Store the circle rois in the hdf5 file.")
        self.chk_traces.setToolTip("Also store the traces of all selected rois as shown in the current preview.")
        self.chk_data.setToolTip("Store the 3D video data the hdf5 file.\n " + \
                                 "This will significantly increase the filesize.")
        self.chk_mask.setToolTip("Store the threshold value and binary mask in the hdf5 file.")
        self.chk_segmentation.setToolTip("Store your segmentations")

class H5LoadDialog(H5Dialog):
    def __init__(self, *args, **kwargs):
        super(H5LoadDialog, self).__init__(*args, **kwargs)

        self.setFileMode(QFileDialog.ExistingFile)
        self.setAcceptMode(QFileDialog.AcceptOpen)

        self.chk_branches.setEnabled(False)
        self.chk_freehand.setEnabled(False)
        self.chk_pixel.setEnabled(False)
        self.chk_circles.setEnabled(False)
        self.chk_traces.setEnabled(False)
        self.chk_data.setEnabled(False)
        self.chk_mask.setEnabled(False)
        self.chk_segmentation.setEnabled(False)

        self.chk_branches.setToolTip("Load the branch rois from the hdf5 file.")
        self.chk_freehand.setToolTip("Load the freehand rois from the hdf5 file.")
        self.chk_pixel.setToolTip("Load the pixel rois from the hdf5 file.")
        self.chk_circles.setToolTip("Load the circle rois from the hdf5 file.")
        self.chk_traces.setToolTip("The traces can never be loaded from hdf files,\n" + \
                                   " since they are defined by the masks and the data.")
        self.chk_data.setToolTip("Load the 3D video data from the hdf5 file.\n " + \
                                 "This will discard the currently selected dataset.")
        self.chk_mask.setToolTip("Load the threshold value and binary mask from the hdf5 file.")
        self.chk_segmentation.setToolTip("Load your segmentations")

        self.currentChanged.connect(self.update_checkboxes)

    def update_checkboxes(self, filename):
        # make QString to str
        filename = str(filename)
        import h5py
        f = h5py.File(filename, mode='r')

        self.chk_branches.setEnabled("branches" in f)
        self.chk_freehand.setEnabled("polygons" in f)
        self.chk_pixel.setEnabled("pixels" in f)
        self.chk_circles.setEnabled("circles" in f)
        self.chk_data.setEnabled("data" in f)
        self.chk_mask.setEnabled("mask" in f)
        self.chk_segmentation.setEnabled("segmentation" in f)

        self.chk_branches.setChecked("branches" in f)
        self.chk_freehand.setChecked("polygons" in f)
        self.chk_pixel.setChecked("pixels" in f)
        self.chk_circles.setChecked("circles" in f)
        self.chk_data.setChecked("data" in f)
        self.chk_mask.setChecked("mask" in f)
        self.chk_segmentation.setChecked("segmentation" in f)

        # write stuff to disc
        f.close()


text:
"""
Mixin for gate operator interface.
"""

from abc import ABC, abstractmethod

class AdjointMixin(ABC):
    """Abstract Mixin for operator adjoint and transpose operations.

    This class defines the following methods

        - :meth:`transpose`
        - :meth:`conjugate`
        - :meth:`adjoint`

    The following abstract methods must be implemented by subclasses
    using this mixin

        - ``conjugate(self)``
        - ``transpose(self)``
    """

    def adjoint(self):
        """Return the adjoint of the CLASS."""
        return self.conjugate().transpose()

    @abstractmethod
    def conjugate(self):
        """Return the conjugate of the CLASS."""

    @abstractmethod
    def transpose(self):
        """Return the transpose of the CLASS."""


text:
class AddIssuerAccount < ActiveRecord::Migration
  def self.up
    @issuer=User.new :email=>ISSUER_EMAIL
    @issuer.save(false)
  end

  def self.down
    @issuer=User.issuer
    @issuer.destroy
  end
end


text:
namespace {

// Defines a suite of tests that exercise browser-level configuration and
// functionality.
class HeadlessTest : public WebEngineBrowserTest {
 public:
  HeadlessTest() { set_test_server_root(base::FilePath(kTestServerRoot)); }
  ~HeadlessTest() override = default;

  HeadlessTest(const HeadlessTest&) = delete;
  HeadlessTest& operator=(const HeadlessTest&) = delete;

 protected:
  void SetUp() override {
    command_line_.GetProcessCommandLine()->AppendSwitchNative(
        switches::kOzonePlatform, switches::kHeadless);
    command_line_.GetProcessCommandLine()->AppendSwitch(switches::kHeadless);
    WebEngineBrowserTest::SetUp();
  }

  base::test::ScopedCommandLine command_line_;
};

IN_PROC_BROWSER_TEST_F(HeadlessTest, AnimationRunsWhenFrameAlreadyActive) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL kAnimationUrl(
      embedded_test_server()->GetURL("/css_animation.html"));

  auto frame =
      FrameForTest::Create(context(), fuchsia::web::CreateFrameParams());
  frame->EnableHeadlessRendering();
  LoadUrlAndExpectResponse(frame.GetNavigationController(),
                           fuchsia::web::LoadUrlParams(), kAnimationUrl.spec());
  frame.navigation_listener().RunUntilUrlAndTitleEquals(kAnimationUrl,
                                                        "animation finished");
}

IN_PROC_BROWSER_TEST_F(HeadlessTest, AnimationNeverRunsWhenInactive) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL kAnimationUrl(
      embedded_test_server()->GetURL("/css_animation.html"));
  auto frame =
      FrameForTest::Create(context(), fuchsia::web::CreateFrameParams());
  LoadUrlAndExpectResponse(frame.GetNavigationController(),
                           fuchsia::web::LoadUrlParams(), kAnimationUrl.spec());
  frame.navigation_listener().RunUntilUrlAndTitleEquals(
      kAnimationUrl, "animation never started");
}

IN_PROC_BROWSER_TEST_F(HeadlessTest, ActivateFrameAfterDocumentLoad) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL kAnimationUrl(
      embedded_test_server()->GetURL("/css_animation.html"));
  auto frame =
      FrameForTest::Create(context(), fuchsia::web::CreateFrameParams());
  LoadUrlAndExpectResponse(frame.GetNavigationController(),
                           fuchsia::web::LoadUrlParams(), kAnimationUrl.spec());
  frame.navigation_listener().RunUntilUrlAndTitleEquals(
      kAnimationUrl, "animation never started");

  frame->EnableHeadlessRendering();
  frame.navigation_listener().RunUntilUrlAndTitleEquals(kAnimationUrl,
                                                        "animation finished");
}

IN_PROC_BROWSER_TEST_F(HeadlessTest, DeactivationPreventsFutureAnimations) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL kAnimationUrl(
      embedded_test_server()->GetURL("/css_animation.html"));

  // Activate the frame and load the page. The animation should run.
  auto frame =
      FrameForTest::Create(context(), fuchsia::web::CreateFrameParams());
  frame->EnableHeadlessRendering();
  LoadUrlAndExpectResponse(frame.GetNavigationController(),
                           fuchsia::web::LoadUrlParams(), kAnimationUrl.spec());
  frame.navigation_listener().RunUntilUrlAndTitleEquals(kAnimationUrl,
                                                        "animation finished");

  // Deactivate the page and reload it. The animation should no longer run.
  frame->DisableHeadlessRendering();
  frame.GetNavigationController()->Reload(fuchsia::web::ReloadType::NO_CACHE);
  frame.navigation_listener().RunUntilUrlAndTitleEquals(
      kAnimationUrl, "animation never started");
}

}  // namespace


text:
namespace Delegates.EventModels
{
    using System;

    public class EventTimer : EventArgs
    {
        private string message;

        public EventTimer()
            : this(null)
        {

        }

        public EventTimer(string message)
        {
            this.Message = message;
        }

        public string Message
        {
            get
            {
                return this.message;
            }

            set
            {
                this.message = value;
            }
        }
    }
}


text:
package firestore

import (
	"context"
	"errors"
	"fmt"
	"sort"

	"google.golang.org/api/iterator"
	firestorepb "google.golang.org/genproto/googleapis/firestore/v1"
)

// A CollectionGroupRef is a reference to a group of collections sharing the
// same ID.
type CollectionGroupRef struct {
	c *Client

	// Use the methods of Query on a CollectionGroupRef to create and run queries.
	Query
}

func newCollectionGroupRef(c *Client, dbPath, collectionID string) *CollectionGroupRef {
	return &CollectionGroupRef{
		c: c,

		Query: Query{
			c:              c,
			collectionID:   collectionID,
			path:           dbPath,
			parentPath:     dbPath + "/documents",
			allDescendants: true,
		},
	}
}

// GetPartitionedQueries returns a slice of Query objects, each containing a
// partition of a collection group. partitionCount must be a positive value and
// the number of returned partitions may be less than the requested number if
// providing the desired number would result in partitions with very few documents.
//
// If a Collection Group Query would return a large number of documents, this
// can help to subdivide the query to smaller working units that can be distributed.
//
// If the goal is to run the queries across processes or workers, it may be useful to use
// `Query.Serialize` and `Query.Deserialize` to serialize the query.
func (cgr CollectionGroupRef) GetPartitionedQueries(ctx context.Context, partitionCount int) ([]Query, error) {
	qp, err := cgr.getPartitions(ctx, partitionCount)
	if err != nil {
		return nil, err
	}
	queries := make([]Query, len(qp))
	for i, part := range qp {
		queries[i] = part.toQuery()
	}
	return queries, nil
}

// getPartitions returns a slice of queryPartition objects, describing a start
// and end range to query a subsection of the collection group. partitionCount
// must be a positive value and the number of returned partitions may be less
// than the requested number if providing the desired number would result in
// partitions with very few documents.
func (cgr CollectionGroupRef) getPartitions(ctx context.Context, partitionCount int) ([]queryPartition, error) {
	orderedQuery := cgr.query().OrderBy(DocumentID, Asc)

	if partitionCount <= 0 {
		return nil, errors.New("a positive partitionCount must be provided")
	} else if partitionCount == 1 {
		return []queryPartition{{CollectionGroupQuery: orderedQuery}}, nil
	}

	db := cgr.c.path()
	ctx = withResourceHeader(ctx, db)

	// CollectionGroup Queries need to be ordered by __name__ ASC.
	query, err := orderedQuery.toProto()
	if err != nil {
		return nil, err
	}
	structuredQuery := &firestorepb.PartitionQueryRequest_StructuredQuery{
		StructuredQuery: query,
	}

	// Uses default PageSize
	pbr := &firestorepb.PartitionQueryRequest{
		Parent:         db + "/documents",
		PartitionCount: int64(partitionCount),
		QueryType:      structuredQuery,
	}
	cursorReferences := make([]*firestorepb.Value, 0, partitionCount)
	iter := cgr.c.c.PartitionQuery(ctx, pbr)
	for {
		cursor, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("GetPartitions: %w", err)
		}
		cursorReferences = append(cursorReferences, cursor.GetValues()...)
	}

	// From Proto documentation:
	// To obtain a complete result set ordered with respect to the results of the
	// query supplied to PartitionQuery, the results sets should be merged:
	// cursor A, cursor B, cursor M, cursor Q, cursor U, cursor W
	// Once we have exhausted the pages, the cursor values need to be sorted in
	// lexicographical order by segment (areas between '/').
	sort.Sort(byFirestoreValue(cursorReferences))

	queryPartitions := make([]queryPartition, 0, len(cursorReferences))
	previousCursor := ""

	for _, cursor := range cursorReferences {
		cursorRef := cursor.GetReferenceValue()

		// remove the root path from the reference, as queries take cursors
		// relative to a collection
		cursorRef = cursorRef[len(orderedQuery.path)+1:]

		qp := queryPartition{
			CollectionGroupQuery: orderedQuery,
			StartAt:              previousCursor,
			EndBefore:            cursorRef,
		}
		queryPartitions = append(queryPartitions, qp)
		previousCursor = cursorRef
	}

	// In the case there were no partitions, we still add a single partition to
	// the result, that covers the complete range.
	lastPart := queryPartition{CollectionGroupQuery: orderedQuery}
	if len(cursorReferences) > 0 {
		cursorRef := cursorReferences[len(cursorReferences)-1].GetReferenceValue()
		lastPart.StartAt = cursorRef[len(orderedQuery.path)+1:]
	}
	queryPartitions = append(queryPartitions, lastPart)

	return queryPartitions, nil
}

// queryPartition provides a Collection Group Reference and start and end split
// points allowing for a section of a collection group to be queried. This is
// used by GetPartitions which, given a CollectionGroupReference returns smaller
// sub-queries or partitions
type queryPartition struct {
	// CollectionGroupQuery is an ordered query on a CollectionGroupReference.
	// This query must be ordered Asc on __name__.
	// Example: client.CollectionGroup("collectionID").query().OrderBy(DocumentID, Asc)
	CollectionGroupQuery Query

	// StartAt is a document reference value, relative to the collection, not
	// a complete parent path.
	// Example: "documents/collectionName/documentName"
	StartAt string

	// EndBefore is a document reference value, relative to the collection, not
	// a complete parent path.
	// Example: "documents/collectionName/documentName"
	EndBefore string
}

// toQuery converts a queryPartition object to a Query object
func (qp queryPartition) toQuery() Query {
	q := *qp.CollectionGroupQuery.query()

	// Remove the leading path before calling StartAt, EndBefore
	if qp.StartAt != "" {
		q = q.StartAt(qp.StartAt)
	}
	if qp.EndBefore != "" {
		q = q.EndBefore(qp.EndBefore)
	}
	return q
}


text:
<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class GeoDistricts extends Entity
{
    protected $_accessible = [
        'geo_division_id' => true,
        'division_bbs_code' => true,
        'district_name_eng' => true,
        'district_name_bng' => true,
        'bbs_code' => true,
        'status' => true,
        'created_by' => true,
        'modified_by' => true,
        'geo_division' => true,
        'geo_city_corporation_wards' => true,
        'geo_city_corporations' => true,
        'geo_municipalities' => true,
        'geo_municipality_wards' => true,
        'geo_post_offices' => true,
        'geo_thanas' => true,
        'geo_unions' => true,
        'geo_upazilas' => true,
        'office_geos' => true,
    ];
}


text:
using System;

[AttributeUsage(AttributeTargets.Enum, AllowMultiple = true)]
public class TypeAttribute : Attribute
{
    public TypeAttribute(string type, string category, string description)
    {
        this.Type = type;
        this.Category = category;
        this.Description = description;
    }

    public string Type { get; set; }
    public string Category { get; set; }
    public string Description { get; set; }
}


text:
package com.swfm.mica;

/**
 * @version     %I% %G%
 * @author      Michael L. Davis
 * @release 	1.4.1
 * @module 	%M%
 * @language	Java (JDK 1.4)
 */
public class MiIExecuteActionHandler extends MiEventHandler
	{
	private		MiiAction	action;

	public				MiIExecuteActionHandler()
		{
		}
	public				MiIExecuteActionHandler(MiEvent trigger, MiiAction action)
		{
		addEventToCommandTranslation(
			Mi_EXECUTE_COMMAND_NAME, trigger.type, trigger.key, trigger.modifiers);
		this.action = action;
		}
	public		void		setActionToExecute(MiiAction action)
		{
		this.action = action;
		}
	public		MiiAction	getActionToExecute()
		{
		return(action);
		}

	public		MiiEventHandler	copy()
		{
		MiIExecuteActionHandler obj = (MiIExecuteActionHandler )super.copy();
		obj.action = action;
		return(obj);
		}
					/**------------------------------------------------------
	 				 * Processes the command generated from the current event.
					 * Both are stored in the MiEventHandler super class.
					 * @return 		Mi_CONSUME_EVENT if no other event
					 *			handlers should see the event that
					 *			generated the command
					 *			Mi_PROPOGATE_EVENT if other event
					 *			handlers can also see the event
					 *			that generated the command
					 * @see			MiEventHandler#isCommand
					 * @overrides		MiEventHandler#processCommand
					 *------------------------------------------------------*/
	protected	int		processCommand()
		{
		action.setActionSource(event.target);
		if (action.getActionHandler().processAction(action))
			return(Mi_PROPOGATE_EVENT);
		else
			return(Mi_CONSUME_EVENT);
		}
	}


text:
using System;
using System.Collections;

namespace NUnit.Framework.Tests
{
	class CollectionAdapter : ICollection
	{
		private readonly ICollection inner;
		public CollectionAdapter(ICollection inner)
		{
			this.inner = inner;
		}
		#region ICollection Members

		public void CopyTo(Array array, int index)
		{
			inner.CopyTo(array, index);
		}

		public int Count
		{
			get { return inner.Count; }
		}

		public bool IsSynchronized
		{
			get { return  inner.IsSynchronized; }
		}

		public object SyncRoot
		{
			get { return inner.SyncRoot; }
		}

		#endregion

		#region IEnumerable Members

		public IEnumerator GetEnumerator()
		{
			return inner.GetEnumerator();
		}

		#endregion
	}
}


text:
001
017
018    package org.apache.commons.math3.analysis.function;
019
020    import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
021    import org.apache.commons.math3.analysis.FunctionUtils;
022    import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
023    import org.apache.commons.math3.analysis.UnivariateFunction;
024    import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
025    import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
026    import org.apache.commons.math3.exception.DimensionMismatchException;
027    import org.apache.commons.math3.exception.NullArgumentException;
028    import org.apache.commons.math3.util.FastMath;
029
030    /**
031     * <a href="http://en.wikipedia.org/wiki/Harmonic_oscillator">
032     *  simple harmonic oscillator</a> function.
033     *
034     * @since 3.0
035     * @version $Id: HarmonicOscillator.java 1383441 2012-09-11 14:56:39Z luc $
036     */
037    public class HarmonicOscillator implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
038        /** Amplitude. */
039        private final double amplitude;
040        /** Angular frequency. */
041        private final double omega;
042        /** Phase. */
043        private final double phase;
044
045        /**
046         * Harmonic oscillator function.
047         *
048         * @param amplitude Amplitude.
049         * @param omega Angular frequency.
050         * @param phase Phase.
051         */
052        public HarmonicOscillator(double amplitude,
053                                  double omega,
054                                  double phase) {
055            this.amplitude = amplitude;
056            this.omega = omega;
057            this.phase = phase;
058        }
059
060        /** {@inheritDoc} */
061        public double value(double x) {
062            return value(omega * x + phase, amplitude);
063        }
064
065        /** {@inheritDoc}
066         * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
067         */
068        @Deprecated
069        public UnivariateFunction derivative() {
070            return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
071        }
072
073        /**
074         * Parametric function where the input array contains the parameters of
075         * the harmonic oscillator function, ordered as follows:
076         * <ul>
077         *  <li>Amplitude</li>
078         *  <li>Angular frequency</li>
079         *  <li>Phase</li>
080         * </ul>
081         */
082        public static class Parametric implements ParametricUnivariateFunction {
083            /**
084             * Computes the value of the harmonic oscillator at {@code x}.
085             *
086             * @param x Value for which the function must be computed.
087             * @param param Values of norm, mean and standard deviation.
088             * @return the value of the function.
089             * @throws NullArgumentException if {@code param} is {@code null}.
090             * @throws DimensionMismatchException if the size of {@code param} is
091             * not 3.
092             */
093            public double value(double x, double ... param)
094                throws NullArgumentException,
095                       DimensionMismatchException {
096                validateParameters(param);
097                return HarmonicOscillator.value(x * param[1] + param[2], param[0]);
098            }
099
100            /**
101             * Computes the value of the gradient at {@code x}.
102             * The components of the gradient vector are the partial
103             * derivatives of the function with respect to each of the
104             * <em>parameters</em> (amplitude, angular frequency and phase).
105             *
106             * @param x Value at which the gradient must be computed.
107             * @param param Values of amplitude, angular frequency and phase.
108             * @return the gradient vector at {@code x}.
109             * @throws NullArgumentException if {@code param} is {@code null}.
110             * @throws DimensionMismatchException if the size of {@code param} is
111             * not 3.
112             */
113            public double[] gradient(double x, double ... param)
114                throws NullArgumentException,
115                       DimensionMismatchException {
116                validateParameters(param);
117
118                final double amplitude = param[0];
119                final double omega = param[1];
120                final double phase = param[2];
121
122                final double xTimesOmegaPlusPhase = omega * x + phase;
123                final double a = HarmonicOscillator.value(xTimesOmegaPlusPhase, 1);
124                final double p = -amplitude * FastMath.sin(xTimesOmegaPlusPhase);
125                final double w = p * x;
126
127                return new double[] { a, w, p };
128            }
129
130            /**
131             * Validates parameters to ensure they are appropriate for the evaluation of
132             * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
133             * methods.
134             *
135             * @param param Values of norm, mean and standard deviation.
136             * @throws NullArgumentException if {@code param} is {@code null}.
137             * @throws DimensionMismatchException if the size of {@code param} is
138             * not 3.
139             */
140            private void validateParameters(double[] param)
141                throws NullArgumentException,
142                       DimensionMismatchException {
143                if (param == null) {
144                    throw new NullArgumentException();
145                }
146                if (param.length != 3) {
147                    throw new DimensionMismatchException(param.length, 3);
148                }
149            }
150        }
151
152        /**
153         * @param xTimesOmegaPlusPhase {@code omega * x + phase}.
154         * @param amplitude Amplitude.
155         * @return the value of the harmonic oscillator function at {@code x}.
156         */
157        private static double value(double xTimesOmegaPlusPhase,
158                                    double amplitude) {
159            return amplitude * FastMath.cos(xTimesOmegaPlusPhase);
160        }
161
162        /** {@inheritDoc}
163         * @since 3.1
164         */
165        public DerivativeStructure value(final DerivativeStructure t) {
166            final double x = t.getValue();
167            double[] f = new double[t.getOrder() + 1];
168
169            final double alpha = omega * x + phase;
170            f[0] = amplitude * FastMath.cos(alpha);
171            if (f.length > 1) {
172                f[1] = -amplitude * omega * FastMath.sin(alpha);
173                final double mo2 = - omega * omega;
174                for (int i = 2; i < f.length; ++i) {
175                    f[i] = mo2 * f[i - 2];
176                }
177            }
178
179            return t.compose(f);
180
181        }
182
183    }


text:
/**
 * Represents an individual tab within a Stack's header
 *
 * @param {lm.controls.Header} header
 * @param {lm.items.AbstractContentItem} contentItem
 *
 * @constructor
 */
lm.controls.Tab = function (header, contentItem) {
	this.header = header;
	this.contentItem = contentItem;
	this.element = $(lm.controls.Tab._template);
	this.titleElement = this.element.find('.lm_title');
	this.closeElement = this.element.find('.lm_close_tab');
	this.closeElement[contentItem.config.isClosable ? 'show' : 'hide']();
	this.isActive = false;

	this.setTitle(contentItem.config.title);
	this.contentItem.on('titleChanged', this.setTitle, this);

	this._layoutManager = this.contentItem.layoutManager;

	if (
		this._layoutManager.config.settings.reorderEnabled === true &&
		contentItem.config.reorderEnabled === true
	) {
		this._dragListener = new lm.utils.DragListener(this.element);
		this._dragListener.on('dragStart', this._onDragStart, this);
		this.contentItem.on('destroy', this._dragListener.destroy, this._dragListener);
	}

	this._onTabClickFn = lm.utils.fnBind(this._onTabClick, this);
	this._onCloseClickFn = lm.utils.fnBind(this._onCloseClick, this);

	if (this._layoutManager.config.settings.mode === "workspace" && contentItem.config.noTabHeader) {
		this.element.hide();
	}

	this.element.on('mousedown touchstart', this._onTabClickFn);

	if (this.contentItem.config.isClosable) {
		this.closeElement.on('click touchstart', this._onCloseClickFn);
		this.closeElement.on('mousedown', this._onCloseMousedown);
	} else {
		this.closeElement.remove();
	}

	this.contentItem.tab = this;
	this.contentItem.emit('tab', this);
	this.contentItem.layoutManager.emit('tabCreated', this);

	if (this.contentItem.isComponent) {
		this.contentItem.container.tab = this;
		this.contentItem.container.emit('tab', this);
	}
};

/**
 * The tab's html template
 *
 * @type {String}
 */
lm.controls.Tab._template = '
' +
	'

' +
	'
';

lm.utils.copy(lm.controls.Tab.prototype, {

	/**
	 * Sets the tab's title to the provided string and sets
	 * its title attribute to a pure text representation (without
	 * html tags) of the same string.
	 *
	 * @public
	 * @param {String} title can contain html
	 */
	setTitle: function (title) {
		this.element.attr('title', lm.utils.stripTags(title));
		this.titleElement.html(title);
	},

	onCloseClick: undefined,

	/**
	 * Sets this tab's active state. To programmatically
	 * switch tabs, use header.setActiveContentItem( item ) instead.
	 *
	 * @public
	 * @param {Boolean} isActive
	 */
	setActive: function (isActive) {
		if (isActive === this.isActive) {
			return;
		}
		this.isActive = isActive;

		if (isActive) {
			this.element.addClass('lm_active');
		} else {
			this.element.removeClass('lm_active');
		}
	},

	/**
	 * Destroys the tab
	 *
	 * @private
	 * @returns {void}
	 */
	_$destroy: function () {
		this.element.off('mousedown touchstart', this._onTabClickFn);
		this.closeElement.off('click touchstart', this._onCloseClickFn);
		if (this._dragListener) {
			this.contentItem.off('destroy', this._dragListener.destroy, this._dragListener);
			this._dragListener.off('dragStart', this._onDragStart);
			this._dragListener = null;
		}
		this.element.remove();
	},

	/**
	 * Callback for the DragListener
	 *
	 * @param   {Number} x The tabs absolute x position
	 * @param   {Number} y The tabs absolute y position
	 *
	 * @private
	 * @returns {void}
	 */
	_onDragStart: function (x, y) {
		if (this.contentItem.parent.isMaximized === true) {
			this.contentItem.parent.toggleMaximise();
		}
		const isWorkspaceLayout = this.contentItem.layoutManager.config.settings.mode === "workspace";
		const hasLessThanTwoTabs = this.contentItem.parent.header.tabs.length < 2;
		const isMissingWindowId = !this.contentItem.config.windowId && (this.contentItem.config.componentState && !this.contentItem.config.componentState.windowId);

		if (isWorkspaceLayout && hasLessThanTwoTabs) {
			return;
		}

		if (!isWorkspaceLayout && isMissingWindowId) {
			return;
		}

		if (this._layoutManager.config && this._layoutManager.config.workspacesOptions.allowExtract === false &&
			this.contentItem.config.workspacesConfig && this.contentItem.config.workspacesConfig.allowExtract !== true) {
			return;
		} else if (this.contentItem.config.workspacesConfig && this.contentItem.config.workspacesConfig.allowExtract === false) {
			return;
		}

		const newProxy = new lm.controls.DragProxy(
			x,
			y,
			this._dragListener,
			this._layoutManager,
			this.contentItem,
			this.header.parent
		);

		this._layoutManager._dragProxies.push(newProxy);
	},

	/**
	 * Callback when the tab is clicked
	 *
	 * @param {jQuery DOM event} event
	 *
	 * @private
	 * @returns {void}
	 */
	_onTabClick: function (event) {
		// left mouse button or tap
		if (event.button === 0 || event.type === 'touchstart') {
			var activeContentItem = this.header.parent.getActiveContentItem();
			if (this.contentItem !== activeContentItem) {
				this.header.parent.setActiveContentItem(this.contentItem);
			}

			// middle mouse button
		} else if (event.button === 1 && this.contentItem.config.isClosable) {
			this._onCloseClick(event);
		}
	},

	/**
	 * Callback when the tab's close button is
	 * clicked
	 *
	 * @param   {jQuery DOM event} event
	 *
	 * @private
	 * @returns {void}
	 */
	_onCloseClick: function (event) {
		this.contentItem.layoutManager.emit('tabCloseRequested', this);
		if (this.onCloseClick) {
			this.onCloseClick(event)
		}
	},

	/**
	 * Callback to capture tab close button mousedown
	 * to prevent tab from activating.
	 *
	 * @param (jQuery DOM event) event
	 *
	 * @private
	 * @returns {void}
	 */
	_onCloseMousedown: function (event) {
		event.stopPropagation();
	}
});


text:
package org.apache.ode.bpel.obj;

import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonCreator;

public class OCompensationHandler extends OScope  implements Serializable{
	public static final long serialVersionUID = -1L;

	@JsonCreator
	public OCompensationHandler(){}

    public OCompensationHandler(OProcess owner, OActivity parent) {
        super(owner, parent);
    }
}