Permalink
Browse files

Updates experimental SC.SelectView for backward-compatibility with le…

…gacy view.
  • Loading branch information...
1 parent 5a96257 commit 99e108665b82303c8d56e75d27e15f673c7431a5 @nicolasbadia nicolasbadia committed with dcporter Oct 30, 2013
View
1 Buildfile
@@ -45,6 +45,7 @@ config :statechart, :required => [:core_foundation], :test_required => [:co
config :ajax, :required => [:runtime, :core_foundation]
config :designer, :required => [:runtime, :foundation, :desktop, :template_view]
+config :"experimental/select_view", :test_required => [:desktop]
config :"experimental/split_view", :test_required => [:desktop]
# WRAPPER FRAMEWORKS
View
17 frameworks/experimental/frameworks/select_view/ext/menu.js
@@ -53,14 +53,24 @@ SC.AutoResizingMenuPane = SC.MenuPane.extend(
menuWidthPadding: SC.propertyFromRenderDelegate('menuWidthPadding', 0),
/**
+ The view class to use when creating new menu item views.
+
+ The menu pane will automatically create an instance of the view class you
+ set here for each item in the `items` array. You may provide your own
+ subclass for this property to display the customized content.
+
+ @type SC.View
+ @default SC.AutoResizingMenuItemView
+ */
+ exampleView: SC.AutoResizingMenuItemView,
+
+ /**
@private
In addition to the normal init, we need to schedule an automatic resize.
*/
init: function() {
sc_super();
- this.set('exampleView', SC.AutoResizingMenuItemView);
-
if (this.get('shouldAutoResize')) {
this.invokeOnce('_updateMenuWidth');
}
@@ -119,7 +129,8 @@ SC.AutoResizingMenuPane = SC.MenuPane.extend(
width = Math.max(width, view.get('measuredSize').width + this.get('menuWidthPadding'));
}
- this.adjust('width', width);
+
+ this.adjust({ 'width': width, height: this.get('menuHeight') });
this.positionPane();
}
});
View
9 frameworks/experimental/frameworks/select_view/mixins/select_view_menu.js
@@ -61,7 +61,7 @@ SC.SelectViewMenu = {
@type {SC.MenuItemView}
@default SC.MenuItemView subclass
*/
- exampleView: SC.MenuItemView.extend({
+ exampleView: SC.AutoResizingMenuItemView.extend({
isChecked: function() {
// _lastIsChecked is used by the SelectViewMenu mixin above to determine whether
// the isChecked property needs to be invalidated.
@@ -79,6 +79,7 @@ SC.SelectViewMenu = {
/** @private */
_svm_bindToProperties: [
'items',
+ 'target', 'action',
'itemTitleKey', 'itemIsEnabledKey', 'itemValueKey', 'itemIconKey',
'itemHeightKey', 'itemSubMenuKey', 'itemSeparatorKey', 'itemTargetKey',
'itemActionKey', 'itemCheckboxKey', 'itemShortCutKey',
@@ -94,11 +95,13 @@ SC.SelectViewMenu = {
return;
}
- var props = this._svm_bindToProperties, idx, len = props.length, key;
+ var props = this._svm_bindToProperties, idx, len = props.length, key, toKey;
for (idx = 0; idx < len; idx++) {
key = props[idx];
- this[key + 'Binding'] = this.bind(key, bindTo, key);
+ toKey = key;
+ if (key === 'items') toKey = 'displayItems';
+ this[key + 'Binding'] = this.bind(key, bindTo, toKey);
}
this._svm_isBoundTo = bindTo;
View
2 frameworks/experimental/frameworks/select_view/tests/ext/menu_resizing.js
@@ -1,7 +1,7 @@
module("Menus -- Automatic Resizing");
function createMenu(items) {
- return SC.MenuPane.create({
+ return SC.AutoResizingMenuPane.create({
items: items
});
}
View
7 frameworks/experimental/frameworks/select_view/tests/mixins/select_view_menu/bindings.js
@@ -2,19 +2,22 @@ module("SelectViewMenu -- Bindings");
function setValues(obj, props, toValue) {
for (var idx = 0; idx < props.length; idx++) {
- obj.set(props[idx], toValue);
+ var prop = props[idx];
+ if (prop === 'items') prop = 'displayItems';
+ obj.set(prop, toValue);
}
}
function validateValues(obj, props, value) {
for (var idx = 0; idx < props.length; idx++) {
- equals(obj.get(props[idx]), value);
+ equals(obj.get(props[idx]), value, props[idx]);
}
}
test("Proxying all properties from SelectView to MenuView works.", function() {
var proxyProperties = [
'items',
+ 'target', 'action',
'itemTitleKey', 'itemIsEnabledKey', 'itemValueKey', 'itemIconKey',
'itemHeightKey', 'itemSubMenuKey', 'itemSeparatorKey', 'itemTargetKey',
'itemActionKey', 'itemCheckboxKey', 'itemShortCutKey',
View
147 frameworks/experimental/frameworks/select_view/tests/views/select/method.js
@@ -0,0 +1,147 @@
+// ==========================================================================
+// 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 htmlbody ok equals same stop start */
+
+var pane, view , view1, view2, view3, view4 ;
+
+module("SC.SelectView",{
+
+ //setup
+ setup: function() {
+ SC.RunLoop.begin();
+ var isDue = NO ;
+
+ //pane
+ pane = SC.MainPane.create({
+ objs : ["Around","The","World"],
+ objs2 : [{ title: "Around", pos: 3},
+ { title: "The", pos: 1},
+ { title: "World", pos: 2 },
+ { title: "Again", pos: 4}],
+ selectedValue: "World",
+ isDue: YES,
+ childViews: [
+
+ //view1
+ SC.SelectView.extend({
+ items: ["To","Back", "You"],
+ disableSort: NO
+ }),
+
+ //view2
+ SC.SelectView.extend({
+ items: ["Drop","Down", "Menu"]
+ }),
+
+ //view3
+ SC.SelectView.extend({
+ itemsBinding: '*owner.objs',
+ valueBinding: '*owner.selectedValue',
+ isVisibleBinding: '*owner.isDue'
+ }),
+
+ //view4
+ SC.SelectView.extend({
+ itemsBinding: '*owner.objs2',
+ valueBinding: '*owner.selectedValue',
+ itemValueKey: 'title',
+ itemTitleKey: 'title',
+ itemSortKey: 'pos'
+ }),
+
+ //view5
+ SC.SelectView.extend({
+ items: ["My","New", "List"]
+ }),
+
+ //view6
+ SC.SelectView.extend({
+ items: ["My","New", "List"],
+ customViewClassName: 'custom-menu-item',
+ customViewMenuOffsetWidth: 46
+ })
+ ]
+ });
+
+ view1 = pane.childViews[0] ;
+ view2 = pane.childViews[1] ;
+ view3 = pane.childViews[2] ;
+ view4 = pane.childViews[3] ;
+ view5 = pane.childViews[4] ;
+ view6 = pane.childViews[5] ;
+
+ pane.append(); // make sure there is a layer...
+ SC.RunLoop.end();
+ },
+
+ //teardown
+ teardown: function() {
+ pane.remove() ;
+ pane = view = null ;
+ }
+});
+
+//test2
+test("Check if valueBinding works", function() {
+ equals('World',view4.get('value'),'Value should be') ;
+});
+
+//test5
+test("sortObjects() sorts the items of the Drop Down component", function() {
+ var obj = view1.get("items");
+ SC.run(function() { view1.showMenu(); });
+ obj = view1.menu.get('displayItems');
+
+ equals("Back",obj.get(0).title,'First item should be') ;
+ equals("To",obj.get(1).title,'Second item should be') ;
+ equals("You",obj.get(2).title,'Third item should be') ;
+
+ SC.run(function() { view1.hideMenu(); });
+});
+
+//test7
+test("isEnabled=NO should add disabled class", function() {
+ SC.RunLoop.begin() ;
+ view1.set('isEnabled', NO) ;
+ SC.RunLoop.end() ;
+ ok(view1.$().hasClass('disabled'), 'should have disabled class') ;
+});
+
+// I think this test is probably somewhat pointless, but perhaps some
+// buggy observers being called could break it or something...
+test("Check if setting a value actually changes the selection value", function() {
+ SC.RunLoop.begin() ;
+ view2.set('value','Menu') ;
+ SC.RunLoop.end() ;
+
+ equals(view2.get('value'), 'Menu', 'value of Drop down should change to') ;
+}) ;
+
+//test10
+test('Setting the view\'s items should not result in an error.', function() {
+ try { view1.set('items', null); }
+ catch (e) {
+ ok(false, 'Nulling out items should not throw an error.');
+ }
+});
+
+//test11
+test("The properties for select button should take default values unless specified", function() {
+ var prop1 = view5.get('customViewClassName');
+ var prop2 = view5.get('customViewMenuOffsetWidth');
+ equals(prop1,null,'Custom view class name should be null');
+ equals(prop2,null,'Custom view menu off set width should be 0');
+});
+
+//test12
+test("The properties for select button should take the specified values", function() {
+ var prop1 = view6.get('customViewClassName');
+ var prop2 = view6.get('customViewMenuOffsetWidth');
+ equals(prop1,'custom-menu-item','Custom view class name should be custom-menu-item');
+ equals(prop2,46,'Custom view menu off set width should be 46');
+});
View
20 frameworks/experimental/frameworks/select_view/tests/views/select/selected_item.js
@@ -187,5 +187,25 @@ test("value does not change when an SC.Object item's value changes when that ite
});
+test("title changes when items are changed", function() {
+ SC.RunLoop.begin();
+ var view = createView({ value: "Found" });
+ SC.RunLoop.end();
+
+ equals(view.get('title'), "Lost", "Title starts at that of selected item");
+
+ SC.RunLoop.begin();
+ view.set('items', [{title: 'Finally found', value: 'Found'}]);
+ SC.RunLoop.end();
+
+ equals(view.get('title'), "Finally found", "Title changed");
+ SC.RunLoop.begin();
+ view.set('items', [{title: 'Hello', value: 'hi'}]);
+ SC.RunLoop.end();
+
+ equals(view.get('title'), "", "Title is empty");
+ equals(view.get('value'), "Found", "Value has not changed");
+
+});
View
289 frameworks/experimental/frameworks/select_view/tests/views/select/ui.js
@@ -0,0 +1,289 @@
+// ==========================================================================
+// 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 htmlbody ok equals same stop start */
+
+
+//control test pane
+var pane = SC.ControlTestPane.design()
+ //sample1
+ .add("Basic", SC.SelectView, {
+ items: ['None', 'Low', 'Medium', 'High']
+ })
+
+ //sample2
+ .add("Disabled", SC.SelectView, {
+ isEnabled: NO, items: ['None', 'Low', 'Medium', 'High']
+ })
+
+ //sample3
+ .add("NotVisible", SC.SelectView, {
+ isVisible: NO, items: ['None', 'Low', 'Medium', 'High']
+ })
+
+ //sample4
+ .add("SortedObjects", SC.SelectView, {
+ items:['None', 'Low', 'Medium', 'High'],
+ disableSort: NO
+ })
+
+ //sample5
+ .add("UnsortedObjects", SC.SelectView, {
+ items: ['None', 'Low', 'Medium', 'High'],
+ disableSort: YES
+ })
+
+ //sample6
+ .add("redraw", SC.SelectView, {
+ layout: { width: '150', right: '0' }
+ })
+
+ //sample7
+ .add("SelectButtonWithIcon", SC.SelectView, {
+ items: [{ title: "None", icon: 'select-button-icon' },
+ { title: "Low", icon: 'select-button-icon' },
+ { title: "Medium", icon: 'select-button-icon' },
+ { title: "High", icon: 'select-button-icon' }],
+ itemTitleKey: 'title',
+ itemIconKey: 'icon',
+ itemValueKey: 'title',
+ value: 'None',
+ showCheckbox: YES
+ })
+
+ //sample8
+ .add("SortKey", SC.SelectView, {
+ items: [{ title: "None", pos: 3},
+ { title: "Low", pos: 1},
+ { title: "Medium", pos: 2 },
+ { title: "High", pos: 4}],
+ itemTitleKey: 'title',
+ disableSort: NO,
+ itemSortKey: 'pos',
+ showCheckbox: YES
+ })
+
+ //sample9
+ .add("StaticLayout", SC.SelectView, {
+ useStaticLayout: YES,
+ items:['None', 'Low', 'Medium', 'High'],
+ layout: { width: '150', right: '0' }
+ })
+
+ //sample10
+ .add("DisableItem", SC.SelectView, {
+ items: [{ title: "None", pos: 3, isEnabled: YES },
+ { title: "Low", pos: 1, isEnabled: NO },
+ { title: "Medium", pos: 2, isEnabled: YES },
+ { title: "High", pos: 4, isEnabled: NO }],
+ itemTitleKey: 'title',
+ disableSort: NO,
+ isEnabledKey: 'isEnabled',
+ itemSortKey: 'pos',
+ showCheckbox: YES
+ })
+
+ // sample11
+ .add("SelectButtonWithEmptyName", SC.SelectView, {
+ items: [{ title: "None", icon: 'select-button-icon' },
+ { title: "Low", icon: 'select-button-icon' },
+ { title: "Medium", icon: 'select-button-icon' },
+ { title: "High", icon: 'select-button-icon' }],
+ itemTitleKey: 'title',
+ itemIconKey: 'icon',
+ emptyName: '&lt;empty&gt;',
+ escapeHTML: NO,
+ showCheckbox: YES
+ })
+
+ .add("SelectWithSeparator", SC.SelectView, {
+ items: [{ title: "None", isSeparator: YES },
+ { title: "Low", isEnabled: NO },
+ { isSeparator: YES },
+ { title: "High" }],
+ itemTitleKey: 'title'
+ });
+
+
+// ..........................................................
+// TEST VIEWS
+//
+
+module('SC.SelectView ui', {
+ setup: function(){
+ htmlbody('<style> .sc-static-layout { border: 1px red dotted; } </style>');
+ pane.standardSetup().setup();
+ },
+ teardown: function(){
+ pane.standardSetup().teardown();
+ clearHtmlbody();
+ }
+});
+
+// test1
+test("Check the visiblity of the selectButtons", function() {
+ ok(pane.view('Basic').get('isVisibleInWindow'), 'Basic.isVisibleInWindow should be YES') ;
+ ok(pane.view('Disabled').get('isVisibleInWindow'), 'Disabled.isVisibleInWindow should be YES') ;
+ ok(!pane.view('NotVisible').get('isVisibleInWindow'), 'NotVisible.isVisibleInWindow should be NO') ;
+ ok(pane.view('SortedObjects').get('isVisibleInWindow'), 'SortedObjects.isVisibleInWindow should be YES') ;
+ ok(pane.view('UnsortedObjects').get('isVisibleInWindow'), 'UnsortedObjects.isVisibleInWindow should be YES') ;
+ ok(pane.view('redraw').get('isVisibleInWindow'), 'redraw.isVisibleInWindow should be YES') ;
+ ok(pane.view('SelectButtonWithIcon').get('isVisibleInWindow'), 'SelectButtonWithIcon.isVisibleInWindow should be YES') ;
+ ok(pane.view('StaticLayout').get('isVisibleInWindow'), 'StaticLayout.isVisibleInWindow should be YES') ;
+ ok(pane.view('SelectButtonWithEmptyName').get('isVisibleInWindow'), 'SelectButtonWithEmptyName.isVisibleInWindow should be YES') ;
+ ok(pane.view('SelectWithSeparator').get('isVisibleInWindow'), 'SelectButtonWithEmptyName.isVisibleInWindow should be YES') ;
+}) ;
+
+//test2
+test("Basic", function() {
+ var view=pane.view('Basic').$();
+ ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.hasClass('icon'), 'hasClass(icon) should be NO') ;
+ ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
+}) ;
+
+//test3
+test("Disabled", function() {
+ var view=pane.view('Disabled').$() ;
+ ok(view.hasClass('disabled'), 'hasClass(disabled) should be YES') ;
+ ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.hasClass('icon'), 'hasClass(icon) should be NO') ;
+ ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
+}) ;
+
+//test4
+test("NotVisible", function() {
+ var view=pane.view('NotVisible').$();
+ ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
+ ok(!view.hasClass('sel'), 'should not have sel class') ;
+}) ;
+
+//test5
+test("SortedObjects", function() {
+ var view = pane.view('SortedObjects');
+ equals(null,view.get('itemSortKey'), 'sortkey not specified') ;
+ ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
+ ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
+ SC.run(function() { view.showMenu(); });
+ equals(view.getPath("menu.displayItems")[0].title, "High", "The first item, when sorted, should be High.");
+ SC.run(function() { view.hideMenu(); });
+}) ;
+
+//test6
+test("UnsortedObjects", function() {
+ var view = pane.view('UnsortedObjects');
+ equals(YES,view.get('disableSort'), 'Sorting disabled') ;
+
+ ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
+ ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
+}) ;
+
+//test7
+test("redraw", function() {
+ var view=pane.view('redraw');
+ ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
+ ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.$().hasClass('def'), 'hasClass(def) should be NO');
+
+ ok(view.get('items') === null, "Items should be empty");
+ SC.RunLoop.begin();
+ view.set('items', ['Calendar', 'Work', 'Home']);
+ SC.RunLoop.end();
+ ok(view.get('items').length === 3, "Items length should be 3");
+
+ // Can someone actually put a redraw test here?
+}) ;
+
+//test8
+test("SelectButtonWithIcon", function() {
+ var view=pane.view('SelectButtonWithIcon').$();
+ ok(view.hasClass('icon'), 'hasClass(Icon) should be YES') ;
+ ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
+}) ;
+
+//test9
+test("Check if the objects are sorted based on sortKey", function() {
+ var view=pane.view('SortKey');
+
+
+ SC.run(function() { view.showMenu(); });
+
+ equals('None',view.getPath("menu.displayItems")[2].title, 'Third object should be "None" ') ;
+ SC.run(function() { view.hideMenu(); });
+}) ;
+
+//test10
+test("StaticLayout", function() {
+ var view = pane.view('StaticLayout');
+ ok(!view.$().hasClass('disabled'), 'should not have disabled class');
+ ok(!view.$().hasClass('sel'), 'should not have sel class');
+});
+
+//test11
+test("SelectButtonWithEmptyName", function() {
+ var view=pane.view('SelectButtonWithEmptyName').$(),
+ label = pane.view('SelectButtonWithEmptyName').$('label');
+ ok(!view.hasClass('icon'), 'hasClass(Icon) should be NO') ;
+ ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
+ equals(label[0].innerHTML, '&lt;empty&gt;', 'The label should be "&lt;empty&gt;"');
+});
+
+/**
+ This is just a simple test that shows that when the first item provided to
+ SC.SelectView is a separator, it ignores it as the default
+ in favor of the first item with a value that is selectable.
+*/
+test("SelectWithSeparator", function() {
+ var view=pane.view('SelectWithSeparator');
+ ok(!view.$().hasClass('icon'), 'hasClass(Icon) should be NO') ;
+ ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
+ ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
+ ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
+ ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
+ ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
+ ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
+
+ SC.run(function() { view.showMenu(); });
+ equals(view.getPath("menu.displayItems")[0].title, 'Low', 'The label should be "Low"');
+ SC.run(function() { view.hideMenu(); });
+});
View
179 frameworks/experimental/frameworks/select_view/views/select.js
@@ -90,7 +90,7 @@ SC.SelectView = SC.PopupButtonView.extend({
@type {String}
@default null
*/
- itemSeparatorKey: "separator",
+ itemSeparatorKey: "isSeparator",
/**
Key used to indicate if the item is to be enabled.
@@ -101,6 +101,39 @@ SC.SelectView = SC.PopupButtonView.extend({
*/
itemIsEnabledKey: "isEnabled",
+ /**
+ Set this to non-null to place an empty option at the top of the menu.
+
+ @property
+ @type String
+ @default null
+ */
+ emptyName: null,
+
+ /**
+ If true, titles will be escaped to avoid scripting attacks.
+
+ @type Boolean
+ @default YES
+ */
+ escapeHTML: YES,
+
+ /**
+ If true, the empty name and the default title will be localized.
+
+ @type Boolean
+ @default YES
+ */
+ localize: YES,
+
+ /**
+ if true, it means that no sorting will occur, items will appear
+ in the same order as in the array
+
+ @type Boolean
+ @default YES
+ */
+ disableSort: YES,
/**
The menu that will pop up when this button is clicked.
@@ -183,6 +216,7 @@ SC.SelectView = SC.PopupButtonView.extend({
var sel = this.get('selectedItem'),
last = this._scsv_lastSelection,
titleKey = this.get('itemTitleKey') || 'title',
+ iconKey = this.get('itemIconKey') || 'icon',
valueKey = this.get('itemValueKey') || 'value';
// selected item could be a menu item from SC.MenuPane's displayItems, or it could
@@ -194,11 +228,13 @@ SC.SelectView = SC.PopupButtonView.extend({
// add/remove observers for the title and value so we can invalidate.
if (last && last.addObserver && sel !== last) {
last.removeObserver(titleKey, this, this._scsv_selectedItemPropertyDidChange);
+ last.removeObserver(iconKey, this, this._scsv_selectedItemPropertyDidChange);
last.removeObserver(valueKey, this, this._scsv_selectedItemPropertyDidChange);
}
if (sel && sel.addObserver && sel !== last) {
sel.addObserver(titleKey, this, this._scsv_selectedItemPropertyDidChange);
+ sel.addObserver(iconKey, this, this._scsv_selectedItemPropertyDidChange);
sel.addObserver(valueKey, this, this._scsv_selectedItemPropertyDidChange);
}
@@ -208,19 +244,11 @@ SC.SelectView = SC.PopupButtonView.extend({
// called when either title or value changes on the selected item
_scsv_selectedItemPropertyDidChange: function(item) {
this.notifyPropertyChange('title');
+ this.notifyPropertyChange('icon');
this.set('value', item.get(this.get('itemValueKey') || 'value'));
},
/**
- The title to show when no item is selected.
-
- @property
- @type String
- @default ""
- */
- defaultTitle: "",
-
- /**
The title of the button, derived from the selected item.
*/
title: function() {
@@ -237,28 +265,139 @@ SC.SelectView = SC.PopupButtonView.extend({
}
}.property('selectedItem').cacheable(),
+ /** @private */
+ defaultTitle: function() {
+ var emptyName = this.get('emptyName');
+ if (emptyName) {
+ emptyName = this.get('localize') ? SC.String.loc(emptyName) : emptyName;
+ emptyName = this.get('escapeHTML') ? SC.RenderContext.escapeHTML(emptyName) : emptyName;
+ }
+ return emptyName || '';
+ }.property('emptyName').cacheable(),
+
+ /**
+ The icon of the button, derived from the selected item.
+ */
+ icon: function() {
+ var sel = this.get('selectedItem');
+
+ if (!sel) {
+ return null;
+ } else if (sel.get) {
+ return sel.get(this.get('itemIconKey') || 'icon');
+ } else if (SC.typeOf(sel) == SC.T_HASH) {
+ return sel[this.get('itemIconKey') || 'icon'];
+ } else {
+ return sel;
+ }
+ }.property('selectedItem').cacheable(),
+
+ /**
+ Returns an array of normalized display items.
+
+ Adds the empty name to the items if applicable.
+
+ `displayItems` should never be set directly; instead, set `items` and
+ `displayItems` will update automatically.
+
+ @type Array
+ @returns {Array} array of display items.
+ @isReadOnly
+ */
+ displayItems: function () {
+ var items = this.get('items'),
+ emptyName = this.get('emptyName'),
+ len,
+ ret = [], idx, item, itemType;
+
+ if (!items) len = 0;
+ else len = items.get('length');
+
+ for (idx = 0; idx < len; idx++) {
+ item = items.objectAt(idx);
+
+ // fast track out if we can't do anything with this item
+ if (!item || (!ret.length && item[this.get('itemSeparatorKey')])) continue;
+
+ itemType = SC.typeOf(item);
+ if (itemType === SC.T_STRING) {
+ item = this._addDisplayItem(item, item);
+ } else if (itemType === SC.T_HASH) {
+ item = SC.Object.create(item);
+ }
+ item.contentIndex = idx;
+
+ ret.push(item);
+ }
+
+ ret = this.sortObjects(ret);
+
+ if (emptyName) {
+ if (len) ret.unshift(this._addDisplayItem(null, null, true));
+ ret.unshift(this._addDisplayItem(emptyName, null));
+ }
+
+ return ret;
+ }.property('items').cacheable(),
+
+ /** @private */
+ _addDisplayItem: function (title, value, isSeparator) {
+ var item = SC.Object.create();
+
+ item[this.get('itemTitleKey')] = title;
+ item[this.get('itemValueKey')] = value;
+ item[this.get('itemIsEnabledKey')] = true;
+ item[this.get('itemSeparatorKey')] = !!isSeparator;
+
+ return item;
+ },
+
+ /**
+
+ override this method to implement your own sorting of the menu. By
+ default, menu items are sorted using the value shown or the sortKey
+
+ @param {SC.Array} objects the unsorted array of objects to display.
+ @returns {SC.Array} sorted array of objects
+ */
+ sortObjects: function (objects) {
+ if (!this.get('disableSort')) {
+ var nameKey = this.get('itemSortKey') || this.get('itemTitleKey');
+ objects = objects.sort(function(a, b) {
+ if (nameKey) {
+ a = a.get ? a.get(nameKey) : a[nameKey];
+ b = b.get ? b.get(nameKey) : b[nameKey];
+ }
+ return (a<b) ? -1 : ((a>b) ? 1 : 0);
+ });
+ }
+ return objects;
+ },
+
/**
* When the value changes, we need to update selectedItem.
* @private
*/
_scsv_valueDidChange: function() {
- var value = this.get('value');
+ var displayItems = this.get('displayItems');
+ if (!displayItems) return;
- if (!this.get('items')) {
- return;
- }
+ var value = this.get('value'),
+ len = displayItems.get('length'),
+ idx;
- var items = this.get('items'), len = items.length, idx;
for (idx = 0; idx < len; idx++) {
- if (this._scsv_getValueForMenuItem(items[idx]) === value) {
- this.setIfChanged('selectedItem', items[idx]);
+ var item = displayItems.objectAt(idx);
+
+ if (this._scsv_getValueForMenuItem(item) === value) {
+ this.setIfChanged('selectedItem', item);
return;
}
}
// if we got here, this means no item is selected
this.setIfChanged('selectedItem', null);
- }.observes('value'),
+ }.observes('value', 'displayItems'),
/**
SelectView must set the selectView property on the menu so that the menu's
@@ -272,7 +411,9 @@ SC.SelectView = SC.PopupButtonView.extend({
var attrs = {
selectView: this,
selectedItem: this.get('selectedItem'),
- minimumMenuWidth: this.get('minimumMenuWidth')
+ minimumMenuWidth: this.get('minimumMenuWidth'),
+ escapeHTML: this.get('escapeHTML'),
+ localize: this.get('localize')
};
return klass.create(attrs);

0 comments on commit 99e1086

Please sign in to comment.