]> BookStack Code Mirror - bookstack/blobdiff - resources/assets/js/directives.js
Page Attachments - Improved UI, Now initially complete
[bookstack] / resources / assets / js / directives.js
index 4e1bb0dfd368737007c89fc5b6cce34dd92232ab..fa6c2c3be03f26a540bf4081cc312d81b153ee13 100644 (file)
@@ -33,6 +33,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
@@ -116,6 +169,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 +424,7 @@ module.exports = function (ngApp, events) {
                     window.ImageManager.showExternal(image => {
                         let caretPos = currentCaretPos;
                         let currentContent = input.val();
-                        let mdImageText = "![" + image.name + "](" + image.url + ")";
+                        let mdImageText = "![" + image.name + "](" + image.thumbs.display + ")";
                         input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
                         input.change();
                     });
@@ -396,6 +450,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 +542,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 +555,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);
                 });
             }