uki 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/VERSION +1 -1
  2. data/bin/uki +53 -2
  3. data/frameworks/uki/README.rdoc +49 -7
  4. data/frameworks/uki/spec/unit/data/model.spec.js +16 -5
  5. data/frameworks/uki/spec/unit/dom.spec.js +1 -1
  6. data/frameworks/uki/spec/unit/utils.spec.js +1 -1
  7. data/frameworks/uki/src/uki-core/background/sliced9.js +1 -1
  8. data/frameworks/uki/src/uki-core/collection.js +5 -5
  9. data/frameworks/uki/src/uki-core/dom/dnd.js +4 -0
  10. data/frameworks/uki/src/uki-core/dom/event.js +15 -8
  11. data/frameworks/uki/src/uki-core/dom/w3cdnd.js +18 -3
  12. data/frameworks/uki/src/uki-core/dom.js +1 -3
  13. data/frameworks/uki/src/uki-core/uki.js +2 -1
  14. data/frameworks/uki/src/uki-core/utils.js +2 -2
  15. data/frameworks/uki/src/uki-core/view/base.js +4 -3
  16. data/frameworks/uki/src/uki-core/view/focusable.js +61 -41
  17. data/frameworks/uki/src/uki-core/view/observable.js +3 -2
  18. data/frameworks/uki/src/uki-core/view/styleable.js +6 -6
  19. data/frameworks/uki/src/uki-data/model.js +83 -17
  20. data/frameworks/uki/src/uki-data/observable.js +8 -5
  21. data/frameworks/uki/src/uki-more/more/view/treeList/render.js +4 -3
  22. data/frameworks/uki/src/uki-more/more/view/treeList.js +18 -7
  23. data/frameworks/uki/src/uki-view/view/checkbox.js +2 -0
  24. data/frameworks/uki/src/uki-view/view/flow.js +28 -9
  25. data/frameworks/uki/src/uki-view/view/list.js +59 -16
  26. data/frameworks/uki/src/uki-view/view/radio.js +2 -1
  27. data/frameworks/uki/src/uki-view/view/scrollPane.js +5 -6
  28. data/frameworks/uki/src/uki-view/view/slider.js +4 -4
  29. data/frameworks/uki/src/uki-view/view/splitPane.js +26 -29
  30. data/frameworks/uki/src/uki-view/view/table/column.js +6 -5
  31. data/frameworks/uki/src/uki-view/view/table/header.js +28 -18
  32. data/frameworks/uki/src/uki-view/view/table.js +41 -2
  33. data/frameworks/uki/src/uki-view/view/textField.js +2 -3
  34. data/lib/uki/project.rb +55 -15
  35. data/templates/controller.js.erb +5 -0
  36. data/templates/layout.js.erb +5 -0
  37. data/templates/myapp.js.erb +1 -2
  38. data/uki.gemspec +4 -2
  39. metadata +5 -3
@@ -33,11 +33,10 @@ uki.view.Styleable = new function() {
33
33
  // };
34
34
  // });
35
35
 
36
- var probe = uki.createElement('div').style,
37
- proto = this;
38
- uki.each(['userSelect', 'MozUserSelect', 'WebkitUserSelect'], function() {
39
- if (typeof probe[this] == 'string') proto._textSelectProp = this;
40
- });
36
+ var probe = uki.createElement('div').style;
37
+ uki.each(['userSelect', 'MozUserSelect', 'WebkitUserSelect'], function(i, name) {
38
+ if (typeof probe[name] == 'string') this._textSelectProp = name;
39
+ }, this);
41
40
 
42
41
  /**
43
42
  * Sets whether text of the view can be selected.
@@ -56,7 +55,7 @@ uki.view.Styleable = new function() {
56
55
  } else {
57
56
  uki.dom[state ? 'unbind' : 'bind'](this.dom(), 'selectstart', uki.dom.preventDefaultHandler);
58
57
  }
59
- this._dom.style.cursor = state ? 'text' : 'default';
58
+ this._dom.style.cursor = state ? '' : 'default';
60
59
  return this;
61
60
  };
62
61
 
@@ -64,6 +63,7 @@ uki.view.Styleable = new function() {
64
63
  if (state === undefined) return this._dom.getAttribute('draggable');
65
64
  this._dom.setAttribute('draggable', true);
66
65
  this._dom.style.WebkitUserDrag = 'element';
66
+ return this;
67
67
  };
68
68
 
69
69
  /**#@-*/
@@ -1,28 +1,94 @@
1
1
  include('observable.js');
2
2
 
3
+ /**
4
+ *
5
+ * @example
6
+ * myModel = uki.newClass(uki.data.Model, function() {
7
+ * uki.data.model.addField(this, ['name', 'age', 'sex']);
8
+ * })
9
+ *
10
+ * var m = new myModel({ age: 22, name: 'Jonh Smith', sex: 'm' })
11
+ * m.bind('change.sex', function() {
12
+ * alert('omg! wtf!');
13
+ * });
14
+ *
15
+ * m.bind('change', function(e) {
16
+ * console.log(e.fields);
17
+ * });
18
+ *
19
+ * m.name('Joe Black') // triggers 'change' and 'change.name'
20
+ * m.sex('w') // triggers'change' and 'change.sex'
21
+ */
3
22
  uki.data.Model = uki.newClass(uki.data.Observable, function(Observable) {
4
23
 
5
- this.change = this.update = function(values, arg2) {
6
- var changes = {}, fields = [];
7
- if (arg2 !== undefined) {
8
- var tmp = {};
9
- tmp[values] = arg2;
10
- values = tmp;
11
- }
12
- uki.each(values, function(i) {
13
- if (this[i] != values[i]) {
14
- changes[i] = true
15
- fields.push(i);
16
- this[i] = values[i];
24
+ this._fields = [];
25
+
26
+ this.init = function(values) {
27
+ this.update(values || {});
28
+ };
29
+
30
+ this.fields = function() {
31
+ return this._fields;
32
+ };
33
+
34
+ this.update = function(values) {
35
+ var fields = [], changes = {};
36
+
37
+ uki.each(values, function(name, value) {
38
+ var field = uki.isFunction(this[name]) ? '_' + name : name;
39
+ if (this[field] != value) {
40
+ changes[name] = true;
41
+ fields.push(name);
42
+ this[field] = value;
43
+ this.trigger('change.' + name, { model: this });
17
44
  }
18
45
  }, this);
19
46
 
20
- this._triggerChange({changes: changes, fields: fields, model: this});
47
+ if (fields.length) this.trigger('change', {changes: changes, fields: fields, model: this});
21
48
  return this;
22
49
  };
23
50
 
24
- this._triggerChange = function(e) {
25
- this.trigger('change', e);
26
- };
51
+ });
52
+
53
+ uki.data.model = {
54
+ _newProp: function(name) {
55
+ return uki.newProp('_' + name, function(v) {
56
+ var changes = {};
57
+ changes[name] = v;
58
+ this.update(changes);
59
+ });
60
+ },
27
61
 
28
- });
62
+ addFields: function(target, names) {
63
+ for (var i=0; i < names.length; i++) {
64
+ target[names[i]] = uki.data.model._newProp(names[i]);
65
+ }
66
+ // do not break prototypes
67
+ target._fields = names.concat(target._fields || []);
68
+ },
69
+
70
+ newLoader: function(name, options) {
71
+ return function(callback) {
72
+ callback = callback || uki.F;
73
+ if (this['loaded.' + name]) {
74
+ callback.call(this, this[name]());
75
+ } else {
76
+ this.bind('load.' + name, callback);
77
+ if (!this['loading.' + name]) {
78
+ this['loading.' + name] = true;
79
+ uki.ajax(uki.extend({
80
+ url: uki.isFunction(options.url) ? options.url.call(this) : options.url,
81
+ data: uki.isFunction(options.data) ? options.data.call(this) : options.data ? options.data : { id: this.id() },
82
+ success: uki.proxy(function(val) {
83
+ this['loading.' + name] = false;
84
+ this['loaded.' + name] = true;
85
+ options.setter ? options.setter.call(this, val) : this['_' + name] = val;
86
+ this.trigger('load.' + name, val);
87
+ this.unbind('load.' + name);
88
+ }, this)
89
+ }, options.ajaxOptions || {}));
90
+ }
91
+ }
92
+ };
93
+ }
94
+ };
@@ -5,6 +5,7 @@ include('data.js');
5
5
  uki.data.Observable = {
6
6
  bind: function(name, callback) {
7
7
  var _this = this;
8
+ callback.huid = callback.huid || uki.guid++;
8
9
  uki.each(name.split(' '), function(i, name) {
9
10
  _this._observersFor(name).push(callback);
10
11
  });
@@ -13,20 +14,22 @@ uki.data.Observable = {
13
14
  unbind: function(name, callback) {
14
15
  var _this = this;
15
16
  uki.each(name.split(' '), function(i, name) {
16
- _this._observers[name] = uki.grep(_this._observersFor(name), function(c) {
17
- return c != callback;
17
+ _this._observers[name] = !callback ? [] : uki.grep(_this._observersFor(name), function(c) {
18
+ return c != callback && c.huid != callback.huid;
18
19
  });
20
+ if (_this._observers[name]) delete _this._observers[name];
19
21
  });
20
22
  },
21
23
 
22
24
  trigger: function(name/*, data1, data2*/) {
23
25
  var attrs = Array.prototype.slice.call(arguments, 1);
24
- uki.each(this._observersFor(name), function(i, callback) {
26
+ uki.each(this._observersFor(name, true), function(i, callback) {
25
27
  callback.apply(this, attrs);
26
- });
28
+ }, this);
27
29
  },
28
30
 
29
- _observersFor: function(name) {
31
+ _observersFor: function(name, skipCreate) {
32
+ if (skipCreate && (!this._observers || !this._observers[name])) return [];
30
33
  if (!this._observers) this._observers = {};
31
34
  if (!this._observers[name]) this._observers[name] = [];
32
35
  return this._observers[name];
@@ -11,7 +11,7 @@ uki.more.view.treeList.Render = uki.newClass(uki.view.list.Render, new function(
11
11
  );
12
12
 
13
13
  this.initStyles = function() {
14
- this.classPrefix = 'treeList-' + uki.dom.guid++;
14
+ this.classPrefix = 'treeList-' + uki.guid++;
15
15
  var style = new uki.theme.Template(
16
16
  '.${classPrefix}-row { color: #333; position:relative; padding-top:3px; } ' +
17
17
  '.${classPrefix}-toggle { overflow: hidden; position:absolute; left:-15px; top:5px; width: 10px; height:9px; } ' +
@@ -30,8 +30,9 @@ uki.more.view.treeList.Render = uki.newClass(uki.view.list.Render, new function(
30
30
 
31
31
  this.render = function(row, rect, i) {
32
32
  this.classPrefix || this.initStyles();
33
- var text = row.data;
34
- if (row.children && row.children.length) {
33
+ var text = row.data,
34
+ children = uki.attr(row, 'children');
35
+ if (children && children.length) {
35
36
  return this._parentTemplate.render({
36
37
  text: text,
37
38
  indent: row.__indent*18 + 22,
@@ -13,7 +13,16 @@ uki.view.declare('uki.more.view.TreeList', uki.view.List, function(Base) {
13
13
 
14
14
  this.data = uki.newProp('_treeData', function(v) {
15
15
  this._treeData = v;
16
- this.listData(this._treeNodeToListData(v));
16
+ this._data = this._treeNodeToListData(v);
17
+ var children = this.listData(), opened = false;
18
+ for (var i=children.length - 1; i >= 0 ; i--) {
19
+ if (this._data[i].__opened) {
20
+ opened = true;
21
+ this._openSubElement(i);
22
+ }
23
+ };
24
+ this.listData(this._data);
25
+ if (opened) this.trigger('open');
17
26
  });
18
27
 
19
28
  this._treeNodeToListData = function(node, indent) {
@@ -35,7 +44,7 @@ uki.view.declare('uki.more.view.TreeList', uki.view.List, function(Base) {
35
44
  }
36
45
 
37
46
  function recursiveLength (item) {
38
- var children = item.children,
47
+ var children = uki.attr(item, 'children'),
39
48
  length = children.length;
40
49
 
41
50
  for (var i=0; i < children.length; i++) {
@@ -46,8 +55,8 @@ uki.view.declare('uki.more.view.TreeList', uki.view.List, function(Base) {
46
55
 
47
56
  this._openSubElement = function(index) {
48
57
  var item = this._data[index],
49
- children = item.children;
50
-
58
+ children = uki.attr(item, 'children');
59
+
51
60
  if (!children || !children.length) return 0;
52
61
  var length = children.length;
53
62
 
@@ -80,13 +89,14 @@ uki.view.declare('uki.more.view.TreeList', uki.view.List, function(Base) {
80
89
  this.listData(this._data);
81
90
  this.selectedIndexes(indexes);
82
91
  this._lastClickIndex = clickIndex > index ? clickIndex + length : clickIndex;
92
+ this.trigger('open');
83
93
  return this;
84
94
  };
85
95
 
86
96
  this.close = function(index) {
87
97
  var item = this._data[index],
88
98
  indexes = this._selectedIndexes,
89
- children = item.children;
99
+ children = uki.attr(item, 'children');
90
100
  if (!children || !children.length || !item.__opened) return;
91
101
 
92
102
  var length = recursiveLength(item);
@@ -109,12 +119,13 @@ uki.view.declare('uki.more.view.TreeList', uki.view.List, function(Base) {
109
119
  this.listData(this._data);
110
120
  this.selectedIndexes(indexes);
111
121
  this._lastClickIndex = clickIndex > index ? clickIndex - length : clickIndex;
122
+ this.trigger('close');
112
123
  };
113
124
 
114
125
  this._mousedown = function(e) {
115
- if (e.domEvent.target.className.indexOf('toggle-tree') > -1) {
126
+ if (e.target.className.indexOf('toggle-tree') > -1) {
116
127
  var o = uki.dom.offset(this._dom),
117
- y = e.domEvent.pageY - o.y,
128
+ y = e.pageY - o.y,
118
129
  p = y / this._rowHeight << 0;
119
130
  this.toggle(p);
120
131
  } else {
@@ -23,8 +23,10 @@ uki.view.declare('uki.view.Checkbox', uki.view.Button, function(Base) {
23
23
  };
24
24
 
25
25
  this.value = this.checked = uki.newProp('_checked', function(state) {
26
+ var changed = this._checked != !!state;
26
27
  this._checked = !!state;
27
28
  this._updateBg();
29
+ if (changed) this.trigger('change', {checked: this._checked, source: this});
28
30
  });
29
31
 
30
32
  this._mouseup = function(e) {
@@ -8,23 +8,41 @@ uki.view.declare('uki.view.VFlow', uki.view.Container, function(Base) {
8
8
 
9
9
  this.hidePartlyVisible = uki.newProp('_hidePartlyVisible');
10
10
 
11
- this._layoutChildViews = function(oldRect) {
11
+ this.resizeToContents = function(autosizeStr) {
12
+ this._resizeChildViews(this._rect);
13
+ return Base.resizeToContents.call(this, autosizeStr);
14
+ }
15
+
16
+ uki.each(['appendChild', 'removeChild', 'insertBefore'], function(i, name) {
17
+ this[name] = function(arg1, arg2) {
18
+ this._contentChanged = true;
19
+ return Base[name].call(this, arg1, arg2);
20
+ };
21
+ }, this)
22
+
23
+ this.layout = function() {
24
+ if (this._contentChanged) this._resizeChildViews(this._rect);
25
+ return Base.layout.call(this);
26
+ };
27
+
28
+ // resize in layout
29
+ this._resizeChildViews = function(oldRect) {
30
+ this._contentChanged = false;
12
31
  var offset = 0, rect, view;
13
32
  for (var i=0, childViews = this.childViews(); i < childViews.length; i++) {
14
33
  view = childViews[i];
15
- view.rect(new Rect(view._rect.x, offset, view._rect.width, view._rect.height));
34
+ view.parentResized(oldRect, this._rect);
35
+ view.rect().y = offset;
36
+ // view.rect(new Rect(view._rect.x, offset, view._rect.width, view._rect.height));
16
37
  if (this._hidePartlyVisible) {
17
38
  view.visible(view._rect.height + offset <= this._rect.height);
18
39
  }
19
40
  if (view.visible()) offset += view._rect.height;
20
41
  };
21
- Base._layoutChildViews.call(this);
22
42
  };
23
43
  });
24
44
 
25
- uki.view.declare('uki.view.HFlow', uki.view.Container, function(Base) {
26
- this.hidePartlyVisible = uki.newProp('_hidePartlyVisible');
27
-
45
+ uki.view.declare('uki.view.HFlow', uki.view.VFlow, function(Base) {
28
46
  this.contentsSize = function() {
29
47
  var value = uki.reduce(0, this._childViews, function(sum, e) {
30
48
  return sum + (e.visible() ? e.rect().width : 0);
@@ -32,17 +50,18 @@ uki.view.declare('uki.view.HFlow', uki.view.Container, function(Base) {
32
50
  return new Size( value, this.contentsHeight() );
33
51
  };
34
52
 
35
- this._layoutChildViews = function(oldRect) {
53
+ this._resizeChildViews = function(oldRect) {
36
54
  var offset = 0, rect, view;
37
55
  for (var i=0, childViews = this.childViews(); i < childViews.length; i++) {
38
56
  view = childViews[i];
39
- view.rect(new Rect(offset, view._rect.y, view._rect.width, view._rect.height));
57
+ view.parentResized(oldRect, this._rect);
58
+ view.rect().x = offset;
59
+ // view.rect(new Rect(offset, view._rect.y, view._rect.width, view._rect.height));
40
60
  if (this._hidePartlyVisible) {
41
61
  view.visible(view._rect.width + offset <= this._rect.width);
42
62
  }
43
63
  if (view.visible()) offset += view._rect.width;
44
64
  };
45
- Base._layoutChildViews.call(this);
46
65
  };
47
66
 
48
67
  });
@@ -3,7 +3,7 @@ include('scrollPane.js');
3
3
  uki.view.list = {};
4
4
  uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Base, Focusable) {
5
5
 
6
- this._throttle = 5; // do not try to render more often than every 5ms
6
+ this._throttle = 42; // do not try to render more often than every 42ms
7
7
  this._visibleRectExt = 300; // extend visible rect by 300 px overflow
8
8
  this._defaultBackground = 'theme(list)';
9
9
 
@@ -22,13 +22,15 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
22
22
  return uki.theme.background('list', this._rowHeight);
23
23
  };
24
24
 
25
- uki.addProps(this, ['render', 'packSize', 'visibleRectExt', 'throttle', 'contentDraggable', 'lastClickIndex', 'multiselect']);
25
+ uki.addProps(this, ['render', 'packSize', 'visibleRectExt', 'throttle', 'contentDraggable', 'lastClickIndex', 'multiselect', 'lastClickIndex']);
26
26
 
27
27
  this.rowHeight = uki.newProp('_rowHeight', function(val) {
28
28
  this._rowHeight = val;
29
+ this.minSize(new Size(this.minSize().width, this._rowHeight * this._data.length));
29
30
  if (this._background) this._background.detach();
30
31
  this._background = null;
31
32
  if (this.background()) this.background().attachTo(this);
33
+ this._relayoutParent();
32
34
  });
33
35
 
34
36
  this.data = function(d) {
@@ -37,8 +39,8 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
37
39
  this._data = d;
38
40
  this._packs[0].itemFrom = this._packs[0].itemTo = this._packs[1].itemFrom = this._packs[1].itemTo = 0;
39
41
 
40
- var minWidth = this._minSize ? this._minSize.width : 0;
41
- this.minSize(new Size(minWidth, this._rowHeight * this._data.length));
42
+ this.minSize(new Size(this.minSize().width, this._rowHeight * this._data.length));
43
+ this.trigger('selection', {source: this})
42
44
  this._relayoutParent();
43
45
  return this;
44
46
  };
@@ -48,16 +50,51 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
48
50
  this.layout();
49
51
  };
50
52
 
53
+ this.contentsSize = function() {
54
+ return new Size(this.rect().width, this._rowHeight * this._data.length);
55
+ };
56
+
57
+ // used in search. should be fast
51
58
  this.addRow = function(position, data) {
52
- this.clearSelection();
53
59
  this._data.splice(position, 0, data);
54
- this.data(this._data);
60
+ var item = this._itemAt(position);
61
+ if (item) {
62
+ var container = doc.createElement('div');
63
+
64
+ container.innerHTML = this._rowTemplate.render({
65
+ height: this._rowHeight,
66
+ text: this._render.render(this._data[position], this._rowRect(position), position)
67
+ });
68
+ item.parentNode.insertBefore(container.firstChild, item);
69
+ if (position < this._packs[0].itemTo) {
70
+ this._packs[0].itemTo++;
71
+ this._packs[1].itemFrom++;
72
+ this._packs[1].itemTo++;
73
+ this._packs[1].dom.style.top = this._packs[1].itemFrom*this._rowHeight + 'px';
74
+ } else {
75
+ this._packs[1].itemTo++;
76
+ }
77
+ }
78
+
79
+ // offset selection
80
+ var selectionPosition = uki.binarySearch(position, this.selectedIndexes());
81
+ for (var i = selectionPosition; i < this._selectedIndexes.length; i++) {
82
+ this._selectedIndexes[i]++;
83
+ };
84
+
85
+ return this;
55
86
  };
56
87
 
57
88
  this.removeRow = function(position, data) {
58
- this.clearSelection();
59
89
  this._data.splice(position, 1);
60
90
  this.data(this._data);
91
+ return this;
92
+ };
93
+
94
+ this.redrawRow = function(row) {
95
+ var item = this._itemAt(row);
96
+ if (item) item.innerHTML = this._render.render(this._data[row], this._rowRect(row), row);
97
+ return this;
61
98
  };
62
99
 
63
100
  this.selectedIndex = function(position) {
@@ -69,12 +106,13 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
69
106
 
70
107
  this.selectedIndexes = function(indexes) {
71
108
  if (indexes === undefined) return this._selectedIndexes;
109
+ var changed = indexes != this._selectedIndexes;
72
110
  this.clearSelection(true);
73
111
  this._selectedIndexes = indexes;
74
112
  for (var i=0; i < this._selectedIndexes.length; i++) {
75
113
  this._setSelected(this._selectedIndexes[i], true);
76
114
  };
77
- this.trigger('selection', {source: this});
115
+ if (changed) this.trigger('selection', {source: this});
78
116
  return this;
79
117
  };
80
118
 
@@ -120,6 +158,10 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
120
158
  if (p > initialP) array.splice(initialP, p - initialP);
121
159
  }
122
160
 
161
+ this._rowRect = function(p) {
162
+ return new Rect(0, p*this._rowHeight, this.rect().width, this._rowHeight);
163
+ };
164
+
123
165
  this._toggleSelection = function(p) {
124
166
  var indexes = [].concat(this._selectedIndexes);
125
167
  var addTo = uki.binarySearch(p, indexes);
@@ -222,6 +264,9 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
222
264
  } else if (e.which == 40 || e.keyCode == 40) { // DOWN
223
265
  nextIndex = Math.min(this._data.length-1, this._lastClickIndex + 1);
224
266
  e.preventDefault();
267
+ } else if (this._multiselect && (e.which == 97 || e.which == 65) && e.metaKey) {
268
+ e.preventDefault();
269
+ this.selectedIndexes(range(0, this._data.length -1));
225
270
  }
226
271
  if (nextIndex > -1 && nextIndex != this._lastClickIndex) {
227
272
  if (e.shiftKey && this._multiselect) {
@@ -230,6 +275,7 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
230
275
  } else {
231
276
  this._toggleSelection(nextIndex);
232
277
  }
278
+ this._scrollToPosition(nextIndex);
233
279
  } else {
234
280
  this.selectedIndex(nextIndex);
235
281
  }
@@ -292,14 +338,12 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
292
338
  this._rowTemplate = new uki.theme.Template('<div style="width:100%;height:${height}px;overflow:hidden;">${text}</div>')
293
339
 
294
340
  this._renderPack = function(pack, itemFrom, itemTo) {
295
- var html = [], position,
296
- rect = new Rect(0, itemFrom*this._rowHeight, this.rect().width, this._rowHeight);
341
+ var html = [], position;
297
342
  for (i=itemFrom; i < itemTo; i++) {
298
343
  html[html.length] = this._rowTemplate.render({
299
344
  height: this._rowHeight,
300
- text: this._render.render(this._data[i], rect, i)
345
+ text: this._render.render(this._data[i], this._rowRect(i), i)
301
346
  });
302
- rect.y += this._rowHeight;
303
347
  };
304
348
  pack.dom.innerHTML = html.join('');
305
349
  pack.itemFrom = itemFrom;
@@ -344,7 +388,7 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
344
388
  scrollableParent = this._scrollableParent;
345
389
 
346
390
  this._visibleRect = uki.view.visibleRect(this, scrollableParent);
347
-
391
+ if (this._focusTarget) this._focusTarget.style.top = this._visibleRect.y + 'px';
348
392
  var prefferedPackSize = CEIL((this._visibleRect.height + this._visibleRectExt*2) / this._rowHeight),
349
393
 
350
394
  minVisibleY = MAX(0, this._visibleRect.y - this._visibleRectExt),
@@ -355,7 +399,6 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
355
399
  itemFrom, itemTo, startAt, updated = true;
356
400
 
357
401
  Base._layoutDom.call(this, rect);
358
-
359
402
  if (
360
403
  maxVisibleY <= minRenderedY || minVisibleY >= maxRenderedY || // both packs below/above visible area
361
404
  (maxVisibleY > maxRenderedY && this._packs[1].itemFrom * this._rowHeight > this._visibleRect.y && this._packs[1].itemTo > this._packs[1].itemFrom) || // need to render below, and pack 2 is not enough to cover
@@ -396,7 +439,7 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
396
439
  } else {
397
440
  updated = false;
398
441
  }
399
- if (updated && /MSIE 7/.test(ua)) this.dom().className += '';
442
+ if (updated && /MSIE 6|7/.test(ua)) this.dom().className += '';
400
443
  };
401
444
 
402
445
  this._bindToDom = function(name) {
@@ -419,7 +462,7 @@ uki.view.declare('uki.view.List', uki.view.Base, uki.view.Focusable, function(Ba
419
462
 
420
463
  });
421
464
 
422
- uki.Collection.addAttrs(['data','selectedIndex']);
465
+ uki.Collection.addAttrs(['data','selectedIndex', 'selectedIndexes', 'selectedRows']);
423
466
 
424
467
  uki.view.declare('uki.view.ScrollableList', uki.view.ScrollPane, function(Base) {
425
468
 
@@ -13,9 +13,11 @@ include('checkbox.js');
13
13
  });
14
14
 
15
15
  this.value = this.checked = uki.newProp('_checked', function(state) {
16
+ var changed = this._checked != !!state;
16
17
  this._checked = !!state;
17
18
  if (state) manager.clearGroup(this);
18
19
  this._updateBg();
20
+ if (changed) this.trigger('change', {checked: this._checked, source: this});
19
21
  });
20
22
 
21
23
  this._mouseup = function() {
@@ -23,7 +25,6 @@ include('checkbox.js');
23
25
  this._down = false;
24
26
  if (!this._checked && !this._disabled) {
25
27
  this.checked(!this._checked);
26
- this.trigger('change', {checked: this._checked, source: this});
27
28
  }
28
29
  }
29
30
  });
@@ -52,12 +52,6 @@
52
52
  if (dy) this.scrollTop(this.scrollTop() + dy);
53
53
  };
54
54
 
55
- uki.each(['appendChild', 'removeChild', 'insertBefore'], function(i, name) {
56
- this[name] = function(arg1, arg2) {
57
- return Base[name].call(this, arg1, arg2);
58
- };
59
- }, this);
60
-
61
55
  uki.each(['scrollTop', 'scrollLeft'], function(i, name) {
62
56
  this[name] = function(v) {
63
57
  if (v == undefined) return this._dom[name];
@@ -86,6 +80,11 @@
86
80
  this.trigger('resize', {oldRect: oldRect, newRect: this._rect, source: this});
87
81
  return this;
88
82
  };
83
+
84
+ this._createDom = function() {
85
+ Base._createDom.call(this);
86
+ if (ua.indexOf('Gecko/') > -1) this._dom.tabIndex = '-1';
87
+ };
89
88
 
90
89
  this._recalcClientRects = function() {
91
90
  initScrollWidth();
@@ -71,7 +71,7 @@ uki.view.declare('uki.view.Slider', uki.view.Base, uki.view.Focusable, function(
71
71
  }, this);
72
72
 
73
73
  this.bind('click', this._click);
74
- this.bind('keydown', this._keydown);
74
+ this.bind(uki.view.List.prototype.keyPressEvent(), this._keypress);
75
75
  };
76
76
 
77
77
  this._mouseenter = function() {
@@ -90,10 +90,10 @@ uki.view.declare('uki.view.Slider', uki.view.Base, uki.view.Focusable, function(
90
90
  this._cachedIndex = undefined;
91
91
  };
92
92
 
93
- this._keydown = function(e) {
94
- if (e.which == 39) {
93
+ this._keypress = function(e) {
94
+ if (e.which == 39 || e.keyCode == 39) {
95
95
  this.value(this.value() + this._keyStep * (this._max - this._min));
96
- } else if (e.which == 37) {
96
+ } else if (e.which == 37 || e.keyCode == 37) {
97
97
  this.value(this.value() - this._keyStep * (this._max - this._min));
98
98
  }
99
99
  };