init repo

This commit is contained in:
nikolay ivanov
2014-07-05 18:22:49 +00:00
commit a8be6b9e72
17348 changed files with 9229832 additions and 0 deletions

View File

@@ -0,0 +1,276 @@
//@require @core
/**
* @private
*/
Ext.define('Ext.event.Controller', {
isFiring: false,
listenerStack: null,
constructor: function(info) {
this.firingListeners = [];
this.firingArguments = [];
this.setInfo(info);
return this;
},
setInfo: function(info) {
this.info = info;
},
getInfo: function() {
return this.info;
},
setListenerStacks: function(listenerStacks) {
this.listenerStacks = listenerStacks;
},
fire: function(args, action) {
var listenerStacks = this.listenerStacks,
firingListeners = this.firingListeners,
firingArguments = this.firingArguments,
push = firingListeners.push,
ln = listenerStacks.length,
listeners, beforeListeners, currentListeners, afterListeners,
isActionBefore = false,
isActionAfter = false,
i;
firingListeners.length = 0;
if (action) {
if (action.order !== 'after') {
isActionBefore = true;
}
else {
isActionAfter = true;
}
}
if (ln === 1) {
listeners = listenerStacks[0].listeners;
beforeListeners = listeners.before;
currentListeners = listeners.current;
afterListeners = listeners.after;
if (beforeListeners.length > 0) {
push.apply(firingListeners, beforeListeners);
}
if (isActionBefore) {
push.call(firingListeners, action);
}
if (currentListeners.length > 0) {
push.apply(firingListeners, currentListeners);
}
if (isActionAfter) {
push.call(firingListeners, action);
}
if (afterListeners.length > 0) {
push.apply(firingListeners, afterListeners);
}
}
else {
for (i = 0; i < ln; i++) {
beforeListeners = listenerStacks[i].listeners.before;
if (beforeListeners.length > 0) {
push.apply(firingListeners, beforeListeners);
}
}
if (isActionBefore) {
push.call(firingListeners, action);
}
for (i = 0; i < ln; i++) {
currentListeners = listenerStacks[i].listeners.current;
if (currentListeners.length > 0) {
push.apply(firingListeners, currentListeners);
}
}
if (isActionAfter) {
push.call(firingListeners, action);
}
for (i = 0; i < ln; i++) {
afterListeners = listenerStacks[i].listeners.after;
if (afterListeners.length > 0) {
push.apply(firingListeners, afterListeners);
}
}
}
if (firingListeners.length === 0) {
return this;
}
if (!args) {
args = [];
}
firingArguments.length = 0;
firingArguments.push.apply(firingArguments, args);
// Backwards compatibility
firingArguments.push(null, this);
this.doFire();
return this;
},
doFire: function() {
var firingListeners = this.firingListeners,
firingArguments = this.firingArguments,
optionsArgumentIndex = firingArguments.length - 2,
i, ln, listener, options, fn, firingFn,
boundFn, isLateBinding, scope, args, result;
this.isPausing = false;
this.isPaused = false;
this.isStopped = false;
this.isFiring = true;
for (i = 0,ln = firingListeners.length; i < ln; i++) {
listener = firingListeners[i];
options = listener.options;
fn = listener.fn;
firingFn = listener.firingFn;
boundFn = listener.boundFn;
isLateBinding = listener.isLateBinding;
scope = listener.scope;
// Re-bind the callback if it has changed since the last time it's bound (overridden)
if (isLateBinding && boundFn && boundFn !== scope[fn]) {
boundFn = false;
firingFn = false;
}
if (!boundFn) {
if (isLateBinding) {
boundFn = scope[fn];
if (!boundFn) {
continue;
}
}
else {
boundFn = fn;
}
listener.boundFn = boundFn;
}
if (!firingFn) {
firingFn = boundFn;
if (options.buffer) {
firingFn = Ext.Function.createBuffered(firingFn, options.buffer, scope);
}
if (options.delay) {
firingFn = Ext.Function.createDelayed(firingFn, options.delay, scope);
}
listener.firingFn = firingFn;
}
firingArguments[optionsArgumentIndex] = options;
args = firingArguments;
if (options.args) {
args = options.args.concat(args);
}
if (options.single === true) {
listener.stack.remove(fn, scope, listener.order);
}
result = firingFn.apply(scope, args);
if (result === false) {
this.stop();
}
if (this.isStopped) {
break;
}
if (this.isPausing) {
this.isPaused = true;
firingListeners.splice(0, i + 1);
return;
}
}
this.isFiring = false;
this.listenerStacks = null;
firingListeners.length = 0;
firingArguments.length = 0;
this.connectingController = null;
},
connect: function(controller) {
this.connectingController = controller;
},
resume: function() {
var connectingController = this.connectingController;
this.isPausing = false;
if (this.isPaused && this.firingListeners.length > 0) {
this.isPaused = false;
this.doFire();
}
if (connectingController) {
connectingController.resume();
}
return this;
},
isInterrupted: function() {
return this.isStopped || this.isPaused;
},
stop: function() {
var connectingController = this.connectingController;
this.isStopped = true;
if (connectingController) {
this.connectingController = null;
connectingController.stop();
}
this.isFiring = false;
this.listenerStacks = null;
return this;
},
pause: function() {
var connectingController = this.connectingController;
this.isPausing = true;
if (connectingController) {
connectingController.pause();
}
return this;
}
});

View File

@@ -0,0 +1,365 @@
//@require @core
/**
* @private
*/
Ext.define('Ext.event.Dispatcher', {
requires: [
'Ext.event.ListenerStack',
'Ext.event.Controller'
],
statics: {
getInstance: function() {
if (!this.instance) {
this.instance = new this();
}
return this.instance;
},
setInstance: function(instance) {
this.instance = instance;
return this;
}
},
config: {
publishers: {}
},
wildcard: '*',
constructor: function(config) {
this.listenerStacks = {};
this.activePublishers = {};
this.publishersCache = {};
this.noActivePublishers = [];
this.controller = null;
this.initConfig(config);
return this;
},
getListenerStack: function(targetType, target, eventName, createIfNotExist) {
var listenerStacks = this.listenerStacks,
map = listenerStacks[targetType],
listenerStack;
createIfNotExist = Boolean(createIfNotExist);
if (!map) {
if (createIfNotExist) {
listenerStacks[targetType] = map = {};
}
else {
return null;
}
}
map = map[target];
if (!map) {
if (createIfNotExist) {
listenerStacks[targetType][target] = map = {};
}
else {
return null;
}
}
listenerStack = map[eventName];
if (!listenerStack) {
if (createIfNotExist) {
map[eventName] = listenerStack = new Ext.event.ListenerStack();
}
else {
return null;
}
}
return listenerStack;
},
getController: function(targetType, target, eventName, connectedController) {
var controller = this.controller,
info = {
targetType: targetType,
target: target,
eventName: eventName
};
if (!controller) {
this.controller = controller = new Ext.event.Controller();
}
if (controller.isFiring) {
controller = new Ext.event.Controller();
}
controller.setInfo(info);
if (connectedController && controller !== connectedController) {
controller.connect(connectedController);
}
return controller;
},
applyPublishers: function(publishers) {
var i, publisher;
this.publishersCache = {};
for (i in publishers) {
if (publishers.hasOwnProperty(i)) {
publisher = publishers[i];
this.registerPublisher(publisher);
}
}
return publishers;
},
registerPublisher: function(publisher) {
var activePublishers = this.activePublishers,
targetType = publisher.getTargetType(),
publishers = activePublishers[targetType];
if (!publishers) {
activePublishers[targetType] = publishers = [];
}
publishers.push(publisher);
publisher.setDispatcher(this);
return this;
},
getCachedActivePublishers: function(targetType, eventName) {
var cache = this.publishersCache,
publishers;
if ((publishers = cache[targetType]) && (publishers = publishers[eventName])) {
return publishers;
}
return null;
},
cacheActivePublishers: function(targetType, eventName, publishers) {
var cache = this.publishersCache;
if (!cache[targetType]) {
cache[targetType] = {};
}
cache[targetType][eventName] = publishers;
return publishers;
},
getActivePublishers: function(targetType, eventName) {
var publishers, activePublishers,
i, ln, publisher;
if ((publishers = this.getCachedActivePublishers(targetType, eventName))) {
return publishers;
}
activePublishers = this.activePublishers[targetType];
if (activePublishers) {
publishers = [];
for (i = 0,ln = activePublishers.length; i < ln; i++) {
publisher = activePublishers[i];
if (publisher.handles(eventName)) {
publishers.push(publisher);
}
}
}
else {
publishers = this.noActivePublishers;
}
return this.cacheActivePublishers(targetType, eventName, publishers);
},
hasListener: function(targetType, target, eventName) {
var listenerStack = this.getListenerStack(targetType, target, eventName);
if (listenerStack) {
return listenerStack.count() > 0;
}
return false;
},
addListener: function(targetType, target, eventName) {
var publishers = this.getActivePublishers(targetType, eventName),
ln = publishers.length,
i;
if (ln > 0) {
for (i = 0; i < ln; i++) {
publishers[i].subscribe(target, eventName);
}
}
return this.doAddListener.apply(this, arguments);
},
doAddListener: function(targetType, target, eventName, fn, scope, options, order) {
var listenerStack = this.getListenerStack(targetType, target, eventName, true);
return listenerStack.add(fn, scope, options, order);
},
removeListener: function(targetType, target, eventName) {
var publishers = this.getActivePublishers(targetType, eventName),
ln = publishers.length,
i;
if (ln > 0) {
for (i = 0; i < ln; i++) {
publishers[i].unsubscribe(target, eventName);
}
}
return this.doRemoveListener.apply(this, arguments);
},
doRemoveListener: function(targetType, target, eventName, fn, scope, order) {
var listenerStack = this.getListenerStack(targetType, target, eventName);
if (listenerStack === null) {
return false;
}
return listenerStack.remove(fn, scope, order);
},
clearListeners: function(targetType, target, eventName) {
var listenerStacks = this.listenerStacks,
ln = arguments.length,
stacks, publishers, i, publisherGroup;
if (ln === 3) {
if (listenerStacks[targetType] && listenerStacks[targetType][target]) {
this.removeListener(targetType, target, eventName);
delete listenerStacks[targetType][target][eventName];
}
}
else if (ln === 2) {
if (listenerStacks[targetType]) {
stacks = listenerStacks[targetType][target];
if (stacks) {
for (eventName in stacks) {
if (stacks.hasOwnProperty(eventName)) {
publishers = this.getActivePublishers(targetType, eventName);
for (i = 0,ln = publishers.length; i < ln; i++) {
publishers[i].unsubscribe(target, eventName, true);
}
}
}
delete listenerStacks[targetType][target];
}
}
}
else if (ln === 1) {
publishers = this.activePublishers[targetType];
for (i = 0,ln = publishers.length; i < ln; i++) {
publishers[i].unsubscribeAll();
}
delete listenerStacks[targetType];
}
else {
publishers = this.activePublishers;
for (targetType in publishers) {
if (publishers.hasOwnProperty(targetType)) {
publisherGroup = publishers[targetType];
for (i = 0,ln = publisherGroup.length; i < ln; i++) {
publisherGroup[i].unsubscribeAll();
}
}
}
delete this.listenerStacks;
this.listenerStacks = {};
}
return this;
},
dispatchEvent: function(targetType, target, eventName) {
var publishers = this.getActivePublishers(targetType, eventName),
ln = publishers.length,
i;
if (ln > 0) {
for (i = 0; i < ln; i++) {
publishers[i].notify(target, eventName);
}
}
return this.doDispatchEvent.apply(this, arguments);
},
doDispatchEvent: function(targetType, target, eventName, args, action, connectedController) {
var listenerStack = this.getListenerStack(targetType, target, eventName),
wildcardStacks = this.getWildcardListenerStacks(targetType, target, eventName),
controller;
if ((listenerStack === null || listenerStack.length == 0)) {
if (wildcardStacks.length == 0 && !action) {
return;
}
}
else {
wildcardStacks.push(listenerStack);
}
controller = this.getController(targetType, target, eventName, connectedController);
controller.setListenerStacks(wildcardStacks);
controller.fire(args, action);
return !controller.isInterrupted();
},
getWildcardListenerStacks: function(targetType, target, eventName) {
var stacks = [],
wildcard = this.wildcard,
isEventNameNotWildcard = eventName !== wildcard,
isTargetNotWildcard = target !== wildcard,
stack;
if (isEventNameNotWildcard && (stack = this.getListenerStack(targetType, target, wildcard))) {
stacks.push(stack);
}
if (isTargetNotWildcard && (stack = this.getListenerStack(targetType, wildcard, eventName))) {
stacks.push(stack);
}
return stacks;
}
});

View File

@@ -0,0 +1,142 @@
/**
* @private
* @extends Object
* DOM event. This class really extends {@link Ext.event.Event}, but for documentation
* purposes it's members are listed inside {@link Ext.event.Event}.
*/
Ext.define('Ext.event.Dom', {
extend: 'Ext.event.Event',
constructor: function(event) {
var target = event.target,
touches;
if (target && target.nodeType !== 1) {
target = target.parentNode;
}
touches = event.changedTouches;
if (touches) {
touches = touches[0];
this.pageX = touches.pageX;
this.pageY = touches.pageY;
}
else {
this.pageX = event.pageX;
this.pageY = event.pageY;
}
this.browserEvent = this.event = event;
this.target = this.delegatedTarget = target;
this.type = event.type;
this.timeStamp = this.time = event.timeStamp;
if (typeof this.time != 'number') {
this.time = new Date(this.time).getTime();
}
return this;
},
/**
* @property {Number} distance
* The distance of the event.
*
* **This is only available when the event type is `swipe` and `pinch`.**
*/
/**
* @property {HTMLElement} target
* The target HTMLElement for this event. For example; if you are listening to a tap event and you tap on a `<div>` element,
* this will return that `<div>` element.
*/
/**
* @property {Number} pageX The browsers x coordinate of the event.
*/
/**
* @property {Number} pageY The browsers y coordinate of the event.
*/
stopEvent: function() {
this.preventDefault();
return this.callParent();
},
/**
* Prevents the browsers default handling of the event.
*/
preventDefault: function() {
this.browserEvent.preventDefault();
},
/**
* Gets the x coordinate of the event.
* @deprecated 2.0 Please use {@link #pageX} property directly.
* @return {Number}
*/
getPageX: function() {
return this.browserEvent.pageX;
},
/**
* Gets the y coordinate of the event.
* @deprecated 2.0 Please use {@link #pageX} property directly.
* @return {Number}
*/
getPageY: function() {
return this.browserEvent.pageY;
},
/**
* Gets the X and Y coordinates of the event.
* @deprecated 2.0 Please use the {@link #pageX} and {@link #pageY} properties directly.
* @return {Array}
*/
getXY: function() {
if (!this.xy) {
this.xy = [this.getPageX(), this.getPageY()];
}
return this.xy;
},
/**
* Gets the target for the event. Unlike {@link #target}, this returns the main element for your event. So if you are
* listening to a tap event on Ext.Viewport.element, and you tap on an inner element of Ext.Viewport.element, this will
* return Ext.Viewport.element.
*
* If you want the element you tapped on, then use {@link #target}.
*
* @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
* @param {Number/Mixed} [maxDepth=10||document.body] (optional) The max depth to
* search as a number or element (defaults to 10 || document.body)
* @param {Boolean} returnEl (optional) `true` to return a Ext.Element object instead of DOM node.
* @return {HTMLElement}
*/
getTarget: function(selector, maxDepth, returnEl) {
if (arguments.length === 0) {
return this.delegatedTarget;
}
return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
},
/**
* Returns the time of the event.
* @return {Date}
*/
getTime: function() {
return this.time;
},
setDelegatedTarget: function(target) {
this.delegatedTarget = target;
},
makeUnpreventable: function() {
this.browserEvent.preventDefault = Ext.emptyFn;
}
});

View File

@@ -0,0 +1,86 @@
// Using @mixins to include all members of Ext.event.Touch
// into here to keep documentation simpler
/**
* @mixins Ext.event.Touch
*
* Just as {@link Ext.dom.Element} wraps around a native DOM node, {@link Ext.event.Event} wraps the browser's native
* event-object normalizing cross-browser differences such as mechanisms to stop event-propagation along with a method
* to prevent default actions from taking place.
*
* Here is a simple example of how you use it:
*
* @example preview
* Ext.Viewport.add({
* layout: 'fit',
* items: [
* {
* docked: 'top',
* xtype: 'toolbar',
* title: 'Ext.event.Event example!'
* },
* {
* id: 'logger',
* styleHtmlContent: true,
* html: 'Tap somewhere!',
* padding: 5
* }
* ]
* });
*
* Ext.Viewport.element.on({
* tap: function(e, node) {
* var string = '';
*
* string += 'You tapped at: <strong>{ x: ' + e.pageX + ', y: ' + e.pageY + ' }</strong> <i>(e.pageX & e.pageY)</i>';
* string += '<hr />';
* string += 'The HTMLElement you tapped has the className of: <strong>' + e.target.className + '</strong> <i>(e.target)</i>';
* string += '<hr />';
* string += 'The HTMLElement which has the listener has a className of: <strong>' + e.getTarget().className + '</strong> <i>(e.getTarget())</i>';
*
* Ext.getCmp('logger').setHtml(string);
* }
* });
*
* ## Recognizers
*
* Sencha Touch includes a bunch of default event recognizers to know when a user taps, swipes, etc.
*
* For a full list of default recognizers, and more information, please view the {@link Ext.event.recognizer.Recognizer} documentation.
*/
Ext.define('Ext.event.Event', {
alternateClassName: 'Ext.EventObject',
isStopped: false,
set: function(name, value) {
if (arguments.length === 1 && typeof name != 'string') {
var info = name;
for (name in info) {
if (info.hasOwnProperty(name)) {
this[name] = info[name];
}
}
}
else {
this[name] = info[name];
}
},
/**
* Stop the event (`preventDefault` and `{@link #stopPropagation}`).
* @chainable
*/
stopEvent: function() {
return this.stopPropagation();
},
/**
* Cancels bubbling of the event.
* @chainable
*/
stopPropagation: function() {
this.isStopped = true;
return this;
}
});

View File

@@ -0,0 +1,138 @@
//@require @core
/**
* @private
*/
Ext.define('Ext.event.ListenerStack', {
currentOrder: 'current',
length: 0,
constructor: function() {
this.listeners = {
before: [],
current: [],
after: []
};
this.lateBindingMap = {};
return this;
},
add: function(fn, scope, options, order) {
var lateBindingMap = this.lateBindingMap,
listeners = this.getAll(order),
i = listeners.length,
bindingMap, listener, id;
if (typeof fn == 'string' && scope.isIdentifiable) {
id = scope.getId();
bindingMap = lateBindingMap[id];
if (bindingMap) {
if (bindingMap[fn]) {
return false;
}
else {
bindingMap[fn] = true;
}
}
else {
lateBindingMap[id] = bindingMap = {};
bindingMap[fn] = true;
}
}
else {
if (i > 0) {
while (i--) {
listener = listeners[i];
if (listener.fn === fn && listener.scope === scope) {
listener.options = options;
return false;
}
}
}
}
listener = this.create(fn, scope, options, order);
if (options && options.prepend) {
delete options.prepend;
listeners.unshift(listener);
}
else {
listeners.push(listener);
}
this.length++;
return true;
},
getAt: function(index, order) {
return this.getAll(order)[index];
},
getAll: function(order) {
if (!order) {
order = this.currentOrder;
}
return this.listeners[order];
},
count: function(order) {
return this.getAll(order).length;
},
create: function(fn, scope, options, order) {
return {
stack: this,
fn: fn,
firingFn: false,
boundFn: false,
isLateBinding: typeof fn == 'string',
scope: scope,
options: options || {},
order: order
};
},
remove: function(fn, scope, order) {
var listeners = this.getAll(order),
i = listeners.length,
isRemoved = false,
lateBindingMap = this.lateBindingMap,
listener, id;
if (i > 0) {
// Start from the end index, faster than looping from the
// beginning for "single" listeners,
// which are normally LIFO
while (i--) {
listener = listeners[i];
if (listener.fn === fn && listener.scope === scope) {
listeners.splice(i, 1);
isRemoved = true;
this.length--;
if (typeof fn == 'string' && scope.isIdentifiable) {
id = scope.getId();
if (lateBindingMap[id] && lateBindingMap[id][fn]) {
delete lateBindingMap[id][fn];
}
}
break;
}
}
}
return isRemoved;
}
});

View File

@@ -0,0 +1,88 @@
/**
* @private
* Touch event.
*/
Ext.define('Ext.event.Touch', {
extend: 'Ext.event.Dom',
requires: [
'Ext.util.Point'
],
constructor: function(event, info) {
if (info) {
this.set(info);
}
this.touchesMap = {};
this.changedTouches = this.cloneTouches(event.changedTouches);
this.touches = this.cloneTouches(event.touches);
this.targetTouches = this.cloneTouches(event.targetTouches);
return this.callParent([event]);
},
clone: function() {
return new this.self(this);
},
setTargets: function(targetsMap) {
this.doSetTargets(this.changedTouches, targetsMap);
this.doSetTargets(this.touches, targetsMap);
this.doSetTargets(this.targetTouches, targetsMap);
},
doSetTargets: function(touches, targetsMap) {
var i, ln, touch, identifier, targets;
for (i = 0,ln = touches.length; i < ln; i++) {
touch = touches[i];
identifier = touch.identifier;
targets = targetsMap[identifier];
if (targets) {
touch.targets = targets;
}
}
},
cloneTouches: function(touches) {
var map = this.touchesMap,
clone = [],
lastIdentifier = null,
i, ln, touch, identifier;
for (i = 0,ln = touches.length; i < ln; i++) {
touch = touches[i];
identifier = touch.identifier;
// A quick fix for a bug found in Bada 1.0 where all touches have
// idenfitier of 0
if (lastIdentifier !== null && identifier === lastIdentifier) {
identifier++;
}
lastIdentifier = identifier;
if (!map[identifier]) {
map[identifier] = {
pageX: touch.pageX,
pageY: touch.pageY,
identifier: identifier,
target: touch.target,
timeStamp: touch.timeStamp,
point: Ext.util.Point.fromTouch(touch),
targets: touch.targets
};
}
clone[i] = map[identifier];
}
return clone;
}
});

View File

@@ -0,0 +1,247 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.ComponentDelegation', {
extend: 'Ext.event.publisher.Publisher',
requires: [
'Ext.Component',
'Ext.ComponentQuery'
],
targetType: 'component',
optimizedSelectorRegex: /^#([\w\-]+)((?:[\s]*)>(?:[\s]*)|(?:\s*))([\w\-]+)$/i,
handledEvents: ['*'],
getSubscribers: function(eventName, createIfNotExist) {
var subscribers = this.subscribers,
eventSubscribers = subscribers[eventName];
if (!eventSubscribers && createIfNotExist) {
eventSubscribers = subscribers[eventName] = {
type: {
$length: 0
},
selector: [],
$length: 0
}
}
return eventSubscribers;
},
subscribe: function(target, eventName) {
// Ignore id-only selectors since they are already handled
if (this.idSelectorRegex.test(target)) {
return false;
}
var optimizedSelector = target.match(this.optimizedSelectorRegex),
subscribers = this.getSubscribers(eventName, true),
typeSubscribers = subscribers.type,
selectorSubscribers = subscribers.selector,
id, isDescendant, type, map, subMap;
if (optimizedSelector !== null) {
id = optimizedSelector[1];
isDescendant = optimizedSelector[2].indexOf('>') === -1;
type = optimizedSelector[3];
map = typeSubscribers[type];
if (!map) {
typeSubscribers[type] = map = {
descendents: {
$length: 0
},
children: {
$length: 0
},
$length: 0
}
}
subMap = isDescendant ? map.descendents : map.children;
if (subMap.hasOwnProperty(id)) {
subMap[id]++;
return true;
}
subMap[id] = 1;
subMap.$length++;
map.$length++;
typeSubscribers.$length++;
}
else {
if (selectorSubscribers.hasOwnProperty(target)) {
selectorSubscribers[target]++;
return true;
}
selectorSubscribers[target] = 1;
selectorSubscribers.push(target);
}
subscribers.$length++;
return true;
},
unsubscribe: function(target, eventName, all) {
var subscribers = this.getSubscribers(eventName);
if (!subscribers) {
return false;
}
var match = target.match(this.optimizedSelectorRegex),
typeSubscribers = subscribers.type,
selectorSubscribers = subscribers.selector,
id, isDescendant, type, map, subMap;
all = Boolean(all);
if (match !== null) {
id = match[1];
isDescendant = match[2].indexOf('>') === -1;
type = match[3];
map = typeSubscribers[type];
if (!map) {
return true;
}
subMap = isDescendant ? map.descendents : map.children;
if (!subMap.hasOwnProperty(id) || (!all && --subMap[id] > 0)) {
return true;
}
delete subMap[id];
subMap.$length--;
map.$length--;
typeSubscribers.$length--;
}
else {
if (!selectorSubscribers.hasOwnProperty(target) || (!all && --selectorSubscribers[target] > 0)) {
return true;
}
delete selectorSubscribers[target];
Ext.Array.remove(selectorSubscribers, target);
}
if (--subscribers.$length === 0) {
delete this.subscribers[eventName];
}
return true;
},
notify: function(target, eventName) {
var subscribers = this.getSubscribers(eventName),
id, component;
if (!subscribers || subscribers.$length === 0) {
return false;
}
id = target.substr(1);
component = Ext.ComponentManager.get(id);
if (component) {
this.dispatcher.doAddListener(this.targetType, target, eventName, 'publish', this, {
args: [eventName, component]
}, 'before');
}
},
matchesSelector: function(component, selector) {
return Ext.ComponentQuery.is(component, selector);
},
dispatch: function(target, eventName, args, connectedController) {
this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args, null, connectedController);
},
publish: function(eventName, component) {
var subscribers = this.getSubscribers(eventName);
if (!subscribers) {
return;
}
var eventController = arguments[arguments.length - 1],
typeSubscribers = subscribers.type,
selectorSubscribers = subscribers.selector,
args = Array.prototype.slice.call(arguments, 2, -2),
types = component.xtypesChain,
descendentsSubscribers, childrenSubscribers,
parentId, ancestorIds, ancestorId, parentComponent,
selector,
i, ln, type, j, subLn;
for (i = 0, ln = types.length; i < ln; i++) {
type = types[i];
subscribers = typeSubscribers[type];
if (subscribers && subscribers.$length > 0) {
descendentsSubscribers = subscribers.descendents;
if (descendentsSubscribers.$length > 0) {
if (!ancestorIds) {
ancestorIds = component.getAncestorIds();
}
for (j = 0, subLn = ancestorIds.length; j < subLn; j++) {
ancestorId = ancestorIds[j];
if (descendentsSubscribers.hasOwnProperty(ancestorId)) {
this.dispatch('#' + ancestorId + ' ' + type, eventName, args, eventController);
}
}
}
childrenSubscribers = subscribers.children;
if (childrenSubscribers.$length > 0) {
if (!parentId) {
if (ancestorIds) {
parentId = ancestorIds[0];
}
else {
parentComponent = component.getParent();
if (parentComponent) {
parentId = parentComponent.getId();
}
}
}
if (parentId) {
if (childrenSubscribers.hasOwnProperty(parentId)) {
this.dispatch('#' + parentId + ' > ' + type, eventName, args, eventController);
}
}
}
}
}
ln = selectorSubscribers.length;
if (ln > 0) {
for (i = 0; i < ln; i++) {
selector = selectorSubscribers[i];
if (this.matchesSelector(component, selector)) {
this.dispatch(selector, eventName, args, eventController);
}
}
}
}
});

View File

@@ -0,0 +1,220 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.ComponentPaint', {
extend: 'Ext.event.publisher.Publisher',
targetType: 'component',
handledEvents: ['erased'],
eventNames: {
painted: 'painted',
erased: 'erased'
},
constructor: function() {
this.callParent(arguments);
this.hiddenQueue = {};
this.renderedQueue = {};
},
getSubscribers: function(eventName, createIfNotExist) {
var subscribers = this.subscribers;
if (!subscribers.hasOwnProperty(eventName)) {
if (!createIfNotExist) {
return null;
}
subscribers[eventName] = {
$length: 0
};
}
return subscribers[eventName];
},
setDispatcher: function(dispatcher) {
var targetType = this.targetType;
dispatcher.doAddListener(targetType, '*', 'renderedchange', 'onBeforeComponentRenderedChange', this, null, 'before');
dispatcher.doAddListener(targetType, '*', 'hiddenchange', 'onBeforeComponentHiddenChange', this, null, 'before');
dispatcher.doAddListener(targetType, '*', 'renderedchange', 'onComponentRenderedChange', this, null, 'after');
dispatcher.doAddListener(targetType, '*', 'hiddenchange', 'onComponentHiddenChange', this, null, 'after');
return this.callParent(arguments);
},
subscribe: function(target, eventName) {
var match = target.match(this.idSelectorRegex),
subscribers,
id;
if (!match) {
return false;
}
id = match[1];
subscribers = this.getSubscribers(eventName, true);
if (subscribers.hasOwnProperty(id)) {
subscribers[id]++;
return true;
}
subscribers[id] = 1;
subscribers.$length++;
return true;
},
unsubscribe: function(target, eventName, all) {
var match = target.match(this.idSelectorRegex),
subscribers,
id;
if (!match || !(subscribers = this.getSubscribers(eventName))) {
return false;
}
id = match[1];
if (!subscribers.hasOwnProperty(id) || (!all && --subscribers[id] > 0)) {
return true;
}
delete subscribers[id];
if (--subscribers.$length === 0) {
delete this.subscribers[eventName];
}
return true;
},
onBeforeComponentRenderedChange: function(container, component, rendered) {
var eventNames = this.eventNames,
eventName = rendered ? eventNames.painted : eventNames.erased,
subscribers = this.getSubscribers(eventName),
queue;
if (subscribers && subscribers.$length > 0) {
this.renderedQueue[component.getId()] = queue = [];
this.publish(subscribers, component, eventName, queue);
}
},
onBeforeComponentHiddenChange: function(component, hidden) {
var eventNames = this.eventNames,
eventName = hidden ? eventNames.erased : eventNames.painted,
subscribers = this.getSubscribers(eventName),
queue;
if (subscribers && subscribers.$length > 0) {
this.hiddenQueue[component.getId()] = queue = [];
this.publish(subscribers, component, eventName, queue);
}
},
onComponentRenderedChange: function(container, component) {
var renderedQueue = this.renderedQueue,
id = component.getId(),
queue;
if (!renderedQueue.hasOwnProperty(id)) {
return;
}
queue = renderedQueue[id];
delete renderedQueue[id];
if (queue.length > 0) {
this.dispatchQueue(queue);
}
},
onComponentHiddenChange: function(component) {
var hiddenQueue = this.hiddenQueue,
id = component.getId(),
queue;
if (!hiddenQueue.hasOwnProperty(id)) {
return;
}
queue = hiddenQueue[id];
delete hiddenQueue[id];
if (queue.length > 0) {
this.dispatchQueue(queue);
}
},
dispatchQueue: function(dispatchingQueue) {
var dispatcher = this.dispatcher,
targetType = this.targetType,
eventNames = this.eventNames,
queue = dispatchingQueue.slice(),
ln = queue.length,
i, item, component, eventName, isPainted;
dispatchingQueue.length = 0;
if (ln > 0) {
for (i = 0; i < ln; i++) {
item = queue[i];
component = item.component;
eventName = item.eventName;
isPainted = component.isPainted();
if ((eventName === eventNames.painted && isPainted) || eventName === eventNames.erased && !isPainted) {
dispatcher.doDispatchEvent(targetType, '#' + item.id, eventName, [component]);
}
}
queue.length = 0;
}
},
publish: function(subscribers, component, eventName, dispatchingQueue) {
var id = component.getId(),
needsDispatching = false,
eventNames, items, i, ln, isPainted;
if (subscribers[id]) {
eventNames = this.eventNames;
isPainted = component.isPainted();
if ((eventName === eventNames.painted && !isPainted) || eventName === eventNames.erased && isPainted) {
needsDispatching = true;
}
else {
return this;
}
}
if (component.isContainer) {
items = component.getItems().items;
for (i = 0,ln = items.length; i < ln; i++) {
this.publish(subscribers, items[i], eventName, dispatchingQueue);
}
}
else if (component.isDecorator) {
this.publish(subscribers, component.getComponent(), eventName, dispatchingQueue);
}
if (needsDispatching) {
dispatchingQueue.push({
id: id,
eventName: eventName,
component: component
});
}
}
});

View File

@@ -0,0 +1,133 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.ComponentSize', {
extend: 'Ext.event.publisher.Publisher',
requires: [
'Ext.ComponentManager'
],
targetType: 'component',
handledEvents: ['resize', 'innerresize'],
constructor: function() {
this.callParent(arguments);
this.sizeMonitors = {};
},
getSubscribers: function(target, createIfNotExist) {
var subscribers = this.subscribers;
if (!subscribers.hasOwnProperty(target)) {
if (!createIfNotExist) {
return null;
}
subscribers[target] = {
$length: 0
};
}
return subscribers[target];
},
subscribe: function(target, eventName) {
var match = target.match(this.idSelectorRegex),
sizeMonitors = this.sizeMonitors,
dispatcher = this.dispatcher,
targetType = this.targetType,
subscribers, component, id;
if (!match) {
return false;
}
id = match[1];
subscribers = this.getSubscribers(target, true);
subscribers.$length++;
if (subscribers.hasOwnProperty(eventName)) {
subscribers[eventName]++;
return true;
}
if (subscribers.$length === 1) {
dispatcher.addListener(targetType, target, 'painted', 'onComponentPainted', this, null, 'before');
}
component = Ext.ComponentManager.get(id);
//<debug error>
if (!component) {
Ext.Logger.error("Adding a listener to the 'resize' event of a non-existing component");
}
//</debug>
if (!sizeMonitors[target]) {
sizeMonitors[target] = {};
}
sizeMonitors[target][eventName] = new Ext.util.SizeMonitor({
element: eventName === 'resize' ? component.element : component.innerElement,
callback: this.onComponentSizeChange,
scope: this,
args: [this, target, eventName]
});
subscribers[eventName] = 1;
return true;
},
unsubscribe: function(target, eventName, all) {
var match = target.match(this.idSelectorRegex),
dispatcher = this.dispatcher,
targetType = this.targetType,
sizeMonitors = this.sizeMonitors,
subscribers,
id;
if (!match || !(subscribers = this.getSubscribers(target))) {
return false;
}
id = match[1];
if (!subscribers.hasOwnProperty(eventName) || (!all && --subscribers[eventName] > 0)) {
return true;
}
delete subscribers[eventName];
sizeMonitors[target][eventName].destroy();
delete sizeMonitors[target][eventName];
if (--subscribers.$length === 0) {
delete sizeMonitors[target];
delete this.subscribers[target];
dispatcher.removeListener(targetType, target, 'painted', 'onComponentPainted', this, 'before');
}
return true;
},
onComponentPainted: function(component) {
var target = component.getObservableId(),
sizeMonitors = this.sizeMonitors[target];
if (sizeMonitors.resize) {
sizeMonitors.resize.refresh();
}
if (sizeMonitors.innerresize) {
sizeMonitors.innerresize.refresh();
}
},
onComponentSizeChange: function(component, observableId, eventName) {
this.dispatcher.doDispatchEvent(this.targetType, observableId, eventName, [component]);
}
});

View File

@@ -0,0 +1,430 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.Dom', {
extend: 'Ext.event.publisher.Publisher',
requires: [
'Ext.env.Browser',
'Ext.Element',
'Ext.event.Dom'
],
targetType: 'element',
idOrClassSelectorRegex: /^([#|\.])([\w\-]+)$/,
handledEvents: ['click', 'focus', 'blur', 'paste', 'input',
'mousemove', 'mousedown', 'mouseup', 'mouseover', 'mouseout',
'keyup', 'keydown', 'keypress', 'submit',
'transitionend', 'animationstart', 'animationend'],
classNameSplitRegex: /\s+/,
SELECTOR_ALL: '*',
constructor: function() {
var eventNames = this.getHandledEvents(),
eventNameMap = {},
i, ln, eventName, vendorEventName;
this.doBubbleEventsMap = {
'click': true,
'submit': true,
'mousedown': true,
'mousemove': true,
'mouseup': true,
'mouseover': true,
'mouseout': true,
'transitionend': true
};
this.onEvent = Ext.Function.bind(this.onEvent, this);
for (i = 0,ln = eventNames.length; i < ln; i++) {
eventName = eventNames[i];
vendorEventName = this.getVendorEventName(eventName);
eventNameMap[vendorEventName] = eventName;
this.attachListener(vendorEventName);
}
this.eventNameMap = eventNameMap;
return this.callParent();
},
getSubscribers: function(eventName) {
var subscribers = this.subscribers,
eventSubscribers = subscribers[eventName];
if (!eventSubscribers) {
eventSubscribers = subscribers[eventName] = {
id: {
$length: 0
},
className: {
$length: 0
},
selector: [],
all: 0,
$length: 0
}
}
return eventSubscribers;
},
getVendorEventName: function(eventName) {
if (eventName === 'transitionend') {
eventName = Ext.browser.getVendorProperyName('transitionEnd');
}
else if (eventName === 'animationstart') {
eventName = Ext.browser.getVendorProperyName('animationStart');
}
else if (eventName === 'animationend') {
eventName = Ext.browser.getVendorProperyName('animationEnd');
}
return eventName;
},
attachListener: function(eventName) {
document.addEventListener(eventName, this.onEvent, !this.doesEventBubble(eventName));
return this;
},
removeListener: function(eventName) {
document.removeEventListener(eventName, this.onEvent, !this.doesEventBubble(eventName));
return this;
},
doesEventBubble: function(eventName) {
return !!this.doBubbleEventsMap[eventName];
},
subscribe: function(target, eventName) {
if (!this.handles(eventName)) {
return false;
}
var idOrClassSelectorMatch = target.match(this.idOrClassSelectorRegex),
subscribers = this.getSubscribers(eventName),
idSubscribers = subscribers.id,
classNameSubscribers = subscribers.className,
selectorSubscribers = subscribers.selector,
type, value;
if (idOrClassSelectorMatch !== null) {
type = idOrClassSelectorMatch[1];
value = idOrClassSelectorMatch[2];
if (type === '#') {
if (idSubscribers.hasOwnProperty(value)) {
idSubscribers[value]++;
return true;
}
idSubscribers[value] = 1;
idSubscribers.$length++;
}
else {
if (classNameSubscribers.hasOwnProperty(value)) {
classNameSubscribers[value]++;
return true;
}
classNameSubscribers[value] = 1;
classNameSubscribers.$length++;
}
}
else {
if (target === this.SELECTOR_ALL) {
subscribers.all++;
}
else {
if (selectorSubscribers.hasOwnProperty(target)) {
selectorSubscribers[target]++;
return true;
}
selectorSubscribers[target] = 1;
selectorSubscribers.push(target);
}
}
subscribers.$length++;
return true;
},
unsubscribe: function(target, eventName, all) {
if (!this.handles(eventName)) {
return false;
}
var idOrClassSelectorMatch = target.match(this.idOrClassSelectorRegex),
subscribers = this.getSubscribers(eventName),
idSubscribers = subscribers.id,
classNameSubscribers = subscribers.className,
selectorSubscribers = subscribers.selector,
type, value;
all = Boolean(all);
if (idOrClassSelectorMatch !== null) {
type = idOrClassSelectorMatch[1];
value = idOrClassSelectorMatch[2];
if (type === '#') {
if (!idSubscribers.hasOwnProperty(value) || (!all && --idSubscribers[value] > 0)) {
return true;
}
delete idSubscribers[value];
idSubscribers.$length--;
}
else {
if (!classNameSubscribers.hasOwnProperty(value) || (!all && --classNameSubscribers[value] > 0)) {
return true;
}
delete classNameSubscribers[value];
classNameSubscribers.$length--;
}
}
else {
if (target === this.SELECTOR_ALL) {
if (all) {
subscribers.all = 0;
}
else {
subscribers.all--;
}
}
else {
if (!selectorSubscribers.hasOwnProperty(target) || (!all && --selectorSubscribers[target] > 0)) {
return true;
}
delete selectorSubscribers[target];
Ext.Array.remove(selectorSubscribers, target);
}
}
subscribers.$length--;
return true;
},
getElementTarget: function(target) {
if (target.nodeType !== 1) {
target = target.parentNode;
if (!target || target.nodeType !== 1) {
return null;
}
}
return target;
},
getBubblingTargets: function(target) {
var targets = [];
if (!target) {
return targets;
}
do {
targets[targets.length] = target;
target = target.parentNode;
} while (target && target.nodeType === 1);
return targets;
},
dispatch: function(target, eventName, args) {
args.push(args[0].target);
this.callParent(arguments);
},
publish: function(eventName, targets, event) {
var subscribers = this.getSubscribers(eventName),
wildcardSubscribers;
if (subscribers.$length === 0 || !this.doPublish(subscribers, eventName, targets, event)) {
wildcardSubscribers = this.getSubscribers('*');
if (wildcardSubscribers.$length > 0) {
this.doPublish(wildcardSubscribers, eventName, targets, event);
}
}
return this;
},
doPublish: function(subscribers, eventName, targets, event) {
var idSubscribers = subscribers.id,
classNameSubscribers = subscribers.className,
selectorSubscribers = subscribers.selector,
hasIdSubscribers = idSubscribers.$length > 0,
hasClassNameSubscribers = classNameSubscribers.$length > 0,
hasSelectorSubscribers = selectorSubscribers.length > 0,
hasAllSubscribers = subscribers.all > 0,
isClassNameHandled = {},
args = [event],
hasDispatched = false,
classNameSplitRegex = this.classNameSplitRegex,
i, ln, j, subLn, target, id, className, classNames, selector;
for (i = 0,ln = targets.length; i < ln; i++) {
target = targets[i];
event.setDelegatedTarget(target);
if (hasIdSubscribers) {
id = target.id;
if (id) {
if (idSubscribers.hasOwnProperty(id)) {
hasDispatched = true;
this.dispatch('#' + id, eventName, args);
}
}
}
if (hasClassNameSubscribers) {
className = target.className;
if (className) {
classNames = className.split(classNameSplitRegex);
for (j = 0,subLn = classNames.length; j < subLn; j++) {
className = classNames[j];
if (!isClassNameHandled[className]) {
isClassNameHandled[className] = true;
if (classNameSubscribers.hasOwnProperty(className)) {
hasDispatched = true;
this.dispatch('.' + className, eventName, args);
}
}
}
}
}
// Stop propagation
if (event.isStopped) {
return hasDispatched;
}
}
if (hasAllSubscribers && !hasDispatched) {
event.setDelegatedTarget(event.browserEvent.target);
hasDispatched = true;
this.dispatch(this.SELECTOR_ALL, eventName, args);
if (event.isStopped) {
return hasDispatched;
}
}
if (hasSelectorSubscribers) {
for (j = 0,subLn = targets.length; j < subLn; j++) {
target = targets[j];
for (i = 0,ln = selectorSubscribers.length; i < ln; i++) {
selector = selectorSubscribers[i];
if (this.matchesSelector(target, selector)) {
event.setDelegatedTarget(target);
hasDispatched = true;
this.dispatch(selector, eventName, args);
}
if (event.isStopped) {
return hasDispatched;
}
}
}
}
return hasDispatched;
},
matchesSelector: function(element, selector) {
if ('webkitMatchesSelector' in element) {
return element.webkitMatchesSelector(selector);
}
return Ext.DomQuery.is(element, selector);
},
onEvent: function(e) {
var eventName = this.eventNameMap[e.type];
// Set the current frame start time to be the timestamp of the event.
Ext.frameStartTime = e.timeStamp;
if (!eventName || this.getSubscribersCount(eventName) === 0) {
return;
}
var target = this.getElementTarget(e.target),
targets;
if (!target) {
return;
}
if (this.doesEventBubble(eventName)) {
targets = this.getBubblingTargets(target);
}
else {
targets = [target];
}
this.publish(eventName, targets, new Ext.event.Dom(e));
},
//<debug>
hasSubscriber: function(target, eventName) {
if (!this.handles(eventName)) {
return false;
}
var match = target.match(this.idOrClassSelectorRegex),
subscribers = this.getSubscribers(eventName),
type, value;
if (match !== null) {
type = match[1];
value = match[2];
if (type === '#') {
return subscribers.id.hasOwnProperty(value);
}
else {
return subscribers.className.hasOwnProperty(value);
}
}
else {
return (subscribers.selector.hasOwnProperty(target) && Ext.Array.indexOf(subscribers.selector, target) !== -1);
}
return false;
},
//</debug>
getSubscribersCount: function(eventName) {
if (!this.handles(eventName)) {
return 0;
}
return this.getSubscribers(eventName).$length + this.getSubscribers('*').$length;
}
});

View File

@@ -0,0 +1,79 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.ElementPaint', {
extend: 'Ext.event.publisher.Publisher',
requires: [
'Ext.util.PaintMonitor',
'Ext.TaskQueue'
],
targetType: 'element',
handledEvents: ['painted'],
constructor: function() {
this.monitors = {};
this.callSuper(arguments);
},
subscribe: function(target) {
var match = target.match(this.idSelectorRegex),
subscribers = this.subscribers,
id, element;
if (!match) {
return false;
}
id = match[1];
if (subscribers.hasOwnProperty(id)) {
subscribers[id]++;
return true;
}
subscribers[id] = 1;
element = Ext.get(id);
this.monitors[id] = new Ext.util.PaintMonitor({
element: element,
callback: this.onElementPainted,
scope: this,
args: [target, element]
});
return true;
},
unsubscribe: function(target, eventName, all) {
var match = target.match(this.idSelectorRegex),
subscribers = this.subscribers,
id;
if (!match) {
return false;
}
id = match[1];
if (!subscribers.hasOwnProperty(id) || (!all && --subscribers[id] > 0)) {
return true;
}
delete subscribers[id];
this.monitors[id].destroy();
delete this.monitors[id];
return true;
},
onElementPainted: function(target, element) {
Ext.TaskQueue.requestRead('dispatch', this, [target, 'painted', [element]]);
}
});

View File

@@ -0,0 +1,85 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.ElementSize', {
extend: 'Ext.event.publisher.Publisher',
requires: [
'Ext.util.SizeMonitor'
],
targetType: 'element',
handledEvents: ['resize'],
constructor: function() {
this.monitors = {};
this.callSuper(arguments);
},
subscribe: function(target) {
var match = target.match(this.idSelectorRegex),
subscribers = this.subscribers,
id, element, sizeMonitor;
if (!match) {
return false;
}
id = match[1];
if (subscribers.hasOwnProperty(id)) {
subscribers[id]++;
return true;
}
subscribers[id] = 1;
element = Ext.get(id);
this.monitors[id] = sizeMonitor = new Ext.util.SizeMonitor({
element: element,
callback: this.onElementResize,
scope: this,
args: [target, element]
});
this.dispatcher.addListener('element', target, 'painted', 'forceRefresh', sizeMonitor);
return true;
},
unsubscribe: function(target, eventName, all) {
var match = target.match(this.idSelectorRegex),
subscribers = this.subscribers,
monitors = this.monitors,
id, sizeMonitor;
if (!match) {
return false;
}
id = match[1];
if (!subscribers.hasOwnProperty(id) || (!all && --subscribers[id] > 0)) {
return true;
}
delete subscribers[id];
sizeMonitor = monitors[id];
this.dispatcher.removeListener('element', target, 'painted', 'forceRefresh', sizeMonitor);
sizeMonitor.destroy();
delete monitors[id];
return true;
},
onElementResize: function(target, element, info) {
Ext.TaskQueue.requestRead('dispatch', this, [target, 'resize', [element, info]]);
}
});

View File

@@ -0,0 +1,67 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.Publisher', {
targetType: '',
idSelectorRegex: /^#([\w\-]+)$/i,
constructor: function() {
var handledEvents = this.handledEvents,
handledEventsMap,
i, ln, event;
handledEventsMap = this.handledEventsMap = {};
for (i = 0,ln = handledEvents.length; i < ln; i++) {
event = handledEvents[i];
handledEventsMap[event] = true;
}
this.subscribers = {};
return this;
},
handles: function(eventName) {
var map = this.handledEventsMap;
return !!map[eventName] || !!map['*'] || eventName === '*';
},
getHandledEvents: function() {
return this.handledEvents;
},
setDispatcher: function(dispatcher) {
this.dispatcher = dispatcher;
},
subscribe: function() {
return false;
},
unsubscribe: function() {
return false;
},
unsubscribeAll: function() {
delete this.subscribers;
this.subscribers = {};
return this;
},
notify: function() {
return false;
},
getTargetType: function() {
return this.targetType;
},
dispatch: function(target, eventName, args) {
this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args);
}
});

View File

@@ -0,0 +1,502 @@
/**
* @private
*/
Ext.define('Ext.event.publisher.TouchGesture', {
extend: 'Ext.event.publisher.Dom',
requires: [
'Ext.util.Point',
'Ext.event.Touch'
],
handledEvents: ['touchstart', 'touchmove', 'touchend', 'touchcancel'],
moveEventName: 'touchmove',
config: {
moveThrottle: 1,
buffering: {
enabled: false,
interval: 10
},
recognizers: {}
},
currentTouchesCount: 0,
constructor: function(config) {
this.processEvents = Ext.Function.bind(this.processEvents, this);
this.eventProcessors = {
touchstart: this.onTouchStart,
touchmove: this.onTouchMove,
touchend: this.onTouchEnd,
touchcancel: this.onTouchEnd
};
this.eventToRecognizerMap = {};
this.activeRecognizers = [];
this.currentRecognizers = [];
this.currentTargets = {};
this.currentTouches = {};
this.buffer = [];
this.initConfig(config);
return this.callParent();
},
applyBuffering: function(buffering) {
if (buffering.enabled === true) {
this.bufferTimer = setInterval(this.processEvents, buffering.interval);
}
else {
clearInterval(this.bufferTimer);
}
return buffering;
},
applyRecognizers: function(recognizers) {
var i, recognizer;
for (i in recognizers) {
if (recognizers.hasOwnProperty(i)) {
recognizer = recognizers[i];
if (recognizer) {
this.registerRecognizer(recognizer);
}
}
}
return recognizers;
},
handles: function(eventName) {
return this.callParent(arguments) || this.eventToRecognizerMap.hasOwnProperty(eventName);
},
doesEventBubble: function() {
// All touch events bubble
return true;
},
eventLogs: [],
onEvent: function(e) {
var buffering = this.getBuffering();
e = new Ext.event.Touch(e);
if (buffering.enabled) {
this.buffer.push(e);
}
else {
this.processEvent(e);
}
},
processEvents: function() {
var buffer = this.buffer,
ln = buffer.length,
moveEvents = [],
events, event, i;
if (ln > 0) {
events = buffer.slice(0);
buffer.length = 0;
for (i = 0; i < ln; i++) {
event = events[i];
if (event.type === this.moveEventName) {
moveEvents.push(event);
}
else {
if (moveEvents.length > 0) {
this.processEvent(this.mergeEvents(moveEvents));
moveEvents.length = 0;
}
this.processEvent(event);
}
}
if (moveEvents.length > 0) {
this.processEvent(this.mergeEvents(moveEvents));
moveEvents.length = 0;
}
}
},
mergeEvents: function(events) {
var changedTouchesLists = [],
ln = events.length,
i, event, targetEvent;
targetEvent = events[ln - 1];
if (ln === 1) {
return targetEvent;
}
for (i = 0; i < ln; i++) {
event = events[i];
changedTouchesLists.push(event.changedTouches);
}
targetEvent.changedTouches = this.mergeTouchLists(changedTouchesLists);
return targetEvent;
},
mergeTouchLists: function(touchLists) {
var touches = {},
list = [],
i, ln, touchList, j, subLn, touch, identifier;
for (i = 0,ln = touchLists.length; i < ln; i++) {
touchList = touchLists[i];
for (j = 0,subLn = touchList.length; j < subLn; j++) {
touch = touchList[j];
identifier = touch.identifier;
touches[identifier] = touch;
}
}
for (identifier in touches) {
if (touches.hasOwnProperty(identifier)) {
list.push(touches[identifier]);
}
}
return list;
},
registerRecognizer: function(recognizer) {
var map = this.eventToRecognizerMap,
activeRecognizers = this.activeRecognizers,
handledEvents = recognizer.getHandledEvents(),
i, ln, eventName;
recognizer.setOnRecognized(this.onRecognized);
recognizer.setCallbackScope(this);
for (i = 0,ln = handledEvents.length; i < ln; i++) {
eventName = handledEvents[i];
map[eventName] = recognizer;
}
activeRecognizers.push(recognizer);
return this;
},
onRecognized: function(eventName, e, touches, info) {
var targetGroups = [],
ln = touches.length,
targets, i, touch;
if (ln === 1) {
return this.publish(eventName, touches[0].targets, e, info);
}
for (i = 0; i < ln; i++) {
touch = touches[i];
targetGroups.push(touch.targets);
}
targets = this.getCommonTargets(targetGroups);
this.publish(eventName, targets, e, info);
},
publish: function(eventName, targets, event, info) {
event.set(info);
return this.callParent([eventName, targets, event]);
},
getCommonTargets: function(targetGroups) {
var firstTargetGroup = targetGroups[0],
ln = targetGroups.length;
if (ln === 1) {
return firstTargetGroup;
}
var commonTargets = [],
i = 1,
target, targets, j;
while (true) {
target = firstTargetGroup[firstTargetGroup.length - i];
if (!target) {
return commonTargets;
}
for (j = 1; j < ln; j++) {
targets = targetGroups[j];
if (targets[targets.length - i] !== target) {
return commonTargets;
}
}
commonTargets.unshift(target);
i++;
}
return commonTargets;
},
invokeRecognizers: function(methodName, e) {
var recognizers = this.activeRecognizers,
ln = recognizers.length,
i, recognizer;
if (methodName === 'onStart') {
for (i = 0; i < ln; i++) {
recognizers[i].isActive = true;
}
}
for (i = 0; i < ln; i++) {
recognizer = recognizers[i];
if (recognizer.isActive && recognizer[methodName].call(recognizer, e) === false) {
recognizer.isActive = false;
}
}
},
getActiveRecognizers: function() {
return this.activeRecognizers;
},
processEvent: function(e) {
this.eventProcessors[e.type].call(this, e);
},
onTouchStart: function(e) {
var currentTargets = this.currentTargets,
currentTouches = this.currentTouches,
currentTouchesCount = this.currentTouchesCount,
currentIdentifiers = {},
changedTouches = e.changedTouches,
changedTouchedLn = changedTouches.length,
touches = e.touches,
touchesLn = touches.length,
i, touch, identifier, fakeEndEvent;
currentTouchesCount += changedTouchedLn;
if (currentTouchesCount > touchesLn) {
for (i = 0; i < touchesLn; i++) {
touch = touches[i];
identifier = touch.identifier;
currentIdentifiers[identifier] = true;
}
if (!Ext.os.is.Android3 && !Ext.os.is.Android4) {
for (identifier in currentTouches) {
if (currentTouches.hasOwnProperty(identifier)) {
if (!currentIdentifiers[identifier]) {
currentTouchesCount--;
fakeEndEvent = e.clone();
touch = currentTouches[identifier];
touch.targets = this.getBubblingTargets(this.getElementTarget(touch.target));
fakeEndEvent.changedTouches = [touch];
this.onTouchEnd(fakeEndEvent);
}
}
}
}
// Fix for a bug found in Motorola Droid X (Gingerbread) and similar
// where there are 2 touchstarts but just one touchend
if (Ext.os.is.Android2 && currentTouchesCount > touchesLn) {
return;
}
}
for (i = 0; i < changedTouchedLn; i++) {
touch = changedTouches[i];
identifier = touch.identifier;
if (!currentTouches.hasOwnProperty(identifier)) {
this.currentTouchesCount++;
}
currentTouches[identifier] = touch;
currentTargets[identifier] = this.getBubblingTargets(this.getElementTarget(touch.target));
}
e.setTargets(currentTargets);
for (i = 0; i < changedTouchedLn; i++) {
touch = changedTouches[i];
this.publish('touchstart', touch.targets, e, {touch: touch});
}
if (!this.isStarted) {
this.isStarted = true;
this.invokeRecognizers('onStart', e);
}
this.invokeRecognizers('onTouchStart', e);
},
onTouchMove: function(e) {
if (!this.isStarted) {
return;
}
var currentTargets = this.currentTargets,
currentTouches = this.currentTouches,
moveThrottle = this.getMoveThrottle(),
changedTouches = e.changedTouches,
stillTouchesCount = 0,
i, ln, touch, point, oldPoint, identifier;
e.setTargets(currentTargets);
for (i = 0,ln = changedTouches.length; i < ln; i++) {
touch = changedTouches[i];
identifier = touch.identifier;
point = touch.point;
oldPoint = currentTouches[identifier].point;
if (moveThrottle && point.isCloseTo(oldPoint, moveThrottle)) {
stillTouchesCount++;
continue;
}
currentTouches[identifier] = touch;
this.publish('touchmove', touch.targets, e, {touch: touch});
}
if (stillTouchesCount < ln) {
this.invokeRecognizers('onTouchMove', e);
}
},
onTouchEnd: function(e) {
if (!this.isStarted) {
return;
}
var currentTargets = this.currentTargets,
currentTouches = this.currentTouches,
changedTouches = e.changedTouches,
ln = changedTouches.length,
isEnded, identifier, i, touch;
e.setTargets(currentTargets);
this.currentTouchesCount -= ln;
isEnded = (this.currentTouchesCount === 0);
if (isEnded) {
this.isStarted = false;
}
for (i = 0; i < ln; i++) {
touch = changedTouches[i];
identifier = touch.identifier;
delete currentTouches[identifier];
delete currentTargets[identifier];
this.publish('touchend', touch.targets, e, {touch: touch});
}
this.invokeRecognizers('onTouchEnd', e);
if (isEnded) {
this.invokeRecognizers('onEnd', e);
}
}
}, function() {
if (!Ext.feature.has.Touch) {
this.override({
moveEventName: 'mousemove',
map: {
mouseToTouch: {
mousedown: 'touchstart',
mousemove: 'touchmove',
mouseup: 'touchend'
},
touchToMouse: {
touchstart: 'mousedown',
touchmove: 'mousemove',
touchend: 'mouseup'
}
},
attachListener: function(eventName) {
eventName = this.map.touchToMouse[eventName];
if (!eventName) {
return;
}
return this.callOverridden([eventName]);
},
lastEventType: null,
onEvent: function(e) {
if ('button' in e && e.button !== 0) {
return;
}
var type = e.type,
touchList = [e];
// Temporary fix for a recent Chrome bugs where events don't seem to bubble up to document
// when the element is being animated
// with webkit-transition (2 mousedowns without any mouseup)
if (type === 'mousedown' && this.lastEventType && this.lastEventType !== 'mouseup') {
var fixedEvent = document.createEvent("MouseEvent");
fixedEvent.initMouseEvent('mouseup', e.bubbles, e.cancelable,
document.defaultView, e.detail, e.screenX, e.screenY, e.clientX,
e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.metaKey,
e.button, e.relatedTarget);
this.onEvent(fixedEvent);
}
if (type !== 'mousemove') {
this.lastEventType = type;
}
e.identifier = 1;
e.touches = (type !== 'mouseup') ? touchList : [];
e.targetTouches = (type !== 'mouseup') ? touchList : [];
e.changedTouches = touchList;
return this.callOverridden([e]);
},
processEvent: function(e) {
this.eventProcessors[this.map.mouseToTouch[e.type]].call(this, e);
}
});
}
});

View File

@@ -0,0 +1,106 @@
/**
* A simple event recognizer which knows when you double tap.
*
* @private
*/
Ext.define('Ext.event.recognizer.DoubleTap', {
extend: 'Ext.event.recognizer.SingleTouch',
inheritableStatics: {
DIFFERENT_TARGET: 0x03
},
config: {
maxDuration: 300
},
handledEvents: ['singletap', 'doubletap'],
/**
* @member Ext.dom.Element
* @event singletap
* Fires when there is a single tap.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event doubletap
* Fires when there is a double tap.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
singleTapTimer: null,
startTime: 0,
lastTapTime: 0,
onTouchStart: function(e) {
if (this.callParent(arguments) === false) {
return false;
}
this.startTime = e.time;
clearTimeout(this.singleTapTimer);
},
onTouchMove: function() {
return this.fail(this.self.TOUCH_MOVED);
},
onEnd: function(e) {
var me = this,
maxDuration = this.getMaxDuration(),
touch = e.changedTouches[0],
time = e.time,
target = e.target,
lastTapTime = this.lastTapTime,
lastTarget = this.lastTarget,
duration;
this.lastTapTime = time;
this.lastTarget = target;
if (lastTapTime) {
duration = time - lastTapTime;
if (duration <= maxDuration) {
if (target !== lastTarget) {
return this.fail(this.self.DIFFERENT_TARGET);
}
this.lastTarget = null;
this.lastTapTime = 0;
this.fire('doubletap', e, [touch], {
touch: touch,
duration: duration
});
return;
}
}
if (time - this.startTime > maxDuration) {
this.fireSingleTap(e, touch);
}
else {
this.singleTapTimer = setTimeout(function() {
me.fireSingleTap(e, touch);
}, maxDuration);
}
},
fireSingleTap: function(e, touch) {
this.fire('singletap', e, [touch], {
touch: touch
});
}
});

View File

@@ -0,0 +1,150 @@
/**
* A simple event recognizer which knows when you drag.
*
* @private
*/
Ext.define('Ext.event.recognizer.Drag', {
extend: 'Ext.event.recognizer.SingleTouch',
isStarted: false,
startPoint: null,
previousPoint: null,
lastPoint: null,
handledEvents: ['dragstart', 'drag', 'dragend'],
/**
* @member Ext.dom.Element
* @event dragstart
* Fired once when a drag has started.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event drag
* Fires continuously when there is dragging (the touch must move for this to be fired).
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event dragend
* Fires when a drag has ended.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
onTouchStart: function(e) {
var startTouches,
startTouch;
if (this.callParent(arguments) === false) {
if (this.isStarted && this.lastMoveEvent !== null) {
this.onTouchEnd(this.lastMoveEvent);
}
return false;
}
this.startTouches = startTouches = e.changedTouches;
this.startTouch = startTouch = startTouches[0];
this.startPoint = startTouch.point;
},
onTouchMove: function(e) {
var touches = e.changedTouches,
touch = touches[0],
point = touch.point,
time = e.time;
if (this.lastPoint) {
this.previousPoint = this.lastPoint;
}
if (this.lastTime) {
this.previousTime = this.lastTime;
}
this.lastTime = time;
this.lastPoint = point;
this.lastMoveEvent = e;
if (!this.isStarted) {
this.isStarted = true;
this.startTime = time;
this.previousTime = time;
this.previousPoint = this.startPoint;
this.fire('dragstart', e, this.startTouches, this.getInfo(e, this.startTouch));
}
else {
this.fire('drag', e, touches, this.getInfo(e, touch));
}
},
onTouchEnd: function(e) {
if (this.isStarted) {
var touches = e.changedTouches,
touch = touches[0],
point = touch.point;
this.isStarted = false;
this.lastPoint = point;
this.fire('dragend', e, touches, this.getInfo(e, touch));
this.startTime = 0;
this.previousTime = 0;
this.lastTime = 0;
this.startPoint = null;
this.previousPoint = null;
this.lastPoint = null;
this.lastMoveEvent = null;
}
},
getInfo: function(e, touch) {
var time = e.time,
startPoint = this.startPoint,
previousPoint = this.previousPoint,
startTime = this.startTime,
previousTime = this.previousTime,
point = this.lastPoint,
deltaX = point.x - startPoint.x,
deltaY = point.y - startPoint.y,
info = {
touch: touch,
startX: startPoint.x,
startY: startPoint.y,
previousX: previousPoint.x,
previousY: previousPoint.y,
pageX: point.x,
pageY: point.y,
deltaX: deltaX,
deltaY: deltaY,
absDeltaX: Math.abs(deltaX),
absDeltaY: Math.abs(deltaY),
previousDeltaX: point.x - previousPoint.x,
previousDeltaY: point.y - previousPoint.y,
time: time,
startTime: startTime,
previousTime: previousTime,
deltaTime: time - startTime,
previousDeltaTime: time - previousTime
};
return info;
}
});

View File

@@ -0,0 +1,65 @@
/**
* A event recognizer created to recognize horizontal swipe movements.
*
* @private
*/
Ext.define('Ext.event.recognizer.HorizontalSwipe', {
extend: 'Ext.event.recognizer.Swipe',
handledEvents: ['swipe'],
onTouchStart: function(e) {
if (this.callParent(arguments) === false) {
return false;
}
var touch = e.changedTouches[0];
this.startTime = e.time;
this.startX = touch.pageX;
this.startY = touch.pageY;
},
onTouchMove: function(e) {
var touch = e.changedTouches[0],
y = touch.pageY,
absDeltaY = Math.abs(y - this.startY),
time = e.time,
maxDuration = this.getMaxDuration(),
maxOffset = this.getMaxOffset();
if (time - this.startTime > maxDuration) {
return this.fail(this.self.MAX_DURATION_EXCEEDED);
}
if (absDeltaY > maxOffset) {
return this.fail(this.self.MAX_OFFSET_EXCEEDED);
}
},
onTouchEnd: function(e) {
if (this.onTouchMove(e) !== false) {
var touch = e.changedTouches[0],
x = touch.pageX,
deltaX = x - this.startX,
distance = Math.abs(deltaX),
duration = e.time - this.startTime,
minDistance = this.getMinDistance(),
direction;
if (distance < minDistance) {
return this.fail(this.self.DISTANCE_NOT_ENOUGH);
}
direction = (deltaX < 0) ? 'left' : 'right';
this.fire('swipe', e, [touch], {
touch: touch,
direction: direction,
distance: distance,
duration: duration
});
}
}
});

View File

@@ -0,0 +1,90 @@
/**
* A event recognizer which knows when you tap and hold for more than 1 second.
*
* @private
*/
Ext.define('Ext.event.recognizer.LongPress', {
extend: 'Ext.event.recognizer.SingleTouch',
inheritableStatics: {
DURATION_NOT_ENOUGH: 0x20
},
config: {
minDuration: 1000
},
handledEvents: ['longpress'],
/**
* @member Ext.dom.Element
* @event longpress
* Fires when you touch and hold still for more than 1 second.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event taphold
* @inheritdoc Ext.dom.Element#longpress
*/
fireLongPress: function(e) {
var touch = e.changedTouches[0];
this.fire('longpress', e, [touch], {
touch: touch,
duration: this.getMinDuration()
});
this.isLongPress = true;
},
onTouchStart: function(e) {
var me = this;
if (this.callParent(arguments) === false) {
return false;
}
this.isLongPress = false;
this.timer = setTimeout(function() {
me.fireLongPress(e);
}, this.getMinDuration());
},
onTouchMove: function() {
return this.fail(this.self.TOUCH_MOVED);
},
onTouchEnd: function() {
if (!this.isLongPress) {
return this.fail(this.self.DURATION_NOT_ENOUGH);
}
},
fail: function() {
clearTimeout(this.timer);
return this.callParent(arguments);
}
}, function() {
this.override({
handledEvents: ['longpress', 'taphold'],
fire: function(eventName) {
if (eventName === 'longpress') {
var args = Array.prototype.slice.call(arguments);
args[0] = 'taphold';
this.fire.apply(this, args);
}
return this.callOverridden(arguments);
}
});
});

View File

@@ -0,0 +1,48 @@
/**
* @private
*/
Ext.define('Ext.event.recognizer.MultiTouch', {
extend: 'Ext.event.recognizer.Touch',
requiredTouchesCount: 2,
isTracking: false,
isStarted: false,
onTouchStart: function(e) {
var requiredTouchesCount = this.requiredTouchesCount,
touches = e.touches,
touchesCount = touches.length;
if (touchesCount === requiredTouchesCount) {
this.start(e);
}
else if (touchesCount > requiredTouchesCount) {
this.end(e);
}
},
onTouchEnd: function(e) {
this.end(e);
},
start: function() {
if (!this.isTracking) {
this.isTracking = true;
this.isStarted = false;
}
},
end: function(e) {
if (this.isTracking) {
this.isTracking = false;
if (this.isStarted) {
this.isStarted = false;
this.fireEnd(e);
}
}
}
});

View File

@@ -0,0 +1,99 @@
/**
* A event recognizer which knows when you pinch.
*
* @private
*/
Ext.define('Ext.event.recognizer.Pinch', {
extend: 'Ext.event.recognizer.MultiTouch',
requiredTouchesCount: 2,
handledEvents: ['pinchstart', 'pinch', 'pinchend'],
/**
* @member Ext.dom.Element
* @event pinchstart
* Fired once when a pinch has started.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event pinch
* Fires continuously when there is pinching (the touch must move for this to be fired).
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event pinchend
* Fires when a pinch has ended.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @property {Number} scale
* The scape of a pinch event.
*
* **This is only available when the event type is `pinch`**
* @member Ext.event.Event
*/
startDistance: 0,
lastTouches: null,
onTouchMove: function(e) {
if (!this.isTracking) {
return;
}
var touches = Array.prototype.slice.call(e.touches),
firstPoint, secondPoint, distance;
firstPoint = touches[0].point;
secondPoint = touches[1].point;
distance = firstPoint.getDistanceTo(secondPoint);
if (distance === 0) {
return;
}
if (!this.isStarted) {
this.isStarted = true;
this.startDistance = distance;
this.fire('pinchstart', e, touches, {
touches: touches,
distance: distance,
scale: 1
});
}
else {
this.fire('pinch', e, touches, {
touches: touches,
distance: distance,
scale: distance / this.startDistance
});
}
this.lastTouches = touches;
},
fireEnd: function(e) {
this.fire('pinchend', e, this.lastTouches);
},
fail: function() {
return this.callParent(arguments);
}
});

View File

@@ -0,0 +1,60 @@
/**
* A base class for all event recognizers in Sencha Touch.
*
* Sencha Touch, by default, includes various different {@link Ext.event.recognizer.Recognizer} subclasses to recognize
* events happening in your application.
*
* ## Default recognizers
*
* * {@link Ext.event.recognizer.Tap}
* * {@link Ext.event.recognizer.DoubleTap}
* * {@link Ext.event.recognizer.LongPress}
* * {@link Ext.event.recognizer.Drag}
* * {@link Ext.event.recognizer.HorizontalSwipe}
* * {@link Ext.event.recognizer.Pinch}
* * {@link Ext.event.recognizer.Rotate}
*
* ## Additional recognizers
*
* * {@link Ext.event.recognizer.VerticalSwipe}
*
* If you want to create custom recognizers, or disable recognizers in your Sencha Touch application, please refer to the
* documentation in {@link Ext#setup}.
*
* @private
*/
Ext.define('Ext.event.recognizer.Recognizer', {
mixins: ['Ext.mixin.Identifiable'],
handledEvents: [],
config: {
onRecognized: Ext.emptyFn,
onFailed: Ext.emptyFn,
callbackScope: null
},
constructor: function(config) {
this.initConfig(config);
return this;
},
getHandledEvents: function() {
return this.handledEvents;
},
onStart: Ext.emptyFn,
onEnd: Ext.emptyFn,
fail: function() {
this.getOnFailed().apply(this.getCallbackScope(), arguments);
return false;
},
fire: function() {
this.getOnRecognized().apply(this.getCallbackScope(), arguments);
}
});

View File

@@ -0,0 +1,119 @@
/**
* A simple event recognizer which knows when you rotate.
*
* @private
*/
Ext.define('Ext.event.recognizer.Rotate', {
extend: 'Ext.event.recognizer.MultiTouch',
requiredTouchesCount: 2,
handledEvents: ['rotatestart', 'rotate', 'rotateend'],
/**
* @member Ext.dom.Element
* @event rotatestart
* Fired once when a rotation has started.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event rotate
* Fires continuously when there is rotation (the touch must move for this to be fired).
* When listening to this, ensure you know about the {@link Ext.event.Event#angle} and {@link Ext.event.Event#rotation}
* properties in the `event` object.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event rotateend
* Fires when a rotation event has ended.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @property {Number} angle
* The angle of the rotation.
*
* **This is only available when the event type is `rotate`**
* @member Ext.event.Event
*/
/**
* @property {Number} rotation
* A amount of rotation, since the start of the event.
*
* **This is only available when the event type is `rotate`**
* @member Ext.event.Event
*/
startAngle: 0,
lastTouches: null,
lastAngle: null,
onTouchMove: function(e) {
if (!this.isTracking) {
return;
}
var touches = Array.prototype.slice.call(e.touches),
lastAngle = this.lastAngle,
firstPoint, secondPoint, angle, nextAngle, previousAngle, diff;
firstPoint = touches[0].point;
secondPoint = touches[1].point;
angle = firstPoint.getAngleTo(secondPoint);
if (lastAngle !== null) {
diff = Math.abs(lastAngle - angle);
nextAngle = angle + 360;
previousAngle = angle - 360;
if (Math.abs(nextAngle - lastAngle) < diff) {
angle = nextAngle;
}
else if (Math.abs(previousAngle - lastAngle) < diff) {
angle = previousAngle;
}
}
this.lastAngle = angle;
if (!this.isStarted) {
this.isStarted = true;
this.startAngle = angle;
this.fire('rotatestart', e, touches, {
touches: touches,
angle: angle,
rotation: 0
});
}
else {
this.fire('rotate', e, touches, {
touches: touches,
angle: angle,
rotation: angle - this.startAngle
});
}
this.lastTouches = touches;
},
fireEnd: function(e) {
this.lastAngle = null;
this.fire('rotateend', e, this.lastTouches);
}
});

View File

@@ -0,0 +1,18 @@
/**
* @private
*/
Ext.define('Ext.event.recognizer.SingleTouch', {
extend: 'Ext.event.recognizer.Touch',
inheritableStatics: {
NOT_SINGLE_TOUCH: 0x01,
TOUCH_MOVED: 0x02
},
onTouchStart: function(e) {
if (e.touches.length > 1) {
return this.fail(this.self.NOT_SINGLE_TOUCH);
}
}
});

View File

@@ -0,0 +1,141 @@
/**
* A base class used for both {@link Ext.event.recognizer.VerticalSwipe} and {@link Ext.event.recognizer.HorizontalSwipe}
* event recognizers.
*
* @private
*/
Ext.define('Ext.event.recognizer.Swipe', {
extend: 'Ext.event.recognizer.SingleTouch',
handledEvents: ['swipe'],
/**
* @member Ext.dom.Element
* @event swipe
* Fires when there is a swipe
* When listening to this, ensure you know about the {@link Ext.event.Event#direction} property in the `event` object.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @property {Number} direction
* The direction of the swipe. Available options are:
*
* - up
* - down
* - left
* - right
*
* __Note:__ In order to recognize swiping up and down, you must enable the vertical swipe recognizer.
*
* **This is only available when the event type is `swipe`**
* @member Ext.event.Event
*/
/**
* @property {Number} duration
* The duration of the swipe.
*
* **This is only available when the event type is `swipe`**
* @member Ext.event.Event
*/
inheritableStatics: {
MAX_OFFSET_EXCEEDED: 0x10,
MAX_DURATION_EXCEEDED: 0x11,
DISTANCE_NOT_ENOUGH: 0x12
},
config: {
minDistance: 80,
maxOffset: 35,
maxDuration: 1000
},
onTouchStart: function(e) {
if (this.callParent(arguments) === false) {
return false;
}
var touch = e.changedTouches[0];
this.startTime = e.time;
this.isHorizontal = true;
this.isVertical = true;
this.startX = touch.pageX;
this.startY = touch.pageY;
},
onTouchMove: function(e) {
var touch = e.changedTouches[0],
x = touch.pageX,
y = touch.pageY,
absDeltaX = Math.abs(x - this.startX),
absDeltaY = Math.abs(y - this.startY),
time = e.time;
if (time - this.startTime > this.getMaxDuration()) {
return this.fail(this.self.MAX_DURATION_EXCEEDED);
}
if (this.isVertical && absDeltaX > this.getMaxOffset()) {
this.isVertical = false;
}
if (this.isHorizontal && absDeltaY > this.getMaxOffset()) {
this.isHorizontal = false;
}
if (!this.isHorizontal && !this.isVertical) {
return this.fail(this.self.MAX_OFFSET_EXCEEDED);
}
},
onTouchEnd: function(e) {
if (this.onTouchMove(e) === false) {
return false;
}
var touch = e.changedTouches[0],
x = touch.pageX,
y = touch.pageY,
deltaX = x - this.startX,
deltaY = y - this.startY,
absDeltaX = Math.abs(deltaX),
absDeltaY = Math.abs(deltaY),
minDistance = this.getMinDistance(),
duration = e.time - this.startTime,
direction, distance;
if (this.isVertical && absDeltaY < minDistance) {
this.isVertical = false;
}
if (this.isHorizontal && absDeltaX < minDistance) {
this.isHorizontal = false;
}
if (this.isHorizontal) {
direction = (deltaX < 0) ? 'left' : 'right';
distance = absDeltaX;
}
else if (this.isVertical) {
direction = (deltaY < 0) ? 'up' : 'down';
distance = absDeltaY;
}
else {
return this.fail(this.self.DISTANCE_NOT_ENOUGH);
}
this.fire('swipe', e, [touch], {
touch: touch,
direction: direction,
distance: distance,
duration: duration
});
}
});

View File

@@ -0,0 +1,83 @@
/**
* A simple event recognizer which knows when you tap.
*
* @private
*/
Ext.define('Ext.event.recognizer.Tap', {
handledEvents: ['tap'],
/**
* @member Ext.dom.Element
* @event tap
* Fires when you tap
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event touchstart
* Fires when touch starts.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event tapstart
* @inheritdoc Ext.dom.Element#touchstart
* @deprecated 2.0.0 Please add listener to 'touchstart' event instead
*/
/**
* @member Ext.dom.Element
* @event touchmove
* Fires when movement while touching.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
/**
* @member Ext.dom.Element
* @event tapcancel
* @inheritdoc Ext.dom.Element#touchmove
* @deprecated 2.0.0 Please add listener to 'touchmove' event instead
*/
extend: 'Ext.event.recognizer.SingleTouch',
onTouchMove: function() {
return this.fail(this.self.TOUCH_MOVED);
},
onTouchEnd: function(e) {
var touch = e.changedTouches[0];
this.fire('tap', e, [touch]);
}
}, function() {
//<deprecated product=touch since=2.0>
this.override({
handledEvents: ['tap', 'tapstart', 'tapcancel'],
onTouchStart: function(e) {
if (this.callOverridden(arguments) === false) {
return false;
}
this.fire('tapstart', e, [e.changedTouches[0]]);
},
onTouchMove: function(e) {
this.fire('tapcancel', e, [e.changedTouches[0]]);
return this.callOverridden(arguments);
}
});
//</deprecated>
});

View File

@@ -0,0 +1,13 @@
/**
* @private
*/
Ext.define('Ext.event.recognizer.Touch', {
extend: 'Ext.event.recognizer.Recognizer',
onTouchStart: Ext.emptyFn,
onTouchMove: Ext.emptyFn,
onTouchEnd: Ext.emptyFn
});

View File

@@ -0,0 +1,69 @@
/**
* A event recognizer created to recognize vertical swipe movements.
*
* This is disabled by default in Sencha Touch as it has a performance impact when your application
* has vertical scrollers, plus, in most cases it is not very useful.
*
* If you wish to recognize vertical swipe movements in your application, please refer to the documentation of
* {@link Ext.event.recognizer.Recognizer} and {@link Ext#setup}.
*
* @private
*/
Ext.define('Ext.event.recognizer.VerticalSwipe', {
extend: 'Ext.event.recognizer.Swipe',
onTouchStart: function(e) {
if (this.callParent(arguments) === false) {
return false;
}
var touch = e.changedTouches[0];
this.startTime = e.time;
this.startX = touch.pageX;
this.startY = touch.pageY;
},
onTouchMove: function(e) {
var touch = e.changedTouches[0],
x = touch.pageX,
absDeltaX = Math.abs(x - this.startX),
maxDuration = this.getMaxDuration(),
maxOffset = this.getMaxOffset(),
time = e.time;
if (time - this.startTime > maxDuration) {
return this.fail(this.self.MAX_DURATION_EXCEEDED);
}
if (absDeltaX > maxOffset) {
return this.fail(this.self.MAX_OFFSET_EXCEEDED);
}
},
onTouchEnd: function(e) {
if (this.onTouchMove(e) !== false) {
var touch = e.changedTouches[0],
y = touch.pageY,
deltaY = y - this.startY,
distance = Math.abs(deltaY),
duration = e.time - this.startTime,
minDistance = this.getMinDistance(),
direction;
if (distance < minDistance) {
return this.fail(this.self.DISTANCE_NOT_ENOUGH);
}
direction = (deltaY < 0) ? 'up' : 'down';
this.fire('swipe', e, [touch], {
touch: touch,
distance: distance,
duration: duration,
duration: duration
});
}
}
});