// ========================================================================== // Project: SproutCore - JavaScript Application Framework // Copyright: ©2006-2011 Strobe Inc. and contributors. // portions copyright ©2011 Apple Inc. // License: Licensed under MIT license (see license.js) // ==========================================================================

/*global module, test, ok, equals, stop, start */

var items = [

{ title: 'Menu Item', keyEquivalent: 'ctrl_shift_n' },
{ title: 'Checked Menu Item', isChecked: YES, keyEquivalent: 'ctrl_a' },
{ title: 'Selected Menu Item', keyEquivalent: 'backspace' },
{ isSeparator: YES },
{ title: 'Menu Item with Icon', icon: 'inbox', keyEquivalent: 'ctrl_m' },
{ title: 'Menu Item with Icon', icon: 'folder', keyEquivalent: ['ctrl_p', 'ctrl_f'] },
{ isSeparator: YES },
{ title: 'Selected Menu Item…', isChecked: YES, keyEquivalent: 'ctrl_shift_o' },
{ title: 'Item with Submenu', subMenu: [{ title: 'Submenu item 1' }, { title: 'Submenu item 2'}] },
{ title: 'Disabled Menu Item', isEnabled: NO },
{ isSeparator: YES },
{ title: 'Unique Menu Item Class Per Item', exampleView: SC.MenuItemView.extend({
    classNames: 'custom-menu-item'.w()
  }) }
// { isSeparator: YES },
// { groupTitle: 'Menu Label', items: [{ title: 'Nested Item' }, { title: 'Nested Item' }] }
];

var menu;

module('SC.MenuPane UI', {

setup: function () {
  SC.run(function () {
    menu = SC.MenuPane.create({
      layout: { width: 206 },

      selectedItemChanged: function () {
        this._selectedItemCount = (this._selectedItemCount || 0) + 1;
      }.observes('selectedItem'),

      countAction: function () {
        this._actionCount = (this._actionCount || 0) + 1;
      }
    });

    items[0].target = menu;
    items[0].action = 'countAction';

    items[1].action = function () {
      menu._functionActionCount = (menu._functionActionCount || 0) + 1;
    };

    menu.set('items', items);
  });
},

teardown: function () {
  SC.run(function () {
    menu.destroy();
  });
  menu = null;
}

});

/**

Simulates clicking on the specified view.

@param {SC.View} view the view
@param {Boolean} [shiftKey] simulate shift key pressed
@param {Boolean} [ctrlKey] simulate ctrlKey pressed

*/ function clickOn(view, shiftKey, ctrlKey) {

var layer    = view.get('layer'),
    opts     = { shiftKey: !!shiftKey, ctrlKey: !!ctrlKey, which: 1 },
    ev;

ok(layer, 'clickOn() precond - view %@ should have layer'.fmt(view.toString()));

ev = SC.Event.simulateEvent(layer, 'mousedown', opts);
SC.Event.trigger(layer, 'mousedown', [ev]);

ev = SC.Event.simulateEvent(layer, 'mouseup', opts);
SC.Event.trigger(layer, 'mouseup', [ev]);
SC.RunLoop.begin().end();
layer = null;

}

/**

Simulates a key press on the specified view.

@param {SC.View} view the view
@param {Number} keyCode key to simulate
@param {Boolean} [isKeyPress] simulate key press event
@param {Boolean} [shiftKey] simulate shift key pressed
@param {Boolean} [ctrlKey] simulate ctrlKey pressed

*/ function keyPressOn(view, keyCode, isKeyPress, shiftKey, ctrlKey) {

var layer    = view.get('layer'),
  opts     = {
    shiftKey: !!shiftKey,
    ctrlKey: !!ctrlKey,
    keyCode: keyCode,
    charCode: isKeyPress ? keyCode : 0,
    which: keyCode
  },
  ev;

ok(layer, 'keyPressOn() precond - view %@ should have layer'.fmt(view.toString()));

ev = SC.Event.simulateEvent(layer, 'keydown', opts);
SC.Event.trigger(layer, 'keydown', [ev]);

if (isKeyPress) {
  ev = SC.Event.simulateEvent(layer, 'keypress', opts);
  SC.Event.trigger(layer, 'keypress', [ev]);
}

ev = SC.Event.simulateEvent(layer, 'keyup', opts);
SC.Event.trigger(layer, 'keyup', [ev]);
SC.RunLoop.begin().end();
layer = null;

}

test('Basic UI', function () {

SC.run(function () {
  menu.popup();
});
ok(menu.$().hasClass('sc-menu'), 'pane should have "sc-menu" class');
ok(menu.$().hasClass('sc-regular-size'), 'pane should have default control size class');
ok(!menu.get('isSubMenu'), 'isSubMenu should be NO on menus that are not submenus');
var menuItem = menu.get('menuItemViews')[0], selectedItem;
menuItem.mouseEntered();
clickOn(menuItem, NO, NO);
stop();

setTimeout(function () {
  selectedItem = menu.get('selectedItem');
  ok(selectedItem, 'menu should have selectedItem property set after clicking on menu item');
  equals(selectedItem ? selectedItem.title : null, menuItem.get('content').title, 'selectedItem should be set to the content item that was clicked');
  equals(1, menu._selectedItemCount, 'selectedItem should only change once when a menu item is clicked');
  equals(1, menu._actionCount, 'action is fired once when menu item is clicked');
  SC.run(function () {
    menu.remove();
  });
  ok(!menu.get('isVisibleInWindow'), 'menu should not be visible after being removed');
  equals(menu.get('currentMenuItem'), null, 'currentMenuItem should be null after being removed');
  start();
}, 250);

});

test('Control size', function () {

var smallPane, largePane, views, items = [
  { title: 'Can I get get get' },
  { title: 'To know know know know', isSeparator: YES },
  { title: 'Ya better better baby' }
];

SC.run(function () {
  smallPane = SC.MenuPane.create({
    controlSize: SC.SMALL_CONTROL_SIZE,
    items: items
  });

  smallPane.popup();
});
views = smallPane.get('menuItemViews');

var small_constants = SC.BaseTheme.menuRenderDelegate['sc-small-size'];
equals(views[0].get('frame').height, small_constants.itemHeight, 'should change itemHeight');
equals(views[1].get('frame').height, small_constants.itemSeparatorHeight, 'should change itemSeparatorHeight');
equals(views[0].get('frame').y, small_constants.menuHeightPadding / 2, 'should change menuHeightPadding');
SC.run(function () {
  smallPane.remove();
});

SC.run(function () {
  largePane = SC.MenuPane.create({
    controlSize: SC.LARGE_CONTROL_SIZE,
    items: items
  });

  largePane.popup();
});
views = largePane.get('menuItemViews');

var large_constants = SC.BaseTheme.menuRenderDelegate['sc-large-size'];
equals(views[0].get('frame').height, large_constants.itemHeight, 'should change itemHeight');
equals(views[1].get('frame').height, large_constants.itemSeparatorHeight, 'should change itemSeparatorHeight');
equals(views[0].get('frame').y, large_constants.menuHeightPadding / 2, 'should change menuHeightPadding');

SC.run(function () {
  largePane.remove();
});

});

test('Legacy Function Support', function () {

SC.run(function () {
  menu.popup();
});
var menuItem = menu.get('menuItemViews')[1], selectedItem;
menuItem.mouseEntered();
clickOn(menuItem, NO, NO);
stop();

setTimeout(function () {
  selectedItem = menu.get('selectedItem');
  equals(1, menu._functionActionCount, 'Function should be called if it is set as the action and the menu item is clicked');

  SC.run(function () {
    menu.remove();
  });
  start();
}, 250);

});

test('Custom MenuItemView Class', function () {

equals(menu.get('exampleView'), SC.MenuItemView, 'SC.MenuPane should generate SC.MenuItemViews by default');
var menu2;

SC.run(function () {
  menu2 = SC.MenuPane.create({
    exampleView: SC.MenuItemView.extend({
      classNames: 'custom-menu-item'.w()
    }),

    items: items
  });

  menu2.popup();
});
ok(menu2.$('.custom-menu-item').length > 0, 'SC.MenuPane should generate instances of custom classes if exampleView is changed');
SC.run(function () {
  menu2.remove();
});

});

test('Custom MenuItemView Class on an item using itemExampleViewKey', function () {

equals(menu.get('exampleView'), SC.MenuItemView, 'SC.MenuPane should generate SC.MenuItemViews by default');
SC.run(function () {
  menu.popup();
});
ok(menu.$('.custom-menu-item').length === 1, 'SC.MenuPane should generate one instance of a custom class if the item has an exampleView property');
ok(SC.$(SC.$('.sc-menu-item')[11]).hasClass('custom-menu-item'), 'The last menu item should have a custom class');

SC.run(function () {
  menu.remove();
});

});

test('Basic Submenus', function () {

var smallMenu,
  menuItem, subMenu;

SC.run(function () {
  smallMenu = SC.MenuPane.create({
    controlSize: SC.SMALL_CONTROL_SIZE,
    items: items
  });
  menuItem = smallMenu.get('menuItemViews')[8];

  smallMenu.popup();
});

menuItem.mouseEntered();
SC.RunLoop.begin().end();
ok(menuItem.get('hasSubMenu'), 'precond - menu item has a submenu');
subMenu = menuItem.get('subMenu');
ok(!subMenu.get('isVisibleInWindow'), 'submenus should not open immediately');
stop();
setTimeout(function () {
  ok(subMenu.get('isVisibleInWindow'), 'submenu should open after 100ms delay');
  ok(subMenu.get('isSubMenu'), 'isSubMenu should be YES on submenus');
  ok(subMenu.get('controlSize'), SC.SMALL_CONTROL_SIZE, "submenu should inherit parent's controlSize");
  SC.run(function () {
    smallMenu.remove();
  });
  ok(!subMenu.get('isVisibleInWindow'), 'submenus should close if their parent menu is closed');
  equals(subMenu.getPath('items.length'), 2, 'submenus should have 2 items');

  menuItem.get('content').set('subMenu', [{ title: 'Submenu item 3' }]);
  subMenu = menuItem.get('subMenu');
  equals(subMenu.getPath('items.length'), 1, 'submenus should have 1 item');

  smallMenu.destroy();
  ok(smallMenu.get('isDestroyed'), 'smallMenu should be destroyed');
  ok(menuItem.get('isDestroyed'), 'menuItem should be destroyed');
  ok(subMenu.get('isDestroyed'), 'submenus should be destroyed');
  start();
}, 150);

});

test('Menu Item Localization', function () {

ok(menu.get('localize'), 'menu panes should be localized by default');
var locMenu, items;

SC.stringsFor('en', { 'Localized.Text': 'LOCALIZED TEXT' });
items = [ 'Localized.Text' ];

SC.run(function () {
  locMenu = SC.MenuPane.create({
    layout: { width: 200 },
    items: items,
    localize: NO
  });

  locMenu.popup();
});
equals('Localized.Text', locMenu.$('.sc-menu-item .value').text(), 'Menu item titles should not be localized if localize is NO');

SC.run(function () {
  locMenu.remove();
});

SC.run(function () {
  locMenu = SC.MenuPane.create({
    items: items,
    localize: YES
  });

  locMenu.popup();
});
equals('LOCALIZED TEXT', locMenu.$('.sc-menu-item .value').text(), 'Menu item titles should be localized if localize is YES');
SC.run(function () {
  locMenu.remove();
});

});

test('Automatic Closing', function () {

SC.run(function () {
  menu.popup();
});
ok(menu.get('isVisibleInWindow'), 'precond - window should be visible');
SC.run(function () {
  menu.windowSizeDidChange();
});
ok(!menu.get('isVisibleInWindow'), 'menu should close if window resizes');

SC.run(function () {
  menu.popup();
});
clickOn(menu);
ok(!menu.get('isVisibleInWindow'), 'menu should close if anywhere other than a menu item is clicked');

});

test('keyEquivalents', function () {

var keyEquivalents = menu._keyEquivalents;

// verify that keyEquivalents were mapped correctly and that multiple
// keyEquivalents work
menu.items.forEach(function (item) {
  var keyEq = item.keyEquivalent, idx, len;
  if (!keyEq) return;

  if (SC.typeOf(keyEq) === SC.T_ARRAY) {
    for (idx = 0, len = keyEq.length; idx < len; idx++) {
      ok(keyEquivalents[keyEq[idx]], "keyEquivalent should map to " + keyEq[idx]);
    }
  }
  else {
    ok(keyEquivalents[keyEq], "keyEquivalent should map to " + keyEq);
  }
});

});

test('scrolling', function () {

var currentMenuItem;

SC.run(function () {
  menu.popup();
});
menu.set('currentMenuItem', menu.get('menuItemViews')[0]);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Menu Item', 'menu should begin at first item');

keyPressOn(menu, SC.Event.KEY_DOWN);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Checked Menu Item', 'arrow down should move one item down');

keyPressOn(menu, SC.Event.KEY_UP);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Menu Item', 'arrow up should move one item up');

keyPressOn(menu, SC.Event.KEY_PAGEDOWN);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Unique Menu Item Class Per Item', 'page down should move one page down');

keyPressOn(menu, SC.Event.KEY_PAGEUP);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Menu Item', 'page up should move one page up');

keyPressOn(menu, SC.Event.KEY_END);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Unique Menu Item Class Per Item', 'end should move to the last item');

keyPressOn(menu, SC.Event.KEY_HOME);
currentMenuItem = menu.get('currentMenuItem');
equals(currentMenuItem.get('title'), 'Menu Item', 'home should move to the first item');

});

test('aria-role attribute', function () {

var menuPane, menuItems, normalItem, itemWithCheckBox, separatorItem;

SC.run(function () {
  menuPane = SC.MenuPane.create({
    layout: { width: 200 },
    items: items,
    itemCheckboxKey: 'isChecked'
  });

  menuPane.popup();
});

equals(menuPane.$().attr('role'), 'menu', "menu pane should have role set");

menuItems = menuPane.get('menuItemViews');
normalItem        = menuItems[0];
itemWithCheckBox  = menuItems[1];
separatorItem     = menuItems[3];

equals(normalItem.$().attr('role'), 'menuitem', "normal menuitem has correct role set");
equals(itemWithCheckBox.$().attr('role'), 'menuitemcheckbox', "menuitem with checkbox has correct role set");
equals(separatorItem.$().attr('role'), 'separator', "separator menuitem has correct role set");
clickOn(menuPane);

});

test('aria-checked attribute', function () {

var menuPane,
  itemWithCheckBox;

SC.run(function () {
  menuPane = SC.MenuPane.create({
    layout: { width: 200 },
    items: items,
    itemCheckboxKey: 'isChecked'
  });

  menuPane.popup();
});

itemWithCheckBox = menuPane.get('menuItemViews')[1];

equals(itemWithCheckBox.$().attr('aria-checked'), "true", "checked menuitem has aria-checked attribute set");
clickOn(menuPane);

});

test('Menu item keys', function () {

var menuPane,
  items,
  submenu,
  menuItem;

submenu = [
  { title: 'Submenu item' }
];

items = [
  {
    TheTitle: 'Menu item',
    TheValue: 1,
    TheToolTip: 'Menu tooltip',
    AmIEnabled: false,
    MyIcon: 'folder',
    MyHeight: 50,
    MySubmenu: submenu,
    AmIASeparator: false
  }
];

SC.run(function () {
  menuPane = SC.MenuPane.create({
    layout: { width: 200 },
    items: items,
    itemTitleKey: 'TheTitle',
    itemValueKey: 'TheValue',
    itemToolTipKey: 'TheToolTip',
    itemIsEnabledKey: 'AmIEnabled',
    itemIconKey: 'MyIcon',
    itemSubMenuKey: 'MySubmenu',
    itemSeparatorKey: 'AmIASeparator'
  });

  menuPane.popup();
});

menuItem = menuPane.get('menuItemViews')[0];

equals(menuItem.get('title'), 'Menu item');
equals(menuItem.get('value'), 1);
equals(menuItem.get('toolTip'), 'Menu tooltip');
equals(menuItem.get('isEnabled'), false);
equals(menuItem.get('icon'), 'folder');
ok(SC.kindOf(menuItem.get('subMenu'), SC.MenuPane));
equals(menuItem.get('isSeparator'), false);
clickOn(menuPane);

});