Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/html/lib/layout/list/source/FlyweightRepeater.js
blob: 01e6d80cf41addee619d46975a81311412d5d897 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
	A control that displays a repeating list of rows, suitable for displaying
	medium-sized lists (up to ~100 items). A flyweight strategy is employed to
	render one set of row controls, as needed, for as many rows as are contained
	in the repeater.

	The FlyweightRepeater's _components_ block contains the controls to be used
	for a single row. This set of controls will be rendered for each row. You
	may customize row rendering by handling the _onSetupItem_ event.

	The controls inside a FlyweightRepeater are non-interactive. This means that
	calling methods that would normally cause rendering to occur (e.g.,
	_setContent_) will not do so. However, you can force a row to render by
	calling	_renderRow(inRow)_.
	
	In addition, you can force a row to be temporarily interactive by calling
	_prepareRow(inRow)_. Call the _lockRow_ method when the	interaction is
	complete.
	
	For more information, see the documentation on
	[Lists](https://github.com/enyojs/enyo/wiki/Lists)
	in the Enyo Developer Guide.
*/
enyo.kind({
	name: "enyo.FlyweightRepeater",
	published: {
		//* Number of rows to render
		count: 0,
		//* If true, multiple selections are allowed
		multiSelect: false,
		//* If true, the selected item will toggle
		toggleSelected: false
	},
	events: {
		/**
			Fires once per row at render time, with event object: 
			_{index: <index of row>, selected: <true if row is selected>}_
		*/
		onSetupItem: ""
	},
	components: [
		{kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"},
		{name: "client"}
	],
	rowOffset: 0,
	bottomUp: false,
	//* @protected
	create: function() {
		this.inherited(arguments);
		this.multiSelectChanged();
	},
	multiSelectChanged: function() {
		this.$.selection.setMulti(this.multiSelect);
	},
	setupItem: function(inIndex) {
		this.doSetupItem({index: inIndex, selected: this.isSelected(inIndex)});
	},
	//* Renders the list.
	generateChildHtml: function() {
		var h = "";
		this.index = null;
		// note: can supply a rowOffset 
		// and indicate if rows should be rendered top down or bottomUp
		for (var i=0, r=0; i<this.count; i++) {
			r = this.rowOffset + (this.bottomUp ? this.count - i-1 : i);
			this.setupItem(r);
			this.$.client.setAttribute("index", r);
			h += this.inherited(arguments);
			this.$.client.teardownRender();
		}
		return h;
	},
	previewDomEvent: function(inEvent) {
		var i = this.index = this.rowForEvent(inEvent);
		inEvent.rowIndex = inEvent.index = i;
		inEvent.flyweight = this;
	},
	decorateEvent: function(inEventName, inEvent, inSender) {
		// decorate event with index found via dom iff event does not already contain an index.
		var i = (inEvent && inEvent.index != null) ? inEvent.index : this.index;
		if (inEvent && i != null) {
			inEvent.index = i;
			inEvent.flyweight = this;
		}
		this.inherited(arguments);
	},
	tap: function(inSender, inEvent) {
		if (this.toggleSelected) {
			this.$.selection.toggle(inEvent.index);
		} else {
			this.$.selection.select(inEvent.index);
		}
	},
	selectDeselect: function(inSender, inEvent) {
		this.renderRow(inEvent.key);
	},
	//* @public
	//* Returns the repeater's _selection_ component.
	getSelection: function() {
		return this.$.selection;
	},
	//* Gets the selection state for the given row index.
	isSelected: function(inIndex) {
		return this.getSelection().isSelected(inIndex);
	},
	//* Renders the row specified by _inIndex_.
	renderRow: function(inIndex) {
		//this.index = null;
		var node = this.fetchRowNode(inIndex);
		if (node) {
			this.setupItem(inIndex);
			node.innerHTML = this.$.client.generateChildHtml();
			this.$.client.teardownChildren();
		}
	},
	//* Fetches the DOM node for the given row index.
	fetchRowNode: function(inIndex) {
		if (this.hasNode()) {
			var n$ = this.node.querySelectorAll('[index="' + inIndex + '"]');
			return n$ && n$[0];
		}
	},
	//* Fetches the DOM node for the given event.
	rowForEvent: function(inEvent) {
		var n = inEvent.target;
		var id = this.hasNode().id;
		while (n && n.parentNode && n.id != id) {
			var i = n.getAttribute && n.getAttribute("index");
			if (i !== null) {
				return Number(i);
			}
			n = n.parentNode;
		}
		return -1;
	},
	//* Prepares the row specified by _inIndex_ such that changes made to the
	//* controls inside the repeater will be rendered for the given row.
	prepareRow: function(inIndex) {
		var n = this.fetchRowNode(inIndex);
		enyo.FlyweightRepeater.claimNode(this.$.client, n);
	},
	//* Prevents rendering of changes made to controls inside the repeater.
	lockRow: function() {
		this.$.client.teardownChildren();
	},
	//* Prepares the row specified by _inIndex_ such that changes made to the 
	//* controls in the row will be rendered in the given row; then performs the
	//* function _inFunc_, and, finally, locks the row.
	performOnRow: function(inIndex, inFunc, inContext) {
		if (inFunc) {
			this.prepareRow(inIndex);
			enyo.call(inContext || null, inFunc);
			this.lockRow();
		}
	},
	statics: {
		//* Associates a flyweight rendered control (_inControl_) with a
		//* rendering context specified by _inNode_.
		claimNode: function(inControl, inNode) {
			var n = inNode && inNode.querySelectorAll("#" + inControl.id);
			n = n && n[0];
			// FIXME: consider controls generated if we found a node or tag: null, the later so can teardown render
			inControl.generated = Boolean(n || !inControl.tag);
			inControl.node = n;
			if (inControl.node) {
				inControl.rendered();
			} else {
				//enyo.log("Failed to find node for",  inControl.id, inControl.generated);
			}
			for (var i=0, c$=inControl.children, c; c=c$[i]; i++) {
				this.claimNode(c, inNode);
			}
		}
	}
});