@webhandle/tree-file-browser 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -0
- package/client-lib/base-image-name.mjs +41 -0
- package/client-lib/data-to-image.mjs +45 -0
- package/client-lib/dynamic-load.mjs +21 -0
- package/client-lib/file-select-dialog.mjs +70 -0
- package/client-lib/form-answer-dialog.mjs +65 -0
- package/client-lib/format-bytes.mjs +10 -0
- package/client-lib/get-extension-from-mime.mjs +17 -0
- package/client-lib/get-file-image-stats.mjs +17 -0
- package/client-lib/get-image-stats.mjs +11 -0
- package/client-lib/image-browser-view-methods/create-directory.mjs +23 -0
- package/client-lib/image-browser-view-methods/delete.mjs +100 -0
- package/client-lib/image-browser-view-methods/drag-and-drop.mjs +57 -0
- package/client-lib/image-browser-view-methods/file-obj-manipulation.mjs +190 -0
- package/client-lib/image-browser-view-methods/sink.mjs +20 -0
- package/client-lib/image-browser-view-methods/upload.mjs +159 -0
- package/client-lib/image-browser-view-methods/utils.mjs +108 -0
- package/client-lib/image-browser-view-methods/view-interactions.mjs +96 -0
- package/client-lib/image-browser-view.mjs +227 -0
- package/client-lib/image-resize.mjs +32 -0
- package/client-lib/info-dialog.mjs +36 -0
- package/client-lib/load-styles.mjs +14 -0
- package/client-lib/make-image-set.mjs +75 -0
- package/client-lib/name-parts.mjs +24 -0
- package/client-lib/styles-loaded.mjs +18 -0
- package/less/components.less +357 -0
- package/package.json +80 -0
- package/public/tree-file-browser/resources/css/tree-browser.css +394 -0
- package/public/tree-file-browser/resources/css/tree-browser.css.map +1 -0
- package/public/tree-file-browser/resources/js/tree-file-browser.js +2 -0
- package/public/tree-file-browser/resources/js/tree-file-browser.js.map +1 -0
- package/server-lib/initialize-tree-browser-resources.cjs +17 -0
- package/server-lib/initialize-tree-browser-resources.mjs +15 -0
- package/views/load-browser-views.js +13 -0
- package/views/webhandle-tree-image-browser/extension-pill.tri +1 -0
- package/views/webhandle-tree-image-browser/guilded-file-upload-form.tri +11 -0
- package/views/webhandle-tree-image-browser/guilded-image-upload-form.tri +33 -0
- package/views/webhandle-tree-image-browser/image-browser-frame.tri +122 -0
- package/views/webhandle-tree-image-browser/variant-choice-box.tri +25 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { View } from '@webhandle/backbone-view'
|
|
2
|
+
import { imageBrowserFrame, variantChoiceBox } from '../views/load-browser-views.js'
|
|
3
|
+
import KalpaTreeOnPage from 'kalpa-tree-on-page'
|
|
4
|
+
import formatBytes from './format-bytes.mjs'
|
|
5
|
+
|
|
6
|
+
import Emitter from '@webhandle/minimal-browser-event-emitter'
|
|
7
|
+
|
|
8
|
+
// method imports
|
|
9
|
+
import { deleteFile, deleteDirectory } from './image-browser-view-methods/delete.mjs'
|
|
10
|
+
import {
|
|
11
|
+
_join, _determineParentPath, _fileToKalpaNode, _determineExtensions,
|
|
12
|
+
_determineSizes, _sortFiles, _compareVariants, sanitizeFileName, _isImageFile
|
|
13
|
+
} from './image-browser-view-methods/utils.mjs'
|
|
14
|
+
import { changeFilesView, applyFilter, clearFilter, selectVariant, showVariantDetails } from './image-browser-view-methods/view-interactions.mjs'
|
|
15
|
+
import { getDropCoverSelector, handleDrop, isFileTypeDrag, dragEnter, dragLeave, dragOver, _cleanupDropDone } from './image-browser-view-methods/drag-and-drop.mjs'
|
|
16
|
+
import { createDirectory } from './image-browser-view-methods/create-directory.mjs'
|
|
17
|
+
import { createVariantValues, _getFilesFromEvent, _getAssociatedRealFiles, _createAccessUrl,
|
|
18
|
+
getSelectedFiles, _transformRelativeUrlToPublic, getSelectedUrl } from './image-browser-view-methods/file-obj-manipulation.mjs'
|
|
19
|
+
import { _uploadGuidedImageFile, _uploadGuidedFile, _uploadAutomaticImageFile, uploadFiles, _uploadFileButton, _uploadFile } from './image-browser-view-methods/upload.mjs'
|
|
20
|
+
import { _uploadData, findDirectories } from './image-browser-view-methods/sink.mjs'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
export default class ImageBrowserView extends View {
|
|
24
|
+
/**
|
|
25
|
+
* Construct a new file browser
|
|
26
|
+
* @param {object} options
|
|
27
|
+
* @param {FileSink} options.sink The file to use as a file source
|
|
28
|
+
* @param {boolean} [options.imagesOnly] Set to true if you would like to display only images
|
|
29
|
+
* @param {boolean} [options.allowFileSelection] Set to true so that selected files are marked
|
|
30
|
+
* @param {EventNotificationPanel} [options.eventNotificationPanel] The panel which status messages will be added to.
|
|
31
|
+
* @param {string} [options.startingDirectory] Opens to that directory path if it exists
|
|
32
|
+
* @param {boolean} [options.deleteWithoutConfirm] False by default
|
|
33
|
+
* @param {boolean} [options.ignoreGlobalEvents] False by default, if true it will not listen to events like paste or keypresses
|
|
34
|
+
* which occur on the document
|
|
35
|
+
* @param {Emitter} [options.emitter] Emitter for various file events
|
|
36
|
+
*/
|
|
37
|
+
constructor(options) {
|
|
38
|
+
super(options)
|
|
39
|
+
}
|
|
40
|
+
preinitialize() {
|
|
41
|
+
this.className = 'image-browser'
|
|
42
|
+
this.idInd = 1
|
|
43
|
+
this.nodes = {}
|
|
44
|
+
this.events = {
|
|
45
|
+
'click .create-directory': 'createDirectory'
|
|
46
|
+
, 'click .upload-file': '_uploadFileButton'
|
|
47
|
+
, 'change [name="fileUpload"]': '_uploadFile'
|
|
48
|
+
, 'click .delete-file': 'deleteFile'
|
|
49
|
+
, 'click .delete-directory': 'deleteDirectory'
|
|
50
|
+
, 'click .variant-choice-box .details': 'showVariantDetails'
|
|
51
|
+
, 'dblclick .variant-choice-box': 'showVariantDetails'
|
|
52
|
+
, 'click .variant-choice-box': 'selectVariant'
|
|
53
|
+
, 'click .view-icons button': 'changeFilesView'
|
|
54
|
+
, 'keyup [name="filter"]': 'applyFilter'
|
|
55
|
+
, 'change [name="filter"]': 'applyFilter'
|
|
56
|
+
, 'click .clear-filter': 'clearFilter'
|
|
57
|
+
, 'dragenter .': 'dragEnter'
|
|
58
|
+
, 'dragleave .': 'dragLeave'
|
|
59
|
+
, 'dragover .': 'dragOver'
|
|
60
|
+
, 'drop .': 'handleDrop'
|
|
61
|
+
}
|
|
62
|
+
this.overCount = 0
|
|
63
|
+
|
|
64
|
+
if (!this.emitter) {
|
|
65
|
+
this.emitter = new Emitter()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.fileUploadSelector = 'input[name="fileUpload"]'
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
document.addEventListener('paste', this.handlePaste.bind(this))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async handlePaste(evt) {
|
|
75
|
+
if(this.ignoreGlobalEvents) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
evt.preventDefault()
|
|
79
|
+
if(evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length > 0) {
|
|
80
|
+
this.uploadFiles(evt.clipboardData.files, { uploadType: 'guided' })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
_addPending(file) {
|
|
86
|
+
let note
|
|
87
|
+
if (this.eventNotificationPanel) {
|
|
88
|
+
note = this.eventNotificationPanel.addNotification({
|
|
89
|
+
model: {
|
|
90
|
+
status: 'pending',
|
|
91
|
+
headline: `uploading ${file.name}`
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
return note
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async render() {
|
|
99
|
+
this.el.innerHTML = imageBrowserFrame(this.model)
|
|
100
|
+
this.data = []
|
|
101
|
+
|
|
102
|
+
this.rootDirectory = await this.sink.getFullFileInfo('')
|
|
103
|
+
this.rootDirectory.name = "Files"
|
|
104
|
+
let rootNode = this.rootNode = this._fileToKalpaNode(this.rootDirectory)
|
|
105
|
+
this.data.push(rootNode)
|
|
106
|
+
|
|
107
|
+
let directories = await this.findDirectories()
|
|
108
|
+
this._sortFiles(directories)
|
|
109
|
+
|
|
110
|
+
this.data.push(...directories.map(this._fileToKalpaNode.bind(this)))
|
|
111
|
+
KalpaTreeOnPage({
|
|
112
|
+
treeContainerSelector: `#${this.id} .treebox`
|
|
113
|
+
, data: this.data
|
|
114
|
+
}).then(tree => {
|
|
115
|
+
this.tree = tree
|
|
116
|
+
tree.on('select', (node) => {
|
|
117
|
+
this.setCurrentNode(node)
|
|
118
|
+
})
|
|
119
|
+
tree.on('selected', (node) => {
|
|
120
|
+
// There's a bug, either in the browser or kalpa tree that causes it
|
|
121
|
+
// not to examine if a scroll bar is needed for the tree if the content
|
|
122
|
+
// area changes in a big way. Part of this bug may be that it's being
|
|
123
|
+
// used in a grid which has some weird width/height effects
|
|
124
|
+
// Anyway, we need to make sure the browser knows to examine the tree so
|
|
125
|
+
// we change the height then change it back.
|
|
126
|
+
// This event is triggered when kalpa-tree thinks it's done with transitions
|
|
127
|
+
|
|
128
|
+
let tree = this.el.querySelector('.tree')
|
|
129
|
+
tree.style.height = '99.99999%'
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
tree.style.height = '100%'
|
|
132
|
+
}, 100)
|
|
133
|
+
})
|
|
134
|
+
if (this.startingDirectory) {
|
|
135
|
+
for (let node of Object.values(this.tree.nodes)) {
|
|
136
|
+
if (node.file && node.file.relPath && node.file.relPath == this.startingDirectory) {
|
|
137
|
+
tree.select(node.id)
|
|
138
|
+
break
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
tree.select(1)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async setCurrentNode(node) {
|
|
149
|
+
this.currentNode = node
|
|
150
|
+
let info = await this.sink.getFullFileInfo(node.file.relPath)
|
|
151
|
+
let variantValues = this.createVariantValues(info)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
let content = ''
|
|
155
|
+
for (let child of variantValues) {
|
|
156
|
+
content += variantChoiceBox(child)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
let choicesBoxes = this.el.querySelector('.choice-boxes')
|
|
161
|
+
choicesBoxes.innerHTML = ''
|
|
162
|
+
choicesBoxes.insertAdjacentHTML('beforeend', content)
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < choicesBoxes.children.length; i++) {
|
|
165
|
+
choicesBoxes.children[i].variant = variantValues[i]
|
|
166
|
+
}
|
|
167
|
+
this.el.querySelector('.box-holder').scrollTop = 0
|
|
168
|
+
this.applyFilter()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// uploads
|
|
172
|
+
_uploadGuidedImageFile = _uploadGuidedImageFile
|
|
173
|
+
_uploadGuidedFile = _uploadGuidedFile
|
|
174
|
+
_uploadAutomaticImageFile = _uploadAutomaticImageFile
|
|
175
|
+
uploadFiles = uploadFiles
|
|
176
|
+
_uploadFileButton = _uploadFileButton
|
|
177
|
+
_uploadFile = _uploadFile
|
|
178
|
+
|
|
179
|
+
// file-obj-manipulation
|
|
180
|
+
createVariantValues = createVariantValues
|
|
181
|
+
_getFilesFromEvent = _getFilesFromEvent
|
|
182
|
+
_getAssociatedRealFiles = _getAssociatedRealFiles
|
|
183
|
+
_createAccessUrl = _createAccessUrl
|
|
184
|
+
getSelectedFiles = getSelectedFiles
|
|
185
|
+
_transformRelativeUrlToPublic = _transformRelativeUrlToPublic
|
|
186
|
+
getSelectedUrl = getSelectedUrl
|
|
187
|
+
|
|
188
|
+
// create-directory
|
|
189
|
+
createDirectory = createDirectory
|
|
190
|
+
|
|
191
|
+
// drag-and-drop
|
|
192
|
+
getDropCoverSelector = getDropCoverSelector
|
|
193
|
+
handleDrop = handleDrop
|
|
194
|
+
isFileTypeDrag = isFileTypeDrag
|
|
195
|
+
dragEnter = dragEnter
|
|
196
|
+
dragLeave = dragLeave
|
|
197
|
+
dragOver = dragOver
|
|
198
|
+
_cleanupDropDone = _cleanupDropDone
|
|
199
|
+
|
|
200
|
+
// view-interactions
|
|
201
|
+
changeFilesView = changeFilesView
|
|
202
|
+
applyFilter = applyFilter
|
|
203
|
+
clearFilter = clearFilter
|
|
204
|
+
selectVariant = selectVariant
|
|
205
|
+
showVariantDetails = showVariantDetails
|
|
206
|
+
|
|
207
|
+
// utils
|
|
208
|
+
sanitizeFileName = sanitizeFileName
|
|
209
|
+
_sortFiles = _sortFiles
|
|
210
|
+
_compareVariants = _compareVariants
|
|
211
|
+
_determineExtensions = _determineExtensions
|
|
212
|
+
_determineSizes = _determineSizes
|
|
213
|
+
_join = _join
|
|
214
|
+
_determineParentPath = _determineParentPath
|
|
215
|
+
_fileToKalpaNode = _fileToKalpaNode
|
|
216
|
+
_formatBytes = formatBytes
|
|
217
|
+
_isImageFile = _isImageFile
|
|
218
|
+
|
|
219
|
+
// delete
|
|
220
|
+
deleteFile = deleteFile
|
|
221
|
+
deleteDirectory = deleteDirectory
|
|
222
|
+
|
|
223
|
+
// sink
|
|
224
|
+
_uploadData = _uploadData
|
|
225
|
+
findDirectories = findDirectories
|
|
226
|
+
|
|
227
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import dataToImage from "./data-to-image.mjs"
|
|
2
|
+
|
|
3
|
+
/*image, base64 encoded data of image, blob, file, or arraybuffer */
|
|
4
|
+
export default async function resizeImage(imageData,
|
|
5
|
+
{ maxWidth = 1920, quality = .7, outputFormat = "image/png" } = {}) {
|
|
6
|
+
return new Promise(async (resolve, reject) => {
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
let resizeImg = await dataToImage(imageData)
|
|
10
|
+
let ratio = resizeImg.naturalWidth / maxWidth
|
|
11
|
+
let maxHeight = resizeImg.naturalHeight / ratio
|
|
12
|
+
|
|
13
|
+
var resizeCanvas = document.createElement('canvas');
|
|
14
|
+
resizeCanvas.width = maxWidth;
|
|
15
|
+
resizeCanvas.height = maxHeight;
|
|
16
|
+
|
|
17
|
+
var ctx = resizeCanvas.getContext('2d');
|
|
18
|
+
ctx.clearRect(0, 0, resizeCanvas.width, resizeCanvas.height);
|
|
19
|
+
ctx.drawImage(resizeImg, 0, 0, resizeCanvas.width, resizeCanvas.height);
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
resizeCanvas.toBlob((blob) => {
|
|
23
|
+
resolve(blob)
|
|
24
|
+
}, outputFormat, quality);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
if (console && console.log) {
|
|
27
|
+
console.log('error resizing image: ' + e);
|
|
28
|
+
}
|
|
29
|
+
reject(e)
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Dialog from 'ei-dialog'
|
|
2
|
+
|
|
3
|
+
export class InfoDialog extends Dialog {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
super(Object.assign({}, options,
|
|
6
|
+
{
|
|
7
|
+
on: {
|
|
8
|
+
'.btn-ok': () => {
|
|
9
|
+
this.resolve()
|
|
10
|
+
return true
|
|
11
|
+
},
|
|
12
|
+
'.mask': () => {
|
|
13
|
+
this.resolve()
|
|
14
|
+
return true
|
|
15
|
+
},
|
|
16
|
+
'.btn-cancel': () => {
|
|
17
|
+
this.resolve()
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async open() {
|
|
27
|
+
this.promise = new Promise((resolve, reject) => {
|
|
28
|
+
this.resolve = resolve
|
|
29
|
+
this.reject = reject
|
|
30
|
+
})
|
|
31
|
+
super.open()
|
|
32
|
+
|
|
33
|
+
return this.promise
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import areStylesLoaded from "./styles-loaded.mjs";
|
|
2
|
+
import ensureIcons from '@dankolz/webhandle-admin-icons/client-js/ensure-styles-are-loaded.js'
|
|
3
|
+
|
|
4
|
+
export default function loadStyles() {
|
|
5
|
+
if(!areStylesLoaded()) {
|
|
6
|
+
let link = document.createElement('link')
|
|
7
|
+
link.href = '/@webhandle/tree-file-browser/resources/css/tree-browser.css'
|
|
8
|
+
link.rel = 'stylesheet'
|
|
9
|
+
document.head.appendChild(link)
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
ensureIcons()
|
|
13
|
+
|
|
14
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import resizeImage from '../client-lib/image-resize.mjs'
|
|
2
|
+
import dataToImage from '../client-lib/data-to-image.mjs'
|
|
3
|
+
import getImageStats from '../client-lib/get-image-stats.mjs'
|
|
4
|
+
import getExtension from './get-extension-from-mime.mjs'
|
|
5
|
+
|
|
6
|
+
let webpMime = 'image/webp'
|
|
7
|
+
|
|
8
|
+
export default async function makeImageSet(data,
|
|
9
|
+
{
|
|
10
|
+
singleDensityWidth = null
|
|
11
|
+
, quality = .7
|
|
12
|
+
, outputFormat = "image/png"
|
|
13
|
+
, doubleDensityInput = true
|
|
14
|
+
, baseFileName
|
|
15
|
+
, altText
|
|
16
|
+
} = {}
|
|
17
|
+
) {
|
|
18
|
+
let source = await dataToImage(data)
|
|
19
|
+
let stats = await getImageStats(source)
|
|
20
|
+
let ratio = stats.width / stats.height
|
|
21
|
+
|
|
22
|
+
if (!baseFileName) {
|
|
23
|
+
baseFileName = "" + (new Date().getTime())
|
|
24
|
+
}
|
|
25
|
+
if (!singleDensityWidth) {
|
|
26
|
+
if (doubleDensityInput) {
|
|
27
|
+
singleDensityWidth = stats.width / 2
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
singleDensityWidth = stats.width
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
let doubleDensityWidth = singleDensityWidth * 2
|
|
34
|
+
|
|
35
|
+
let suffixes = {
|
|
36
|
+
'-2x': doubleDensityWidth
|
|
37
|
+
, '': singleDensityWidth
|
|
38
|
+
, '-half': Math.floor(singleDensityWidth / 2)
|
|
39
|
+
, '-quarter': Math.floor(singleDensityWidth / 4)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let files = {}
|
|
43
|
+
|
|
44
|
+
for (let key of Object.keys(suffixes)) {
|
|
45
|
+
let width = suffixes[key]
|
|
46
|
+
let data = await resizeImage(source, {
|
|
47
|
+
maxWidth: width
|
|
48
|
+
, quality: quality
|
|
49
|
+
, outputFormat: outputFormat
|
|
50
|
+
})
|
|
51
|
+
files[baseFileName + key + '.' + getExtension(outputFormat)] = data
|
|
52
|
+
|
|
53
|
+
data = await resizeImage(source, {
|
|
54
|
+
maxWidth: width
|
|
55
|
+
, quality: quality
|
|
56
|
+
, outputFormat: webpMime
|
|
57
|
+
})
|
|
58
|
+
files[baseFileName + key + '.' + getExtension(webpMime)] = data
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
let info = {
|
|
63
|
+
"name": baseFileName,
|
|
64
|
+
"size": doubleDensityWidth + "x" + (doubleDensityWidth / ratio),
|
|
65
|
+
"displaySize": singleDensityWidth + "x" + (singleDensityWidth / ratio),
|
|
66
|
+
"fallback": getExtension(outputFormat),
|
|
67
|
+
"altText": altText || baseFileName
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
files[baseFileName + '.json'] = JSON.stringify(info, null, '\t')
|
|
72
|
+
|
|
73
|
+
return files
|
|
74
|
+
|
|
75
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import baseImageName from "./base-image-name.mjs"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {File,string} file
|
|
6
|
+
*/
|
|
7
|
+
export default function nameParts(file) {
|
|
8
|
+
let name
|
|
9
|
+
if(typeof file === 'string') {
|
|
10
|
+
name = file
|
|
11
|
+
}
|
|
12
|
+
else if(file instanceof File) {
|
|
13
|
+
name = file.name
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let result = [baseImageName(name)]
|
|
17
|
+
|
|
18
|
+
let parts = name.split('.')
|
|
19
|
+
if(parts.length > 1) {
|
|
20
|
+
result.push(parts.pop())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result
|
|
24
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the styles for the image browser have been loaded
|
|
4
|
+
*/
|
|
5
|
+
export default function areStylesLoaded() {
|
|
6
|
+
let d = document.createElement('div')
|
|
7
|
+
d.classList.add('webhandle-file-tree-image-browser-test')
|
|
8
|
+
|
|
9
|
+
d.style.position = 'absolute'
|
|
10
|
+
d.style.left = '-10000px'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
document.body.appendChild(d)
|
|
14
|
+
let color = window.getComputedStyle(d)['background-color']
|
|
15
|
+
d.remove()
|
|
16
|
+
|
|
17
|
+
return color === 'rgb(101, 105, 99)'
|
|
18
|
+
}
|