X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/23e014cb25263aa20f21edd343704c88a3131fc6..refs/pull/232/head:/resources/assets/js/directives.js
diff --git a/resources/assets/js/directives.js b/resources/assets/js/directives.js
index 4e1bb0dfd..44d1a14e1 100644
--- a/resources/assets/js/directives.js
+++ b/resources/assets/js/directives.js
@@ -2,10 +2,6 @@
const DropZone = require('dropzone');
const markdown = require('marked');
-const toggleSwitchTemplate = require('./components/toggle-switch.html');
-const imagePickerTemplate = require('./components/image-picker.html');
-const dropZoneTemplate = require('./components/drop-zone.html');
-
module.exports = function (ngApp, events) {
/**
@@ -16,7 +12,12 @@ module.exports = function (ngApp, events) {
ngApp.directive('toggleSwitch', function () {
return {
restrict: 'A',
- template: toggleSwitchTemplate,
+ template: `
+
+ `,
scope: true,
link: function (scope, element, attrs) {
scope.name = attrs.name;
@@ -33,6 +34,59 @@ module.exports = function (ngApp, events) {
};
});
+ /**
+ * Common tab controls using simple jQuery functions.
+ */
+ ngApp.directive('tabContainer', function() {
+ return {
+ restrict: 'A',
+ link: function (scope, element, attrs) {
+ const $content = element.find('[tab-content]');
+ const $buttons = element.find('[tab-button]');
+
+ if (attrs.tabContainer) {
+ let initial = attrs.tabContainer;
+ $buttons.filter(`[tab-button="${initial}"]`).addClass('selected');
+ $content.hide().filter(`[tab-content="${initial}"]`).show();
+ } else {
+ $content.hide().first().show();
+ $buttons.first().addClass('selected');
+ }
+
+ $buttons.click(function() {
+ let clickedTab = $(this);
+ $buttons.removeClass('selected');
+ $content.hide();
+ let name = clickedTab.addClass('selected').attr('tab-button');
+ $content.filter(`[tab-content="${name}"]`).show();
+ });
+ }
+ };
+ });
+
+ /**
+ * Sub form component to allow inner-form sections to act like thier own forms.
+ */
+ ngApp.directive('subForm', function() {
+ return {
+ restrict: 'A',
+ link: function (scope, element, attrs) {
+ element.on('keypress', e => {
+ if (e.keyCode === 13) {
+ submitEvent(e);
+ }
+ });
+
+ element.find('button[type="submit"]').click(submitEvent);
+
+ function submitEvent(e) {
+ e.preventDefault()
+ if (attrs.subForm) scope.$eval(attrs.subForm);
+ }
+ }
+ };
+ });
+
/**
* Image Picker
@@ -41,7 +95,22 @@ module.exports = function (ngApp, events) {
ngApp.directive('imagePicker', ['$http', 'imageManagerService', function ($http, imageManagerService) {
return {
restrict: 'E',
- template: imagePickerTemplate,
+ template: `
+
+
+
![Image Preview]()
+
![Image Preview]()
+
+
+
+
+
+
|
+
+
+
+
+ `,
scope: {
name: '@',
resizeHeight: '@',
@@ -108,7 +177,11 @@ module.exports = function (ngApp, events) {
ngApp.directive('dropZone', [function () {
return {
restrict: 'E',
- template: dropZoneTemplate,
+ template: `
+
+
Drop files or click here to upload
+
+ `,
scope: {
uploadUrl: '@',
eventSuccess: '=',
@@ -116,6 +189,7 @@ module.exports = function (ngApp, events) {
uploadedTo: '@'
},
link: function (scope, element, attrs) {
+ if (attrs.placeholder) element[0].querySelector('.dz-message').textContent = attrs.placeholder;
var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
url: scope.uploadUrl,
init: function () {
@@ -370,7 +444,7 @@ module.exports = function (ngApp, events) {
window.ImageManager.showExternal(image => {
let caretPos = currentCaretPos;
let currentContent = input.val();
- let mdImageText = "";
+ let mdImageText = "";
input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
input.change();
});
@@ -396,6 +470,83 @@ module.exports = function (ngApp, events) {
}
insertEntityLink.click(showLinkSelector);
+ // Upload and insert image on paste
+ function editorPaste(e) {
+ e = e.originalEvent;
+ if (!e.clipboardData) return
+ var items = e.clipboardData.items;
+ if (!items) return;
+ for (var i = 0; i < items.length; i++) {
+ uploadImage(items[i].getAsFile());
+ }
+ }
+
+ input.on('paste', editorPaste);
+
+ // Handle image drop, Uploads images to BookStack.
+ function handleImageDrop(event) {
+ event.stopPropagation();
+ event.preventDefault();
+ let files = event.originalEvent.dataTransfer.files;
+ for (let i = 0; i < files.length; i++) {
+ uploadImage(files[i]);
+ }
+ }
+
+ input.on('drop', handleImageDrop);
+
+ // Handle image upload and add image into markdown content
+ function uploadImage(file) {
+ if (file.type.indexOf('image') !== 0) return;
+ var formData = new FormData();
+ var ext = 'png';
+ var xhr = new XMLHttpRequest();
+
+ if (file.name) {
+ var fileNameMatches = file.name.match(/\.(.+)$/);
+ if (fileNameMatches) {
+ ext = fileNameMatches[1];
+ }
+ }
+
+ // Insert image into markdown
+ let id = "image-" + Math.random().toString(16).slice(2);
+ let selectStart = input[0].selectionStart;
+ let selectEnd = input[0].selectionEnd;
+ let content = input[0].value;
+ let selectText = content.substring(selectStart, selectEnd);
+ let placeholderImage = window.baseUrl(`/loading.gif#upload${id}`);
+ let innerContent = ((selectEnd > selectStart) ? `![${selectText}]` : '![]') + `(${placeholderImage})`;
+ input[0].value = content.substring(0, selectStart) + innerContent + content.substring(selectEnd);
+
+ input.focus();
+ input[0].selectionStart = selectStart;
+ input[0].selectionEnd = selectStart;
+
+ let remoteFilename = "image-" + Date.now() + "." + ext;
+ formData.append('file', file, remoteFilename);
+ formData.append('_token', document.querySelector('meta[name="token"]').getAttribute('content'));
+
+ xhr.open('POST', window.baseUrl('/images/gallery/upload'));
+ xhr.onload = function () {
+ let selectStart = input[0].selectionStart;
+ if (xhr.status === 200 || xhr.status === 201) {
+ var result = JSON.parse(xhr.responseText);
+ input[0].value = input[0].value.replace(placeholderImage, result.thumbs.display);
+ input.change();
+ } else {
+ console.log('An error occurred uploading the image');
+ console.log(xhr.responseText);
+ input[0].value = input[0].value.replace(innerContent, '');
+ input.change();
+ }
+ input.focus();
+ input[0].selectionStart = selectStart;
+ input[0].selectionEnd = selectStart;
+ };
+ xhr.send(formData);
+ }
+
}
}
}]);
@@ -411,8 +562,8 @@ module.exports = function (ngApp, events) {
link: function (scope, elem, attrs) {
// Get common elements
- const $buttons = elem.find('[tab-button]');
- const $content = elem.find('[tab-content]');
+ const $buttons = elem.find('[toolbox-tab-button]');
+ const $content = elem.find('[toolbox-tab-content]');
const $toggle = elem.find('[toolbox-toggle]');
// Handle toolbox toggle click
@@ -424,17 +575,17 @@ module.exports = function (ngApp, events) {
function setActive(tabName, openToolbox) {
$buttons.removeClass('active');
$content.hide();
- $buttons.filter(`[tab-button="${tabName}"]`).addClass('active');
- $content.filter(`[tab-content="${tabName}"]`).show();
+ $buttons.filter(`[toolbox-tab-button="${tabName}"]`).addClass('active');
+ $content.filter(`[toolbox-tab-content="${tabName}"]`).show();
if (openToolbox) elem.addClass('open');
}
// Set the first tab content active on load
- setActive($content.first().attr('tab-content'), false);
+ setActive($content.first().attr('toolbox-tab-content'), false);
// Handle tab button click
$buttons.click(function (e) {
- let name = $(this).attr('tab-button');
+ let name = $(this).attr('toolbox-tab-button');
setActive(name, true);
});
}
@@ -472,7 +623,7 @@ module.exports = function (ngApp, events) {
let val = $input.val();
let url = $input.attr('autosuggest');
let type = $input.attr('autosuggest-type');
-
+
// Add name param to request if for a value
if (type.toLowerCase() === 'value') {
let $nameInput = $input.closest('tr').find('[autosuggest-type="name"]').first();
@@ -773,17 +924,3 @@ module.exports = function (ngApp, events) {
};
}]);
};
-
-
-
-
-
-
-
-
-
-
-
-
-
-