diff --git a/gulpfile.js b/gulpfile.js
index fdd99d4f..b067b565 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -825,4 +825,4 @@ gulp.task('default', function(done){
tasks.push('watch');
seq('build', tasks, done);
-});
+});
\ No newline at end of file
diff --git a/src/js/directives/pad.js b/src/js/directives/pad.js
index 8133d0f1..42ff45dc 100644
--- a/src/js/directives/pad.js
+++ b/src/js/directives/pad.js
@@ -7,23 +7,23 @@
* # Chat Ctrl
* Show Pad for a given project
*/
-
+let timer;
angular.module('Teem')
- .directive('pad', function() {
+ .directive('pad', function () {
return {
scope: true,
- link: function($scope, elem, attrs) {
+ link: function ($scope, elem, attrs) {
$scope.editingDefault = attrs.editingDefault;
},
controller: [
'SessionSvc', '$rootScope', '$scope', '$route', '$location',
- '$timeout', 'SharedState', 'needWidget', '$element',
- function(SessionSvc, $rootScope, $scope, $route, $location,
- $timeout, SharedState, needWidget, $element) {
+ '$timeout', 'SharedState', 'needWidget', '$element', 'linkPreview',
+ function (SessionSvc, $rootScope, $scope, $route, $location,
+ $timeout, SharedState, needWidget, $element, linkPreview) {
var buttons = ['text_fields', 'format_bold', 'format_italic', 'format_strikethrough',
- 'format_align_left', 'format_align_center', 'format_align_right',
- 'format_list_bulleted', 'format_list_numbered'];
+ 'format_align_left', 'format_align_center', 'format_align_right',
+ 'format_list_bulleted', 'format_list_numbered'];
var annotationMap = {
'text_fields': 'paragraph/header=h3',
@@ -39,6 +39,100 @@ angular.module('Teem')
var annotations = {};
+ function openLinkPopover(event, range) {
+ timer = $timeout(() => {
+ event.stopPropagation();
+ let div = document.createElement('div');
+ let btn = event.target;
+ //cannot inject the spinner HTML directly here
+ let inHTML = `
+
+
+ `;
+ linkPreview.getMetaData(btn.href)
+ .then((meta) => {
+ if (!meta) {
+ div.style.display = 'none';
+ return;
+ }
+ let urlImage = meta.image,
+ urlAuthor = meta.author,
+ urlTitle = meta.title,
+ urlDescription = meta.description;
+ let innerEle = document.createElement('div');
+ if (urlImage && urlDescription) {
+ innerEle.innerHTML = document.getElementById('urlImage-and-urlDescription').innerHTML;
+ innerEle.querySelector('#popoverLinkTitle').innerHTML = urlTitle;
+ innerEle.querySelector('#popoverLinkImage').src = urlImage;
+ innerEle.querySelector('#popoverLinkDescription').innerHTML = urlDescription;
+ innerEle.querySelector('#popoverLinkUrl').innerHTML = btn.href;
+ inHTML = innerEle.innerHTML;
+ }
+ else if (urlDescription && !urlImage) {
+ div.style.height = '110px';
+ innerEle.innerHTML = document.getElementById('description-and-no-image').innerHTML;
+ innerEle.querySelector('#popoverLinkTitle').innerHTML = urlTitle;
+ innerEle.querySelector('#popoverLinkDescription').innerHTML = urlDescription;
+ innerEle.querySelector('#popoverLinkUrl').innerHTML = btn.href;
+ inHTML = innerEle.innerHTML;
+ }
+ else {
+ if (!urlTitle) {
+ div.style.height = '110px';
+ innerEle.innerHTML = document.getElementById('no-title-in-url').innerHTML;
+ innerEle.querySelector('#popoverLinkDescription').innerHTML = urlDescription;
+ innerEle.querySelector('#popoverLinkUrl').innerHTML = btn.href;
+ inHTML = innerEle.innerHTML;
+ }
+ else {
+ div.style.height = '110px';
+ innerEle.innerHTML = document.getElementById('no-title-in-url').innerHTML;
+ innerEle.querySelector('#popoverLinkTitle').innerHTML = urlTitle;
+ innerEle.querySelector('#popoverLinkDescription').innerHTML = urlDescription;
+ innerEle.querySelector('#popoverLinkUrl').innerHTML = btn.href;
+ inHTML = innerEle.innerHTML;
+ }
+ }
+ div.innerHTML = inHTML;
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ let clientRect = range.node.nextSibling ?
+ range.node.nextSibling.getBoundingClientRect() :
+ range.node.parentElement.getBoundingClientRect();
+ div.innerHTML = inHTML;
+ //these styles need to be present here for positioning purposes
+ div.style.top = clientRect.top + 35 + 'px';
+ div.style.left = clientRect.left + 'px';
+ div.id = 'popover-container';
+ document.body.appendChild(div);
+ }, 700);
+ }
+
+ function closeLinkPopover(delay) {
+ if (timer) {
+ $timeout.cancel(timer);
+ timer = null;
+ $timeout(() => {
+ if (document.getElementById('popover-container')) {
+ document.body.removeChild(document.getElementById('popover-container'));
+ }
+ }, delay);
+ }
+ }
+
function imgWidget(parentElement, before, state) {
state = state || before;
@@ -48,13 +142,13 @@ angular.module('Teem')
// cannot use spinner template directly here
parentElement.innerHTML = `
- `;
+ `;
$scope.project.attachments[state].file.getUrl().then(url => {
parentElement.innerHTML = ``;
@@ -71,25 +165,33 @@ angular.module('Teem')
$scope.padAnnotations = {
'paragraph/header': {
- onAdd: function() {
+ onAdd: function () {
$scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
$timeout();
},
- onChange: function() {
+ onChange: function () {
$scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
$timeout();
},
- onRemove: function() {
+ onRemove: function () {
$scope.pad.outline = this.editor.getAnnotationSet('paragraph/header');
$timeout();
}
},
'link': {
- onEvent: function(range, event) {
+ onEvent: function (range, event) {
if (event.type === 'click') {
event.stopPropagation();
+ closeLinkPopover(0);
$scope.linkModal.open(range);
}
+ else if (event.type === 'mouseover') {
+ openLinkPopover(event, range);
+ console.log(range);
+ }
+ else if (event.type === 'mouseout') {
+ closeLinkPopover(500);
+ }
}
}
};
@@ -108,10 +210,10 @@ angular.module('Teem')
$timeout();
}
- $scope.padCreate = function(editor) {
+ $scope.padCreate = function (editor) {
$scope.linkModal = {
- add: function(event) {
+ add: function (event) {
event.stopPropagation();
let range = editor.getSelection();
if (range.text) {
@@ -119,7 +221,7 @@ angular.module('Teem')
}
$scope.linkModal.open(range);
},
- open: function(range) {
+ open: function (range) {
let annotation = editor.getAnnotationInRange(range, 'link');
$scope.linkModal.range = range;
@@ -135,17 +237,17 @@ angular.module('Teem')
$scope.linkModal.link = annotation ? annotation.value : '';
$scope.linkModal.show = true;
- let emptyInput = !range.text ? 'text': 'link';
+ let emptyInput = !range.text ? 'text' : 'link';
let autofocus = document.querySelector('#link-modal [ng-model="linkModal.' + emptyInput + '"]');
$timeout(() => autofocus && autofocus.focus());
},
- change: function() {
+ change: function () {
let range = editor.setText($scope.linkModal.range, $scope.linkModal.text);
editor.setAnnotationInRange(range, 'link', $scope.linkModal.link);
$scope.linkModal.show = false;
$scope.linkModal.edit = false;
},
- clear: function() {
+ clear: function () {
editor.clearAnnotationInRange($scope.linkModal.range, 'link');
$scope.linkModal.show = false;
$scope.linkModal.edit = false;
@@ -154,13 +256,13 @@ angular.module('Teem')
disableAllButtons();
- editor.onSelectionChanged(function(range) {
+ editor.onSelectionChanged(function (range) {
annotations = range.annotations;
updateAllButtons();
});
};
- $scope.padReady = function(editor) {
+ $scope.padReady = function (editor) {
// FIXME
// SwellRT editor is created with .wave-editor-off
// Should use .wave-editor-on when SwellRT editor callback is available
@@ -172,7 +274,7 @@ angular.module('Teem')
$scope.pad.outline = editor.getAnnotationSet('paragraph/header');
- $scope.annotate = function(btn) {
+ $scope.annotate = function (btn) {
let [key, val] = annotationMap[btn].split('=');
let currentVal = annotations[key];
if (currentVal === val) {
@@ -184,12 +286,12 @@ angular.module('Teem')
editorElement.focus();
};
- $scope.clearFormat = function() {
+ $scope.clearFormat = function () {
editor.clearAnnotation('style');
editorElement.focus();
};
- $scope.widget = function(type) {
+ $scope.widget = function (type) {
if (type === 'need') {
needWidget.add(editor, $scope);
}
@@ -235,9 +337,9 @@ angular.module('Teem')
};
- $scope.$watchCollection(function() {
+ $scope.$watchCollection(function () {
return SessionSvc.status;
- }, function(current) {
+ }, function (current) {
$scope.pad.saving = !current.sync;
});
@@ -248,7 +350,7 @@ angular.module('Teem')
});
};
- }],
+ }],
templateUrl: 'pad.html'
};
});
diff --git a/src/js/services/pad/linkPreview.js b/src/js/services/pad/linkPreview.js
new file mode 100644
index 00000000..1d7a0284
--- /dev/null
+++ b/src/js/services/pad/linkPreview.js
@@ -0,0 +1,37 @@
+(function() {
+ 'use strict';
+
+
+/**
+ * @module Teem
+ * @method linkPreview
+ * @param {String} url
+ * Returns the parsed meta data of the given link
+ */
+
+let linkPreviewFactory = angular.module('Teem');
+
+
+function linkPreview($http) {
+ const LINK_PREVIEW_SERVER_URL = 'http://localhost:9090/fetch';
+ function getMetaData(url){
+ //TODO: implement a check for the URL to be correct
+ if(!url){
+ return;
+ }
+ return $http.post(LINK_PREVIEW_SERVER_URL,{url})
+ .then((res) => {
+ return res.data;
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ }
+
+ return {
+ getMetaData
+ };
+}
+linkPreview.$inject = ['$http'];
+linkPreviewFactory.factory('linkPreview', linkPreview);
+})();
\ No newline at end of file
diff --git a/src/sass/colors.sass b/src/sass/colors.sass
index baa69613..583c916c 100644
--- a/src/sass/colors.sass
+++ b/src/sass/colors.sass
@@ -6,7 +6,8 @@ $teal: #00bfa0 /* Teem-green */
$blue: #4A87E0
$aubergine: #B2409E
$yellow: #D8AB32
-
+$black: #000
+$seashell: #f1f1f1
$twilight-blue: #0C5464
$pale-blue: #4E828F
diff --git a/src/sass/pad.sass b/src/sass/pad.sass
index 8d1817e2..8ff4d309 100644
--- a/src/sass/pad.sass
+++ b/src/sass/pad.sass
@@ -77,7 +77,7 @@
.project-desktop
.project-pad-editing
- margin-top: -61px
+ margin-top: -61px
.swellrt-placeholder:empty
color: $placeholder-color
font-family: "Lato", sans-serif, "Material Icons"
@@ -183,3 +183,70 @@
font-weight: bold
color: $pad-empty-tip-modal-description
margin-top: 20px
+
+#popover
+ max-width: 330px
+ max-height: 270px
+ margin: 0 5px
+ border-radius: 6px
+
+div
+ &.popover-link-image
+ max-width: 330px
+ max-height: 190px
+ margin: 5px auto
+
+ &.popover-link-description
+ width: 320px
+ height: auto
+ max-height: 50px
+ margin: 0 auto
+ overflow: hidden
+ word-wrap: break-word
+
+
+.popover-link-title
+ word-wrap: break-word
+ text-overflow: ellpsis
+ overflow: hidden
+ white-space: nowrap
+ font-weight: 600
+ margin-left: 5px
+
+.popover-link-address
+ color: $black
+ margin-left: 5px
+ overflow: hidden
+ word-wrap: break-word
+ text-overflow: ellipsis
+ white-space: nowrap
+
+#popover-container
+ width: 345px
+ height: 300px
+ position: absolute
+ border: 1px solid $grey
+ z-index: 3
+ background-color: darken(#fff, 5%)
+ padding-top: 5px
+ &:after
+ content: ""
+ position: absolute
+ bottom: -25px
+ left: 175px
+ border-style: solid
+ visibility: hidden
+ width: 0
+ z-index: 1
+
+ &:before
+ content: ""
+ position: absolute
+ top: -11px
+ left: -1px
+ border-style: solid
+ border-width: 0 10px 10px
+ border-color: $seashell transparent
+ display: block
+ width: 0
+ z-index: 0
diff --git a/src/templates/pad.html b/src/templates/pad.html
index db88fc30..42c50b35 100644
--- a/src/templates/pad.html
+++ b/src/templates/pad.html
@@ -1,6 +1,6 @@
+ ngf-drop="widget('img', $file)" ngf-fix-orientation="true">