mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 16:04:53 +00:00
Format samples and samples-dev directories.
BUG= R=sra@google.com Review-Url: https://codereview.chromium.org/2828603002 .
This commit is contained in:
parent
ed2d79a872
commit
b33097b9b4
|
@ -13,7 +13,6 @@ part of swarmlib;
|
|||
* This class or something similar belongs in the standard DOM library.
|
||||
*/
|
||||
class App {
|
||||
|
||||
App() {}
|
||||
|
||||
/** Begins executing code in this [App]. */
|
||||
|
@ -32,10 +31,10 @@ class App {
|
|||
Timer.run(onLoad);
|
||||
} else {
|
||||
window.onContentLoaded.listen(
|
||||
// TODO(sigmund): Consider eliminating the call to "wrap", for
|
||||
// instance, modify event listeners to always wrap, or extend DOM code
|
||||
// to intercept the beginning & end of each event loop
|
||||
EventBatch.wrap((Event event) => onLoad()));
|
||||
// TODO(sigmund): Consider eliminating the call to "wrap", for
|
||||
// instance, modify event listeners to always wrap, or extend DOM code
|
||||
// to intercept the beginning & end of each event loop
|
||||
EventBatch.wrap((Event event) => onLoad()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,8 +51,7 @@ class App {
|
|||
// Swap and reload the cache if ready
|
||||
if (!swapAndReloadCache()) {
|
||||
// Otherwise wait until an update to the cache is ready
|
||||
window.applicationCache.onUpdateReady.listen(
|
||||
(e) => swapAndReloadCache());
|
||||
window.applicationCache.onUpdateReady.listen((e) => swapAndReloadCache());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ part of swarmlib;
|
|||
* a set of items. (Bi-directional)
|
||||
*/
|
||||
class BiIterator<E> {
|
||||
|
||||
/**
|
||||
* Provides forward and backward iterator functionality to keep track
|
||||
* which item is currently selected.
|
||||
|
@ -18,7 +17,7 @@ class BiIterator<E> {
|
|||
List<E> list;
|
||||
|
||||
BiIterator(this.list, [List<ChangeListener> oldListeners = null])
|
||||
: currentIndex = new ObservableValue<int>(0) {
|
||||
: currentIndex = new ObservableValue<int>(0) {
|
||||
if (oldListeners != null) {
|
||||
currentIndex.listeners = oldListeners;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -16,13 +16,14 @@ class ConfigHintDialog extends DialogView {
|
|||
}
|
||||
|
||||
ConfigHintDialog._impl(this._parent, this._doneHandler, View content)
|
||||
: super('Feed configuration', '', content);
|
||||
: super('Feed configuration', '', content);
|
||||
|
||||
void onDone() { _doneHandler(); }
|
||||
void onDone() {
|
||||
_doneHandler();
|
||||
}
|
||||
|
||||
static View makeContent() {
|
||||
return new View.html(
|
||||
'''
|
||||
return new View.html('''
|
||||
<div>
|
||||
Add or remove feeds in
|
||||
<a href="https://www.google.com/reader" target="_blank">
|
||||
|
|
|
@ -15,8 +15,7 @@ class Sections extends IterableBase<Section> {
|
|||
|
||||
int get length => _sections.length;
|
||||
|
||||
List<String> get sectionTitles =>
|
||||
_sections.map((s) => s.title).toList();
|
||||
List<String> get sectionTitles => _sections.map((s) => s.title).toList();
|
||||
|
||||
void refresh() {
|
||||
// TODO(jimhug): http://b/issue?id=5351067
|
||||
|
@ -50,24 +49,25 @@ class Sections extends IterableBase<Section> {
|
|||
int nSections = decoder.readInt();
|
||||
final sections = new List<Section>();
|
||||
|
||||
for (int i=0; i < nSections; i++) {
|
||||
for (int i = 0; i < nSections; i++) {
|
||||
sections.add(Section.decode(decoder));
|
||||
}
|
||||
callback(new Sections(sections));
|
||||
}
|
||||
|
||||
static void initializeFromUrl(bool useCannedData,
|
||||
void callback(Sections sections)) {
|
||||
static void initializeFromUrl(
|
||||
bool useCannedData, void callback(Sections sections)) {
|
||||
if (Sections.runningFromFile || useCannedData) {
|
||||
initializeFromData(CannedData.data['user.data'], callback);
|
||||
} else {
|
||||
// TODO(jmesserly): display an error if we fail here! Silent failure bad.
|
||||
HttpRequest.getString('data/user.data').then(
|
||||
EventBatch.wrap((responseText) {
|
||||
// TODO(jimhug): Nice response if get error back from server.
|
||||
// TODO(jimhug): Might be more efficient to parse request
|
||||
// in sections.
|
||||
initializeFromData(responseText, callback);
|
||||
HttpRequest
|
||||
.getString('data/user.data')
|
||||
.then(EventBatch.wrap((responseText) {
|
||||
// TODO(jimhug): Nice response if get error back from server.
|
||||
// TODO(jimhug): Might be more efficient to parse request
|
||||
// in sections.
|
||||
initializeFromData(responseText, callback);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,6 @@ class Sections extends IterableBase<Section> {
|
|||
bool get isEmpty => length == 0;
|
||||
}
|
||||
|
||||
|
||||
/** A collection of data sources representing a page in the UI. */
|
||||
class Section {
|
||||
final String id;
|
||||
|
@ -116,7 +115,7 @@ class Section {
|
|||
|
||||
final nSources = decoder.readInt();
|
||||
final feeds = new ObservableList<Feed>();
|
||||
for (int j=0; j < nSources; j++) {
|
||||
for (int j = 0; j < nSources; j++) {
|
||||
feeds.add(Feed.decode(decoder));
|
||||
}
|
||||
return new Section(sectionId, sectionTitle, feeds);
|
||||
|
@ -137,8 +136,8 @@ class Feed {
|
|||
ObservableValue<bool> error; // TODO(jimhug): Check if dead code.
|
||||
|
||||
Feed(this.id, this.title, this.iconUrl, {this.description: ''})
|
||||
: articles = new ObservableList<Article>(),
|
||||
error = new ObservableValue<bool>(false);
|
||||
: articles = new ObservableList<Article>(),
|
||||
error = new ObservableValue<bool>(false);
|
||||
|
||||
static Feed decode(Decoder decoder) {
|
||||
final sourceId = decoder.readString();
|
||||
|
@ -147,7 +146,7 @@ class Feed {
|
|||
final feed = new Feed(sourceId, sourceTitle, sourceIcon);
|
||||
final nItems = decoder.readInt();
|
||||
|
||||
for (int i=0; i < nItems; i++) {
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
feed.articles.add(Article.decodeHeader(feed, decoder));
|
||||
}
|
||||
return feed;
|
||||
|
@ -160,7 +159,6 @@ class Feed {
|
|||
void refresh() {}
|
||||
}
|
||||
|
||||
|
||||
/** A single article or posting to display. */
|
||||
class Article {
|
||||
final String id;
|
||||
|
@ -179,7 +177,8 @@ class Article {
|
|||
Article(this.dataSource, this.id, this.date, this.title, this.author,
|
||||
this.srcUrl, this.hasThumbnail, this.textBody,
|
||||
{htmlBody: null, bool unread: true, this.error: false})
|
||||
: unread = new ObservableValue<bool>(unread), this._htmlBody = htmlBody;
|
||||
: unread = new ObservableValue<bool>(unread),
|
||||
this._htmlBody = htmlBody;
|
||||
|
||||
String get htmlBody {
|
||||
_ensureLoaded();
|
||||
|
@ -187,8 +186,10 @@ class Article {
|
|||
}
|
||||
|
||||
String get dataUri {
|
||||
return SwarmUri.encodeComponent(id).replaceAll('%2F', '/').
|
||||
replaceAll('%253A', '%3A');
|
||||
return SwarmUri
|
||||
.encodeComponent(id)
|
||||
.replaceAll('%2F', '/')
|
||||
.replaceAll('%253A', '%3A');
|
||||
}
|
||||
|
||||
String get thumbUrl {
|
||||
|
@ -232,9 +233,9 @@ class Article {
|
|||
final author = decoder.readString();
|
||||
final dateInSeconds = decoder.readInt();
|
||||
final snippet = decoder.readString();
|
||||
final date =
|
||||
new DateTime.fromMillisecondsSinceEpoch(dateInSeconds*1000, isUtc: true);
|
||||
return new Article(source, id, date, title, author, srcUrl, hasThumbnail,
|
||||
snippet);
|
||||
final date = new DateTime.fromMillisecondsSinceEpoch(dateInSeconds * 1000,
|
||||
isUtc: true);
|
||||
return new Article(
|
||||
source, id, date, title, author, srcUrl, hasThumbnail, snippet);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class Decoder {
|
|||
// For more info, see appengine/encoder.py.
|
||||
int readInt() {
|
||||
var r = 0;
|
||||
for (var i=0; ; i++) {
|
||||
for (var i = 0;; i++) {
|
||||
var v = data.codeUnitAt(index++);
|
||||
r |= (v & 0x3F) << (6 * i);
|
||||
if ((v & 0x40) == 0) break;
|
||||
|
@ -29,15 +29,14 @@ class Decoder {
|
|||
|
||||
bool readBool() {
|
||||
final ch = data[index++];
|
||||
assert (ch == 'T' || ch == 'F');
|
||||
assert(ch == 'T' || ch == 'F');
|
||||
return ch == 'T';
|
||||
}
|
||||
|
||||
String readString() {
|
||||
int len = readInt();
|
||||
String s = data.substring(index, index+len);
|
||||
String s = data.substring(index, index + len);
|
||||
index += len;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,13 +15,14 @@ class HelpDialog extends DialogView {
|
|||
Function _doneHandler;
|
||||
|
||||
HelpDialog(this._parent, this._doneHandler)
|
||||
: super('Information', '', makeContent());
|
||||
: super('Information', '', makeContent());
|
||||
|
||||
void onDone() { _doneHandler(); }
|
||||
void onDone() {
|
||||
_doneHandler();
|
||||
}
|
||||
|
||||
static View makeContent() {
|
||||
return new View.html(
|
||||
'''
|
||||
return new View.html('''
|
||||
<div>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -23,7 +23,9 @@ class Swarm extends App {
|
|||
/** Observable UI state. */
|
||||
SwarmState state;
|
||||
|
||||
Swarm({bool useCannedData : false}) : super(), onLoadFired = false {
|
||||
Swarm({bool useCannedData: false})
|
||||
: super(),
|
||||
onLoadFired = false {
|
||||
Sections.initializeFromUrl(useCannedData, (currSections) {
|
||||
sections = currSections;
|
||||
state = new SwarmState(sections);
|
||||
|
|
|
@ -54,18 +54,17 @@ class SwarmState extends UIState {
|
|||
BiIterator<Section> _sectionIterator;
|
||||
|
||||
SwarmState(this._dataModel)
|
||||
: super(),
|
||||
currentArticle = new ObservableValue<Article>(null),
|
||||
selectedArticle = new ObservableValue<Article>(null),
|
||||
storyMaximized = new ObservableValue<bool>(false),
|
||||
storyTextMode = new ObservableValue<bool>(true) {
|
||||
: super(),
|
||||
currentArticle = new ObservableValue<Article>(null),
|
||||
selectedArticle = new ObservableValue<Article>(null),
|
||||
storyMaximized = new ObservableValue<bool>(false),
|
||||
storyTextMode = new ObservableValue<bool>(true) {
|
||||
startHistoryTracking();
|
||||
// TODO(efortuna): consider having this class just hold observable
|
||||
// currentIndecies instead of iterators with observablevalues..
|
||||
_sectionIterator = new BiIterator<Section>(_dataModel.sections);
|
||||
_feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds);
|
||||
_articleIterator =
|
||||
new BiIterator<Article>(_feedIterator.current.articles);
|
||||
_articleIterator = new BiIterator<Article>(_feedIterator.current.articles);
|
||||
|
||||
currentArticle.addChangeListener((e) {
|
||||
_articleIterator.jumpToValue(currentArticle.value);
|
||||
|
@ -99,8 +98,8 @@ class SwarmState extends UIState {
|
|||
void loadFromHistory(Map values) {
|
||||
// TODO(jimhug): There's a better way of doing this...
|
||||
if (values['section'] != null) {
|
||||
_sectionIterator.jumpToValue(_dataModel.
|
||||
findSectionById(values['section']));
|
||||
_sectionIterator
|
||||
.jumpToValue(_dataModel.findSectionById(values['section']));
|
||||
} else {
|
||||
_sectionIterator = new BiIterator<Section>(_dataModel.sections);
|
||||
}
|
||||
|
@ -125,7 +124,7 @@ class SwarmState extends UIState {
|
|||
* Move the currentArticle pointer to the next item in the Feed.
|
||||
*/
|
||||
void goToNextArticle() {
|
||||
currentArticle.value = _articleIterator.next();
|
||||
currentArticle.value = _articleIterator.next();
|
||||
selectedArticle.value = _articleIterator.current;
|
||||
}
|
||||
|
||||
|
@ -159,8 +158,8 @@ class SwarmState extends UIState {
|
|||
var newFeed = _feedIterator.next();
|
||||
int oldIndex = _articleIterator.currentIndex.value;
|
||||
|
||||
_articleIterator = new BiIterator<Article>(newFeed.articles,
|
||||
_articleIterator.currentIndex.listeners);
|
||||
_articleIterator = new BiIterator<Article>(
|
||||
newFeed.articles, _articleIterator.currentIndex.listeners);
|
||||
|
||||
_articleIterator.currentIndex.value = oldIndex;
|
||||
selectedArticle.value = _articleIterator.current;
|
||||
|
@ -174,11 +173,11 @@ class SwarmState extends UIState {
|
|||
var newFeed = _feedIterator.previous();
|
||||
int oldIndex = _articleIterator.currentIndex.value;
|
||||
|
||||
_articleIterator = new BiIterator<Article>(newFeed.articles,
|
||||
_articleIterator.currentIndex.listeners);
|
||||
_articleIterator = new BiIterator<Article>(
|
||||
newFeed.articles, _articleIterator.currentIndex.listeners);
|
||||
_articleIterator.currentIndex.value = oldIndex;
|
||||
selectedArticle.value = _articleIterator.current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the next section (page) of feeds in the UI.
|
||||
|
@ -195,10 +194,9 @@ class SwarmState extends UIState {
|
|||
// This check prevents our selector from wrapping around when we try to
|
||||
// go to the "next section", but we're already at the last section.
|
||||
if (oldSection != _sectionIterator.current) {
|
||||
_feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds,
|
||||
_feedIterator.currentIndex.listeners);
|
||||
_articleIterator =
|
||||
new BiIterator<Article>(_feedIterator.current.articles,
|
||||
_feedIterator = new BiIterator<Feed>(
|
||||
_sectionIterator.current.feeds, _feedIterator.currentIndex.listeners);
|
||||
_articleIterator = new BiIterator<Article>(_feedIterator.current.articles,
|
||||
_articleIterator.currentIndex.listeners);
|
||||
_articleIterator.currentIndex.value = oldIndex;
|
||||
selectedArticle.value = _articleIterator.current;
|
||||
|
@ -223,12 +221,11 @@ class SwarmState extends UIState {
|
|||
// This check prevents our selector from wrapping around when we try to
|
||||
// go to the "previous section", but we're already at the first section.
|
||||
if (oldSection != _sectionIterator.current) {
|
||||
_feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds,
|
||||
_feedIterator.currentIndex.listeners);
|
||||
_feedIterator = new BiIterator<Feed>(
|
||||
_sectionIterator.current.feeds, _feedIterator.currentIndex.listeners);
|
||||
// Jump to back of feed set if we are moving backwards through sections.
|
||||
_feedIterator.currentIndex.value = _feedIterator.list.length - 1;
|
||||
_articleIterator =
|
||||
new BiIterator<Article>(_feedIterator.current.articles,
|
||||
_articleIterator = new BiIterator<Article>(_feedIterator.current.articles,
|
||||
_articleIterator.currentIndex.listeners);
|
||||
_articleIterator.currentIndex.value = oldIndex;
|
||||
selectedArticle.value = _articleIterator.current;
|
||||
|
@ -287,10 +284,9 @@ class SwarmState extends UIState {
|
|||
void moveToNewSection(String sectionTitle) {
|
||||
_sectionIterator.currentIndex.value =
|
||||
_dataModel.findSectionIndex(sectionTitle);
|
||||
_feedIterator = new BiIterator<Feed>(_sectionIterator.current.feeds,
|
||||
_feedIterator.currentIndex.listeners);
|
||||
_articleIterator =
|
||||
new BiIterator<Article>(_feedIterator.current.articles,
|
||||
_feedIterator = new BiIterator<Feed>(
|
||||
_sectionIterator.current.feeds, _feedIterator.currentIndex.listeners);
|
||||
_articleIterator = new BiIterator<Article>(_feedIterator.current.articles,
|
||||
_articleIterator.currentIndex.listeners);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,13 +70,12 @@ class FrontView extends CompositeView {
|
|||
headerView = new HeaderView(swarm);
|
||||
topView.addChild(headerView);
|
||||
|
||||
sliderMenu = new SliderMenu(swarm.sections.sectionTitles,
|
||||
(sectionTitle) {
|
||||
swarm.state.moveToNewSection(sectionTitle);
|
||||
_onSectionSelected(sectionTitle);
|
||||
// Start with no articles selected.
|
||||
swarm.state.selectedArticle.value = null;
|
||||
});
|
||||
sliderMenu = new SliderMenu(swarm.sections.sectionTitles, (sectionTitle) {
|
||||
swarm.state.moveToNewSection(sectionTitle);
|
||||
_onSectionSelected(sectionTitle);
|
||||
// Start with no articles selected.
|
||||
swarm.state.selectedArticle.value = null;
|
||||
});
|
||||
topView.addChild(sliderMenu);
|
||||
addChild(topView);
|
||||
|
||||
|
@ -99,8 +98,12 @@ class FrontView extends CompositeView {
|
|||
|
||||
void afterRender(Element node) {
|
||||
_createSectionViews();
|
||||
attachWatch(swarm.state.currentArticle, (e) { _refreshCurrentArticle(); });
|
||||
attachWatch(swarm.state.storyMaximized, (e) { _refreshMaximized(); });
|
||||
attachWatch(swarm.state.currentArticle, (e) {
|
||||
_refreshCurrentArticle();
|
||||
});
|
||||
attachWatch(swarm.state.storyMaximized, (e) {
|
||||
_refreshMaximized();
|
||||
});
|
||||
}
|
||||
|
||||
void _refreshCurrentArticle() {
|
||||
|
@ -121,8 +124,8 @@ class FrontView extends CompositeView {
|
|||
|
||||
headerView.startTransitionToMainView();
|
||||
|
||||
currentSection.dataSourceView.reattachSubview(
|
||||
detachedView.source, detachedView, true);
|
||||
currentSection.dataSourceView
|
||||
.reattachSubview(detachedView.source, detachedView, true);
|
||||
|
||||
storyView.node.onTransitionEnd.first.then((e) {
|
||||
currentSection.hidden = false;
|
||||
|
@ -157,8 +160,8 @@ class FrontView extends CompositeView {
|
|||
// TODO(jmesserly): make this code better
|
||||
final view = currentSection.findView(source);
|
||||
|
||||
final newPosition = FxUtil.computeRelativePosition(
|
||||
view.node, bottomView.node);
|
||||
final newPosition =
|
||||
FxUtil.computeRelativePosition(view.node, bottomView.node);
|
||||
currentSection.dataSourceView.detachSubview(view.source);
|
||||
detachedView = view;
|
||||
|
||||
|
@ -197,8 +200,8 @@ class FrontView extends CompositeView {
|
|||
void _animateDataSourceToMaximized() {
|
||||
FxUtil.setWebkitTransform(topView.node, 0, -HeaderView.HEIGHT);
|
||||
if (detachedView != null) {
|
||||
FxUtil.setWebkitTransform(detachedView.node, 0,
|
||||
-DataSourceView.TAB_ONLY_HEIGHT);
|
||||
FxUtil.setWebkitTransform(
|
||||
detachedView.node, 0, -DataSourceView.TAB_ONLY_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +329,9 @@ class SwarmBackButton extends View {
|
|||
Element render() => new Element.html('<div class="back-arrow button"></div>');
|
||||
|
||||
void afterRender(Element node) {
|
||||
addOnClick((e) { _backToMain(swarm.state); });
|
||||
addOnClick((e) {
|
||||
_backToMain(swarm.state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,9 +370,13 @@ class HeaderView extends CompositeView {
|
|||
|
||||
void afterRender(Element node) {
|
||||
// Respond to changes to whether the story is being shown as text or web.
|
||||
attachWatch(swarm.state.storyTextMode, (e) { refreshWebStoryButtons(); });
|
||||
attachWatch(swarm.state.storyTextMode, (e) {
|
||||
refreshWebStoryButtons();
|
||||
});
|
||||
|
||||
_title.addOnClick((e) { _backToMain(swarm.state); });
|
||||
_title.addOnClick((e) {
|
||||
_backToMain(swarm.state);
|
||||
});
|
||||
|
||||
// Wire up the events.
|
||||
_configButton.addOnClick((e) {
|
||||
|
@ -418,7 +427,6 @@ class HeaderView extends CompositeView {
|
|||
startTransitionToMainView();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refreshes whether or not the buttons specific to the display of a story in
|
||||
* the web perspective are visible.
|
||||
|
@ -433,7 +441,7 @@ class HeaderView extends CompositeView {
|
|||
|
||||
_webBackButton.hidden = webButtonsHidden;
|
||||
_webForwardButton.hidden = webButtonsHidden;
|
||||
_newWindowButton.hidden = webButtonsHidden;
|
||||
_newWindowButton.hidden = webButtonsHidden;
|
||||
}
|
||||
|
||||
void startTransitionToMainView() {
|
||||
|
@ -457,7 +465,6 @@ class HeaderView extends CompositeView {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/** A back button for the web view of a story that is equivalent to clicking
|
||||
* "back" in the browser. */
|
||||
// TODO(rnystrom): We have nearly identical versions of this littered through
|
||||
|
@ -470,7 +477,9 @@ class WebBackButton extends View {
|
|||
}
|
||||
|
||||
void afterRender(Element node) {
|
||||
addOnClick((e) { back(); });
|
||||
addOnClick((e) {
|
||||
back();
|
||||
});
|
||||
}
|
||||
|
||||
/** Equivalent to [window.history.back] */
|
||||
|
@ -491,7 +500,9 @@ class WebForwardButton extends View {
|
|||
}
|
||||
|
||||
void afterRender(Element node) {
|
||||
addOnClick((e) { forward(); });
|
||||
addOnClick((e) {
|
||||
forward();
|
||||
});
|
||||
}
|
||||
|
||||
/** Equivalent to [window.history.forward] */
|
||||
|
@ -514,12 +525,11 @@ class DataSourceViewFactory implements ViewFactory<Feed> {
|
|||
int get height => null; // Width for this view isn't known.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A view for the items from a single data source.
|
||||
* Shows a title and a list of items.
|
||||
*/
|
||||
class DataSourceView extends CompositeView {
|
||||
class DataSourceView extends CompositeView {
|
||||
// TODO(jacobr): make this value be coupled with the CSS file.
|
||||
static const TAB_ONLY_HEIGHT = 34;
|
||||
|
||||
|
@ -527,19 +537,20 @@ class DataSourceView extends CompositeView {
|
|||
VariableSizeListView<Article> itemsView;
|
||||
|
||||
DataSourceView(this.source, Swarm swarm) : super('query') {
|
||||
|
||||
// TODO(jacobr): make the title a view or decide it is sane for a subclass
|
||||
// of component view to manually add some DOM cruft.
|
||||
node.nodes.add(new Element.html(
|
||||
'<h2>${source.title}</h2>'));
|
||||
node.nodes.add(new Element.html('<h2>${source.title}</h2>'));
|
||||
|
||||
// TODO(jacobr): use named arguments when available.
|
||||
itemsView = addChild(new VariableSizeListView<Article>(
|
||||
source.articles,
|
||||
new ArticleViewFactory(swarm),
|
||||
true, /* scrollable */
|
||||
true, /* vertical */
|
||||
swarm.state.currentArticle, /* selectedItem */
|
||||
true,
|
||||
/* scrollable */
|
||||
true,
|
||||
/* vertical */
|
||||
swarm.state.currentArticle,
|
||||
/* selectedItem */
|
||||
!Device.supportsTouch /* snapToArticles */,
|
||||
false /* paginate */,
|
||||
true /* removeClippedViews */,
|
||||
|
@ -562,14 +573,16 @@ class ToggleButton extends View {
|
|||
List<String> states;
|
||||
|
||||
ToggleButton(this.states)
|
||||
: super(),
|
||||
onChanged = new EventListeners();
|
||||
: super(),
|
||||
onChanged = new EventListeners();
|
||||
|
||||
Element render() => new Element.tag('button');
|
||||
|
||||
void afterRender(Element node) {
|
||||
state = states[0];
|
||||
node.onClick.listen((event) { toggle(); });
|
||||
node.onClick.listen((event) {
|
||||
toggle();
|
||||
});
|
||||
}
|
||||
|
||||
String get state {
|
||||
|
@ -599,8 +612,7 @@ class ArticleViewFactory implements VariableSizeViewFactory<Article> {
|
|||
Swarm swarm;
|
||||
|
||||
ArticleViewLayout layout;
|
||||
ArticleViewFactory(this.swarm)
|
||||
: layout = ArticleViewLayout.getSingleton();
|
||||
ArticleViewFactory(this.swarm) : layout = ArticleViewLayout.getSingleton();
|
||||
|
||||
View newView(Article item) => new ArticleView(item, swarm, layout);
|
||||
|
||||
|
@ -637,9 +649,9 @@ class ArticleViewLayout {
|
|||
|
||||
int width;
|
||||
static ArticleViewLayout _singleton;
|
||||
ArticleViewLayout() :
|
||||
measureBodyText = new MeasureText(BODY_FONT),
|
||||
measureTitleText = new MeasureText(TITLE_FONT) {
|
||||
ArticleViewLayout()
|
||||
: measureBodyText = new MeasureText(BODY_FONT),
|
||||
measureTitleText = new MeasureText(TITLE_FONT) {
|
||||
num screenWidth = window.screen.width;
|
||||
width = DESKTOP_WIDTH;
|
||||
}
|
||||
|
@ -665,19 +677,18 @@ class ArticleViewLayout {
|
|||
* titleContainer and snippetContainer may be null in which case the size is
|
||||
* computed but no actual layout is performed.
|
||||
*/
|
||||
ArticleViewMetrics computeLayout(Article item,
|
||||
StringBuffer titleBuffer,
|
||||
StringBuffer snippetBuffer) {
|
||||
ArticleViewMetrics computeLayout(
|
||||
Article item, StringBuffer titleBuffer, StringBuffer snippetBuffer) {
|
||||
int titleWidth = width - BODY_MARGIN_LEFT;
|
||||
|
||||
if (item.hasThumbnail) {
|
||||
titleWidth = width - TITLE_MARGIN_LEFT;
|
||||
}
|
||||
|
||||
final titleLines = measureTitleText.addLineBrokenText(titleBuffer,
|
||||
item.title, titleWidth, MAX_TITLE_LINES);
|
||||
final bodyLines = measureBodyText.addLineBrokenText(snippetBuffer,
|
||||
item.textBody, width - BODY_MARGIN_LEFT, MAX_BODY_LINES);
|
||||
final titleLines = measureTitleText.addLineBrokenText(
|
||||
titleBuffer, item.title, titleWidth, MAX_TITLE_LINES);
|
||||
final bodyLines = measureBodyText.addLineBrokenText(
|
||||
snippetBuffer, item.textBody, width - BODY_MARGIN_LEFT, MAX_BODY_LINES);
|
||||
|
||||
int height = bodyLines * LINE_HEIGHT + TOTAL_MARGIN;
|
||||
|
||||
|
@ -741,7 +752,6 @@ class ArticleView extends View {
|
|||
}
|
||||
|
||||
void afterRender(Element node) {
|
||||
|
||||
// Select this view's item.
|
||||
addOnClick((e) {
|
||||
// Mark the item as read, so it shows as read in other views
|
||||
|
@ -791,13 +801,13 @@ class ArticleView extends View {
|
|||
// Story View.
|
||||
swarm.frontView.detachedView.itemsView.showView(selArticle);
|
||||
} else {
|
||||
if(swarm.frontView.currentSection.inCurrentView(selArticle)) {
|
||||
if (swarm.frontView.currentSection.inCurrentView(selArticle)) {
|
||||
// Scroll horizontally if needed.
|
||||
swarm.frontView.currentSection.dataSourceView.showView(
|
||||
selArticle.dataSource);
|
||||
DataSourceView dataView = swarm.frontView.currentSection
|
||||
.findView(selArticle.dataSource);
|
||||
if(dataView != null) {
|
||||
swarm.frontView.currentSection.dataSourceView
|
||||
.showView(selArticle.dataSource);
|
||||
DataSourceView dataView =
|
||||
swarm.frontView.currentSection.findView(selArticle.dataSource);
|
||||
if (dataView != null) {
|
||||
dataView.itemsView.showView(selArticle);
|
||||
}
|
||||
}
|
||||
|
@ -807,8 +817,8 @@ class ArticleView extends View {
|
|||
|
||||
String getDataUriForImage(final img) {
|
||||
// TODO(hiltonc,jimhug) eval perf of this vs. reusing one canvas element
|
||||
final CanvasElement canvas = new CanvasElement(
|
||||
height: img.height, width: img.width);
|
||||
final CanvasElement canvas =
|
||||
new CanvasElement(height: img.height, width: img.width);
|
||||
|
||||
final CanvasRenderingContext2D ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
|
@ -848,8 +858,8 @@ class StoryContentView extends View {
|
|||
get childViews => [_pagedStory];
|
||||
|
||||
Element render() {
|
||||
final storyContent = new Element.html(
|
||||
'<div class="story-content">${item.htmlBody}</div>');
|
||||
final storyContent =
|
||||
new Element.html('<div class="story-content">${item.htmlBody}</div>');
|
||||
for (Element element in storyContent.querySelectorAll(
|
||||
"iframe, script, style, object, embed, frameset, frame")) {
|
||||
element.remove();
|
||||
|
@ -896,9 +906,9 @@ class SectionView extends CompositeView {
|
|||
final PageState pageState;
|
||||
|
||||
SectionView(this.swarm, this.section, this._viewFactory)
|
||||
: super('section-view'),
|
||||
loadingText = new View.html('<div class="loading-section"></div>'),
|
||||
pageState = new PageState() {
|
||||
: super('section-view'),
|
||||
loadingText = new View.html('<div class="loading-section"></div>'),
|
||||
pageState = new PageState() {
|
||||
addChild(loadingText);
|
||||
}
|
||||
|
||||
|
@ -912,14 +922,16 @@ class SectionView extends CompositeView {
|
|||
if (dataSourceView == null) {
|
||||
// TODO(jacobr): use named arguments when available.
|
||||
dataSourceView = new ListView<Feed>(
|
||||
section.feeds, _viewFactory,
|
||||
section.feeds,
|
||||
_viewFactory,
|
||||
true /* scrollable */,
|
||||
false /* vertical */,
|
||||
null /* selectedItem */,
|
||||
true /* snapToItems */,
|
||||
true /* paginate */,
|
||||
true /* removeClippedViews */,
|
||||
false, /* showScrollbar */
|
||||
false,
|
||||
/* showScrollbar */
|
||||
pageState);
|
||||
dataSourceView.addClass("data-source-view");
|
||||
addChild(dataSourceView);
|
||||
|
|
|
@ -60,8 +60,8 @@ abstract class UIState {
|
|||
// TODO(jmesserly): [state] should be an Object, and we should pass it to
|
||||
// the state parameter instead of as a #hash URL. Right now we're working
|
||||
// around b/4582542.
|
||||
window.history.pushState(null,
|
||||
'${document.title}', '${document.title}#$state');
|
||||
window.history
|
||||
.pushState(null, '${document.title}', '${document.title}#$state');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,7 @@ abstract class VariableSizeViewFactory<D> {
|
|||
/** A collection of event listeners. */
|
||||
class EventListeners {
|
||||
var listeners;
|
||||
EventListeners() {
|
||||
EventListeners() {
|
||||
listeners = new List();
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,6 @@ class EventListeners {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private view class used to store placeholder views for detatched ListView
|
||||
* elements.
|
||||
|
@ -98,8 +97,7 @@ abstract class ListViewLayout<D> {
|
|||
* Returns an interval specifying what views are currently visible given a
|
||||
* particular [:offset:].
|
||||
*/
|
||||
Interval computeVisibleInterval(num offset, num viewLength,
|
||||
num bufferLength);
|
||||
Interval computeVisibleInterval(num offset, num viewLength, num bufferLength);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,19 +136,19 @@ class GenericListView<D> extends View {
|
|||
* and update the view appropriately.
|
||||
*/
|
||||
GenericListView(
|
||||
this._layout,
|
||||
this._data,
|
||||
this._scrollable,
|
||||
this._vertical,
|
||||
this._selectedItem,
|
||||
this._snapToItems,
|
||||
this._paginate,
|
||||
this._removeClippedViews,
|
||||
this._showScrollbar,
|
||||
this._pages)
|
||||
: super(),
|
||||
_activeInterval = new Interval(0, 0),
|
||||
_itemViews = new Map<int, View>() {
|
||||
this._layout,
|
||||
this._data,
|
||||
this._scrollable,
|
||||
this._vertical,
|
||||
this._selectedItem,
|
||||
this._snapToItems,
|
||||
this._paginate,
|
||||
this._removeClippedViews,
|
||||
this._showScrollbar,
|
||||
this._pages)
|
||||
: super(),
|
||||
_activeInterval = new Interval(0, 0),
|
||||
_itemViews = new Map<int, View>() {
|
||||
// TODO(rnystrom): Move this into enterDocument once we have an exitDocument
|
||||
// that we can use to unregister it.
|
||||
if (_scrollable) {
|
||||
|
@ -216,16 +214,16 @@ class GenericListView<D> extends View {
|
|||
_containerElem,
|
||||
_vertical /* verticalScrollEnabled */,
|
||||
!_vertical /* horizontalScrollEnabled */,
|
||||
true /* momentumEnabled */,
|
||||
() {
|
||||
num width = _layout.getWidth(_viewLength);
|
||||
num height = _layout.getHeight(_viewLength);
|
||||
width = width != null ? width : 0;
|
||||
height = height != null ? height : 0;
|
||||
return new Size(width, height);
|
||||
},
|
||||
_paginate && _snapToItems ?
|
||||
Scroller.FAST_SNAP_DECELERATION_FACTOR : 1);
|
||||
true /* momentumEnabled */, () {
|
||||
num width = _layout.getWidth(_viewLength);
|
||||
num height = _layout.getHeight(_viewLength);
|
||||
width = width != null ? width : 0;
|
||||
height = height != null ? height : 0;
|
||||
return new Size(width, height);
|
||||
},
|
||||
_paginate && _snapToItems
|
||||
? Scroller.FAST_SNAP_DECELERATION_FACTOR
|
||||
: 1);
|
||||
scroller.onContentMoved.listen((e) => renderVisibleItems(false));
|
||||
if (_pages != null) {
|
||||
watch(_pages.target, (s) => _onPageSelected());
|
||||
|
@ -251,14 +249,16 @@ class GenericListView<D> extends View {
|
|||
if (_data is ObservableList<D>) {
|
||||
ObservableList<D> observable = _data;
|
||||
attachWatch(observable, (EventSummary e) {
|
||||
if (e.target == observable) {
|
||||
onDataChange();
|
||||
}
|
||||
});
|
||||
if (e.target == observable) {
|
||||
onDataChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (_selectedItem != null) {
|
||||
addOnClick((Event e) { _onClick(e); });
|
||||
addOnClick((Event e) {
|
||||
_onClick(e);
|
||||
});
|
||||
}
|
||||
|
||||
if (_selectedItem != null) {
|
||||
|
@ -286,7 +286,6 @@ class GenericListView<D> extends View {
|
|||
style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
|
||||
void onResize() {
|
||||
int lastViewLength = _viewLength;
|
||||
scheduleMicrotask(() {
|
||||
|
@ -322,10 +321,12 @@ class GenericListView<D> extends View {
|
|||
}
|
||||
|
||||
void _decelStart() {
|
||||
num currentTarget = scroller.verticalEnabled ?
|
||||
scroller.currentTarget.y : scroller.currentTarget.x;
|
||||
num current = scroller.verticalEnabled ?
|
||||
scroller.contentOffset.y : scroller.contentOffset.x;
|
||||
num currentTarget = scroller.verticalEnabled
|
||||
? scroller.currentTarget.y
|
||||
: scroller.currentTarget.x;
|
||||
num current = scroller.verticalEnabled
|
||||
? scroller.contentOffset.y
|
||||
: scroller.contentOffset.x;
|
||||
num targetIndex = _layout.getSnapIndex(currentTarget, _viewLength);
|
||||
if (current != currentTarget) {
|
||||
// The user is throwing rather than statically releasing.
|
||||
|
@ -333,8 +334,8 @@ class GenericListView<D> extends View {
|
|||
// as long as they made at least a minimal throw gesture.
|
||||
num currentIndex = _layout.getSnapIndex(current, _viewLength);
|
||||
if (currentIndex == targetIndex &&
|
||||
(currentTarget - current).abs() > SNAP_TO_NEXT_THROW_THRESHOLD &&
|
||||
-_layout.getOffset(targetIndex) != currentTarget) {
|
||||
(currentTarget - current).abs() > SNAP_TO_NEXT_THROW_THRESHOLD &&
|
||||
-_layout.getOffset(targetIndex) != currentTarget) {
|
||||
num snappedCurrentPosition = -_layout.getOffset(targetIndex);
|
||||
targetIndex = getNextIndex(targetIndex, currentTarget < current);
|
||||
}
|
||||
|
@ -349,7 +350,7 @@ class GenericListView<D> extends View {
|
|||
} else {
|
||||
// Update the target page only after we are all done animating.
|
||||
if (_pages != null) {
|
||||
_pages.target.value =_layout.getPage(targetIndex, _viewLength);
|
||||
_pages.target.value = _layout.getPage(targetIndex, _viewLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,16 +368,16 @@ class GenericListView<D> extends View {
|
|||
}
|
||||
|
||||
void _onPageSelected() {
|
||||
if (_pages.target !=
|
||||
_layout.getPage(_activeInterval.start, _viewLength)) {
|
||||
if (_pages.target != _layout.getPage(_activeInterval.start, _viewLength)) {
|
||||
_throwTo(_layout.getOffset(
|
||||
_layout.getPageStartIndex(_pages.target.value, _viewLength)));
|
||||
}
|
||||
}
|
||||
|
||||
num get _offset {
|
||||
return scroller.verticalEnabled ?
|
||||
scroller.getVerticalOffset() : scroller.getHorizontalOffset();
|
||||
return scroller.verticalEnabled
|
||||
? scroller.getVerticalOffset()
|
||||
: scroller.getHorizontalOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -396,12 +397,12 @@ class GenericListView<D> extends View {
|
|||
}
|
||||
|
||||
if (_pages != null) {
|
||||
_pages.current.value =
|
||||
_layout.getPage(targetInterval.start, _viewLength);
|
||||
_pages.current.value = _layout.getPage(targetInterval.start, _viewLength);
|
||||
}
|
||||
if (_pages != null) {
|
||||
_pages.length.value = _data.length > 0 ?
|
||||
_layout.getPage(_data.length - 1, _viewLength) + 1 : 0;
|
||||
_pages.length.value = _data.length > 0
|
||||
? _layout.getPage(_data.length - 1, _viewLength) + 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
if (!_removeClippedViews) {
|
||||
|
@ -418,23 +419,27 @@ class GenericListView<D> extends View {
|
|||
|
||||
// Remove views that are not needed anymore
|
||||
for (int i = _activeInterval.start,
|
||||
end = Math.min(targetInterval.start, _activeInterval.end);
|
||||
i < end; i++) {
|
||||
end = Math.min(targetInterval.start, _activeInterval.end);
|
||||
i < end;
|
||||
i++) {
|
||||
_removeView(i);
|
||||
}
|
||||
for (int i = Math.max(targetInterval.end, _activeInterval.start);
|
||||
i < _activeInterval.end; i++) {
|
||||
i < _activeInterval.end;
|
||||
i++) {
|
||||
_removeView(i);
|
||||
}
|
||||
|
||||
// Add new views
|
||||
for (int i = targetInterval.start,
|
||||
end = Math.min(_activeInterval.start, targetInterval.end);
|
||||
i < end; i++) {
|
||||
end = Math.min(_activeInterval.start, targetInterval.end);
|
||||
i < end;
|
||||
i++) {
|
||||
_addView(i);
|
||||
}
|
||||
for (int i = Math.max(_activeInterval.end, targetInterval.start);
|
||||
i < targetInterval.end; i++) {
|
||||
i < targetInterval.end;
|
||||
i++) {
|
||||
_addView(i);
|
||||
}
|
||||
|
||||
|
@ -462,7 +467,7 @@ class GenericListView<D> extends View {
|
|||
final view = _itemViews[index];
|
||||
_addViewHelper(view, index);
|
||||
childViewAdded(view);
|
||||
return view;
|
||||
return view;
|
||||
}
|
||||
|
||||
final view = _newView(index);
|
||||
|
@ -523,7 +528,7 @@ class GenericListView<D> extends View {
|
|||
currentPosition =
|
||||
FxUtil.computeRelativePosition(view.node, _containerElem);
|
||||
}
|
||||
assert (_itemViews[index] is _PlaceholderView);
|
||||
assert(_itemViews[index] is _PlaceholderView);
|
||||
view.enterDocument();
|
||||
_itemViews[index].node.replaceWith(view.node);
|
||||
_itemViews[index] = view;
|
||||
|
@ -531,7 +536,9 @@ class GenericListView<D> extends View {
|
|||
FxUtil.setTranslate(view.node, currentPosition.x, currentPosition.y, 0);
|
||||
// The view's position is unchanged except now re-parented to
|
||||
// the list view.
|
||||
Timer.run(() { _positionSubview(view.node, index); });
|
||||
Timer.run(() {
|
||||
_positionSubview(view.node, index);
|
||||
});
|
||||
} else {
|
||||
_positionSubview(view.node, index);
|
||||
}
|
||||
|
@ -551,7 +558,7 @@ class GenericListView<D> extends View {
|
|||
}
|
||||
|
||||
void _positionSubview(Element node, int index) {
|
||||
if (_vertical) {
|
||||
if (_vertical) {
|
||||
FxUtil.setTranslate(node, 0, _layout.getOffset(index), 0);
|
||||
} else {
|
||||
FxUtil.setTranslate(node, _layout.getOffset(index), 0, 0);
|
||||
|
@ -609,8 +616,8 @@ class FixedSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
List<D> _data;
|
||||
bool _paginate;
|
||||
|
||||
FixedSizeListViewLayout(this.itemViewFactory, this._data, this._vertical,
|
||||
this._paginate);
|
||||
FixedSizeListViewLayout(
|
||||
this.itemViewFactory, this._data, this._vertical, this._paginate);
|
||||
|
||||
void onDataChange() {}
|
||||
|
||||
|
@ -622,7 +629,6 @@ class FixedSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
return _vertical ? itemViewFactory.height : itemViewFactory.width;
|
||||
}
|
||||
|
||||
|
||||
int getWidth(int viewLength) {
|
||||
return _vertical ? itemViewFactory.width : getLength(viewLength);
|
||||
}
|
||||
|
@ -647,15 +653,14 @@ class FixedSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
}
|
||||
|
||||
int getLength(int viewLength) {
|
||||
int itemLength =
|
||||
_vertical ? itemViewFactory.height : itemViewFactory.width;
|
||||
int itemLength = _vertical ? itemViewFactory.height : itemViewFactory.width;
|
||||
if (viewLength == null || viewLength == 0) {
|
||||
return itemLength * _data.length;
|
||||
} else if (_paginate) {
|
||||
if (_data.length > 0) {
|
||||
final pageLength = getPageLength(viewLength);
|
||||
return getPage(_data.length - 1, viewLength)
|
||||
* pageLength + Math.max(viewLength, pageLength);
|
||||
return getPage(_data.length - 1, viewLength) * pageLength +
|
||||
Math.max(viewLength, pageLength);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -705,23 +710,30 @@ class FixedSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
* Simple list view class where each item has fixed width and height.
|
||||
*/
|
||||
class ListView<D> extends GenericListView<D> {
|
||||
|
||||
/**
|
||||
* Creates a new ListView for the given data. If [:_data:] is an
|
||||
* [:ObservableList<T>:] then it will listen to changes to the list and
|
||||
* update the view appropriately.
|
||||
*/
|
||||
ListView(List<D> data, ViewFactory<D> itemViewFactory, bool scrollable,
|
||||
bool vertical, ObservableValue<D> selectedItem,
|
||||
[bool snapToItems = false,
|
||||
bool paginate = false,
|
||||
bool removeClippedViews = false,
|
||||
bool showScrollbar = false,
|
||||
PageState pages = null])
|
||||
: super(new FixedSizeListViewLayout<D>(itemViewFactory, data, vertical,
|
||||
paginate),
|
||||
data, scrollable, vertical, selectedItem, snapToItems, paginate,
|
||||
removeClippedViews, showScrollbar, pages);
|
||||
bool vertical, ObservableValue<D> selectedItem,
|
||||
[bool snapToItems = false,
|
||||
bool paginate = false,
|
||||
bool removeClippedViews = false,
|
||||
bool showScrollbar = false,
|
||||
PageState pages = null])
|
||||
: super(
|
||||
new FixedSizeListViewLayout<D>(
|
||||
itemViewFactory, data, vertical, paginate),
|
||||
data,
|
||||
scrollable,
|
||||
vertical,
|
||||
selectedItem,
|
||||
snapToItems,
|
||||
paginate,
|
||||
removeClippedViews,
|
||||
showScrollbar,
|
||||
pages);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -738,18 +750,18 @@ class VariableSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
VariableSizeViewFactory<D> itemViewFactory;
|
||||
Interval _lastVisibleInterval;
|
||||
|
||||
VariableSizeListViewLayout(this.itemViewFactory, data, this._vertical,
|
||||
this._paginate) :
|
||||
_data = data,
|
||||
_lastVisibleInterval = new Interval(0, 0) {
|
||||
VariableSizeListViewLayout(
|
||||
this.itemViewFactory, data, this._vertical, this._paginate)
|
||||
: _data = data,
|
||||
_lastVisibleInterval = new Interval(0, 0) {
|
||||
_itemOffsets = <int>[];
|
||||
_lengths = <int>[];
|
||||
_itemOffsets.add(0);
|
||||
}
|
||||
|
||||
void onDataChange() {
|
||||
_itemOffsets.clear();
|
||||
_itemOffsets.add(0);
|
||||
_itemOffsets.clear();
|
||||
_itemOffsets.add(0);
|
||||
_lengths.clear();
|
||||
}
|
||||
|
||||
|
@ -800,12 +812,12 @@ class VariableSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
num lengthFromAllButLastElement = 0;
|
||||
if (_itemOffsets.length > 2) {
|
||||
lengthFromAllButLastElement =
|
||||
(getOffset(_itemOffsets.length - 2) -
|
||||
getOffset(0)) *
|
||||
(_data.length / (_itemOffsets.length - 2));
|
||||
(getOffset(_itemOffsets.length - 2) - getOffset(0)) *
|
||||
(_data.length / (_itemOffsets.length - 2));
|
||||
}
|
||||
return (lengthFromAllButLastElement +
|
||||
Math.max(viewLength, _lengths[_lengths.length - 1])).toInt();
|
||||
Math.max(viewLength, _lengths[_lengths.length - 1]))
|
||||
.toInt();
|
||||
} else {
|
||||
if (_lengths.length == 1) {
|
||||
return Math.max(viewLength, _lengths[0]);
|
||||
|
@ -821,7 +833,7 @@ class VariableSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
} else {
|
||||
// Hack so that _lengths[length - 1] is available.
|
||||
getOffset(_data.length);
|
||||
return (getOffset(_data.length - 1) - getOffset(0)) +
|
||||
return (getOffset(_data.length - 1) - getOffset(0)) +
|
||||
Math.max(_lengths[_lengths.length - 1], viewLength);
|
||||
}
|
||||
}
|
||||
|
@ -830,7 +842,8 @@ class VariableSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
if (index >= _itemOffsets.length) {
|
||||
int offset = _itemOffsets[_itemOffsets.length - 1];
|
||||
for (int i = _itemOffsets.length; i <= index; i++) {
|
||||
int length = _vertical ? itemViewFactory.getHeight(_data[i - 1])
|
||||
int length = _vertical
|
||||
? itemViewFactory.getHeight(_data[i - 1])
|
||||
: itemViewFactory.getWidth(_data[i - 1]);
|
||||
offset += length;
|
||||
_itemOffsets.add(offset);
|
||||
|
@ -860,13 +873,11 @@ class VariableSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
}
|
||||
|
||||
Interval computeVisibleInterval(
|
||||
num offset, num viewLength, num bufferLength) {
|
||||
num offset, num viewLength, num bufferLength) {
|
||||
offset = offset.toInt();
|
||||
int start = _findFirstItemBefore(
|
||||
-offset - bufferLength,
|
||||
int start = _findFirstItemBefore(-offset - bufferLength,
|
||||
_lastVisibleInterval != null ? _lastVisibleInterval.start : 0);
|
||||
int end = _findFirstItemAfter(
|
||||
-offset + viewLength + bufferLength,
|
||||
int end = _findFirstItemAfter(-offset + viewLength + bufferLength,
|
||||
_lastVisibleInterval != null ? _lastVisibleInterval.end : 0);
|
||||
_lastVisibleInterval = new Interval(start, Math.max(start, end));
|
||||
_lastOffset = offset;
|
||||
|
@ -896,21 +907,25 @@ class VariableSizeListViewLayout<D> implements ListViewLayout<D> {
|
|||
}
|
||||
|
||||
class VariableSizeListView<D> extends GenericListView<D> {
|
||||
|
||||
VariableSizeListView(List<D> data,
|
||||
VariableSizeViewFactory<D> itemViewFactory,
|
||||
bool scrollable,
|
||||
bool vertical,
|
||||
ObservableValue<D> selectedItem,
|
||||
[bool snapToItems = false,
|
||||
bool paginate = false,
|
||||
bool removeClippedViews = false,
|
||||
bool showScrollbar = false,
|
||||
PageState pages = null])
|
||||
: super(new VariableSizeListViewLayout(itemViewFactory, data, vertical,
|
||||
paginate),
|
||||
data, scrollable, vertical, selectedItem, snapToItems,
|
||||
paginate, removeClippedViews, showScrollbar, pages);
|
||||
VariableSizeListView(List<D> data, VariableSizeViewFactory<D> itemViewFactory,
|
||||
bool scrollable, bool vertical, ObservableValue<D> selectedItem,
|
||||
[bool snapToItems = false,
|
||||
bool paginate = false,
|
||||
bool removeClippedViews = false,
|
||||
bool showScrollbar = false,
|
||||
PageState pages = null])
|
||||
: super(
|
||||
new VariableSizeListViewLayout(
|
||||
itemViewFactory, data, vertical, paginate),
|
||||
data,
|
||||
scrollable,
|
||||
vertical,
|
||||
selectedItem,
|
||||
snapToItems,
|
||||
paginate,
|
||||
removeClippedViews,
|
||||
showScrollbar,
|
||||
pages);
|
||||
}
|
||||
|
||||
/** A back button that is equivalent to clicking "back" in the browser. */
|
||||
|
@ -924,7 +939,6 @@ class BackButton extends View {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO(terry): Maybe should be part of ButtonView class in appstack/view?
|
||||
/** OS button. */
|
||||
class PushButtonView extends View {
|
||||
|
@ -943,7 +957,6 @@ class PushButtonView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO(terry): Add a drop shadow around edge and corners need to be rounded.
|
||||
// Need to support conveyor for contents of dialog so it's not
|
||||
// larger than the parent window.
|
||||
|
@ -968,8 +981,8 @@ class DialogView extends View {
|
|||
</div>
|
||||
</div>''');
|
||||
|
||||
_done = new PushButtonView('Done', 'done-button',
|
||||
EventBatch.wrap((e) => onDone()));
|
||||
_done = new PushButtonView(
|
||||
'Done', 'done-button', EventBatch.wrap((e) => onDone()));
|
||||
final titleArea = node.querySelector('.dialog-title-area');
|
||||
titleArea.nodes.add(_done.node);
|
||||
|
||||
|
@ -980,5 +993,5 @@ class DialogView extends View {
|
|||
}
|
||||
|
||||
/** Override to handle dialog done. */
|
||||
void onDone() { }
|
||||
void onDone() {}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,7 @@ class AnimationScheduler {
|
|||
CssStyleDeclaration _safariHackStyle;
|
||||
int _frameCount = 0;
|
||||
|
||||
AnimationScheduler()
|
||||
: _callbacks = new List<CallbackData>() {
|
||||
AnimationScheduler() : _callbacks = new List<CallbackData>() {
|
||||
if (_isMobileSafari) {
|
||||
// TODO(jacobr): find a better workaround for the issue that 3d transforms
|
||||
// sometimes don't render on iOS without forcing a layout.
|
||||
|
@ -72,8 +71,7 @@ class AnimationScheduler {
|
|||
* pending callback.
|
||||
*/
|
||||
int requestAnimationFrame(AnimationCallback callback,
|
||||
[Element element = null,
|
||||
num minTime = null]) {
|
||||
[Element element = null, num minTime = null]) {
|
||||
final callbackData = new CallbackData(callback, minTime);
|
||||
_requestAnimationFrameHelper(callbackData);
|
||||
return callbackData.id;
|
||||
|
@ -85,8 +83,9 @@ class AnimationScheduler {
|
|||
}
|
||||
|
||||
void _setupInterval() {
|
||||
window.requestAnimationFrame(
|
||||
(num ignored) { _step(); });
|
||||
window.requestAnimationFrame((num ignored) {
|
||||
_step();
|
||||
});
|
||||
}
|
||||
|
||||
void _step() {
|
||||
|
|
|
@ -10,7 +10,6 @@ part of base;
|
|||
* Utils for device detection.
|
||||
*/
|
||||
class Device {
|
||||
|
||||
/**
|
||||
* The regular expression for detecting an iPhone or iPod.
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,9 @@ class Dom {
|
|||
Timer.run(f);
|
||||
} else {
|
||||
// TODO(jacobr): give this event a named property.
|
||||
window.onContentLoaded.listen((Event e) { f(); });
|
||||
window.onContentLoaded.listen((Event e) {
|
||||
f();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,7 @@ class Env {
|
|||
* cancel the pending callback.
|
||||
*/
|
||||
static int requestAnimationFrame(AnimationCallback callback,
|
||||
[Element element = null,
|
||||
num minTime = null]) {
|
||||
[Element element = null, num minTime = null]) {
|
||||
if (_animationScheduler == null) {
|
||||
_animationScheduler = new AnimationScheduler();
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@ class Size {
|
|||
num width;
|
||||
num height;
|
||||
|
||||
Size(num this.width, num this.height) {
|
||||
}
|
||||
Size(num this.width, num this.height) {}
|
||||
|
||||
bool operator ==(Size other) {
|
||||
return other != null && width == other.width && height == other.height;
|
||||
|
@ -127,8 +126,9 @@ class Size {
|
|||
* Returns this Size object, after optional scaling.
|
||||
*/
|
||||
Size scaleToFit(Size target) {
|
||||
num s = aspectRatio() > target.aspectRatio() ?
|
||||
target.width / width : target.height / height;
|
||||
num s = aspectRatio() > target.aspectRatio()
|
||||
? target.width / width
|
||||
: target.height / height;
|
||||
return scale(s);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library base;
|
||||
|
||||
import 'dart:html';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
|
|
@ -43,7 +43,6 @@ part of layout;
|
|||
// - Optimize for the case of no content sized tracks
|
||||
// - Optimize for the "incremental update" cases
|
||||
class GridLayout extends ViewLayout {
|
||||
|
||||
/** Configuration parameters defined in CSS. */
|
||||
final GridTrackList rows;
|
||||
final GridTrackList columns;
|
||||
|
@ -72,22 +71,18 @@ class GridLayout extends ViewLayout {
|
|||
Dimension _dimension;
|
||||
|
||||
GridLayout(Positionable view)
|
||||
: super(view),
|
||||
rows = _GridTrackParser.parse(view.customStyle['grid-rows']),
|
||||
columns = _GridTrackParser.parse(view.customStyle['grid-columns']),
|
||||
template = _GridTemplateParser.parse(view.customStyle['grid-template']),
|
||||
|
||||
rowSizing = _GridTrackParser.parseTrackSizing(
|
||||
view.customStyle['grid-row-sizing']),
|
||||
|
||||
columnSizing = _GridTrackParser.parseTrackSizing(
|
||||
view.customStyle['grid-column-sizing']) {
|
||||
|
||||
: super(view),
|
||||
rows = _GridTrackParser.parse(view.customStyle['grid-rows']),
|
||||
columns = _GridTrackParser.parse(view.customStyle['grid-columns']),
|
||||
template = _GridTemplateParser.parse(view.customStyle['grid-template']),
|
||||
rowSizing = _GridTrackParser
|
||||
.parseTrackSizing(view.customStyle['grid-row-sizing']),
|
||||
columnSizing = _GridTrackParser
|
||||
.parseTrackSizing(view.customStyle['grid-column-sizing']) {
|
||||
_rowTracks = rows != null ? rows.tracks : new List<GridTrack>();
|
||||
_columnTracks = columns != null ? columns.tracks : new List<GridTrack>();
|
||||
}
|
||||
|
||||
|
||||
int get currentWidth => _gridWidth;
|
||||
int get currentHeight => _gridHeight;
|
||||
|
||||
|
@ -152,11 +147,8 @@ class GridLayout extends ViewLayout {
|
|||
*/
|
||||
// Note: spec does not correctly doc all the parameters to this function.
|
||||
void _computeUsedBreadthOfTracks(List<GridTrack> tracks) {
|
||||
|
||||
// TODO(jmesserly): as a performance optimization we could cache this
|
||||
final items = view.childViews
|
||||
.map((view_) => view_.layout)
|
||||
.toList();
|
||||
final items = view.childViews.map((view_) => view_.layout).toList();
|
||||
CollectionUtils.sortBy(items, (item) => _getSpanCount(item));
|
||||
|
||||
// 1. Initialize per Grid Track variables
|
||||
|
@ -192,8 +184,8 @@ class GridLayout extends ViewLayout {
|
|||
// maxBreadth value until RemainingSpace is exhausted.
|
||||
// Note: it's not spec'd what to pass as the accumulator, but usedBreadth
|
||||
// seems right.
|
||||
_distributeSpaceToTracks(tracks, _getRemainingSpace(tracks),
|
||||
USED_BREADTH, false);
|
||||
_distributeSpaceToTracks(
|
||||
tracks, _getRemainingSpace(tracks), USED_BREADTH, false);
|
||||
|
||||
// Spec wording is confusing about which direction this assignment happens,
|
||||
// but this is the way that makes sense.
|
||||
|
@ -204,8 +196,8 @@ class GridLayout extends ViewLayout {
|
|||
// 6. Grow all Grid Tracks having a fraction as their maxSizing
|
||||
final tempBreadth = _calcNormalizedFractionBreadth(tracks);
|
||||
for (final t in tracks) {
|
||||
t.usedBreadth = Math.max(t.usedBreadth,
|
||||
tempBreadth * t.maxSizing.fractionValue);
|
||||
t.usedBreadth =
|
||||
Math.max(t.usedBreadth, tempBreadth * t.maxSizing.fractionValue);
|
||||
}
|
||||
|
||||
_computeTrackPositions(tracks);
|
||||
|
@ -255,7 +247,6 @@ class GridLayout extends ViewLayout {
|
|||
* freeSpace less the sum of the current UsedBreadths.
|
||||
*/
|
||||
num _calcNormalizedFractionBreadth(List<GridTrack> tracks) {
|
||||
|
||||
final fractionTracks = tracks.where((t) => t.maxSizing.isFraction).toList();
|
||||
|
||||
// Note: the spec has various bugs in this function, such as mismatched
|
||||
|
@ -295,11 +286,10 @@ class GridLayout extends ViewLayout {
|
|||
*/
|
||||
void _distributeSpaceToTracks(List<GridTrack> tracks, num freeSpace,
|
||||
_BreadthAccumulator breadth, bool ignoreMaxBreadth) {
|
||||
|
||||
// TODO(jmesserly): in some cases it would be safe to sort the passed in
|
||||
// list in place. Not always though.
|
||||
tracks = CollectionUtils.orderBy(tracks,
|
||||
(t) => t.maxBreadth - breadth.getSize(t));
|
||||
tracks = CollectionUtils.orderBy(
|
||||
tracks, (t) => t.maxBreadth - breadth.getSize(t));
|
||||
|
||||
// Give each Grid Track an equal share of the space, but without exceeding
|
||||
// their maxBreadth values. Because there are different MaxBreadths
|
||||
|
@ -341,9 +331,10 @@ class GridLayout extends ViewLayout {
|
|||
*/
|
||||
void _distributeSpaceBySpanCount(List<ViewLayout> items,
|
||||
ContentSizeMode sizeMode, _BreadthAccumulator breadth) {
|
||||
|
||||
items = items.where((item) =>
|
||||
_hasContentSizedTracks(_getTracks(item), sizeMode, breadth)).toList();
|
||||
items = items
|
||||
.where((item) =>
|
||||
_hasContentSizedTracks(_getTracks(item), sizeMode, breadth))
|
||||
.toList();
|
||||
|
||||
var tracks = [];
|
||||
|
||||
|
@ -368,8 +359,7 @@ class GridLayout extends ViewLayout {
|
|||
|
||||
if (spanCountFinished) {
|
||||
for (final t in tracks) {
|
||||
breadth.setSize(t,
|
||||
Math.max(breadth.getSize(t), t.updatedBreadth));
|
||||
breadth.setSize(t, Math.max(breadth.getSize(t), t.updatedBreadth));
|
||||
}
|
||||
tracks = [];
|
||||
}
|
||||
|
@ -382,12 +372,10 @@ class GridLayout extends ViewLayout {
|
|||
*/
|
||||
static bool _hasContentSizedTracks(Iterable<GridTrack> tracks,
|
||||
ContentSizeMode sizeMode, _BreadthAccumulator breadth) {
|
||||
|
||||
for (final t in tracks) {
|
||||
final fn = breadth.getSizingFunction(t);
|
||||
if (sizeMode == ContentSizeMode.MAX && fn.isMaxContentSized ||
|
||||
sizeMode == ContentSizeMode.MIN && fn.isContentSized) {
|
||||
|
||||
// Make sure we don't cross a fractional track
|
||||
return tracks.length == 1 || !tracks.any((t_) => t_.isFractional);
|
||||
}
|
||||
|
@ -396,8 +384,8 @@ class GridLayout extends ViewLayout {
|
|||
}
|
||||
|
||||
/** Ensures that the numbered track exists. */
|
||||
void _ensureTrack(List<GridTrack> tracks, TrackSizing sizing,
|
||||
int start, int span) {
|
||||
void _ensureTrack(
|
||||
List<GridTrack> tracks, TrackSizing sizing, int start, int span) {
|
||||
// Start is 1-based. Make it 0-based.
|
||||
start -= 1;
|
||||
|
||||
|
@ -515,7 +503,8 @@ class GridLayout extends ViewLayout {
|
|||
|
||||
int _getSpanCount(ViewLayout item) {
|
||||
GridLayoutParams childLayout = item.layoutParams;
|
||||
return (_dimension == Dimension.WIDTH ?
|
||||
childLayout.columnSpan : childLayout.rowSpan);
|
||||
return (_dimension == Dimension.WIDTH
|
||||
? childLayout.columnSpan
|
||||
: childLayout.rowSpan);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,44 +24,41 @@ class GridLayoutParams extends LayoutParams {
|
|||
GridItemAlignment rowAlign;
|
||||
GridItemAlignment columnAlign;
|
||||
|
||||
GridLayoutParams(Positionable view, GridLayout layout)
|
||||
: super(view.node) {
|
||||
|
||||
GridLayoutParams(Positionable view, GridLayout layout) : super(view.node) {
|
||||
// TODO(jmesserly): this can be cleaned up a lot by just passing "view"
|
||||
// into the parsers.
|
||||
|
||||
rowAlign = new GridItemAlignment.fromString(
|
||||
view.customStyle['grid-row-align']);
|
||||
columnAlign = new GridItemAlignment.fromString(
|
||||
view.customStyle['grid-column-align']);
|
||||
rowAlign =
|
||||
new GridItemAlignment.fromString(view.customStyle['grid-row-align']);
|
||||
columnAlign =
|
||||
new GridItemAlignment.fromString(view.customStyle['grid-column-align']);
|
||||
|
||||
layer = StringUtils.parseInt(view.customStyle['grid-layer'], 0);
|
||||
|
||||
rowSpan = StringUtils.parseInt(view.customStyle['grid-row-span']);
|
||||
columnSpan = StringUtils.parseInt(view.customStyle['grid-column-span']);
|
||||
|
||||
var line = _GridItemParser.parse(
|
||||
view.customStyle['grid-row'], layout.rows);
|
||||
var line = _GridItemParser.parse(view.customStyle['grid-row'], layout.rows);
|
||||
if (line != null) {
|
||||
row = line.start;
|
||||
if (line.length != null) {
|
||||
if (rowSpan != null) {
|
||||
throw new UnsupportedError(
|
||||
'grid-row-span cannot be with grid-row that defines an end');
|
||||
'grid-row-span cannot be with grid-row that defines an end');
|
||||
}
|
||||
rowSpan = line.length;
|
||||
}
|
||||
}
|
||||
|
||||
line = _GridItemParser.parse(
|
||||
view.customStyle['grid-column'], layout.columns);
|
||||
line =
|
||||
_GridItemParser.parse(view.customStyle['grid-column'], layout.columns);
|
||||
|
||||
if (line != null) {
|
||||
column = line.start;
|
||||
if (line.length != null) {
|
||||
if (columnSpan != null) {
|
||||
throw new UnsupportedError(
|
||||
'grid-column-span cannot be with grid-column that defines an end');
|
||||
'grid-column-span cannot be with grid-column that defines an end');
|
||||
}
|
||||
columnSpan = line.length;
|
||||
}
|
||||
|
@ -73,8 +70,10 @@ class GridLayoutParams extends LayoutParams {
|
|||
// error handling. For now, throw an error on a misconfigured view.
|
||||
// CSS is designed to be a permissive language, though, so we should do
|
||||
// better and resolve conflicts more intelligently.
|
||||
if (row != null || column != null ||
|
||||
rowSpan != null || columnSpan != null) {
|
||||
if (row != null ||
|
||||
column != null ||
|
||||
rowSpan != null ||
|
||||
columnSpan != null) {
|
||||
throw new UnsupportedError(
|
||||
'grid-cell cannot be used with grid-row and grid-column');
|
||||
}
|
||||
|
@ -89,15 +88,14 @@ class GridLayoutParams extends LayoutParams {
|
|||
column = rect.column;
|
||||
rowSpan = rect.rowSpan;
|
||||
columnSpan = rect.columnSpan;
|
||||
|
||||
} else {
|
||||
// Apply default row, column span values.
|
||||
if (rowSpan == null) rowSpan = 1;
|
||||
if (columnSpan == null) columnSpan = 1;
|
||||
|
||||
if (row == null && column == null) {
|
||||
throw new UnsupportedError('grid-flow is not implemented'
|
||||
+ ' so at least one row or one column must be defined');
|
||||
throw new UnsupportedError('grid-flow is not implemented' +
|
||||
' so at least one row or one column must be defined');
|
||||
}
|
||||
|
||||
if (row == null) row = 1;
|
||||
|
|
|
@ -13,18 +13,18 @@ class _Parser {
|
|||
|
||||
// TODO(jmesserly): shouldn't need this optimization, but dart_json parser
|
||||
// found that they needed this.
|
||||
static const A_BIG = 65; // 'A'.codeUnitAt(0)
|
||||
static const Z_BIG = 90; // 'Z'.codeUnitAt(0)
|
||||
static const A_SMALL = 97; // 'a'.codeUnitAt(0)
|
||||
static const Z_SMALL = 122; // 'z'.codeUnitAt(0)
|
||||
static const TAB = 9; // '\t'.codeUnitAt(0)
|
||||
static const NEW_LINE = 10; // '\n'.codeUnitAt(0)
|
||||
static const LINE_FEED = 13; // '\r'.codeUnitAt(0)
|
||||
static const SPACE = 32; // ' '.codeUnitAt(0)
|
||||
static const ZERO = 48; // '0'.codeUnitAt(0)
|
||||
static const NINE = 57; // '9'.codeUnitAt(0)
|
||||
static const DOT = 46; // '.'.codeUnitAt(0)
|
||||
static const R_PAREN = 41; // ')'.codeUnitAt(0)
|
||||
static const A_BIG = 65; // 'A'.codeUnitAt(0)
|
||||
static const Z_BIG = 90; // 'Z'.codeUnitAt(0)
|
||||
static const A_SMALL = 97; // 'a'.codeUnitAt(0)
|
||||
static const Z_SMALL = 122; // 'z'.codeUnitAt(0)
|
||||
static const TAB = 9; // '\t'.codeUnitAt(0)
|
||||
static const NEW_LINE = 10; // '\n'.codeUnitAt(0)
|
||||
static const LINE_FEED = 13; // '\r'.codeUnitAt(0)
|
||||
static const SPACE = 32; // ' '.codeUnitAt(0)
|
||||
static const ZERO = 48; // '0'.codeUnitAt(0)
|
||||
static const NINE = 57; // '9'.codeUnitAt(0)
|
||||
static const DOT = 46; // '.'.codeUnitAt(0)
|
||||
static const R_PAREN = 41; // ')'.codeUnitAt(0)
|
||||
|
||||
final String _src;
|
||||
int _offset;
|
||||
|
@ -74,8 +74,8 @@ class _Parser {
|
|||
}
|
||||
|
||||
bool _maybeEatMultiLineComment() {
|
||||
if (_maybeEat('/*', /*eatWhitespace:*/false)) {
|
||||
while (!_maybeEat('*/', /*eatWhitespace:*/false)) {
|
||||
if (_maybeEat('/*', /*eatWhitespace:*/ false)) {
|
||||
while (!_maybeEat('*/', /*eatWhitespace:*/ false)) {
|
||||
if (_offset >= length) {
|
||||
_error('expected */');
|
||||
}
|
||||
|
@ -330,9 +330,9 @@ class _GridTrackParser extends _Parser {
|
|||
final Map<String, int> _lineNames;
|
||||
|
||||
_GridTrackParser._internal(String src)
|
||||
: super(src),
|
||||
_tracks = new List<GridTrack>(),
|
||||
_lineNames = new Map<String, int>();
|
||||
: super(src),
|
||||
_tracks = new List<GridTrack>(),
|
||||
_lineNames = new Map<String, int>();
|
||||
|
||||
/** Parses the grid-rows and grid-columns CSS properties into object form. */
|
||||
static GridTrackList parse(String str) {
|
||||
|
@ -465,7 +465,6 @@ class _GridTrackParser extends _Parser {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exception thrown because the grid style properties had incorrect values.
|
||||
*/
|
||||
|
|
|
@ -24,7 +24,6 @@ class GridTrackList {
|
|||
GridTrackList(this.tracks, this.lineNames) {}
|
||||
}
|
||||
|
||||
|
||||
/** Represents a row or a column. */
|
||||
class GridTrack {
|
||||
/**
|
||||
|
@ -62,7 +61,6 @@ class GridTrack {
|
|||
bool get isFractional => minSizing.isFraction || maxSizing.isFraction;
|
||||
}
|
||||
|
||||
|
||||
/** Represents the grid-row-align or grid-column-align. */
|
||||
class GridItemAlignment {
|
||||
// TODO(jmesserly): should this be stored as an int for performance?
|
||||
|
@ -70,14 +68,15 @@ class GridItemAlignment {
|
|||
|
||||
// 'start' | 'end' | 'center' | 'stretch'
|
||||
GridItemAlignment.fromString(String value)
|
||||
: this.value = (value == null) ? 'stretch' : value {
|
||||
|
||||
switch (this.value) {
|
||||
case 'start': case 'end': case 'center': case 'stretch':
|
||||
: this.value = (value == null) ? 'stretch' : value {
|
||||
switch (this.value) {
|
||||
case 'start':
|
||||
case 'end':
|
||||
case 'center':
|
||||
case 'stretch':
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedError(
|
||||
'invalid row/column alignment "$value"');
|
||||
throw new UnsupportedError('invalid row/column alignment "$value"');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +97,6 @@ class GridItemAlignment {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a grid-template. Used in conjunction with a grid-cell to
|
||||
* place cells in the grid, without needing to specify the exact row/column.
|
||||
|
@ -108,8 +106,8 @@ class GridTemplate {
|
|||
final int _numRows;
|
||||
|
||||
GridTemplate(List<String> rows)
|
||||
: _rects = new Map<int, _GridTemplateRect>(),
|
||||
_numRows = rows.length {
|
||||
: _rects = new Map<int, _GridTemplateRect>(),
|
||||
_numRows = rows.length {
|
||||
_buildRects(rows);
|
||||
}
|
||||
|
||||
|
@ -145,7 +143,7 @@ class GridTemplate {
|
|||
final rect = _rects[cell.codeUnitAt(0)];
|
||||
if (rect == null) {
|
||||
throw new UnsupportedError(
|
||||
'grid-cell "$cell" not found in parent\'s grid-template');
|
||||
'grid-cell "$cell" not found in parent\'s grid-template');
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
@ -155,10 +153,12 @@ class GridTemplate {
|
|||
class _GridTemplateRect {
|
||||
int row, column, rowSpan, columnSpan, _count, _char;
|
||||
_GridTemplateRect(this._char, this.row, this.column)
|
||||
: rowSpan = 1, columnSpan = 1, _count = 1 {}
|
||||
: rowSpan = 1,
|
||||
columnSpan = 1,
|
||||
_count = 1 {}
|
||||
|
||||
void add(int r, int c) {
|
||||
assert (r >= row && c >= column);
|
||||
assert(r >= row && c >= column);
|
||||
_count++;
|
||||
rowSpan = Math.max(rowSpan, r - row + 1);
|
||||
columnSpan = Math.max(columnSpan, c - column + 1);
|
||||
|
@ -176,7 +176,6 @@ class _GridTemplateRect {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to return a row/column and span during parsing of grid-row and
|
||||
* grid-column during parsing.
|
||||
|
|
|
@ -42,8 +42,8 @@ class FixedSizing extends SizingFunction {
|
|||
bool _contentSized;
|
||||
|
||||
FixedSizing(this.length, [this.units = 'px'])
|
||||
: super(),
|
||||
_contentSized = false {
|
||||
: super(),
|
||||
_contentSized = false {
|
||||
if (units != 'px' && units != '%') {
|
||||
// TODO(jmesserly): support other unit types
|
||||
throw new UnsupportedError('Units other than px and %');
|
||||
|
@ -73,7 +73,6 @@ class FixedSizing extends SizingFunction {
|
|||
String toString() => 'FixedSizing: ${length}${units} $_contentSized';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fraction is a non-negative floating-point number followed by 'fr'. Each
|
||||
* fraction value takes a share of the remaining space proportional to its
|
||||
|
@ -99,7 +98,9 @@ class MinContentSizing extends SizingFunction {
|
|||
class MaxContentSizing extends SizingFunction {
|
||||
const MaxContentSizing() : super();
|
||||
|
||||
bool get isMaxContentSized { return true; }
|
||||
bool get isMaxContentSized {
|
||||
return true;
|
||||
}
|
||||
|
||||
String toString() => 'MaxContentSizing';
|
||||
}
|
||||
|
@ -113,8 +114,8 @@ class TrackSizing {
|
|||
final SizingFunction max;
|
||||
|
||||
const TrackSizing.auto()
|
||||
: min = const MinContentSizing(),
|
||||
max = const MaxContentSizing();
|
||||
: min = const MinContentSizing(),
|
||||
max = const MaxContentSizing();
|
||||
|
||||
TrackSizing(this.min, this.max) {}
|
||||
|
||||
|
@ -134,7 +135,10 @@ abstract class _BreadthAccumulator {
|
|||
class _UsedBreadthAccumulator implements _BreadthAccumulator {
|
||||
const _UsedBreadthAccumulator();
|
||||
|
||||
void setSize(GridTrack t, num value) { t.usedBreadth = value; }
|
||||
void setSize(GridTrack t, num value) {
|
||||
t.usedBreadth = value;
|
||||
}
|
||||
|
||||
num getSize(GridTrack t) => t.usedBreadth;
|
||||
|
||||
SizingFunction getSizingFunction(GridTrack t) => t.minSizing;
|
||||
|
@ -143,9 +147,11 @@ class _UsedBreadthAccumulator implements _BreadthAccumulator {
|
|||
class _MaxBreadthAccumulator implements _BreadthAccumulator {
|
||||
const _MaxBreadthAccumulator();
|
||||
|
||||
void setSize(GridTrack t, num value) { t.maxBreadth = value; }
|
||||
void setSize(GridTrack t, num value) {
|
||||
t.maxBreadth = value;
|
||||
}
|
||||
|
||||
num getSize(GridTrack t) => t.maxBreadth;
|
||||
|
||||
SizingFunction getSizingFunction(GridTrack t) => t.maxSizing;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ abstract class Positionable {
|
|||
void doLayout();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Caches the layout parameters that were specified in CSS during a layout
|
||||
* computation. These values are immutable during a layout.
|
||||
|
@ -128,8 +127,7 @@ class ViewLayout {
|
|||
int get borderHeight => borderTopWidth + borderBottomWidth;
|
||||
|
||||
/** Implements the custom layout computation. */
|
||||
void measureLayout(Future<Size> size, Completer<bool> changed) {
|
||||
}
|
||||
void measureLayout(Future<Size> size, Completer<bool> changed) {}
|
||||
|
||||
/**
|
||||
* Positions the view within its parent container.
|
||||
|
@ -182,7 +180,7 @@ class ViewLayout {
|
|||
}
|
||||
|
||||
int measureContent(ViewLayout parent, Dimension dimension,
|
||||
[ContentSizeMode mode = null]) {
|
||||
[ContentSizeMode mode = null]) {
|
||||
if (dimension == Dimension.WIDTH) {
|
||||
return measureWidth(parent, mode);
|
||||
} else if (dimension == Dimension.HEIGHT) {
|
||||
|
@ -193,11 +191,9 @@ class ViewLayout {
|
|||
int measureWidth(ViewLayout parent, ContentSizeMode mode) {
|
||||
final style = layoutParams.style;
|
||||
if (mode == ContentSizeMode.MIN) {
|
||||
return _styleToPixels(
|
||||
style.minWidth, currentWidth, parent.currentWidth);
|
||||
return _styleToPixels(style.minWidth, currentWidth, parent.currentWidth);
|
||||
} else if (mode == ContentSizeMode.MAX) {
|
||||
return _styleToPixels(
|
||||
style.maxWidth, currentWidth, parent.currentWidth);
|
||||
return _styleToPixels(style.maxWidth, currentWidth, parent.currentWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,16 +47,21 @@ class ChangeEvent {
|
|||
/** Factory constructor for property change events. */
|
||||
ChangeEvent.property(
|
||||
this.target, this.propertyName, this.newValue, this.oldValue)
|
||||
: type = UPDATE, index = null;
|
||||
: type = UPDATE,
|
||||
index = null;
|
||||
|
||||
/** Factory constructor for list change events. */
|
||||
ChangeEvent.list(
|
||||
this.target, this.type, this.index, this.newValue, this.oldValue)
|
||||
: propertyName = null;
|
||||
: propertyName = null;
|
||||
|
||||
/** Factory constructor for [GLOBAL] change events. */
|
||||
ChangeEvent.global(this.target)
|
||||
: type = GLOBAL, newValue = null, oldValue = null, propertyName = null, index = null;
|
||||
: type = GLOBAL,
|
||||
newValue = null,
|
||||
oldValue = null,
|
||||
propertyName = null,
|
||||
index = null;
|
||||
}
|
||||
|
||||
/** A collection of change events on a single observable instance. */
|
||||
|
|
|
@ -11,7 +11,6 @@ part of observable;
|
|||
* AbstractObservable, which works with this class to implement batching.
|
||||
*/
|
||||
class EventBatch {
|
||||
|
||||
/** The current active batch, if any. */
|
||||
static EventBatch current;
|
||||
|
||||
|
@ -88,7 +87,7 @@ class EventBatch {
|
|||
int uid = obj.uid;
|
||||
EventSummary summary = summaries[uid];
|
||||
if (summary == null) {
|
||||
assert (!sealed);
|
||||
assert(!sealed);
|
||||
summary = new EventSummary(obj);
|
||||
summaries[uid] = summary;
|
||||
}
|
||||
|
|
|
@ -35,10 +35,8 @@ abstract class Observable {
|
|||
bool removeChangeListener(ChangeListener listener);
|
||||
}
|
||||
|
||||
|
||||
/** Common functionality for observable objects. */
|
||||
class AbstractObservable implements Observable {
|
||||
|
||||
/** Unique id to identify this model in an event batch. */
|
||||
final int uid;
|
||||
|
||||
|
@ -59,8 +57,8 @@ class AbstractObservable implements Observable {
|
|||
}
|
||||
|
||||
AbstractObservable([Observable this.parent = null])
|
||||
: uid = EventBatch.genUid(),
|
||||
listeners = new List<ChangeListener>();
|
||||
: uid = EventBatch.genUid(),
|
||||
listeners = new List<ChangeListener>();
|
||||
|
||||
bool addChangeListener(ChangeListener listener) {
|
||||
if (listeners.indexOf(listener, 0) == -1) {
|
||||
|
@ -82,8 +80,8 @@ class AbstractObservable implements Observable {
|
|||
}
|
||||
|
||||
void recordPropertyUpdate(String propertyName, newValue, oldValue) {
|
||||
recordEvent(new ChangeEvent.property(
|
||||
this, propertyName, newValue, oldValue));
|
||||
recordEvent(
|
||||
new ChangeEvent.property(this, propertyName, newValue, oldValue));
|
||||
}
|
||||
|
||||
void recordListUpdate(int index, newValue, oldValue) {
|
||||
|
@ -92,13 +90,13 @@ class AbstractObservable implements Observable {
|
|||
}
|
||||
|
||||
void recordListInsert(int index, newValue) {
|
||||
recordEvent(new ChangeEvent.list(
|
||||
this, ChangeEvent.INSERT, index, newValue, null));
|
||||
recordEvent(
|
||||
new ChangeEvent.list(this, ChangeEvent.INSERT, index, newValue, null));
|
||||
}
|
||||
|
||||
void recordListRemove(int index, oldValue) {
|
||||
recordEvent(new ChangeEvent.list(
|
||||
this, ChangeEvent.REMOVE, index, null, oldValue));
|
||||
recordEvent(
|
||||
new ChangeEvent.list(this, ChangeEvent.REMOVE, index, null, oldValue));
|
||||
}
|
||||
|
||||
void recordGlobalChange() {
|
||||
|
@ -113,7 +111,7 @@ class AbstractObservable implements Observable {
|
|||
|
||||
if (EventBatch.current != null) {
|
||||
// Already in a batch, so just add it.
|
||||
assert (!EventBatch.current.sealed);
|
||||
assert(!EventBatch.current.sealed);
|
||||
// TODO(sigmund): measure the performance implications of this indirection
|
||||
// and consider whether caching the summary object in this instance helps.
|
||||
var summary = EventBatch.current.getEvents(this);
|
||||
|
@ -121,22 +119,23 @@ class AbstractObservable implements Observable {
|
|||
} else {
|
||||
// Not in a batch, so create a one-off one.
|
||||
// TODO(rnystrom): Needing to do ignore and (null) here is awkward.
|
||||
EventBatch.wrap((ignore) { recordEvent(event); })(null);
|
||||
EventBatch.wrap((ignore) {
|
||||
recordEvent(event);
|
||||
})(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A growable list that fires events when it's modified. */
|
||||
class ObservableList<T>
|
||||
extends AbstractObservable
|
||||
class ObservableList<T> extends AbstractObservable
|
||||
implements List<T>, Observable {
|
||||
|
||||
/** Underlying list. */
|
||||
// TODO(rnystrom): Make this final if we get list.remove().
|
||||
List<T> _internal;
|
||||
|
||||
ObservableList([Observable parent = null])
|
||||
: super(parent), _internal = new List<T>();
|
||||
: super(parent),
|
||||
_internal = new List<T>();
|
||||
|
||||
T operator [](int index) => _internal[index];
|
||||
|
||||
|
@ -246,7 +245,8 @@ class ObservableList<T>
|
|||
|
||||
if (srcStart < dstStart) {
|
||||
for (int i = srcStart + count - 1, j = dstStart + count - 1;
|
||||
i >= srcStart; i--, j--) {
|
||||
i >= srcStart;
|
||||
i--, j--) {
|
||||
dst[j] = src[i];
|
||||
}
|
||||
} else {
|
||||
|
@ -288,8 +288,8 @@ class ObservableList<T>
|
|||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
dynamic fold(var initialValue,
|
||||
dynamic combine(var previousValue, T element)) {
|
||||
dynamic fold(
|
||||
var initialValue, dynamic combine(var previousValue, T element)) {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
|
@ -303,11 +303,15 @@ class ObservableList<T>
|
|||
List<T> take(int count) => _internal.take(count);
|
||||
bool every(bool f(T element)) => _internal.every(f);
|
||||
bool any(bool f(T element)) => _internal.any(f);
|
||||
void forEach(void f(T element)) { _internal.forEach(f); }
|
||||
void forEach(void f(T element)) {
|
||||
_internal.forEach(f);
|
||||
}
|
||||
|
||||
String join([String separator = ""]) => _internal.join(separator);
|
||||
dynamic firstWhere(bool test(T value), {Object orElse()}) {
|
||||
return _internal.firstWhere(test, orElse: orElse);
|
||||
}
|
||||
|
||||
dynamic lastWhere(bool test(T value), {Object orElse()}) {
|
||||
return _internal.lastWhere(test, orElse: orElse);
|
||||
}
|
||||
|
@ -316,7 +320,7 @@ class ObservableList<T>
|
|||
bool remove(T element) => throw new UnimplementedError();
|
||||
void removeWhere(bool test(T element)) => throw new UnimplementedError();
|
||||
void retainWhere(bool test(T element)) => throw new UnimplementedError();
|
||||
List<T> toList({bool growable:true}) => throw new UnimplementedError();
|
||||
List<T> toList({bool growable: true}) => throw new UnimplementedError();
|
||||
Set<T> toSet() => throw new UnimplementedError();
|
||||
Iterable<T> takeWhile(bool test(T value)) => throw new UnimplementedError();
|
||||
Iterable<T> skipWhile(bool test(T value)) => throw new UnimplementedError();
|
||||
|
@ -324,14 +328,15 @@ class ObservableList<T>
|
|||
T singleWhere(bool test(T value)) {
|
||||
return _internal.singleWhere(test);
|
||||
}
|
||||
|
||||
T elementAt(int index) {
|
||||
return _internal.elementAt(index);
|
||||
}
|
||||
|
||||
Map<int, T> asMap() {
|
||||
return _internal.asMap();
|
||||
}
|
||||
|
||||
|
||||
bool get isEmpty => length == 0;
|
||||
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
|
@ -346,7 +351,8 @@ class ObservableList<T>
|
|||
/** A wrapper around a single value whose change can be observed. */
|
||||
class ObservableValue<T> extends AbstractObservable {
|
||||
ObservableValue(T value, [Observable parent = null])
|
||||
: super(parent), _value = value;
|
||||
: super(parent),
|
||||
_value = value;
|
||||
|
||||
T get value => _value;
|
||||
|
||||
|
|
|
@ -24,8 +24,12 @@ class BezierPhysics {
|
|||
* bezier when the final velocity is zero. This is a special case for which
|
||||
* these control points are constants.
|
||||
*/
|
||||
static const List<num> _FINAL_VELOCITY_ZERO_BEZIER =
|
||||
const [_ONE_THIRD, _TWO_THIRDS, _TWO_THIRDS, 1];
|
||||
static const List<num> _FINAL_VELOCITY_ZERO_BEZIER = const [
|
||||
_ONE_THIRD,
|
||||
_TWO_THIRDS,
|
||||
_TWO_THIRDS,
|
||||
1
|
||||
];
|
||||
|
||||
/**
|
||||
* Given consistent kinematics parameters for constant acceleration, returns
|
||||
|
@ -34,9 +38,8 @@ class BezierPhysics {
|
|||
* Returns a list [:[x1, y1, x2, y2]:] representing the intermediate control
|
||||
* points of the cubic Bezier.
|
||||
*/
|
||||
static List<num> calculateCubicBezierFromKinematics(
|
||||
num initialVelocity, num finalVelocity, num totalTime,
|
||||
num totalDisplacement) {
|
||||
static List<num> calculateCubicBezierFromKinematics(num initialVelocity,
|
||||
num finalVelocity, num totalTime, num totalDisplacement) {
|
||||
// Total time must be greater than 0.
|
||||
assert(!GoogleMath.nearlyEquals(totalTime, 0) && totalTime > 0);
|
||||
// Total displacement must not be 0.
|
||||
|
@ -51,8 +54,8 @@ class BezierPhysics {
|
|||
}
|
||||
List<num> controlPoint = _tangentLinesToQuadraticBezier(
|
||||
initialVelocity, finalVelocity, totalTime, totalDisplacement);
|
||||
controlPoint = _normalizeQuadraticBezier(controlPoint[0], controlPoint[1],
|
||||
totalTime, totalDisplacement);
|
||||
controlPoint = _normalizeQuadraticBezier(
|
||||
controlPoint[0], controlPoint[1], totalTime, totalDisplacement);
|
||||
return _quadraticToCubic(controlPoint[0], controlPoint[1]);
|
||||
}
|
||||
|
||||
|
@ -86,8 +89,7 @@ class BezierPhysics {
|
|||
* [y2] The y-coordinate of the end point.
|
||||
* Returns a list [:[x1, y1]:] representing the intermediate control point.
|
||||
*/
|
||||
static List<num> _normalizeQuadraticBezier(
|
||||
num x1, num y1, num x2, num y2) {
|
||||
static List<num> _normalizeQuadraticBezier(num x1, num y1, num x2, num y2) {
|
||||
// The end point must not lie on any axes.
|
||||
assert(!GoogleMath.nearlyEquals(x2, 0) && !GoogleMath.nearlyEquals(y2, 0));
|
||||
return [x1 / x2, y1 / y2];
|
||||
|
|
|
@ -69,10 +69,8 @@ class ClickBuster {
|
|||
*/
|
||||
DoubleLinkedQueueEntry<num> entry = _coordinates.firstEntry();
|
||||
while (entry != null) {
|
||||
if (_hitTest(entry.element,
|
||||
entry.nextEntry().element,
|
||||
coord.x,
|
||||
coord.y)) {
|
||||
if (_hitTest(
|
||||
entry.element, entry.nextEntry().element, coord.x, coord.y)) {
|
||||
entry.nextEntry().remove();
|
||||
entry.remove();
|
||||
return;
|
||||
|
@ -96,9 +94,9 @@ class ClickBuster {
|
|||
final coord = new Coordinate.fromClient(te.touches[0]);
|
||||
_coordinates.add(coord.x);
|
||||
_coordinates.add(coord.y);
|
||||
new Timer(
|
||||
const Duration(milliseconds: _TIME_THRESHOLD),
|
||||
() { _removeCoordinate(coord.x, coord.y); });
|
||||
new Timer(const Duration(milliseconds: _TIME_THRESHOLD), () {
|
||||
_removeCoordinate(coord.x, coord.y);
|
||||
});
|
||||
_toggleTapHighlights(true);
|
||||
}
|
||||
|
||||
|
@ -180,27 +178,29 @@ class ClickBuster {
|
|||
if (_coordinates == null) {
|
||||
// Listen to clicks on capture phase so they can be busted before anything
|
||||
// else gets a chance to handle them.
|
||||
Element.clickEvent.forTarget(document, useCapture: true).listen(
|
||||
(e) { _onClick(e); });
|
||||
Element.focusEvent.forTarget(document, useCapture: true).listen(
|
||||
(e) { _lastPreventedTime = 0; });
|
||||
Element.clickEvent.forTarget(document, useCapture: true).listen((e) {
|
||||
_onClick(e);
|
||||
});
|
||||
Element.focusEvent.forTarget(document, useCapture: true).listen((e) {
|
||||
_lastPreventedTime = 0;
|
||||
});
|
||||
|
||||
// Listen to touchstart on capture phase since it must be called prior to
|
||||
// every click or else we will accidentally prevent the click even if we
|
||||
// don't call preventGhostClick.
|
||||
Function startFn = (e) { _onTouchStart(e); };
|
||||
Function startFn = (e) {
|
||||
_onTouchStart(e);
|
||||
};
|
||||
if (!Device.supportsTouch) {
|
||||
startFn = mouseToTouchCallback(startFn);
|
||||
}
|
||||
var stream;
|
||||
if (Device.supportsTouch) {
|
||||
stream = Element.touchStartEvent.forTarget(document, useCapture:true);
|
||||
stream = Element.touchStartEvent.forTarget(document, useCapture: true);
|
||||
} else {
|
||||
stream = Element.mouseDownEvent.forTarget(document, useCapture:true);
|
||||
stream = Element.mouseDownEvent.forTarget(document, useCapture: true);
|
||||
}
|
||||
EventUtil.observe(document,
|
||||
stream,
|
||||
startFn, true);
|
||||
EventUtil.observe(document, stream, startFn, true);
|
||||
_coordinates = new DoubleLinkedQueue<num>();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ part of touch;
|
|||
* Common events related helpers.
|
||||
*/
|
||||
class EventUtil {
|
||||
|
||||
/**
|
||||
* Add an event listener to an element.
|
||||
* The event callback is specified by [handler].
|
||||
|
@ -16,14 +15,18 @@ class EventUtil {
|
|||
* If [removeHandlerOnFocus] is true the handler is removed when there is any
|
||||
* focus event, and added back on blur events.
|
||||
*/
|
||||
static void observe(/*Element or Document*/ element,
|
||||
Stream stream, Function handler,
|
||||
[bool removeHandlerOnFocus = false]) {
|
||||
static void observe(
|
||||
/*Element or Document*/ element, Stream stream, Function handler,
|
||||
[bool removeHandlerOnFocus = false]) {
|
||||
var subscription = stream.listen(handler);
|
||||
// TODO(jacobr): this remove on focus behavior seems really ugly.
|
||||
if (removeHandlerOnFocus) {
|
||||
element.onFocus.listen((e) { subscription.cancel(); });
|
||||
element.onBlur.listen((e) { subscription.cancel(); });
|
||||
element.onFocus.listen((e) {
|
||||
subscription.cancel();
|
||||
});
|
||||
element.onBlur.listen((e) {
|
||||
subscription.cancel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,10 +37,12 @@ class FxUtil {
|
|||
}
|
||||
|
||||
/** Apply a -webkit-transform using translate3d to an HTML element. */
|
||||
static void setWebkitTransform(
|
||||
Element el, num x, num y, [num z = 0,
|
||||
num rotation = null, num scale = null,
|
||||
num originX = null, num originY = null]) {
|
||||
static void setWebkitTransform(Element el, num x, num y,
|
||||
[num z = 0,
|
||||
num rotation = null,
|
||||
num scale = null,
|
||||
num originX = null,
|
||||
num originY = null]) {
|
||||
final style = el.style;
|
||||
// TODO(jacobr): create a helper class that simplifies building
|
||||
// transformation matricies that will be set as CSS styles. We should
|
||||
|
|
|
@ -8,7 +8,6 @@ part of touch;
|
|||
* Represents a point in 2 dimensional space.
|
||||
*/
|
||||
class Coordinate {
|
||||
|
||||
/**
|
||||
* X-value
|
||||
*/
|
||||
|
@ -19,8 +18,7 @@ class Coordinate {
|
|||
*/
|
||||
num y;
|
||||
|
||||
Coordinate([num this.x = 0, num this.y = 0]) {
|
||||
}
|
||||
Coordinate([num this.x = 0, num this.y = 0]) {}
|
||||
|
||||
/**
|
||||
* Gets the coordinates of a touch's location relative to the window's
|
||||
|
@ -66,7 +64,6 @@ class Coordinate {
|
|||
* Represents the interval { x | start <= x < end }.
|
||||
*/
|
||||
class Interval {
|
||||
|
||||
final num start;
|
||||
final num end;
|
||||
|
||||
|
@ -83,8 +80,7 @@ class Interval {
|
|||
int get hashCode => throw new UnimplementedError();
|
||||
|
||||
Interval union(Interval other) {
|
||||
return new Interval(Math.min(start, other.start),
|
||||
Math.max(end, other.end));
|
||||
return new Interval(Math.min(start, other.start), Math.max(end, other.end));
|
||||
}
|
||||
|
||||
bool contains(num value) {
|
||||
|
|
|
@ -54,16 +54,15 @@ class InfiniteScroller {
|
|||
Element _bottomDiv;
|
||||
Element _bottomLoadingDiv;
|
||||
|
||||
InfiniteScroller(Scroller scroller,
|
||||
Function onTopScroll, Function onBottomScroll,
|
||||
double offsetTop, [double offsetBottom = null])
|
||||
InfiniteScroller(Scroller scroller, Function onTopScroll,
|
||||
Function onBottomScroll, double offsetTop,
|
||||
[double offsetBottom = null])
|
||||
: _scroller = scroller,
|
||||
_onTopScroll = onTopScroll,
|
||||
_onBottomScroll = onBottomScroll,
|
||||
_offsetTop = offsetTop,
|
||||
_offsetBottom = offsetBottom == null ? offsetTop : offsetBottom,
|
||||
_lastScrollY = 0.0 {
|
||||
}
|
||||
_lastScrollY = 0.0 {}
|
||||
|
||||
/**
|
||||
* Adds the loading divs.
|
||||
|
@ -74,10 +73,11 @@ class InfiniteScroller {
|
|||
* [bottomLoadingDiv] is the div to show at the bottom when waiting for more
|
||||
* content to load at the end of the page.
|
||||
*/
|
||||
void addLoadingDivs([Element topDiv = null,
|
||||
Element topLoadingDiv = null,
|
||||
Element bottomDiv = null,
|
||||
Element bottomLoadingDiv = null]) {
|
||||
void addLoadingDivs(
|
||||
[Element topDiv = null,
|
||||
Element topLoadingDiv = null,
|
||||
Element bottomDiv = null,
|
||||
Element bottomLoadingDiv = null]) {
|
||||
_topDiv = topDiv;
|
||||
_topLoadingDiv = topLoadingDiv;
|
||||
_bottomDiv = bottomDiv;
|
||||
|
@ -129,14 +129,16 @@ class InfiniteScroller {
|
|||
* Register the event listeners.
|
||||
*/
|
||||
void _registerEventListeners() {
|
||||
_scroller.onScrollerEnd.listen((Event event) { _onScrollEnd(); });
|
||||
_scroller.onScrollerEnd.listen((Event event) {
|
||||
_onScrollEnd();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides one div and shows another.
|
||||
*/
|
||||
void _updateVisibility(bool isLoading, Element element,
|
||||
Element loadingElement) {
|
||||
void _updateVisibility(
|
||||
bool isLoading, Element element, Element loadingElement) {
|
||||
if (element != null) {
|
||||
element.style.display = isLoading ? "none" : "";
|
||||
}
|
||||
|
|
|
@ -26,15 +26,15 @@ part of touch;
|
|||
* -webkit-transform style property.
|
||||
*/
|
||||
abstract class Momentum {
|
||||
|
||||
factory Momentum(MomentumDelegate delegate, [num defaultDecelerationFactor = 1])
|
||||
=> new TimeoutMomentum(delegate, defaultDecelerationFactor);
|
||||
factory Momentum(MomentumDelegate delegate,
|
||||
[num defaultDecelerationFactor = 1]) =>
|
||||
new TimeoutMomentum(delegate, defaultDecelerationFactor);
|
||||
|
||||
bool get decelerating;
|
||||
|
||||
num get decelerationFactor;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Transition end handler. This function must be invoked after any transition
|
||||
* that occurred as a result of a call to the delegate's onDecelerate callback.
|
||||
*/
|
||||
|
@ -49,14 +49,15 @@ abstract class Momentum {
|
|||
* Returns true if deceleration has been initiated.
|
||||
*/
|
||||
bool start(Coordinate velocity, Coordinate minCoord, Coordinate maxCoord,
|
||||
Coordinate initialOffset, [num decelerationFactor]);
|
||||
Coordinate initialOffset,
|
||||
[num decelerationFactor]);
|
||||
|
||||
/**
|
||||
* Calculate the velocity required to transition between coordinates [start]
|
||||
* and [target] optionally specifying a custom [decelerationFactor].
|
||||
*/
|
||||
Coordinate calculateVelocity(Coordinate start, Coordinate target,
|
||||
[num decelerationFactor]);
|
||||
[num decelerationFactor]);
|
||||
|
||||
/** Stop decelerating and return the current velocity. */
|
||||
Coordinate stop();
|
||||
|
@ -111,9 +112,8 @@ class _Move {
|
|||
* class at all.
|
||||
*/
|
||||
class Solver {
|
||||
|
||||
static num solve(num fn(num), num targetY, num startX,
|
||||
[int maxIterations = 50]) {
|
||||
[int maxIterations = 50]) {
|
||||
num lastX = 0;
|
||||
num lastY = fn(lastX);
|
||||
num deltaX;
|
||||
|
@ -123,7 +123,7 @@ class Solver {
|
|||
num x = startX;
|
||||
num delta = startX;
|
||||
for (int i = 0; i < maxIterations; i++) {
|
||||
num y = fn(x);
|
||||
num y = fn(x);
|
||||
if (y.round() == targetY.round()) {
|
||||
return x;
|
||||
}
|
||||
|
@ -141,11 +141,10 @@ class Solver {
|
|||
// Avoid divide by zero and as a hack just repeat the previous delta.
|
||||
// Obviously this is a little dangerous and we might not converge.
|
||||
if (deltaY != 0) {
|
||||
delta = errorY * deltaX / deltaY;
|
||||
delta = errorY * deltaX / deltaY;
|
||||
}
|
||||
x += delta;
|
||||
if (minX != null && maxX != null
|
||||
&& (x > minX || x < maxX)) {
|
||||
if (minX != null && maxX != null && (x > minX || x < maxX)) {
|
||||
// Fall back to binary search.
|
||||
x = (minX + maxX) / 2;
|
||||
}
|
||||
|
@ -194,7 +193,6 @@ class SingleDimensionPhysics {
|
|||
*/
|
||||
static const _DECELERATION_FACTOR = 0.97;
|
||||
|
||||
|
||||
static const _MAX_VELOCITY_STATIC_FRICTION = 0.08 * _MS_PER_FRAME;
|
||||
static const _DECELERATION_FACTOR_STATIC_FRICTION = 0.92;
|
||||
|
||||
|
@ -228,7 +226,6 @@ class SingleDimensionPhysics {
|
|||
/** The bouncing state. */
|
||||
int _bouncingState;
|
||||
|
||||
|
||||
num velocity;
|
||||
num _currentOffset;
|
||||
|
||||
|
@ -239,12 +236,10 @@ class SingleDimensionPhysics {
|
|||
*/
|
||||
static const _VELOCITY_GUESS = 20;
|
||||
|
||||
SingleDimensionPhysics() : _bouncingState = BouncingState.NOT_BOUNCING {
|
||||
}
|
||||
SingleDimensionPhysics() : _bouncingState = BouncingState.NOT_BOUNCING {}
|
||||
|
||||
void configure(num minCoord, num maxCoord,
|
||||
num initialOffset, num customDecelerationFactor_,
|
||||
num velocity_) {
|
||||
void configure(num minCoord, num maxCoord, num initialOffset,
|
||||
num customDecelerationFactor_, num velocity_) {
|
||||
_bouncingState = BouncingState.NOT_BOUNCING;
|
||||
_minCoord = minCoord;
|
||||
_maxCoord = maxCoord;
|
||||
|
@ -253,23 +248,22 @@ class SingleDimensionPhysics {
|
|||
_adjustInitialVelocityAndBouncingState(velocity_);
|
||||
}
|
||||
|
||||
num solve(num initialOffset, num targetOffset,
|
||||
num customDecelerationFactor_) {
|
||||
num solve(
|
||||
num initialOffset, num targetOffset, num customDecelerationFactor_) {
|
||||
initialOffset = initialOffset.round();
|
||||
targetOffset = targetOffset.round();
|
||||
if (initialOffset == targetOffset) {
|
||||
return 0;
|
||||
}
|
||||
return Solver.solve((num velocity_) {
|
||||
// Don't specify min and max coordinates as we don't need to bother
|
||||
// with the simulating bouncing off the edges.
|
||||
configure(null, null, initialOffset.round(),
|
||||
customDecelerationFactor_, velocity_);
|
||||
stepAll();
|
||||
return _currentOffset;
|
||||
},
|
||||
targetOffset,
|
||||
targetOffset > initialOffset ? _VELOCITY_GUESS : -_VELOCITY_GUESS);
|
||||
// Don't specify min and max coordinates as we don't need to bother
|
||||
// with the simulating bouncing off the edges.
|
||||
configure(null, null, initialOffset.round(), customDecelerationFactor_,
|
||||
velocity_);
|
||||
stepAll();
|
||||
return _currentOffset;
|
||||
}, targetOffset,
|
||||
targetOffset > initialOffset ? _VELOCITY_GUESS : -_VELOCITY_GUESS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -318,16 +312,17 @@ class SingleDimensionPhysics {
|
|||
}
|
||||
if (stretchDistance != null) {
|
||||
if (stretchDistance * velocity < 0) {
|
||||
_bouncingState = _bouncingState == BouncingState.BOUNCING_BACK ?
|
||||
BouncingState.NOT_BOUNCING : BouncingState.BOUNCING_AWAY;
|
||||
_bouncingState = _bouncingState == BouncingState.BOUNCING_BACK
|
||||
? BouncingState.NOT_BOUNCING
|
||||
: BouncingState.BOUNCING_AWAY;
|
||||
velocity += stretchDistance * _PRE_BOUNCE_COEFFICIENT;
|
||||
} else {
|
||||
_bouncingState = BouncingState.BOUNCING_BACK;
|
||||
velocity = stretchDistance > 0 ?
|
||||
Math.max(stretchDistance * _POST_BOUNCE_COEFFICIENT,
|
||||
_MIN_STEP_VELOCITY) :
|
||||
Math.min(stretchDistance * _POST_BOUNCE_COEFFICIENT,
|
||||
-_MIN_STEP_VELOCITY);
|
||||
velocity = stretchDistance > 0
|
||||
? Math.max(
|
||||
stretchDistance * _POST_BOUNCE_COEFFICIENT, _MIN_STEP_VELOCITY)
|
||||
: Math.min(stretchDistance * _POST_BOUNCE_COEFFICIENT,
|
||||
-_MIN_STEP_VELOCITY);
|
||||
}
|
||||
} else {
|
||||
_bouncingState = BouncingState.NOT_BOUNCING;
|
||||
|
@ -344,7 +339,7 @@ class SingleDimensionPhysics {
|
|||
}
|
||||
|
||||
void stepAll() {
|
||||
while(!isDone()) {
|
||||
while (!isDone()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +363,6 @@ class SingleDimensionPhysics {
|
|||
* and timeouts.
|
||||
*/
|
||||
class TimeoutMomentum implements Momentum {
|
||||
|
||||
SingleDimensionPhysics physicsX;
|
||||
SingleDimensionPhysics physicsY;
|
||||
Coordinate _previousOffset;
|
||||
|
@ -384,11 +378,11 @@ class TimeoutMomentum implements Momentum {
|
|||
num _defaultDecelerationFactor;
|
||||
|
||||
TimeoutMomentum(this._delegate, [num defaultDecelerationFactor = 1])
|
||||
: _defaultDecelerationFactor = defaultDecelerationFactor,
|
||||
_decelerating = false,
|
||||
_moves = new Queue<_Move>(),
|
||||
physicsX = new SingleDimensionPhysics(),
|
||||
physicsY = new SingleDimensionPhysics();
|
||||
: _defaultDecelerationFactor = defaultDecelerationFactor,
|
||||
_decelerating = false,
|
||||
_moves = new Queue<_Move>(),
|
||||
physicsX = new SingleDimensionPhysics(),
|
||||
physicsY = new SingleDimensionPhysics();
|
||||
|
||||
/**
|
||||
* Calculate and return the moves for the deceleration motion.
|
||||
|
@ -400,9 +394,8 @@ class TimeoutMomentum implements Momentum {
|
|||
_stepWithoutAnimation();
|
||||
time += SingleDimensionPhysics._MS_PER_FRAME;
|
||||
if (_isStepNecessary()) {
|
||||
_moves.add(new _Move(_nextX, _nextY,
|
||||
physicsX.velocity,
|
||||
physicsY.velocity, time));
|
||||
_moves.add(new _Move(
|
||||
_nextX, _nextY, physicsX.velocity, physicsY.velocity, time));
|
||||
_previousOffset.y = _nextY;
|
||||
_previousOffset.x = _nextX;
|
||||
}
|
||||
|
@ -426,18 +419,18 @@ class TimeoutMomentum implements Momentum {
|
|||
* The [TouchHandler] requires this function but we don't need to do
|
||||
* anything here.
|
||||
*/
|
||||
void onTransitionEnd() {
|
||||
}
|
||||
void onTransitionEnd() {}
|
||||
|
||||
Coordinate calculateVelocity(Coordinate start_, Coordinate target,
|
||||
[num decelerationFactor = null]) {
|
||||
[num decelerationFactor = null]) {
|
||||
return new Coordinate(
|
||||
physicsX.solve(start_.x, target.x, decelerationFactor),
|
||||
physicsY.solve(start_.y, target.y, decelerationFactor));
|
||||
}
|
||||
|
||||
bool start(Coordinate velocity, Coordinate minCoord, Coordinate maxCoord,
|
||||
Coordinate initialOffset, [num decelerationFactor = null]) {
|
||||
Coordinate initialOffset,
|
||||
[num decelerationFactor = null]) {
|
||||
_customDecelerationFactor = _defaultDecelerationFactor;
|
||||
if (decelerationFactor != null) {
|
||||
_customDecelerationFactor = decelerationFactor;
|
||||
|
@ -448,20 +441,19 @@ class TimeoutMomentum implements Momentum {
|
|||
_stepTimeout = null;
|
||||
}
|
||||
|
||||
assert (_stepTimeout == null);
|
||||
assert(_stepTimeout == null);
|
||||
assert(minCoord.x <= maxCoord.x);
|
||||
assert(minCoord.y <= maxCoord.y);
|
||||
_previousOffset = initialOffset.clone();
|
||||
physicsX.configure(minCoord.x, maxCoord.x, initialOffset.x,
|
||||
_customDecelerationFactor, velocity.x);
|
||||
_customDecelerationFactor, velocity.x);
|
||||
physicsY.configure(minCoord.y, maxCoord.y, initialOffset.y,
|
||||
_customDecelerationFactor, velocity.y);
|
||||
_customDecelerationFactor, velocity.y);
|
||||
if (!physicsX.isDone() || !physicsY.isDone()) {
|
||||
_calculateMoves();
|
||||
if (!_moves.isEmpty) {
|
||||
num firstTime = _moves.first.time;
|
||||
_stepTimeout = Env.requestAnimationFrame(
|
||||
_step, null, firstTime);
|
||||
_stepTimeout = Env.requestAnimationFrame(_step, null, firstTime);
|
||||
_decelerating = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -492,8 +484,9 @@ class TimeoutMomentum implements Momentum {
|
|||
// Prune moves that are more than 1 frame behind when we have more
|
||||
// available moves.
|
||||
num lastEpoch = timestamp - SingleDimensionPhysics._MS_PER_FRAME;
|
||||
while (!_moves.isEmpty && !identical(_moves.first, _moves.last)
|
||||
&& _moves.first.time < lastEpoch) {
|
||||
while (!_moves.isEmpty &&
|
||||
!identical(_moves.first, _moves.last) &&
|
||||
_moves.first.time < lastEpoch) {
|
||||
_moves.removeFirst();
|
||||
}
|
||||
|
||||
|
@ -529,8 +522,8 @@ class TimeoutMomentum implements Momentum {
|
|||
// passed a velocity in to this Momentum implementation.
|
||||
num velocityScale = SingleDimensionPhysics._MS_PER_FRAME *
|
||||
SingleDimensionPhysics._INITIAL_VELOCITY_BOOST_FACTOR;
|
||||
velocity = new Coordinate(
|
||||
move.vx / velocityScale, move.vy / velocityScale);
|
||||
velocity =
|
||||
new Coordinate(move.vx / velocityScale, move.vy / velocityScale);
|
||||
} else {
|
||||
velocity = new Coordinate(0, 0);
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ class ScrollWatcher {
|
|||
Element _scrollerEl;
|
||||
|
||||
ScrollWatcher(Scroller scroller)
|
||||
: _scroller = scroller, _listeners = new List<ScrollListener>() {
|
||||
}
|
||||
: _scroller = scroller,
|
||||
_listeners = new List<ScrollListener>() {}
|
||||
|
||||
void addListener(ScrollListener listener) {
|
||||
_listeners.add(listener);
|
||||
|
@ -38,8 +38,7 @@ class ScrollWatcher {
|
|||
* Send the scroll event to all listeners.
|
||||
* [decelerating] is true if the offset is changing because of deceleration.
|
||||
*/
|
||||
void _dispatchScroll(num scrollX, num scrollY,
|
||||
[bool decelerating = false]) {
|
||||
void _dispatchScroll(num scrollX, num scrollY, [bool decelerating = false]) {
|
||||
for (final listener in _listeners) {
|
||||
listener.onScrollerMoved(scrollX, scrollY, decelerating);
|
||||
}
|
||||
|
@ -51,7 +50,9 @@ class ScrollWatcher {
|
|||
*/
|
||||
void initialize() {
|
||||
_scrollerEl = _scroller.getElement();
|
||||
_scroller.onContentMoved.listen((e) { _onContentMoved(e); });
|
||||
_scroller.onContentMoved.listen((e) {
|
||||
_onContentMoved(e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,22 +57,24 @@ class Scrollbar implements ScrollListener {
|
|||
bool _displayOnHover;
|
||||
bool _hovering = false;
|
||||
|
||||
Scrollbar(Scroller scroller, [displayOnHover = true]) :
|
||||
_displayOnHover = displayOnHover,
|
||||
_scroller = scroller,
|
||||
_frame = scroller.getFrame(),
|
||||
_cachedSize = new Map<String, num>() {
|
||||
_boundHideFn = () { _showScrollbars(false); };
|
||||
Scrollbar(Scroller scroller, [displayOnHover = true])
|
||||
: _displayOnHover = displayOnHover,
|
||||
_scroller = scroller,
|
||||
_frame = scroller.getFrame(),
|
||||
_cachedSize = new Map<String, num>() {
|
||||
_boundHideFn = () {
|
||||
_showScrollbars(false);
|
||||
};
|
||||
}
|
||||
|
||||
bool get _scrollBarDragInProgress => _scrollBarDragInProgressValue;
|
||||
|
||||
void set _scrollBarDragInProgress(bool value) {
|
||||
_scrollBarDragInProgressValue = value;
|
||||
_toggleClass(_verticalElement, DRAG_CLASS_NAME,
|
||||
value && _currentScrollVertical);
|
||||
_toggleClass(_horizontalElement, DRAG_CLASS_NAME,
|
||||
value && !_currentScrollVertical);
|
||||
_toggleClass(
|
||||
_verticalElement, DRAG_CLASS_NAME, value && _currentScrollVertical);
|
||||
_toggleClass(
|
||||
_horizontalElement, DRAG_CLASS_NAME, value && !_currentScrollVertical);
|
||||
}
|
||||
|
||||
// TODO(jacobr): move this helper method into the DOM.
|
||||
|
@ -121,44 +123,42 @@ class Scrollbar implements ScrollListener {
|
|||
// instead attach a single global event listener and let data in the
|
||||
// DOM drive.
|
||||
_frame.onClick.listen((Event e) {
|
||||
// Always focus on click as one of our children isn't all focused.
|
||||
if (!_frame.contains(document.activeElement)) {
|
||||
scrollerEl.focus();
|
||||
}
|
||||
});
|
||||
// Always focus on click as one of our children isn't all focused.
|
||||
if (!_frame.contains(document.activeElement)) {
|
||||
scrollerEl.focus();
|
||||
}
|
||||
});
|
||||
_frame.onMouseOver.listen((Event e) {
|
||||
final activeElement = document.activeElement;
|
||||
// TODO(jacobr): don't steal focus from a child element or a truly
|
||||
// focusable element. Only support stealing focus ffrom another
|
||||
// element that was given fake focus.
|
||||
if (activeElement is BodyElement ||
|
||||
(!_frame.contains(activeElement) &&
|
||||
activeElement is DivElement)) {
|
||||
scrollerEl.focus();
|
||||
}
|
||||
if (_hovering == false) {
|
||||
_hovering = true;
|
||||
_cancelTimeout();
|
||||
_showScrollbars(true);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
final activeElement = document.activeElement;
|
||||
// TODO(jacobr): don't steal focus from a child element or a truly
|
||||
// focusable element. Only support stealing focus ffrom another
|
||||
// element that was given fake focus.
|
||||
if (activeElement is BodyElement ||
|
||||
(!_frame.contains(activeElement) && activeElement is DivElement)) {
|
||||
scrollerEl.focus();
|
||||
}
|
||||
if (_hovering == false) {
|
||||
_hovering = true;
|
||||
_cancelTimeout();
|
||||
_showScrollbars(true);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
_frame.onMouseOut.listen((e) {
|
||||
_hovering = false;
|
||||
// Start hiding immediately if we aren't
|
||||
// scrolling or already in the process of
|
||||
// hidng the scrollbar
|
||||
if (!_scrollInProgress && _timer == null) {
|
||||
_boundHideFn();
|
||||
}
|
||||
});
|
||||
_hovering = false;
|
||||
// Start hiding immediately if we aren't
|
||||
// scrolling or already in the process of
|
||||
// hidng the scrollbar
|
||||
if (!_scrollInProgress && _timer == null) {
|
||||
_boundHideFn();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onStart(/*MouseEvent | Touch*/ e) {
|
||||
Element elementOver = e.target;
|
||||
if (elementOver == _verticalElement ||
|
||||
elementOver == _horizontalElement) {
|
||||
if (elementOver == _verticalElement || elementOver == _horizontalElement) {
|
||||
_currentScrollVertical = elementOver == _verticalElement;
|
||||
if (_currentScrollVertical) {
|
||||
_currentScrollStartMouse = e.page.y;
|
||||
|
@ -180,15 +180,14 @@ class Scrollbar implements ScrollListener {
|
|||
_refreshScrollRatioHelper(
|
||||
_scroller._scrollSize.height, contentSize.height);
|
||||
} else {
|
||||
_refreshScrollRatioHelper(_scroller._scrollSize.width,
|
||||
contentSize.width);
|
||||
_refreshScrollRatioHelper(_scroller._scrollSize.width, contentSize.width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _refreshScrollRatioHelper(num frameSize, num contentSize) {
|
||||
num frameTravelDistance = frameSize - _defaultScrollSize(
|
||||
frameSize, contentSize) -_PADDING_LENGTH * 2;
|
||||
num frameTravelDistance = frameSize -
|
||||
_defaultScrollSize(frameSize, contentSize) -
|
||||
_PADDING_LENGTH * 2;
|
||||
if (frameTravelDistance < 0.001) {
|
||||
_currentScrollRatio = 0;
|
||||
} else {
|
||||
|
@ -222,20 +221,19 @@ class Scrollbar implements ScrollListener {
|
|||
void _onEnd(UIEvent e) {
|
||||
_scrollBarDragInProgress = false;
|
||||
// TODO(jacobr): make scrollbar less tightly coupled to the scroller.
|
||||
_scroller._onScrollerDragEnd.add(
|
||||
new Event(ScrollerEventType.DRAG_END));
|
||||
_scroller._onScrollerDragEnd.add(new Event(ScrollerEventType.DRAG_END));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When scrolling ends, schedule a timeout to hide the scrollbars.
|
||||
*/
|
||||
void _onScrollerEnd(Event e) {
|
||||
_cancelTimeout();
|
||||
_timer = new Timer(const Duration(milliseconds: _DISPLAY_TIME),
|
||||
_boundHideFn);
|
||||
_timer =
|
||||
new Timer(const Duration(milliseconds: _DISPLAY_TIME), _boundHideFn);
|
||||
_scrollInProgress = false;
|
||||
}
|
||||
|
||||
void onScrollerMoved(num scrollX, num scrollY, bool decelerating) {
|
||||
if (_scrollInProgress == false) {
|
||||
// Display the scrollbar and then immediately prepare to hide it...
|
||||
|
@ -251,8 +249,8 @@ class Scrollbar implements ScrollListener {
|
|||
return;
|
||||
}
|
||||
_scroller._resize(() {
|
||||
updateScrollbars(_scroller.getHorizontalOffset(),
|
||||
_scroller.getVerticalOffset());
|
||||
updateScrollbars(
|
||||
_scroller.getHorizontalOffset(), _scroller.getVerticalOffset());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -261,14 +259,12 @@ class Scrollbar implements ScrollListener {
|
|||
if (_scroller._shouldScrollHorizontally()) {
|
||||
num scrollPercentX = _scroller.getHorizontalScrollPercent(scrollX);
|
||||
_updateScrollbar(_horizontalElement, scrollX, scrollPercentX,
|
||||
_scroller._scrollSize.width,
|
||||
contentSize.width, 'right', 'width');
|
||||
_scroller._scrollSize.width, contentSize.width, 'right', 'width');
|
||||
}
|
||||
if (_scroller._shouldScrollVertically()) {
|
||||
num scrollPercentY = _scroller.getVerticalScrollPercent(scrollY);
|
||||
_updateScrollbar(_verticalElement, scrollY, scrollPercentY,
|
||||
_scroller._scrollSize.height,
|
||||
contentSize.height, 'bottom', 'height');
|
||||
_scroller._scrollSize.height, contentSize.height, 'bottom', 'height');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,8 +305,9 @@ class Scrollbar implements ScrollListener {
|
|||
|
||||
num _defaultScrollSize(num frameSize, num contentSize) {
|
||||
return GoogleMath.clamp(
|
||||
(frameSize -_PADDING_LENGTH * 2) * frameSize / contentSize,
|
||||
_MIN_SIZE, frameSize -_PADDING_LENGTH * 2);
|
||||
(frameSize - _PADDING_LENGTH * 2) * frameSize / contentSize,
|
||||
_MIN_SIZE,
|
||||
frameSize - _PADDING_LENGTH * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -319,9 +316,8 @@ class Scrollbar implements ScrollListener {
|
|||
* specified by [cssPos]. The CSS property to adjust for size (height|width)
|
||||
* is specified by [cssSize].
|
||||
*/
|
||||
void _updateScrollbar(Element element, num offset,
|
||||
num scrollPercent, num frameSize,
|
||||
num contentSize, String cssPos, String cssSize) {
|
||||
void _updateScrollbar(Element element, num offset, num scrollPercent,
|
||||
num frameSize, num contentSize, String cssPos, String cssSize) {
|
||||
if (!_cachedSize.containsKey(cssSize)) {
|
||||
if (offset == null || contentSize < frameSize) {
|
||||
return;
|
||||
|
@ -337,9 +333,9 @@ class Scrollbar implements ScrollListener {
|
|||
num scrollPx = stretchPercent * (contentSize - frameSize);
|
||||
num maxSize = _defaultScrollSize(frameSize, contentSize);
|
||||
num size = Math.max(_MIN_COMPRESSED_SIZE, maxSize - scrollPx);
|
||||
num maxOffset = frameSize - size -_PADDING_LENGTH * 2;
|
||||
num pos = GoogleMath.clamp(scrollPercent * maxOffset,
|
||||
0, maxOffset) + _PADDING_LENGTH;
|
||||
num maxOffset = frameSize - size - _PADDING_LENGTH * 2;
|
||||
num pos = GoogleMath.clamp(scrollPercent * maxOffset, 0, maxOffset) +
|
||||
_PADDING_LENGTH;
|
||||
pos = pos.round();
|
||||
size = size.round();
|
||||
final style = element.style;
|
||||
|
|
|
@ -44,13 +44,13 @@ void joinFutures(List<Future> futures, Callback callback) {
|
|||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
for (Future p in futures) {
|
||||
p.then(helper);
|
||||
}
|
||||
}
|
||||
|
||||
class Scroller implements Draggable, MomentumDelegate {
|
||||
|
||||
/** Pixels to move each time an arrow key is pressed. */
|
||||
static const ARROW_KEY_DELTA = 30;
|
||||
static const SCROLL_WHEEL_VELOCITY = 0.01;
|
||||
|
@ -141,18 +141,21 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
bool _activeGesture = false;
|
||||
ScrollWatcher _scrollWatcher;
|
||||
|
||||
Scroller(Element scrollableElem, [this.verticalEnabled = false,
|
||||
this.horizontalEnabled = false,
|
||||
momentumEnabled = true,
|
||||
lookupContentSizeDelegate = null,
|
||||
num defaultDecelerationFactor = 1,
|
||||
int scrollTechnique = null, bool capture = false])
|
||||
Scroller(Element scrollableElem,
|
||||
[this.verticalEnabled = false,
|
||||
this.horizontalEnabled = false,
|
||||
momentumEnabled = true,
|
||||
lookupContentSizeDelegate = null,
|
||||
num defaultDecelerationFactor = 1,
|
||||
int scrollTechnique = null,
|
||||
bool capture = false])
|
||||
: _momentumEnabled = momentumEnabled,
|
||||
_lookupContentSizeDelegate = lookupContentSizeDelegate,
|
||||
_element = scrollableElem,
|
||||
_frame = scrollableElem.parent,
|
||||
_scrollTechnique = scrollTechnique != null
|
||||
? scrollTechnique : ScrollerScrollTechnique.TRANSFORM_3D,
|
||||
? scrollTechnique
|
||||
: ScrollerScrollTechnique.TRANSFORM_3D,
|
||||
_minPoint = new Coordinate(0, 0),
|
||||
_maxPoint = new Coordinate(0, 0),
|
||||
_maxOffset = new Coordinate(0, 0),
|
||||
|
@ -178,33 +181,28 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
});
|
||||
|
||||
_frame.onKeyDown.listen((KeyboardEvent e) {
|
||||
bool handled = false;
|
||||
// We ignore key events where further scrolling in that direction
|
||||
// would have no impact which matches default browser behavior with
|
||||
// nested scrollable areas.
|
||||
bool handled = false;
|
||||
// We ignore key events where further scrolling in that direction
|
||||
// would have no impact which matches default browser behavior with
|
||||
// nested scrollable areas.
|
||||
|
||||
switch(e.keyCode) {
|
||||
case 33: // page-up
|
||||
throwDelta(
|
||||
0,
|
||||
_scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
|
||||
handled = true;
|
||||
break;
|
||||
case 34: // page-down
|
||||
throwDelta(
|
||||
0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
|
||||
handled = true;
|
||||
break;
|
||||
case 35: // End
|
||||
throwTo(_maxPoint.x, _minPoint.y,
|
||||
FAST_SNAP_DECELERATION_FACTOR);
|
||||
handled = true;
|
||||
break;
|
||||
case 36: // Home
|
||||
throwTo(_maxPoint.x,_maxPoint.y,
|
||||
FAST_SNAP_DECELERATION_FACTOR);
|
||||
handled = true;
|
||||
break;
|
||||
switch (e.keyCode) {
|
||||
case 33: // page-up
|
||||
throwDelta(0, _scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
|
||||
handled = true;
|
||||
break;
|
||||
case 34: // page-down
|
||||
throwDelta(0, -_scrollSize.height * PAGE_KEY_SCROLL_FRACTION);
|
||||
handled = true;
|
||||
break;
|
||||
case 35: // End
|
||||
throwTo(_maxPoint.x, _minPoint.y, FAST_SNAP_DECELERATION_FACTOR);
|
||||
handled = true;
|
||||
break;
|
||||
case 36: // Home
|
||||
throwTo(_maxPoint.x, _maxPoint.y, FAST_SNAP_DECELERATION_FACTOR);
|
||||
handled = true;
|
||||
break;
|
||||
/* TODO(jacobr): enable arrow keys when the don't conflict with other
|
||||
application keyboard shortcuts.
|
||||
case 38: // up
|
||||
|
@ -230,11 +228,11 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
FAST_SNAP_DECELERATION_FACTOR);
|
||||
break;
|
||||
*/
|
||||
}
|
||||
if (handled) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (handled) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
// The scrollable element must be relatively positioned.
|
||||
// TODO(jacobr): this assert fires asynchronously which could be confusing.
|
||||
if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) {
|
||||
|
@ -284,7 +282,6 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
return _onDecelStartStream;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a scroll listener. This allows other classes to subscribe to scroll
|
||||
* notifications from this scroller.
|
||||
|
@ -301,8 +298,7 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
* Adjust the new calculated scroll position based on the minimum allowed
|
||||
* position and returns the adjusted scroll value.
|
||||
*/
|
||||
num _adjustValue(num newPosition, num minPosition,
|
||||
num maxPosition) {
|
||||
num _adjustValue(num newPosition, num minPosition, num maxPosition) {
|
||||
assert(minPosition <= maxPosition);
|
||||
|
||||
if (newPosition < minPosition) {
|
||||
|
@ -347,9 +343,7 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
|
||||
_startDeceleration(
|
||||
_momentum.calculateVelocity(
|
||||
_contentOffset,
|
||||
snappedTarget,
|
||||
decelerationFactor),
|
||||
_contentOffset, snappedTarget, decelerationFactor),
|
||||
decelerationFactor);
|
||||
if (_onDecelStart != null) {
|
||||
_onDecelStart.add(new Event(ScrollerEventType.DECEL_START));
|
||||
|
@ -383,17 +377,18 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
_snapContentOffsetToBounds();
|
||||
_setContentOffset(_contentOffset.x, _contentOffset.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusted content size is a size with the combined largest height and width
|
||||
* of both the content and the frame.
|
||||
*/
|
||||
Size _getAdjustedContentSize() {
|
||||
return new Size(Math.max(_scrollSize.width, _contentSize.width),
|
||||
Math.max(_scrollSize.height, _contentSize.height));
|
||||
Math.max(_scrollSize.height, _contentSize.height));
|
||||
}
|
||||
|
||||
// TODO(jmesserly): these should be properties instead of get* methods
|
||||
num getDefaultVerticalOffset() => _maxPoint.y;
|
||||
num getDefaultVerticalOffset() => _maxPoint.y;
|
||||
Element getElement() => _element;
|
||||
Element getFrame() => _frame;
|
||||
num getHorizontalOffset() => _contentOffset.x;
|
||||
|
@ -408,7 +403,7 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
return (x - _minPoint.x) / (_maxPoint.x - _minPoint.x);
|
||||
}
|
||||
|
||||
num getMaxPointY()=> _maxPoint.y;
|
||||
num getMaxPointY() => _maxPoint.y;
|
||||
num getMinPointY() => _minPoint.y;
|
||||
Momentum get momentum => _momentum;
|
||||
|
||||
|
@ -487,10 +482,12 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
Coordinate contentStart = _contentStartOffset;
|
||||
num newX = contentStart.x + _touchHandler.getDragDeltaX();
|
||||
num newY = contentStart.y + _touchHandler.getDragDeltaY();
|
||||
newY = _shouldScrollVertically() ?
|
||||
_adjustValue(newY, _minPoint.y, _maxPoint.y) : 0;
|
||||
newX = _shouldScrollHorizontally() ?
|
||||
_adjustValue(newX, _minPoint.x, _maxPoint.x) : 0;
|
||||
newY = _shouldScrollVertically()
|
||||
? _adjustValue(newY, _minPoint.y, _maxPoint.y)
|
||||
: 0;
|
||||
newX = _shouldScrollHorizontally()
|
||||
? _adjustValue(newX, _minPoint.x, _maxPoint.x)
|
||||
: 0;
|
||||
if (!_activeGesture) {
|
||||
_activeGesture = true;
|
||||
_dragInProgress = true;
|
||||
|
@ -515,8 +512,7 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
return !!(shouldVertical || shouldHorizontal && !verticalish);
|
||||
}
|
||||
|
||||
void onTouchEnd() {
|
||||
}
|
||||
void onTouchEnd() {}
|
||||
|
||||
/**
|
||||
* Prepare the scrollable area for possible movement.
|
||||
|
@ -570,15 +566,14 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
_contentSize = new Size(_element.scrollWidth, _element.scrollHeight);
|
||||
}
|
||||
|
||||
_scrollSize = new Size(_frame.offset.width,
|
||||
_frame.offset.height);
|
||||
_scrollSize = new Size(_frame.offset.width, _frame.offset.height);
|
||||
Size adjusted = _getAdjustedContentSize();
|
||||
_maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y);
|
||||
_minPoint = new Coordinate(
|
||||
Math.min(
|
||||
_scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x),
|
||||
Math.min(
|
||||
_scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y));
|
||||
Math.min(_scrollSize.height - adjusted.height + _minOffset.y,
|
||||
_maxPoint.y));
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
@ -616,7 +611,7 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
_setContentOffset(_contentOffset.x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Whether the scrollable area should scroll horizontally. Only
|
||||
* returns true if the client has enabled horizontal scrolling, and the
|
||||
* content is wider than the frame.
|
||||
|
@ -642,10 +637,8 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
* the frame, snap it back in to place.
|
||||
*/
|
||||
void _snapContentOffsetToBounds() {
|
||||
num clampX =
|
||||
GoogleMath.clamp(_minPoint.x, _contentOffset.x, _maxPoint.x);
|
||||
num clampY =
|
||||
GoogleMath.clamp(_minPoint.y, _contentOffset.y, _maxPoint.y);
|
||||
num clampX = GoogleMath.clamp(_minPoint.x, _contentOffset.x, _maxPoint.x);
|
||||
num clampY = GoogleMath.clamp(_minPoint.y, _contentOffset.y, _maxPoint.y);
|
||||
if (_contentOffset.x != clampX || _contentOffset.y != clampY) {
|
||||
_setContentOffset(clampX, clampY);
|
||||
}
|
||||
|
@ -656,7 +649,7 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
* Returns true if deceleration has been initiated.
|
||||
*/
|
||||
bool _startDeceleration(Coordinate velocity,
|
||||
[num decelerationFactor = null]) {
|
||||
[num decelerationFactor = null]) {
|
||||
if (!_shouldScrollHorizontally()) {
|
||||
velocity.x = 0;
|
||||
}
|
||||
|
@ -665,8 +658,8 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
}
|
||||
assert(_minPoint != null); // Min point is not set
|
||||
assert(_maxPoint != null); // Max point is not set
|
||||
return _momentum.start(velocity, _minPoint, _maxPoint, _contentOffset,
|
||||
decelerationFactor);
|
||||
return _momentum.start(
|
||||
velocity, _minPoint, _maxPoint, _contentOffset, decelerationFactor);
|
||||
}
|
||||
|
||||
Coordinate stop() {
|
||||
|
@ -682,9 +675,13 @@ class Scroller implements Draggable, MomentumDelegate {
|
|||
}
|
||||
|
||||
static Function _getOffsetFunction(int scrollTechnique) {
|
||||
return scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D ?
|
||||
(el, x, y) { FxUtil.setTranslate(el, x, y, 0); } :
|
||||
(el, x, y) { FxUtil.setLeftAndTop(el, x, y); };
|
||||
return scrollTechnique == ScrollerScrollTechnique.TRANSFORM_3D
|
||||
? (el, x, y) {
|
||||
FxUtil.setTranslate(el, x, y, 0);
|
||||
}
|
||||
: (el, x, y) {
|
||||
FxUtil.setLeftAndTop(el, x, y);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -153,8 +153,9 @@ class TouchHandler {
|
|||
num _correctVelocity(num velocity) {
|
||||
num absVelocity = velocity.abs();
|
||||
if (absVelocity > _MAXIMUM_VELOCITY) {
|
||||
absVelocity = _recentTouchesY.length < 6 ?
|
||||
_VELOCITY_FOR_INCORRECT_EVENTS : _MAXIMUM_VELOCITY;
|
||||
absVelocity = _recentTouchesY.length < 6
|
||||
? _VELOCITY_FOR_INCORRECT_EVENTS
|
||||
: _MAXIMUM_VELOCITY;
|
||||
}
|
||||
return absVelocity * (velocity < 0 ? -1 : 1);
|
||||
}
|
||||
|
@ -165,11 +166,14 @@ class TouchHandler {
|
|||
* phase.
|
||||
*/
|
||||
void enable([bool capture = false]) {
|
||||
Function onEnd = (e) { _onEnd(e.timeStamp, e); };
|
||||
_addEventListeners(
|
||||
_element,
|
||||
(e) { _onStart(e); },
|
||||
(e) { _onMove(e); }, onEnd, onEnd, capture);
|
||||
Function onEnd = (e) {
|
||||
_onEnd(e.timeStamp, e);
|
||||
};
|
||||
_addEventListeners(_element, (e) {
|
||||
_onStart(e);
|
||||
}, (e) {
|
||||
_onMove(e);
|
||||
}, onEnd, onEnd, capture);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,7 +222,7 @@ class TouchHandler {
|
|||
* Return the touch of the last event.
|
||||
*/
|
||||
Touch _getLastTouch() {
|
||||
assert (_lastEvent != null); // Last event not set
|
||||
assert(_lastEvent != null); // Last event not set
|
||||
return _lastEvent.touches[0];
|
||||
}
|
||||
|
||||
|
@ -274,8 +278,8 @@ class TouchHandler {
|
|||
_lastTouchY = clientY;
|
||||
if (!_dragging &&
|
||||
((_totalMoveY > _MIN_TRACKING_FOR_DRAG && _draggable.verticalEnabled) ||
|
||||
(_totalMoveX > _MIN_TRACKING_FOR_DRAG &&
|
||||
_draggable.horizontalEnabled))) {
|
||||
(_totalMoveX > _MIN_TRACKING_FOR_DRAG &&
|
||||
_draggable.horizontalEnabled))) {
|
||||
_dragging = _draggable.onDragStart(e);
|
||||
if (!_dragging) {
|
||||
_endTracking();
|
||||
|
@ -337,14 +341,13 @@ class TouchHandler {
|
|||
* or y component of the recent touch and the second item is the touch time
|
||||
* stamp. The time of the most recent event is specified by [recentTime].
|
||||
*/
|
||||
List<int> _removeOldTouches(List<int> recentTouches,
|
||||
int recentTime) {
|
||||
List<int> _removeOldTouches(List<int> recentTouches, int recentTime) {
|
||||
int count = 0;
|
||||
final len = recentTouches.length;
|
||||
assert (len % 2 == 0);
|
||||
assert(len % 2 == 0);
|
||||
while (count < len &&
|
||||
recentTime - recentTouches[count + 1] > _MAX_TRACKING_TIME ||
|
||||
(len - count) > _MAX_TRACKING_TOUCHES * 2) {
|
||||
recentTime - recentTouches[count + 1] > _MAX_TRACKING_TIME ||
|
||||
(len - count) > _MAX_TRACKING_TOUCHES * 2) {
|
||||
count += 2;
|
||||
}
|
||||
return count == 0 ? recentTouches : _removeFirstN(recentTouches, count);
|
||||
|
@ -362,9 +365,11 @@ class TouchHandler {
|
|||
* stamp. The x or y component of the most recent move is specified by
|
||||
* [recentMove].
|
||||
*/
|
||||
List<int> _removeTouchesInWrongDirection(List<int> recentTouches,
|
||||
int lastMove, int recentMove) {
|
||||
if (lastMove !=0 && recentMove != 0 && recentTouches.length > 2 &&
|
||||
List<int> _removeTouchesInWrongDirection(
|
||||
List<int> recentTouches, int lastMove, int recentMove) {
|
||||
if (lastMove != 0 &&
|
||||
recentMove != 0 &&
|
||||
recentTouches.length > 2 &&
|
||||
_xor(lastMove > 0, recentMove > 0)) {
|
||||
return _removeFirstN(recentTouches, recentTouches.length - 2);
|
||||
}
|
||||
|
|
|
@ -30,10 +30,9 @@ EventListener mouseToTouchCallback(EventListener callback) {
|
|||
}
|
||||
|
||||
/** Helper method to attach event listeners to a [node]. */
|
||||
void _addEventListeners(Element node,
|
||||
EventListener onStart, EventListener onMove, EventListener onEnd,
|
||||
EventListener onCancel, [bool capture = false]) {
|
||||
|
||||
void _addEventListeners(Element node, EventListener onStart,
|
||||
EventListener onMove, EventListener onEnd, EventListener onCancel,
|
||||
[bool capture = false]) {
|
||||
Function removeListeners;
|
||||
|
||||
onEndWrapper(e) {
|
||||
|
@ -65,14 +64,18 @@ void _addEventListeners(Element node,
|
|||
};
|
||||
|
||||
Element.touchStartEvent.forTarget(node, useCapture: capture).listen((e) {
|
||||
touchMoveSub = Element.touchMoveEvent.forTarget(
|
||||
document, useCapture: capture).listen(onMove);
|
||||
touchEndSub = Element.touchEndEvent.forTarget(
|
||||
document, useCapture: capture).listen(onEndWrapper);
|
||||
touchLeaveSub = Element.touchLeaveEvent.forTarget(
|
||||
document, useCapture: capture).listen(onLeaveWrapper);
|
||||
touchCancelSub = Element.touchCancelEvent.forTarget(
|
||||
document, useCapture: capture).listen(onCancelWrapper);
|
||||
touchMoveSub = Element.touchMoveEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onMove);
|
||||
touchEndSub = Element.touchEndEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onEndWrapper);
|
||||
touchLeaveSub = Element.touchLeaveEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onLeaveWrapper);
|
||||
touchCancelSub = Element.touchCancelEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onCancelWrapper);
|
||||
return onStart(e);
|
||||
});
|
||||
} else {
|
||||
|
@ -92,12 +95,15 @@ void _addEventListeners(Element node,
|
|||
};
|
||||
|
||||
Element.mouseDownEvent.forTarget(node, useCapture: capture).listen((e) {
|
||||
mouseMoveSub = Element.mouseMoveEvent.forTarget(
|
||||
document, useCapture: capture).listen(onMove);
|
||||
mouseUpSub = Element.mouseUpEvent.forTarget(
|
||||
document, useCapture: capture).listen(onEndWrapper);
|
||||
touchCancelSub = Element.touchCancelEvent.forTarget(
|
||||
document, useCapture: capture).listen(onCancelWrapper);
|
||||
mouseMoveSub = Element.mouseMoveEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onMove);
|
||||
mouseUpSub = Element.mouseUpEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onEndWrapper);
|
||||
touchCancelSub = Element.touchCancelEvent
|
||||
.forTarget(document, useCapture: capture)
|
||||
.listen(onCancelWrapper);
|
||||
return onStart(e);
|
||||
});
|
||||
}
|
||||
|
@ -182,20 +188,51 @@ class MockTouch implements Touch {
|
|||
|
||||
int get screenX => wrapped.screen.x;
|
||||
|
||||
int get screenY {return wrapped.screen.y; }
|
||||
int get screenY {
|
||||
return wrapped.screen.y;
|
||||
}
|
||||
|
||||
EventTarget get target => wrapped.target;
|
||||
|
||||
double get force { throw new UnimplementedError(); }
|
||||
Point get page { throw new UnimplementedError(); }
|
||||
int get radiusX { throw new UnimplementedError(); }
|
||||
int get radiusY { throw new UnimplementedError(); }
|
||||
num get rotationAngle { throw new UnimplementedError(); }
|
||||
Point get screen { throw new UnimplementedError(); }
|
||||
num get webkitForce { throw new UnimplementedError(); }
|
||||
int get webkitRadiusX { throw new UnimplementedError(); }
|
||||
int get webkitRadiusY { throw new UnimplementedError(); }
|
||||
num get webkitRotationAngle { throw new UnimplementedError(); }
|
||||
double get force {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
Point get page {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
int get radiusX {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
int get radiusY {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
num get rotationAngle {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
Point get screen {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
num get webkitForce {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
int get webkitRadiusX {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
int get webkitRadiusY {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
num get webkitRotationAngle {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
class MockTouchEvent implements TouchEvent {
|
||||
|
@ -205,14 +242,15 @@ class MockTouchEvent implements TouchEvent {
|
|||
final List<Touch> targetTouches;
|
||||
final List<Touch> changedTouches;
|
||||
MockTouchEvent(MouseEvent this.wrapped, List<Touch> this.touches,
|
||||
List<Touch> this.targetTouches,
|
||||
List<Touch> this.changedTouches) {}
|
||||
List<Touch> this.targetTouches, List<Touch> this.changedTouches) {}
|
||||
|
||||
bool get bubbles => wrapped.bubbles;
|
||||
|
||||
bool get cancelBubble => wrapped.cancelBubble;
|
||||
|
||||
void set cancelBubble(bool value) { wrapped.cancelBubble = value; }
|
||||
void set cancelBubble(bool value) {
|
||||
wrapped.cancelBubble = value;
|
||||
}
|
||||
|
||||
bool get cancelable => wrapped.cancelable;
|
||||
|
||||
|
@ -222,7 +260,9 @@ class MockTouchEvent implements TouchEvent {
|
|||
|
||||
int get eventPhase => wrapped.eventPhase;
|
||||
|
||||
void set returnValue(bool value) { wrapped.returnValue = value; }
|
||||
void set returnValue(bool value) {
|
||||
wrapped.returnValue = value;
|
||||
}
|
||||
|
||||
bool get returnValue => wrapped.returnValue;
|
||||
|
||||
|
@ -232,11 +272,17 @@ class MockTouchEvent implements TouchEvent {
|
|||
|
||||
String get type => wrapped.type;
|
||||
|
||||
void preventDefault() { wrapped.preventDefault(); }
|
||||
void preventDefault() {
|
||||
wrapped.preventDefault();
|
||||
}
|
||||
|
||||
void stopImmediatePropagation() { wrapped.stopImmediatePropagation(); }
|
||||
void stopImmediatePropagation() {
|
||||
wrapped.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
void stopPropagation() { wrapped.stopPropagation(); }
|
||||
void stopPropagation() {
|
||||
wrapped.stopPropagation();
|
||||
}
|
||||
|
||||
int get charCode => wrapped.charCode;
|
||||
|
||||
|
@ -265,11 +311,31 @@ class MockTouchEvent implements TouchEvent {
|
|||
|
||||
bool get shiftKey => wrapped.shiftKey;
|
||||
|
||||
DataTransfer get clipboardData { throw new UnimplementedError(); }
|
||||
Point get layer { throw new UnimplementedError(); }
|
||||
Element get matchingTarget { throw new UnimplementedError(); }
|
||||
Point get page { throw new UnimplementedError(); }
|
||||
List get path { throw new UnimplementedError(); }
|
||||
Point get screen { throw new UnimplementedError(); }
|
||||
/*InputDevice*/ get sourceDevice { throw new UnimplementedError(); }
|
||||
DataTransfer get clipboardData {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
Point get layer {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
Element get matchingTarget {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
Point get page {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
List get path {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
Point get screen {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
/*InputDevice*/ get sourceDevice {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,9 @@ typedef num NumericValueSelector<T>(T value);
|
|||
* TODO(jmesserly): make these top level functions?
|
||||
*/
|
||||
class CollectionUtils {
|
||||
|
||||
static void insertAt(List arr, int pos, value) {
|
||||
assert (pos >= 0);
|
||||
assert (pos <= arr.length);
|
||||
assert(pos >= 0);
|
||||
assert(pos <= arr.length);
|
||||
|
||||
if (pos == arr.length) {
|
||||
arr.add(value);
|
||||
|
|
|
@ -9,8 +9,15 @@ part of utilslib;
|
|||
*/
|
||||
class DateUtils {
|
||||
// TODO(jmesserly): localized strings
|
||||
static const WEEKDAYS = const ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
|
||||
'Friday', 'Saturday', 'Sunday'];
|
||||
static const WEEKDAYS = const [
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
'Sunday'
|
||||
];
|
||||
|
||||
static const YESTERDAY = 'Yesterday';
|
||||
|
||||
|
@ -34,9 +41,20 @@ class DateUtils {
|
|||
|
||||
int day = int.parse(parts[1]);
|
||||
|
||||
final months = const['Jan', 'Feb', 'Mar', 'Apr',
|
||||
'May', 'Jun', 'Jul', 'Aug',
|
||||
'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
final months = const [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec'
|
||||
];
|
||||
int month = months.indexOf(parts[2], 0) + 1;
|
||||
if (month < 0) {
|
||||
throw 'bad month, expected 3 letter month code, got: ${parts[2]}';
|
||||
|
@ -58,7 +76,8 @@ class DateUtils {
|
|||
int zoneOffset = int.parse(parts[5]) ~/ 100;
|
||||
|
||||
// Pretend it's a UTC time
|
||||
DateTime result = new DateTime.utc(year, month, day, hours, minutes, seconds, 0);
|
||||
DateTime result =
|
||||
new DateTime.utc(year, month, day, hours, minutes, seconds, 0);
|
||||
// Shift it to the proper zone, but it's still a UTC time
|
||||
result = result.subtract(new Duration(hours: zoneOffset));
|
||||
// Then render it as a local time
|
||||
|
@ -117,7 +136,8 @@ class DateUtils {
|
|||
*/
|
||||
static String toRecentTimeString(DateTime then) {
|
||||
bool datesAreEqual(DateTime d1, DateTime d2) {
|
||||
return (d1.year == d2.year) && (d1.month == d2.month) &&
|
||||
return (d1.year == d2.year) &&
|
||||
(d1.month == d2.month) &&
|
||||
(d1.day == d2.day);
|
||||
}
|
||||
|
||||
|
@ -143,6 +163,7 @@ class DateUtils {
|
|||
if (n >= 10) return "${n}";
|
||||
return "0${n}";
|
||||
}
|
||||
|
||||
String twoDigitMonth = twoDigits(then.month);
|
||||
String twoDigitDay = twoDigits(then.day);
|
||||
return "${then.year}-${twoDigitMonth}-${twoDigitDay}";
|
||||
|
@ -180,6 +201,7 @@ class DateUtils {
|
|||
if (n >= 10) return "${n}";
|
||||
return "0${n}";
|
||||
}
|
||||
|
||||
String mm =
|
||||
twoDigits(duration.inMinutes.remainder(Duration.MINUTES_PER_HOUR));
|
||||
return "${hours}:${mm} ${a}";
|
||||
|
|
|
@ -11,7 +11,7 @@ class StringUtils {
|
|||
/**
|
||||
* Returns either [str], or if [str] is null, the value of [defaultStr].
|
||||
*/
|
||||
static String defaultString(String str, [String defaultStr='']) {
|
||||
static String defaultString(String str, [String defaultStr = '']) {
|
||||
return str == null ? defaultStr : str;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,13 @@ class SwarmUri {
|
|||
|
||||
// TODO(terry): Added b/5096547 to track replace should by default behave
|
||||
// like replaceAll to avoid a problematic usage pattern.
|
||||
return component.replaceAll(':', '%3A')
|
||||
.replaceAll('/', '%2F')
|
||||
.replaceAll('?', '%3F')
|
||||
.replaceAll('=', '%3D')
|
||||
.replaceAll('&', '%26')
|
||||
.replaceAll(' ', '%20');
|
||||
return component
|
||||
.replaceAll(':', '%3A')
|
||||
.replaceAll('/', '%2F')
|
||||
.replaceAll('?', '%3F')
|
||||
.replaceAll('=', '%3D')
|
||||
.replaceAll('&', '%26')
|
||||
.replaceAll(' ', '%20');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,11 +68,12 @@ class SwarmUri {
|
|||
static String decodeComponent(String component) {
|
||||
if (component == null) return component;
|
||||
|
||||
return component.replaceAll('%3A', ':')
|
||||
.replaceAll('%2F', '/')
|
||||
.replaceAll('%3F', '?')
|
||||
.replaceAll('%3D', '=')
|
||||
.replaceAll('%26', '&')
|
||||
.replaceAll('%20', ' ');
|
||||
return component
|
||||
.replaceAll('%3A', ':')
|
||||
.replaceAll('%2F', '/')
|
||||
.replaceAll('%3F', '?')
|
||||
.replaceAll('%3D', '=')
|
||||
.replaceAll('%26', '&')
|
||||
.replaceAll('%20', ' ');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ part of view;
|
|||
* A View that is composed of child views.
|
||||
*/
|
||||
class CompositeView extends View {
|
||||
|
||||
List<View> childViews;
|
||||
|
||||
// TODO(rnystrom): Allowing this to be public is gross. CompositeView should
|
||||
|
@ -25,32 +24,34 @@ class CompositeView extends View {
|
|||
final bool _nestedContainer;
|
||||
final bool _showScrollbar;
|
||||
|
||||
CompositeView(String this._cssName, [nestedContainer = false,
|
||||
scrollable = false, vertical = false,
|
||||
showScrollbar = false])
|
||||
: super(),
|
||||
_nestedContainer = nestedContainer,
|
||||
_scrollable = scrollable,
|
||||
_vertical = vertical,
|
||||
_showScrollbar = showScrollbar,
|
||||
childViews = new List<View>() {
|
||||
}
|
||||
CompositeView(String this._cssName,
|
||||
[nestedContainer = false,
|
||||
scrollable = false,
|
||||
vertical = false,
|
||||
showScrollbar = false])
|
||||
: super(),
|
||||
_nestedContainer = nestedContainer,
|
||||
_scrollable = scrollable,
|
||||
_vertical = vertical,
|
||||
_showScrollbar = showScrollbar,
|
||||
childViews = new List<View>() {}
|
||||
|
||||
Element render() {
|
||||
Element node = new Element.html('<div class="$_cssName"></div>');
|
||||
|
||||
if (_nestedContainer) {
|
||||
container = new Element.html('<div class="scroll-container"></div>');
|
||||
node.nodes.add(container);
|
||||
} else {
|
||||
container = node;
|
||||
}
|
||||
container = new Element.html('<div class="scroll-container"></div>');
|
||||
node.nodes.add(container);
|
||||
} else {
|
||||
container = node;
|
||||
}
|
||||
|
||||
if (_scrollable) {
|
||||
scroller = new Scroller(container,
|
||||
_vertical /* verticalScrollEnabled */,
|
||||
!_vertical /* horizontalScrollEnabled */,
|
||||
true /* momementumEnabled */);
|
||||
scroller = new Scroller(
|
||||
container,
|
||||
_vertical /* verticalScrollEnabled */,
|
||||
!_vertical /* horizontalScrollEnabled */,
|
||||
true /* momementumEnabled */);
|
||||
if (_showScrollbar) {
|
||||
_scrollbar = new Scrollbar(scroller);
|
||||
}
|
||||
|
@ -80,7 +81,9 @@ class CompositeView extends View {
|
|||
}
|
||||
|
||||
void removeChild(View view) {
|
||||
childViews = childViews.where((e) { return view != e; }).toList();
|
||||
childViews = childViews.where((e) {
|
||||
return view != e;
|
||||
}).toList();
|
||||
// TODO(rnystrom): Container shouldn't be null. Remove this check.
|
||||
if (container != null) {
|
||||
view.node.remove();
|
||||
|
|
|
@ -26,9 +26,8 @@ class ConveyorView extends CompositeView {
|
|||
Timer animationTimer;
|
||||
|
||||
ConveyorView()
|
||||
: super('conveyor-view', true),
|
||||
animationTimer = null {
|
||||
}
|
||||
: super('conveyor-view', true),
|
||||
animationTimer = null {}
|
||||
|
||||
Element render() {
|
||||
final result = super.render();
|
||||
|
@ -63,8 +62,9 @@ class ConveyorView extends CompositeView {
|
|||
|
||||
if (animate) {
|
||||
animationTimer = new Timer(
|
||||
new Duration(milliseconds: ((durationSeconds * 1000).toInt())),
|
||||
() { _onAnimationEnd(); });
|
||||
new Duration(milliseconds: ((durationSeconds * 1000).toInt())), () {
|
||||
_onAnimationEnd();
|
||||
});
|
||||
}
|
||||
// TODO(mattsh), we should set the visibility to hide everything but the
|
||||
// selected view.
|
||||
|
|
|
@ -47,7 +47,7 @@ class MeasureText {
|
|||
String quickTruncate(String text, num lineWidth, int maxLines) {
|
||||
int targetLength = lineWidth * maxLines ~/ _typicalCharLength;
|
||||
// Advance to next word break point.
|
||||
while(targetLength < text.length && !isWhitespace(text[targetLength])) {
|
||||
while (targetLength < text.length && !isWhitespace(text[targetLength])) {
|
||||
targetLength++;
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,8 @@ class MeasureText {
|
|||
* This function is safe to call with [:sb == null:] in which case just the
|
||||
* line count is returned.
|
||||
*/
|
||||
int addLineBrokenText(StringBuffer sb, String text, num lineWidth,
|
||||
int maxLines) {
|
||||
int addLineBrokenText(
|
||||
StringBuffer sb, String text, num lineWidth, int maxLines) {
|
||||
// Strip surrounding whitespace. This ensures we create zero lines if there
|
||||
// is no visible text.
|
||||
text = text.trim();
|
||||
|
@ -112,8 +112,7 @@ class MeasureText {
|
|||
return lines;
|
||||
}
|
||||
|
||||
void lineBreak(String text, num lineWidth, int maxLines,
|
||||
Function callback) {
|
||||
void lineBreak(String text, num lineWidth, int maxLines, Function callback) {
|
||||
_context.font = font;
|
||||
int lines = 0;
|
||||
num currentLength = 0;
|
||||
|
@ -128,8 +127,8 @@ class MeasureText {
|
|||
// Treat the char after the end of the string as whitespace.
|
||||
bool whitespace = i == len || isWhitespace(text[i]);
|
||||
if (whitespace && !lastWhitespace) {
|
||||
num wordLength = _context.measureText(text.substring(
|
||||
wordStartIndex, i)).width;
|
||||
num wordLength =
|
||||
_context.measureText(text.substring(wordStartIndex, i)).width;
|
||||
// TODO(jimhug): Replace the line above with this one to workaround
|
||||
// dartium bug - error: unimplemented code
|
||||
// num wordLength = (i - wordStartIndex) * 17;
|
||||
|
@ -146,7 +145,7 @@ class MeasureText {
|
|||
return;
|
||||
}
|
||||
startIndex = wordStartIndex;
|
||||
currentLength = wordLength;
|
||||
currentLength = wordLength;
|
||||
}
|
||||
lastWordEndIndex = i;
|
||||
currentLength += _spaceLength;
|
||||
|
|
|
@ -8,10 +8,10 @@ class PageState {
|
|||
final ObservableValue<int> current;
|
||||
final ObservableValue<int> target;
|
||||
final ObservableValue<int> length;
|
||||
PageState() :
|
||||
current = new ObservableValue<int>(0),
|
||||
target = new ObservableValue<int>(0),
|
||||
length = new ObservableValue<int>(1);
|
||||
PageState()
|
||||
: current = new ObservableValue<int>(0),
|
||||
target = new ObservableValue<int>(0),
|
||||
length = new ObservableValue<int>(1);
|
||||
}
|
||||
|
||||
/** Simplifies using a PageNumberView and PagedColumnView together. */
|
||||
|
@ -20,8 +20,8 @@ class PagedContentView extends CompositeView {
|
|||
final PageState pages;
|
||||
|
||||
PagedContentView(this.content)
|
||||
: super('paged-content'),
|
||||
pages = new PageState() {
|
||||
: super('paged-content'),
|
||||
pages = new PageState() {
|
||||
addChild(new PagedColumnView(pages, content));
|
||||
addChild(new PageNumberView(pages));
|
||||
}
|
||||
|
@ -85,7 +85,6 @@ class PageNumberView extends View {
|
|||
* the number of pages using [:scrollWidth:] and [:offsetWidth:].
|
||||
*/
|
||||
class PagedColumnView extends View {
|
||||
|
||||
static const MIN_THROW_PAGE_FRACTION = 0.01;
|
||||
final View contentView;
|
||||
|
||||
|
@ -112,15 +111,10 @@ class PagedColumnView extends View {
|
|||
// the scroller configured the default way.
|
||||
|
||||
// TODO(jacobr): use named arguments when available.
|
||||
scroller = new Scroller(
|
||||
_container,
|
||||
false /* verticalScrollEnabled */,
|
||||
true /* horizontalScrollEnabled */,
|
||||
true /* momementumEnabled */,
|
||||
() {
|
||||
return new Size(_getViewLength(_container), 1);
|
||||
},
|
||||
Scroller.FAST_SNAP_DECELERATION_FACTOR);
|
||||
scroller = new Scroller(_container, false /* verticalScrollEnabled */,
|
||||
true /* horizontalScrollEnabled */, true /* momementumEnabled */, () {
|
||||
return new Size(_getViewLength(_container), 1);
|
||||
}, Scroller.FAST_SNAP_DECELERATION_FACTOR);
|
||||
|
||||
scroller.onDecelStart.listen(_snapToPage);
|
||||
scroller.onScrollerDragEnd.listen(_snapToPage);
|
||||
|
@ -193,8 +187,8 @@ class PagedColumnView extends View {
|
|||
int pageLength = 1;
|
||||
scheduleMicrotask(() {
|
||||
if (_container.scrollWidth > _container.offset.width) {
|
||||
pageLength = (_container.scrollWidth / _computePageSize(_container))
|
||||
.ceil();
|
||||
pageLength =
|
||||
(_container.scrollWidth / _computePageSize(_container)).ceil();
|
||||
}
|
||||
pageLength = Math.max(pageLength, 1);
|
||||
|
||||
|
@ -236,13 +230,14 @@ class PagedColumnView extends View {
|
|||
pageNumber = pageNumber.round();
|
||||
} else {
|
||||
if (currentPageNumber == pageNumber.round() &&
|
||||
(pageNumber - currentPageNumber).abs() > MIN_THROW_PAGE_FRACTION &&
|
||||
-current + _viewportSize < _getViewLength(_container) &&
|
||||
current < 0) {
|
||||
(pageNumber - currentPageNumber).abs() > MIN_THROW_PAGE_FRACTION &&
|
||||
-current + _viewportSize < _getViewLength(_container) &&
|
||||
current < 0) {
|
||||
// The user is trying to throw so we want to round up to the
|
||||
// nearest page in the direction they are throwing.
|
||||
pageNumber = currentTarget < current
|
||||
? currentPageNumber + 1 : currentPageNumber - 1;
|
||||
? currentPageNumber + 1
|
||||
: currentPageNumber - 1;
|
||||
} else {
|
||||
pageNumber = pageNumber.round();
|
||||
}
|
||||
|
@ -266,8 +261,8 @@ class PagedColumnView extends View {
|
|||
|
||||
// Figure out how many columns we're rendering.
|
||||
// The algorithm ensures we're bigger than the specified min size.
|
||||
int perPage = Math.max(1,
|
||||
(_viewportSize + _columnGap) ~/ (_columnWidth + _columnGap));
|
||||
int perPage = Math.max(
|
||||
1, (_viewportSize + _columnGap) ~/ (_columnWidth + _columnGap));
|
||||
|
||||
// Divide up the viewport between the columns.
|
||||
int columnSize = (_viewportSize - (perPage - 1) * _columnGap) ~/ perPage;
|
||||
|
|
|
@ -138,7 +138,7 @@ class SliderMenu extends View {
|
|||
}
|
||||
|
||||
void selectNext(bool animate) {
|
||||
final result = node.querySelector('.sm-item.sel').nextElementSibling;
|
||||
final result = node.querySelector('.sm-item.sel').nextElementSibling;
|
||||
if (result != null) {
|
||||
selectItem(result, animate);
|
||||
}
|
||||
|
@ -159,7 +159,8 @@ class SliderMenu extends View {
|
|||
// calculate where we want to put the triangle
|
||||
scheduleMicrotask(() {
|
||||
num x = selectedItem.offset.left +
|
||||
selectedItem.offset.width / 2 - TRIANGLE_WIDTH / 2;
|
||||
selectedItem.offset.width / 2 -
|
||||
TRIANGLE_WIDTH / 2;
|
||||
_moveIndicator(x, animate);
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -19,7 +19,6 @@ part 'MeasureText.dart';
|
|||
part 'PagedViews.dart';
|
||||
part 'SliderMenu.dart';
|
||||
|
||||
|
||||
// TODO(rnystrom): Note! This class is undergoing heavy construction. It will
|
||||
// temporary support both some old and some new ways of doing things until all
|
||||
// subclasses are refactored to use the new way. There will be some scaffolding
|
||||
|
@ -43,15 +42,13 @@ class View implements Positionable {
|
|||
// object instead, and integrating with built in CSS properties.
|
||||
final Map<String, String> customStyle;
|
||||
|
||||
View()
|
||||
: customStyle = new Map<String, String>();
|
||||
View() : customStyle = new Map<String, String>();
|
||||
|
||||
View.fromNode(Element this._node)
|
||||
: customStyle = new Map<String, String>();
|
||||
View.fromNode(Element this._node) : customStyle = new Map<String, String>();
|
||||
|
||||
View.html(String html)
|
||||
: customStyle = new Map<String, String>(),
|
||||
_node = new Element.html(html);
|
||||
: customStyle = new Map<String, String>(),
|
||||
_node = new Element.html(html);
|
||||
|
||||
// TODO(rnystrom): Get rid of this when all views are refactored to not use
|
||||
// it.
|
||||
|
@ -111,9 +108,9 @@ class View implements Positionable {
|
|||
* document or not.
|
||||
*/
|
||||
bool get isInDocument {
|
||||
return _node != null
|
||||
&& node.ownerDocument is HtmlDocument
|
||||
&& (node.ownerDocument as HtmlDocument).body.contains(node);
|
||||
return _node != null &&
|
||||
node.ownerDocument is HtmlDocument &&
|
||||
(node.ownerDocument as HtmlDocument).body.contains(node);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,7 +139,9 @@ class View implements Positionable {
|
|||
* Override this to generate the DOM structure for the view.
|
||||
*/
|
||||
// TODO(rnystrom): make this method abstract, see b/5015671
|
||||
Element render() { throw 'abstract'; }
|
||||
Element render() {
|
||||
throw 'abstract';
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to perform initialization behavior that requires access to
|
||||
|
@ -310,7 +309,6 @@ class View implements Positionable {
|
|||
_resizeSubscription = window.onResize.listen(handler);
|
||||
}
|
||||
|
||||
|
||||
// Trigger the initial layout.
|
||||
doLayout();
|
||||
}
|
||||
|
@ -358,8 +356,8 @@ class View implements Positionable {
|
|||
// TODO(10459): code should not use Completer.sync.
|
||||
Completer sizeCompleter = new Completer<Size>.sync();
|
||||
scheduleMicrotask(() {
|
||||
sizeCompleter.complete(
|
||||
new Size(_node.client.width, _node.client.height));
|
||||
sizeCompleter
|
||||
.complete(new Size(_node.client.width, _node.client.height));
|
||||
});
|
||||
layout.measureLayout(sizeCompleter.future, changed);
|
||||
} else {
|
||||
|
|
|
@ -33,15 +33,16 @@ void main() {
|
|||
getStoryNode() => swarm.frontView.storyView.node;
|
||||
|
||||
getView(Section section) {
|
||||
return CollectionUtils.find(swarm.frontView.sections.childViews,
|
||||
(view) => view.section == section);
|
||||
return CollectionUtils.find(
|
||||
swarm.frontView.sections.childViews, (view) => view.section == section);
|
||||
}
|
||||
|
||||
getHistory(Article article) {
|
||||
final feed = article.dataSource;
|
||||
return {
|
||||
'section': CollectionUtils.find(swarm.sections,
|
||||
(s) => s.feeds.indexOf(feed, 0) >= 0).id,
|
||||
'section': CollectionUtils
|
||||
.find(swarm.sections, (s) => s.feeds.indexOf(feed, 0) >= 0)
|
||||
.id,
|
||||
'feed': feed.id,
|
||||
'article': article.id
|
||||
};
|
||||
|
@ -113,7 +114,6 @@ click(Element element) {
|
|||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
|
||||
/** A proxy so we can intercept history calls */
|
||||
class UIStateProxy extends SwarmState {
|
||||
List<Map<String, String>> history;
|
||||
|
@ -153,6 +153,7 @@ void _serialInvokeAsync(List closures) {
|
|||
Timer.run(expectAsync(invokeNext));
|
||||
}
|
||||
}
|
||||
|
||||
Timer.run(expectAsync(invokeNext));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,33 +6,33 @@ part of layout_tests;
|
|||
class AdaptiveLayout {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : 'auto minmax(min-content, 1fr)',
|
||||
'grid-rows' : 'auto minmax(min-content, 1fr) auto',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': 'auto minmax(min-content, 1fr)',
|
||||
'grid-rows': 'auto minmax(min-content, 1fr) auto',
|
||||
},
|
||||
'#title' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '1',
|
||||
'#title': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '1',
|
||||
},
|
||||
'#score' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '3',
|
||||
'#score': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '3',
|
||||
},
|
||||
'#stats' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '2',
|
||||
'grid-row-align' : 'start',
|
||||
'#stats': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '2',
|
||||
'grid-row-align': 'start',
|
||||
},
|
||||
'#board' : const {
|
||||
'grid-column' : '2',
|
||||
'grid-row' : '1',
|
||||
'grid-row-span' : '2',
|
||||
'#board': const {
|
||||
'grid-column': '2',
|
||||
'grid-row': '1',
|
||||
'grid-row-span': '2',
|
||||
},
|
||||
'#controls' : const {
|
||||
'grid-column' : '2',
|
||||
'grid-row' : '3',
|
||||
'grid-column-align' : 'center',
|
||||
'#controls': const {
|
||||
'grid-column': '2',
|
||||
'grid-row': '3',
|
||||
'grid-column-align': 'center',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -40,26 +40,26 @@ class AdaptiveLayout {
|
|||
class SourceIndependencePortrait {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-template' : '"ta" "sa" "bb" "cc"',
|
||||
'grid-columns' : 'auto minmax(min-content, 1fr)',
|
||||
'grid-rows' : 'auto auto minmax(min-content, 1fr) auto',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-template': '"ta" "sa" "bb" "cc"',
|
||||
'grid-columns': 'auto minmax(min-content, 1fr)',
|
||||
'grid-rows': 'auto auto minmax(min-content, 1fr) auto',
|
||||
},
|
||||
'#title' : const {
|
||||
'grid-cell' : '"t"',
|
||||
'#title': const {
|
||||
'grid-cell': '"t"',
|
||||
},
|
||||
'#score' : const {
|
||||
'grid-cell' : '"s"',
|
||||
'#score': const {
|
||||
'grid-cell': '"s"',
|
||||
},
|
||||
'#stats' : const {
|
||||
'grid-cell' : '"a"',
|
||||
'#stats': const {
|
||||
'grid-cell': '"a"',
|
||||
},
|
||||
'#board' : const {
|
||||
'grid-cell' : '"b"',
|
||||
'#board': const {
|
||||
'grid-cell': '"b"',
|
||||
},
|
||||
'#controls' : const {
|
||||
'grid-cell' : '"c"',
|
||||
'#controls': const {
|
||||
'grid-cell': '"c"',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -67,26 +67,26 @@ class SourceIndependencePortrait {
|
|||
class SourceIndependenceLandscape {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-template' : '"tb" "ab" "sc"',
|
||||
'grid-columns' : 'auto minmax(min-content, 1fr)',
|
||||
'grid-rows' : 'auto minmax(min-content, 1fr) auto',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-template': '"tb" "ab" "sc"',
|
||||
'grid-columns': 'auto minmax(min-content, 1fr)',
|
||||
'grid-rows': 'auto minmax(min-content, 1fr) auto',
|
||||
},
|
||||
'#title' : const {
|
||||
'grid-cell' : '"t"',
|
||||
'#title': const {
|
||||
'grid-cell': '"t"',
|
||||
},
|
||||
'#score' : const {
|
||||
'grid-cell' : '"s"',
|
||||
'#score': const {
|
||||
'grid-cell': '"s"',
|
||||
},
|
||||
'#stats' : const {
|
||||
'grid-cell' : '"a"',
|
||||
'#stats': const {
|
||||
'grid-cell': '"a"',
|
||||
},
|
||||
'#board' : const {
|
||||
'grid-cell' : '"b"',
|
||||
'#board': const {
|
||||
'grid-cell': '"b"',
|
||||
},
|
||||
'#controls' : const {
|
||||
'grid-cell' : '"c"',
|
||||
'#controls': const {
|
||||
'grid-cell': '"c"',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -94,33 +94,34 @@ class SourceIndependenceLandscape {
|
|||
class GridLayering {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '"start" auto "track-start" 0.5fr "thumb-start" auto "fill-split" auto "thumb-end" 0.5fr "track-end" auto "end"',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns':
|
||||
'"start" auto "track-start" 0.5fr "thumb-start" auto "fill-split" auto "thumb-end" 0.5fr "track-end" auto "end"',
|
||||
},
|
||||
'#lower-label' : const {
|
||||
'grid-column' : '"start"',
|
||||
'#lower-label': const {
|
||||
'grid-column': '"start"',
|
||||
},
|
||||
'#track' : const {
|
||||
'grid-column' : '"track-start" "track-end"',
|
||||
'grid-row-align' : 'center',
|
||||
'#track': const {
|
||||
'grid-column': '"track-start" "track-end"',
|
||||
'grid-row-align': 'center',
|
||||
},
|
||||
'#upper-label' : const {
|
||||
'grid-column' : '"track-end"',
|
||||
'#upper-label': const {
|
||||
'grid-column': '"track-end"',
|
||||
},
|
||||
'#lower-fill' : const {
|
||||
'grid-column' : '"track-start" "fill-split"',
|
||||
'grid-row-align' : 'center',
|
||||
'grid-layer' : '5',
|
||||
'#lower-fill': const {
|
||||
'grid-column': '"track-start" "fill-split"',
|
||||
'grid-row-align': 'center',
|
||||
'grid-layer': '5',
|
||||
},
|
||||
'#upper-fill' : const {
|
||||
'grid-column' : '"fill-split" "track-end"',
|
||||
'grid-row-align' : 'center',
|
||||
'grid-layer' : '5',
|
||||
'#upper-fill': const {
|
||||
'grid-column': '"fill-split" "track-end"',
|
||||
'grid-row-align': 'center',
|
||||
'grid-layer': '5',
|
||||
},
|
||||
'#thumb' : const {
|
||||
'grid-column' : '"thumb-start" "thumb-end"',
|
||||
'grid-layer' : '10',
|
||||
'#thumb': const {
|
||||
'grid-column': '"thumb-start" "thumb-end"',
|
||||
'grid-layer': '10',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -128,14 +129,14 @@ class GridLayering {
|
|||
class GridLines_5 {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '150px 1fr',
|
||||
'grid-rows' : '50px 1fr 50px',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '150px 1fr',
|
||||
'grid-rows': '50px 1fr 50px',
|
||||
},
|
||||
'#item1' : const {
|
||||
'grid-column' : '2',
|
||||
'grid-row' : '1 4',
|
||||
'#item1': const {
|
||||
'grid-column': '2',
|
||||
'grid-row': '1 4',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -143,14 +144,14 @@ class GridLines_5 {
|
|||
class GridLines_6 {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '150px "item1-start" 1fr "item1-end"',
|
||||
'grid-rows' : '"item1-start" 50px 1fr 50px "item1-end"',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '150px "item1-start" 1fr "item1-end"',
|
||||
'grid-rows': '"item1-start" 50px 1fr 50px "item1-end"',
|
||||
},
|
||||
'#item1' : const {
|
||||
'grid-column' : '"item1-start" "item1-end"',
|
||||
'grid-row' : '"item1-start" "item1-end"',
|
||||
'#item1': const {
|
||||
'grid-column': '"item1-start" "item1-end"',
|
||||
'grid-row': '"item1-start" "item1-end"',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -158,20 +159,20 @@ class GridLines_6 {
|
|||
class GridCells {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-template' : '"ad" "bd" "cd"',
|
||||
'grid-columns' : '150px 1fr',
|
||||
'grid-rows' : '50px 1fr 50px',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-template': '"ad" "bd" "cd"',
|
||||
'grid-columns': '150px 1fr',
|
||||
'grid-rows': '50px 1fr 50px',
|
||||
},
|
||||
'#item2' : const {
|
||||
'grid-cell' : '"b"',
|
||||
'grid-row-align' : 'start',
|
||||
'#item2': const {
|
||||
'grid-cell': '"b"',
|
||||
'grid-row-align': 'start',
|
||||
},
|
||||
'#item3' : const {
|
||||
'grid-cell' : '"b"',
|
||||
'grid-column-align' : 'end',
|
||||
'grid-row-align' : 'end',
|
||||
'#item3': const {
|
||||
'grid-cell': '"b"',
|
||||
'grid-column-align': 'end',
|
||||
'grid-row-align': 'end',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -179,14 +180,14 @@ class GridCells {
|
|||
class StartEndingGridlines11a {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '50px 1fr',
|
||||
'grid-rows' : '"first" 250px 1fr 250px "last"',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '50px 1fr',
|
||||
'grid-rows': '"first" 250px 1fr 250px "last"',
|
||||
},
|
||||
'#item' : const {
|
||||
'grid-column' : '1 3',
|
||||
'grid-row' : '"first" "last"',
|
||||
'#item': const {
|
||||
'grid-column': '1 3',
|
||||
'grid-row': '"first" "last"',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -194,14 +195,14 @@ class StartEndingGridlines11a {
|
|||
class StartEndingGridlines11b {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '50px 1fr',
|
||||
'grid-rows' : '"first" 250px 1fr 250px "last"',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '50px 1fr',
|
||||
'grid-rows': '"first" 250px 1fr 250px "last"',
|
||||
},
|
||||
'#item' : const {
|
||||
'grid-column' : 'start end',
|
||||
'grid-row' : 'start end',
|
||||
'#item': const {
|
||||
'grid-column': 'start end',
|
||||
'grid-row': 'start end',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -209,22 +210,22 @@ class StartEndingGridlines11b {
|
|||
class RepeatingColumnsRows {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '10px ("content" 1fr 10px) [4]',
|
||||
'grid-rows' : '1fr',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '10px ("content" 1fr 10px) [4]',
|
||||
'grid-rows': '1fr',
|
||||
},
|
||||
'#col2' : const {
|
||||
'grid-column' : '2',
|
||||
'#col2': const {
|
||||
'grid-column': '2',
|
||||
},
|
||||
'#col4' : const {
|
||||
'grid-column' : '4',
|
||||
'#col4': const {
|
||||
'grid-column': '4',
|
||||
},
|
||||
'#col6' : const {
|
||||
'grid-column' : '6',
|
||||
'#col6': const {
|
||||
'grid-column': '6',
|
||||
},
|
||||
'#col8' : const {
|
||||
'grid-column' : '8',
|
||||
'#col8': const {
|
||||
'grid-column': '8',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -232,22 +233,22 @@ class RepeatingColumnsRows {
|
|||
class AnonymousGridCells {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-rows' : '"header" auto "main" 1fr "footer" auto',
|
||||
'grid-columns' : '1fr',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-rows': '"header" auto "main" 1fr "footer" auto',
|
||||
'grid-columns': '1fr',
|
||||
},
|
||||
'#header' : const {
|
||||
'grid-row' : '"header"',
|
||||
'grid-column' : 'start',
|
||||
'#header': const {
|
||||
'grid-row': '"header"',
|
||||
'grid-column': 'start',
|
||||
},
|
||||
'#main' : const {
|
||||
'grid-row' : '"main"',
|
||||
'grid-column' : 'start',
|
||||
'#main': const {
|
||||
'grid-row': '"main"',
|
||||
'grid-column': 'start',
|
||||
},
|
||||
'#footer' : const {
|
||||
'grid-row' : '"footer"',
|
||||
'grid-column' : 'start',
|
||||
'#footer': const {
|
||||
'grid-row': '"footer"',
|
||||
'grid-column': 'start',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -255,26 +256,26 @@ class AnonymousGridCells {
|
|||
class ImplicitColumnsRows {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '20px',
|
||||
'grid-rows' : '20px',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '20px',
|
||||
'grid-rows': '20px',
|
||||
},
|
||||
'#A' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '1',
|
||||
'grid-column-align' : 'start',
|
||||
'grid-row-align' : 'start',
|
||||
'#A': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '1',
|
||||
'grid-column-align': 'start',
|
||||
'grid-row-align': 'start',
|
||||
},
|
||||
'#B' : const {
|
||||
'grid-column' : '5',
|
||||
'grid-row' : '1',
|
||||
'grid-row-span' : '2',
|
||||
'#B': const {
|
||||
'grid-column': '5',
|
||||
'grid-row': '1',
|
||||
'grid-row-span': '2',
|
||||
},
|
||||
'#C' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '2',
|
||||
'grid-column-span' : '2',
|
||||
'#C': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '2',
|
||||
'grid-column-span': '2',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -282,22 +283,22 @@ class ImplicitColumnsRows {
|
|||
class AlignGridItems {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '1fr 1fr',
|
||||
'grid-rows' : '1fr 1fr',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '1fr 1fr',
|
||||
'grid-rows': '1fr 1fr',
|
||||
},
|
||||
'#A' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '1',
|
||||
'grid-column-align' : 'start',
|
||||
'grid-row-align' : 'start',
|
||||
'#A': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '1',
|
||||
'grid-column-align': 'start',
|
||||
'grid-row-align': 'start',
|
||||
},
|
||||
'#B' : const {
|
||||
'grid-column' : '2',
|
||||
'grid-row' : '2',
|
||||
'grid-column-align' : 'end',
|
||||
'grid-row-align' : 'end',
|
||||
'#B': const {
|
||||
'grid-column': '2',
|
||||
'grid-row': '2',
|
||||
'grid-column-align': 'end',
|
||||
'grid-row-align': 'end',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -305,42 +306,42 @@ class AlignGridItems {
|
|||
class DrawOrderGridItems {
|
||||
// selector, properties<propertyName, value>
|
||||
static const selectors = const {
|
||||
'#grid' : const {
|
||||
'display' : '-dart-grid',
|
||||
'grid-columns' : '1fr 1fr',
|
||||
'grid-rows' : '1fr 1fr',
|
||||
'#grid': const {
|
||||
'display': '-dart-grid',
|
||||
'grid-columns': '1fr 1fr',
|
||||
'grid-rows': '1fr 1fr',
|
||||
},
|
||||
'#A' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '2',
|
||||
'grid-column-span' : '2',
|
||||
'grid-row-align' : 'end',
|
||||
'#A': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '2',
|
||||
'grid-column-span': '2',
|
||||
'grid-row-align': 'end',
|
||||
},
|
||||
'#B' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '1',
|
||||
'grid-layer' : '10',
|
||||
'#B': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '1',
|
||||
'grid-layer': '10',
|
||||
},
|
||||
'#C' : const {
|
||||
'grid-column' : '2',
|
||||
'grid-row' : '1',
|
||||
'grid-row-align' : 'start',
|
||||
'margin-left' : '-20px',
|
||||
'#C': const {
|
||||
'grid-column': '2',
|
||||
'grid-row': '1',
|
||||
'grid-row-align': 'start',
|
||||
'margin-left': '-20px',
|
||||
},
|
||||
'#D' : const {
|
||||
'grid-column' : '2',
|
||||
'grid-row' : '2',
|
||||
'grid-column-align' : 'end',
|
||||
'grid-row-align' : 'start',
|
||||
'#D': const {
|
||||
'grid-column': '2',
|
||||
'grid-row': '2',
|
||||
'grid-column-align': 'end',
|
||||
'grid-row-align': 'start',
|
||||
},
|
||||
'#E' : const {
|
||||
'grid-column' : '1',
|
||||
'grid-row' : '1',
|
||||
'grid-column-span' : '2',
|
||||
'grid-row-span' : '2',
|
||||
'grid-layer' : '5',
|
||||
'grid-column-align' : 'center',
|
||||
'grid-row-align' : 'center',
|
||||
'#E': const {
|
||||
'grid-column': '1',
|
||||
'grid-row': '1',
|
||||
'grid-column-span': '2',
|
||||
'grid-row-span': '2',
|
||||
'grid-layer': '5',
|
||||
'grid-column-align': 'center',
|
||||
'grid-row-align': 'center',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -66,8 +66,14 @@ void addGridStyles(String width, String height, [String margin = '']) {
|
|||
|
||||
void _addColorStyles() {
|
||||
final grid = document.body.querySelector('#grid');
|
||||
final colors = const [ 'darkred', 'darkorange', 'darkgoldenrod',
|
||||
'darkgreen', 'darkblue', 'darkviolet'];
|
||||
final colors = const [
|
||||
'darkred',
|
||||
'darkorange',
|
||||
'darkgoldenrod',
|
||||
'darkgreen',
|
||||
'darkblue',
|
||||
'darkviolet'
|
||||
];
|
||||
int c = 0;
|
||||
var node = grid.children[0];
|
||||
while (node != null) {
|
||||
|
@ -79,8 +85,7 @@ void _addColorStyles() {
|
|||
}
|
||||
|
||||
class MockCompositeView extends CompositeView {
|
||||
MockCompositeView(String id, Map styles, List childViews)
|
||||
: super('') {
|
||||
MockCompositeView(String id, Map styles, List childViews) : super('') {
|
||||
node.id = id;
|
||||
CollectionUtils.copyMap(customStyle, styles);
|
||||
|
||||
|
@ -92,8 +97,8 @@ class MockCompositeView extends CompositeView {
|
|||
|
||||
class MockView extends View {
|
||||
MockView(String id, Map styles)
|
||||
: super.fromNode(new Element.html(
|
||||
'<div class="grid-item">MockView-$id</div>')) {
|
||||
: super.fromNode(
|
||||
new Element.html('<div class="grid-item">MockView-$id</div>')) {
|
||||
node.id = id;
|
||||
CollectionUtils.copyMap(customStyle, styles);
|
||||
// TODO(jmesserly): this is needed to get horizontal content-sizing to work
|
||||
|
@ -101,7 +106,6 @@ class MockView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void printMetrics(String example) {
|
||||
final node = document.body.querySelector('#grid');
|
||||
String exampleId = example.split(' ')[0];
|
||||
|
|
|
@ -8,7 +8,7 @@ testAbstractObservable() {
|
|||
group('addChangeListener()', () {
|
||||
test('adding the same listener twice returns false the second time', () {
|
||||
final target = new AbstractObservable();
|
||||
final listener = (e) { };
|
||||
final listener = (e) {};
|
||||
|
||||
expect(target.addChangeListener(listener), isTrue);
|
||||
expect(target.addChangeListener(listener), isFalse);
|
||||
|
@ -17,10 +17,10 @@ testAbstractObservable() {
|
|||
test('modifies listeners list', () {
|
||||
// check that add/remove works, see contents of listeners too
|
||||
final target = new AbstractObservable();
|
||||
final l1 = (e) { };
|
||||
final l2 = (e) { };
|
||||
final l3 = (e) { };
|
||||
final l4 = (e) { };
|
||||
final l1 = (e) {};
|
||||
final l2 = (e) {};
|
||||
final l3 = (e) {};
|
||||
final l4 = (e) {};
|
||||
|
||||
expect(target.listeners, orderedEquals([]));
|
||||
|
||||
|
|
|
@ -9,21 +9,30 @@ testChangeEvent() {
|
|||
// create property, list, global and check the proper initialization.
|
||||
final target = new AbstractObservable();
|
||||
|
||||
validateUpdate(
|
||||
new ChangeEvent.property(target, 'pK', 33, '12'),
|
||||
target, 'pK', null, 33, '12');
|
||||
validateUpdate(new ChangeEvent.property(target, 'pK', 33, '12'), target,
|
||||
'pK', null, 33, '12');
|
||||
|
||||
validateUpdate(
|
||||
new ChangeEvent.list(target, ChangeEvent.UPDATE, 3, 33, '12'),
|
||||
target, null, 3, 33, '12');
|
||||
target,
|
||||
null,
|
||||
3,
|
||||
33,
|
||||
'12');
|
||||
|
||||
validateInsert(
|
||||
new ChangeEvent.list(target, ChangeEvent.INSERT, 3, 33, null),
|
||||
target, null, 3, 33);
|
||||
target,
|
||||
null,
|
||||
3,
|
||||
33);
|
||||
|
||||
validateRemove(
|
||||
new ChangeEvent.list(target, ChangeEvent.REMOVE, 3, null, '12'),
|
||||
target, null, 3, '12');
|
||||
target,
|
||||
null,
|
||||
3,
|
||||
'12');
|
||||
|
||||
validateGlobal(
|
||||
new ChangeEvent.list(target, ChangeEvent.GLOBAL, null, null, null),
|
||||
|
|
|
@ -54,15 +54,15 @@ testObservableList() {
|
|||
called = false;
|
||||
expect(arr, orderedEquals([1, 2, 3, 1, 3, 4]));
|
||||
EventBatch.wrap((e) {
|
||||
arr.add(5); // 1 2 3 1 3 4(5)
|
||||
arr.add(6); // 1 2 3 1 3 4 5(6)
|
||||
arr[1] = arr[arr.length - 1]; // 1(6)3 1 3 4 5 6
|
||||
arr.add(7); // 1 6 3 1 3 4 5 6(7)
|
||||
arr[5] = arr[8]; // 1 6 3 1 3(7)5 6 7
|
||||
arr.add(42); // 1 6 3 1 3 7 5 6 7(42)
|
||||
expect(arr.removeAt(3), equals(1)); // 1 6 3( )3 7 5 6 7 42
|
||||
expect(arr.removeFirstElement(3), isTrue); // 1 6( ) 3 7 5 6 7 42
|
||||
expect(arr.removeLast(), equals(42)); // 1 6 3 7 5 6 7( )
|
||||
arr.add(5); // 1 2 3 1 3 4(5)
|
||||
arr.add(6); // 1 2 3 1 3 4 5(6)
|
||||
arr[1] = arr[arr.length - 1]; // 1(6)3 1 3 4 5 6
|
||||
arr.add(7); // 1 6 3 1 3 4 5 6(7)
|
||||
arr[5] = arr[8]; // 1 6 3 1 3(7)5 6 7
|
||||
arr.add(42); // 1 6 3 1 3 7 5 6 7(42)
|
||||
expect(arr.removeAt(3), equals(1)); // 1 6 3( )3 7 5 6 7 42
|
||||
expect(arr.removeFirstElement(3), isTrue); // 1 6( ) 3 7 5 6 7 42
|
||||
expect(arr.removeLast(), equals(42)); // 1 6 3 7 5 6 7( )
|
||||
expect(arr.removeAllElements(6), equals(2)); // 1( ) 3 7 5( )7
|
||||
called = true;
|
||||
})(null);
|
||||
|
@ -91,7 +91,9 @@ testObservableList() {
|
|||
arr.add(4);
|
||||
arr.add(10);
|
||||
arr.add(9);
|
||||
arr.sort((int a, int b) { return a - b; });
|
||||
arr.sort((int a, int b) {
|
||||
return a - b;
|
||||
});
|
||||
called = true;
|
||||
})(null);
|
||||
expect(called, isTrue);
|
||||
|
|
|
@ -33,7 +33,9 @@ testObservableValue() {
|
|||
expect(value.value, equals('foo'));
|
||||
|
||||
bool called = false;
|
||||
value.addChangeListener((summary) { called = true; });
|
||||
value.addChangeListener((summary) {
|
||||
called = true;
|
||||
});
|
||||
|
||||
// Set it to the same value.
|
||||
value.value = 'foo';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
library touchTests;
|
||||
|
||||
import 'dart:html'; // TODO(rnystrom): Only needed to tell architecture.py
|
||||
// that this is a web test. Come up with cleaner solution.
|
||||
// that this is a web test. Come up with cleaner solution.
|
||||
import 'package:unittest/html_config.dart';
|
||||
import 'package:unittest/unittest.dart';
|
||||
import '../../../swarm_ui_lib/touch/touch.dart';
|
||||
|
@ -39,8 +39,8 @@ class TestMomentumDelegate {
|
|||
Function onDecelerationEndCallback;
|
||||
|
||||
void onDecelerate(num x, num y,
|
||||
[num duration = 0, String timingFunction = null]) {
|
||||
onDecelerateCallback(x, y, duration, timingFunction);
|
||||
[num duration = 0, String timingFunction = null]) {
|
||||
onDecelerateCallback(x, y, duration, timingFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,8 +34,9 @@ void main() {
|
|||
return new Element.html('<div class="test"></div>');
|
||||
};
|
||||
|
||||
view.afterRenderFn = (node) { result = '${result}after'; };
|
||||
|
||||
view.afterRenderFn = (node) {
|
||||
result = '${result}after';
|
||||
};
|
||||
|
||||
view.addToDocument(document.body);
|
||||
expect(result, equals('renderafter'));
|
||||
|
@ -44,7 +45,9 @@ void main() {
|
|||
test('calls enterDocument()', () {
|
||||
final view = new TestView();
|
||||
bool entered = false;
|
||||
view.enterDocumentFn = () { entered = true; };
|
||||
view.enterDocumentFn = () {
|
||||
entered = true;
|
||||
};
|
||||
|
||||
view.addToDocument(document.body);
|
||||
expect(entered, isTrue);
|
||||
|
@ -56,7 +59,9 @@ void main() {
|
|||
final rendered = new Element.html('<div class="node"></div>');
|
||||
final view = new TestView();
|
||||
view.renderFn = () => rendered;
|
||||
view.afterRenderFn = (node) { expect(node, equals(rendered)); };
|
||||
view.afterRenderFn = (node) {
|
||||
expect(node, equals(rendered));
|
||||
};
|
||||
|
||||
view.addToDocument(document.body);
|
||||
});
|
||||
|
@ -69,7 +74,9 @@ void main() {
|
|||
|
||||
bool entered = false;
|
||||
final child = new TestView();
|
||||
child.enterDocumentFn = () { entered = true; };
|
||||
child.enterDocumentFn = () {
|
||||
entered = true;
|
||||
};
|
||||
|
||||
// Add the child.
|
||||
parent.childViews = [child];
|
||||
|
@ -83,7 +90,9 @@ void main() {
|
|||
|
||||
bool entered = false;
|
||||
final child = new TestView();
|
||||
child.enterDocumentFn = () { entered = true; };
|
||||
child.enterDocumentFn = () {
|
||||
entered = true;
|
||||
};
|
||||
|
||||
// Add the child.
|
||||
parent.childViews = [child];
|
||||
|
@ -98,7 +107,9 @@ void main() {
|
|||
|
||||
var entered = 0;
|
||||
final child = new TestView();
|
||||
child.enterDocumentFn = () { entered++; };
|
||||
child.enterDocumentFn = () {
|
||||
entered++;
|
||||
};
|
||||
|
||||
// Add the child.
|
||||
parent.childViews = [child];
|
||||
|
@ -120,7 +131,9 @@ void main() {
|
|||
|
||||
bool exited = false;
|
||||
final child = new TestView();
|
||||
child.exitDocumentFn = () { exited = true; };
|
||||
child.exitDocumentFn = () {
|
||||
exited = true;
|
||||
};
|
||||
|
||||
// Remove the child.
|
||||
parent.childViews = [];
|
||||
|
@ -134,7 +147,9 @@ void main() {
|
|||
|
||||
bool exited = false;
|
||||
final child = new TestView();
|
||||
child.exitDocumentFn = () { exited = true; };
|
||||
child.exitDocumentFn = () {
|
||||
exited = true;
|
||||
};
|
||||
|
||||
// Remove the child.
|
||||
parent.childViews = [];
|
||||
|
@ -149,7 +164,9 @@ void main() {
|
|||
|
||||
var exited = 0;
|
||||
final child = new TestView();
|
||||
child.exitDocumentFn = () { exited++; };
|
||||
child.exitDocumentFn = () {
|
||||
exited++;
|
||||
};
|
||||
|
||||
// Add the child.
|
||||
parent.childViews = [child];
|
||||
|
@ -169,10 +186,14 @@ void main() {
|
|||
var result = '';
|
||||
|
||||
final parent = new TestView();
|
||||
parent.enterDocumentFn = () { result = '${result}parent'; };
|
||||
parent.enterDocumentFn = () {
|
||||
result = '${result}parent';
|
||||
};
|
||||
|
||||
final child = new TestView();
|
||||
child.enterDocumentFn = () { result = '${result}child'; };
|
||||
child.enterDocumentFn = () {
|
||||
result = '${result}child';
|
||||
};
|
||||
|
||||
parent.childViews = [child];
|
||||
|
||||
|
@ -189,7 +210,9 @@ class TestView extends View {
|
|||
Function exitDocumentFn;
|
||||
List<View> childViews;
|
||||
|
||||
TestView() : super(), childViews = [] {
|
||||
TestView()
|
||||
: super(),
|
||||
childViews = [] {
|
||||
// Default behavior.
|
||||
renderFn = () => new Element.html('<div class="test"></div>');
|
||||
afterRenderFn = (node) {};
|
||||
|
@ -198,7 +221,15 @@ class TestView extends View {
|
|||
}
|
||||
|
||||
Element render() => renderFn();
|
||||
void afterRender(Element node) { afterRenderFn(node); }
|
||||
void enterDocument() { enterDocumentFn(); }
|
||||
void exitDocument() { exitDocumentFn(); }
|
||||
void afterRender(Element node) {
|
||||
afterRenderFn(node);
|
||||
}
|
||||
|
||||
void enterDocument() {
|
||||
enterDocumentFn();
|
||||
}
|
||||
|
||||
void exitDocument() {
|
||||
exitDocumentFn();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,14 +42,14 @@ void main(List<String> arguments) {
|
|||
*/
|
||||
void processArgs(List<String> arguments) {
|
||||
var parser = new ArgParser();
|
||||
parser.addOption("changed", help: "the file has changed since the last build",
|
||||
allowMultiple: true);
|
||||
parser.addOption("removed", help: "the file was removed since the last build",
|
||||
allowMultiple: true);
|
||||
parser.addOption("changed",
|
||||
help: "the file has changed since the last build", allowMultiple: true);
|
||||
parser.addOption("removed",
|
||||
help: "the file was removed since the last build", allowMultiple: true);
|
||||
parser.addFlag("clean", negatable: false, help: "remove any build artifacts");
|
||||
parser.addFlag("full", negatable: false, help: "perform a full build");
|
||||
parser.addFlag("machine",
|
||||
negatable: false, help: "produce warnings in a machine parseable format");
|
||||
negatable: false, help: "produce warnings in a machine parseable format");
|
||||
parser.addFlag("help", negatable: false, help: "display this help and exit");
|
||||
|
||||
var args = parser.parse(arguments);
|
||||
|
@ -85,11 +85,10 @@ void handleFullBuild() {
|
|||
var files = <String>[];
|
||||
|
||||
Directory.current.list(recursive: true).listen((entity) {
|
||||
if (entity is File) {
|
||||
files.add((entity as File).resolveSymbolicLinksSync());
|
||||
}
|
||||
},
|
||||
onDone: () => handleChangedFiles(files));
|
||||
if (entity is File) {
|
||||
files.add((entity as File).resolveSymbolicLinksSync());
|
||||
}
|
||||
}, onDone: () => handleChangedFiles(files));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,9 +101,7 @@ void handleChangedFiles(List<String> files) {
|
|||
/**
|
||||
* Process the given list of removed files.
|
||||
*/
|
||||
void handleRemovedFiles(List<String> files) {
|
||||
|
||||
}
|
||||
void handleRemovedFiles(List<String> files) {}
|
||||
|
||||
/**
|
||||
* Convert a .foo file to a .foobar file.
|
||||
|
|
|
@ -11,6 +11,4 @@ import '../bin/test.dart' as test2;
|
|||
* This test exists to ensure that the build_dart sample compiles without
|
||||
* errors.
|
||||
*/
|
||||
void main() {
|
||||
|
||||
}
|
||||
void main() {}
|
||||
|
|
|
@ -11,6 +11,4 @@ import '../test.dart' as test1;
|
|||
* This test exists to ensure that the build_dart_simple sample compiles
|
||||
* without errors.
|
||||
*/
|
||||
void main() {
|
||||
|
||||
}
|
||||
void main() {}
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'dart-ext:sample_extension';
|
|||
class RandomArray {
|
||||
static SendPort _port;
|
||||
|
||||
Future<List<int> > randomArray(int seed, int length) {
|
||||
Future<List<int>> randomArray(int seed, int length) {
|
||||
var completer = new Completer();
|
||||
var replyPort = new RawReceivePort();
|
||||
var args = new List(3);
|
||||
|
|
|
@ -54,15 +54,19 @@ Future testNativeExtensions(String snapshotKind) async {
|
|||
|
||||
// Copy sample_extension dart files and sample_extension tests to the
|
||||
// temporary test directory.
|
||||
for (var file in ['sample_synchronous_extension.dart',
|
||||
'sample_asynchronous_extension.dart',
|
||||
'test_sample_synchronous_extension.dart',
|
||||
'test_sample_asynchronous_extension.dart']) {
|
||||
for (var file in [
|
||||
'sample_synchronous_extension.dart',
|
||||
'sample_asynchronous_extension.dart',
|
||||
'test_sample_synchronous_extension.dart',
|
||||
'test_sample_asynchronous_extension.dart'
|
||||
]) {
|
||||
await copyFileToDirectory(join(sourceDirectory, file), testDirectory);
|
||||
}
|
||||
|
||||
for (var test in ['test_sample_synchronous_extension.dart',
|
||||
'test_sample_asynchronous_extension.dart']) {
|
||||
for (var test in [
|
||||
'test_sample_synchronous_extension.dart',
|
||||
'test_sample_asynchronous_extension.dart'
|
||||
]) {
|
||||
String script = join(testDirectory, test);
|
||||
String snapshot;
|
||||
if (snapshotKind == null) {
|
||||
|
@ -70,9 +74,7 @@ Future testNativeExtensions(String snapshotKind) async {
|
|||
} else {
|
||||
snapshot = join(testDirectory, "$test.snapshot");
|
||||
await run(Platform.executable,
|
||||
['--snapshot=$snapshot',
|
||||
'--snapshot-kind=$snapshotKind',
|
||||
script]);
|
||||
['--snapshot=$snapshot', '--snapshot-kind=$snapshotKind', script]);
|
||||
}
|
||||
|
||||
await run(Platform.executable, [snapshot]);
|
||||
|
|
|
@ -31,14 +31,18 @@ void checkNormal(List l) {
|
|||
// Count how many times each byte value occurs. Assert that the counts
|
||||
// are all within a reasonable (six-sigma) range.
|
||||
List counts = new List<int>.filled(256, 0);
|
||||
for (var e in l) { counts[e]++; }
|
||||
for (var e in l) {
|
||||
counts[e]++;
|
||||
}
|
||||
new RandomArray().randomArray(18, 256000).then(checkCorrelation(counts));
|
||||
}
|
||||
|
||||
Function checkCorrelation(List counts) {
|
||||
return (List l) {
|
||||
List counts_2 = new List<int>.filled(256, 0);
|
||||
for (var e in l) { counts_2[e]++; }
|
||||
for (var e in l) {
|
||||
counts_2[e]++;
|
||||
}
|
||||
var product = 0;
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
check(counts[i] < 1200, "counts[i] < 1200");
|
||||
|
|
Loading…
Reference in a new issue