elixir(function(mix) {
mix.sass('styles.scss')
.sass('print-styles.scss')
- .browserify(['jquery-extensions.js', 'global.js'], 'public/js/common.js')
+ .browserify('global.js', 'public/js/common.js')
.queryVersion(['css/styles.css', 'css/print-styles.css', 'js/common.js']);
});
{
"private": true,
"devDependencies": {
- "gulp": "^3.9.0",
- "insert-css": "^0.2.0"
+ "gulp": "^3.9.0"
},
"dependencies": {
"angular": "^1.5.0-rc.0",
+ "angular-animate": "^1.5.0-rc.0",
"angular-resource": "^1.5.0-rc.0",
+ "angular-sanitize": "^1.5.0-rc.0",
"babel-runtime": "^5.8.29",
"bootstrap-sass": "^3.0.0",
"dropzone": "^4.0.1",
"laravel-elixir": "^3.4.0",
- "vue": "^1.0.13",
- "vue-hot-reload-api": "^1.2.1",
- "vue-resource": "^0.5.1",
- "vueify": "^5.0.1",
- "vueify-insert-css": "^1.0.0",
"zeroclipboard": "^2.2.0"
}
}
--- /dev/null
+<div class="dropzone-container">
+ <div class="dz-message">Drop files or click here to upload</div>
+</div>
\ No newline at end of file
+++ /dev/null
-<template>
- <div id="image-manager">
- <div class="overlay" v-el:overlay @click="overlayClick">
- <div class="image-manager-body">
- <div class="image-manager-content">
- <div class="image-manager-list">
- <div v-for="image in images">
- <img class="anim fadeIn"
- :class="{selected: (image==selectedImage)}"
- :src="image.thumbs.gallery" :alt="image.title" :title="image.name"
- @click="imageClick(image)"
- :style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
- </div>
- <div class="load-more" v-show="hasMore" @click="fetchData">Load More</div>
- </div>
- </div>
- <button class="neg button image-manager-close" @click="hide">x</button>
- <div class="image-manager-sidebar">
- <h2 v-el:image-title>Images</h2>
- <hr class="even">
- <div class="dropzone-container" v-el:drop-zone>
- <div class="dz-message">Drop files or click here to upload</div>
- </div>
- <div class="image-manager-details anim fadeIn" v-show="selectedImage">
- <hr class="even">
- <form @submit="saveImageDetails" v-el:image-form>
- <div class="form-group">
- <label for="name">Image Name</label>
- <input type="text" id="name" name="name" v-model="selectedImage.name">
- </div>
- </form>
- <hr class="even">
- <div v-show="dependantPages">
- <p class="text-neg text-small">
- This image is used in the pages below, Click delete again to confirm you want to delete
- this image.
- </p>
- <ul class="text-neg">
- <li v-for="page in dependantPages">
- <a :href="page.url" target="_blank" class="text-neg">{{ page.name }}</a>
- </li>
- </ul>
- </div>
-
- <form @submit="deleteImage" v-el:image-delete-form>
- <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
- </form>
- </div>
- <div class="image-manager-bottom">
- <button class="button pos anim fadeIn" v-show="selectedImage" @click="selectButtonClick"><i
- class="zmdi zmdi-square-right"></i>Select Image
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
-</template>
-
-<script>
-
- var Dropzone = require('dropzone');
-
- module.exports = {
- data: function () {
- return {
- images: [],
- hasMore: false,
- page: 0,
- cClickTime: 0,
- selectedImage: false,
- dependantPages: false,
- deleteForm: {},
- token: document.querySelector('meta[name=token]').getAttribute('content'),
- dataLoaded: false
- }
- },
-
- props: {
- imageType: {
- type: String,
- required: true
- }
- },
-
- created: function () {
- window.ImageManager = this;
- },
-
- ready: function () {
- // Create dropzone
- this.setupDropZone();
- },
-
- methods: {
- fetchData: function () {
- var url = '/images/' + this.imageType + '/all/' + this.page;
- this.$http.get(url).then((response) => {
- this.images = this.images.concat(response.data.images);
- this.hasMore = response.data.hasMore;
- this.page++;
- });
- },
-
- setupDropZone: function () {
- var _this = this;
- var dropZone = new Dropzone(_this.$els.dropZone, {
- url: '/images/' + _this.imageType + '/upload',
- init: function () {
- var dz = this;
- dz.on("sending", function (file, xhr, data) {
- data.append("_token", _this.token);
- });
- dz.on("success", function (file, data) {
- _this.images.unshift(data);
- $(file.previewElement).fadeOut(400, function () {
- dz.removeFile(file);
- });
- });
- dz.on('error', function (file, errorMessage, xhr) {
- if (errorMessage.file) {
- $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
- }
- console.log(errorMessage);
- });
- }
- });
- },
-
- returnCallback: function (image) {
- this.callback(image);
- },
-
- imageClick: function (image) {
- var dblClickTime = 380;
- var cTime = (new Date()).getTime();
- var timeDiff = cTime - this.cClickTime;
- if (this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
- // DoubleClick
- if (this.callback) {
- this.returnCallback(image);
- }
- this.hide();
- } else {
- this.selectedImage = (this.selectedImage === image) ? false : image;
- this.dependantPages = false;
- }
- this.cClickTime = cTime;
- },
-
- selectButtonClick: function () {
- if (this.callback) this.returnCallback(this.selectedImage);
- this.hide();
- },
-
- show: function (callback) {
- this.callback = callback;
- this.$els.overlay.style.display = 'block';
- // Get initial images if they have not yet been loaded in.
- if (!this.dataLoaded) {
- this.fetchData(this.page);
- this.dataLoaded = true;
- }
- },
-
- overlayClick: function (e) {
- if (e.target.className === 'overlay') {
- this.hide();
- }
- },
-
- hide: function () {
- this.$els.overlay.style.display = 'none';
- },
-
- saveImageDetails: function (e) {
- e.preventDefault();
- this.selectedImage._token = this.token;
- var form = $(this.$els.imageForm);
- var url = '/images/update/' + this.selectedImage.id;
- this.$http.put(url, this.selectedImage).then((response) => {
- form.showSuccess('Image name updated');
- }, (response) => {
- form.showFailure(response.data);
- });
- },
-
- deleteImage: function (e) {
- e.preventDefault();
- var _this = this;
- _this.deleteForm.force = _this.dependantPages !== false;
- _this.deleteForm._token = _this.token;
- var url = '/images/' + _this.selectedImage.id;
- this.$http.delete(url, this.deleteForm).then((response) => {
- this.images.splice(this.images.indexOf(this.selectedImage), 1);
- this.selectedImage = false;
- $(this.$els.imageTitle).showSuccess('Image Deleted');
- }, (response) => {
- // Pages failure
- if (response.status === 400) {
- _this.dependantPages = response.data;
- }
- });
- }
-
- }
-
- };
-</script>
\ No newline at end of file
--- /dev/null
+
+<div class="image-picker">
+ <div>
+ <img ng-if="image && image !== 'none'" ng-src="{{image}}" ng-class="{{imageClass}}" alt="Image Preview">
+ <img ng-if="image === '' && defaultImage" ng-src="{{defaultImage}}" ng-class="{{imageClass}}" alt="Image Preview">
+ </div>
+ <button class="button" type="button" ng-click="showImageManager()">Select Image</button>
+ <br>
+
+ <button class="text-button" ng-click="reset()" type="button">Reset</button>
+ <span ng-show="showRemove" class="sep">|</span>
+ <button ng-show="showRemove" class="text-button neg" ng-click="remove()" type="button">Remove</button>
+
+ <input type="hidden" ng-attr-name="{{name}}" ng-attr-id="{{name}}" ng-attr-value="{{value}}">
+</div>
\ No newline at end of file
+++ /dev/null
-
-<template>
- <div class="image-picker">
- <div>
- <img v-if="image && image !== 'none'" :src="image" :class="imageClass" alt="Image Preview">
- <img v-if="image === '' && defaultImage" :src="defaultImage" :class="imageClass" alt="Image Preview">
- </div>
- <button class="button" type="button" @click="showImageManager">Select Image</button>
- <br>
- <button class="text-button" @click="reset" type="button">Reset</button> <span v-show="showRemove" class="sep">|</span> <button v-show="showRemove" class="text-button neg" @click="remove" type="button">Remove</button>
- <input type="hidden" :name="name" :id="name" v-model="value">
- </div>
-</template>
-
-<script>
- module.exports = {
- props: {
- currentImage: {
- required: true,
- type: String
- },
- currentId: {
- required: false,
- default: 'false',
- type: String
- },
- name: {
- required: true,
- type: String
- },
- defaultImage: {
- required: true,
- type: String
- },
- imageClass: {
- required: true,
- type: String
- },
- resizeWidth: {
- type: String
- },
- resizeHeight: {
- type: String
- },
- resizeCrop: {
- type: Boolean
- },
- showRemove: {
- type: Boolean,
- default: 'true'
- }
- },
- data: function() {
- return {
- image: this.currentImage,
- value: false
- }
- },
- compiled: function() {
- this.value = this.currentId === 'false' ? this.currentImage : this.currentId;
- },
- methods: {
- setCurrentValue: function(imageModel, imageUrl) {
- this.image = imageUrl;
- this.value = this.currentId === 'false' ? imageUrl : imageModel.id;
- },
- showImageManager: function(e) {
- ImageManager.show((image) => {
- this.updateImageFromModel(image);
- });
- },
- reset: function() {
- this.setCurrentValue({id: 0}, this.defaultImage);
- },
- remove: function() {
- this.image = 'none';
- this.value = 'none';
- },
- updateImageFromModel: function(model) {
- var isResized = this.resizeWidth && this.resizeHeight;
-
- if (!isResized) {
- this.setCurrentValue(model, model.url);
- return;
- }
-
- var cropped = this.resizeCrop ? 'true' : 'false';
- var requestString = '/images/thumb/' + model.id + '/' + this.resizeWidth + '/' + this.resizeHeight + '/' + cropped;
- this.$http.get(requestString).then((response) => {
- this.setCurrentValue(model, response.data.url);
- });
- }
- }
- };
-</script>
\ No newline at end of file
--- /dev/null
+"use strict";
+
+module.exports = function(ngApp) {
+
+ 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;
+ var page = 0;
+ var previousClickTime = 0;
+ var dataLoaded = false;
+ var callback = false;
+
+ $scope.getUploadUrl = function() {
+ return '/images/' + $scope.imageType + '/upload';
+ };
+
+ $scope.uploadSuccess = function(file, data) {
+ $scope.$apply(() => {
+ $scope.images.unshift(data);
+ });
+ };
+
+ function callbackAndHide(returnData) {
+ if (callback) callback(returnData);
+ $scope.showing = false;
+ }
+
+ $scope.imageSelect = function (image) {
+ var dblClickTime = 300;
+ var currentTime = Date.now();
+ var timeDiff = currentTime - previousClickTime;
+
+ if (timeDiff < dblClickTime) {
+ // If double click
+ callbackAndHide(image);
+ } else {
+ // If single
+ $scope.selectedImage = image;
+ $scope.dependantPages = false;
+ }
+ previousClickTime = currentTime;
+ };
+
+ $scope.selectButtonClick = function() {
+ callbackAndHide($scope.selectedImage);
+ };
+
+ function show(doneCallback) {
+ callback = doneCallback;
+ $scope.showing = true;
+ // Get initial images if they have not yet been loaded in.
+ if (!dataLoaded) {
+ fetchData();
+ dataLoaded = true;
+ }
+ }
+
+ imageManagerService.show = show;
+ imageManagerService.showExternal = function(doneCallback) {
+ $scope.$apply(() => {
+ show(doneCallback);
+ });
+ };
+ window.ImageManager = imageManagerService;
+
+ $scope.hide = function() {
+ $scope.showing = false;
+ };
+
+ function fetchData() {
+ var url = '/images/' + $scope.imageType + '/all/' + page;
+ $http.get(url).then((response) => {
+ $scope.images = $scope.images.concat(response.data.images);
+ $scope.hasMore = response.data.hasMore;
+ page++;
+ });
+ }
+
+ $scope.saveImageDetails = function(event) {
+ event.preventDefault();
+ var url = '/images/update/' + $scope.selectedImage.id;
+ $http.put(url, this.selectedImage).then((response) => {
+ $scope.imageUpdateSuccess = true;
+ $timeout(() => {
+ $scope.imageUpdateSuccess = false;
+ }, 3000);
+ }, (response) => {
+ var errors = response.data;
+ var message = '';
+ Object.keys(errors).forEach((key) => {
+ message += errors[key].join('\n');
+ });
+ $scope.imageUpdateFailure = message;
+ $timeout(() => {
+ $scope.imageUpdateFailure = false;
+ }, 5000);
+ });
+ };
+
+ $scope.deleteImage = function(event) {
+ event.preventDefault();
+ var force = $scope.dependantPages !== false;
+ var url = '/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;
+ $scope.imageDeleteSuccess = true;
+ $timeout(() => {
+ $scope.imageDeleteSuccess = false;
+ }, 3000);
+ }, (response) => {
+ // Pages failure
+ if (response.status === 400) {
+ $scope.dependantPages = response.data;
+ }
+ });
+ };
+
+ }]);
+
+
+ ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', function($scope, $http, $attrs) {
+ $scope.searching = false;
+ $scope.searchTerm = '';
+ $scope.searchResults = '';
+
+ $scope.searchBook = function (e) {
+ e.preventDefault();
+ var term = $scope.searchTerm;
+ if (term.length == 0) return;
+ $scope.searching = true;
+ $scope.searchResults = '';
+ var searchUrl = '/search/book/' + $attrs.bookId;
+ searchUrl += '?term=' + encodeURIComponent(term);
+ $http.get(searchUrl).then((response) => {
+ $scope.searchResults = response.data;
+ });
+ };
+
+ $scope.checkSearchForm = function () {
+ if ($scope.searchTerm.length < 1) {
+ $scope.searching = false;
+ }
+ };
+
+ $scope.clearSearch = function() {
+ $scope.searching = false;
+ $scope.searchTerm = '';
+ };
+
+ }]);
+
+
+};
\ No newline at end of file
+"use strict";
+var DropZone = require('dropzone');
var toggleSwitchTemplate = require('./components/toggle-switch.html');
+var imagePickerTemplate = require('./components/image-picker.html');
+var dropZoneTemplate = require('./components/drop-zone.html');
module.exports = function(ngApp) {
/**
* Toggle Switches
- * Have basic on/off functionality.
+ * Has basic on/off functionality.
* Use string values of 'true' & 'false' to dictate the current state.
*/
ngApp.directive('toggleSwitch', function() {
});
+ /**
+ * Image Picker
+ * Is a simple front-end interface that connects to an ImageManager if present.
+ */
+ ngApp.directive('imagePicker', ['$http', 'imageManagerService', function($http, imageManagerService) {
+ return {
+ restrict: 'E',
+ template: imagePickerTemplate,
+ scope: {
+ name: '@',
+ resizeHeight: '@',
+ resizeWidth: '@',
+ resizeCrop: '@',
+ showRemove: '=',
+ currentImage: '@',
+ currentId: '@',
+ defaultImage: '@',
+ imageClass: '@'
+ },
+ link: function(scope, element, attrs) {
+ var usingIds = typeof scope.currentId !== 'undefined' || scope.currentId === 'false';
+ scope.image = scope.currentImage;
+ scope.value = scope.currentImage || '';
+
+ function setImage(imageModel, imageUrl) {
+ scope.image = imageUrl;
+ scope.value = usingIds ? imageModel.id : imageUrl;
+ }
+
+ scope.reset = function() {
+ setImage({id: 0}, scope.defaultImage);
+ };
+
+ scope.remove = function() {
+ scope.image = 'none';
+ scope.value = 'none';
+ };
+
+ scope.showImageManager = function() {
+ imageManagerService.show((image) => {
+ scope.updateImageFromModel(image);
+ });
+ };
+
+ scope.updateImageFromModel = function(model) {
+ var isResized = scope.resizeWidth && scope.resizeHeight;
+
+ if (!isResized) {
+ scope.$apply(() => {
+ setImage(model, model.url);
+ });
+ return;
+ }
+
+ var cropped = scope.resizeCrop ? 'true' : 'false';
+ var requestString = '/images/thumb/' + model.id + '/' + scope.resizeWidth + '/' + scope.resizeHeight + '/' + cropped;
+ $http.get(requestString).then((response) => {
+ setImage(model, response.data.url);
+ });
+ };
+
+ }
+ };
+ }]);
+
+ /**
+ * DropZone
+ * Used for uploading images
+ */
+ ngApp.directive('dropZone', [function() {
+ return {
+ restrict: 'E',
+ template: dropZoneTemplate,
+ scope: {
+ uploadUrl: '@',
+ eventSuccess: '=',
+ eventError: '='
+ },
+ link: function(scope, element, attrs) {
+ var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
+ url: scope.uploadUrl,
+ init: function() {
+ var dz = this;
+ dz.on('sending', function(file, xhr, data) {
+ var token = window.document.querySelector('meta[name=token]').getAttribute('content');
+ data.append('_token', token);
+ });
+ if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess);
+ dz.on('success', function(file, data) {
+ $(file.previewElement).fadeOut(400, function () {
+ dz.removeFile(file);
+ });
+ });
+ if (typeof scope.eventError !== 'undefined') dz.on('error', scope.eventError);
+ dz.on('error', function (file, errorMessage, xhr) {
+ if (errorMessage.file) {
+ $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
+ }
+ });
+ }
+ });
+ }
+ };
+ }]);
+
+
+ ngApp.directive('dropdown', [function() {
+ return {
+ restrict: 'A',
+ link: function(scope, element, attrs) {
+ var menu = element.find('ul');
+ element.find('[dropdown-toggle]').on('click', function() {
+ menu.show().addClass('anim menuIn');
+ element.mouseleave(function() {
+ menu.hide();
+ menu.removeClass('anim menuIn');
+ });
+ });
+ }
+ };
+ }]);
+
+
};
\ No newline at end of file
// AngularJS - Create application and load components
var angular = require('angular');
-var angularResource = require('angular-resource');
-var app = angular.module('bookStack', ['ngResource']);
-var directives = require('./directives')(app);
+var ngResource = require('angular-resource');
+var ngAnimate = require('angular-animate');
+var ngSanitize = require('angular-sanitize');
+
+var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize']);
+var services = require('./services')(ngApp);
+var directives = require('./directives')(ngApp);
+var controllers = require('./controllers')(ngApp);
+
+//Global jQuery Config & Extensions
+
+// Smooth scrolling
+jQuery.fn.smoothScrollTo = function() {
+ if(this.length === 0) return;
+ $('body').animate({
+ scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
+ }, 800); // Adjust to change animations speed (ms)
+ return this;
+};
+
+// Making contains text expression not worry about casing
+$.expr[":"].contains = $.expr.createPseudo(function(arg) {
+ return function( elem ) {
+ return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
+ };
+});
// Global jQuery Elements
$(function () {
$(this).fadeOut(100);
});
- // Dropdown toggles
- $('[data-dropdown]').dropDown();
-
// Chapter page list toggles
$('.chapter-toggle').click(function(e) {
e.preventDefault();
});
+
+function elemExists(selector) {
+ return document.querySelector(selector) !== null;
+}
+
// TinyMCE editor
if(elemExists('#html-editor')) {
var tinyMceOptions = require('./pages/page-form');
+++ /dev/null
-
-// Smooth scrolling
-jQuery.fn.smoothScrollTo = function() {
- if(this.length === 0) return;
- $('body').animate({
- scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
- }, 800); // Adjust to change animations speed (ms)
- return this;
-};
-
-// Making contains text expression not worry about casing
-$.expr[":"].contains = $.expr.createPseudo(function(arg) {
- return function( elem ) {
- return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
- };
-});
-
-// Show a success message after the element it's called upon.
-jQuery.fn.showSuccess = function (message) {
- var elem = $(this);
- var success = $('<div class="text-pos" style="display:none;"><i class="zmdi zmdi-check-circle"></i>' + message + '</div>');
- elem.after(success);
- success.slideDown(400, function () {
- setTimeout(function () {
- success.slideUp(400, function () {
- success.remove();
- })
- }, 2000);
- });
-};
-
-// Show a failure messages from laravel. Searches for the name of the inputs.
-jQuery.fn.showFailure = function (messageMap) {
- var elem = $(this);
- $.each(messageMap, function (key, messages) {
- var input = elem.find('[name="' + key + '"]').last();
- var fail = $('<div class="text-neg" style="display:none;"><i class="zmdi zmdi-alert-circle"></i>' + messages.join("\n") + '</div>');
- input.after(fail);
- fail.slideDown(400, function () {
- setTimeout(function () {
- fail.slideUp(400, function () {
- fail.remove();
- })
- }, 2000);
- });
- });
-
-};
-
-// Submit the form that the called upon element sits in.
-jQuery.fn.submitForm = function() {
- $(this).closest('form').submit();
-};
-
-// Dropdown menu display
-jQuery.fn.dropDown = function() {
- var container = $(this),
- menu = container.find('ul');
- container.find('[data-dropdown-toggle]').on('click', function() {
- menu.show().addClass('anim menuIn');
- container.mouseleave(function() {
- menu.hide();
- menu.removeClass('anim menuIn');
- });
- });
-};
\ No newline at end of file
+++ /dev/null
-
-module.exports = {
- el: '#book-dashboard',
- data: {
- searching: false,
- searchTerm: '',
- searchResults: ''
- },
- methods: {
- searchBook: function (e) {
- e.preventDefault();
- var term = this.searchTerm;
- if (term.length == 0) return;
- this.searching = true;
- this.searchResults = '';
- var searchUrl = this.$els.form.getAttribute('action');
- searchUrl += '?term=' + encodeURIComponent(term);
- this.$http.get(searchUrl, function (data) {
- this.$set('searchResults', data);
- });
- },
- checkSearchForm: function (e) {
- if (this.searchTerm.length < 1) {
- this.searching = false;
- }
- },
- clearSearch: function(e) {
- this.searching = false;
- this.searchTerm = '';
- }
- }
-};
\ No newline at end of file
icon: 'image',
tooltip: 'Insert an image',
onclick: function() {
- ImageManager.show(function(image) {
+ window.ImageManager.showExternal(function(image) {
var html = '<a href="'+image.url+'" target="_blank">';
html += '<img src="'+image.thumbs.display+'" alt="'+image.name+'">';
html += '</a>';
});
}
});
+
// Paste image-uploads
editor.on('paste', function(e) {
if(e.clipboardData) {
--- /dev/null
+"use strict";
+
+module.exports = function(ngApp) {
+
+ ngApp.factory('imageManagerService', function() {
+ return {
+ show: false,
+ showExternal: false
+ };
+ });
+
+};
\ No newline at end of file
.overlay {
background-color: rgba(0, 0, 0, 0.2);
position: fixed;
- display: none;
z-index: 95536;
width: 100%;
height: 100%;
[v-cloak], [v-show] {display: none;}
+[ng\:cloak], [ng-cloak], .ng-cloak {
+ display: none !important;
+}
+
// Jquery Sortable Styles
.dragged {
position: absolute;
@endif
</div>
@if($signedIn)
- <div class="dropdown-container" data-dropdown>
- <span class="user-name" data-dropdown-toggle>
+ <div class="dropdown-container" dropdown>
+ <span class="user-name" dropdown-toggle>
<img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
<span class="name">{{ $currentUser->name }}</span> <i class="zmdi zmdi-caret-down"></i>
</span>
</div>
- <div class="container" id="book-dashboard">
+ <div class="container" id="book-dashboard" ng-controller="BookShowController" book-id="{{ $book->id }}">
<div class="row">
<div class="col-md-7">
<h1>{{$book->name}}</h1>
- <div class="book-content anim fadeIn" v-show="!searching">
- <p class="text-muted">{{$book->description}}</p>
+ <div class="book-content" ng-show="!searching">
+ <p class="text-muted" ng-non-bindable>{{$book->description}}</p>
- <div class="page-list">
+ <div class="page-list" ng-non-bindable>
<hr>
@if(count($bookChildren) > 0)
@foreach($bookChildren as $childElement)
</p>
</div>
</div>
- <div class="search-results" v-show="searching">
- <h3 class="text-muted">Search Results <a v-if="searching" @click="clearSearch" class="text-small"><i class="zmdi zmdi-close"></i>Clear Search</a></h3>
- <div v-if="!searchResults">
+ <div class="search-results" ng-cloak ng-show="searching">
+ <h3 class="text-muted">Search Results <a ng-if="searching" ng-click="clearSearch()" class="text-small"><i class="zmdi zmdi-close"></i>Clear Search</a></h3>
+ <div ng-if="!searchResults">
@include('partials/loading-icon')
</div>
- <div v-html="searchResults"></div>
+ <div ng-bind-html="searchResults"></div>
</div>
<div class="col-md-4 col-md-offset-1">
<div class="margin-top large"></div>
<div class="search-box">
- <form @submit="searchBook" @input="checkSearchForm" v-el:form action="/search/book/{{ $book->id }}">
- {!! csrf_field() !!}
- <input v-model="searchTerm" type="text" name="term" placeholder="Search This Book">
+ <form ng-submit="searchBook($event)">
+ <input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="Search This Book">
<button type="submit"><i class="zmdi zmdi-search"></i></button>
- <button v-if="searching" @click="clearSearch" type="button"><i class="zmdi zmdi-close"></i></button>
+ <button ng-if="searching" ng-click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
</form>
</div>
<div class="activity anim fadeIn">
@extends('base')
+@section('head')
+ <script src="/libs/jquery-sortable/jquery-sortable.min.js"></script>
+@stop
+
@section('content')
<div class="container">
@endif
</form>
</div>
- <image-manager image-type="gallery"></image-manager>
+ @include('partials/image-manager', ['imageType' => 'gallery'])
@stop
\ No newline at end of file
@include('pages/form', ['model' => $page])
</form>
</div>
- <image-manager image-type="gallery"></image-manager>
+ @include('partials/image-manager', ['imageType' => 'gallery'])
@stop
\ No newline at end of file
-<div v-pre>
+<div ng-non-bindable>
<h1 id="bkmrk-page-title">{{$page->name}}</h1>
{!! $page->html !!}
--- /dev/null
+<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController">
+ <div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()">
+ <div class="image-manager-body" ng-click="$event.stopPropagation()">
+
+ <div class="image-manager-content">
+ <div class="image-manager-list">
+ <div ng-repeat="image in images">
+ <img class="anim fadeIn"
+ ng-class="{selected: (image==selectedImage)}"
+ ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}"
+ ng-click="imageSelect(image)"
+ ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
+ </div>
+ <div class="load-more" ng-show="hasMore" ng-click="fetchData()">Load More</div>
+ </div>
+ </div>
+
+ <button class="neg button image-manager-close" ng-click="hide()">x</button>
+
+ <div class="image-manager-sidebar">
+ <h2>Images</h2>
+ <hr class="even">
+ <drop-zone upload-url="@{{getUploadUrl()}}" event-success="uploadSuccess"></drop-zone>
+ <div class="image-manager-details anim fadeIn" ng-show="selectedImage">
+
+ <hr class="even">
+
+ <form ng-submit="saveImageDetails($event)">
+ <div class="form-group">
+ <label for="name">Image Name</label>
+ <input type="text" id="name" name="name" ng-model="selectedImage.name">
+ <p class="text-pos text-small" ng-show="imageUpdateSuccess"><i class="fa fa-check"></i> Image name updated</p>
+ <p class="text-neg text-small" ng-show="imageUpdateFailure"><i class="fa fa-times"></i> <span ng-bind="imageUpdateFailure"></span></p>
+ </div>
+ </form>
+
+ <hr class="even">
+
+ <div ng-show="dependantPages">
+ <p class="text-neg text-small">
+ This image is used in the pages below, Click delete again to confirm you want to delete
+ this image.
+ </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>
+ </ul>
+ </div>
+
+ <form ng-submit="deleteImage($event)">
+ <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
+ </form>
+ </div>
+
+ <p class="text-pos" ng-show="imageDeleteSuccess"><i class="fa fa-check"></i> Image deleted</p>
+
+ <div class="image-manager-bottom">
+ <button class="button pos anim fadeIn" ng-show="selectedImage" ng-click="selectButtonClick()">
+ <i class="zmdi zmdi-square-right"></i>Select Image
+ </button>
+ </div>
+
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
<div class="form-group" id="logo-control">
<label for="setting-app-logo">Application Logo</label>
<p class="small">This image should be 43px in height. <br>Large images will be scaled down.</p>
- <image-picker resize-height="43" resize-width="200" current-image="{{ Setting::get('app-logo', '') }}" default-image="/logo.png" name="setting-app-logo" image-class="logo-image"></image-picker>
+ <image-picker resize-height="43" show-remove="true" resize-width="200" current-image="{{ Setting::get('app-logo', '') }}" default-image="/logo.png" name="setting-app-logo" image-class="logo-image"></image-picker>
</div>
</div>
</div>
</div>
-<image-manager image-type="system"></image-manager>
+@include('partials/image-manager', ['imageType' => 'system'])
@stop
<div class="form-group" id="logo-control">
<label for="user-avatar">User Avatar</label>
<p class="small">This image should be approx 256px square.</p>
- <image-picker resize-height="512" resize-width="512" current-image="{{ $user->getAvatar(80) }}" current-id="{{ $user->image_id }}" default-image="/user_avatar.png" name="image_id" show-remove="false" image-class="avatar large"></image-picker>
+ <image-picker resize-height="512" resize-width="512" current-image="{{ $user->getAvatar(80) }}" current-id="{{ $user->image_id }}" default-image="/user_avatar.png" name="image_id" show-remove="false" image-class="['avatar' ,'large']"></image-picker>
</div>
</div>
</div>
</div>
<p class="margin-top large"><br></p>
- <image-manager image-type="user"></image-manager>
+ @include('partials/image-manager', ['imageType' => 'user'])
@stop