2015-02-13 07:22:32 +03:00
|
|
|
import Ember from 'ember';
|
2015-06-13 17:34:09 +03:00
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
const {TextField, computed, run} = Ember;
|
2015-09-16 20:02:06 +03:00
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
let joinUrlParts = function (url, path) {
|
2015-01-18 03:16:54 +03:00
|
|
|
if (path[0] !== '/' && url.slice(-1) !== '/') {
|
2015-10-28 14:36:45 +03:00
|
|
|
path = `/${path}`;
|
2015-01-18 03:16:54 +03:00
|
|
|
} else if (path[0] === '/' && url.slice(-1) === '/') {
|
|
|
|
path = path.slice(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return url + path;
|
2015-09-16 20:02:06 +03:00
|
|
|
};
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
let isRelative = function (url) {
|
2015-09-16 20:02:06 +03:00
|
|
|
// "protocol://", "//example.com", "scheme:", "#anchor", & invalid paths
|
|
|
|
// should all be treated as absolute
|
|
|
|
return !url.match(/\s/) && !validator.isURL(url) && !url.match(/^(\/\/|#|[a-zA-Z0-9\-]+:)/);
|
|
|
|
};
|
2015-01-18 03:16:54 +03:00
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
export default TextField.extend({
|
2015-09-16 20:02:06 +03:00
|
|
|
classNames: 'gh-input',
|
2015-01-18 03:16:54 +03:00
|
|
|
classNameBindings: ['fakePlaceholder'],
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
isBaseUrl: computed('baseUrl', 'value', function () {
|
|
|
|
return this.get('baseUrl') === this.get('value');
|
|
|
|
}),
|
|
|
|
|
|
|
|
fakePlaceholder: computed('isBaseUrl', 'hasFocus', function () {
|
|
|
|
return this.get('isBaseUrl') && this.get('last') && !this.get('hasFocus');
|
|
|
|
}),
|
|
|
|
|
|
|
|
didReceiveAttrs() {
|
2015-11-15 14:06:49 +03:00
|
|
|
this._super(...arguments);
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
let baseUrl = this.get('baseUrl');
|
|
|
|
let url = this.get('url');
|
2015-06-13 17:34:09 +03:00
|
|
|
|
|
|
|
// if we have a relative url, create the absolute url to be displayed in the input
|
2015-09-16 20:02:06 +03:00
|
|
|
if (isRelative(url)) {
|
2015-06-13 17:34:09 +03:00
|
|
|
url = joinUrlParts(baseUrl, url);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.set('value', url);
|
|
|
|
},
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
focusIn(event) {
|
2015-01-18 03:16:54 +03:00
|
|
|
this.set('hasFocus', true);
|
|
|
|
|
|
|
|
if (this.get('isBaseUrl')) {
|
|
|
|
// position the cursor at the end of the input
|
2015-10-28 14:36:45 +03:00
|
|
|
run.next(function (el) {
|
|
|
|
let {length} = el.value;
|
2015-01-18 03:16:54 +03:00
|
|
|
|
|
|
|
el.setSelectionRange(length, length);
|
|
|
|
}, event.target);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
keyDown(event) {
|
2015-01-18 03:16:54 +03:00
|
|
|
// delete the "placeholder" value all at once
|
|
|
|
if (this.get('isBaseUrl') && (event.keyCode === 8 || event.keyCode === 46)) {
|
|
|
|
this.set('value', '');
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
2015-09-16 20:02:06 +03:00
|
|
|
|
|
|
|
// CMD-S
|
|
|
|
if (event.keyCode === 83 && event.metaKey) {
|
|
|
|
this.notifyUrlChanged();
|
|
|
|
}
|
2015-01-18 03:16:54 +03:00
|
|
|
},
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
keyPress(event) {
|
2015-01-28 02:14:45 +03:00
|
|
|
// enter key
|
|
|
|
if (event.keyCode === 13) {
|
|
|
|
event.preventDefault();
|
|
|
|
this.notifyUrlChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
focusOut() {
|
2015-01-18 03:16:54 +03:00
|
|
|
this.set('hasFocus', false);
|
2015-01-28 02:14:45 +03:00
|
|
|
|
|
|
|
this.notifyUrlChanged();
|
|
|
|
},
|
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
notifyUrlChanged() {
|
2015-12-01 18:23:21 +03:00
|
|
|
let url = this.get('value').trim();
|
2015-10-28 14:36:45 +03:00
|
|
|
let urlParts = document.createElement('a');
|
|
|
|
let baseUrl = this.get('baseUrl');
|
|
|
|
let baseUrlParts = document.createElement('a');
|
2015-01-18 03:16:54 +03:00
|
|
|
|
2015-10-28 14:36:45 +03:00
|
|
|
// ensure value property is trimmed
|
2015-12-01 18:23:21 +03:00
|
|
|
this.set('value', url);
|
2015-09-16 20:02:06 +03:00
|
|
|
|
|
|
|
// leverage the browser's native URI parsing
|
|
|
|
urlParts.href = url;
|
|
|
|
baseUrlParts.href = baseUrl;
|
|
|
|
|
|
|
|
// if we have an email address, add the mailto:
|
|
|
|
if (validator.isEmail(url)) {
|
|
|
|
url = `mailto:${url}`;
|
|
|
|
this.set('value', url);
|
|
|
|
}
|
2015-01-18 03:16:54 +03:00
|
|
|
|
2015-03-08 21:27:00 +03:00
|
|
|
// if we have a relative url, create the absolute url to be displayed in the input
|
2015-09-16 20:02:06 +03:00
|
|
|
if (isRelative(url)) {
|
|
|
|
url = joinUrlParts(baseUrl, url);
|
|
|
|
this.set('value', url);
|
|
|
|
}
|
|
|
|
|
2015-12-01 18:23:21 +03:00
|
|
|
// get our baseUrl relativity checks in order
|
|
|
|
let isOnSameHost = urlParts.host === baseUrlParts.host;
|
|
|
|
let isAnchorLink = url.match(/^#/);
|
|
|
|
let isRelativeToBasePath = urlParts.pathname.indexOf(baseUrlParts.pathname) === 0;
|
|
|
|
|
|
|
|
// if our pathname is only missing a trailing / mark it as relative
|
|
|
|
if (`${urlParts.pathname}/` === baseUrlParts.pathname) {
|
|
|
|
isRelativeToBasePath = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if relative to baseUrl, remove the base url before sending to action
|
|
|
|
if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) {
|
2015-09-16 20:02:06 +03:00
|
|
|
url = url.replace(/^[a-zA-Z0-9\-]+:/, '');
|
|
|
|
url = url.replace(/^\/\//, '');
|
|
|
|
url = url.replace(baseUrlParts.host, '');
|
|
|
|
url = url.replace(baseUrlParts.pathname, '');
|
2015-12-01 18:23:21 +03:00
|
|
|
|
|
|
|
// handle case where url path is same as baseUrl path but missing trailing slash
|
|
|
|
if (urlParts.pathname.slice(-1) !== '/') {
|
|
|
|
url = url.replace(baseUrlParts.pathname.slice(0, -1), '');
|
|
|
|
}
|
|
|
|
|
2015-09-16 20:02:06 +03:00
|
|
|
if (!url.match(/^\//)) {
|
2015-10-28 14:36:45 +03:00
|
|
|
url = `/${url}`;
|
2015-09-16 20:02:06 +03:00
|
|
|
}
|
2015-01-18 03:16:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
this.sendAction('change', url);
|
|
|
|
}
|
|
|
|
});
|