Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into timob-9207

  • Loading branch information...
commit be0654248268fdd8e1e48f4cd5d1a71789bfa412 2 parents d9f218f + 77618a6
@billdawson billdawson authored
Showing with 637 additions and 284 deletions.
  1. +23 −4 android/modules/ui/src/java/ti/modules/titanium/ui/ScrollableViewProxy.java
  2. +91 −21 android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIScrollableView.java
  3. +10 −0 android/titanium/src/java/org/appcelerator/titanium/TiC.java
  4. +18 −4 apidoc/Titanium/App/App.yml
  5. +44 −15 apidoc/Titanium/UI/ScrollableView.yml
  6. +1 −4 drillbit/runtest
  7. +70 −2 drillbit/tests/ui.controls.js
  8. +8 −0 iphone/Classes/AppModule.m
  9. +1 −0  iphone/Classes/TiApp.mm
  10. +1 −0  iphone/Classes/TiBase.h
  11. +1 −0  iphone/Classes/TiBase.m
  12. +6 −0 iphone/Classes/TiModule.h
  13. +5 −0 iphone/Classes/TiModule.m
  14. +1 −1  iphone/Classes/TiUIScrollViewProxy.m
  15. +15 −6 iphone/Classes/TiUIScrollableView.m
  16. +15 −9 iphone/Classes/TiUITableView.m
  17. +156 −89 mobileweb/titanium/Ti/UI/ScrollableView.js
  18. +4 −3 mobileweb/titanium/Ti/UI/Tab.js
  19. +18 −7 mobileweb/titanium/Ti/UI/TabGroup.js
  20. +3 −0  mobileweb/titanium/Ti/UI/TableViewRow.js
  21. +3 −0  mobileweb/titanium/Ti/UI/TableViewSection.js
  22. +3 −2 mobileweb/titanium/Ti/UI/WebView.js
  23. +0 −5 mobileweb/titanium/Ti/XML.js
  24. +1 −1  mobileweb/titanium/Ti/_/Evented.js
  25. +46 −66 mobileweb/titanium/Ti/_/Gestures/Swipe.js
  26. +54 −39 mobileweb/titanium/Ti/_/UI/Element.js
  27. +25 −3 mobileweb/titanium/Ti/_/UI/SuperView.js
  28. +2 −2 mobileweb/titanium/Ti/_/UI/WebViewBridge.js
  29. +1 −1  mobileweb/titanium/Ti/_/lang.js
  30. BIN  support/iphone/titanium_prep
  31. +11 −0 support/mobileweb/compiler.py
View
27 android/modules/ui/src/java/ti/modules/titanium/ui/ScrollableViewProxy.java
@@ -210,13 +210,32 @@ public void setPagerTimeout()
}
}
- public void fireScroll(int to)
+ public void fireDragEnd(int currentPage, TiViewProxy currentView) {
+ if (hasListeners(TiC.EVENT_DRAGEND)) {
+ KrollDict options = new KrollDict();
+ options.put("view", currentView);
+ options.put("currentPage", currentPage);
+ fireEvent(TiC.EVENT_DRAGEND, options);
+ }
+ }
+
+ public void fireScrollEnd(int currentPage, TiViewProxy currentView)
+ {
+ if (hasListeners(TiC.EVENT_SCROLLEND)) {
+ KrollDict options = new KrollDict();
+ options.put("view", currentView);
+ options.put("currentPage", currentPage);
+ fireEvent(TiC.EVENT_SCROLLEND, options);
+ }
+ }
+
+ public void fireScroll(int currentPage, float currentPageAsFloat, TiViewProxy currentView)
{
if (hasListeners(TiC.EVENT_SCROLL)) {
KrollDict options = new KrollDict();
- options.put("index", to);
- options.put("view", this);
- options.put("currentPage", getView().getCurrentPage());
+ options.put("view", currentView);
+ options.put("currentPage", currentPage);
+ options.put("currentPageAsFloat", currentPageAsFloat);
fireEvent(TiC.EVENT_SCROLL, options);
}
}
View
112 android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIScrollableView.java
@@ -7,6 +7,7 @@
package ti.modules.titanium.ui.widget;
import java.util.ArrayList;
+import java.lang.Math;
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
@@ -91,33 +92,102 @@ public boolean onInterceptTouchEvent(MotionEvent event) {
pager.setAdapter(adapter);
pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener()
{
+ private boolean isValidScroll = false;
+ private boolean justFiredDragEnd = false;
+
@Override
- public void onPageSelected(int position)
+ public void onPageScrollStateChanged(int scrollState)
{
- super.onPageSelected(position);
- int oldIndex = mCurIndex;
- mCurIndex = position;
- if (mCurIndex >= 0) {
- if (oldIndex >=0 && oldIndex != mCurIndex && oldIndex < mViews.size()) {
- // Don't know what these focused and unfocused
- // events are good for, but they were in our previous
- // scrollable implementation.
- // cf. https://github.com/appcelerator/titanium_mobile/blob/20335d8603e2708b59a18bafbb91b7292278de8e/android/modules/ui/src/ti/modules/titanium/ui/widget/TiScrollableView.java#L260
- TiEventHelper.fireFocused(mViews.get(oldIndex));
- }
- TiEventHelper.fireUnfocused(mViews.get(mCurIndex));
- if (oldIndex >= 0) {
- // oldIndex will be -1 if the view has just
- // been created and is setting currentPage
- // to something other than 0. In that case we
- // don't want a scroll to fire.
- ((ScrollableViewProxy)proxy).fireScroll(mCurIndex);
+ if ((scrollState == ViewPager.SCROLL_STATE_IDLE) && isValidScroll) {
+ int oldIndex = mCurIndex;
+
+ if (mCurIndex >= 0) {
+ if (oldIndex >=0 && oldIndex != mCurIndex && oldIndex < mViews.size()) {
+ // Don't know what these focused and unfocused
+ // events are good for, but they were in our previous
+ // scrollable implementation.
+ // cf. https://github.com/appcelerator/titanium_mobile/blob/20335d8603e2708b59a18bafbb91b7292278de8e/android/modules/ui/src/ti/modules/titanium/ui/widget/TiScrollableView.java#L260
+ TiEventHelper.fireFocused(mViews.get(oldIndex));
+ }
+
+ TiEventHelper.fireUnfocused(mViews.get(mCurIndex));
+ if (oldIndex >= 0) {
+ // oldIndex will be -1 if the view has just
+ // been created and is setting currentPage
+ // to something other than 0. In that case we
+ // don't want a scrollEnd to fire.
+ ((ScrollableViewProxy)proxy).fireScrollEnd(mCurIndex, mViews.get(mCurIndex));
+ }
+
+ if (shouldShowPager()) {
+ showPager();
+ }
}
+
+ // If we don't use this state variable to check if it's a valid
+ // scroll, this event will fire when the view is first created
+ // because on creation, the scroll state is initialized to
+ // `idle` and this handler is called.
+ isValidScroll = false;
+ } else if (scrollState == ViewPager.SCROLL_STATE_SETTLING) {
+ ((ScrollableViewProxy)proxy).fireDragEnd(mCurIndex, mViews.get(mCurIndex));
+
+ // Note that we just fired a dragEnd so the `onPageSelected`
+ // handler below doesn't fire a `scrollEnd`. Read below comment.
+ justFiredDragEnd = true;
}
- if (shouldShowPager()) {
- showPager();
+ }
+
+ @Override
+ public void onPageSelected(int page)
+ {
+
+ // If we didn't just fire a `dragEnd` event then this is the case
+ // where a user drags the view and settles it on a different view.
+ // Since the OS settling logic is never run, the
+ // `onPageScrollStateChanged` handler is never run, and therefore
+ // we forgot to inform the Javascripters that the user just scrolled
+ // their thing.
+
+ if (!justFiredDragEnd && mCurIndex != -1) {
+ ((ScrollableViewProxy)proxy).fireScrollEnd(mCurIndex, mViews.get(mCurIndex));
+
+ if (shouldShowPager()) {
+ showPager();
+ }
}
}
+
+ @Override
+ public void onPageScrolled(int positionRoundedDown, float positionOffset, int positionOffsetPixels)
+ {
+ isValidScroll = true;
+
+ // When we touch and drag the view and hold it inbetween the second
+ // and third sub-view, this function will have been called with values
+ // similar to:
+ // positionRoundedDown: 1
+ // positionOffset: 0.5
+ // ie, the first parameter is always rounded down; the second parameter
+ // is always just an offset between the current and next view, it does
+ // not take into account the current view.
+
+ // If we add positionRoundedDown to positionOffset, positionOffset will
+ // have the 'correct' value; ie, will be a natural number when we're on
+ // one particular view, something.5 when inbetween views, etc.
+ float positionFloat = positionOffset + positionRoundedDown;
+
+ // `positionFloat` can now be used to calculate the correct value for
+ // the current index. We add 0.5 so that positionFloat will be rounded
+ // half up; ie, if it has a value of 1.5, it will be rounded up to 2; if
+ // it has a value of 1.4, it will be rounded down to 1.
+ mCurIndex = (int) Math.floor(positionFloat + 0.5);
+ ((ScrollableViewProxy)proxy).fireScroll(mCurIndex, positionFloat, mViews.get(mCurIndex));
+
+ // Note that we didn't just fire a dragEnd. See the above comment
+ // in `onPageSelected`.
+ justFiredDragEnd = false;
+ }
});
return pager;
}
View
10 android/titanium/src/java/org/appcelerator/titanium/TiC.java
@@ -371,6 +371,16 @@
/**
* @module.api
*/
+ public static final String EVENT_SCROLLEND = "scrollEnd";
+
+ /**
+ * @module.api
+ */
+ public static final String EVENT_DRAGEND = "dragEnd";
+
+ /**
+ * @module.api
+ */
public static final String EVENT_SINGLE_TAP = "singletap";
/**
View
22 apidoc/Titanium/App/App.yml
@@ -16,22 +16,36 @@ since: "0.1"
events:
- name: pause
- summary: Fired when the application transitions to the background on a multitasked system.
+ summary: Fired when the application transitions from active to inactive state on a multitasked system.
description: |
Available on iOS 4.0 and later.
- This event fires when the user leaves the application or it is paused due to a notification
- or incoming phone call.
+ This event fires when the user leaves the application or for certain types of temporary interruptions
+ such as a notification or incoming phone call.
See the `applicationWillResignActive` section of the official Apple documentation about
[Monitoring Application State Changes](https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006786-CH3-SW5)
for the exact behavior that triggers this event.
Note that calls to functions that modify the UI during this event may be partially executed,
- **up to** the UI call before the suspension. If this happens, the remainder of the code will
+ **up to** the UI call before the suspension. See [paused](Titanium.App.paused) event. If this happens, the remainder of the code will
be executed after the application is resumed, but before the `resume` event is triggered.
platforms: [iphone, ipad]
+ - name: paused
+ summary: Fired when the application transitions to the background on a multitasked system.
+ description: |
+ Available on iOS 4.0 and later.
+
+ This event fires when the user leaves the application.
+
+ See the `applicationDidEnterBackground` section of the official Apple documentation about
+ [Monitoring Application State Changes](https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006786-CH3-SW5)
+ for the exact behavior that triggers this event.
+
+ platforms: [iphone, ipad]
+ since: '2.1.0'
+
- name: proximity
summary: Fired when the proximity sensor changes state.
description: |
View
59 apidoc/Titanium/UI/ScrollableView.yml
@@ -123,27 +123,21 @@ events:
- name: y
summary: Y coordinate of the event from the `source` view's coordinate system.
type: Number
-
- - name: scroll
- summary: Fired when the current page is changed.
+
+ - name: scrollEnd
+ summary: Fired when the view has stopped moving completely.
+ platforms: [iphone, ipad, android]
+ since: "2.1.0"
+
properties:
- name: currentPage
- summary: Index of the current page of <Titanium.UI.ScrollableView.views>.
+ summary: Index of the currently visible view of <Titanium.UI.ScrollableView.views>.
type: Number
- - name: globalPoint
- summary: |
- Coordinates `x` and `y` describing the location of the event in terms of screen
- coordinates.
- type: Dictionary
- deprecated:
- since: "1.8.0"
- platforms: [iphone, ipad]
-
- name: view
- summary: The current page.
+ summary: The currently visible view.
type: Titanium.UI.View
-
+
- name: singletap
summary: Fired when the device detects a single tap against this view.
platforms: [iphone, ipad]
@@ -231,6 +225,41 @@ events:
summary: X coordinate of the event from the `source` view's coordinate system.
type: Number
+ - name: scroll
+ summary: Fired repeatedly as the view is being scrolled.
+ platforms: [iphone, ipad, android]
+ since: "2.1.0"
+ properties:
+
+ - name: currentPage
+ summary: Index of the currently visible view of <Titanium.UI.ScrollableView.views>.
+ type: Number
+
+ - name: currentPageAsFloat
+ type: Number
+ summary: |
+ Current page index that the view is scrolled to as a float. For
+ example, if the user is holding the `ScrollableView` in between
+ the first and second page, this will have a value of 0.5.
+
+ - name: view
+ summary: The currently visible view.
+ type: Titanium.UI.View
+
+ - name: dragEnd
+ summary: Fired when the scrolling drag gesture on the view has been completed.
+ platforms: [android]
+ since: "2.1.0"
+ properties:
+
+ - name: currentPage
+ summary: Index of the currently visible view of <Titanium.UI.ScrollableView.views>.
+ type: Number
+
+ - name: view
+ summary: The currently visible view.
+ type: Titanium.UI.View
+
properties:
- name: cacheSize
summary: Number of pages to cache (pre-render).
View
5 drillbit/runtest
@@ -5,7 +5,4 @@
./drillbit/drillbit.py --platforms=iphone --tests=filesystem --results-dir=/work/temp0521f --iphone-version=5.0 --autorun
./drillbit/drillbit.py --platforms=iphone --tests=json --results-dir=/work/temp0521f --iphone-version=5.0 --autorun
./drillbit/drillbit.py --platforms=iphone --tests=jss --results-dir=/work/temp0521f --iphone-version=5.0 --autorun
-
-Mon May 21 23:00:35 PDT 2012
-Friday June 1
-
+Sunday June 3 2012 21:41
View
72 drillbit/tests/ui.controls.js
@@ -47,5 +47,73 @@ describe("Ti.UI control tests", {
});
hasText = textField3.hasText();
valueOf(hasText).shouldBe(false);
- }
-});
+ },
+
+ scrollableViewScrollEvents_as_async: function (callback) {
+ // functional test for TIMOB-8933, TIMOB-9061: `scroll` event and `scrollEnd` event
+ var win = Ti.UI.createWindow({layout:'horizontal'});
+
+ var view1 = Ti.UI.createView({ backgroundColor:'#123', width: 250 });
+ var view2 = Ti.UI.createView({ backgroundColor:'#246', width: 250 });
+ var view3 = Ti.UI.createView({ backgroundColor:'#48b', width: 250 });
+
+ var scrollableView = Ti.UI.createScrollableView({
+ views: [view1,view2,view3],
+ showPagingControl: true,
+ width: 300,
+ height: 430
+ });
+
+ win.add(scrollableView);
+ win.open();
+
+ var scrollingEvents = [];
+
+ // Catch all scrolling events, then validate them
+ scrollableView.addEventListener('scroll', function (e) {
+ Ti.API.debug('scrollableView got a scroll event: float:' + e.currentPageAsFloat + ' int: ' + e.currentPage);
+ scrollingEvents.push(e);
+ });
+
+ setTimeout(function () {
+ scrollableView.scrollToView(1);
+ }, 300);
+
+ scrollableView.addEventListener('dragEnd', function (e) {
+ Ti.API.debug('scrollableView got dragEnd event: ' + e.currentPage);
+ });
+
+ // This is fired when the scrollToView has completed; time to validate
+ // our events!
+ scrollableView.addEventListener('scrollEnd', function (endEvent) {
+ Ti.API.debug('scrollableView got a scrollEnd event: ' + endEvent.currentPage);
+
+ var numEvents = scrollingEvents.length;
+
+ try {
+ valueOf(endEvent.currentPage).shouldBe(1);
+
+ // On Android, sometimes, we don't collect enough events to have some that
+ // are within these checks. If that appears to be the case, don't run these
+ // checks.
+ if (numEvents > 5) {
+ valueOf(scrollingEvents[0].currentPage).shouldBe(0);
+ valueOf(scrollingEvents[0].view).shouldBe(view1);
+
+ valueOf(scrollingEvents[0].currentPageAsFloat).shouldBeLessThan(0.8);
+ valueOf(scrollingEvents[numEvents - 1].currentPageAsFloat).shouldBeGreaterThan(0.2);
+ }
+
+ valueOf(scrollingEvents[numEvents - 1].currentPage).shouldBe(1);
+ valueOf(scrollingEvents[numEvents - 1].view).shouldBe(view2);
+
+ callback.passed();
+ Ti.API.debug('passed');
+ } catch (exception) {
+ Ti.API.debug(exception);
+ callback.failed(exception);
+ }
+ });
+
+ }
+});
View
8 iphone/Classes/AppModule.m
@@ -336,6 +336,14 @@ -(void)shutdown:(id)sender
[super shutdown:sender];
}
+-(void)paused:(id)sender
+{
+ if ([self _hasListeners:@"paused"])
+ {
+ [self fireEvent:@"paused" withObject:nil];
+ }
+}
+
-(void)suspend:(id)sender
{
// make sure we force any changes made on suspend in case we don't come back
View
1  iphone/Classes/TiApp.mm
@@ -424,6 +424,7 @@ - (void)applicationDidBecomeActive:(UIApplication *)application
-(void)applicationDidEnterBackground:(UIApplication *)application
{
+ [[NSNotificationCenter defaultCenter] postNotificationName:kTiPausedNotification object:self];
[TiUtils queueAnalytics:@"ti.background" name:@"ti.background" data:nil];
if (backgroundServices==nil)
View
1  iphone/Classes/TiBase.h
@@ -545,6 +545,7 @@ extern NSString * const kTiContextShutdownNotification;
extern NSString * const kTiWillShutdownNotification;
extern NSString * const kTiShutdownNotification;
extern NSString * const kTiSuspendNotification;
+extern NSString * const kTiPausedNotification;
extern NSString * const kTiResumeNotification;
extern NSString * const kTiResumedNotification;
extern NSString * const kTiAnalyticsNotification;
View
1  iphone/Classes/TiBase.m
@@ -79,6 +79,7 @@ void TiLogMessage(NSString* str, ...) {
NSString * const kTiWillShutdownNotification = @"TiWillShutdown";
NSString * const kTiShutdownNotification = @"TiShutdown";
NSString * const kTiSuspendNotification = @"TiSuspend";
+NSString * const kTiPausedNotification = @"TiPaused";
NSString * const kTiResumeNotification = @"TiResume";
NSString * const kTiResumedNotification = @"TiResumed";
NSString * const kTiAnalyticsNotification = @"TiAnalytics";
View
6 iphone/Classes/TiModule.h
@@ -64,6 +64,12 @@
-(void)suspend:(id)sender;
/**
+ Titanium Platform calls this method on entering background.
+ @param sender The sender of the event.
+ */
+-(void)paused:(id)sender;
+
+/**
Titanium Platform calls this method on resume.
@param sender The sender of the event.
*/
View
5 iphone/Classes/TiModule.m
@@ -72,6 +72,10 @@ -(void)shutdown:(id)sender
{
}
+-(void)paused:(id)sender
+{
+}
+
-(void)suspend:(id)sender
{
}
@@ -90,6 +94,7 @@ -(void)registerForNotifications
WARN_IF_BACKGROUND_THREAD_OBJ; //NSNotificationCenter is not threadsafe!
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(shutdown:) name:kTiShutdownNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(suspend:) name:kTiSuspendNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(paused:) name:kTiPausedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resume:) name:kTiResumeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resumed:) name:kTiResumedNotification object:nil];
}
View
2  iphone/Classes/TiUIScrollViewProxy.m
@@ -330,4 +330,4 @@ -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)
@end
-#endif
+#endif
View
21 iphone/Classes/TiUIScrollableView.m
@@ -586,7 +586,16 @@ -(void)scrollViewDidScroll:(UIScrollView *)sender
//switch page control at 50% across the center - this visually looks better
CGFloat pageWidth = scrollview.frame.size.width;
int page = currentPage;
- int nextPage = floor((scrollview.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
+ float nextPageAsFloat = ((scrollview.contentOffset.x - pageWidth / 2) / pageWidth) + 0.5;
+ int nextPage = floor(nextPageAsFloat - 0.5) + 1;
+ if ([self.proxy _hasListeners:@"scroll"])
+ {
+ [self.proxy fireEvent:@"scroll" withObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ NUMINT(nextPage), @"currentPage",
+ NUMFLOAT(nextPageAsFloat), @"currentPageAsFloat",
+ [[self proxy] viewAtIndex:nextPage], @"view", nil]];
+
+ }
if (page != nextPage) {
int curCacheSize = cacheSize;
int minCacheSize = cacheSize;
@@ -597,9 +606,9 @@ -(void)scrollViewDidScroll:(UIScrollView *)sender
}
}
cacheSize = minCacheSize;
- [pageControl setCurrentPage:nextPage];
- currentPage = nextPage;
- [self.proxy replaceValue:NUMINT(currentPage) forKey:@"currentPage" notification:NO];
+ [pageControl setCurrentPage:nextPage];
+ currentPage = nextPage;
+ [self.proxy replaceValue:NUMINT(currentPage) forKey:@"currentPage" notification:NO];
[self manageCache:currentPage];
cacheSize = curCacheSize;
}
@@ -625,9 +634,9 @@ -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
[self.proxy replaceValue:NUMINT(pageNum) forKey:@"currentPage" notification:NO];
- if ([self.proxy _hasListeners:@"scroll"])
+ if ([self.proxy _hasListeners:@"scrollEnd"])
{
- [self.proxy fireEvent:@"scroll" withObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [self.proxy fireEvent:@"scrollEnd" withObject:[NSDictionary dictionaryWithObjectsAndKeys:
NUMINT(pageNum),@"currentPage",
[[self proxy] viewAtIndex:pageNum],@"view",nil]];
}
View
24 iphone/Classes/TiUITableView.m
@@ -2176,23 +2176,29 @@ - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
return YES;
}
+- (void)fireScrollEvent:(UIScrollView *)scrollView {
+ if ([self.proxy _hasListeners:@"scroll"])
+ {
+ NSMutableDictionary *event = [NSMutableDictionary dictionary];
+ [event setObject:[TiUtils pointToDictionary:scrollView.contentOffset] forKey:@"contentOffset"];
+ [event setObject:[TiUtils sizeToDictionary:scrollView.contentSize] forKey:@"contentSize"];
+ [event setObject:[TiUtils sizeToDictionary:tableview.bounds.size] forKey:@"size"];
+ [self.proxy fireEvent:@"scroll" withObject:event];
+ }
+}
+
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.isDragging || scrollView.isDecelerating)
{
- if ([self.proxy _hasListeners:@"scroll"])
- {
- NSMutableDictionary *event = [NSMutableDictionary dictionary];
- [event setObject:[TiUtils pointToDictionary:scrollView.contentOffset] forKey:@"contentOffset"];
- [event setObject:[TiUtils sizeToDictionary:scrollView.contentSize] forKey:@"contentSize"];
- [event setObject:[TiUtils sizeToDictionary:tableview.bounds.size] forKey:@"size"];
- [self.proxy fireEvent:@"scroll" withObject:event];
- }
- }
+ [self fireScrollEvent:scrollView];
+ }
}
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
+ [self fireScrollEvent:scrollView];
+
// resume image loader when we're done scrolling
[[ImageLoader sharedLoader] resume];
}
View
245 mobileweb/titanium/Ti/UI/ScrollableView.js
@@ -1,20 +1,31 @@
-define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/lang", "Ti/_/dom", "Ti/_/style", "Ti/UI"],
- function(declare, Widget, lang, dom, style, UI) {
+define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/lang", "Ti/_/dom", "Ti/_/style", "Ti/UI", "Ti/_/event"],
+ function(declare, Widget, lang, dom, style, UI, event) {
var setStyle = style.set,
is = require.is,
- unitize = dom.unitize;
+ isDef = lang.isDef,
+ unitize = dom.unitize,
+ on = require.on,
- return declare("Ti.UI.ScrollableView", Widget, {
+ // This specifies the minimum distance that a finger must travel before it is considered a swipe
+ distanceThreshold = 50,
+
+ // The maximum angle, in radians, from the axis a swipe is allowed to travel before it is no longer considered a swipe
+ angleThreshold = Math.PI/6, // 30 degrees
// This sets the minimum velocity that determines whether a swipe was a flick or a drag
- _velocityThreshold: 0.4,
+ velocityThreshold = 0.5,
// This determines the minimum distance scale (i.e. width divided by this value) before a flick requests a page turn
- _minimumFlickDistanceScaleFactor: 15,
+ minimumFlickDistanceScaleFactor = 15,
// This determines the minimum distance scale (i.e. width divided by this value) before a drag requests a page turn
- _minimumDragDistanceScaleFactor: 2,
+ minimumDragDistanceScaleFactor = 2;
+
+ // This is the velocity used to animate to the end when there is no available velocity
+ defaultVelocity = 0.2;
+
+ return declare("Ti.UI.ScrollableView", Widget, {
constructor: function(args){
@@ -50,48 +61,75 @@ define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/lang", "Ti/_/dom", "Ti/_/style",
this._viewToRemoveAfterScroll = -1;
var initialPosition,
+ startX,
animationView,
- swipeInitialized = false,
viewsToScroll,
- touchEndHandled,
- startTime;
-
- // This touch end handles the case where a swipe was started, but turned out not to be a swipe
- this.addEventListener("touchend", function(e) {
- if (!touchEndHandled && swipeInitialized) {
- var width = this._measuredWidth,
- destinationLeft = viewsToScroll.indexOf(this.views[this.currentPage]) * -width;
+ startTime,
+ previousPosition,
+ self = this,
+ width,
+ mouseIsDown,
+ handles;
+
+ function touchify(e, finalize) {
+ return require.mix(e, {
+ touches: finalize ? [] : [e],
+ targetTouches: [],
+ changedTouches: [e]
+ });
+ }
+
+ function finalizeSwipe(destinationIndex, x, y) {
+ self._contentContainer._removeAllChildren();
+ self._contentContainer._add(self.views[destinationIndex]);
+ self._triggerLayout(true);
+ self.currentPage !== destinationIndex && self.fireEvent("scroll",{
+ currentPage: destinationIndex,
+ view: self.views[destinationIndex],
+ x: x,
+ y: y
+ });
+ self.properties.__values__.currentPage = destinationIndex;
+ }
+
+ function cancelScroll() {
+ if (startX) {
+ // Update paging control
+ self._updatePagingControl(self.currentPage);
+
+ // Animate the view and set the final view
animationView.animate({
- duration: (300 + 0.2 * width) / (width - Math.abs(e._distance)) * 10,
- left: destinationLeft,
+ duration: 400,
+ left: -width,
curve: UI.ANIMATION_CURVE_EASE_OUT
- },lang.hitch(this,function(){
- this._contentContainer._removeAllChildren();
- this._contentContainer._add(this.views[this.currentPage]);
- }));
+ },function() {
+ finalizeSwipe(self.currentPage);
+ });
+
+ startX = null;
}
- })
-
- this.addEventListener("swipe", function(e){
- // If we haven't started swiping yet, start swiping,
- var width = this._measuredWidth;
- if (!swipeInitialized) {
- swipeInitialized = true;
- touchEndHandled = false;
- startTime = (new Date()).getTime();
-
+ }
+
+ function touchStart(e) {
+ if (e.touches.length == 1 && e.changedTouches.length == 1) {
+ var i = 0,
+ win = window;
+ width = self._measuredWidth,
+ startTime = (new Date).getTime();
+ startX = e.changedTouches[0].clientX;
+
// Create the list of views that can be scrolled, the ones immediately to the left and right of the current view
initialPosition = 0;
viewsToScroll = [];
- if (this.currentPage > 0) {
- viewsToScroll.push(this.views[this.currentPage - 1]);
+ if (self.currentPage > 0) {
+ viewsToScroll.push(self.views[self.currentPage - 1]);
initialPosition = -width;
}
- viewsToScroll.push(this.views[this.currentPage]);
- if (this.currentPage < this.views.length - 1) {
- viewsToScroll.push(this.views[this.currentPage + 1]);
+ viewsToScroll.push(self.views[self.currentPage]);
+ if (self.currentPage < self.views.length - 1) {
+ viewsToScroll.push(self.views[self.currentPage + 1]);
}
-
+
// Create the animation div
animationView = UI.createView({
width: unitize(viewsToScroll.length * width),
@@ -99,10 +137,10 @@ define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/lang", "Ti/_/dom", "Ti/_/style",
left: initialPosition,
top: 0
});
-
+
// Attach the child views, each contained in their own div so we can mess with positioning w/o touching the views
- this._contentContainer._removeAllChildren();
- for (var i = 0; i < viewsToScroll.length; i++) {
+ self._contentContainer._removeAllChildren();
+ for (; i < viewsToScroll.length; i++) {
var viewContainer = UI.createView({
left: unitize(i * width),
top: 0,
@@ -115,75 +153,104 @@ define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/lang", "Ti/_/dom", "Ti/_/style",
viewContainer._add(viewsToScroll[i]);
animationView._add(viewContainer);
}
-
+
// Set the initial position
animationView.left = unitize(initialPosition);
- this._contentContainer._add(animationView);
- this._triggerLayout(true);
+ self._contentContainer._add(animationView);
+ self._triggerLayout(true);
+
+ handles = [
+ // Register for move type events
+ on(win, "touchmove", touchMove),
+ on(win, "mousemove", function(e) {
+ mouseIsDown && touchMove(touchify(e));
+ }),
+
+ // Register for cancel type events
+ on(win, "touchcancel", touchCancel),
+
+ // Register for end type events
+ on(win, "touchend", touchEnd),
+ on(win, "mouseup", function(e) {
+ mouseIsDown = 0;
+ touchEnd(touchify(e, 1));
+ })
+ ];
}
-
- // Update the position of the animation div
- var newPosition = initialPosition + e._distance;
- newPosition = newPosition < 0 ? newPosition > -animationView._measuredWidth + width ? newPosition :-animationView._measuredWidth + width : 0;
- animationView.domNode.style.left = unitize(newPosition);
-
- // If the swipe is finished, we animate to the final position
- if (e._finishedSwiping) {
- swipeInitialized = false;
- touchEndHandled = true;
-
- // Determine whether this was a flick or a drag
- var velocity = Math.abs((e._distance) / ((new Date()).getTime() - startTime));
- var scaleFactor = velocity > this._velocityThreshold ?
- this._minimumFlickDistanceScaleFactor : this._minimumDragDistanceScaleFactor
+ };
+ on(this.domNode, "touchstart", touchStart);
+ on(this.domNode, "mousedown", function(e) {
+ mouseIsDown = 1;
+ touchStart(touchify(e));
+ });
+
+ function touchMove(e) {
+ if (e.touches.length == 1 && e.changedTouches.length == 1 && isDef(startX)) {
+ width = self._measuredWidth;
+ // Update the position of the animation div
+ var newPosition = initialPosition + e.changedTouches[0].clientX - startX;
+ newPosition = newPosition < 0 ? newPosition > -animationView._measuredWidth + width ? newPosition :-animationView._measuredWidth + width : 0;
+ animationView.domNode.style.left = unitize(newPosition);
+ } else {
+ cancelScroll();
+ }
+ }
+
+ function touchCancel() {
+ event.off(handles);
+ cancelScroll();
+ }
+
+ function touchEnd(e) {
+ event.off(handles);
+ if (e.touches.length == 0 && e.changedTouches.length == 1 && isDef(startX)) {
+ width = self._measuredWidth;
+
+ var x = e.changedTouches[0].clientX,
+ y = e.changedTouches[0].clientX,
+ distance = x - startX,
+ destinationIndex = self.currentPage,
+ animationLeft = initialPosition,
+ velocity = Math.abs(distance / ((new Date).getTime() - startTime)),
+ scaleFactor = velocity > velocityThreshold ?
+ minimumFlickDistanceScaleFactor : minimumDragDistanceScaleFactor,
+ newPosition = initialPosition + e.changedTouches[0].clientX - startX;
+ newPosition = newPosition < 0 ? newPosition > -animationView._measuredWidth + width ? newPosition :-animationView._measuredWidth + width : 0;
+ animationView.domNode.style.left = unitize(newPosition);
+
// Find out which view we are animating to
- var destinationIndex = this.currentPage,
- animationLeft = initialPosition;
- if (e._distance > width / scaleFactor && this.currentPage > 0) {
- destinationIndex = this.currentPage - 1;
+ if (distance > width / scaleFactor && self.currentPage > 0) {
+ destinationIndex = self.currentPage - 1;
animationLeft = 0;
- } else if (e._distance < -width / scaleFactor && this.currentPage < this.views.length - 1) {
- destinationIndex = this.currentPage + 1;
+ } else if (distance < -width / scaleFactor && self.currentPage < self.views.length - 1) {
+ destinationIndex = self.currentPage + 1;
if (viewsToScroll.length === 3) {
animationLeft = -2 * width;
} else {
animationLeft = -width;
}
}
-
- var self = this;
- function finalizeSwipe() {
- self._contentContainer._removeAllChildren();
- self._contentContainer._add(self.views[destinationIndex]);
- self._triggerLayout(true);
-
- self.currentPage !== destinationIndex && self.fireEvent("scroll",{
- currentPage: destinationIndex,
- view: self.views[destinationIndex],
- x: e.x,
- y: e.y
- });
-
- self.properties.__values__.currentPage = destinationIndex;
- }
-
+
// Check if the user attempted to scroll past the edge, in which case we directly reset the view instead of animation
- this._updatePagingControl(destinationIndex);
+ self._updatePagingControl(destinationIndex);
if (newPosition == 0 || newPosition == -animationView._measuredWidth + width) {
- finalizeSwipe();
+ finalizeSwipe(destinationIndex, x, y);
} else {
// Animate the view and set the final view
animationView.animate({
- duration: 200 + (0.2 * width) / (width - Math.abs(e._distance)) * 10,
+ duration: 200 + (0.2 * width) / (width - Math.abs(distance)) * 10,
left: animationLeft,
curve: UI.ANIMATION_CURVE_EASE_OUT
- },lang.hitch(this,function(){
- finalizeSwipe();
- }));
+ },function() {
+ finalizeSwipe(destinationIndex, x, y);
+ });
}
+ startX = null;
+ } else {
+ cancelScroll();
}
- });
+ }
},
addView: function(view){
View
7 mobileweb/titanium/Ti/UI/Tab.js
@@ -96,15 +96,16 @@ define(["Ti/_/declare", "Ti/UI/View", "Ti/_/dom", "Ti/Locale", "Ti/UI", "Ti/UI/M
},
post: function(value) {
var tabGroup = this._tabGroup,
- navGroup = this._tabNavigationGroup;
+ navGroup = this._tabNavigationGroup,
+ doEvents = tabGroup._focused && tabGroup._opened;
if (value) {
navGroup.navBarAtTop = tabGroup.tabsAtBottom;
navGroup._updateTitle();
tabGroup._addTabContents(navGroup);
- tabGroup._opened && this._focus();
+ doEvents && this._focus();
} else {
tabGroup._removeTabContents(navGroup);
- tabGroup._opened && this._blur();
+ doEvents && this._blur();
}
}
},
View
25 mobileweb/titanium/Ti/UI/TabGroup.js
@@ -121,18 +121,29 @@ define(["Ti/_/declare", "Ti/_/UI/SuperView", "Ti/UI/View", "Ti/UI", "Ti/_/lang"]
previousTab && previousTab._blur();
- this._focused || this._opened && this.fireEvent("focus", this._getEventData());
+ if (!this._focused && this._opened) {
+ this.fireEvent("focus", this._getEventData());
+ activeTab && activeTab._focus();
+ }
this._focused = 1;
-
- this._opened && activeTab && activeTab._focus();
},
- _handleBlurEvent: function(closing) {
+ _handleBlurEvent: function(blurTabs) {
// TabGroup is about to be closed or a window was opened
- closing && this.tabs.forEach(function(tab) {
- tab._blur();
- });
+ // blurTabs: 1) blur all tabs, 2) blur active tab only
+ if (blurTabs) {
+ var i = 0,
+ len = this.tabs.length,
+ tab;
+
+ while (i < len) {
+ tab = this.tabs[i++];
+ (blurTabs !== 2 || tab === this._activeTab) && tab._blur();
+ }
+
+ this._previousTab = void 0;
+ }
this._focused && this._opened && this.fireEvent("blur", this._getEventData());
this._focused = 0;
View
3  mobileweb/titanium/Ti/UI/TableViewRow.js
@@ -37,6 +37,9 @@ define(["Ti/_/declare", "Ti/_/lang", "Ti/UI/View", "Ti/_/dom", "Ti/_/css", "Ti/_
width: UI.SIZE,
height: UI.SIZE
}));
+
+ // Force single tap to be processed.
+ this.addEventListener("singletap");
},
_defaultWidth: UI.INHERIT,
View
3  mobileweb/titanium/Ti/UI/TableViewSection.js
@@ -19,6 +19,9 @@ define(["Ti/_/declare", "Ti/_/lang", "Ti/_/UI/Widget", "Ti/_/style","Ti/UI/Mobil
// Create the parts out of Ti controls so we can make use of the layout system
this.layout = UI._LAYOUT_CONSTRAINING_VERTICAL;
+
+ // Force single tap to be processed.
+ this.addEventListener("singletap");
},
_defaultWidth: UI.INHERIT,
View
5 mobileweb/titanium/Ti/UI/WebView.js
@@ -53,7 +53,8 @@ define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/dom", "Ti/_/event", "Ti/_/lang",
var i = Math.max(isSameDomain | 0, 0),
cw = iframe.contentWindow,
prop,
- url;
+ url,
+ html;
if (i !== -1) {
// we can always guarantee that the first load we'll know if it's the same domain
@@ -69,7 +70,7 @@ define(["Ti/_/declare", "Ti/_/UI/Widget", "Ti/_/dom", "Ti/_/event", "Ti/_/lang",
if (i > 0) {
url = cw.location.href;
this.evalJS(bridge.replace("WEBVIEW_ID", this.widgetId + ":unload"));
- this.html && this._setContent(this.html);
+ (html = this.properties.__values__.html) && this._setContent(html);
} else {
API.warn("Unable to inject WebView bridge into cross-domain URL, ignore browser security message");
}
View
5 mobileweb/titanium/Ti/XML.js
@@ -49,11 +49,6 @@ define(["Ti/_/Evented", "Ti/_/lang"], function(Evented, lang) {
f && lang.generateAccessors(f, e[1], e[2]);
});
- Object.defineProperty(Element.prototype, "text", {
- get: function() { return this.textContent; },
- enumerable: true
- });
-
return lang.setObject("Ti.XML", Evented, {
parseString: function(xml) {
View
2  mobileweb/titanium/Ti/_/Evented.js
@@ -19,7 +19,7 @@ define(function() {
var i = 0,
events = this.listeners[name],
l = events && events.length || 0;
-
+
for (; i < l; i++) {
events[i] === handler && events.splice(i, 1);
}
View
112 mobileweb/titanium/Ti/_/Gestures/Swipe.js
@@ -1,97 +1,77 @@
define(["Ti/_/declare", "Ti/_/lang","Ti/_/Gestures/GestureRecognizer"], function(declare,lang,GestureRecognizer) {
+ // This specifies the minimum distance that a finger must travel before it is considered a swipe
+ var distanceThreshold = 50,
+
+ // The maximum angle, in radians, from the axis a swipe is allowed to travel before it is no longer considered a swipe
+ angleThreshold = Math.PI/6, // 30 degrees
+
+ // This sets the minimum velocity that determines this is a swipe, or just a drag
+ velocityThreshold = 0.5;
+
return declare("Ti._.Gestures.Swipe", GestureRecognizer, {
-
+
name: "swipe",
-
- _touchStartLocation: null,
+
_distanceThresholdPassed: false,
-
- // This specifies the minimum distance that a finger must travel before it is considered a swipe
- _distanceThreshold: 25,
-
- // The masimum angle, in radians, from the axis a swipe is allowed to travel before it is no longer considered a swipe
- _angleThreshold: Math.PI/12, // 15 degrees
-
+
processTouchStartEvent: function(e, element){
if (e.touches.length == 1 && e.changedTouches.length == 1) {
this._distanceThresholdPassed = false;
this._touchStartLocation = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY
- }
+ };
+ this._startTime = (new Date).getTime();
} else {
this._touchStartLocation = null;
}
},
-
+
processTouchEndEvent: function(e, element){
- if (e.touches.length == 0 && e.changedTouches.length == 1) {
- this._processSwipeEvent(e,element,true);
- }
- this._touchStartLocation = null;
- },
-
- processTouchMoveEvent: function(e, element){
- if (e.touches.length == 1 && e.changedTouches.length == 1) {
- this._processSwipeEvent(e,element,false);
- }
- },
-
- processTouchCancelEvent: function(e, element){
- this._touchStartLocation = null;
- },
-
- _processSwipeEvent: function(e,element,finishedSwiping) {
- var x = e.changedTouches[0].clientX,
- y = e.changedTouches[0].clientY;
- if (this._touchStartLocation) {
- var xDiff = Math.abs(this._touchStartLocation.x - x),
- yDiff = Math.abs(this._touchStartLocation.y - y),
- distance = Math.sqrt(Math.pow(this._touchStartLocation.x - x,2) + Math.pow(this._touchStartLocation.y - y,2)),
- angleOK;
- !this._distanceThresholdPassed && (this._distanceThresholdPassed = distance > this._distanceThreshold);
-
- if (this._distanceThresholdPassed) {
- // If the distance is small, then the angle is way restrictive, so we ignore it
- if (distance <= this._distanceThreshold || xDiff === 0 || yDiff === 0) {
- angleOK = true;
- } else if (xDiff > yDiff) {
- angleOK = Math.atan(yDiff/xDiff) < this._angleThreshold;
- } else {
- angleOK = Math.atan(xDiff/yDiff) < this._angleThreshold;
- }
- if (!angleOK) {
- this._touchStartLocation = null;
- } else {
-
- if (!element._isGestureBlocked(this.name)) {
-
+ if (e.touches.length == 0 && e.changedTouches.length == 1 && this._touchStartLocation) {
+ var x = e.changedTouches[0].clientX,
+ y = e.changedTouches[0].clientY,
+ xDiff = Math.abs(this._touchStartLocation.x - x),
+ yDiff = Math.abs(this._touchStartLocation.y - y),
+ distance = Math.sqrt(Math.pow(this._touchStartLocation.x - x, 2) + Math.pow(this._touchStartLocation.y - y, 2)),
+ angleOK,
+ direction,
+ velocity;
+ !this._distanceThresholdPassed && (this._distanceThresholdPassed = distance > distanceThreshold);
+
+ if (this._distanceThresholdPassed) {
+ // If the distance is small, then the angle is way restrictive, so we ignore it
+ if (distance <= distanceThreshold || xDiff === 0 || yDiff === 0) {
+ angleOK = true;
+ } else if (xDiff > yDiff) {
+ angleOK = Math.atan(yDiff/xDiff) < angleThreshold;
+ } else {
+ angleOK = Math.atan(xDiff/yDiff) < angleThreshold;
+ }
+ if (angleOK) {
// Calculate the direction
- var direction;
- if (xDiff > yDiff) {
- direction = this._touchStartLocation.x - x > 0 ? "left" : "right";
- } else {
- direction = this._touchStartLocation.y - y > 0 ? "down" : "up";
- }
-
- // Right now only left and right are supported
- if (direction === "left" || direction === "right") {
+ direction = xDiff > yDiff ?
+ this._touchStartLocation.x - x > 0 ? "left" : "right" :
+ this._touchStartLocation.y - y < 0 ? "down" : "up";
+ velocity = Math.abs(distance / ((new Date).getTime() - this._startTime));
+ if (velocity > velocityThreshold) {
lang.hitch(element,element._handleTouchEvent(this.name,{
x: x,
y: y,
direction: direction,
- _distance: x - this._touchStartLocation.x,
- _finishedSwiping: finishedSwiping,
source: this.getSourceNode(e,element)
}));
}
}
}
- }
}
+ this._touchStartLocation = null;
+ },
+
+ processTouchCancelEvent: function(e, element){
+ this._touchStartLocation = null;
}
-
});
});
View
93 mobileweb/titanium/Ti/_/UI/Element.js
@@ -1,11 +1,7 @@
define(
["Ti/_/browser", "Ti/_/css", "Ti/_/declare", "Ti/_/dom", "Ti/_/event", "Ti/_/lang", "Ti/_/style", "Ti/_/Evented",
- "Ti/UI", "Ti/_/Gestures/DoubleTap","Ti/_/Gestures/LongPress","Ti/_/Gestures/Pinch","Ti/_/Gestures/SingleTap",
- "Ti/_/Gestures/Swipe","Ti/_/Gestures/TouchCancel","Ti/_/Gestures/TouchEnd","Ti/_/Gestures/TouchMove",
- "Ti/_/Gestures/TouchStart","Ti/_/Gestures/TwoFingerTap", "Ti/_/Promise"],
- function(browser, css, declare, dom, event, lang, style, Evented, UI,
- DoubleTap, LongPress, Pinch, SingleTap, Swipe, TouchCancel, TouchEnd,
- TouchMove, TouchStart, TwoFingerTap, Promise) {
+ "Ti/UI", "Ti/_/Promise", "Ti/_/string"],
+ function(browser, css, declare, dom, event, lang, style, Evented, UI, Promise, string) {
var unitize = dom.unitize,
computeSize = dom.computeSize,
@@ -36,7 +32,21 @@ define(
postLayoutProp = {
set: postLayoutPropFunction
},
- pixelUnits = "px";
+ pixelUnits = "px",
+ gestureMapping = {
+ pinch: "Pinch",
+ swipe: "Swipe",
+ twofingertap: "TwoFingerTap",
+ doubletap: "DoubleTap",
+ longpress: "LongPress",
+ singletap: "SingleTap",
+ click: "SingleTap",
+ doubleclick: "DoubleTap",
+ touchstart: "TouchStart",
+ touchend: "TouchEnd",
+ touchmove: "TouchMove",
+ touchcancel: "TouchCancel"
+ };
return declare("Ti._.UI.Element", Evented, {
@@ -54,27 +64,7 @@ define(
})),
// Handle click/touch/gestures
- recognizers = this._gestureRecognizers = {
- Pinch: new Pinch,
- Swipe: new Swipe,
- TwoFingerTap: new TwoFingerTap,
- DoubleTap: new DoubleTap,
- LongPress: new LongPress,
- SingleTap: new SingleTap,
- TouchStart: new TouchStart,
- TouchEnd: new TouchEnd,
- TouchMove: new TouchMove,
- TouchCancel: new TouchCancel
- },
-
- // Each event could require a slightly different precedence of execution, which is why we have these separate lists.
- // For now they are the same, but I suspect they will be different once the android-iphone parity is determined.
- touchRecognizers = {
- Start: recognizers,
- Move: recognizers,
- End: recognizers,
- Cancel: recognizers
- },
+ recognizers = this._gestureRecognizers = {},
useTouch = "ontouchstart" in window,
bg = lang.hitch(this, "_doBackground");
@@ -82,9 +72,7 @@ define(
require.has("devmode") && args && args._debug && dom.attr.set(node, "data-debug", args._debug);
function processTouchEvent(eventType, evt) {
var i,
- gestureRecognizers = touchRecognizers[eventType],
touches = evt.changedTouches;
- eventType = "Touch" + eventType + "Event";
if (this._preventDefaultTouchEvent) {
this._preventDefaultTouchEvent && evt.preventDefault && evt.preventDefault();
for (i in touches) {
@@ -96,11 +84,11 @@ define(
targetTouches: [],
changedTouches: [evt]
});
- for (i in gestureRecognizers) {
- gestureRecognizers[i]["process" + eventType](evt, self);
+ for (i in recognizers) {
+ recognizers[i].recognizer["process" + eventType](evt, self);
}
- for (i in gestureRecognizers) {
- gestureRecognizers[i]["finalize" + eventType]();
+ for (i in recognizers) {
+ recognizers[i].recognizer["finalize" + eventType]();
}
}
@@ -113,7 +101,7 @@ define(
on(window, useTouch ? "touchmove" : "mousemove", function(evt){
if (!touchMoveBlocked) {
touchMoveBlocked = true;
- (useTouch || self._touching) && processTouchEvent("Move", evt);
+ (useTouch || self._touching) && processTouchEvent("TouchMoveEvent", evt);
setTimeout(function(){
touchMoveBlocked = false;
}, 30);
@@ -121,16 +109,16 @@ define(
}),
on(window, useTouch ? "touchend" : "mouseup", function(evt){
self._touching = false;
- processTouchEvent("End", evt);
+ processTouchEvent("TouchEndEvent", evt);
event.off(handles);
}),
useTouch && on(window, "touchcancel", function(evt){
- processTouchEvent("Cancel", evt);
+ processTouchEvent("TouchCancelEvent", evt);
event.off(handles);
})
];
self._touching = true;
- processTouchEvent("Start", evt);
+ processTouchEvent("TouchStartEvent", evt);
});
this.addEventListener("touchstart", bg);
@@ -184,10 +172,37 @@ define(
};
},
+ addEventListener: function(name, handler) {
+ if (name in gestureMapping) {
+ var gestureRecognizers = this._gestureRecognizers,
+ gestureRecognizer;
+
+ if (!(name in gestureRecognizers)) {
+ gestureRecognizers[name] = {
+ count: 0,
+ recognizer: new (require("Ti/_/Gestures/" + gestureMapping[name]))
+ };
+ }
+
+ gestureRecognizers[name].count++;
+ }
+ handler && Evented.addEventListener.apply(this, arguments);
+ },
+
+ removeEventListener: function(name) {
+ if (name in gestureMapping) {
+ var gestureRecognizers = this._gestureRecognizers;
+ if (name in gestureRecognizers && !(--gestureRecognizers[name].count)) {
+ delete gestureRecognizers[name];
+ }
+ }
+ Evented.removeEventListener.apply(this, arguments);
+ },
+
_setParent: function(view) {
this._parent = view;
},
-
+
_add: function(view, hidden) {
view._hidden = hidden;
View
28 mobileweb/titanium/Ti/_/UI/SuperView.js
@@ -1,5 +1,7 @@
define(["Ti/_/declare", "Ti/UI", "Ti/UI/View"], function(declare, UI, View) {
+ var windows = [];
+
return declare("Ti._.UI.SuperView", View, {
destroy: function() {
@@ -11,6 +13,11 @@ define(["Ti/_/declare", "Ti/UI", "Ti/UI/View"], function(declare, UI, View) {
if (!this._opened) {
this._opened = 1;
UI._addWindow(this, 1).show();
+
+ var len = windows.length;
+ len && windows[len-1]._handleBlurEvent(2); // only blur the active tab
+ windows.push(this);
+
this.fireEvent("open");
this._handleFocusEvent();
}
@@ -20,15 +27,30 @@ define(["Ti/_/declare", "Ti/UI", "Ti/UI/View"], function(declare, UI, View) {
if (this.tab) {
this.tab.close(this);
} else if (this._opened) {
- this._opened = 0;
+ var i = windows.indexOf(this),
+ same = i === windows.length - 1;
+
UI._removeWindow(this);
- this._handleBlurEvent(1);
+
+ if (~i) {
+ same && this._handleBlurEvent(1); // blur all tabs
+ windows.splice(i, 1);
+ }
+
this.fireEvent("close");
+
+ // if we just closed the active window, focus the next top-most window
+ if (same) {
+ for (i = windows.length - 1; i >= 0 && !windows[i]._opened; i--) {}
+ i >= 0 && windows[i]._handleFocusEvent();
+ }
+
+ this._opened = 0;
}
},
_handleFocusEvent: function(args) {
- this._opened && this.fireEvent("focus", args);
+ this.fireEvent("focus", args);
},
_handleBlurEvent: function(args) {
View
4 mobileweb/titanium/Ti/_/UI/WebViewBridge.js
@@ -3,10 +3,10 @@ var a, b,
p = w.parent,
u = w.onunload;
-if(p && p.Ti){
+if (!Ti && !Titanium && p && p.Ti) {
a = p.Ti.API;
b = p.Ti.App;
- Ti = {
+ Ti = Titanium = {
API: {
log: a.log,
debug: a.debug,
View
2  mobileweb/titanium/Ti/_/lang.js
@@ -57,7 +57,7 @@ define(function() {
var d, i, p, v, special = { properties: 1, constants: 0 };
for (p in src) {
if (src.hasOwnProperty(p) && !/^(constructor|__values__)$/.test(p)) {
- if (p in special) {
+ if (special.hasOwnProperty(p)) {
d = dest[p] || (dest[p] = {});
d.__values__ || (d.__values__ = {});
for (i in src[p]) {
View
BIN  support/iphone/titanium_prep
Binary file not shown
View
11 support/mobileweb/compiler.py
@@ -720,6 +720,17 @@ def find_project_dependencies(self):
'Ti/Filesystem/File',
'Ti/Filesystem/FileStream',
'Ti/Gesture',
+ 'Ti/_/Gestures/GestureRecognizer',
+ 'Ti/_/Gestures/DoubleTap',
+ 'Ti/_/Gestures/LongPress',
+ 'Ti/_/Gestures/Pinch',
+ 'Ti/_/Gestures/SingleTap',
+ 'Ti/_/Gestures/Swipe',
+ 'Ti/_/Gestures/TouchCancel',
+ 'Ti/_/Gestures/TouchEnd',
+ 'Ti/_/Gestures/TouchMove',
+ 'Ti/_/Gestures/TouchStart',
+ 'Ti/_/Gestures/TwoFingerTap',
'Ti/Geolocation',
'Ti/IOStream',
'Ti/Locale',
Please sign in to comment.
Something went wrong with that request. Please try again.