ionic-angular
Version:
[](https://circleci.com/gh/driftyco/ionic)
671 lines (574 loc) • 20.9 kB
JavaScript
/**
* @ngdoc provider
* @name $ionicConfigProvider
* @module ionic
* @description
* Ionic automatically takes platform configurations into account to adjust things like what
* transition style to use and whether tab icons should show on the top or bottom. For example,
* iOS will move forward by transitioning the entering view from right to center and the leaving
* view from center to left. However, Android will transition with the entering view going from
* bottom to center, covering the previous view, which remains stationary. It should be noted
* that when a platform is not iOS or Android, then it'll default to iOS. So if you are
* developing on a desktop browser, it's going to take on iOS default configs.
*
* These configs can be changed using the `$ionicConfigProvider` during the configuration phase
* of your app. Additionally, `$ionicConfig` can also set and get config values during the run
* phase and within the app itself.
*
* By default, all base config variables are set to `'platform'`, which means it'll take on the
* default config of the platform on which it's running. Config variables can be set at this
* level so all platforms follow the same setting, rather than its platform config.
* The following code would set the same config variable for all platforms:
*
* ```js
* $ionicConfigProvider.views.maxCache(10);
* ```
*
* Additionally, each platform can have it's own config within the `$ionicConfigProvider.platform`
* property. The config below would only apply to Android devices.
*
* ```js
* $ionicConfigProvider.platform.android.views.maxCache(5);
* ```
*
* @usage
* ```js
* var myApp = angular.module('reallyCoolApp', ['ionic']);
*
* myApp.config(function($ionicConfigProvider) {
* $ionicConfigProvider.views.maxCache(5);
*
* // note that you can also chain configs
* $ionicConfigProvider.backButton.text('Go Back').icon('ion-chevron-left');
* });
* ```
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#views.transition
* @description Animation style when transitioning between views. Default `platform`.
*
* @param {string} transition Which style of view transitioning to use.
*
* * `platform`: Dynamically choose the correct transition style depending on the platform
* the app is running from. If the platform is not `ios` or `android` then it will default
* to `ios`.
* * `ios`: iOS style transition.
* * `android`: Android style transition.
* * `none`: Do not perform animated transitions.
*
* @returns {string} value
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#views.maxCache
* @description Maximum number of view elements to cache in the DOM. When the max number is
* exceeded, the view with the longest time period since it was accessed is removed. Views that
* stay in the DOM cache the view's scope, current state, and scroll position. The scope is
* disconnected from the `$watch` cycle when it is cached and reconnected when it enters again.
* When the maximum cache is `0`, the leaving view's element will be removed from the DOM after
* each view transition, and the next time the same view is shown, it will have to re-compile,
* attach to the DOM, and link the element again. This disables caching, in effect.
* @param {number} maxNumber Maximum number of views to retain. Default `10`.
* @returns {number} How many views Ionic will hold onto until the a view is removed.
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#views.forwardCache
* @description By default, when navigating, views that were recently visited are cached, and
* the same instance data and DOM elements are referenced when navigating back. However, when
* navigating back in the history, the "forward" views are removed from the cache. If you
* navigate forward to the same view again, it'll create a new DOM element and controller
* instance. Basically, any forward views are reset each time. Set this config to `true` to have
* forward views cached and not reset on each load.
* @param {boolean} value
* @returns {boolean}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#views.swipeBackEnabled
* @description By default on iOS devices, swipe to go back functionality is enabled by default.
* This method can be used to disable it globally, or on a per-view basis.
* Note: This functionality is only supported on iOS.
* @param {boolean} value
* @returns {boolean}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#scrolling.jsScrolling
* @description Whether to use JS or Native scrolling. Defaults to native scrolling. Setting this to
* `true` has the same effect as setting each `ion-content` to have `overflow-scroll='false'`.
* @param {boolean} value Defaults to `false` as of Ionic 1.2
* @returns {boolean}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#backButton.icon
* @description Back button icon.
* @param {string} value
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#backButton.text
* @description Back button text.
* @param {string} value Defaults to `Back`.
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#backButton.previousTitleText
* @description If the previous title text should become the back button text. This
* is the default for iOS.
* @param {boolean} value
* @returns {boolean}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#form.checkbox
* @description Checkbox style. Android defaults to `square` and iOS defaults to `circle`.
* @param {string} value
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#form.toggle
* @description Toggle item style. Android defaults to `small` and iOS defaults to `large`.
* @param {string} value
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#spinner.icon
* @description Default spinner icon to use.
* @param {string} value Can be: `android`, `ios`, `ios-small`, `bubbles`, `circles`, `crescent`,
* `dots`, `lines`, `ripple`, or `spiral`.
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#tabs.style
* @description Tab style. Android defaults to `striped` and iOS defaults to `standard`.
* @param {string} value Available values include `striped` and `standard`.
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#tabs.position
* @description Tab position. Android defaults to `top` and iOS defaults to `bottom`.
* @param {string} value Available values include `top` and `bottom`.
* @returns {string}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#templates.maxPrefetch
* @description Sets the maximum number of templates to prefetch from the templateUrls defined in
* $stateProvider.state. If set to `0`, the user will have to wait
* for a template to be fetched the first time when navigating to a new page. Default `30`.
* @param {integer} value Max number of template to prefetch from the templateUrls defined in
* `$stateProvider.state()`.
* @returns {integer}
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#navBar.alignTitle
* @description Which side of the navBar to align the title. Default `center`.
*
* @param {string} value side of the navBar to align the title.
*
* * `platform`: Dynamically choose the correct title style depending on the platform
* the app is running from. If the platform is `ios`, it will default to `center`.
* If the platform is `android`, it will default to `left`. If the platform is not
* `ios` or `android`, it will default to `center`.
*
* * `left`: Left align the title in the navBar
* * `center`: Center align the title in the navBar
* * `right`: Right align the title in the navBar.
*
* @returns {string} value
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#navBar.positionPrimaryButtons
* @description Which side of the navBar to align the primary navBar buttons. Default `left`.
*
* @param {string} value side of the navBar to align the primary navBar buttons.
*
* * `platform`: Dynamically choose the correct title style depending on the platform
* the app is running from. If the platform is `ios`, it will default to `left`.
* If the platform is `android`, it will default to `right`. If the platform is not
* `ios` or `android`, it will default to `left`.
*
* * `left`: Left align the primary navBar buttons in the navBar
* * `right`: Right align the primary navBar buttons in the navBar.
*
* @returns {string} value
*/
/**
* @ngdoc method
* @name $ionicConfigProvider#navBar.positionSecondaryButtons
* @description Which side of the navBar to align the secondary navBar buttons. Default `right`.
*
* @param {string} value side of the navBar to align the secondary navBar buttons.
*
* * `platform`: Dynamically choose the correct title style depending on the platform
* the app is running from. If the platform is `ios`, it will default to `right`.
* If the platform is `android`, it will default to `right`. If the platform is not
* `ios` or `android`, it will default to `right`.
*
* * `left`: Left align the secondary navBar buttons in the navBar
* * `right`: Right align the secondary navBar buttons in the navBar.
*
* @returns {string} value
*/
IonicModule
.provider('$ionicConfig', function() {
var provider = this;
provider.platform = {};
var PLATFORM = 'platform';
var configProperties = {
views: {
maxCache: PLATFORM,
forwardCache: PLATFORM,
transition: PLATFORM,
swipeBackEnabled: PLATFORM,
swipeBackHitWidth: PLATFORM
},
navBar: {
alignTitle: PLATFORM,
positionPrimaryButtons: PLATFORM,
positionSecondaryButtons: PLATFORM,
transition: PLATFORM
},
backButton: {
icon: PLATFORM,
text: PLATFORM,
previousTitleText: PLATFORM
},
form: {
checkbox: PLATFORM,
toggle: PLATFORM
},
scrolling: {
jsScrolling: PLATFORM
},
spinner: {
icon: PLATFORM
},
tabs: {
style: PLATFORM,
position: PLATFORM
},
templates: {
maxPrefetch: PLATFORM
},
platform: {}
};
createConfig(configProperties, provider, '');
// Default
// -------------------------
setPlatformConfig('default', {
views: {
maxCache: 10,
forwardCache: false,
transition: 'ios',
swipeBackEnabled: true,
swipeBackHitWidth: 45
},
navBar: {
alignTitle: 'center',
positionPrimaryButtons: 'left',
positionSecondaryButtons: 'right',
transition: 'view'
},
backButton: {
icon: 'ion-ios-arrow-back',
text: 'Back',
previousTitleText: true
},
form: {
checkbox: 'circle',
toggle: 'large'
},
scrolling: {
jsScrolling: true
},
spinner: {
icon: 'ios'
},
tabs: {
style: 'standard',
position: 'bottom'
},
templates: {
maxPrefetch: 30
}
});
// iOS (it is the default already)
// -------------------------
setPlatformConfig('ios', {});
// Android
// -------------------------
setPlatformConfig('android', {
views: {
transition: 'android',
swipeBackEnabled: false
},
navBar: {
alignTitle: 'left',
positionPrimaryButtons: 'right',
positionSecondaryButtons: 'right'
},
backButton: {
icon: 'ion-android-arrow-back',
text: false,
previousTitleText: false
},
form: {
checkbox: 'square',
toggle: 'small'
},
spinner: {
icon: 'android'
},
tabs: {
style: 'striped',
position: 'top'
},
scrolling: {
jsScrolling: false
}
});
// Windows Phone
// -------------------------
setPlatformConfig('windowsphone', {
//scrolling: {
// jsScrolling: false
//}
spinner: {
icon: 'android'
}
});
provider.transitions = {
views: {},
navBar: {}
};
// iOS Transitions
// -----------------------
provider.transitions.views.ios = function(enteringEle, leavingEle, direction, shouldAnimate) {
function setStyles(ele, opacity, x, boxShadowOpacity) {
var css = {};
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0;
css.opacity = opacity;
if (boxShadowOpacity > -1) {
css.boxShadow = '0 0 10px rgba(0,0,0,' + (d.shouldAnimate ? boxShadowOpacity * 0.45 : 0.3) + ')';
}
css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)';
ionic.DomUtil.cachedStyles(ele, css);
}
var d = {
run: function(step) {
if (direction == 'forward') {
setStyles(enteringEle, 1, (1 - step) * 99, 1 - step); // starting at 98% prevents a flicker
setStyles(leavingEle, (1 - 0.1 * step), step * -33, -1);
} else if (direction == 'back') {
setStyles(enteringEle, (1 - 0.1 * (1 - step)), (1 - step) * -33, -1);
setStyles(leavingEle, 1, step * 100, 1 - step);
} else {
// swap, enter, exit
setStyles(enteringEle, 1, 0, -1);
setStyles(leavingEle, 0, 0, -1);
}
},
shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back')
};
return d;
};
provider.transitions.navBar.ios = function(enteringHeaderBar, leavingHeaderBar, direction, shouldAnimate) {
function setStyles(ctrl, opacity, titleX, backTextX) {
var css = {};
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : '0ms';
css.opacity = opacity === 1 ? '' : opacity;
ctrl.setCss('buttons-left', css);
ctrl.setCss('buttons-right', css);
ctrl.setCss('back-button', css);
css[ionic.CSS.TRANSFORM] = 'translate3d(' + backTextX + 'px,0,0)';
ctrl.setCss('back-text', css);
css[ionic.CSS.TRANSFORM] = 'translate3d(' + titleX + 'px,0,0)';
ctrl.setCss('title', css);
}
function enter(ctrlA, ctrlB, step) {
if (!ctrlA || !ctrlB) return;
var titleX = (ctrlA.titleTextX() + ctrlA.titleWidth()) * (1 - step);
var backTextX = (ctrlB && (ctrlB.titleTextX() - ctrlA.backButtonTextLeft()) * (1 - step)) || 0;
setStyles(ctrlA, step, titleX, backTextX);
}
function leave(ctrlA, ctrlB, step) {
if (!ctrlA || !ctrlB) return;
var titleX = (-(ctrlA.titleTextX() - ctrlB.backButtonTextLeft()) - (ctrlA.titleLeftRight())) * step;
setStyles(ctrlA, 1 - step, titleX, 0);
}
var d = {
run: function(step) {
var enteringHeaderCtrl = enteringHeaderBar.controller();
var leavingHeaderCtrl = leavingHeaderBar && leavingHeaderBar.controller();
if (d.direction == 'back') {
leave(enteringHeaderCtrl, leavingHeaderCtrl, 1 - step);
enter(leavingHeaderCtrl, enteringHeaderCtrl, 1 - step);
} else {
enter(enteringHeaderCtrl, leavingHeaderCtrl, step);
leave(leavingHeaderCtrl, enteringHeaderCtrl, step);
}
},
direction: direction,
shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back')
};
return d;
};
// Android Transitions
// -----------------------
provider.transitions.views.android = function(enteringEle, leavingEle, direction, shouldAnimate) {
shouldAnimate = shouldAnimate && (direction == 'forward' || direction == 'back');
function setStyles(ele, x, opacity) {
var css = {};
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0;
css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)';
css.opacity = opacity;
ionic.DomUtil.cachedStyles(ele, css);
}
var d = {
run: function(step) {
if (direction == 'forward') {
setStyles(enteringEle, (1 - step) * 99, 1); // starting at 98% prevents a flicker
setStyles(leavingEle, step * -100, 1);
} else if (direction == 'back') {
setStyles(enteringEle, (1 - step) * -100, 1);
setStyles(leavingEle, step * 100, 1);
} else {
// swap, enter, exit
setStyles(enteringEle, 0, 1);
setStyles(leavingEle, 0, 0);
}
},
shouldAnimate: shouldAnimate
};
return d;
};
provider.transitions.navBar.android = function(enteringHeaderBar, leavingHeaderBar, direction, shouldAnimate) {
function setStyles(ctrl, opacity) {
if (!ctrl) return;
var css = {};
css.opacity = opacity === 1 ? '' : opacity;
ctrl.setCss('buttons-left', css);
ctrl.setCss('buttons-right', css);
ctrl.setCss('back-button', css);
ctrl.setCss('back-text', css);
ctrl.setCss('title', css);
}
return {
run: function(step) {
setStyles(enteringHeaderBar.controller(), step);
setStyles(leavingHeaderBar && leavingHeaderBar.controller(), 1 - step);
},
shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back')
};
};
// No Transition
// -----------------------
provider.transitions.views.none = function(enteringEle, leavingEle) {
return {
run: function(step) {
provider.transitions.views.android(enteringEle, leavingEle, false, false).run(step);
},
shouldAnimate: false
};
};
provider.transitions.navBar.none = function(enteringHeaderBar, leavingHeaderBar) {
return {
run: function(step) {
provider.transitions.navBar.ios(enteringHeaderBar, leavingHeaderBar, false, false).run(step);
provider.transitions.navBar.android(enteringHeaderBar, leavingHeaderBar, false, false).run(step);
},
shouldAnimate: false
};
};
// private: used to set platform configs
function setPlatformConfig(platformName, platformConfigs) {
configProperties.platform[platformName] = platformConfigs;
provider.platform[platformName] = {};
addConfig(configProperties, configProperties.platform[platformName]);
createConfig(configProperties.platform[platformName], provider.platform[platformName], '');
}
// private: used to recursively add new platform configs
function addConfig(configObj, platformObj) {
for (var n in configObj) {
if (n != PLATFORM && configObj.hasOwnProperty(n)) {
if (angular.isObject(configObj[n])) {
if (!isDefined(platformObj[n])) {
platformObj[n] = {};
}
addConfig(configObj[n], platformObj[n]);
} else if (!isDefined(platformObj[n])) {
platformObj[n] = null;
}
}
}
}
// private: create methods for each config to get/set
function createConfig(configObj, providerObj, platformPath) {
forEach(configObj, function(value, namespace) {
if (angular.isObject(configObj[namespace])) {
// recursively drill down the config object so we can create a method for each one
providerObj[namespace] = {};
createConfig(configObj[namespace], providerObj[namespace], platformPath + '.' + namespace);
} else {
// create a method for the provider/config methods that will be exposed
providerObj[namespace] = function(newValue) {
if (arguments.length) {
configObj[namespace] = newValue;
return providerObj;
}
if (configObj[namespace] == PLATFORM) {
// if the config is set to 'platform', then get this config's platform value
var platformConfig = stringObj(configProperties.platform, ionic.Platform.platform() + platformPath + '.' + namespace);
if (platformConfig || platformConfig === false) {
return platformConfig;
}
// didnt find a specific platform config, now try the default
return stringObj(configProperties.platform, 'default' + platformPath + '.' + namespace);
}
return configObj[namespace];
};
}
});
}
function stringObj(obj, str) {
str = str.split(".");
for (var i = 0; i < str.length; i++) {
if (obj && isDefined(obj[str[i]])) {
obj = obj[str[i]];
} else {
return null;
}
}
return obj;
}
provider.setPlatformConfig = setPlatformConfig;
// private: Service definition for internal Ionic use
/**
* @ngdoc service
* @name $ionicConfig
* @module ionic
* @private
*/
provider.$get = function() {
return provider;
};
})
// Fix for URLs in Cordova apps on Windows Phone
// http://blogs.msdn.com/b/msdn_answers/archive/2015/02/10/
// running-cordova-apps-on-windows-and-windows-phone-8-1-using-ionic-angularjs-and-other-frameworks.aspx
.config(['$compileProvider', function($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|sms|tel|geo|ftp|mailto|file|ghttps?|ms-appx-web|ms-appx|x-wmapp0):/);
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|content|blob|ms-appx|ms-appx-web|x-wmapp0):|data:image\//);
}]);