if (typeof window.components[name] === "undefined") window.components[name] = [];
for (let j = 0, jLen = elems.length; j < jLen; j++) {
let instance = new component(elems[j]);
+ if (typeof elems[j].components === 'undefined') elems[j].components = {};
+ elems[j].components[name] = instance;
window.components[name].push(instance);
}
}
\ No newline at end of file
this.textElem = elem.querySelector('span');
this.autohide = this.elem.hasAttribute('data-autohide');
window.Events.listen(this.type, text => {
- console.log('show', text);
this.show(text);
});
elem.addEventListener('click', this.hide.bind(this));
if (elem.hasAttribute('data-show')) this.show(this.textElem.textContent);
+
+ this.hideCleanup = this.hideCleanup.bind(this);
}
show(textToShow = '') {
+ this.elem.removeEventListener('transitionend', this.hideCleanup);
this.textElem.textContent = textToShow;
this.elem.style.display = 'block';
setTimeout(() => {
hide() {
this.elem.classList.remove('showing');
+ this.elem.addEventListener('transitionend', this.hideCleanup);
+ }
- function transitionEnd() {
- this.elem.style.display = 'none';
- this.elem.removeEventListener('transitionend', transitionEnd);
- }
-
- this.elem.addEventListener('transitionend', transitionEnd.bind(this));
+ hideCleanup() {
+ this.elem.style.display = 'none';
+ this.elem.removeEventListener('transitionend', this.hideCleanup);
}
}
module.exports = function (ngApp, events) {
- ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService',
- function ($scope, $attrs, $http, $timeout, imageManagerService) {
-
- $scope.images = [];
- $scope.imageType = $attrs.imageType;
- $scope.selectedImage = false;
- $scope.dependantPages = false;
- $scope.showing = false;
- $scope.hasMore = false;
- $scope.imageUpdateSuccess = false;
- $scope.imageDeleteSuccess = false;
- $scope.uploadedTo = $attrs.uploadedTo;
- $scope.view = 'all';
-
- $scope.searching = false;
- $scope.searchTerm = '';
-
- let page = 0;
- let previousClickTime = 0;
- let previousClickImage = 0;
- let dataLoaded = false;
- let callback = false;
-
- let preSearchImages = [];
- let preSearchHasMore = false;
-
- /**
- * Used by dropzone to get the endpoint to upload to.
- * @returns {string}
- */
- $scope.getUploadUrl = function () {
- return window.baseUrl('/images/' + $scope.imageType + '/upload');
- };
-
- /**
- * Cancel the current search operation.
- */
- function cancelSearch() {
- $scope.searching = false;
- $scope.searchTerm = '';
- $scope.images = preSearchImages;
- $scope.hasMore = preSearchHasMore;
- }
- $scope.cancelSearch = cancelSearch;
-
-
- /**
- * Runs on image upload, Adds an image to local list of images
- * and shows a success message to the user.
- * @param file
- * @param data
- */
- $scope.uploadSuccess = function (file, data) {
- $scope.$apply(() => {
- $scope.images.unshift(data);
- });
- events.emit('success', trans('components.image_upload_success'));
- };
-
- /**
- * Runs the callback and hides the image manager.
- * @param returnData
- */
- function callbackAndHide(returnData) {
- if (callback) callback(returnData);
- $scope.hide();
- }
-
- /**
- * Image select action. Checks if a double-click was fired.
- * @param image
- */
- $scope.imageSelect = function (image) {
- let dblClickTime = 300;
- let currentTime = Date.now();
- let timeDiff = currentTime - previousClickTime;
-
- if (timeDiff < dblClickTime && image.id === previousClickImage) {
- // If double click
- callbackAndHide(image);
- } else {
- // If single
- $scope.selectedImage = image;
- $scope.dependantPages = false;
- }
- previousClickTime = currentTime;
- previousClickImage = image.id;
- };
-
- /**
- * Action that runs when the 'Select image' button is clicked.
- * Runs the callback and hides the image manager.
- */
- $scope.selectButtonClick = function () {
- callbackAndHide($scope.selectedImage);
- };
-
- /**
- * Show the image manager.
- * Takes a callback to execute later on.
- * @param doneCallback
- */
- function show(doneCallback) {
- callback = doneCallback;
- $scope.showing = true;
- $('#image-manager').find('[overlay]').css('display', 'flex').hide().fadeIn(240);
- // Get initial images if they have not yet been loaded in.
- if (!dataLoaded) {
- fetchData();
- dataLoaded = true;
- }
- }
-
- // Connects up the image manger so it can be used externally
- // such as from TinyMCE.
- imageManagerService.show = show;
- imageManagerService.showExternal = function (doneCallback) {
- $scope.$apply(() => {
- show(doneCallback);
- });
- };
- window.ImageManager = imageManagerService;
-
- /**
- * Hide the image manager
- */
- $scope.hide = function () {
- $scope.showing = false;
- $('#image-manager').find('[overlay]').fadeOut(240);
- };
-
- let baseUrl = window.baseUrl('/images/' + $scope.imageType + '/all/');
-
- /**
- * Fetch the list image data from the server.
- */
- function fetchData() {
- let url = baseUrl + page + '?';
- let components = {};
- if ($scope.uploadedTo) components['page_id'] = $scope.uploadedTo;
- if ($scope.searching) components['term'] = $scope.searchTerm;
-
-
- url += Object.keys(components).map((key) => {
- return key + '=' + encodeURIComponent(components[key]);
- }).join('&');
-
- $http.get(url).then((response) => {
- $scope.images = $scope.images.concat(response.data.images);
- $scope.hasMore = response.data.hasMore;
- page++;
- });
- }
- $scope.fetchData = fetchData;
-
- /**
- * Start a search operation
- */
- $scope.searchImages = function() {
-
- if ($scope.searchTerm === '') {
- cancelSearch();
- return;
- }
-
- if (!$scope.searching) {
- preSearchImages = $scope.images;
- preSearchHasMore = $scope.hasMore;
- }
-
- $scope.searching = true;
- $scope.images = [];
- $scope.hasMore = false;
- page = 0;
- baseUrl = window.baseUrl('/images/' + $scope.imageType + '/search/');
- fetchData();
- };
-
- /**
- * Set the current image listing view.
- * @param viewName
- */
- $scope.setView = function(viewName) {
- cancelSearch();
- $scope.images = [];
- $scope.hasMore = false;
- page = 0;
- $scope.view = viewName;
- baseUrl = window.baseUrl('/images/' + $scope.imageType + '/' + viewName + '/');
- fetchData();
- };
-
- /**
- * Save the details of an image.
- * @param event
- */
- $scope.saveImageDetails = function (event) {
- event.preventDefault();
- let url = window.baseUrl('/images/update/' + $scope.selectedImage.id);
- $http.put(url, this.selectedImage).then(response => {
- events.emit('success', trans('components.image_update_success'));
- }, (response) => {
- if (response.status === 422) {
- let errors = response.data;
- let message = '';
- Object.keys(errors).forEach((key) => {
- message += errors[key].join('\n');
- });
- events.emit('error', message);
- } else if (response.status === 403) {
- events.emit('error', response.data.error);
- }
- });
- };
-
- /**
- * Delete an image from system and notify of success.
- * Checks if it should force delete when an image
- * has dependant pages.
- * @param event
- */
- $scope.deleteImage = function (event) {
- event.preventDefault();
- let force = $scope.dependantPages !== false;
- let url = window.baseUrl('/images/' + $scope.selectedImage.id);
- if (force) url += '?force=true';
- $http.delete(url).then((response) => {
- $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
- $scope.selectedImage = false;
- events.emit('success', trans('components.image_delete_success'));
- }, (response) => {
- // Pages failure
- if (response.status === 400) {
- $scope.dependantPages = response.data;
- } else if (response.status === 403) {
- events.emit('error', response.data.error);
- }
- });
- };
-
- /**
- * Simple date creator used to properly format dates.
- * @param stringDate
- * @returns {Date}
- */
- $scope.getDate = function (stringDate) {
- return new Date(stringDate);
- };
-
- }]);
ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce',
function ($scope, $http, $attrs, $interval, $timeout, $sce) {
// Show the image manager and handle image insertion
function showImageManager() {
let cursorPos = cm.getCursor('from');
- window.ImageManager.showExternal(image => {
+ window.ImageManager.show(image => {
let selectedText = cm.getSelection();
let newText = "";
cm.focus();
require("./components");
// Load in angular specific items
-const Services = require('./services');
const Directives = require('./directives');
const Controllers = require('./controllers');
-Services(ngApp, window.Events);
Directives(ngApp, window.Events);
Controllers(ngApp, window.Events);
if (type === 'image') {
// Show image manager
- window.ImageManager.showExternal(function (image) {
+ window.ImageManager.show(function (image) {
// Set popover link input to image url then fire change event
// to ensure the new value sticks
icon: 'image',
tooltip: 'Insert an image',
onclick: function () {
- window.ImageManager.showExternal(function (image) {
+ window.ImageManager.show(function (image) {
let html = `<a href="${image.url}" target="_blank">`;
html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
html += '</a>';
+++ /dev/null
-"use strict";
-
-module.exports = function(ngApp, events) {
-
- ngApp.factory('imageManagerService', function() {
- return {
- show: false,
- showExternal: false
- };
- });
-
-};
\ No newline at end of file
--- /dev/null
+const DropZone = require("dropzone");
+
+const template = `
+ <div class="dropzone-container">
+ <div class="dz-message">{{placeholder}}</div>
+ </div>
+`;
+
+const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
+
+// TODO - Remove jQuery usage
+function mounted() {
+ let container = this.$el;
+ let _this = this;
+ new DropZone(container, {
+ url: function() {
+ return _this.uploadUrl;
+ },
+ init: function () {
+ let dz = this;
+
+ dz.on('sending', function (file, xhr, data) {
+ let token = window.document.querySelector('meta[name=token]').getAttribute('content');
+ data.append('_token', token);
+ let uploadedTo = typeof _this.uploadedTo === 'undefined' ? 0 : _this.uploadedTo;
+ data.append('uploaded_to', uploadedTo);
+ });
+
+ dz.on('success', function (file, data) {
+ _this.$emit('success', {file, data});
+ $(file.previewElement).fadeOut(400, function () {
+ dz.removeFile(file);
+ });
+ });
+
+ dz.on('error', function (file, errorMessage, xhr) {
+ _this.$emit('error', {file, errorMessage, xhr});
+ console.log(errorMessage);
+ console.log(xhr);
+ function setMessage(message) {
+ $(file.previewElement).find('[data-dz-errormessage]').text(message);
+ }
+
+ if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
+ if (errorMessage.file) setMessage(errorMessage.file[0]);
+ });
+ }
+ });
+}
+
+function data() {
+ return {}
+}
+
+module.exports = {
+ template,
+ props,
+ mounted,
+ data,
+};
\ No newline at end of file
--- /dev/null
+const dropzone = require('./components/dropzone');
+
+let page = 0;
+let previousClickTime = 0;
+let previousClickImage = 0;
+let dataLoaded = false;
+let callback = false;
+let baseUrl = '';
+
+let preSearchImages = [];
+let preSearchHasMore = false;
+
+const data = {
+ images: [],
+
+ imageType: false,
+ uploadedTo: false,
+
+ selectedImage: false,
+ dependantPages: false,
+ showing: false,
+ view: 'all',
+ hasMore: false,
+ searching: false,
+ searchTerm: '',
+
+ imageUpdateSuccess: false,
+ imageDeleteSuccess: false,
+};
+
+const methods = {
+
+ show(providedCallback) {
+ callback = providedCallback;
+ this.showing = true;
+ this.$el.children[0].components.overlay.show();
+
+ // Get initial images if they have not yet been loaded in.
+ if (dataLoaded) return;
+ this.fetchData();
+ dataLoaded = true;
+ },
+
+ hide() {
+ this.showing = false;
+ this.$el.children[0].components.overlay.hide();
+ },
+
+ fetchData() {
+ let url = baseUrl + page;
+ let query = {};
+ if (this.uploadedTo !== false) query.page_id = this.uploadedTo;
+ if (this.searching) query.term = this.searchTerm;
+
+ this.$http.get(url, {params: query}).then(response => {
+ this.images = this.images.concat(response.data.images);
+ this.hasMore = response.data.hasMore;
+ page++;
+ });
+ },
+
+ setView(viewName) {
+ this.cancelSearch();
+ this.images = [];
+ this.hasMore = false;
+ page = 0;
+ this.view = viewName;
+ baseUrl = window.baseUrl(`/images/${this.imageType}/${viewName}/`);
+ this.fetchData();
+ },
+
+ searchImages() {
+ if (this.searchTerm === '') return this.cancelSearch();
+
+ // Cache current settings for later
+ if (!this.searching) {
+ preSearchImages = this.images;
+ preSearchHasMore = this.hasMore;
+ }
+
+ this.searching = true;
+ this.images = [];
+ this.hasMore = false;
+ page = 0;
+ baseUrl = window.baseUrl(`/images/${this.imageType}/search/`);
+ this.fetchData();
+ },
+
+ cancelSearch() {
+ this.searching = false;
+ this.searchTerm = '';
+ this.images = preSearchImages;
+ this.hasMore = preSearchHasMore;
+ },
+
+ imageSelect(image) {
+ let dblClickTime = 300;
+ let currentTime = Date.now();
+ let timeDiff = currentTime - previousClickTime;
+ let isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
+
+ if (isDblClick) {
+ this.callbackAndHide(image);
+ } else {
+ this.selectedImage = image;
+ this.dependantPages = false;
+ }
+
+ previousClickTime = currentTime;
+ previousClickImage = image.id;
+ },
+
+ callbackAndHide(imageResult) {
+ if (callback) callback(imageResult);
+ this.hide();
+ },
+
+ saveImageDetails() {
+ let url = window.baseUrl(`/images/update/${this.selectedImage.id}`);
+ this.$http.put(url, this.selectedImage).then(response => {
+ this.$events.emit('success', trans('components.image_update_success'));
+ }).catch(error => {
+ if (error.response.status === 422) {
+ let errors = error.response.data;
+ let message = '';
+ Object.keys(errors).forEach((key) => {
+ message += errors[key].join('\n');
+ });
+ this.$events.emit('error', message);
+ } else if (error.response.status === 403) {
+ this.$events.emit('error', error.response.data.error);
+ }
+ });
+ },
+
+ deleteImage() {
+ let force = this.dependantPages !== false;
+ let url = window.baseUrl('/images/' + this.selectedImage.id);
+ if (force) url += '?force=true';
+ this.$http.delete(url).then(response => {
+ this.images.splice(this.images.indexOf(this.selectedImage), 1);
+ this.selectedImage = false;
+ this.$events.emit('success', trans('components.image_delete_success'));
+ }).catch(error=> {
+ if (error.response.status === 400) {
+ this.dependantPages = error.response.data;
+ } else if (error.response.status === 403) {
+ this.$events.emit('error', error.response.data.error);
+ }
+ });
+ },
+
+ getDate(stringDate) {
+ return new Date(stringDate);
+ },
+
+ uploadSuccess(event) {
+ this.images.unshift(event.data);
+ this.$events.emit('success', trans('components.image_upload_success'));
+ },
+};
+
+const computed = {
+ uploadUrl() {
+ return window.baseUrl(`/images/${this.imageType}/upload`);
+ }
+};
+
+function mounted() {
+ window.ImageManager = this;
+ this.imageType = this.$el.getAttribute('image-type');
+ this.uploadedTo = this.$el.getAttribute('uploaded-to');
+ baseUrl = window.baseUrl('/images/' + this.imageType + '/all/')
+}
+
+module.exports = {
+ mounted,
+ methods,
+ data,
+ computed,
+ components: {dropzone},
+};
\ No newline at end of file
let vueMapping = {
'search-system': require('./search'),
'entity-dashboard': require('./entity-search'),
- 'code-editor': require('./code-editor')
+ 'code-editor': require('./code-editor'),
+ 'image-manager': require('./image-manager'),
};
window.vues = {};
-<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}">
- <div overlay ng-cloak ng-click="hide()">
- <div class="popup-body" ng-click="$event.stopPropagation()">
+<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
+ <div overlay v-cloak>
+ <div class="popup-body" @click.stop="">
<div class="popup-header primary-background">
<div class="popup-title">{{ trans('components.image_select') }}</div>
<div class="flex-fill image-manager-body">
<div class="image-manager-content">
- <div ng-if="imageType === 'gallery'" class="container">
+ <div v-if="imageType === 'gallery'" class="container">
<div class="image-manager-header row faded-small nav-tabs">
- <div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
- <div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
- <div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
+ <div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: (view=='all')}" @click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
+ <div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" :class="{selected: (view=='book')}" @click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
+ <div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (view=='page')}" @click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
</div>
</div>
- <div ng-show="view === 'all'" >
- <form ng-submit="searchImages()" class="contained-search-box">
- <input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm">
- <button ng-class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
- <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
+ <div v-show="view === 'all'" >
+ <form @submit="searchImages" class="contained-search-box">
+ <input placeholder="{{ trans('components.image_search_hint') }}" v-model="searchTerm">
+ <button :class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" @click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
+ <button title="{{ trans('common.search') }}" class="text-button"><i class="zmdi zmdi-search"></i></button>
</form>
</div>
<div class="image-manager-list">
- <div ng-repeat="image in images">
- <div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}"
- ng-class="{selected: (image==selectedImage)}" ng-click="imageSelect(image)">
- <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
+ <div v-if="images.length > 0" v-for="(image, idx) in images">
+ <div class="image anim fadeIn" :style="{animationDelay: (idx > 26) ? '160ms' : ((idx * 25) + 'ms')}"
+ :class="{selected: (image==selectedImage)}" @click="imageSelect(image)">
+ <img :src="image.thumbs.gallery" :alt="image.title" :title="image.name">
<div class="image-meta">
- <span class="name" ng-bind="image.name"></span>
+ <span class="name" v-text="image.name"></span>
<span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
</div>
</div>
</div>
- <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div>
+ <div class="load-more" v-show="hasMore" @click="fetchData">{{ trans('components.image_load_more') }}</div>
</div>
</div>
<div class="image-manager-sidebar">
<div class="inner">
- <div class="image-manager-details anim fadeIn" ng-show="selectedImage">
+ <div class="image-manager-details anim fadeIn" v-if="selectedImage">
- <form ng-submit="saveImageDetails($event)">
+ <form @submit.prevent="saveImageDetails">
<div>
- <a ng-href="@{{selectedImage.url}}" target="_blank" style="display: block;">
- <img ng-src="@{{selectedImage.thumbs.gallery}}" ng-attr-alt="@{{selectedImage.title}}" ng-attr-title="@{{selectedImage.name}}">
+ <a :href="selectedImage.url" target="_blank" style="display: block;">
+ <img :src="selectedImage.thumbs.gallery" :alt="selectedImage.title"
+ :title="selectedImage.name">
</a>
</div>
<div class="form-group">
<label for="name">{{ trans('components.image_image_name') }}</label>
- <input type="text" id="name" name="name" ng-model="selectedImage.name">
+ <input id="name" name="name" v-model="selectedImage.name">
</div>
</form>
- <div ng-show="dependantPages">
+ <div v-show="dependantPages">
<p class="text-neg text-small">
{{ trans('components.image_delete_confirm') }}
</p>
<ul class="text-neg">
- <li ng-repeat="page in dependantPages">
- <a ng-href="@{{ page.url }}" target="_blank" class="text-neg" ng-bind="page.name"></a>
+ <li v-for="page in dependantPages">
+ <a :href="page.url" target="_blank" class="text-neg" v-text="page.name"></a>
</li>
</ul>
</div>
<div class="clearfix">
- <form class="float left" ng-submit="deleteImage($event)">
+ <form class="float left" @submit.prevent="deleteImage">
<button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
</form>
- <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
+ <button class="button pos anim fadeIn float right" v-show="selectedImage" @click="callbackAndHide(selectedImage)">
<i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
</button>
</div>
</div>
- <drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
-
+ <dropzone placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
</div>
</div>
-
-
</div>
</div>