<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('cr.ui', function() {

  /** @const */ var Menu = cr.ui.Menu;

  /**
   * Handles context menus.
   * @constructor
   */
  function ContextMenuHandler() {}

  ContextMenuHandler.prototype = {

    /**
     * The menu that we are currently showing.
     * @type {cr.ui.Menu}
     */
    menu_: null,
    get menu() {
      return this.menu_;
    },

    /**
     * Shows a menu as a context menu.
     * @param {!Event} e The event triggering the show (usually a contextmenu
     *     event).
     * @param {!cr.ui.Menu} menu The menu to show.
     */
    showMenu: function(e, menu) {
      this.menu_ = menu;
      menu.updateCommands(e.currentTarget);
      menu.hidden = false;
      menu.contextElement = e.currentTarget;

      // when the menu is shown we steal all keyboard events.
      var doc = menu.ownerDocument;
      doc.addEventListener('keydown', this, true);
      doc.addEventListener('mousedown', this, true);
      doc.addEventListener('focus', this, true);
      doc.defaultView.addEventListener('resize', this);
      menu.addEventListener('contextmenu', this);
      menu.addEventListener('activate', this);
      this.positionMenu_(e, menu);
    },

    /**
     * Hide the currently shown menu.
     */
    hideMenu: function() {
      var menu = this.menu;
      if (!menu)
        return;

      menu.hidden = true;
      menu.contextElement = null;
      var doc = menu.ownerDocument;
      doc.removeEventListener('keydown', this, true);
      doc.removeEventListener('mousedown', this, true);
      doc.removeEventListener('focus', this, true);
      doc.defaultView.removeEventListener('resize', this);
      menu.removeEventListener('contextmenu', this);
      menu.removeEventListener('activate', this);
      menu.selectedIndex = -1;
      this.menu_ = null;

      // On windows we might hide the menu in a right mouse button up and if
      // that is the case we wait some short period before we allow the menu
      // to be shown again.
      this.hideTimestamp_ = cr.isWindows ? Date.now() : 0;
    },

    /**
     * Positions the menu
     * @param {!Event} e The event object triggering the showing.
     * @param {!cr.ui.Menu} menu The menu to position.
     * @private
     */
    positionMenu_: function(e, menu) {
      // TODO(arv): Handle scrolled documents when needed.

      var element = e.currentTarget;
      var x, y;
      // When the user presses the context menu key (on the keyboard) we need
      // to detect this.
      if (this.keyIsDown_) {
        var rect = element.getRectForContextMenu ?
                       element.getRectForContextMenu() :
                       element.getBoundingClientRect();
        var offset = Math.min(rect.width, rect.height) / 2;
        x = rect.left + offset;
        y = rect.top + offset;
      } else {
        x = e.clientX;
        y = e.clientY;
      }

      cr.ui.positionPopupAtPoint(x, y, menu);
    },

    /**
     * Handles event callbacks.
     * @param {!Event} e The event object.
     */
    handleEvent: function(e) {
      // Keep track of keydown state so that we can use that to determine the
      // reason for the contextmenu event.
      switch (e.type) {
        case 'keydown':
          this.keyIsDown_ = !e.ctrlKey && !e.altKey &&
              // context menu key or Shift-F10
              (e.keyCode == 93 && !e.shiftKey ||
               e.keyIdentifier == 'F10' && e.shiftKey);
          break;

        case 'keyup':
          this.keyIsDown_ = false;
          break;
      }

      // Context menu is handled even when we have no menu.
      if (e.type != 'contextmenu' && !this.menu)
        return;

      switch (e.type) {
        case 'mousedown':
          if (!this.menu.contains(e.target))
            this.hideMenu();
          else
            e.preventDefault();
          break;
        case 'keydown':
          // keyIdentifier does not report 'Esc' correctly
          if (e.keyCode == 27 /* Esc */) {
            this.hideMenu();
            e.stopPropagation();
            e.preventDefault();

          // If the menu is visible we let it handle all the keyboard events.
          } else if (this.menu) {
            this.menu.handleKeyDown(e);
            e.preventDefault();
            e.stopPropagation();
          }
          break;

        case 'focus':
          if (!this.menu.contains(e.target))
            this.hideMenu();
          break;

        case 'activate':
        case 'resize':
          this.hideMenu();
          break;

        case 'contextmenu':
          if ((!this.menu || !this.menu.contains(e.target)) &&
              (!this.hideTimestamp_ || Date.now() - this.hideTimestamp_ > 50))
            this.showMenu(e, e.currentTarget.contextMenu);
          e.preventDefault();
          // Don't allow elements further up in the DOM to show their menus.
          e.stopPropagation();
          break;
      }
    },

    /**
     * Adds a contextMenu property to an element or element class.
     * @param {!Element|!Function} element The element or class to add the
     *     contextMenu property to.
     */
    addContextMenuProperty: function(element) {
      if (typeof element == 'function')
        element = element.prototype;

      element.__defineGetter__('contextMenu', function() {
        return this.contextMenu_;
      });
      element.__defineSetter__('contextMenu', function(menu) {
        var oldContextMenu = this.contextMenu;

        if (typeof menu == 'string' && menu[0] == '#') {
          menu = this.ownerDocument.getElementById(menu.slice(1));
          cr.ui.decorate(menu, Menu);
        }

        if (menu === oldContextMenu)
          return;

        if (oldContextMenu && !menu) {
          this.removeEventListener('contextmenu', contextMenuHandler);
          this.removeEventListener('keydown', contextMenuHandler);
          this.removeEventListener('keyup', contextMenuHandler);
        }
        if (menu && !oldContextMenu) {
          this.addEventListener('contextmenu', contextMenuHandler);
          this.addEventListener('keydown', contextMenuHandler);
          this.addEventListener('keyup', contextMenuHandler);
        }

        this.contextMenu_ = menu;

        if (menu && menu.id)
          this.setAttribute('contextmenu', '#' + menu.id);

        cr.dispatchPropertyChange(this, 'contextMenu', menu, oldContextMenu);
      });

      if (!element.getRectForContextMenu) {
        /**
         * @return {!ClientRect} The rect to use for positioning the context
         *     menu when the context menu is not opened using a mouse position.
         */
        element.getRectForContextMenu = function() {
          return this.getBoundingClientRect();
        };
      }
    },

    /**
     * Sets the given contextMenu to the given element. A contextMenu property
     * would be added if necessary.
     * @param {!Element} element The element or class to set the contextMenu to.
     * @param {!cr.ui.Menu} contextMenu The contextMenu property to be set.
     */
    setContextMenu: function(element, contextMenu) {
      if (!element.contextMenu)
        this.addContextMenuProperty(element);
      element.contextMenu = contextMenu;
    }
  };

  /**
   * The singleton context menu handler.
   * @type {!ContextMenuHandler}
   */
  var contextMenuHandler = new ContextMenuHandler;

  // Export
  return {
    contextMenuHandler: contextMenuHandler
  };
});
<commit_msg>Listen for blur in context menu handler.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('cr.ui', function() {

  /** @const */ var Menu = cr.ui.Menu;

  /**
   * Handles context menus.
   * @constructor
   */
  function ContextMenuHandler() {}

  ContextMenuHandler.prototype = {

    /**
     * The menu that we are currently showing.
     * @type {cr.ui.Menu}
     */
    menu_: null,
    get menu() {
      return this.menu_;
    },

    /**
     * Shows a menu as a context menu.
     * @param {!Event} e The event triggering the show (usually a contextmenu
     *     event).
     * @param {!cr.ui.Menu} menu The menu to show.
     */
    showMenu: function(e, menu) {
      this.menu_ = menu;
      menu.updateCommands(e.currentTarget);
      menu.hidden = false;
      menu.contextElement = e.currentTarget;

      // when the menu is shown we steal all keyboard events.
      var doc = menu.ownerDocument;
      doc.addEventListener('keydown', this, true);
      doc.addEventListener('mousedown', this, true);
      // Note: this should be listening for focus, as in menu_button.js, but
      // as per crbug.com/162190 this indirectly causes odd behaviour.
      // Since the context menu is currently not keyboard-accessible, blur
      // is sufficient for now.
      doc.addEventListener('blur', this, true);
      doc.defaultView.addEventListener('resize', this);
      menu.addEventListener('contextmenu', this);
      menu.addEventListener('activate', this);
      this.positionMenu_(e, menu);
    },

    /**
     * Hide the currently shown menu.
     */
    hideMenu: function() {
      var menu = this.menu;
      if (!menu)
        return;

      menu.hidden = true;
      menu.contextElement = null;
      var doc = menu.ownerDocument;
      doc.removeEventListener('keydown', this, true);
      doc.removeEventListener('mousedown', this, true);
      doc.removeEventListener('blur', this, true);
      doc.defaultView.removeEventListener('resize', this);
      menu.removeEventListener('contextmenu', this);
      menu.removeEventListener('activate', this);
      menu.selectedIndex = -1;
      this.menu_ = null;

      // On windows we might hide the menu in a right mouse button up and if
      // that is the case we wait some short period before we allow the menu
      // to be shown again.
      this.hideTimestamp_ = cr.isWindows ? Date.now() : 0;
    },

    /**
     * Positions the menu
     * @param {!Event} e The event object triggering the showing.
     * @param {!cr.ui.Menu} menu The menu to position.
     * @private
     */
    positionMenu_: function(e, menu) {
      // TODO(arv): Handle scrolled documents when needed.

      var element = e.currentTarget;
      var x, y;
      // When the user presses the context menu key (on the keyboard) we need
      // to detect this.
      if (this.keyIsDown_) {
        var rect = element.getRectForContextMenu ?
                       element.getRectForContextMenu() :
                       element.getBoundingClientRect();
        var offset = Math.min(rect.width, rect.height) / 2;
        x = rect.left + offset;
        y = rect.top + offset;
      } else {
        x = e.clientX;
        y = e.clientY;
      }

      cr.ui.positionPopupAtPoint(x, y, menu);
    },

    /**
     * Handles event callbacks.
     * @param {!Event} e The event object.
     */
    handleEvent: function(e) {
      // Keep track of keydown state so that we can use that to determine the
      // reason for the contextmenu event.
      switch (e.type) {
        case 'keydown':
          this.keyIsDown_ = !e.ctrlKey && !e.altKey &&
              // context menu key or Shift-F10
              (e.keyCode == 93 && !e.shiftKey ||
               e.keyIdentifier == 'F10' && e.shiftKey);
          break;

        case 'keyup':
          this.keyIsDown_ = false;
          break;
      }

      // Context menu is handled even when we have no menu.
      if (e.type != 'contextmenu' && !this.menu)
        return;

      switch (e.type) {
        case 'mousedown':
          if (!this.menu.contains(e.target))
            this.hideMenu();
          else
            e.preventDefault();
          break;
        case 'keydown':
          // keyIdentifier does not report 'Esc' correctly
          if (e.keyCode == 27 /* Esc */) {
            this.hideMenu();
            e.stopPropagation();
            e.preventDefault();

          // If the menu is visible we let it handle all the keyboard events.
          } else if (this.menu) {
            this.menu.handleKeyDown(e);
            e.preventDefault();
            e.stopPropagation();
          }
          break;

        case 'activate':
        case 'blur':
        case 'resize':
          this.hideMenu();
          break;

        case 'contextmenu':
          if ((!this.menu || !this.menu.contains(e.target)) &&
              (!this.hideTimestamp_ || Date.now() - this.hideTimestamp_ > 50))
            this.showMenu(e, e.currentTarget.contextMenu);
          e.preventDefault();
          // Don't allow elements further up in the DOM to show their menus.
          e.stopPropagation();
          break;
      }
    },

    /**
     * Adds a contextMenu property to an element or element class.
     * @param {!Element|!Function} element The element or class to add the
     *     contextMenu property to.
     */
    addContextMenuProperty: function(element) {
      if (typeof element == 'function')
        element = element.prototype;

      element.__defineGetter__('contextMenu', function() {
        return this.contextMenu_;
      });
      element.__defineSetter__('contextMenu', function(menu) {
        var oldContextMenu = this.contextMenu;

        if (typeof menu == 'string' && menu[0] == '#') {
          menu = this.ownerDocument.getElementById(menu.slice(1));
          cr.ui.decorate(menu, Menu);
        }

        if (menu === oldContextMenu)
          return;

        if (oldContextMenu && !menu) {
          this.removeEventListener('contextmenu', contextMenuHandler);
          this.removeEventListener('keydown', contextMenuHandler);
          this.removeEventListener('keyup', contextMenuHandler);
        }
        if (menu && !oldContextMenu) {
          this.addEventListener('contextmenu', contextMenuHandler);
          this.addEventListener('keydown', contextMenuHandler);
          this.addEventListener('keyup', contextMenuHandler);
        }

        this.contextMenu_ = menu;

        if (menu && menu.id)
          this.setAttribute('contextmenu', '#' + menu.id);

        cr.dispatchPropertyChange(this, 'contextMenu', menu, oldContextMenu);
      });

      if (!element.getRectForContextMenu) {
        /**
         * @return {!ClientRect} The rect to use for positioning the context
         *     menu when the context menu is not opened using a mouse position.
         */
        element.getRectForContextMenu = function() {
          return this.getBoundingClientRect();
        };
      }
    },

    /**
     * Sets the given contextMenu to the given element. A contextMenu property
     * would be added if necessary.
     * @param {!Element} element The element or class to set the contextMenu to.
     * @param {!cr.ui.Menu} contextMenu The contextMenu property to be set.
     */
    setContextMenu: function(element, contextMenu) {
      if (!element.contextMenu)
        this.addContextMenuProperty(element);
      element.contextMenu = contextMenu;
    }
  };

  /**
   * The singleton context menu handler.
   * @type {!ContextMenuHandler}
   */
  var contextMenuHandler = new ContextMenuHandler;

  // Export
  return {
    contextMenuHandler: contextMenuHandler
  };
});
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
(function($, cloudStack) {
  $.extend(cloudStack, {
    ui: {
      widgets: {} // Defines API methods for UI widgets
    },
    uiCustom: {}
  });

  /**
   * Generate navigation
s
   *
   * @param args cloudStack data args
   */
  var makeNavigation = function(args) {
    var $navList = $('
');
    var preFilter = cloudStack.sectionPreFilter ?
          cloudStack.sectionPreFilter({
            context: $.extend(true, {}, args.context, {
              sections: $.map(cloudStack.sections, function(value, key) {
                return key;
              })
            })
          }) : null;

    $.each(args.sections, function(sectionID, args) {
      if (preFilter && $.inArray(sectionID, preFilter) == -1) {
        return true;
      }

      var $li = $('
')
            .addClass('navigation-item')
            .addClass(sectionID)
            .append($('').addClass('icon').html(' '))
            .append($('').html(_l(args.title)))
            .data('cloudStack-section-id', sectionID);

      $li.appendTo($navList);

      return true;
    });

    // Special classes for first and last items
    $navList.find('li:first').addClass('first');
    $navList.find('li:last').addClass('last');

    return $navList;
  };

  /**
   * Create section contents
   *
   * @param sectionID Section's ID to show
   * @param args CloudStack3 configuration
   */
  var showSection = function(sectionID, args, $browser) {
    var $navItem = $('#navigation').find('li').filter(function() {
      return $(this).hasClass(sectionID);
    });
    var data = args.sections[sectionID];

    data.$browser = $browser;
    $navItem.siblings().removeClass('active');
    $navItem.addClass('active');

    // Reset browser panels
    $browser.cloudBrowser('removeAllPanels');
    $browser.cloudBrowser('addPanel', {
      title: '' + _l(data.title) + '' + '',
      data: '',
      complete: function($panel, $breadcrumb) {
        $breadcrumb.attr('title', _l(data.title));
        data.$breadcrumb = $breadcrumb;

        // Hide breadcrumb if this is the home section
        if (args.home === sectionID) {
          $('#breadcrumbs').find('li:first, div.end:last').hide();
        }

        // Append specified widget to view
        if (data.show)
          $panel.append(data.show(data));
        else if (data.treeView)
          $panel.treeView(data, { context: args.context });
        else
          $panel.listView(data, { context: args.context });
      }
    });

    return $navItem;
  };

  // Define page element generation fns
  var pageElems = {
    header: function(args) {
      // Make notification area
      var $notificationArea = $('
').addClass('button notifications')
            .append(
              $('
').addClass('total')
              // Total notifications
                .append($('').html(0))
            )
            .append($('').html(_l('label.notifications')))
            .notifications();

      // Project switcher
      var $viewSwitcher = $('
').addClass('button view-switcher')
            .append(
              // Default View
              $('
').addClass('select default-view active')
                .html(_l('label.default.view'))
                .prepend(
                  $('').addClass('icon').html(' ')
                )
            )
            .append(
              // Project View
              $('
').addClass('select project-view')
                .html(_l('label.project.view'))
                .prepend(
                  $('').addClass('icon').html(' ')
                )
            )
            .click(function(event) {
              var $target = $(event.target);
              var $projectSwitcher = $(this);
              var $container = $('html body');
              var $navDisabled = $(
                $.map([
                  'projects',
                  'accounts',
                  'domains',
                  'system',
                  'global-settings',
                  'configuration'
                ], function(id) {
                  return '#navigation li.' + id;
                }).join(',')
              );

              if ($target.closest('.select.project-view').size()) {
                $('#cloudStack3-container').addClass('project-view');
                $projectSwitcher.addClass('alt');
                $projectSwitcher.find('.select.project-view').addClass('active')
                  .siblings().removeClass('active');

                // Activate project view
                $navDisabled.hide();
                cloudStack.uiCustom.projects({
                  $projectSelect: $projectSelect.hide().find('select')
                });
              } else {
                $navDisabled.show();
                $('#cloudStack3-container').removeClass('project-view');
                $projectSwitcher.removeClass('alt');
                $projectSelect.hide();
                $projectSwitcher.find('.select.default-view').addClass('active')
                  .siblings().removeClass('active');

                // Put project name in header
                $('.select.project-view').html(
                  ' ' + _l('label.project.view')
                ).attr('title', '');

                // Clear out project
                cloudStack.context.projects = null;
              }

              $('#navigation li.dashboard').click();

              return false;
            });
      var $projectSelect = $('
').addClass('view-switcher').hide()
            .append($('<select>'));

      // User status area
      var userLabel = args.context.users[0].name ?
            args.context.users[0].name : args.context.users[0].login;
      var $userInfo = $('
').attr({ id: 'user' }).addClass('button')
            .append(
              $('
').addClass('name').html(
                args.context && args.context.users ?
                  cloudStack.concat(userLabel, 14) : 'Invalid User'
              )
            )
            .append(
              $('
').addClass('icon options')
                .append(
                  $('
').addClass('icon arrow')
                )
            );
      $userInfo.attr('title', userLabel);

      return [
        $('
').addClass('logo'),
        $('
').addClass('controls')
          .append($notificationArea)
          .append($viewSwitcher)
          .append($projectSelect)
          .append($userInfo)
      ];
    },

    'main-area': function(args) {
      var $navigation = $('
').attr({ id: 'navigation' });
      var $browser = $('
').attr({ id: 'browser' })
            .append(
              // Home breadcrumb
              $('
').attr({ id: 'breadcrumbs' })
                .append($('
').addClass('home'))
                .append($('
').addClass('end'))
            )

            .append(
              // Panel container
              $('
').addClass('container')
            );

      makeNavigation(args).appendTo($navigation);

      return [
        $navigation, $browser
      ];
    }
  };

  $.fn.cloudStack = function(args) {
    var $container = $('
')
          .attr({
            id: 'container',
            'cloudStack-container': true
          })
          .data('cloudStack-args', args)
          .appendTo(this);
    var context = args.context;

    // Create pageElems
    $.each(pageElems, function(id, fn) {
      var $elem = $('
').attr({ id: id });

      $(fn(args)).each(function() {
        $elem.append($(this));
      });

      $elem.appendTo($container);
    });

    // User options
    var $options = $('
').attr({ id: 'user-options' })
          .appendTo($('#header'));

    $(['label.logout', 'label.help', 'About']).each(function() {
      var $link = $('')
            .attr({ href: '#' })
            .html(_l(this.toString()))
            .appendTo($options);

      if (this == 'label.help') {
        $link.click(function() {
          var helpURL = 'http://docs.cloud.com/CloudStack_Documentation';

          window.open(helpURL, '_blank');

          return false;
        });
      }
      if (this == 'About') {
        $link.click(function() {
          var $logo = $('
').addClass('logo').html('CloudStack'),
          $version = $('
').addClass('version').html(g_cloudstackversion),
          $about = $('
').addClass('about').append($logo).append($version);
          $about.dialog({
            modal: true,
            width: 300,
            title: 'About CloudStack',
            closeOnEscape: false,
            dialogClass: 'dialog-about',
            buttons: {
              'Close': function() {
                $( this ).dialog( "close" );
                $(':ui-dialog, .overlay').remove();
              }
            }
          }).closest('.ui-dialog').overlay();

          return false;
        });
      }
    });

    // Initialize browser
    $container.find('#browser div.container').cloudBrowser();
    $container.find('#navigation li')
      .filter(function() {
        return $(this).hasClass(args.home);
      })
      .click();

    // Validation
    $.extend($.validator.messages, { required: _l('label.required') });

    $.validator.addMethod(
      "disallowSpecialCharacters",
      function(value, element) {
        return (value.indexOf("<") == -1 && value.indexOf(">") == -1);
      },
      jQuery.format("Disallowed characters: <, >")
    );

    // Check for pending project invitations
    if (args.projects) {
      args.projects.invitationCheck({
        context: cloudStack.context,
        response: {
          success: function(args) {
            if (!args.data.length) return;

            var projectList = $.map(args.data, function(invitation) {
              return '
' + invitation.project + '
';
            }).join('');

            cloudStack.dialog.notice({
              message: _l('message.pending.projects.1') +
                '
' + projectList + '
' +
                '
' + _l('message.pending.projects.2') + '
'
            });
          }
        }
      });
    }

    // Hide logo conditionally
    if (!args.hasLogo) $('#header, #header .controls').addClass('nologo');

    return this;
  };

  // Events
  $(function() {
    // Check if target should be hovered
    function checkHoveredLabel($target) {
      var $multiWizard = $('div.ui-dialog div.multi-wizard');
      if (($target.is('label[for]') && !$target.parents('body.login')) ||
        ($multiWizard.size() &&
          ($target.is('.multi-wizard label') && $target.prev('input[type="radio"],input[type="checkbox"]').size()) ||
          ($target.is('.multi-wizard .select-desc div.name') && $target.parent('div.select-desc').prev('input[type="radio"],input[type="checkbox"]').size())
        ))
        return true;

      return false;
    }

    // Rollover behavior for user options
    $(document).bind('mouseover', function(event) {
      var $target = $(event.target);
      if (checkHoveredLabel($target)) {
        $target.addClass('label-hovered');
      }
      if ($target.closest('#user, #user-options').size()) {
        return false;
      }
      else $('#user-options').hide();

      return false;
    });

    $(document).bind('mouseout', function(event) {
      var $target = $(event.target);
      if (checkHoveredLabel($target)) {
        $target.removeClass('label-hovered');
      }
    });

    $(document).bind('click', function(event) {
      var $target = $(event.target);
      var $container = $target.closest('[cloudStack-container]');
      var args = $container.data('cloudStack-args');
      var $browser = $container.find('#browser .container');
      var $multiWizard = $('div.ui-dialog div.multi-wizard');

      // Wizard: trigger click event for input when click it label
      if ($multiWizard.size()) {
          if ($target.is('.multi-wizard label') && $target.prev('input[type="radio"],input[type="checkbox"]').size()) {
            $target.prev('input').trigger('click');
          }
          if ($target.is('.multi-wizard .select-desc div.name') && $target.parent('div.select-desc').prev('input[type="radio"],input[type="checkbox"]').size()) {
            $target.parent('div.select-desc').prev('input').trigger('click');
          }
      }

      if (!$container.size()) return true;

      // Navigation items
      if ($target.closest('li.navigation-item').size() && $target.closest('#navigation').size()) {
        var $navItem = $target.closest('li.navigation-item');

        if ($navItem.is('.disabled')) return false;
        showSection($navItem.data('cloudStack-section-id'), args, $browser);

        return false;
      }

      // Browser expand
      if ($target.hasClass('control expand') && $target.closest('div.panel div.toolbar').size()) {
        $browser.cloudBrowser('toggleMaximizePanel', {
          panel: $target.closest('div.panel')
        });

        return false;
      }

      // Home breadcrumb
      if ($target.is('#breadcrumbs div.home')) {
        showSection(args.home, args, $browser);
        return false;
      }

      // User options
      if ($target.closest('#user div.icon.options').size()) {
        $('#user-options').toggle();

        return false;
      }

      return true;
    });
  });
})(window.jQuery,
   window.cloudStack ? window.cloudStack : window.cloudStack = {});
<commit_msg>UI sections: Support per-section pre-filter<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
(function($, cloudStack) {
  $.extend(cloudStack, {
    ui: {
      widgets: {} // Defines API methods for UI widgets
    },
    uiCustom: {}
  });

  /**
   * Generate navigation
s
   *
   * @param args cloudStack data args
   */
  var makeNavigation = function(args) {
    var $navList = $('
');
    var preFilter = cloudStack.sectionPreFilter ?
          cloudStack.sectionPreFilter({
            context: $.extend(true, {}, args.context, {
              sections: $.map(cloudStack.sections, function(value, key) {
                return key;
              })
            })
          }) : null;

    $.each(args.sections, function(sectionID, args) {
      if (preFilter && $.inArray(sectionID, preFilter) == -1) {
        if (!(args.preFilter && args.preFilter())) {
          return true;
        }
      }

      var $li = $('
')
            .addClass('navigation-item')
            .addClass(sectionID)
            .append($('').addClass('icon').html(' '))
            .append($('').html(_l(args.title)))
            .data('cloudStack-section-id', sectionID);

      $li.appendTo($navList);

      return true;
    });

    // Special classes for first and last items
    $navList.find('li:first').addClass('first');
    $navList.find('li:last').addClass('last');

    return $navList;
  };

  /**
   * Create section contents
   *
   * @param sectionID Section's ID to show
   * @param args CloudStack3 configuration
   */
  var showSection = function(sectionID, args, $browser) {
    var $navItem = $('#navigation').find('li').filter(function() {
      return $(this).hasClass(sectionID);
    });
    var data = args.sections[sectionID];

    data.$browser = $browser;
    $navItem.siblings().removeClass('active');
    $navItem.addClass('active');

    // Reset browser panels
    $browser.cloudBrowser('removeAllPanels');
    $browser.cloudBrowser('addPanel', {
      title: '' + _l(data.title) + '' + '',
      data: '',
      complete: function($panel, $breadcrumb) {
        $breadcrumb.attr('title', _l(data.title));
        data.$breadcrumb = $breadcrumb;

        // Hide breadcrumb if this is the home section
        if (args.home === sectionID) {
          $('#breadcrumbs').find('li:first, div.end:last').hide();
        }

        // Append specified widget to view
        if (data.show)
          $panel.append(data.show(data));
        else if (data.treeView)
          $panel.treeView(data, { context: args.context });
        else
          $panel.listView(data, { context: args.context });
      }
    });

    return $navItem;
  };

  // Define page element generation fns
  var pageElems = {
    header: function(args) {
      // Make notification area
      var $notificationArea = $('
').addClass('button notifications')
            .append(
              $('
').addClass('total')
              // Total notifications
                .append($('').html(0))
            )
            .append($('').html(_l('label.notifications')))
            .notifications();

      // Project switcher
      var $viewSwitcher = $('
').addClass('button view-switcher')
            .append(
              // Default View
              $('
').addClass('select default-view active')
                .html(_l('label.default.view'))
                .prepend(
                  $('').addClass('icon').html(' ')
                )
            )
            .append(
              // Project View
              $('
').addClass('select project-view')
                .html(_l('label.project.view'))
                .prepend(
                  $('').addClass('icon').html(' ')
                )
            )
            .click(function(event) {
              var $target = $(event.target);
              var $projectSwitcher = $(this);
              var $container = $('html body');
              var $navDisabled = $(
                $.map([
                  'projects',
                  'accounts',
                  'domains',
                  'system',
                  'global-settings',
                  'configuration'
                ], function(id) {
                  return '#navigation li.' + id;
                }).join(',')
              );

              if ($target.closest('.select.project-view').size()) {
                $('#cloudStack3-container').addClass('project-view');
                $projectSwitcher.addClass('alt');
                $projectSwitcher.find('.select.project-view').addClass('active')
                  .siblings().removeClass('active');

                // Activate project view
                $navDisabled.hide();
                cloudStack.uiCustom.projects({
                  $projectSelect: $projectSelect.hide().find('select')
                });
              } else {
                $navDisabled.show();
                $('#cloudStack3-container').removeClass('project-view');
                $projectSwitcher.removeClass('alt');
                $projectSelect.hide();
                $projectSwitcher.find('.select.default-view').addClass('active')
                  .siblings().removeClass('active');

                // Put project name in header
                $('.select.project-view').html(
                  ' ' + _l('label.project.view')
                ).attr('title', '');

                // Clear out project
                cloudStack.context.projects = null;
              }

              $('#navigation li.dashboard').click();

              return false;
            });
      var $projectSelect = $('
').addClass('view-switcher').hide()
            .append($('<select>'));

      // User status area
      var userLabel = args.context.users[0].name ?
            args.context.users[0].name : args.context.users[0].login;
      var $userInfo = $('
').attr({ id: 'user' }).addClass('button')
            .append(
              $('
').addClass('name').html(
                args.context && args.context.users ?
                  cloudStack.concat(userLabel, 14) : 'Invalid User'
              )
            )
            .append(
              $('
').addClass('icon options')
                .append(
                  $('
').addClass('icon arrow')
                )
            );
      $userInfo.attr('title', userLabel);

      return [
        $('
').addClass('logo'),
        $('
').addClass('controls')
          .append($notificationArea)
          .append($viewSwitcher)
          .append($projectSelect)
          .append($userInfo)
      ];
    },

    'main-area': function(args) {
      var $navigation = $('
').attr({ id: 'navigation' });
      var $browser = $('
').attr({ id: 'browser' })
            .append(
              // Home breadcrumb
              $('
').attr({ id: 'breadcrumbs' })
                .append($('
').addClass('home'))
                .append($('
').addClass('end'))
            )

            .append(
              // Panel container
              $('
').addClass('container')
            );

      makeNavigation(args).appendTo($navigation);

      return [
        $navigation, $browser
      ];
    }
  };

  $.fn.cloudStack = function(args) {
    var $container = $('
')
          .attr({
            id: 'container',
            'cloudStack-container': true
          })
          .data('cloudStack-args', args)
          .appendTo(this);
    var context = args.context;

    // Create pageElems
    $.each(pageElems, function(id, fn) {
      var $elem = $('
').attr({ id: id });

      $(fn(args)).each(function() {
        $elem.append($(this));
      });

      $elem.appendTo($container);
    });

    // User options
    var $options = $('
').attr({ id: 'user-options' })
          .appendTo($('#header'));

    $(['label.logout', 'label.help', 'About']).each(function() {
      var $link = $('')
            .attr({ href: '#' })
            .html(_l(this.toString()))
            .appendTo($options);

      if (this == 'label.help') {
        $link.click(function() {
          var helpURL = 'http://docs.cloud.com/CloudStack_Documentation';

          window.open(helpURL, '_blank');

          return false;
        });
      }
      if (this == 'About') {
        $link.click(function() {
          var $logo = $('
').addClass('logo').html('CloudStack'),
          $version = $('
').addClass('version').html(g_cloudstackversion),
          $about = $('
').addClass('about').append($logo).append($version);
          $about.dialog({
            modal: true,
            width: 300,
            title: 'About CloudStack',
            closeOnEscape: false,
            dialogClass: 'dialog-about',
            buttons: {
              'Close': function() {
                $( this ).dialog( "close" );
                $(':ui-dialog, .overlay').remove();
              }
            }
          }).closest('.ui-dialog').overlay();

          return false;
        });
      }
    });

    // Initialize browser
    $container.find('#browser div.container').cloudBrowser();
    $container.find('#navigation li')
      .filter(function() {
        return $(this).hasClass(args.home);
      })
      .click();

    // Validation
    $.extend($.validator.messages, { required: _l('label.required') });

    $.validator.addMethod(
      "disallowSpecialCharacters",
      function(value, element) {
        return (value.indexOf("<") == -1 && value.indexOf(">") == -1);
      },
      jQuery.format("Disallowed characters: <, >")
    );

    // Check for pending project invitations
    if (args.projects) {
      args.projects.invitationCheck({
        context: cloudStack.context,
        response: {
          success: function(args) {
            if (!args.data.length) return;

            var projectList = $.map(args.data, function(invitation) {
              return '
' + invitation.project + '
';
            }).join('');

            cloudStack.dialog.notice({
              message: _l('message.pending.projects.1') +
                '
' + projectList + '
' +
                '
' + _l('message.pending.projects.2') + '
'
            });
          }
        }
      });
    }

    // Hide logo conditionally
    if (!args.hasLogo) $('#header, #header .controls').addClass('nologo');

    return this;
  };

  // Events
  $(function() {
    // Check if target should be hovered
    function checkHoveredLabel($target) {
      var $multiWizard = $('div.ui-dialog div.multi-wizard');
      if (($target.is('label[for]') && !$target.parents('body.login')) ||
        ($multiWizard.size() &&
          ($target.is('.multi-wizard label') && $target.prev('input[type="radio"],input[type="checkbox"]').size()) ||
          ($target.is('.multi-wizard .select-desc div.name') && $target.parent('div.select-desc').prev('input[type="radio"],input[type="checkbox"]').size())
        ))
        return true;

      return false;
    }

    // Rollover behavior for user options
    $(document).bind('mouseover', function(event) {
      var $target = $(event.target);
      if (checkHoveredLabel($target)) {
        $target.addClass('label-hovered');
      }
      if ($target.closest('#user, #user-options').size()) {
        return false;
      }
      else $('#user-options').hide();

      return false;
    });

    $(document).bind('mouseout', function(event) {
      var $target = $(event.target);
      if (checkHoveredLabel($target)) {
        $target.removeClass('label-hovered');
      }
    });

    $(document).bind('click', function(event) {
      var $target = $(event.target);
      var $container = $target.closest('[cloudStack-container]');
      var args = $container.data('cloudStack-args');
      var $browser = $container.find('#browser .container');
      var $multiWizard = $('div.ui-dialog div.multi-wizard');

      // Wizard: trigger click event for input when click it label
      if ($multiWizard.size()) {
          if ($target.is('.multi-wizard label') && $target.prev('input[type="radio"],input[type="checkbox"]').size()) {
            $target.prev('input').trigger('click');
          }
          if ($target.is('.multi-wizard .select-desc div.name') && $target.parent('div.select-desc').prev('input[type="radio"],input[type="checkbox"]').size()) {
            $target.parent('div.select-desc').prev('input').trigger('click');
          }
      }

      if (!$container.size()) return true;

      // Navigation items
      if ($target.closest('li.navigation-item').size() && $target.closest('#navigation').size()) {
        var $navItem = $target.closest('li.navigation-item');

        if ($navItem.is('.disabled')) return false;
        showSection($navItem.data('cloudStack-section-id'), args, $browser);

        return false;
      }

      // Browser expand
      if ($target.hasClass('control expand') && $target.closest('div.panel div.toolbar').size()) {
        $browser.cloudBrowser('toggleMaximizePanel', {
          panel: $target.closest('div.panel')
        });

        return false;
      }

      // Home breadcrumb
      if ($target.is('#breadcrumbs div.home')) {
        showSection(args.home, args, $browser);
        return false;
      }

      // User options
      if ($target.closest('#user div.icon.options').size()) {
        $('#user-options').toggle();

        return false;
      }

      return true;
    });
  });
})(window.jQuery,
   window.cloudStack ? window.cloudStack : window.cloudStack = {});
<|endoftext|>


<commit_before>
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import {Link} from 'react-router-dom';
import Highlight from 'react-syntax-highlight';
import './PatternPage.css';
import 'highlight.js/styles/default.css';
import 'highlight.js/styles/atom-one-dark.css';

const SetShape = {
  name: PropTypes.string,
  description: PropTypes.string,
  rendered: PropTypes.string.isRequired
}
const PatternShape = {
  name: PropTypes.string.isRequired,
  description: PropTypes.string,
  rendered: PropTypes.string,
  used: PropTypes.arrayOf(PropTypes.string),
  sets: PropTypes.arrayOf(PropTypes.shape(SetShape))
};

const PatternPageLoadingWrapper = ({pattern, set, used, onSetChange}) => (

    {pattern && <PatternPage pattern={pattern} set={set} used={used} onSetChange={onSetChange} />}

)
PatternPageLoadingWrapper.propTypes = {
  pattern: PropTypes.shape(PatternShape),
  set: PropTypes.shape(SetShape),
  used: PropTypes.arrayOf(PropTypes.string),
  onSetChange: PropTypes.func.isRequired,
}
/**
 * These reselect selectors pull data out of redux state based on URL params.
 */
const getPatternsFromState = state => state.patterns;
const getSelectedPatternId = (state, ownProps) => ownProps.match.params.pattern;
const getSelectedSetId = (state, ownProps) => ownProps.match.params.set;
const getPattern = createSelector(
  [getPatternsFromState, getSelectedPatternId],
  (patterns, patternId) => {
    return patterns.filter(p => p.id === patternId).pop();
  }
)
const getSet = createSelector(
  [getPattern, getSelectedSetId],
  (pattern, setId) => {
    return pattern ? pattern.sets.filter(s => s.id === setId).pop() : undefined;
  }
)
const getUsed = createSelector(
  [getPatternsFromState, getPattern],
  (patterns, pattern) => {
    return pattern ? pattern.used.map(id => (
      patterns.filter(p => p.id === id).pop()
    )) : [];
  }
)

const mapStateToProps = (state, ownProps) => {
  return {
    pattern: getPattern(state, ownProps),
    set: getSet(state, ownProps),
    used: getUsed(state, ownProps)
  }
}
const mapDispatchToProps = (state, ownProps) => {
  return {
    onSetChange: (sid) => {
      ownProps.history.push(`/pattern/${ownProps.match.params.pattern}/set/${sid}`)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(PatternPageLoadingWrapper);

class PatternPage extends Component {
  constructor(props) {
    super(props)
    this.state = {showingInfo: false}
    this.toggleInfo = this.toggleInfo.bind(this)
    this.openWindow = this.openWindow.bind(this)
  }
  toggleInfo(e) {
    this.setState(state => ({
      showingInfo: !state.showingInfo
    }))
  }
  openWindow() {
    window.open(this.props.set.rendered, this.props.pattern.name, 'resizable');
  }
  render() {
    const {pattern, set, used, onSetChange} = this.props;
    const {showingInfo} = this.state;
    return (

        <PatternTopBar pattern={pattern} set={set} openWindow={this.openWindow} toggleInfo={this.toggleInfo} changeSet={onSetChange} />

        <div className={`PatternInfo ${showingInfo ? 'open' : 'closed'}`}>

            <button onClick={this.toggleInfo} className="close-button" aria-label="Open Info" type="button">×</button>

{pattern.name}

                Uses

{used.map(p => (
                  <Link key={p.id} to={`/pattern/${p.id}`}>{p.name}</Link>
                ))}

                Description

{pattern.description}

            <CodeToggleFrame className="code" html={set.source} raw={pattern.source} rawFormat={pattern.format} />

    );
  }
}

const PatternTopBar = ({pattern, set, openWindow, toggleInfo, changeSet}) => {
  return (

{pattern.name}

<SetSelector sets={pattern.sets} selected={set.id} onChange={changeSet} />

Open

View Pattern Info

  )
}

class CodeToggleFrame extends Component {
  constructor(props) {
    super(props);
    this.state = {mode: 'html'}
    this.switchMode = this.switchMode.bind(this);
  }
  switchMode(e) {
    this.setState({mode: e.target.getAttribute('data-mode')})
  }
  render() {
    const {className} = this.props;
    const {mode} = this.state;
    const src = this.props[mode];
    const title = mode === 'html' ?  'View Html' : 'View Raw';
    const format = mode === 'html' ? 'html' : this.props.rawFormat;

    return (
      <div className={`CodeFrame ${className}`}>
        <CodeFrame frameBorder="0" title={title} src={src} format={format}></CodeFrame>

          HTML
          Raw

    )
  }
}

class CodeFrame extends Component {
  constructor(props) {
    super(props)
    this.state = {code: ''}
  }
  componentDidMount() {
    this.fetch();
  }
  componentDidUpdate(prevProps) {
    if(prevProps.src !== this.props.src) {
      this.fetch();
    }
  }
  fetch() {
    this.setState({loading: true, err: false});
    fetch(this.props.src)
      .then(res => {
        this.setState({loading: false});
        res.text().then(code => this.setState({code}));
      })
      .catch(err => {
        this.setState({loading: false, err: err})
      })
  }
  render() {
    const {code, loading, err} = this.state;
    const {format} = this.props;

    if(loading) {
      return
Loading...

    }
    if(err) {
      return
Error: {err}

    }
    return <Highlight lang={format} value={code} />
  }
}

const SetSelector = ({sets, selected, onChange}) => (
  <select className="SetSelector" value={selected} onChange={e => onChange(e.target.value)}>
    {sets.map(set => (
      <option key={set.id} value={set.id}>{set.name}</option>
    ))}
  </select>
)<commit_msg>Accessibility for pattern page<commit_after>
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import {Link} from 'react-router-dom';
import Highlight from 'react-syntax-highlight';
import './PatternPage.css';
import 'highlight.js/styles/default.css';
import 'highlight.js/styles/atom-one-dark.css';

const SetShape = {
  name: PropTypes.string,
  description: PropTypes.string,
  rendered: PropTypes.string.isRequired
}
const PatternShape = {
  name: PropTypes.string.isRequired,
  description: PropTypes.string,
  rendered: PropTypes.string,
  used: PropTypes.arrayOf(PropTypes.string),
  sets: PropTypes.arrayOf(PropTypes.shape(SetShape))
};

const PatternPageLoadingWrapper = ({pattern, set, used, onSetChange}) => (

    {pattern && <PatternPage pattern={pattern} set={set} used={used} onSetChange={onSetChange} />}

)
PatternPageLoadingWrapper.propTypes = {
  pattern: PropTypes.shape(PatternShape),
  set: PropTypes.shape(SetShape),
  used: PropTypes.arrayOf(PropTypes.string),
  onSetChange: PropTypes.func.isRequired,
}
/**
 * These reselect selectors pull data out of redux state based on URL params.
 */
const getPatternsFromState = state => state.patterns;
const getSelectedPatternId = (state, ownProps) => ownProps.match.params.pattern;
const getSelectedSetId = (state, ownProps) => ownProps.match.params.set;
const getPattern = createSelector(
  [getPatternsFromState, getSelectedPatternId],
  (patterns, patternId) => {
    return patterns.filter(p => p.id === patternId).pop();
  }
)
const getSet = createSelector(
  [getPattern, getSelectedSetId],
  (pattern, setId) => {
    return pattern ? pattern.sets.filter(s => s.id === setId).pop() : undefined;
  }
)
const getUsed = createSelector(
  [getPatternsFromState, getPattern],
  (patterns, pattern) => {
    return pattern ? pattern.used.map(id => (
      patterns.filter(p => p.id === id).pop()
    )) : [];
  }
)

const mapStateToProps = (state, ownProps) => {
  return {
    pattern: getPattern(state, ownProps),
    set: getSet(state, ownProps),
    used: getUsed(state, ownProps)
  }
}
const mapDispatchToProps = (state, ownProps) => {
  return {
    onSetChange: (sid) => {
      ownProps.history.push(`/pattern/${ownProps.match.params.pattern}/set/${sid}`)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(PatternPageLoadingWrapper);

class PatternPage extends Component {
  constructor(props) {
    super(props)
    this.state = {showingInfo: false}
    this.toggleInfo = this.toggleInfo.bind(this)
    this.openWindow = this.openWindow.bind(this)
  }
  toggleInfo(e) {
    this.setState(state => ({
      showingInfo: !state.showingInfo
    }))
  }
  openWindow(e) {
    window.open(this.props.set.rendered, this.props.pattern.name, 'resizable');
  }
  render() {
    const {pattern, set, used, onSetChange} = this.props;
    const {showingInfo} = this.state;
    return (

        <PatternTopBar pattern={pattern} set={set} openWindow={this.openWindow} toggleInfo={this.toggleInfo} changeSet={onSetChange} />

        <div className={`PatternInfo ${showingInfo ? 'open' : 'closed'}`}>

            <button onClick={this.toggleInfo} className="close-button" aria-label="Open Info" type="button">×</button>

{pattern.name}

                Uses

{used.map(p => (
                  <Link key={p.id} to={`/pattern/${p.id}`}>{p.name}</Link>
                ))}

                Description

{pattern.description}

            <CodeToggleFrame className="code" html={set.source} raw={pattern.source} rawFormat={pattern.format} />

    );
  }
}

const PatternTopBar = ({pattern, set, openWindow, toggleInfo, changeSet}) => {
  return (

{pattern.name}

<SetSelector sets={pattern.sets} selected={set.id} onChange={changeSet} />

Open

View Pattern Info

  )
}

class CodeToggleFrame extends Component {
  constructor(props) {
    super(props);
    this.state = {mode: 'html'}
    this.switchMode = this.switchMode.bind(this);
  }
  switchMode(e) {
    this.setState({mode: e.target.getAttribute('data-mode')})
  }
  render() {
    const {className} = this.props;
    const {mode} = this.state;
    const src = this.props[mode];
    const title = mode === 'html' ?  'View Html' : 'View Raw';
    const format = mode === 'html' ? 'html' : this.props.rawFormat;

    return (
      <div className={`CodeFrame ${className}`}>
        <CodeFrame frameBorder="0" title={title} src={src} format={format}></CodeFrame>

          HTML
          Raw

    )
  }
}

class CodeFrame extends Component {
  constructor(props) {
    super(props)
    this.state = {code: ''}
  }
  componentDidMount() {
    this.fetch();
  }
  componentDidUpdate(prevProps) {
    if(prevProps.src !== this.props.src) {
      this.fetch();
    }
  }
  fetch() {
    this.setState({loading: true, err: false});
    fetch(this.props.src)
      .then(res => {
        this.setState({loading: false});
        res.text().then(code => this.setState({code}));
      })
      .catch(err => {
        this.setState({loading: false, err: err})
      })
  }
  render() {
    const {code, loading, err} = this.state;
    const {format} = this.props;

    if(loading) {
      return
Loading...

    }
    if(err) {
      return
Error: {err}

    }
    return <Highlight lang={format} value={code} />
  }
}

const SetSelector = ({sets, selected, onChange}) => (
  <select className="SetSelector" value={selected} onChange={e => onChange(e.target.value)}>
    {sets.map(set => (
      <option key={set.id} value={set.id}>{set.name}</option>
    ))}
  </select>
)<|endoftext|>


<commit_before>describe('searchDetails_Search for VANTAGE', function() {
  it('returns 2 results', function() {
    // Open the main site
    browser.get('http://di2e.github.io/openstorefront');

    // Enter the search term (changed to enter after updates to search keys 7/28)
    element(by.id('mainSearchBar')).sendKeys('VANTAGE', protractor.Key.ENTER);

    // Should only be two results
    expect(element.all(by.repeater('item in data')).count()).toEqual(2);
  });
});

describe('searchDetails_Click on the results', function() {
  it('click on the Details Reviews and Q&A tabs -- content loads from those tabs', function() {
    // Click on the second or OZONE results
    element.all(by.css('.results-content-title-content')).get(1).click();

    // Verify tabs (Summary, Details, Reviews, Q&A) on the page
    var list = element.all(by.css('.nav-tabs li'));
    expect(list.count()).toBe(7);  //Strange, must be some blank ones in here?

    // Click on the tabs from search results details page
    element.all(by.css('.nav-tabs li')).get(1).click();
    element.all(by.css('.nav-tabs li')).get(2).click();
    element.all(by.css('.nav-tabs li')).get(3).click();
    element.all(by.css('.nav-tabs li')).get(0).click();
    // ALTERNATE:  element(by.cssContainingText('.nav-tabs','REVIEWS')).click();
    expect(element.all(by.binding('results-content-description')));

  });

  it('click page tabs to hide parts of page  click tabs again', function() {
    element(by.id('showPageLeft')).click();
    browser.driver.sleep(1000);
    element(by.id('showPageRight')).click();
    browser.driver.sleep(1000);
    element(by.id('showPageRight')).click();
    browser.driver.sleep(1000);
    // Assume if buttons are they they were clicked on (if not visible still there)
    expect(true).toBe(true);

    // Click on the tabs from search results details page
    element.all(by.css('.nav-tabs li')).get(1).click();
    element.all(by.css('.nav-tabs li')).get(2).click();
    element.all(by.css('.nav-tabs li')).get(3).click();
    element.all(by.css('.nav-tabs li')).get(0).click();
    expect(element.all(by.binding('results-content-description')));
  });

});
<commit_msg>Added button clicks at end of script for tags and full page<commit_after>describe('searchDetails_Search for VANTAGE', function() {
  it('returns 2 results', function() {
    // Open the main site
    browser.get('http://di2e.github.io/openstorefront');

    // Enter the search term (changed to enter after updates to search keys 7/28)
    element(by.id('mainSearchBar')).sendKeys('VANTAGE', protractor.Key.ENTER);

    // Should only be two results
    expect(element.all(by.repeater('item in data')).count()).toEqual(2);
  });
});

describe('searchDetails_Click on the results', function() {
  it('click on the Details Reviews and Q&A tabs -- content loads from those tabs', function() {
    // Click on the second or OZONE results
    element.all(by.css('.results-content-title-content')).get(1).click();

    // Verify tabs (Summary, Details, Reviews, Q&A) on the page
    var list = element.all(by.css('.nav-tabs li'));
    expect(list.count()).toBe(7);  //Strange, must be some blank ones in here?

    // Click on the tabs from search results details page
    element.all(by.css('.nav-tabs li')).get(1).click();
    element.all(by.css('.nav-tabs li')).get(2).click();
    element.all(by.css('.nav-tabs li')).get(3).click();
    element.all(by.css('.nav-tabs li')).get(0).click();
    // ALTERNATE:  element(by.cssContainingText('.nav-tabs','REVIEWS')).click();
    expect(element.all(by.binding('results-content-description')));

  });

  it('click page tabs to hide parts of page  click tabs again', function() {
    element(by.id('showPageLeft')).click();
    browser.driver.sleep(500);
    element(by.id('showPageRight')).click();
    browser.driver.sleep(500);
    element(by.id('showPageRight')).click();
    browser.driver.sleep(500);
    // Assume if buttons are they they were clicked on (if not visible still there)
    expect(true).toBe(true);

    // Click on the tabs from search results details page
    element.all(by.css('.nav-tabs li')).get(1).click();
    element.all(by.css('.nav-tabs li')).get(2).click();
    element.all(by.css('.nav-tabs li')).get(3).click();
    element.all(by.css('.nav-tabs li')).get(0).click();
    expect(element.all(by.binding('results-content-description')));
  });

 it('click on view edit tags and full page', function() {
    // Click on "View/Edit Tags"
    element(by.id('data-collapse-tags')).click();
    // Can't get id (class) of wach button (2 different states)
    element(by.id('permenantLink')).click();
    // Check for popped up window, then close
    expect(true).toBe(true);
 });

});
<|endoftext|>


<commit_before>/* The background process for chrome */
var storage = chrome.storage.local;

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.message == 'settingsclick'){
        // open new tab
        chrome.tabs.create(
            {url: chrome.extension.getURL('data/settings/index.html')}
        );
    }
});

function broadcast(message){
    /*
        Broadcast a message to ALL tabs. No callbacks allowed. Tested this with
        enough tabs open to make my computer sluggish. Didn't seem to make it worse,
        I assume this is a really light weight function.
    */

    // Don't put the extensionId as the first argument.
    // All you will get is WAT.
    chrome.runtime.sendMessage(message);

    // You need the tabs permission to match on url
    chrome.tabs.query({url: '*://*.archiveofourown.org/*'}, function(tabs) {
        // This DOES have the matching tabs
        for (var idx in tabs) {
            if (tabs[idx].id){
                chrome.tabs.sendMessage(tabs[idx].id, message);
            }
        }
    });
}

// NOTE! You have to call this twice to get it to send
// first with the port, then with the data you want to send.
var callbackMessage = (function(port){
    return function(message, data, data_type) {
        port.postMessage({'message': message, 'data': data, 'data_type': data_type});
    };
});

chrome.runtime.onConnect.addListener(function(port) {
    port.onMessage.addListener(function(request) {
        if (request.message == "reveal-token"){
            getUser(callbackMessage(port));
        }

        if (request.message == 'save-token'){
            validateAndSaveToken(request.data, port);
        }

        if (request.message == 'prefs') {
            savePrefs(request.data);
        }

        if (request.message == 'fetchdata') {
            fetchDataRequest(request, port);
        }

        if (request.message == 'restorefrombackup'){
            restoreFromBackup(request);
        }

        if (request.message == 'ficdata'){
            handleNewFic(request.data.metadata, request.data.mutable_data, port);
        }

        if (request.message == 'runsync'){
            runSync();
        }
    });
});

/* Backend sync */

function newUser(){

    var xhr = new XMLHttpRequest();
    xhr.open("POST", backendUrl + "user", true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 201){
                var resp = JSON.parse(xhr.responseText);
                var user_id = resp['user_id'];
                savePrefs({'user_id': user_id});
            }
        }
    }
    xhr.send();

}

function validateAndSaveToken(token, port){

    var xhr = new XMLHttpRequest();
    xhr.open("GET", backendUrl + "user/" + token, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 200){
                var resp = JSON.parse(xhr.responseText);
                if ('user_id' in resp){
                    var user_id = resp['user_id'];
                    savePrefs({'user_id': user_id });
                    port.postMessage({
                        message: 'token-saved',
                        data: {'token_status': 'valid', user_id: user_id}
                    });
                    // Pass in new token to be sure against race conditions
                    syncData(resp['user_id'], port);
                }
            } else {
                port.postMessage({
                    message: 'token-saved',
                    data: {'token_status': 'invalid'}
                });
            }
        }
    }
    xhr.send();

}

function syncWork(data){
    if (!data.ao3id){
        return;
    }

    var callbk = (function(data){
        return function(user_id) {

            var xhr = new XMLHttpRequest();
            var url = backendUrl + 'user/' + user_id + "/work/" + data['ao3id'];
            xhr.open("POST", url, true);
            xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
            xhr.onreadystatechange = function() {
                if ((xhr.readyState == 4) && (xhr.status == 200 || xhr.status == 201)) {
                    var diff = JSON.parse(xhr.responseText)['diff'];

                    // This will only contain the diff of one work.
                    // If there was no difference, it will be empty.
                    if ('user_id' in diff)
                        delete diff['user_id'];
                    if (Object.keys(diff).length === 0){
                        diff['ao3id'] = data['ao3id'];
                        saveArticle(diff, true, null, false, true);
                    }
                }
            }
            xhr.send(data);

        };
    })(JSON.stringify(data));

    getUserForSync(callbk);

}

function syncData(user_id_override, port){
    /* Use the user_id_override when saving a new user_id/"token" and storage hasn't caught up. */
    // Grab all data
    var callbk = function(user_id, data){
        if (user_id_override)
            user_id = user_id_override;
        var xhr = new XMLHttpRequest();
        var url = backendUrl + 'user/' + user_id + "/collection";
        xhr.open("POST", url, true);
        xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
        xhr.onreadystatechange = function() {
            if ((xhr.readyState == 4) && (xhr.status == 200 || xhr.status == 201)) {
                var diff = JSON.parse(xhr.responseText)['diff'];

                // Iterate through the dictionary of changed articles and update our DB
                // the key is the work ID
                // Also contains the settings!
                var i = 0;
                var do_broadcast = true;
                for (var key in diff) {
                    if (diff.hasOwnProperty(key)) {
                        if (key == 'settings'){
                            // TODO: update the settings
                        } else if (key == 'user_id'){
                            ; // You can safely ignore
                        } else {
                            var article = diff[key];
                            if ('user_id' in article){
                                delete article['user_id'];
                            }
                            i += 1;
                            // Don't broadcast more than 10 works
                            if (i > 10)
                                do_broadcast = false;
                            if (!(Object.keys(article).length === 0)){
                                article['ao3id'] = key;
                                saveArticle(article, true, port, false, do_broadcast);
                            }
                        }
                    }
                }
                savePrefs({'last_sync': new Date().getTime() / 1000});

            }
        }
        xhr.send(JSON.stringify(data));
    }

    getDataForSync(callbk);
}
<commit_msg>Bug causing changes to works to not sync<commit_after>/* The background process for chrome */
var storage = chrome.storage.local;

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.message == 'settingsclick'){
        // open new tab
        chrome.tabs.create(
            {url: chrome.extension.getURL('data/settings/index.html')}
        );
    }
});

function broadcast(message){
    /*
        Broadcast a message to ALL tabs. No callbacks allowed. Tested this with
        enough tabs open to make my computer sluggish. Didn't seem to make it worse,
        I assume this is a really light weight function.
    */

    // Don't put the extensionId as the first argument.
    // All you will get is WAT.
    chrome.runtime.sendMessage(message);

    // You need the tabs permission to match on url
    chrome.tabs.query({url: '*://*.archiveofourown.org/*'}, function(tabs) {
        // This DOES have the matching tabs
        for (var idx in tabs) {
            if (tabs[idx].id){
                chrome.tabs.sendMessage(tabs[idx].id, message);
            }
        }
    });
}

// NOTE! You have to call this twice to get it to send
// first with the port, then with the data you want to send.
var callbackMessage = (function(port){
    return function(message, data, data_type) {
        port.postMessage({'message': message, 'data': data, 'data_type': data_type});
    };
});

chrome.runtime.onConnect.addListener(function(port) {
    port.onMessage.addListener(function(request) {
        if (request.message == "reveal-token"){
            getUser(callbackMessage(port));
        }

        if (request.message == 'save-token'){
            validateAndSaveToken(request.data, port);
        }

        if (request.message == 'prefs') {
            savePrefs(request.data);
        }

        if (request.message == 'fetchdata') {
            fetchDataRequest(request, port);
        }

        if (request.message == 'restorefrombackup'){
            restoreFromBackup(request);
        }

        if (request.message == 'ficdata'){
            handleNewFic(request.data.metadata, request.data.mutable_data, port);
        }

        if (request.message == 'runsync'){
            runSync();
        }
    });
});

/* Backend sync */

function newUser(){

    var xhr = new XMLHttpRequest();
    xhr.open("POST", backendUrl + "user", true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 201){
                var resp = JSON.parse(xhr.responseText);
                var user_id = resp['user_id'];
                savePrefs({'user_id': user_id});
            }
        }
    }
    xhr.send();

}

function validateAndSaveToken(token, port){

    var xhr = new XMLHttpRequest();
    xhr.open("GET", backendUrl + "user/" + token, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 200){
                var resp = JSON.parse(xhr.responseText);
                if ('user_id' in resp){
                    var user_id = resp['user_id'];
                    savePrefs({'user_id': user_id });
                    port.postMessage({
                        message: 'token-saved',
                        data: {'token_status': 'valid', user_id: user_id}
                    });
                    // Pass in new token to be sure against race conditions
                    syncData(resp['user_id'], port);
                }
            } else {
                port.postMessage({
                    message: 'token-saved',
                    data: {'token_status': 'invalid'}
                });
            }
        }
    }
    xhr.send();

}

function syncWork(data){
    if (!data.ao3id){
        return;
    }

    var callbk = (function(data){
        return function(user_id) {

            var xhr = new XMLHttpRequest();
            var url = backendUrl + 'user/' + user_id + "/work/" + JSON.parse(data)['ao3id'];
            xhr.open("POST", url, true);
            xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
            xhr.onreadystatechange = function() {
                if ((xhr.readyState == 4) && (xhr.status == 200 || xhr.status == 201)) {
                    var diff = JSON.parse(xhr.responseText)['diff'];

                    // This will only contain the diff of one work.
                    // If there was no difference, it will be empty.
                    if ('user_id' in diff)
                        delete diff['user_id'];
                    if (Object.keys(diff).length === 0){
                        diff['ao3id'] = data['ao3id'];
                        saveArticle(diff, true, null, false, true);
                    }
                }
            }
            xhr.send(data);

        };
    })(JSON.stringify(data));

    getUserForSync(callbk);

}

function syncData(user_id_override, port){
    /* Use the user_id_override when saving a new user_id/"token" and storage hasn't caught up. */
    // Grab all data
    var callbk = function(user_id, data){
        if (user_id_override)
            user_id = user_id_override;
        var xhr = new XMLHttpRequest();
        var url = backendUrl + 'user/' + user_id + "/collection";
        xhr.open("POST", url, true);
        xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
        xhr.onreadystatechange = function() {
            if ((xhr.readyState == 4) && (xhr.status == 200 || xhr.status == 201)) {
                var diff = JSON.parse(xhr.responseText)['diff'];

                // Iterate through the dictionary of changed articles and update our DB
                // the key is the work ID
                // Also contains the settings!
                var i = 0;
                var do_broadcast = true;
                for (var key in diff) {
                    if (diff.hasOwnProperty(key)) {
                        if (key == 'settings'){
                            // TODO: update the settings
                        } else if (key == 'user_id'){
                            ; // You can safely ignore
                        } else {
                            var article = diff[key];
                            if ('user_id' in article){
                                delete article['user_id'];
                            }
                            i += 1;
                            // Don't broadcast more than 10 works
                            if (i > 10)
                                do_broadcast = false;
                            if (!(Object.keys(article).length === 0)){
                                article['ao3id'] = key;
                                saveArticle(article, true, port, false, do_broadcast);
                            }
                        }
                    }
                }
                savePrefs({'last_sync': new Date().getTime() / 1000});

            }
        }
        xhr.send(JSON.stringify(data));
    }

    getDataForSync(callbk);
}
<|endoftext|>


<commit_before>// NOTES - follows first-first highlight method, block is locked after highlight, different from SyntaxHl
Prism.languages.autohotkey= {
	'comment': /(;[^\n]*|^\s*\/\*[\s\S]*\s*\*\/)/gm,
	'string': /"(([^"\n\r]|"")*)"/gm,
	'function': /[^\(\); \t\,\n\+\*\-\=\?>:\\\/<\&%\[\]]+?(?=\()/gm,  //function - don't use .*\) in the end bcoz string locks it
	'tag': /^[ \t]*[^\s:]+?(?=:[^:])/gm,  //labels
	'variable': /\%\w+\%/g,
	'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,
	'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,
	'ignore': /&(lt|gt|amp);/gi,
	'punctuation': /[\{}[\]\(\):]/g,
	'boolean': /\b(true|false)\b/g,

	'selector': /\b(AutoTrim|BlockInput|Break|Click|ClipWait|Continue|Control|ControlClick|ControlFocus|ControlGet|ControlGetFocus|ControlGetPos|ControlGetText|ControlMove|ControlSend|ControlSendRaw|ControlSetText|CoordMode|Critical|DetectHiddenText|DetectHiddenWindows|Drive|DriveGet|DriveSpaceFree|Else|EnvAdd|EnvDiv|EnvGet|EnvMult|EnvSet|EnvSub|EnvUpdate|Exit|ExitApp|FileAppend|FileCopy|FileCopyDir|FileCreateDir|FileCreateShortcut|FileDelete|FileGetAttrib|FileGetShortcut|FileGetSize|FileGetTime|FileGetVersion|FileInstall|FileMove|FileMoveDir|FileRead|FileReadLine|FileRecycle|FileRecycleEmpty|FileRemoveDir|FileSelectFile|FileSelectFolder|FileSetAttrib|FileSetTime|FormatTime|GetKeyState|Gosub|Goto|GroupActivate|GroupAdd|GroupClose|GroupDeactivate|Gui|GuiControl|GuiControlGet|Hotkey|ImageSearch|IniDelete|IniRead|IniWrite|Input|InputBox|KeyWait|ListHotkeys|ListLines|ListVars|Loop|Menu|MouseClick|MouseClickDrag|MouseGetPos|MouseMove|MsgBox|OnExit|OutputDebug|Pause|PixelGetColor|PixelSearch|PostMessage|Process|Progress|Random|RegDelete|RegRead|RegWrite|Reload|Repeat|Return|Run|RunAs|RunWait|Send|SendEvent|SendInput|SendMessage|SendMode|SendPlay|SendRaw|SetBatchLines|SetCapslockState|SetControlDelay|SetDefaultMouseSpeed|SetEnv|SetFormat|SetKeyDelay|SetMouseDelay|SetNumlockState|SetScrollLockState|SetStoreCapslockMode|SetTimer|SetTitleMatchMode|SetWinDelay|SetWorkingDir|Shutdown|Sleep|Sort|SoundBeep|SoundGet|SoundGetWaveVolume|SoundPlay|SoundSet|SoundSetWaveVolume|SplashImage|SplashTextOff|SplashTextOn|SplitPath|StatusBarGetText|StatusBarWait|StringCaseSense|StringGetPos|StringLeft|StringLen|StringLower|StringMid|StringReplace|StringRight|StringSplit|StringTrimLeft|StringTrimRight|StringUpper|Suspend|SysGet|Thread|ToolTip|Transform|TrayTip|URLDownloadToFile|While|WinActivate|WinActivateBottom|WinClose|WinGet|WinGetActiveStats|WinGetActiveTitle|WinGetClass|WinGetPos|WinGetText|WinGetTitle|WinHide|WinKill|WinMaximize|WinMenuSelectItem|WinMinimize|WinMinimizeAll|WinMinimizeAllUndo|WinMove|WinRestore|WinSet|WinSetTitle|WinShow|WinWait|WinWaitActive|WinWaitClose|WinWaitNotActive)\b/i,

	'constant': /\b(a_ahkpath|a_ahkversion|a_appdata|a_appdatacommon|a_autotrim|a_batchlines|a_caretx|a_carety|a_computername|a_controldelay|a_cursor|a_dd|a_ddd|a_dddd|a_defaultmousespeed|a_desktop|a_desktopcommon|a_detecthiddentext|a_detecthiddenwindows|a_endchar|a_eventinfo|a_exitreason|a_formatfloat|a_formatinteger|a_gui|a_guievent|a_guicontrol|a_guicontrolevent|a_guiheight|a_guiwidth|a_guix|a_guiy|a_hour|a_iconfile|a_iconhidden|a_iconnumber|a_icontip|a_index|a_ipaddress1|a_ipaddress2|a_ipaddress3|a_ipaddress4|a_isadmin|a_iscompiled|a_iscritical|a_ispaused|a_issuspended|a_isunicode|a_keydelay|a_language|a_lasterror|a_linefile|a_linenumber|a_loopfield|a_loopfileattrib|a_loopfiledir|a_loopfileext|a_loopfilefullpath|a_loopfilelongpath|a_loopfilename|a_loopfileshortname|a_loopfileshortpath|a_loopfilesize|a_loopfilesizekb|a_loopfilesizemb|a_loopfiletimeaccessed|a_loopfiletimecreated|a_loopfiletimemodified|a_loopreadline|a_loopregkey|a_loopregname|a_loopregsubkey|a_loopregtimemodified|a_loopregtype|a_mday|a_min|a_mm|a_mmm|a_mmmm|a_mon|a_mousedelay|a_msec|a_mydocuments|a_now|a_nowutc|a_numbatchlines|a_ostype|a_osversion|a_priorhotkey|programfiles|a_programfiles|a_programs|a_programscommon|a_screenheight|a_screenwidth|a_scriptdir|a_scriptfullpath|a_scriptname|a_sec|a_space|a_startmenu|a_startmenucommon|a_startup|a_startupcommon|a_stringcasesense|a_tab|a_temp|a_thisfunc|a_thishotkey|a_thislabel|a_thismenu|a_thismenuitem|a_thismenuitempos|a_tickcount|a_timeidle|a_timeidlephysical|a_timesincepriorhotkey|a_timesincethishotkey|a_titlematchmode|a_titlematchmodespeed|a_username|a_wday|a_windelay|a_windir|a_workingdir|a_yday|a_year|a_yweek|a_yyyy|clipboard|clipboardall|comspec|errorlevel)\b/i,

	'builtin': /\b(abs|acos|asc|asin|atan|ceil|chr|class|cos|dllcall|exp|fileexist|Fileopen|floor|getkeystate|il_add|il_create|il_destroy|instr|substr|isfunc|islabel|IsObject|ln|log|lv_add|lv_delete|lv_deletecol|lv_getcount|lv_getnext|lv_gettext|lv_insert|lv_insertcol|lv_modify|lv_modifycol|lv_setimagelist|mod|onmessage|numget|numput|registercallback|regexmatch|regexreplace|round|sin|tan|sqrt|strlen|sb_seticon|sb_setparts|sb_settext|tv_add|tv_delete|tv_getchild|tv_getcount|tv_getnext|tv_get|tv_getparent|tv_getprev|tv_getselection|tv_gettext|tv_modify|varsetcapacity|winactive|winexist|__New|__Call|__Get|__Set)\b/i,

	'symbol': /\b(alt|altdown|altup|appskey|backspace|browser_back|browser_favorites|browser_forward|browser_home|browser_refresh|browser_search|browser_stop|bs|capslock|control|ctrl|ctrlbreak|ctrldown|ctrlup|del|delete|down|end|enter|esc|escape|f1|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f2|f20|f21|f22|f23|f24|f3|f4|f5|f6|f7|f8|f9|home|ins|insert|joy1|joy10|joy11|joy12|joy13|joy14|joy15|joy16|joy17|joy18|joy19|joy2|joy20|joy21|joy22|joy23|joy24|joy25|joy26|joy27|joy28|joy29|joy3|joy30|joy31|joy32|joy4|joy5|joy6|joy7|joy8|joy9|joyaxes|joybuttons|joyinfo|joyname|joypov|joyr|joyu|joyv|joyx|joyy|joyz|lalt|launch_app1|launch_app2|launch_mail|launch_media|lbutton|lcontrol|lctrl|left|lshift|lwin|lwindown|lwinup|mbutton|media_next|media_play_pause|media_prev|media_stop|numlock|numpad0|numpad1|numpad2|numpad3|numpad4|numpad5|numpad6|numpad7|numpad8|numpad9|numpadadd|numpadclear|numpaddel|numpaddiv|numpaddot|numpaddown|numpadend|numpadenter|numpadhome|numpadins|numpadleft|numpadmult|numpadpgdn|numpadpgup|numpadright|numpadsub|numpadup|pause|pgdn|pgup|printscreen|ralt|rbutton|rcontrol|rctrl|right|rshift|rwin|rwindown|rwinup|scrolllock|shift|shiftdown|shiftup|space|tab|up|volume_down|volume_mute|volume_up|wheeldown|wheelleft|wheelright|wheelup|xbutton1|xbutton2)\b/i,

	'important': /#\b(AllowSameLineComments|ClipboardTimeout|CommentFlag|ErrorStdOut|EscapeChar|HotkeyInterval|HotkeyModifierTimeout|Hotstring|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Include|IncludeAgain|InstallKeybdHook|InstallMouseHook|KeyHistory|LTrim|MaxHotkeysPerInterval|MaxMem|MaxThreads|MaxThreadsBuffer|MaxThreadsPerHotkey|NoEnv|NoTrayIcon|Persistent|SingleInstance|UseHook|WinActivateForce)\b/i,

	'keyword': /\b(Abort|AboveNormal|Add|ahk_class|ahk_group|ahk_id|ahk_pid|All|Alnum|Alpha|AltSubmit|AltTab|AltTabAndMenu|AltTabMenu|AltTabMenuDismiss|AlwaysOnTop|AutoSize|Background|BackgroundTrans|BelowNormal|between|BitAnd|BitNot|BitOr|BitShiftLeft|BitShiftRight|BitXOr|Bold|Border|Button|ByRef|Checkbox|Checked|CheckedGray|Choose|ChooseString|Click|Close|Color|ComboBox|Contains|ControlList|Count|Date|DateTime|Days|DDL|Default|Delete|DeleteAll|Delimiter|Deref|Destroy|Digit|Disable|Disabled|DropDownList|Edit|Eject|Enable|Enabled|Error|Exist|Exp|Expand|ExStyle|FileSystem|First|Flash|Float|FloatFast|Focus|Font|for|global|Grid|Group|GroupBox|GuiClose|GuiContextMenu|GuiDropFiles|GuiEscape|GuiSize|Hdr|Hidden|Hide|High|HKCC|HKCR|HKCU|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_LOCAL_MACHINE|HKEY_USERS|HKLM|HKU|Hours|HScroll|Icon|IconSmall|ID|IDLast|If|IfEqual|IfExist|IfGreater|IfGreaterOrEqual|IfInString|IfLess|IfLessOrEqual|IfMsgBox|IfNotEqual|IfNotExist|IfNotInString|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Ignore|ImageList|in|Integer|IntegerFast|Interrupt|is|italic|Join|Label|LastFound|LastFoundExist|Limit|Lines|List|ListBox|ListView|Ln|local|Lock|Logoff|Low|Lower|Lowercase|MainWindow|Margin|Maximize|MaximizeBox|MaxSize|Minimize|MinimizeBox|MinMax|MinSize|Minutes|MonthCal|Mouse|Move|Multi|NA|No|NoActivate|NoDefault|NoHide|NoIcon|NoMainWindow|norm|Normal|NoSort|NoSortHdr|NoStandard|Not|NoTab|NoTimers|Number|Off|Ok|On|OwnDialogs|Owner|Parse|Password|Picture|Pixel|Pos|Pow|Priority|ProcessName|Radio|Range|Read|ReadOnly|Realtime|Redraw|REG_BINARY|REG_DWORD|REG_EXPAND_SZ|REG_MULTI_SZ|REG_SZ|Region|Relative|Rename|Report|Resize|Restore|Retry|RGB|Right|Screen|Seconds|Section|Serial|SetLabel|ShiftAltTab|Show|Single|Slider|SortDesc|Standard|static|Status|StatusBar|StatusCD|strike|Style|Submit|SysMenu|Tab|Tab2|TabStop|Text|Theme|Tile|ToggleCheck|ToggleEnable|ToolWindow|Top|Topmost|TransColor|Transparent|Tray|TreeView|TryAgain|Type|UnCheck|underline|Unicode|Unlock|UpDown|Upper|Uppercase|UseErrorLevel|Vis|VisFirst|Visible|VScroll|Wait|WaitClose|WantCtrlA|WantF2|WantReturn|Wrap|Xdigit|xm|xp|xs|Yes|ym|yp|ys)\b/i
};<commit_msg>fixed some keywords<commit_after>// NOTES - follows first-first highlight method, block is locked after highlight, different from SyntaxHl
Prism.languages.autohotkey= {
	'comment': /(;[^\n]*|^\s*\/\*[\s\S]*\s*\*\/)/gm,
	'string': /"(([^"\n\r]|"")*)"/gm,
	'function': /[^\(\); \t\,\n\+\*\-\=\?>:\\\/<\&%\[\]]+?(?=\()/gm,  //function - don't use .*\) in the end bcoz string locks it
	'tag': /^[ \t]*[^\s:]+?(?=:[^:])/gm,  //labels
	'variable': /\%\w+\%/g,
	'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,
	'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,
	'ignore': /&(lt|gt|amp);/gi,
	'punctuation': /[\{}[\]\(\):]/g,
	'boolean': /\b(true|false)\b/g,

	'selector': /\b(AutoTrim|BlockInput|Break|Click|ClipWait|Continue|Control|ControlClick|ControlFocus|ControlGet|ControlGetFocus|ControlGetPos|ControlGetText|ControlMove|ControlSend|ControlSendRaw|ControlSetText|CoordMode|Critical|DetectHiddenText|DetectHiddenWindows|Drive|DriveGet|DriveSpaceFree|EnvAdd|EnvDiv|EnvGet|EnvMult|EnvSet|EnvSub|EnvUpdate|Exit|ExitApp|FileAppend|FileCopy|FileCopyDir|FileCreateDir|FileCreateShortcut|FileDelete|FileEncoding|FileGetAttrib|FileGetShortcut|FileGetSize|FileGetTime|FileGetVersion|FileInstall|FileMove|FileMoveDir|FileRead|FileReadLine|FileRecycle|FileRecycleEmpty|FileRemoveDir|FileSelectFile|FileSelectFolder|FileSetAttrib|FileSetTime|FormatTime|GetKeyState|Gosub|Goto|GroupActivate|GroupAdd|GroupClose|GroupDeactivate|Gui|GuiControl|GuiControlGet|Hotkey|ImageSearch|IniDelete|IniRead|IniWrite|Input|InputBox|KeyWait|ListHotkeys|ListLines|ListVars|Loop|Menu|MouseClick|MouseClickDrag|MouseGetPos|MouseMove|MsgBox|OnExit|OutputDebug|Pause|PixelGetColor|PixelSearch|PostMessage|Process|Progress|Random|RegDelete|RegRead|RegWrite|Reload|Repeat|Return|Run|RunAs|RunWait|Send|SendEvent|SendInput|SendMessage|SendMode|SendPlay|SendRaw|SetBatchLines|SetCapslockState|SetControlDelay|SetDefaultMouseSpeed|SetEnv|SetFormat|SetKeyDelay|SetMouseDelay|SetNumlockState|SetScrollLockState|SetStoreCapslockMode|SetTimer|SetTitleMatchMode|SetWinDelay|SetWorkingDir|Shutdown|Sleep|Sort|SoundBeep|SoundGet|SoundGetWaveVolume|SoundPlay|SoundSet|SoundSetWaveVolume|SplashImage|SplashTextOff|SplashTextOn|SplitPath|StatusBarGetText|StatusBarWait|StringCaseSense|StringGetPos|StringLeft|StringLen|StringLower|StringMid|StringReplace|StringRight|StringSplit|StringTrimLeft|StringTrimRight|StringUpper|Suspend|SysGet|Thread|ToolTip|Transform|TrayTip|URLDownloadToFile|WinActivate|WinActivateBottom|WinClose|WinGet|WinGetActiveStats|WinGetActiveTitle|WinGetClass|WinGetPos|WinGetText|WinGetTitle|WinHide|WinKill|WinMaximize|WinMenuSelectItem|WinMinimize|WinMinimizeAll|WinMinimizeAllUndo|WinMove|WinRestore|WinSet|WinSetTitle|WinShow|WinWait|WinWaitActive|WinWaitClose|WinWaitNotActive)\b/i,

	'constant': /\b(a_ahkpath|a_ahkversion|a_appdata|a_appdatacommon|a_autotrim|a_batchlines|a_caretx|a_carety|a_computername|a_controldelay|a_cursor|a_dd|a_ddd|a_dddd|a_defaultmousespeed|a_desktop|a_desktopcommon|a_detecthiddentext|a_detecthiddenwindows|a_endchar|a_eventinfo|a_exitreason|a_formatfloat|a_formatinteger|a_gui|a_guievent|a_guicontrol|a_guicontrolevent|a_guiheight|a_guiwidth|a_guix|a_guiy|a_hour|a_iconfile|a_iconhidden|a_iconnumber|a_icontip|a_index|a_ipaddress1|a_ipaddress2|a_ipaddress3|a_ipaddress4|a_isadmin|a_iscompiled|a_iscritical|a_ispaused|a_issuspended|a_isunicode|a_keydelay|a_language|a_lasterror|a_linefile|a_linenumber|a_loopfield|a_loopfileattrib|a_loopfiledir|a_loopfileext|a_loopfilefullpath|a_loopfilelongpath|a_loopfilename|a_loopfileshortname|a_loopfileshortpath|a_loopfilesize|a_loopfilesizekb|a_loopfilesizemb|a_loopfiletimeaccessed|a_loopfiletimecreated|a_loopfiletimemodified|a_loopreadline|a_loopregkey|a_loopregname|a_loopregsubkey|a_loopregtimemodified|a_loopregtype|a_mday|a_min|a_mm|a_mmm|a_mmmm|a_mon|a_mousedelay|a_msec|a_mydocuments|a_now|a_nowutc|a_numbatchlines|a_ostype|a_osversion|a_priorhotkey|programfiles|a_programfiles|a_programs|a_programscommon|a_screenheight|a_screenwidth|a_scriptdir|a_scriptfullpath|a_scriptname|a_sec|a_space|a_startmenu|a_startmenucommon|a_startup|a_startupcommon|a_stringcasesense|a_tab|a_temp|a_thisfunc|a_thishotkey|a_thislabel|a_thismenu|a_thismenuitem|a_thismenuitempos|a_tickcount|a_timeidle|a_timeidlephysical|a_timesincepriorhotkey|a_timesincethishotkey|a_titlematchmode|a_titlematchmodespeed|a_username|a_wday|a_windelay|a_windir|a_workingdir|a_yday|a_year|a_yweek|a_yyyy|clipboard|clipboardall|comspec|errorlevel)\b/i,

	'builtin': /\b(abs|acos|asc|asin|atan|ceil|chr|class|cos|dllcall|exp|fileexist|Fileopen|floor|getkeystate|il_add|il_create|il_destroy|instr|substr|isfunc|islabel|IsObject|ln|log|lv_add|lv_delete|lv_deletecol|lv_getcount|lv_getnext|lv_gettext|lv_insert|lv_insertcol|lv_modify|lv_modifycol|lv_setimagelist|mod|onmessage|numget|numput|registercallback|regexmatch|regexreplace|round|sin|tan|sqrt|strlen|sb_seticon|sb_setparts|sb_settext|strsplit|tv_add|tv_delete|tv_getchild|tv_getcount|tv_getnext|tv_get|tv_getparent|tv_getprev|tv_getselection|tv_gettext|tv_modify|varsetcapacity|winactive|winexist|__New|__Call|__Get|__Set)\b/i,

	'symbol': /\b(alt|altdown|altup|appskey|backspace|browser_back|browser_favorites|browser_forward|browser_home|browser_refresh|browser_search|browser_stop|bs|capslock|control|ctrl|ctrlbreak|ctrldown|ctrlup|del|delete|down|end|enter|esc|escape|f1|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f2|f20|f21|f22|f23|f24|f3|f4|f5|f6|f7|f8|f9|home|ins|insert|joy1|joy10|joy11|joy12|joy13|joy14|joy15|joy16|joy17|joy18|joy19|joy2|joy20|joy21|joy22|joy23|joy24|joy25|joy26|joy27|joy28|joy29|joy3|joy30|joy31|joy32|joy4|joy5|joy6|joy7|joy8|joy9|joyaxes|joybuttons|joyinfo|joyname|joypov|joyr|joyu|joyv|joyx|joyy|joyz|lalt|launch_app1|launch_app2|launch_mail|launch_media|lbutton|lcontrol|lctrl|left|lshift|lwin|lwindown|lwinup|mbutton|media_next|media_play_pause|media_prev|media_stop|numlock|numpad0|numpad1|numpad2|numpad3|numpad4|numpad5|numpad6|numpad7|numpad8|numpad9|numpadadd|numpadclear|numpaddel|numpaddiv|numpaddot|numpaddown|numpadend|numpadenter|numpadhome|numpadins|numpadleft|numpadmult|numpadpgdn|numpadpgup|numpadright|numpadsub|numpadup|pause|pgdn|pgup|printscreen|ralt|rbutton|rcontrol|rctrl|right|rshift|rwin|rwindown|rwinup|scrolllock|shift|shiftdown|shiftup|space|tab|up|volume_down|volume_mute|volume_up|wheeldown|wheelleft|wheelright|wheelup|xbutton1|xbutton2)\b/i,

	'important': /#\b(AllowSameLineComments|ClipboardTimeout|CommentFlag|ErrorStdOut|EscapeChar|HotkeyInterval|HotkeyModifierTimeout|Hotstring|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Include|IncludeAgain|InstallKeybdHook|InstallMouseHook|KeyHistory|LTrim|MaxHotkeysPerInterval|MaxMem|MaxThreads|MaxThreadsBuffer|MaxThreadsPerHotkey|NoEnv|NoTrayIcon|Persistent|SingleInstance|UseHook|WinActivateForce)\b/i,

	'keyword': /\b(Abort|AboveNormal|Add|ahk_class|ahk_group|ahk_id|ahk_pid|All|Alnum|Alpha|AltSubmit|AltTab|AltTabAndMenu|AltTabMenu|AltTabMenuDismiss|AlwaysOnTop|AutoSize|Background|BackgroundTrans|BelowNormal|between|BitAnd|BitNot|BitOr|BitShiftLeft|BitShiftRight|BitXOr|Bold|Border|Button|ByRef|Checkbox|Checked|CheckedGray|Choose|ChooseString|Click|Close|Color|ComboBox|Contains|ControlList|Count|Date|DateTime|Days|DDL|Default|Delete|DeleteAll|Delimiter|Deref|Destroy|Digit|Disable|Disabled|DropDownList|Edit|Eject|Else|Enable|Enabled|Error|Exist|Exp|Expand|ExStyle|FileSystem|First|Flash|Float|FloatFast|Focus|Font|for|global|Grid|Group|GroupBox|GuiClose|GuiContextMenu|GuiDropFiles|GuiEscape|GuiSize|Hdr|Hidden|Hide|High|HKCC|HKCR|HKCU|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_LOCAL_MACHINE|HKEY_USERS|HKLM|HKU|Hours|HScroll|Icon|IconSmall|ID|IDLast|If|IfEqual|IfExist|IfGreater|IfGreaterOrEqual|IfInString|IfLess|IfLessOrEqual|IfMsgBox|IfNotEqual|IfNotExist|IfNotInString|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Ignore|ImageList|in|Integer|IntegerFast|Interrupt|is|italic|Join|Label|LastFound|LastFoundExist|Limit|Lines|List|ListBox|ListView|Ln|local|Lock|Logoff|Low|Lower|Lowercase|MainWindow|Margin|Maximize|MaximizeBox|MaxSize|Minimize|MinimizeBox|MinMax|MinSize|Minutes|MonthCal|Mouse|Move|Multi|NA|No|NoActivate|NoDefault|NoHide|NoIcon|NoMainWindow|norm|Normal|NoSort|NoSortHdr|NoStandard|Not|NoTab|NoTimers|Number|Off|Ok|On|OwnDialogs|Owner|Parse|Password|Picture|Pixel|Pos|Pow|Priority|ProcessName|Radio|Range|Read|ReadOnly|Realtime|Redraw|REG_BINARY|REG_DWORD|REG_EXPAND_SZ|REG_MULTI_SZ|REG_SZ|Region|Relative|Rename|Report|Resize|Restore|Retry|RGB|Right|Screen|Seconds|Section|Serial|SetLabel|ShiftAltTab|Show|Single|Slider|SortDesc|Standard|static|Status|StatusBar|StatusCD|strike|Style|Submit|SysMenu|Tab|Tab2|TabStop|Text|Theme|Tile|ToggleCheck|ToggleEnable|ToolWindow|Top|Topmost|TransColor|Transparent|Tray|TreeView|TryAgain|Type|UnCheck|underline|Unicode|Unlock|UpDown|Upper|Uppercase|UseErrorLevel|Vis|VisFirst|Visible|VScroll|Wait|WaitClose|WantCtrlA|WantF2|WantReturn|While|Wrap|Xdigit|xm|xp|xs|Yes|ym|yp|ys)\b/i
};<|endoftext|>