Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/html/lib/layout/list
diff options
context:
space:
mode:
Diffstat (limited to 'html/lib/layout/list')
-rw-r--r--html/lib/layout/list/package.js3
-rw-r--r--html/lib/layout/list/source/AlphaJumpList.js37
-rw-r--r--html/lib/layout/list/source/AlphaJumper.css36
-rw-r--r--html/lib/layout/list/source/AlphaJumper.js70
-rw-r--r--html/lib/layout/list/source/FlyweightRepeater.js175
-rw-r--r--html/lib/layout/list/source/List.css15
-rw-r--r--html/lib/layout/list/source/List.js368
-rw-r--r--html/lib/layout/list/source/PulldownList.css60
-rw-r--r--html/lib/layout/list/source/PulldownList.js180
-rw-r--r--html/lib/layout/list/source/Selection.js141
-rw-r--r--html/lib/layout/list/source/package.js7
-rw-r--r--html/lib/layout/list/source/wip-package.js6
12 files changed, 1098 insertions, 0 deletions
diff --git a/html/lib/layout/list/package.js b/html/lib/layout/list/package.js
new file mode 100644
index 0000000..f1de4f2
--- /dev/null
+++ b/html/lib/layout/list/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "source/"
+); \ No newline at end of file
diff --git a/html/lib/layout/list/source/AlphaJumpList.js b/html/lib/layout/list/source/AlphaJumpList.js
new file mode 100644
index 0000000..6b6e326
--- /dev/null
+++ b/html/lib/layout/list/source/AlphaJumpList.js
@@ -0,0 +1,37 @@
+/**
+ A control that presents an alphabetic panel that you can select from, in
+ order to perform actions based on the item selected.
+
+ {kind: "AlphaJumpList", onSetupItem: "setupItem",
+ onAlphaJump: "alphaJump",
+ components: [
+ {name: "divider"},
+ {kind: "onyx.Item"}
+ ]
+ }
+
+*/
+enyo.kind({
+ name: "enyo.AlphaJumpList",
+ kind: "List",
+ //* @protected
+ scrollTools: [
+ {name: "jumper", kind: "AlphaJumper"}
+ ],
+ initComponents: function() {
+ this.createChrome(this.scrollTools);
+ this.inherited(arguments);
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.centerJumper();
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.centerJumper();
+ },
+ centerJumper: function() {
+ var b = this.getBounds(), sb = this.$.jumper.getBounds();
+ this.$.jumper.applyStyle("top", ((b.height - sb.height) / 2) + "px");
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/AlphaJumper.css b/html/lib/layout/list/source/AlphaJumper.css
new file mode 100644
index 0000000..df1265c
--- /dev/null
+++ b/html/lib/layout/list/source/AlphaJumper.css
@@ -0,0 +1,36 @@
+.enyo-alpha-jumper {
+ text-transform: uppercase;
+ font-size: 11px;
+ xborder-radius: 12px;
+ position: absolute;
+ xbackground: white;
+ text-align: center;
+ right: 10px;
+ z-index: 1;
+ color: #333;
+ padding: 0 14px;
+}
+
+.enyo-alpha-jumper > *:first-child {
+ padding-top: 4px;
+ border-radius: 12px 12px 0 0;
+ border-top: 1px solid #333;
+}
+
+.enyo-alpha-jumper > *:last-child {
+ padding-bottom: 4px;
+ border-radius: 0 0 12px 12px;
+ border-bottom: 1px solid #333;
+}
+
+.enyo-alpha-jumper > * {
+ background: #eee;
+ padding: 1px 4px;
+ border-right: 1px solid #333;
+ border-left: 1px solid #333;
+}
+
+.enyo-alpha-jumper > .active {
+ background: #333;
+ color: white;
+} \ No newline at end of file
diff --git a/html/lib/layout/list/source/AlphaJumper.js b/html/lib/layout/list/source/AlphaJumper.js
new file mode 100644
index 0000000..2f85d00
--- /dev/null
+++ b/html/lib/layout/list/source/AlphaJumper.js
@@ -0,0 +1,70 @@
+enyo.kind({
+ name: "enyo.AlphaJumper",
+ classes: "enyo-alpha-jumper enyo-border-box",
+ published: {
+ marker: null
+ },
+ events: {
+ onAlphaJump: ""
+ },
+ handlers: {
+ ondown: "down",
+ onmove: "move",
+ onup: "up"
+ },
+ initComponents: function() {
+ for (var s="A".charCodeAt(0), i=s; i<s+26; i++) {
+ this.createComponent({content: String.fromCharCode(i)});
+ }
+ this.inherited(arguments);
+ },
+ down: function(inSender, inEvent) {
+ if (this.tracking) {
+ enyo.dispatcher.release();
+ }
+ this.tracking = true;
+ if (this.hasNode()) {
+ var b = this.node.getBoundingClientRect();
+ // IE8 does not return width
+ var w = (b.width === undefined) ? (b.right - b.left) : b.width;
+ this.x = b.left + w/2;
+ }
+ enyo.dispatcher.capture(this);
+ this.track(inEvent);
+ },
+ move: function(inSender, inEvent) {
+ if (this.tracking) {
+ this.track(inEvent);
+ }
+ },
+ up: function() {
+ if (this.tracking) {
+ enyo.dispatcher.release();
+ this.setMarker(null);
+ this.tracking = false;
+ }
+ },
+ track: function(inEvent) {
+ var n = document.elementFromPoint(this.x, inEvent.pageY);
+ var c = this.nodeToControl(n);
+ if (c) {
+ this.setMarker(c);
+ }
+ },
+ nodeToControl: function(inNode) {
+ for (var i=0, c$=this.controls, c; c=c$[i]; i++) {
+ if (inNode == c.hasNode()) {
+ return c;
+ }
+ }
+ },
+ markerChanged: function(inLast) {
+ if (inLast) {
+ inLast.removeClass("active");
+ }
+ if (this.marker) {
+ this.marker.addClass("active");
+ this.doAlphaJump({letter: this.marker.getContent(), index: this.marker.indexInContainer()});
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/FlyweightRepeater.js b/html/lib/layout/list/source/FlyweightRepeater.js
new file mode 100644
index 0000000..01e6d80
--- /dev/null
+++ b/html/lib/layout/list/source/FlyweightRepeater.js
@@ -0,0 +1,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);
+ }
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/List.css b/html/lib/layout/list/source/List.css
new file mode 100644
index 0000000..72da968
--- /dev/null
+++ b/html/lib/layout/list/source/List.css
@@ -0,0 +1,15 @@
+.enyo-list {
+ position: relative;
+}
+
+.enyo-list-port {
+ overflow: hidden;
+ position: relative;
+ height: 10000000px;
+}
+
+.enyo-list-page {
+ position: absolute;
+ left: 0;
+ right: 0;
+} \ No newline at end of file
diff --git a/html/lib/layout/list/source/List.js b/html/lib/layout/list/source/List.js
new file mode 100644
index 0000000..c4252b9
--- /dev/null
+++ b/html/lib/layout/list/source/List.js
@@ -0,0 +1,368 @@
+/**
+ A control that displays a scrolling list of rows, suitable for displaying
+ very large lists. _enyo.List_ is optimized such that only a small portion of
+ the list is rendered at a given time. A flyweight pattern is employed, in
+ which controls placed inside the list are created once, but rendered for
+ each list item. For this reason, it's best to use only simple controls in
+ a List, such as <a href="#enyo.Control">enyo.Control</a> and
+ <a href="#enyo.Image">enyo.Image</a>.
+
+ A List'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.
+
+ Events fired from within list rows contain the _index_ property, which may
+ be used to identify the row from which the event originated.
+
+ The controls inside a List 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.List",
+ kind: "Scroller",
+ classes: "enyo-list",
+ published: {
+ /**
+ The number of rows contained in the list. Note that as the amount of
+ list data changes, _setRows_ can be called to adjust the number of
+ rows. To re-render the list at the current position when the count
+ has changed, call the _refresh_ method.
+ */
+ count: 0,
+ /**
+ The number of rows to be shown on a given list page segment.
+ There is generally no need to adjust this value.
+ */
+ rowsPerPage: 50,
+ /**
+ If true, renders the list such that row 0 is at the bottom of the
+ viewport and the beginning position of the list is scrolled to the
+ bottom
+ */
+ bottomUp: false,
+ //* If true, multiple selections are allowed
+ multiSelect: false,
+ //* If true, the selected item will toggle
+ toggleSelected: false,
+ //* If true, the list will assume all rows have the same height for optimization
+ fixedHeight: false
+ },
+ events: {
+ /**
+ Fires once per row at render time, with event object:
+ _{index: <index of row>}_
+ */
+ onSetupItem: ""
+ },
+ handlers: {
+ onAnimateFinish: "animateFinish"
+ },
+ //* @protected
+ rowHeight: 0,
+ listTools: [
+ {name: "port", classes: "enyo-list-port enyo-border-box", components: [
+ {name: "generator", kind: "FlyweightRepeater", canGenerate: false, components: [
+ {tag: null, name: "client"}
+ ]},
+ {name: "page0", allowHtml: true, classes: "enyo-list-page"},
+ {name: "page1", allowHtml: true, classes: "enyo-list-page"}
+ ]}
+ ],
+ create: function() {
+ this.pageHeights = [];
+ this.inherited(arguments);
+ this.getStrategy().translateOptimized = true;
+ this.bottomUpChanged();
+ this.multiSelectChanged();
+ this.toggleSelectedChanged();
+ },
+ createStrategy: function() {
+ this.controlParentName = "strategy";
+ this.inherited(arguments);
+ this.createChrome(this.listTools);
+ this.controlParentName = "client";
+ this.discoverControlParent();
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.$.generator.node = this.$.port.hasNode();
+ this.$.generator.generated = true;
+ this.reset();
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.refresh();
+ },
+ bottomUpChanged: function() {
+ this.$.generator.bottomUp = this.bottomUp;
+ this.$.page0.applyStyle(this.pageBound, null);
+ this.$.page1.applyStyle(this.pageBound, null);
+ this.pageBound = this.bottomUp ? "bottom" : "top";
+ if (this.hasNode()) {
+ this.reset();
+ }
+ },
+ multiSelectChanged: function() {
+ this.$.generator.setMultiSelect(this.multiSelect);
+ },
+ toggleSelectedChanged: function() {
+ this.$.generator.setToggleSelected(this.toggleSelected);
+ },
+ countChanged: function() {
+ if (this.hasNode()) {
+ this.updateMetrics();
+ }
+ },
+ updateMetrics: function() {
+ this.defaultPageHeight = this.rowsPerPage * (this.rowHeight || 100);
+ this.pageCount = Math.ceil(this.count / this.rowsPerPage);
+ this.portSize = 0;
+ for (var i=0; i < this.pageCount; i++) {
+ this.portSize += this.getPageHeight(i);
+ }
+ this.adjustPortSize();
+ },
+ generatePage: function(inPageNo, inTarget) {
+ this.page = inPageNo;
+ var r = this.$.generator.rowOffset = this.rowsPerPage * this.page;
+ var rpp = this.$.generator.count = Math.min(this.count - r, this.rowsPerPage);
+ var html = this.$.generator.generateChildHtml();
+ inTarget.setContent(html);
+ var pageHeight = inTarget.getBounds().height;
+ // if rowHeight is not set, use the height from the first generated page
+ if (!this.rowHeight && pageHeight > 0) {
+ this.rowHeight = Math.floor(pageHeight / rpp);
+ this.updateMetrics();
+ }
+ // update known page heights
+ if (!this.fixedHeight) {
+ var h0 = this.getPageHeight(inPageNo);
+ if (h0 != pageHeight && pageHeight > 0) {
+ this.pageHeights[inPageNo] = pageHeight;
+ this.portSize += pageHeight - h0;
+ }
+ }
+ },
+ update: function(inScrollTop) {
+ var updated = false;
+ // get page info for position
+ var pi = this.positionToPageInfo(inScrollTop);
+ // zone line position
+ var pos = pi.pos + this.scrollerHeight/2;
+ // leap-frog zone position
+ var k = Math.floor(pos/Math.max(pi.height, this.scrollerHeight) + 1/2) + pi.no;
+ // which page number for page0 (even number pages)?
+ var p = k % 2 == 0 ? k : k-1;
+ if (this.p0 != p && this.isPageInRange(p)) {
+ //this.log("update page0", p);
+ this.generatePage(p, this.$.page0);
+ this.positionPage(p, this.$.page0);
+ this.p0 = p;
+ updated = true;
+ }
+ // which page number for page1 (odd number pages)?
+ p = k % 2 == 0 ? Math.max(1, k-1) : k;
+ // position data page 1
+ if (this.p1 != p && this.isPageInRange(p)) {
+ //this.log("update page1", p);
+ this.generatePage(p, this.$.page1);
+ this.positionPage(p, this.$.page1);
+ this.p1 = p;
+ updated = true;
+ }
+ if (updated && !this.fixedHeight) {
+ this.adjustBottomPage();
+ this.adjustPortSize();
+ }
+ },
+ updateForPosition: function(inPos) {
+ this.update(this.calcPos(inPos));
+ },
+ calcPos: function(inPos) {
+ return (this.bottomUp ? (this.portSize - this.scrollerHeight - inPos) : inPos);
+ },
+ adjustBottomPage: function() {
+ var bp = this.p0 >= this.p1 ? this.$.page0 : this.$.page1;
+ this.positionPage(bp.pageNo, bp);
+ },
+ adjustPortSize: function() {
+ this.scrollerHeight = this.getBounds().height;
+ var s = Math.max(this.scrollerHeight, this.portSize);
+ this.$.port.applyStyle("height", s + "px");
+ },
+ positionPage: function(inPage, inTarget) {
+ inTarget.pageNo = inPage;
+ var y = this.pageToPosition(inPage);
+ inTarget.applyStyle(this.pageBound, y + "px");
+ },
+ pageToPosition: function(inPage) {
+ var y = 0;
+ var p = inPage;
+ while (p > 0) {
+ p--;
+ y += this.getPageHeight(p);
+ }
+ return y;
+ },
+ positionToPageInfo: function(inY) {
+ var page = -1;
+ var p = this.calcPos(inY);
+ var h = this.defaultPageHeight;
+ while (p >= 0) {
+ page++;
+ h = this.getPageHeight(page);
+ p -= h;
+ }
+ //page = Math.min(page, this.pageCount-1);
+ return {no: page, height: h, pos: p+h};
+ },
+ isPageInRange: function(inPage) {
+ return inPage == Math.max(0, Math.min(this.pageCount-1, inPage));
+ },
+ getPageHeight: function(inPageNo) {
+ return this.pageHeights[inPageNo] || this.defaultPageHeight;
+ },
+ invalidatePages: function() {
+ this.p0 = this.p1 = null;
+ // clear the html in our render targets
+ this.$.page0.setContent("");
+ this.$.page1.setContent("");
+ },
+ invalidateMetrics: function() {
+ this.pageHeights = [];
+ this.rowHeight = 0;
+ this.updateMetrics();
+ },
+ scroll: function(inSender, inEvent) {
+ var r = this.inherited(arguments);
+ this.update(this.getScrollTop());
+ return r;
+ },
+ //* @public
+ scrollToBottom: function() {
+ this.update(this.getScrollBounds().maxTop);
+ this.inherited(arguments);
+ },
+ setScrollTop: function(inScrollTop) {
+ this.update(inScrollTop);
+ this.inherited(arguments);
+ this.twiddle();
+ },
+ getScrollPosition: function() {
+ return this.calcPos(this.getScrollTop());
+ },
+ setScrollPosition: function(inPos) {
+ this.setScrollTop(this.calcPos(inPos));
+ },
+ //* Scrolls to a specific row.
+ scrollToRow: function(inRow) {
+ var page = Math.floor(inRow / this.rowsPerPage);
+ var pageRow = inRow % this.rowsPerPage;
+ var h = this.pageToPosition(page);
+ // update the page
+ this.updateForPosition(h);
+ // call pageToPosition again and this time should return the right pos since the page info is populated
+ h = this.pageToPosition(page);
+ this.setScrollPosition(h);
+ if (page == this.p0 || page == this.p1) {
+ var rowNode = this.$.generator.fetchRowNode(inRow);
+ if (rowNode) {
+ // calc row offset
+ var offset = rowNode.offsetTop;
+ if (this.bottomUp) {
+ offset = this.getPageHeight(page) - rowNode.offsetHeight - offset;
+ }
+ var y = this.getScrollPosition() + offset;
+ this.setScrollPosition(y);
+ }
+ }
+ },
+ //* Scrolls to the beginning of the list.
+ scrollToStart: function() {
+ this[this.bottomUp ? "scrollToBottom" : "scrollToTop"]();
+ },
+ //* Scrolls to the end of the list.
+ scrollToEnd: function() {
+ this[this.bottomUp ? "scrollToTop" : "scrollToBottom"]();
+ },
+ //* Re-renders the list at the current position.
+ refresh: function() {
+ this.invalidatePages();
+ this.update(this.getScrollTop());
+ this.stabilize();
+
+ //FIXME: Necessary evil for Android 4.0.4 refresh bug
+ if (enyo.platform.android === 4) {
+ this.twiddle();
+ }
+ },
+ //* Re-renders the list from the beginning.
+ reset: function() {
+ this.getSelection().clear();
+ this.invalidateMetrics();
+ this.invalidatePages();
+ this.stabilize();
+ this.scrollToStart();
+ },
+ /**
+ Returns the _selection_ component that manages the selection state for
+ this list.
+ */
+ getSelection: function() {
+ return this.$.generator.getSelection();
+ },
+ //* Sets the selection state for the given row index.
+ select: function(inIndex, inData) {
+ return this.getSelection().select(inIndex, inData);
+ },
+ //* Gets the selection state for the given row index.
+ isSelected: function(inIndex) {
+ return this.$.generator.isSelected(inIndex);
+ },
+ /**
+ Re-renders the specified row. Call after making modifications to a row,
+ to force it to render.
+ */
+ renderRow: function(inIndex) {
+ this.$.generator.renderRow(inIndex);
+ },
+ //* Prepares the row to become interactive.
+ prepareRow: function(inIndex) {
+ this.$.generator.prepareRow(inIndex);
+ },
+ //* Restores the row to being non-interactive.
+ lockRow: function() {
+ this.$.generator.lockRow();
+ },
+ /**
+ Performs a set of tasks by running the function _inFunc_ on a row (which
+ must be interactive at the time the tasks are performed). Locks the row
+ when done.
+ */
+ performOnRow: function(inIndex, inFunc, inContext) {
+ this.$.generator.performOnRow(inIndex, inFunc, inContext);
+ },
+ //* @protected
+ animateFinish: function(inSender) {
+ this.twiddle();
+ return true;
+ },
+ // FIXME: Android 4.04 has issues with nested composited elements; for example, a SwipeableItem,
+ // can incorrectly generate taps on its content when it has slid off the screen;
+ // we address this BUG here by forcing the Scroller to "twiddle" which corrects the bug by
+ // provoking a dom update.
+ twiddle: function() {
+ var s = this.getStrategy();
+ enyo.call(s, "twiddle");
+ }
+});
diff --git a/html/lib/layout/list/source/PulldownList.css b/html/lib/layout/list/source/PulldownList.css
new file mode 100644
index 0000000..89f6da0
--- /dev/null
+++ b/html/lib/layout/list/source/PulldownList.css
@@ -0,0 +1,60 @@
+.enyo-list-pulldown {
+ position: absolute;
+ bottom: 100%;
+ left: 0;
+ right: 0;
+}
+
+.enyo-puller {
+ position: relative;
+ height: 50px;
+ font-size: 22px;
+ color: #444;
+ padding: 20px 0 0px 34px;
+}
+
+.enyo-puller-text {
+ position: absolute;
+ left: 80px;
+ top: 22px;
+}
+
+.enyo-puller-arrow {
+ position: relative;
+ background: #444;
+ width: 7px;
+ height: 28px;
+ transition: transform 0.3s;
+ -webkit-transition: -webkit-transform 0.3s;
+ -moz-transition: -moz-transform 0.3s;
+ -o-transition: -o-transform 0.3s;
+ -ms-transition: -ms-transform 0.3s;
+}
+
+.enyo-puller-arrow:after {
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ border: 10px solid transparent;
+ border-bottom-color: #444;
+ bottom: 100%;
+ left: 50%;
+ margin-left: -10px;
+}
+
+.enyo-puller-arrow-up {
+ transform: rotate(0deg);
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+}
+
+.enyo-puller-arrow-down {
+ transform: rotate(180deg);
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+} \ No newline at end of file
diff --git a/html/lib/layout/list/source/PulldownList.js b/html/lib/layout/list/source/PulldownList.js
new file mode 100644
index 0000000..8d0dab1
--- /dev/null
+++ b/html/lib/layout/list/source/PulldownList.js
@@ -0,0 +1,180 @@
+/**
+A list that provides a pull-to-refresh feature, which allows new data to be
+retrieved and updated in the list.
+
+PulldownList provides the onPullRelease event to allow an application to start
+retrieving new data. The onPullComplete event indicates that the pull is
+complete and it's time to update the list with the new data.
+
+ {name: "list", kind: "PulldownList", onSetupItem: "setupItem",
+ onPullRelease: "pullRelease", onPullComplete: "pullComplete",
+ components: [
+ {name: "item"}
+ ]}
+
+ pullRelease: function() {
+ this.search();
+ },
+ processSearchResults: function(inRequest, inResponse) {
+ this.results = inResponse.results;
+ this.$.list.setCount(this.results.length);
+ this.$.list.completePull();
+ },
+ pullComplete: function() {
+ this.$.list.reset();
+ }
+*/
+enyo.kind({
+ name: "enyo.PulldownList",
+ kind: "List",
+ touch: true,
+ pully: null,
+ pulldownTools: [
+ {name: "pulldown", classes: "enyo-list-pulldown", components: [
+ {name: "puller", kind: "Puller"}
+ ]}
+ ],
+ events: {
+ onPullStart: "",
+ onPullCancel: "",
+ onPull: "",
+ onPullRelease: "",
+ onPullComplete: ""
+ },
+ handlers: {
+ onScrollStart: "scrollStartHandler",
+ onScroll: "scrollHandler",
+ onScrollStop: "scrollStopHandler",
+ ondragfinish: "dragfinish"
+ },
+ //
+ pullingMessage: "Pull down to refresh...",
+ pulledMessage: "Release to refresh...",
+ loadingMessage: "Loading...",
+ //
+ pullingIconClass: "enyo-puller-arrow enyo-puller-arrow-down",
+ pulledIconClass: "enyo-puller-arrow enyo-puller-arrow-up",
+ loadingIconClass: "",
+ //* @protected
+ create: function() {
+ var p = {kind: "Puller", showing: false, text: this.loadingMessage, iconClass: this.loadingIconClass, onCreate: "setPully"};
+ this.listTools.splice(0, 0, p);
+ this.inherited(arguments);
+ this.setPulling();
+ },
+ initComponents: function() {
+ this.createChrome(this.pulldownTools);
+ this.accel = enyo.dom.canAccelerate();
+ this.translation = this.accel ? "translate3d" : "translate";
+ this.inherited(arguments);
+ },
+ setPully: function(inSender, inEvent) {
+ this.pully = inEvent.originator;
+ },
+ scrollStartHandler: function() {
+ this.firedPullStart = false;
+ this.firedPull = false;
+ this.firedPullCancel = false;
+ },
+ scrollHandler: function(inSender) {
+ if (this.completingPull) {
+ this.pully.setShowing(false);
+ }
+ var s = this.getStrategy().$.scrollMath;
+ var over = s.y;
+ if (s.isInOverScroll() && over > 0) {
+ enyo.dom.transformValue(this.$.pulldown, this.translation, "0," + over + "px" + (this.accel ? ",0" : ""));
+ if (!this.firedPullStart) {
+ this.firedPullStart = true;
+ this.pullStart();
+ this.pullHeight = this.$.pulldown.getBounds().height;
+ }
+ if (over > this.pullHeight && !this.firedPull) {
+ this.firedPull = true;
+ this.firedPullCancel = false;
+ this.pull();
+ }
+ if (this.firedPull && !this.firedPullCancel && over < this.pullHeight) {
+ this.firedPullCancel = true;
+ this.firedPull = false;
+ this.pullCancel();
+ }
+ }
+ },
+ scrollStopHandler: function() {
+ if (this.completingPull) {
+ this.completingPull = false;
+ this.doPullComplete();
+ }
+ },
+ dragfinish: function() {
+ if (this.firedPull) {
+ var s = this.getStrategy().$.scrollMath;
+ s.setScrollY(s.y - this.pullHeight);
+ this.pullRelease();
+ }
+ },
+ //* @public
+ //* To signal that the list should execute pull completion. This usually be called after the application has received the new data.
+ completePull: function() {
+ this.completingPull = true;
+ this.$.strategy.$.scrollMath.setScrollY(this.pullHeight);
+ this.$.strategy.$.scrollMath.start();
+ },
+ //* @protected
+ pullStart: function() {
+ this.setPulling();
+ this.pully.setShowing(false);
+ this.$.puller.setShowing(true);
+ this.doPullStart();
+ },
+ pull: function() {
+ this.setPulled();
+ this.doPull();
+ },
+ pullCancel: function() {
+ this.setPulling();
+ this.doPullCancel();
+ },
+ pullRelease: function() {
+ this.$.puller.setShowing(false);
+ this.pully.setShowing(true);
+ this.doPullRelease();
+ },
+ setPulling: function() {
+ this.$.puller.setText(this.pullingMessage);
+ this.$.puller.setIconClass(this.pullingIconClass);
+ },
+ setPulled: function() {
+ this.$.puller.setText(this.pulledMessage);
+ this.$.puller.setIconClass(this.pulledIconClass);
+ }
+});
+
+enyo.kind({
+ name: "enyo.Puller",
+ classes: "enyo-puller",
+ published: {
+ text: "",
+ iconClass: ""
+ },
+ events: {
+ onCreate: ""
+ },
+ components: [
+ {name: "icon"},
+ {name: "text", tag: "span", classes: "enyo-puller-text"}
+ ],
+ create: function() {
+ this.inherited(arguments);
+ this.doCreate();
+ this.textChanged();
+ this.iconClassChanged();
+ },
+ textChanged: function() {
+ this.$.text.setContent(this.text);
+ },
+ iconClassChanged: function() {
+ this.$.icon.setClasses(this.iconClass);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/Selection.js b/html/lib/layout/list/source/Selection.js
new file mode 100644
index 0000000..8581f8d
--- /dev/null
+++ b/html/lib/layout/list/source/Selection.js
@@ -0,0 +1,141 @@
+/**
+ _enyo.Selection_ is used to manage row selection state for lists. It
+ provides selection state management for both single-select and multi-select
+ lists.
+
+ // The following is an excerpt from enyo.FlyweightRepeater.
+ enyo.kind({
+ name: "enyo.FlyweightRepeater",
+ ...
+ components: [
+ {kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"},
+ ...
+ ],
+ tap: function(inSender, inEvent) {
+ ...
+ // mark the tapped row as selected
+ this.$.selection.select(inEvent.index);
+ ...
+ },
+ selectDeselect: function(inSender, inEvent) {
+ // this is where a row selection highlight might be applied
+ this.renderRow(inEvent.key);
+ }
+ ...
+ })
+*/
+enyo.kind({
+ name: "enyo.Selection",
+ kind: enyo.Component,
+ published: {
+ //* If true, multiple selections are allowed.
+ multi: false
+ },
+ events: {
+ /**
+ Fires when an item is selected.
+
+ {kind: "Selection", onSelect: "selectRow"...
+ ...
+ selectRow: function(inSender, inKey, inPrivateData) {
+ ...
+
+ _inKey_ is whatever key was used to register
+ the selection (usually a row index).
+
+ _inPrivateData_ references data registered
+ with this key by the code that made the original selection.
+ */
+ onSelect: "",
+ /**
+ Fires when an item is deselected.
+
+ {kind: "Selection", onSelect: "deselectRow"...
+ ...
+ deselectRow: function(inSender, inKey, inPrivateData)
+ ...
+
+ _inKey_ is whatever key was used to request
+ the deselection (usually a row index).
+
+ _inPrivateData_ references data registered
+ with this key by the code that made the selection.
+ */
+ onDeselect: "",
+ //* Sent when selection changes (but not when the selection is cleared).
+ onChange: ""
+ },
+ //* @protected
+ create: function() {
+ this.clear();
+ this.inherited(arguments);
+ },
+ multiChanged: function() {
+ if (!this.multi) {
+ this.clear();
+ }
+ this.doChange();
+ },
+ highlander: function(inKey) {
+ if (!this.multi) {
+ this.deselect(this.lastSelected);
+ }
+ },
+ //* @public
+ //* Removes all selections.
+ clear: function() {
+ this.selected = {};
+ },
+ //* Returns true if the _inKey_ row is selected.
+ isSelected: function(inKey) {
+ return this.selected[inKey];
+ },
+ //* Manually sets a row's state to selected or unselected.
+ setByKey: function(inKey, inSelected, inData) {
+ if (inSelected) {
+ this.selected[inKey] = (inData || true);
+ this.lastSelected = inKey;
+ this.doSelect({key: inKey, data: this.selected[inKey]});
+ } else {
+ var was = this.isSelected(inKey);
+ delete this.selected[inKey];
+ this.doDeselect({key: inKey, data: was});
+ }
+ this.doChange();
+ },
+ //* Deselects a row.
+ deselect: function(inKey) {
+ if (this.isSelected(inKey)) {
+ this.setByKey(inKey, false);
+ }
+ },
+ /**
+ Selects a row. If the _multi_ property is set to false, _select_ will
+ also deselect the previous selection.
+ */
+ select: function(inKey, inData) {
+ if (this.multi) {
+ this.setByKey(inKey, !this.isSelected(inKey), inData);
+ } else if (!this.isSelected(inKey)) {
+ this.highlander();
+ this.setByKey(inKey, true, inData);
+ }
+ },
+ /**
+ Toggles selection state for a row. If the _multi_ property is set to
+ false, toggling a selection on will deselect the previous selection.
+ */
+ toggle: function(inKey, inData) {
+ if (!this.multi && this.lastSelected != inKey) {
+ this.deselect(this.lastSelected);
+ }
+ this.setByKey(inKey, !this.isSelected(inKey), inData);
+ },
+ /**
+ Returns the selection as a hash in which each selected item has a value;
+ unselected items are undefined.
+ */
+ getSelected: function() {
+ return this.selected;
+ }
+});
diff --git a/html/lib/layout/list/source/package.js b/html/lib/layout/list/source/package.js
new file mode 100644
index 0000000..391e38e
--- /dev/null
+++ b/html/lib/layout/list/source/package.js
@@ -0,0 +1,7 @@
+enyo.depends(
+ "FlyweightRepeater.js",
+ "List.css",
+ "List.js",
+ "PulldownList.css",
+ "PulldownList.js"
+); \ No newline at end of file
diff --git a/html/lib/layout/list/source/wip-package.js b/html/lib/layout/list/source/wip-package.js
new file mode 100644
index 0000000..bafc093
--- /dev/null
+++ b/html/lib/layout/list/source/wip-package.js
@@ -0,0 +1,6 @@
+enyo.depends(
+ // Add wip controls here
+ "AlphaJumper.css",
+ "AlphaJumper.js",
+ "AlphaJumpList.js"
+); \ No newline at end of file