' +
'{{title}}
' +
'' +
- '' +
+ '' +
'' +
''
})
diff --git a/ionic/animations/animation.ts b/ionic/animations/animation.ts
index 4a0bd81689..c88febc0eb 100644
--- a/ionic/animations/animation.ts
+++ b/ionic/animations/animation.ts
@@ -408,6 +408,21 @@ export class Animation {
}
}
+ /**
+ * Get the current time of the first animation
+ * in the list. To get a specific time of an animation, call
+ * subAnimationInstance.getCurrentTime()
+ */
+ getCurrentTime() {
+ if(this._chld.length > 0) {
+ return this._chld[0].getCurrentTime();
+ }
+ if(this._ani.length > 0) {
+ return this._ani[0].getCurrentTime();
+ }
+ return 0;
+ }
+
progressEnd(shouldComplete, rate=3) {
let promises = [];
@@ -604,6 +619,10 @@ class Animate {
}
}
+ getCurrentTime() {
+ return this.ani && this.ani.currentTime;
+ }
+
playbackRate(value) {
this.rate = value;
if (this.ani) {
diff --git a/ionic/components/app/app.ts b/ionic/components/app/app.ts
index def11107a2..56d46155e3 100644
--- a/ionic/components/app/app.ts
+++ b/ionic/components/app/app.ts
@@ -14,6 +14,7 @@ import {Popup} from '../popup/popup';
import {FocusHolder} from '../form/focus-holder';
import {Events} from '../../util/events';
import {NavRegistry} from '../nav/nav-registry';
+import {Translate} from '../../translation/translate';
/**
* @name IonicApp
@@ -298,6 +299,8 @@ export function ionicBootstrap(rootComponentType, views, config) {
let modal = new Modal(app, config);
let popup = new Popup(app, config);
let events = new Events();
+ let translate = new Translate();
+ console.log('Translate', translate);
let navRegistry = new NavRegistry(views);
// add injectables that will be available to all child components
@@ -312,6 +315,7 @@ export function ionicBootstrap(rootComponentType, views, config) {
bind(Events).toValue(events),
ROUTER_BINDINGS,
bind(LocationStrategy).toClass(HashLocationStrategy),
+ bind(Translate).toValue(translate),
bind(NavRegistry).toValue(navRegistry)
]);
diff --git a/ionic/components/tap-click/ripple.ts b/ionic/components/tap-click/ripple.ts
index 5d34126edb..e0b259de78 100644
--- a/ionic/components/tap-click/ripple.ts
+++ b/ionic/components/tap-click/ripple.ts
@@ -4,6 +4,16 @@ import {Animation} from '../../animations/animation';
export class RippleActivator extends Activator {
+ static TOUCH_DOWN_ACCEL = 512;
+ static TOUCH_UP_ACCEL = 1024;
+ static OPACITY_DECAY_VEL = 1.6;
+ static OUTER_OPACITY_VEL = 1.2;
+
+ static OPACITY_OUT_DURATION = 750;
+
+ static EXPAND_OUT_PLAYBACK_RATE = 3.5;
+ static DOWN_PLAYBACK_RATE = 0.65;
+ static OPACITY_OUT_PLAYBACK_RATE = 1;
constructor(app, config) {
super(app, config);
@@ -17,7 +27,19 @@ export class RippleActivator extends Activator {
super.downAction(ev, activatableEle, pointerX, pointerY);
// create a new ripple element
- let r = activatableEle.getBoundingClientRect();
+ let r = targetEle.getBoundingClientRect();
+ let w = r.width;
+ let h = r.height;
+
+ let halfW = w/2;
+ let halfH = h/2;
+ let outerRadius = Math.sqrt(halfW * halfW + halfH * halfH);
+
+ let radiusDuration = (1000 * Math.sqrt(outerRadius / RippleActivator.TOUCH_DOWN_ACCEL) + 0.5);
+ let outerDuration = 1000 * (1/RippleActivator.OUTER_OPACITY_VEL);
+
+ //console.log('Rippling', radiusDuration);
+
let x = Math.max(Math.abs(r.width - pointerX), pointerX) * 2;
let y = Math.max(Math.abs(r.height - pointerY), pointerY) * 2;
let size = (Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) - 10;
@@ -34,14 +56,16 @@ export class RippleActivator extends Activator {
activatableEle.appendChild(rippleEle);
let ripple = this.ripples[Date.now()] = { ele: rippleEle };
+ ripple.outerRadius = outerRadius;
+ ripple.radiusDuration = radiusDuration;
// expand the circle from the users starting point
// start slow, and when they let up, then speed up the animation
ripple.expand = new Animation(rippleEle, {renderDelay: 0});
ripple.expand
.fromTo('scale', '0.001', '1')
- .duration(300)
- .playbackRate(0.35)
+ .duration(radiusDuration)
+ .playbackRate(RippleActivator.DOWN_PLAYBACK_RATE)
.onFinish(()=> {
// finished expanding
ripple.expand && ripple.expand.dispose();
@@ -60,16 +84,26 @@ export class RippleActivator extends Activator {
let ripple;
for (let rippleId in this.ripples) {
ripple = this.ripples[rippleId];
+ if(ripple.expand) {
+ let currentTime = ripple.expand.getCurrentTime();
+
+ // How much more time do we need to finish the radius animation?
+ // Math: (radius/second) * ((total_radius_time) - current_time)
+ let remainingTime = (ripple.outerRadius / ripple.radiusDuration) *
+ ((ripple.radiusDuration / RippleActivator.DOWN_PLAYBACK_RATE)- (currentTime));
+ ripple.expand.remainingTime = remainingTime;
+ }
if (!ripple.fade) {
// ripple has not been let up yet
// spped up the rate if the animation is still going
setTimeout(() => {
- ripple.expand && ripple.expand.playbackRate(1);
+ ripple.expand && ripple.expand.playbackRate(RippleActivator.EXPAND_OUT_PLAYBACK_RATE);
ripple.fade = new Animation(ripple.ele);
ripple.fade
.fadeOut()
- .duration(750)
+ .duration(ripple.epxand && ripple.expand.remaingTime || RippleActivator.OPACITY_OUT_DURATION)
+ .playbackRate(RippleActivator.OPACITY_OUT_PLAYBACK_RATE)
.onFinish(() => {
ripple.fade && ripple.fade.dispose();
ripple.fade = null;
diff --git a/ionic/ionic.ts b/ionic/ionic.ts
index 6ccc5c4203..c7fa97ad3f 100644
--- a/ionic/ionic.ts
+++ b/ionic/ionic.ts
@@ -24,3 +24,6 @@ export * from './transitions/ios-transition'
export * from './transitions/md-transition'
export * from './platform/plugins'
+
+export * from './translation/translate'
+export * from './translation/translate_pipe'
diff --git a/ionic/translation/translate.ts b/ionic/translation/translate.ts
new file mode 100644
index 0000000000..835433e57d
--- /dev/null
+++ b/ionic/translation/translate.ts
@@ -0,0 +1,41 @@
+import {Injectable} from 'angular2/angular2';
+
+@Injectable()
+export class Translate {
+ constructor() {
+ this._transMap = {};
+ }
+
+ translations(lang, map) {
+ this._transMap[lang] = map;
+ }
+
+ setLanguage(lang) {
+ this._language = lang;
+ }
+
+ getTranslations(lang) {
+ return this._transMap[lang];
+ }
+
+ translate(key, lang) {
+ // If the language isn't specified and we have no overridden one, return the string passed.
+ if(!lang && !this._language) {
+ return key;
+ }
+
+ let setLanguage = lang || this._language;
+
+ let map = this.getTranslations(setLanguage);
+
+ if(!map) {
+ console.warn('I18N: No translation for key', key, 'using language', setLanguage);
+ return '';
+ }
+ return this._getTranslation(map, key);
+ }
+
+ _getTranslation(map, key) {
+ return map && map[key] || '';
+ }
+}
diff --git a/ionic/translation/translate_pipe.ts b/ionic/translation/translate_pipe.ts
new file mode 100644
index 0000000000..34181b6240
--- /dev/null
+++ b/ionic/translation/translate_pipe.ts
@@ -0,0 +1,30 @@
+import {Injectable, Pipe, PipeTransform} from 'angular2/angular2';
+
+import {Translate} from './translate';
+
+/**
+ * The Translate pipe makes it easy to translate strings.
+ *
+ * @usage
+ * Translate using the current language or language set through Translate.setLanguage
+ * {{ 'Please enter your location' | translate }}
+ *
+ * Translate using a specific language
+ * {{ 'Please enter your location' | translate:"de" }}
+ */
+@Pipe({name: 'translate'})
+@Injectable()
+export class TranslatePipe implements PipeTransform {
+ constructor(translate: Translate) {
+ this.translate = translate;
+ }
+ transform(value, args) {
+ let lang;
+ if(args.length > 0) {
+ lang = args[0];
+ }
+ return this.translate.translate(value, lang);
+ }
+
+ supports(obj) { return true; }
+}