/**
* @class Ext
*/
Ext.apply(Ext, {
/**
* The version of the framework
* @type String
*/
version : '1.0.0',
versionDetail : {
major : 1,
minor : 0,
patch : 0
},
/**
* Sets up a page for use on a mobile device.
* @param {Object} config
*
* Valid configurations are:
*
* - fullscreen - Boolean - Sets an appropriate meta tag for Apple devices to run in full-screen mode.
* - tabletStartupScreen - String - Startup screen to be used on an iPad. The image must be 768x1004 and in portrait orientation.
* - phoneStartupScreen - String - Startup screen to be used on an iPhone or iPod touch. The image must be 320x460 and in
* portrait orientation.
* - icon - Default icon to use. This will automatically apply to both tablets and phones. These should be 72x72.
* - tabletIcon - String - An icon for only tablets. (This config supersedes icon.) These should be 72x72.
* - phoneIcon - String - An icon for only phones. (This config supersedes icon.) These should be 57x57.
* - glossOnIcon - Boolean - Add gloss on icon on iPhone, iPad and iPod Touch
* - statusBarStyle - String - Sets the status bar style for fullscreen iPhone OS web apps. Valid options are default, black,
* or black-translucent.
* - onReady - Function - Function to be run when the DOM is ready.
-
*
- scope - Scope - Scope for the onReady configuraiton to be run in.
*
*/
setup: function(config) {
if (config && typeof config == 'object') {
if (config.addMetaTags !== false) {
this.addMetaTags(config);
}
if (Ext.isFunction(config.onReady)) {
var me = this;
Ext.onReady(function() {
var args = arguments;
if (config.fullscreen !== false) {
Ext.Viewport.init(function() {
config.onReady.apply(me, args);
});
}
else {
config.onReady.apply(this, args);
}
}, config.scope);
}
}
},
/**
* Return the dom node for the passed String (id), dom node, or Ext.Element.
* Here are some examples:
*
// gets dom node based on id
var elDom = Ext.getDom('elId');
// gets dom node based on the dom node
var elDom1 = Ext.getDom(elDom);
// If we don't know if we are working with an
// Ext.Element or a dom node use Ext.getDom
function(el){
var dom = Ext.getDom(el);
// do something with the dom node
}
* Note: the dom node to be found actually needs to exist (be rendered, etc)
* when this method is called to be successful.
* @param {Mixed} el
* @return HTMLElement
*/
getDom : function(el) {
if (!el || !document) {
return null;
}
return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
},
/**
* Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
* All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval} is
* true
, then DOM event listeners are also removed from all child nodes. The body node
* will be ignored if passed in.
* @param {HTMLElement} node The node to remove
*/
removeNode : function(node) {
if (node && node.parentNode && node.tagName != 'BODY') {
Ext.EventManager.removeAll(node);
node.parentNode.removeChild(node);
delete Ext.cache[node.id];
}
},
/**
* @private
* Creates meta tags for a given config object. This is usually just called internally from Ext.setup - see
* that method for full usage. Extracted into its own function so that Ext.Application and other classes can
* call it without invoking all of the logic inside Ext.setup.
* @param {Object} config The meta tag configuration object
*/
addMetaTags: function(config) {
if (!Ext.isObject(config)) {
return;
}
var head = Ext.get(document.getElementsByTagName('head')[0]),
tag, precomposed;
/*
* The viewport meta tag. This disables user scaling. This is supported
* on most Android phones and all iOS devices and will probably be supported
* on most future devices (like Blackberry, Palm etc).
*/
if (!Ext.is.Desktop) {
tag = Ext.get(document.createElement('meta'));
tag.set({
name: 'viewport',
content: 'width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;'
});
head.appendChild(tag);
}
/*
* We want to check now for iOS specific meta tags. Unfortunately most
* of these are not supported on devices other then iOS.
*/
if (Ext.is.iOS) {
/*
* On iOS, if you save to home screen, you can decide if you want
* to launch the app fullscreen (without address bar). You can also
* change the styling of the status bar at the top of the screen.
*/
if (config.fullscreen !== false) {
tag = Ext.get(document.createElement('meta'));
tag.set({
name: 'apple-mobile-web-app-capable',
content: 'yes'
});
head.appendChild(tag);
if (Ext.isString(config.statusBarStyle)) {
tag = Ext.get(document.createElement('meta'));
tag.set({
name: 'apple-mobile-web-app-status-bar-style',
content: config.statusBarStyle
});
head.appendChild(tag);
}
}
/*
* iOS allows you to specify a startup screen. This is displayed during
* the startup of your app if you save to your homescreen. Since we could
* be dealing with an iPad or iPhone/iPod, we have a tablet startup screen
* and a phone startup screen.
*/
if (config.tabletStartupScreen && Ext.is.iPad) {
tag = Ext.get(document.createElement('link'));
tag.set({
rel: 'apple-touch-startup-image',
href: config.tabletStartupScreen
});
head.appendChild(tag);
}
if (config.phoneStartupScreen && !Ext.is.iPad) {
tag = Ext.get(document.createElement('link'));
tag.set({
rel: 'apple-touch-startup-image',
href: config.phoneStartupScreen
});
head.appendChild(tag);
}
/*
* On iOS you can specify the icon used when you save the app to your
* homescreen. You can set an icon that will be used for both iPads
* and iPhone/iPod, or you can specify specific icons for both.
*/
if (config.icon) {
config.phoneIcon = config.tabletIcon = config.icon;
}
precomposed = (config.glossOnIcon === false) ? '-precomposed' : '';
if (Ext.is.iPad && Ext.isString(config.tabletIcon)) {
tag = Ext.get(document.createElement('link'));
tag.set({
rel: 'apple-touch-icon' + precomposed,
href: config.tabletIcon
});
head.appendChild(tag);
}
else if (!Ext.is.iPad && Ext.isString(config.phoneIcon)) {
tag = Ext.get(document.createElement('link'));
tag.set({
rel: 'apple-touch-icon' + precomposed,
href: config.phoneIcon
});
head.appendChild(tag);
}
}
}
});
Ext.Viewport = new (Ext.extend(Ext.util.Observable, {
constructor: function() {
this.addEvents(
'orientationchange',
'resize'
);
if (Ext.supports.OrientationChange) {
window.addEventListener('orientationchange', Ext.createDelegate(this.onOrientationChange, this), false);
}
else {
window.addEventListener('resize', Ext.createDelegate(this.onResize, this), false);
}
if (!Ext.desktop) {
document.addEventListener('touchstart', Ext.createDelegate(this.onTouchStartCapturing, this), true);
document.addEventListener('touchmove', Ext.createDelegate(this.onTouchMoveBubbling, this), false);
}
this.stretchSizes = {};
},
init: function(fn) {
var me = this,
stretchSize = Math.max(window.innerHeight, window.innerWidth) * 2;
me.updateOrientation();
if (!Ext.is.Desktop) {
me.stretchEl = Ext.getBody().createChild({
cls: 'x-body-stretcher',
style: 'height: ' + stretchSize + 'px; width:' + (Ext.is.Android ? stretchSize + 'px' : '1px')
});
}
setTimeout(function() {
me.scrollToTop();
setTimeout(function() {
Ext.getBody().setStyle('overflow', 'hidden');
if (Ext.is.Android && me.stretchEl) {
me.stretchEl.remove();
delete me.stretchEl;
}
if (fn) {
setTimeout(function() {
fn();
if (!Ext.is.Android) {
me.updateBodySize();
}
}, 500);
}
}, 200);
}, 500);
if (!Ext.is.Android) {
this.on('orientationchange', function() {
if (Ext.currentlyFocusedField) {
Ext.currentlyFocusedField.blur();
}
});
}
},
showStretchEl: function() {
if (!Ext.is.Android && this.stretchEl) {
this.stretchEl.setStyle('display', 'block');
}
},
hideStretchEl: function() {
if (!Ext.is.Android && this.stretchEl) {
this.stretchEl.setStyle('display', 'none');
}
},
updateBodySize: function() {
// Why Ext.getBody() is undefined in here? WTF!
document.body.style.width = window.innerWidth + 'px';
document.body.style.height = window.innerHeight + 'px';
},
updateOrientation: function() {
this.lastSize = this.getSize();
this.orientation = this.getOrientation();
},
onTouchStartCapturing: function(e) {
var target = e.target;
if (!Ext.currentlyFocusedField && !Ext.is.Android) {
this.scrollToTop();
}
if (Ext.is.Android) {
if (target.nodeType == 3) {
target = target.parentNode;
}
if (target.tagName && (/input|select|textarea/i.test(target.tagName))) {
return;
}
e.preventDefault();
e.stopPropagation();
}
},
onTouchMoveBubbling: function(e) {
e.preventDefault();
e.stopPropagation();
},
onOrientationChange: function() {
var me = this;
this.updateOrientation();
this.fireEvent('orientationchange', this, this.orientation);
if (!Ext.is.Android) {
this.scrollToTop(1, function() {
me.updateBodySize();
me.fireResizeEvent();
}, true);
} else {
this.scrollToTop(100, function() {
me.fireResizeEvent();
});
}
},
fireResizeEvent: function() {
var me = this;
var size = me.getSize();
if (Ext.is.Android) {
if (this.resizeEventTimer) {
clearTimeout(this.resizeEventTimer);
}
this.resizeEventTimer = setTimeout(function() {
me.fireEvent('resize', me, me.getSize());
}, 500);
} else {
me.fireEvent('resize', me, me.getSize());
}
},
onResize: function() {
if (this.orientation != this.getOrientation()) {
this.onOrientationChange();
} else {
var size = this.getSize();
if (Ext.is.Android) {
if ((size.width == this.lastSize.width && size.height > this.lastSize.height) ||
(size.height == this.lastSize.height && size.width > this.lastSize.width)) {
this.fireEvent('resize', this, size);
}
} else {
this.fireEvent('resize', this, size);
}
}
},
getSize: function() {
return {
width: window.innerWidth,
height: window.innerHeight
};
},
getOffset: function() {
return {
x: window.pageXOffset,
y: window.pageYOffset
};
},
scrollToTop: function(delay, fn) {
if (delay) {
setTimeout(function() {
window.scrollTo(0, Ext.is.Android ? -1 : 0);
if (fn) {
setTimeout(fn, 0);
}
}, delay);
}
else {
window.scrollTo(0, Ext.is.Android ? -1 : 0);
if (fn) {
setTimeout(fn, 0);
}
}
},
getOrientation: function() {
var size = this.getSize();
if (window.hasOwnProperty('orientation')) {
return (window.orientation == 0 || window.orientation == 180) ? 'portrait' : 'landscape';
}
else {
if (Ext.is.Android) {
if ((size.width == this.lastSize.width && size.height < this.lastSize.height) ||
(size.height == this.lastSize.height && size.width < this.lastSize.width)) {
return this.orientation;
}
}
return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
}
}
}));
//Initialize doc classes and feature detections
(function() {
var initExt = function() {
// find the body element
var bd = Ext.getBody(),
cls = [];
if (!bd) {
return false;
}
var Is = Ext.is;
if (Is.Phone) {
cls.push('x-phone');
}
else if (Is.Tablet) {
cls.push('x-tablet');
}
else if (Is.Desktop) {
cls.push('x-desktop');
}
if (Is.iPad) {
cls.push('x-ipad');
}
if (Is.iOS) {
cls.push('x-ios');
}
if (Is.Android) {
cls.push('x-android');
}
if (Is.Standalone) {
cls.push('x-standalone');
}
if (cls.length) {
bd.addCls(cls);
}
return true;
};
if (!initExt()) {
Ext.onReady(initExt);
}
})();