Dan Brown

Moved all vuejs parts over to angular

...@@ -21,6 +21,6 @@ elixir.extend('queryVersion', function(inputFiles) { ...@@ -21,6 +21,6 @@ elixir.extend('queryVersion', function(inputFiles) {
21 elixir(function(mix) { 21 elixir(function(mix) {
22 mix.sass('styles.scss') 22 mix.sass('styles.scss')
23 .sass('print-styles.scss') 23 .sass('print-styles.scss')
24 - .browserify(['jquery-extensions.js', 'global.js'], 'public/js/common.js') 24 + .browserify('global.js', 'public/js/common.js')
25 .queryVersion(['css/styles.css', 'css/print-styles.css', 'js/common.js']); 25 .queryVersion(['css/styles.css', 'css/print-styles.css', 'js/common.js']);
26 }); 26 });
......
1 { 1 {
2 "private": true, 2 "private": true,
3 "devDependencies": { 3 "devDependencies": {
4 - "gulp": "^3.9.0", 4 + "gulp": "^3.9.0"
5 - "insert-css": "^0.2.0"
6 }, 5 },
7 "dependencies": { 6 "dependencies": {
8 "angular": "^1.5.0-rc.0", 7 "angular": "^1.5.0-rc.0",
8 + "angular-animate": "^1.5.0-rc.0",
9 "angular-resource": "^1.5.0-rc.0", 9 "angular-resource": "^1.5.0-rc.0",
10 + "angular-sanitize": "^1.5.0-rc.0",
10 "babel-runtime": "^5.8.29", 11 "babel-runtime": "^5.8.29",
11 "bootstrap-sass": "^3.0.0", 12 "bootstrap-sass": "^3.0.0",
12 "dropzone": "^4.0.1", 13 "dropzone": "^4.0.1",
13 "laravel-elixir": "^3.4.0", 14 "laravel-elixir": "^3.4.0",
14 - "vue": "^1.0.13",
15 - "vue-hot-reload-api": "^1.2.1",
16 - "vue-resource": "^0.5.1",
17 - "vueify": "^5.0.1",
18 - "vueify-insert-css": "^1.0.0",
19 "zeroclipboard": "^2.2.0" 15 "zeroclipboard": "^2.2.0"
20 } 16 }
21 } 17 }
......
1 +<div class="dropzone-container">
2 + <div class="dz-message">Drop files or click here to upload</div>
3 +</div>
...\ No newline at end of file ...\ No newline at end of file
1 -<template>
2 - <div id="image-manager">
3 - <div class="overlay" v-el:overlay @click="overlayClick">
4 - <div class="image-manager-body">
5 - <div class="image-manager-content">
6 - <div class="image-manager-list">
7 - <div v-for="image in images">
8 - <img class="anim fadeIn"
9 - :class="{selected: (image==selectedImage)}"
10 - :src="image.thumbs.gallery" :alt="image.title" :title="image.name"
11 - @click="imageClick(image)"
12 - :style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
13 - </div>
14 - <div class="load-more" v-show="hasMore" @click="fetchData">Load More</div>
15 - </div>
16 - </div>
17 - <button class="neg button image-manager-close" @click="hide">x</button>
18 - <div class="image-manager-sidebar">
19 - <h2 v-el:image-title>Images</h2>
20 - <hr class="even">
21 - <div class="dropzone-container" v-el:drop-zone>
22 - <div class="dz-message">Drop files or click here to upload</div>
23 - </div>
24 - <div class="image-manager-details anim fadeIn" v-show="selectedImage">
25 - <hr class="even">
26 - <form @submit="saveImageDetails" v-el:image-form>
27 - <div class="form-group">
28 - <label for="name">Image Name</label>
29 - <input type="text" id="name" name="name" v-model="selectedImage.name">
30 - </div>
31 - </form>
32 - <hr class="even">
33 - <div v-show="dependantPages">
34 - <p class="text-neg text-small">
35 - This image is used in the pages below, Click delete again to confirm you want to delete
36 - this image.
37 - </p>
38 - <ul class="text-neg">
39 - <li v-for="page in dependantPages">
40 - <a :href="page.url" target="_blank" class="text-neg">{{ page.name }}</a>
41 - </li>
42 - </ul>
43 - </div>
44 -
45 - <form @submit="deleteImage" v-el:image-delete-form>
46 - <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
47 - </form>
48 - </div>
49 - <div class="image-manager-bottom">
50 - <button class="button pos anim fadeIn" v-show="selectedImage" @click="selectButtonClick"><i
51 - class="zmdi zmdi-square-right"></i>Select Image
52 - </button>
53 - </div>
54 - </div>
55 - </div>
56 - </div>
57 - </div>
58 -</template>
59 -
60 -<script>
61 -
62 - var Dropzone = require('dropzone');
63 -
64 - module.exports = {
65 - data: function () {
66 - return {
67 - images: [],
68 - hasMore: false,
69 - page: 0,
70 - cClickTime: 0,
71 - selectedImage: false,
72 - dependantPages: false,
73 - deleteForm: {},
74 - token: document.querySelector('meta[name=token]').getAttribute('content'),
75 - dataLoaded: false
76 - }
77 - },
78 -
79 - props: {
80 - imageType: {
81 - type: String,
82 - required: true
83 - }
84 - },
85 -
86 - created: function () {
87 - window.ImageManager = this;
88 - },
89 -
90 - ready: function () {
91 - // Create dropzone
92 - this.setupDropZone();
93 - },
94 -
95 - methods: {
96 - fetchData: function () {
97 - var url = '/images/' + this.imageType + '/all/' + this.page;
98 - this.$http.get(url).then((response) => {
99 - this.images = this.images.concat(response.data.images);
100 - this.hasMore = response.data.hasMore;
101 - this.page++;
102 - });
103 - },
104 -
105 - setupDropZone: function () {
106 - var _this = this;
107 - var dropZone = new Dropzone(_this.$els.dropZone, {
108 - url: '/images/' + _this.imageType + '/upload',
109 - init: function () {
110 - var dz = this;
111 - dz.on("sending", function (file, xhr, data) {
112 - data.append("_token", _this.token);
113 - });
114 - dz.on("success", function (file, data) {
115 - _this.images.unshift(data);
116 - $(file.previewElement).fadeOut(400, function () {
117 - dz.removeFile(file);
118 - });
119 - });
120 - dz.on('error', function (file, errorMessage, xhr) {
121 - if (errorMessage.file) {
122 - $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
123 - }
124 - console.log(errorMessage);
125 - });
126 - }
127 - });
128 - },
129 -
130 - returnCallback: function (image) {
131 - this.callback(image);
132 - },
133 -
134 - imageClick: function (image) {
135 - var dblClickTime = 380;
136 - var cTime = (new Date()).getTime();
137 - var timeDiff = cTime - this.cClickTime;
138 - if (this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
139 - // DoubleClick
140 - if (this.callback) {
141 - this.returnCallback(image);
142 - }
143 - this.hide();
144 - } else {
145 - this.selectedImage = (this.selectedImage === image) ? false : image;
146 - this.dependantPages = false;
147 - }
148 - this.cClickTime = cTime;
149 - },
150 -
151 - selectButtonClick: function () {
152 - if (this.callback) this.returnCallback(this.selectedImage);
153 - this.hide();
154 - },
155 -
156 - show: function (callback) {
157 - this.callback = callback;
158 - this.$els.overlay.style.display = 'block';
159 - // Get initial images if they have not yet been loaded in.
160 - if (!this.dataLoaded) {
161 - this.fetchData(this.page);
162 - this.dataLoaded = true;
163 - }
164 - },
165 -
166 - overlayClick: function (e) {
167 - if (e.target.className === 'overlay') {
168 - this.hide();
169 - }
170 - },
171 -
172 - hide: function () {
173 - this.$els.overlay.style.display = 'none';
174 - },
175 -
176 - saveImageDetails: function (e) {
177 - e.preventDefault();
178 - this.selectedImage._token = this.token;
179 - var form = $(this.$els.imageForm);
180 - var url = '/images/update/' + this.selectedImage.id;
181 - this.$http.put(url, this.selectedImage).then((response) => {
182 - form.showSuccess('Image name updated');
183 - }, (response) => {
184 - form.showFailure(response.data);
185 - });
186 - },
187 -
188 - deleteImage: function (e) {
189 - e.preventDefault();
190 - var _this = this;
191 - _this.deleteForm.force = _this.dependantPages !== false;
192 - _this.deleteForm._token = _this.token;
193 - var url = '/images/' + _this.selectedImage.id;
194 - this.$http.delete(url, this.deleteForm).then((response) => {
195 - this.images.splice(this.images.indexOf(this.selectedImage), 1);
196 - this.selectedImage = false;
197 - $(this.$els.imageTitle).showSuccess('Image Deleted');
198 - }, (response) => {
199 - // Pages failure
200 - if (response.status === 400) {
201 - _this.dependantPages = response.data;
202 - }
203 - });
204 - }
205 -
206 - }
207 -
208 - };
209 -</script>
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +<div class="image-picker">
3 + <div>
4 + <img ng-if="image && image !== 'none'" ng-src="{{image}}" ng-class="{{imageClass}}" alt="Image Preview">
5 + <img ng-if="image === '' && defaultImage" ng-src="{{defaultImage}}" ng-class="{{imageClass}}" alt="Image Preview">
6 + </div>
7 + <button class="button" type="button" ng-click="showImageManager()">Select Image</button>
8 + <br>
9 +
10 + <button class="text-button" ng-click="reset()" type="button">Reset</button>
11 + <span ng-show="showRemove" class="sep">|</span>
12 + <button ng-show="showRemove" class="text-button neg" ng-click="remove()" type="button">Remove</button>
13 +
14 + <input type="hidden" ng-attr-name="{{name}}" ng-attr-id="{{name}}" ng-attr-value="{{value}}">
15 +</div>
...\ No newline at end of file ...\ No newline at end of file
1 -
2 -<template>
3 - <div class="image-picker">
4 - <div>
5 - <img v-if="image && image !== 'none'" :src="image" :class="imageClass" alt="Image Preview">
6 - <img v-if="image === '' && defaultImage" :src="defaultImage" :class="imageClass" alt="Image Preview">
7 - </div>
8 - <button class="button" type="button" @click="showImageManager">Select Image</button>
9 - <br>
10 - <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>
11 - <input type="hidden" :name="name" :id="name" v-model="value">
12 - </div>
13 -</template>
14 -
15 -<script>
16 - module.exports = {
17 - props: {
18 - currentImage: {
19 - required: true,
20 - type: String
21 - },
22 - currentId: {
23 - required: false,
24 - default: 'false',
25 - type: String
26 - },
27 - name: {
28 - required: true,
29 - type: String
30 - },
31 - defaultImage: {
32 - required: true,
33 - type: String
34 - },
35 - imageClass: {
36 - required: true,
37 - type: String
38 - },
39 - resizeWidth: {
40 - type: String
41 - },
42 - resizeHeight: {
43 - type: String
44 - },
45 - resizeCrop: {
46 - type: Boolean
47 - },
48 - showRemove: {
49 - type: Boolean,
50 - default: 'true'
51 - }
52 - },
53 - data: function() {
54 - return {
55 - image: this.currentImage,
56 - value: false
57 - }
58 - },
59 - compiled: function() {
60 - this.value = this.currentId === 'false' ? this.currentImage : this.currentId;
61 - },
62 - methods: {
63 - setCurrentValue: function(imageModel, imageUrl) {
64 - this.image = imageUrl;
65 - this.value = this.currentId === 'false' ? imageUrl : imageModel.id;
66 - },
67 - showImageManager: function(e) {
68 - ImageManager.show((image) => {
69 - this.updateImageFromModel(image);
70 - });
71 - },
72 - reset: function() {
73 - this.setCurrentValue({id: 0}, this.defaultImage);
74 - },
75 - remove: function() {
76 - this.image = 'none';
77 - this.value = 'none';
78 - },
79 - updateImageFromModel: function(model) {
80 - var isResized = this.resizeWidth && this.resizeHeight;
81 -
82 - if (!isResized) {
83 - this.setCurrentValue(model, model.url);
84 - return;
85 - }
86 -
87 - var cropped = this.resizeCrop ? 'true' : 'false';
88 - var requestString = '/images/thumb/' + model.id + '/' + this.resizeWidth + '/' + this.resizeHeight + '/' + cropped;
89 - this.$http.get(requestString).then((response) => {
90 - this.setCurrentValue(model, response.data.url);
91 - });
92 - }
93 - }
94 - };
95 -</script>
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +
3 +module.exports = function(ngApp) {
4 +
5 + ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout','imageManagerService',
6 + function($scope, $attrs, $http, $timeout, imageManagerService) {
7 + $scope.images = [];
8 + $scope.imageType = $attrs.imageType;
9 + $scope.selectedImage = false;
10 + $scope.dependantPages = false;
11 + $scope.showing = false;
12 + $scope.hasMore = false;
13 + $scope.imageUpdateSuccess = false;
14 + $scope.imageDeleteSuccess = false;
15 + var page = 0;
16 + var previousClickTime = 0;
17 + var dataLoaded = false;
18 + var callback = false;
19 +
20 + $scope.getUploadUrl = function() {
21 + return '/images/' + $scope.imageType + '/upload';
22 + };
23 +
24 + $scope.uploadSuccess = function(file, data) {
25 + $scope.$apply(() => {
26 + $scope.images.unshift(data);
27 + });
28 + };
29 +
30 + function callbackAndHide(returnData) {
31 + if (callback) callback(returnData);
32 + $scope.showing = false;
33 + }
34 +
35 + $scope.imageSelect = function (image) {
36 + var dblClickTime = 300;
37 + var currentTime = Date.now();
38 + var timeDiff = currentTime - previousClickTime;
39 +
40 + if (timeDiff < dblClickTime) {
41 + // If double click
42 + callbackAndHide(image);
43 + } else {
44 + // If single
45 + $scope.selectedImage = image;
46 + $scope.dependantPages = false;
47 + }
48 + previousClickTime = currentTime;
49 + };
50 +
51 + $scope.selectButtonClick = function() {
52 + callbackAndHide($scope.selectedImage);
53 + };
54 +
55 + function show(doneCallback) {
56 + callback = doneCallback;
57 + $scope.showing = true;
58 + // Get initial images if they have not yet been loaded in.
59 + if (!dataLoaded) {
60 + fetchData();
61 + dataLoaded = true;
62 + }
63 + }
64 +
65 + imageManagerService.show = show;
66 + imageManagerService.showExternal = function(doneCallback) {
67 + $scope.$apply(() => {
68 + show(doneCallback);
69 + });
70 + };
71 + window.ImageManager = imageManagerService;
72 +
73 + $scope.hide = function() {
74 + $scope.showing = false;
75 + };
76 +
77 + function fetchData() {
78 + var url = '/images/' + $scope.imageType + '/all/' + page;
79 + $http.get(url).then((response) => {
80 + $scope.images = $scope.images.concat(response.data.images);
81 + $scope.hasMore = response.data.hasMore;
82 + page++;
83 + });
84 + }
85 +
86 + $scope.saveImageDetails = function(event) {
87 + event.preventDefault();
88 + var url = '/images/update/' + $scope.selectedImage.id;
89 + $http.put(url, this.selectedImage).then((response) => {
90 + $scope.imageUpdateSuccess = true;
91 + $timeout(() => {
92 + $scope.imageUpdateSuccess = false;
93 + }, 3000);
94 + }, (response) => {
95 + var errors = response.data;
96 + var message = '';
97 + Object.keys(errors).forEach((key) => {
98 + message += errors[key].join('\n');
99 + });
100 + $scope.imageUpdateFailure = message;
101 + $timeout(() => {
102 + $scope.imageUpdateFailure = false;
103 + }, 5000);
104 + });
105 + };
106 +
107 + $scope.deleteImage = function(event) {
108 + event.preventDefault();
109 + var force = $scope.dependantPages !== false;
110 + var url = '/images/' + $scope.selectedImage.id;
111 + if (force) url += '?force=true';
112 + $http.delete(url).then((response) => {
113 + $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
114 + $scope.selectedImage = false;
115 + $scope.imageDeleteSuccess = true;
116 + $timeout(() => {
117 + $scope.imageDeleteSuccess = false;
118 + }, 3000);
119 + }, (response) => {
120 + // Pages failure
121 + if (response.status === 400) {
122 + $scope.dependantPages = response.data;
123 + }
124 + });
125 + };
126 +
127 + }]);
128 +
129 +
130 + ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', function($scope, $http, $attrs) {
131 + $scope.searching = false;
132 + $scope.searchTerm = '';
133 + $scope.searchResults = '';
134 +
135 + $scope.searchBook = function (e) {
136 + e.preventDefault();
137 + var term = $scope.searchTerm;
138 + if (term.length == 0) return;
139 + $scope.searching = true;
140 + $scope.searchResults = '';
141 + var searchUrl = '/search/book/' + $attrs.bookId;
142 + searchUrl += '?term=' + encodeURIComponent(term);
143 + $http.get(searchUrl).then((response) => {
144 + $scope.searchResults = response.data;
145 + });
146 + };
147 +
148 + $scope.checkSearchForm = function () {
149 + if ($scope.searchTerm.length < 1) {
150 + $scope.searching = false;
151 + }
152 + };
153 +
154 + $scope.clearSearch = function() {
155 + $scope.searching = false;
156 + $scope.searchTerm = '';
157 + };
158 +
159 + }]);
160 +
161 +
162 +};
...\ No newline at end of file ...\ No newline at end of file
1 +"use strict";
2 +var DropZone = require('dropzone');
1 3
2 var toggleSwitchTemplate = require('./components/toggle-switch.html'); 4 var toggleSwitchTemplate = require('./components/toggle-switch.html');
5 +var imagePickerTemplate = require('./components/image-picker.html');
6 +var dropZoneTemplate = require('./components/drop-zone.html');
3 7
4 module.exports = function(ngApp) { 8 module.exports = function(ngApp) {
5 9
6 /** 10 /**
7 * Toggle Switches 11 * Toggle Switches
8 - * Have basic on/off functionality. 12 + * Has basic on/off functionality.
9 * Use string values of 'true' & 'false' to dictate the current state. 13 * Use string values of 'true' & 'false' to dictate the current state.
10 */ 14 */
11 ngApp.directive('toggleSwitch', function() { 15 ngApp.directive('toggleSwitch', function() {
...@@ -29,4 +33,127 @@ module.exports = function(ngApp) { ...@@ -29,4 +33,127 @@ module.exports = function(ngApp) {
29 }); 33 });
30 34
31 35
36 + /**
37 + * Image Picker
38 + * Is a simple front-end interface that connects to an ImageManager if present.
39 + */
40 + ngApp.directive('imagePicker', ['$http', 'imageManagerService', function($http, imageManagerService) {
41 + return {
42 + restrict: 'E',
43 + template: imagePickerTemplate,
44 + scope: {
45 + name: '@',
46 + resizeHeight: '@',
47 + resizeWidth: '@',
48 + resizeCrop: '@',
49 + showRemove: '=',
50 + currentImage: '@',
51 + currentId: '@',
52 + defaultImage: '@',
53 + imageClass: '@'
54 + },
55 + link: function(scope, element, attrs) {
56 + var usingIds = typeof scope.currentId !== 'undefined' || scope.currentId === 'false';
57 + scope.image = scope.currentImage;
58 + scope.value = scope.currentImage || '';
59 +
60 + function setImage(imageModel, imageUrl) {
61 + scope.image = imageUrl;
62 + scope.value = usingIds ? imageModel.id : imageUrl;
63 + }
64 +
65 + scope.reset = function() {
66 + setImage({id: 0}, scope.defaultImage);
67 + };
68 +
69 + scope.remove = function() {
70 + scope.image = 'none';
71 + scope.value = 'none';
72 + };
73 +
74 + scope.showImageManager = function() {
75 + imageManagerService.show((image) => {
76 + scope.updateImageFromModel(image);
77 + });
78 + };
79 +
80 + scope.updateImageFromModel = function(model) {
81 + var isResized = scope.resizeWidth && scope.resizeHeight;
82 +
83 + if (!isResized) {
84 + scope.$apply(() => {
85 + setImage(model, model.url);
86 + });
87 + return;
88 + }
89 +
90 + var cropped = scope.resizeCrop ? 'true' : 'false';
91 + var requestString = '/images/thumb/' + model.id + '/' + scope.resizeWidth + '/' + scope.resizeHeight + '/' + cropped;
92 + $http.get(requestString).then((response) => {
93 + setImage(model, response.data.url);
94 + });
95 + };
96 +
97 + }
98 + };
99 + }]);
100 +
101 + /**
102 + * DropZone
103 + * Used for uploading images
104 + */
105 + ngApp.directive('dropZone', [function() {
106 + return {
107 + restrict: 'E',
108 + template: dropZoneTemplate,
109 + scope: {
110 + uploadUrl: '@',
111 + eventSuccess: '=',
112 + eventError: '='
113 + },
114 + link: function(scope, element, attrs) {
115 + var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
116 + url: scope.uploadUrl,
117 + init: function() {
118 + var dz = this;
119 + dz.on('sending', function(file, xhr, data) {
120 + var token = window.document.querySelector('meta[name=token]').getAttribute('content');
121 + data.append('_token', token);
122 + });
123 + if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess);
124 + dz.on('success', function(file, data) {
125 + $(file.previewElement).fadeOut(400, function () {
126 + dz.removeFile(file);
127 + });
128 + });
129 + if (typeof scope.eventError !== 'undefined') dz.on('error', scope.eventError);
130 + dz.on('error', function (file, errorMessage, xhr) {
131 + if (errorMessage.file) {
132 + $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
133 + }
134 + });
135 + }
136 + });
137 + }
138 + };
139 + }]);
140 +
141 +
142 + ngApp.directive('dropdown', [function() {
143 + return {
144 + restrict: 'A',
145 + link: function(scope, element, attrs) {
146 + var menu = element.find('ul');
147 + element.find('[dropdown-toggle]').on('click', function() {
148 + menu.show().addClass('anim menuIn');
149 + element.mouseleave(function() {
150 + menu.hide();
151 + menu.removeClass('anim menuIn');
152 + });
153 + });
154 + }
155 + };
156 + }]);
157 +
158 +
32 }; 159 };
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -6,9 +6,32 @@ window.ZeroClipboard.config({ ...@@ -6,9 +6,32 @@ window.ZeroClipboard.config({
6 6
7 // AngularJS - Create application and load components 7 // AngularJS - Create application and load components
8 var angular = require('angular'); 8 var angular = require('angular');
9 -var angularResource = require('angular-resource'); 9 +var ngResource = require('angular-resource');
10 -var app = angular.module('bookStack', ['ngResource']); 10 +var ngAnimate = require('angular-animate');
11 -var directives = require('./directives')(app); 11 +var ngSanitize = require('angular-sanitize');
12 +
13 +var ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize']);
14 +var services = require('./services')(ngApp);
15 +var directives = require('./directives')(ngApp);
16 +var controllers = require('./controllers')(ngApp);
17 +
18 +//Global jQuery Config & Extensions
19 +
20 +// Smooth scrolling
21 +jQuery.fn.smoothScrollTo = function() {
22 + if(this.length === 0) return;
23 + $('body').animate({
24 + scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
25 + }, 800); // Adjust to change animations speed (ms)
26 + return this;
27 +};
28 +
29 +// Making contains text expression not worry about casing
30 +$.expr[":"].contains = $.expr.createPseudo(function(arg) {
31 + return function( elem ) {
32 + return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
33 + };
34 +});
12 35
13 // Global jQuery Elements 36 // Global jQuery Elements
14 $(function () { 37 $(function () {
...@@ -18,9 +41,6 @@ $(function () { ...@@ -18,9 +41,6 @@ $(function () {
18 $(this).fadeOut(100); 41 $(this).fadeOut(100);
19 }); 42 });
20 43
21 - // Dropdown toggles
22 - $('[data-dropdown]').dropDown();
23 -
24 // Chapter page list toggles 44 // Chapter page list toggles
25 $('.chapter-toggle').click(function(e) { 45 $('.chapter-toggle').click(function(e) {
26 e.preventDefault(); 46 e.preventDefault();
...@@ -30,6 +50,11 @@ $(function () { ...@@ -30,6 +50,11 @@ $(function () {
30 50
31 }); 51 });
32 52
53 +
54 +function elemExists(selector) {
55 + return document.querySelector(selector) !== null;
56 +}
57 +
33 // TinyMCE editor 58 // TinyMCE editor
34 if(elemExists('#html-editor')) { 59 if(elemExists('#html-editor')) {
35 var tinyMceOptions = require('./pages/page-form'); 60 var tinyMceOptions = require('./pages/page-form');
......
1 -
2 -// Smooth scrolling
3 -jQuery.fn.smoothScrollTo = function() {
4 - if(this.length === 0) return;
5 - $('body').animate({
6 - scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
7 - }, 800); // Adjust to change animations speed (ms)
8 - return this;
9 -};
10 -
11 -// Making contains text expression not worry about casing
12 -$.expr[":"].contains = $.expr.createPseudo(function(arg) {
13 - return function( elem ) {
14 - return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
15 - };
16 -});
17 -
18 -// Show a success message after the element it's called upon.
19 -jQuery.fn.showSuccess = function (message) {
20 - var elem = $(this);
21 - var success = $('<div class="text-pos" style="display:none;"><i class="zmdi zmdi-check-circle"></i>' + message + '</div>');
22 - elem.after(success);
23 - success.slideDown(400, function () {
24 - setTimeout(function () {
25 - success.slideUp(400, function () {
26 - success.remove();
27 - })
28 - }, 2000);
29 - });
30 -};
31 -
32 -// Show a failure messages from laravel. Searches for the name of the inputs.
33 -jQuery.fn.showFailure = function (messageMap) {
34 - var elem = $(this);
35 - $.each(messageMap, function (key, messages) {
36 - var input = elem.find('[name="' + key + '"]').last();
37 - var fail = $('<div class="text-neg" style="display:none;"><i class="zmdi zmdi-alert-circle"></i>' + messages.join("\n") + '</div>');
38 - input.after(fail);
39 - fail.slideDown(400, function () {
40 - setTimeout(function () {
41 - fail.slideUp(400, function () {
42 - fail.remove();
43 - })
44 - }, 2000);
45 - });
46 - });
47 -
48 -};
49 -
50 -// Submit the form that the called upon element sits in.
51 -jQuery.fn.submitForm = function() {
52 - $(this).closest('form').submit();
53 -};
54 -
55 -// Dropdown menu display
56 -jQuery.fn.dropDown = function() {
57 - var container = $(this),
58 - menu = container.find('ul');
59 - container.find('[data-dropdown-toggle]').on('click', function() {
60 - menu.show().addClass('anim menuIn');
61 - container.mouseleave(function() {
62 - menu.hide();
63 - menu.removeClass('anim menuIn');
64 - });
65 - });
66 -};
...\ No newline at end of file ...\ No newline at end of file
1 -
2 -module.exports = {
3 - el: '#book-dashboard',
4 - data: {
5 - searching: false,
6 - searchTerm: '',
7 - searchResults: ''
8 - },
9 - methods: {
10 - searchBook: function (e) {
11 - e.preventDefault();
12 - var term = this.searchTerm;
13 - if (term.length == 0) return;
14 - this.searching = true;
15 - this.searchResults = '';
16 - var searchUrl = this.$els.form.getAttribute('action');
17 - searchUrl += '?term=' + encodeURIComponent(term);
18 - this.$http.get(searchUrl, function (data) {
19 - this.$set('searchResults', data);
20 - });
21 - },
22 - checkSearchForm: function (e) {
23 - if (this.searchTerm.length < 1) {
24 - this.searching = false;
25 - }
26 - },
27 - clearSearch: function(e) {
28 - this.searching = false;
29 - this.searchTerm = '';
30 - }
31 - }
32 -};
...\ No newline at end of file ...\ No newline at end of file
...@@ -98,7 +98,7 @@ module.exports = { ...@@ -98,7 +98,7 @@ module.exports = {
98 icon: 'image', 98 icon: 'image',
99 tooltip: 'Insert an image', 99 tooltip: 'Insert an image',
100 onclick: function() { 100 onclick: function() {
101 - ImageManager.show(function(image) { 101 + window.ImageManager.showExternal(function(image) {
102 var html = '<a href="'+image.url+'" target="_blank">'; 102 var html = '<a href="'+image.url+'" target="_blank">';
103 html += '<img src="'+image.thumbs.display+'" alt="'+image.name+'">'; 103 html += '<img src="'+image.thumbs.display+'" alt="'+image.name+'">';
104 html += '</a>'; 104 html += '</a>';
...@@ -106,6 +106,7 @@ module.exports = { ...@@ -106,6 +106,7 @@ module.exports = {
106 }); 106 });
107 } 107 }
108 }); 108 });
109 +
109 // Paste image-uploads 110 // Paste image-uploads
110 editor.on('paste', function(e) { 111 editor.on('paste', function(e) {
111 if(e.clipboardData) { 112 if(e.clipboardData) {
......
1 +"use strict";
2 +
3 +module.exports = function(ngApp) {
4 +
5 + ngApp.factory('imageManagerService', function() {
6 + return {
7 + show: false,
8 + showExternal: false
9 + };
10 + });
11 +
12 +};
...\ No newline at end of file ...\ No newline at end of file
1 .overlay { 1 .overlay {
2 background-color: rgba(0, 0, 0, 0.2); 2 background-color: rgba(0, 0, 0, 0.2);
3 position: fixed; 3 position: fixed;
4 - display: none;
5 z-index: 95536; 4 z-index: 95536;
6 width: 100%; 5 width: 100%;
7 height: 100%; 6 height: 100%;
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
18 18
19 [v-cloak], [v-show] {display: none;} 19 [v-cloak], [v-show] {display: none;}
20 20
21 +[ng\:cloak], [ng-cloak], .ng-cloak {
22 + display: none !important;
23 +}
24 +
21 // Jquery Sortable Styles 25 // Jquery Sortable Styles
22 .dragged { 26 .dragged {
23 position: absolute; 27 position: absolute;
......
...@@ -52,8 +52,8 @@ ...@@ -52,8 +52,8 @@
52 @endif 52 @endif
53 </div> 53 </div>
54 @if($signedIn) 54 @if($signedIn)
55 - <div class="dropdown-container" data-dropdown> 55 + <div class="dropdown-container" dropdown>
56 - <span class="user-name" data-dropdown-toggle> 56 + <span class="user-name" dropdown-toggle>
57 <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}"> 57 <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
58 <span class="name">{{ $currentUser->name }}</span> <i class="zmdi zmdi-caret-down"></i> 58 <span class="name">{{ $currentUser->name }}</span> <i class="zmdi zmdi-caret-down"></i>
59 </span> 59 </span>
......
...@@ -27,15 +27,15 @@ ...@@ -27,15 +27,15 @@
27 </div> 27 </div>
28 28
29 29
30 - <div class="container" id="book-dashboard"> 30 + <div class="container" id="book-dashboard" ng-controller="BookShowController" book-id="{{ $book->id }}">
31 <div class="row"> 31 <div class="row">
32 <div class="col-md-7"> 32 <div class="col-md-7">
33 33
34 <h1>{{$book->name}}</h1> 34 <h1>{{$book->name}}</h1>
35 - <div class="book-content anim fadeIn" v-show="!searching"> 35 + <div class="book-content" ng-show="!searching">
36 - <p class="text-muted">{{$book->description}}</p> 36 + <p class="text-muted" ng-non-bindable>{{$book->description}}</p>
37 37
38 - <div class="page-list"> 38 + <div class="page-list" ng-non-bindable>
39 <hr> 39 <hr>
40 @if(count($bookChildren) > 0) 40 @if(count($bookChildren) > 0)
41 @foreach($bookChildren as $childElement) 41 @foreach($bookChildren as $childElement)
...@@ -62,12 +62,12 @@ ...@@ -62,12 +62,12 @@
62 </p> 62 </p>
63 </div> 63 </div>
64 </div> 64 </div>
65 - <div class="search-results" v-show="searching"> 65 + <div class="search-results" ng-cloak ng-show="searching">
66 - <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> 66 + <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>
67 - <div v-if="!searchResults"> 67 + <div ng-if="!searchResults">
68 @include('partials/loading-icon') 68 @include('partials/loading-icon')
69 </div> 69 </div>
70 - <div v-html="searchResults"></div> 70 + <div ng-bind-html="searchResults"></div>
71 </div> 71 </div>
72 72
73 73
...@@ -76,11 +76,10 @@ ...@@ -76,11 +76,10 @@
76 <div class="col-md-4 col-md-offset-1"> 76 <div class="col-md-4 col-md-offset-1">
77 <div class="margin-top large"></div> 77 <div class="margin-top large"></div>
78 <div class="search-box"> 78 <div class="search-box">
79 - <form @submit="searchBook" @input="checkSearchForm" v-el:form action="/search/book/{{ $book->id }}"> 79 + <form ng-submit="searchBook($event)">
80 - {!! csrf_field() !!} 80 + <input ng-model="searchTerm" ng-change="checkSearchForm()" type="text" name="term" placeholder="Search This Book">
81 - <input v-model="searchTerm" type="text" name="term" placeholder="Search This Book">
82 <button type="submit"><i class="zmdi zmdi-search"></i></button> 81 <button type="submit"><i class="zmdi zmdi-search"></i></button>
83 - <button v-if="searching" @click="clearSearch" type="button"><i class="zmdi zmdi-close"></i></button> 82 + <button ng-if="searching" ng-click="clearSearch()" type="button"><i class="zmdi zmdi-close"></i></button>
84 </form> 83 </form>
85 </div> 84 </div>
86 <div class="activity anim fadeIn"> 85 <div class="activity anim fadeIn">
......
1 @extends('base') 1 @extends('base')
2 2
3 +@section('head')
4 + <script src="/libs/jquery-sortable/jquery-sortable.min.js"></script>
5 +@stop
6 +
3 @section('content') 7 @section('content')
4 8
5 <div class="container"> 9 <div class="container">
......
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
16 @endif 16 @endif
17 </form> 17 </form>
18 </div> 18 </div>
19 - <image-manager image-type="gallery"></image-manager> 19 + @include('partials/image-manager', ['imageType' => 'gallery'])
20 @stop 20 @stop
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
14 @include('pages/form', ['model' => $page]) 14 @include('pages/form', ['model' => $page])
15 </form> 15 </form>
16 </div> 16 </div>
17 - <image-manager image-type="gallery"></image-manager> 17 + @include('partials/image-manager', ['imageType' => 'gallery'])
18 18
19 @stop 19 @stop
...\ No newline at end of file ...\ No newline at end of file
......
1 -<div v-pre> 1 +<div ng-non-bindable>
2 <h1 id="bkmrk-page-title">{{$page->name}}</h1> 2 <h1 id="bkmrk-page-title">{{$page->name}}</h1>
3 3
4 {!! $page->html !!} 4 {!! $page->html !!}
......
1 +<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController">
2 + <div class="overlay anim-slide" ng-show="showing" ng-cloak ng-click="hide()">
3 + <div class="image-manager-body" ng-click="$event.stopPropagation()">
4 +
5 + <div class="image-manager-content">
6 + <div class="image-manager-list">
7 + <div ng-repeat="image in images">
8 + <img class="anim fadeIn"
9 + ng-class="{selected: (image==selectedImage)}"
10 + ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}"
11 + ng-click="imageSelect(image)"
12 + ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
13 + </div>
14 + <div class="load-more" ng-show="hasMore" ng-click="fetchData()">Load More</div>
15 + </div>
16 + </div>
17 +
18 + <button class="neg button image-manager-close" ng-click="hide()">x</button>
19 +
20 + <div class="image-manager-sidebar">
21 + <h2>Images</h2>
22 + <hr class="even">
23 + <drop-zone upload-url="@{{getUploadUrl()}}" event-success="uploadSuccess"></drop-zone>
24 + <div class="image-manager-details anim fadeIn" ng-show="selectedImage">
25 +
26 + <hr class="even">
27 +
28 + <form ng-submit="saveImageDetails($event)">
29 + <div class="form-group">
30 + <label for="name">Image Name</label>
31 + <input type="text" id="name" name="name" ng-model="selectedImage.name">
32 + <p class="text-pos text-small" ng-show="imageUpdateSuccess"><i class="fa fa-check"></i> Image name updated</p>
33 + <p class="text-neg text-small" ng-show="imageUpdateFailure"><i class="fa fa-times"></i> <span ng-bind="imageUpdateFailure"></span></p>
34 + </div>
35 + </form>
36 +
37 + <hr class="even">
38 +
39 + <div ng-show="dependantPages">
40 + <p class="text-neg text-small">
41 + This image is used in the pages below, Click delete again to confirm you want to delete
42 + this image.
43 + </p>
44 + <ul class="text-neg">
45 + <li ng-repeat="page in dependantPages">
46 + <a ng-href="@{{ page.url }}" target="_blank" class="text-neg" ng-bind="page.name"></a>
47 + </li>
48 + </ul>
49 + </div>
50 +
51 + <form ng-submit="deleteImage($event)">
52 + <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
53 + </form>
54 + </div>
55 +
56 + <p class="text-pos" ng-show="imageDeleteSuccess"><i class="fa fa-check"></i> Image deleted</p>
57 +
58 + <div class="image-manager-bottom">
59 + <button class="button pos anim fadeIn" ng-show="selectedImage" ng-click="selectButtonClick()">
60 + <i class="zmdi zmdi-square-right"></i>Select Image
61 + </button>
62 + </div>
63 +
64 + </div>
65 + </div>
66 + </div>
67 +</div>
...\ No newline at end of file ...\ No newline at end of file
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
33 <div class="form-group" id="logo-control"> 33 <div class="form-group" id="logo-control">
34 <label for="setting-app-logo">Application Logo</label> 34 <label for="setting-app-logo">Application Logo</label>
35 <p class="small">This image should be 43px in height. <br>Large images will be scaled down.</p> 35 <p class="small">This image should be 43px in height. <br>Large images will be scaled down.</p>
36 - <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> 36 + <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>
37 </div> 37 </div>
38 </div> 38 </div>
39 </div> 39 </div>
...@@ -86,6 +86,6 @@ ...@@ -86,6 +86,6 @@
86 86
87 </div> 87 </div>
88 88
89 -<image-manager image-type="system"></image-manager> 89 +@include('partials/image-manager', ['imageType' => 'system'])
90 90
91 @stop 91 @stop
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
33 <div class="form-group" id="logo-control"> 33 <div class="form-group" id="logo-control">
34 <label for="user-avatar">User Avatar</label> 34 <label for="user-avatar">User Avatar</label>
35 <p class="small">This image should be approx 256px square.</p> 35 <p class="small">This image should be approx 256px square.</p>
36 - <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> 36 + <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>
37 </div> 37 </div>
38 </div> 38 </div>
39 </div> 39 </div>
...@@ -79,5 +79,5 @@ ...@@ -79,5 +79,5 @@
79 </div> 79 </div>
80 80
81 <p class="margin-top large"><br></p> 81 <p class="margin-top large"><br></p>
82 - <image-manager image-type="user"></image-manager> 82 + @include('partials/image-manager', ['imageType' => 'user'])
83 @stop 83 @stop
......