vue updates ^_^
							parent
							
								
									2d33b664b5
								
							
						
					
					
						commit
						e2077efbc6
					
				| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
.DS_Store
 | 
			
		||||
node_modules/
 | 
			
		||||
dist/
 | 
			
		||||
dist_dev/
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
yarn-error.log*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,21 +9,7 @@ var selectedItems = []
 | 
			
		|||
var overlay
 | 
			
		||||
var clickOverlay
 | 
			
		||||
 | 
			
		||||
// Removes an element, if exists, from an array
 | 
			
		||||
Array.prototype.removeElement = function (element) {
 | 
			
		||||
  var i = this.indexOf(element)
 | 
			
		||||
  if (i !== -1) {
 | 
			
		||||
    this.splice(i, 1)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Replaces an element inside an array by another
 | 
			
		||||
Array.prototype.replaceElement = function (oldElement, newElement) {
 | 
			
		||||
  var i = this.indexOf(oldElement)
 | 
			
		||||
  if (i !== -1) {
 | 
			
		||||
    this[i] = newElement
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sends a costum event to itself
 | 
			
		||||
Document.prototype.sendCostumEvent = function (text) {
 | 
			
		||||
| 
						 | 
				
			
			@ -37,34 +23,6 @@ Document.prototype.getCookie = function (name) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function getCSSRule (rules) {
 | 
			
		||||
  for (let i = 0; i < rules.length; i++) {
 | 
			
		||||
    rules[i] = rules[i].toLowerCase()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let result = null
 | 
			
		||||
  let find = Array.prototype.find
 | 
			
		||||
 | 
			
		||||
  find.call(document.styleSheets, styleSheet => {
 | 
			
		||||
    result = find.call(styleSheet.cssRules, cssRule => {
 | 
			
		||||
      let found = false
 | 
			
		||||
 | 
			
		||||
      if (cssRule instanceof CSSStyleRule) {
 | 
			
		||||
        for (let i = 0; i < rules.length; i++) {
 | 
			
		||||
          if (cssRule.selectorText.toLowerCase() === rules[i]) {
 | 
			
		||||
            found = true
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return found
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return result != null
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* * * * * * * * * * * * * * * *
 | 
			
		||||
 *                             *
 | 
			
		||||
| 
						 | 
				
			
			@ -127,106 +85,7 @@ buttons.setDone = function (name, success = true) {
 | 
			
		|||
 *            WEBDAV           *
 | 
			
		||||
 *                             *
 | 
			
		||||
 * * * * * * * * * * * * * * * */
 | 
			
		||||
var webdav = {}
 | 
			
		||||
 | 
			
		||||
webdav.convertURL = function (url) {
 | 
			
		||||
  return window.location.origin + url.replace(data.baseURL + '/', data.webdavURL + '/')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
webdav.move = function (oldLink, newLink) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    let destination = newLink.replace(data.baseURL + '/', data.webdavURL + '/')
 | 
			
		||||
 | 
			
		||||
    destination = window.location.origin + destination.substring(data.baseURL.length)
 | 
			
		||||
 | 
			
		||||
    request.open('MOVE', webdav.convertURL(oldLink), true)
 | 
			
		||||
    request.setRequestHeader('Destination', destination)
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 201 || request.status === 204) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
webdav.put = function (link, body, headers = {}) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open('PUT', webdav.convertURL(link), true)
 | 
			
		||||
 | 
			
		||||
    for (let key in headers) {
 | 
			
		||||
      request.setRequestHeader(key, headers[key])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status == 201) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send(body)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
webdav.propfind = function (link, body, headers = {}) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open('PROPFIND', webdav.convertURL(link), true)
 | 
			
		||||
 | 
			
		||||
    for (let key in headers) {
 | 
			
		||||
      request.setRequestHeader(key, headers[key])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status < 300) {
 | 
			
		||||
        resolve(request.responseText)
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send(body)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
webdav.delete = function (link) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open('DELETE', webdav.convertURL(link), true)
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 204) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
webdav.new = function (link) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open((link.endsWith('/') ? 'MKCOL' : 'PUT'), webdav.convertURL(link), true)
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 201) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* * * * * * * * * * * * * * * *
 | 
			
		||||
 *                             *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,80 +21,6 @@ var listing = {
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  itemDragStart: function (event) {
 | 
			
		||||
    let el = event.target
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < 5; i++) {
 | 
			
		||||
      if (!el.classList.contains('item')) {
 | 
			
		||||
        el = el.parentElement
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event.dataTransfer.setData('id', el.id)
 | 
			
		||||
    event.dataTransfer.setData('name', el.querySelector('.name').innerHTML)
 | 
			
		||||
  },
 | 
			
		||||
  itemDragOver: function (event) {
 | 
			
		||||
    event.preventDefault()
 | 
			
		||||
    let el = event.target
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < 5; i++) {
 | 
			
		||||
      if (!el.classList.contains('item')) {
 | 
			
		||||
        el = el.parentElement
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    el.style.opacity = 1
 | 
			
		||||
  },
 | 
			
		||||
  itemDrop: function (e) {
 | 
			
		||||
    e.preventDefault()
 | 
			
		||||
 | 
			
		||||
    let el = e.target,
 | 
			
		||||
      id = e.dataTransfer.getData('id'),
 | 
			
		||||
      name = e.dataTransfer.getData('name')
 | 
			
		||||
 | 
			
		||||
    if (id == '' || name == '') return
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < 5; i++) {
 | 
			
		||||
      if (!el.classList.contains('item')) {
 | 
			
		||||
        el = el.parentElement
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (el.id === id) return
 | 
			
		||||
 | 
			
		||||
    let oldLink = document.getElementById(id).dataset.url,
 | 
			
		||||
      newLink = el.dataset.url + name
 | 
			
		||||
 | 
			
		||||
    webdav.move(oldLink, newLink)
 | 
			
		||||
      .then(() => listing.reload())
 | 
			
		||||
      .catch(e => console.log(e))
 | 
			
		||||
  },
 | 
			
		||||
  documentDrop: function (event) {
 | 
			
		||||
    event.preventDefault()
 | 
			
		||||
    let dt = event.dataTransfer,
 | 
			
		||||
      files = dt.files,
 | 
			
		||||
      el = event.target,
 | 
			
		||||
      items = document.getElementsByClassName('item')
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < 5; i++) {
 | 
			
		||||
      if (el != null && !el.classList.contains('item')) {
 | 
			
		||||
        el = el.parentElement
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (files.length > 0) {
 | 
			
		||||
      if (el != null && el.classList.contains('item') && el.dataset.dir == 'true') {
 | 
			
		||||
        listing.handleFiles(files, el.querySelector('.name').innerHTML + '/')
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      listing.handleFiles(files, '')
 | 
			
		||||
    } else {
 | 
			
		||||
      Array.from(items).forEach(file => {
 | 
			
		||||
        file.style.opacity = 1
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  rename: function (event) {
 | 
			
		||||
    if (!selectedItems.length || selectedItems.length > 1) {
 | 
			
		||||
      return false
 | 
			
		||||
| 
						 | 
				
			
			@ -150,67 +76,9 @@ var listing = {
 | 
			
		|||
    return false
 | 
			
		||||
  },
 | 
			
		||||
  handleFiles: function (files, base) {
 | 
			
		||||
    buttons.setLoading('upload')
 | 
			
		||||
 | 
			
		||||
    let promises = []
 | 
			
		||||
 | 
			
		||||
    for (let file of files) {
 | 
			
		||||
      promises.push(webdav.put(window.location.pathname + base + file.name, file))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Promise.all(promises)
 | 
			
		||||
      .then(() => {
 | 
			
		||||
        listing.reload()
 | 
			
		||||
        buttons.setDone('upload')
 | 
			
		||||
      })
 | 
			
		||||
      .catch(e => {
 | 
			
		||||
        console.log(e)
 | 
			
		||||
        buttons.setDone('upload', false)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.unselectAll = function () {
 | 
			
		||||
  let items = document.getElementsByClassName('item')
 | 
			
		||||
  Array.from(items).forEach(link => {
 | 
			
		||||
    link.setAttribute('aria-selected', false)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  selectedItems = []
 | 
			
		||||
 | 
			
		||||
  listing.handleSelectionChange()
 | 
			
		||||
  return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.handleSelectionChange = function (event) {
 | 
			
		||||
  listing.redefineDownloadURLs()
 | 
			
		||||
 | 
			
		||||
  let selectedNumber = selectedItems.length,
 | 
			
		||||
    fileAction = document.getElementById('file-only')
 | 
			
		||||
 | 
			
		||||
  if (selectedNumber) {
 | 
			
		||||
    fileAction.classList.remove('disabled')
 | 
			
		||||
 | 
			
		||||
    if (selectedNumber > 1) {
 | 
			
		||||
      buttons.rename.classList.add('disabled')
 | 
			
		||||
      buttons.info.classList.add('disabled')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (selectedNumber == 1) {
 | 
			
		||||
      buttons.info.classList.remove('disabled')
 | 
			
		||||
      buttons.rename.classList.remove('disabled')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buttons.info.classList.remove('disabled')
 | 
			
		||||
  fileAction.classList.add('disabled')
 | 
			
		||||
  return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.redefineDownloadURLs = function () {
 | 
			
		||||
  let files = ''
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -228,28 +96,6 @@ listing.redefineDownloadURLs = function () {
 | 
			
		|||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.openItem = function (event) {
 | 
			
		||||
  window.location = event.currentTarget.dataset.url
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.selectItem = function (event) {
 | 
			
		||||
  let el = event.currentTarget
 | 
			
		||||
 | 
			
		||||
  if (selectedItems.length != 0) event.preventDefault()
 | 
			
		||||
  if (selectedItems.indexOf(el.id) == -1) {
 | 
			
		||||
    if (!event.ctrlKey && !listing.selectMultiple) listing.unselectAll()
 | 
			
		||||
 | 
			
		||||
    el.setAttribute('aria-selected', true)
 | 
			
		||||
    selectedItems.push(el.id)
 | 
			
		||||
  } else {
 | 
			
		||||
    el.setAttribute('aria-selected', false)
 | 
			
		||||
    selectedItems.removeElement(el.id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  listing.handleSelectionChange()
 | 
			
		||||
  return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.newFileButton = function (event) {
 | 
			
		||||
  event.preventDefault()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -284,13 +130,6 @@ listing.newFilePrompt = function (event) {
 | 
			
		|||
  return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.updateColumns = function (event) {
 | 
			
		||||
  let columns = Math.floor(document.getElementById('listing').offsetWidth / 300),
 | 
			
		||||
    items = getCSSRule(['#listing.mosaic .item', '.mosaic#listing .item'])
 | 
			
		||||
 | 
			
		||||
  items.style.width = `calc(${100/columns}% - 1em)`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
listing.addDoubleTapEvent = function () {
 | 
			
		||||
  let items = document.getElementsByClassName('item'),
 | 
			
		||||
    touches = {
 | 
			
		||||
| 
						 | 
				
			
			@ -343,10 +182,6 @@ window.addEventListener('keydown', (event) => {
 | 
			
		|||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
window.addEventListener('resize', () => {
 | 
			
		||||
  listing.updateColumns()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
listing.selectMoveFolder = function (event) {
 | 
			
		||||
  if (event.target.getAttribute('aria-selected') === 'true') {
 | 
			
		||||
    event.target.setAttribute('aria-selected', false)
 | 
			
		||||
| 
						 | 
				
			
			@ -542,25 +377,6 @@ document.addEventListener('DOMContentLoaded', event => {
 | 
			
		|||
      document.getElementById('upload-input').click()
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    buttons.new.addEventListener('click', listing.newFileButton)
 | 
			
		||||
 | 
			
		||||
    // Drag and Drop
 | 
			
		||||
    document.addEventListener('dragover', function (event) {
 | 
			
		||||
      event.preventDefault()
 | 
			
		||||
    }, false)
 | 
			
		||||
 | 
			
		||||
    document.addEventListener('dragenter', (event) => {
 | 
			
		||||
      Array.from(items).forEach(file => {
 | 
			
		||||
        file.style.opacity = 0.5
 | 
			
		||||
      })
 | 
			
		||||
    }, false)
 | 
			
		||||
 | 
			
		||||
    document.addEventListener('dragend', (event) => {
 | 
			
		||||
      Array.from(items).forEach(file => {
 | 
			
		||||
        file.style.opacity = 1
 | 
			
		||||
      })
 | 
			
		||||
    }, false)
 | 
			
		||||
 | 
			
		||||
    document.addEventListener('drop', listing.documentDrop, false)
 | 
			
		||||
    buttons.new.addEventListener('click', listing.newFileButton)    
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
{{ define "item" }}
 | 
			
		||||
<div ondragstart="listing.itemDragStart(event)" 
 | 
			
		||||
    {{ if .IsDir}}ondragover="listing.itemDragOver(event)" ondrop="listing.itemDrop(event)"{{ end }} 
 | 
			
		||||
 | 
			
		||||
    onclick="listing.selectItem(event)"
 | 
			
		||||
    ondblclick="listing.openItem(event)"
 | 
			
		||||
    data-dir="{{ .IsDir }}" 
 | 
			
		||||
    data-url="{{ .URL }}">
 | 
			
		||||
    
 | 
			
		||||
    <div>
 | 
			
		||||
 | 
			
		||||
        <p class="modified">
 | 
			
		||||
        <time datetime="{{.HumanModTime "2006-01-02T15:04:05Z"}}">{{.HumanModTime "2 Jan 2006 03:04 PM"}}</time>
 | 
			
		||||
        </p>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
{{ end }}
 | 
			
		||||
| 
						 | 
				
			
			@ -21,9 +21,9 @@
 | 
			
		|||
    <meta name="msapplication-TileImage" content="{{ .BaseURL }}/_/img/icons/msapplication-icon-144x144.png">
 | 
			
		||||
    <meta name="msapplication-TileColor" content="#000000">
 | 
			
		||||
    
 | 
			
		||||
    <link rel="preload" href="{{ .BaseURL }}/_/js/app.3746baae0d5525e95c4e.js" as="script">
 | 
			
		||||
    <link rel="preload" href="{{ .BaseURL }}/_/js/vendor.89d687258dcaf46efc29.js" as="script">
 | 
			
		||||
    <link rel="preload" href="{{ .BaseURL }}/_/js/manifest.83ada933c74b4b19b61c.js" as="script">
 | 
			
		||||
    <link rel="preload" href="{{ .BaseURL }}/_/js/vendor.429043e9736e4450f715.js" as="script">
 | 
			
		||||
    <link rel="preload" href="{{ .BaseURL }}/_/js/app.fcde30b3eb6e988cd456.js" as="script">
 | 
			
		||||
    <link rel="preload" href="{{ .BaseURL }}/_/js/manifest.f2713ae24188290498c4.js" as="script">
 | 
			
		||||
  
 | 
			
		||||
    
 | 
			
		||||
    {{- if ne .User.StyleSheet "" -}}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,13 +32,12 @@
 | 
			
		|||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <script>
 | 
			
		||||
    var data = {
 | 
			
		||||
    var info = {
 | 
			
		||||
        user: JSON.parse('{{ Marshal .User }}'),
 | 
			
		||||
        page: JSON.parse('{{ Marshal . }}'),
 | 
			
		||||
        webdavURL: "{{ .WebDavURL }}",
 | 
			
		||||
        baseURL: "{{.BaseURL}}"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var page = JSON.parse('{{ Marshal . }}')
 | 
			
		||||
    </script>
 | 
			
		||||
    <div id="app">
 | 
			
		||||
      <!-- TODO: loading -->
 | 
			
		||||
| 
						 | 
				
			
			@ -61,5 +60,5 @@ self.addEventListener('activate', () => {
 | 
			
		|||
    }
 | 
			
		||||
  });
 | 
			
		||||
});</script>
 | 
			
		||||
  <script type="text/javascript" src="{{ .BaseURL }}/_/js/manifest.83ada933c74b4b19b61c.js"></script><script type="text/javascript" src="{{ .BaseURL }}/_/js/vendor.89d687258dcaf46efc29.js"></script><script type="text/javascript" src="{{ .BaseURL }}/_/js/app.3746baae0d5525e95c4e.js"></script></body>
 | 
			
		||||
  <script type="text/javascript" src="{{ .BaseURL }}/_/js/manifest.f2713ae24188290498c4.js"></script><script type="text/javascript" src="{{ .BaseURL }}/_/js/vendor.429043e9736e4450f715.js"></script><script type="text/javascript" src="{{ .BaseURL }}/_/js/app.fcde30b3eb6e988cd456.js"></script></body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,13 +32,12 @@
 | 
			
		|||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <script>
 | 
			
		||||
    var data = {
 | 
			
		||||
    var info = {
 | 
			
		||||
        user: JSON.parse('{{ Marshal .User }}'),
 | 
			
		||||
        page: JSON.parse('{{ Marshal . }}'),
 | 
			
		||||
        webdavURL: "{{ .WebDavURL }}",
 | 
			
		||||
        baseURL: "{{.BaseURL}}"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var page = JSON.parse('{{ Marshal . }}')
 | 
			
		||||
    </script>
 | 
			
		||||
    <div id="app">
 | 
			
		||||
      <!-- TODO: loading -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
        <div id="click-overlay"></div>
 | 
			
		||||
    </header>
 | 
			
		||||
    <nav id="sidebar">
 | 
			
		||||
      <a class="action" :href="baseURL()">
 | 
			
		||||
      <a class="action" :href="baseURL + '/'">
 | 
			
		||||
        <i class="material-icons">folder</i>
 | 
			
		||||
        <span>My Files</span>
 | 
			
		||||
      </a>
 | 
			
		||||
| 
						 | 
				
			
			@ -21,10 +21,10 @@
 | 
			
		|||
      </div>
 | 
			
		||||
    </nav>
 | 
			
		||||
    <main>
 | 
			
		||||
      <listing v-if="Kind == 'listing'"></listing> 
 | 
			
		||||
      <listing v-if="page.kind == 'listing'"></listing> 
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <preview v-if="Kind == 'preview'"></preview> 
 | 
			
		||||
    <preview v-if="page.kind == 'preview'"></preview> 
 | 
			
		||||
 | 
			
		||||
    <div class="overlay"></div>
 | 
			
		||||
    <!-- TODO: show on listing and allowedit -->
 | 
			
		||||
| 
						 | 
				
			
			@ -50,17 +50,24 @@
 | 
			
		|||
import Search from './components/Search'
 | 
			
		||||
import Preview from './components/Preview'
 | 
			
		||||
import Listing from './components/Listing'
 | 
			
		||||
import css from './css.js'
 | 
			
		||||
 | 
			
		||||
function updateColumnSizes () {
 | 
			
		||||
  let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
 | 
			
		||||
  let items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
 | 
			
		||||
 | 
			
		||||
  items.style.width = `calc(${100 / columns}% - 1em)`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'app',
 | 
			
		||||
  components: { Search, Preview, Listing },
 | 
			
		||||
  data: function () {
 | 
			
		||||
    return window.page
 | 
			
		||||
  mounted: function () {
 | 
			
		||||
    updateColumnSizes()
 | 
			
		||||
    window.addEventListener('resize', updateColumnSizes)
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    baseURL: function () {
 | 
			
		||||
      return window.data.baseURL + '/'
 | 
			
		||||
    }
 | 
			
		||||
  data: function () {
 | 
			
		||||
    return window.info
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
// Removes an element, if exists, from an array
 | 
			
		||||
function removeElement (array, element) {
 | 
			
		||||
  var i = array.indexOf(element)
 | 
			
		||||
  if (i !== -1) {
 | 
			
		||||
    array.splice(i, 1)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Replaces an element inside an array by another
 | 
			
		||||
function replaceElement (array, oldElement, newElement) {
 | 
			
		||||
  var i = array.indexOf(oldElement)
 | 
			
		||||
  if (i !== -1) {
 | 
			
		||||
    array[i] = newElement
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  remove: removeElement,
 | 
			
		||||
  replace: replaceElement
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,20 @@
 | 
			
		|||
<template>
 | 
			
		||||
    <div id="listing" :class="Data.Display">
 | 
			
		||||
    <div id="listing" 
 | 
			
		||||
      :class="data.display" 
 | 
			
		||||
      @drop="drop"
 | 
			
		||||
      @dragenter="dragEnter" 
 | 
			
		||||
      @dragend="dragEnd">
 | 
			
		||||
        <div>
 | 
			
		||||
            <div class="item header">
 | 
			
		||||
                <div></div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <p v-bind:class="{ active: Data.Sort === 'name' }" class="name"><span>Name</span>
 | 
			
		||||
                        <a v-if="Data.Sort === 'name' && Data.Order != 'asc'" href="?sort=name&order=asc"><i class="material-icons">arrow_upward</i></a>
 | 
			
		||||
                    <p v-bind:class="{ active: data.sort === 'name' }" class="name"><span>Name</span>
 | 
			
		||||
                        <a v-if="data.sort === 'name' && data.order != 'asc'" href="?sort=name&order=asc"><i class="material-icons">arrow_upward</i></a>
 | 
			
		||||
                        <a v-else href="?sort=name&order=desc"><i class="material-icons">arrow_downward</i></a>
 | 
			
		||||
                    </p>
 | 
			
		||||
 | 
			
		||||
                    <p v-bind:class="{ active: Data.Sort === 'size' }" class="size"><span>Size</span>
 | 
			
		||||
                        <a v-if="Data.Sort === 'size' && Data.Order != 'asc'" href="?sort=size&order=asc"><i class="material-icons">arrow_upward</i></a>
 | 
			
		||||
                    <p v-bind:class="{ active: data.sort === 'size' }" class="size"><span>Size</span>
 | 
			
		||||
                        <a v-if="data.sort === 'size' && data.order != 'asc'" href="?sort=size&order=asc"><i class="material-icons">arrow_upward</i></a>
 | 
			
		||||
                        <a v-else href="?sort=size&order=desc"><i class="material-icons">arrow_downward</i></a>
 | 
			
		||||
                    </p>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,37 +23,39 @@
 | 
			
		|||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <h2 v-if="(Data.NumDirs + Data.NumFiles) == 0" class="message">It feels lonely here :'(</h2>
 | 
			
		||||
        <h2 v-if="(data.numDirs + data.numFiles) == 0" class="message">It feels lonely here :'(</h2>
 | 
			
		||||
 | 
			
		||||
        <h2 v-if="Data.NumDirs !== 0">Folders</h2>
 | 
			
		||||
        <div v-if="Data.NumDirs !== 0">
 | 
			
		||||
        <h2 v-if="data.numDirs > 0">Folders</h2>
 | 
			
		||||
        <div v-if="data.numDirs > 0">
 | 
			
		||||
          <item
 | 
			
		||||
            v-for="(item, index) in Data.Items"
 | 
			
		||||
            v-if="item.IsDir"
 | 
			
		||||
            :key="base64(item.Name)"
 | 
			
		||||
            :id="base64(item.Name)"
 | 
			
		||||
            v-bind:name="item.Name"
 | 
			
		||||
            v-bind:isDir="item.IsDir"
 | 
			
		||||
            v-bind:url="item.URL"
 | 
			
		||||
            v-bind:modified="item.ModTime"  
 | 
			
		||||
            v-bind:type="item.Type"
 | 
			
		||||
            v-bind:size="item.Size">
 | 
			
		||||
            v-for="(item, index) in data.items"
 | 
			
		||||
            v-if="item.isDir"
 | 
			
		||||
            :key="base64(item.name)"
 | 
			
		||||
            :id="base64(item.name)"
 | 
			
		||||
            v-bind:selected="selected"
 | 
			
		||||
            v-bind:name="item.name"
 | 
			
		||||
            v-bind:isDir="item.isDir"
 | 
			
		||||
            v-bind:url="item.url"
 | 
			
		||||
            v-bind:modified="item.modified"  
 | 
			
		||||
            v-bind:type="item.type"
 | 
			
		||||
            v-bind:size="item.size">
 | 
			
		||||
          </item>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <h2 v-if="Data.NumItems !== 0">Files</h2>
 | 
			
		||||
        <div v-if="Data.NumItems !== 0">
 | 
			
		||||
        <h2 v-if="data.numFiles > 0">Files</h2>
 | 
			
		||||
        <div v-if="data.numFiles > 0">
 | 
			
		||||
          <item
 | 
			
		||||
            v-for="(item, index) in Data.Items"
 | 
			
		||||
            v-if="!item.IsDir"
 | 
			
		||||
            :key="base64(item.Name)"
 | 
			
		||||
            :id="base64(item.Name)"
 | 
			
		||||
            v-bind:name="item.Name"
 | 
			
		||||
            v-bind:isDir="item.IsDir"
 | 
			
		||||
            v-bind:modified="item.ModTime"  
 | 
			
		||||
            v-bind:url="item.URL"
 | 
			
		||||
            v-bind:type="item.Type"
 | 
			
		||||
            v-bind:size="item.Size">
 | 
			
		||||
            v-for="(item, index) in data.items"
 | 
			
		||||
            v-if="!item.isDir"
 | 
			
		||||
            :key="base64(item.name)"
 | 
			
		||||
            :id="base64(item.name)"
 | 
			
		||||
            v-bind:selected="selected"
 | 
			
		||||
            v-bind:name="item.name"
 | 
			
		||||
            v-bind:isDir="item.isDir"
 | 
			
		||||
            v-bind:url="item.url"
 | 
			
		||||
            v-bind:modified="item.modified"  
 | 
			
		||||
            v-bind:type="item.type"
 | 
			
		||||
            v-bind:size="item.size">
 | 
			
		||||
          </item>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,16 +67,119 @@
 | 
			
		|||
 | 
			
		||||
<script>
 | 
			
		||||
import Item from './ListingItem'
 | 
			
		||||
import webdav from '../webdav.js'
 | 
			
		||||
import page from '../page.js'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'preview',
 | 
			
		||||
  components: { Item },
 | 
			
		||||
  data: function () {
 | 
			
		||||
    return window.page
 | 
			
		||||
    return {
 | 
			
		||||
      data: window.info.page.data,
 | 
			
		||||
      selected: [],
 | 
			
		||||
      multiple: false
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  components: { Item },
 | 
			
		||||
  mounted: function () {
 | 
			
		||||
    document.addEventListener('dragover', function (event) {
 | 
			
		||||
      event.preventDefault()
 | 
			
		||||
    }, false)
 | 
			
		||||
 | 
			
		||||
    document.addEventListener('drop', this.drop, false)
 | 
			
		||||
  },
 | 
			
		||||
  beforeUpdate: function () {
 | 
			
		||||
    /*
 | 
			
		||||
      listing.redefineDownloadURLs()
 | 
			
		||||
 | 
			
		||||
  let selectedNumber = selectedItems.length,
 | 
			
		||||
    fileAction = document.getElementById('file-only')
 | 
			
		||||
 | 
			
		||||
  if (selectedNumber) {
 | 
			
		||||
    fileAction.classList.remove('disabled')
 | 
			
		||||
 | 
			
		||||
    if (selectedNumber > 1) {
 | 
			
		||||
      buttons.rename.classList.add('disabled')
 | 
			
		||||
      buttons.info.classList.add('disabled')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (selectedNumber == 1) {
 | 
			
		||||
      buttons.info.classList.remove('disabled')
 | 
			
		||||
      buttons.rename.classList.remove('disabled')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buttons.info.classList.remove('disabled')
 | 
			
		||||
  fileAction.classList.add('disabled')
 | 
			
		||||
  */
 | 
			
		||||
    console.log('before upding')
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    base64: function (name) {
 | 
			
		||||
      return window.btoa(name)
 | 
			
		||||
    },
 | 
			
		||||
    dragEnter: function (event) {
 | 
			
		||||
      let items = document.getElementsByClassName('item')
 | 
			
		||||
 | 
			
		||||
      Array.from(items).forEach(file => {
 | 
			
		||||
        file.style.opacity = 0.5
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    dragEnd: function (event) {
 | 
			
		||||
      let items = document.getElementsByClassName('item')
 | 
			
		||||
 | 
			
		||||
      Array.from(items).forEach(file => {
 | 
			
		||||
        file.style.opacity = 1
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    drop: function (event) {
 | 
			
		||||
      event.preventDefault()
 | 
			
		||||
 | 
			
		||||
      let dt = event.dataTransfer
 | 
			
		||||
      let files = dt.files
 | 
			
		||||
      let el = event.target
 | 
			
		||||
 | 
			
		||||
      for (let i = 0; i < 5; i++) {
 | 
			
		||||
        if (el !== null && !el.classList.contains('item')) {
 | 
			
		||||
          el = el.parentElement
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (files.length > 0) {
 | 
			
		||||
        if (el !== null && el.classList.contains('item') && el.dataset.dir === 'true') {
 | 
			
		||||
          this.handleFiles(files, el.querySelector('.name').innerHTML + '/')
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.handleFiles(files, '')
 | 
			
		||||
      } else {
 | 
			
		||||
        let items = document.getElementsByClassName('item')
 | 
			
		||||
 | 
			
		||||
        Array.from(items).forEach(file => {
 | 
			
		||||
          file.style.opacity = 1
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    handleFiles: function (files, base) {
 | 
			
		||||
      // buttons.setLoading('upload')
 | 
			
		||||
      let promises = []
 | 
			
		||||
 | 
			
		||||
      for (let file of files) {
 | 
			
		||||
        promises.push(webdav.put(window.location.pathname + base + file.name, file))
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Promise.all(promises)
 | 
			
		||||
      .then(() => {
 | 
			
		||||
        page.reload()
 | 
			
		||||
        // buttons.setDone('upload')
 | 
			
		||||
      })
 | 
			
		||||
      .catch(e => {
 | 
			
		||||
        console.log(e)
 | 
			
		||||
        // buttons.setDone('upload', false)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,12 @@
 | 
			
		|||
<template>
 | 
			
		||||
    <div class="item" 
 | 
			
		||||
        draggable="true"
 | 
			
		||||
        :id="base64()"
 | 
			
		||||
        :data-dir="isDir" 
 | 
			
		||||
        :data-url="url" >
 | 
			
		||||
        @dragstart="dragStart"
 | 
			
		||||
        @dragover="dragOver"
 | 
			
		||||
        @drop="drop"
 | 
			
		||||
        @click="click"
 | 
			
		||||
        @dblclick="open"
 | 
			
		||||
        :id="base64()">
 | 
			
		||||
        <div>
 | 
			
		||||
            <i class="material-icons">{{ icon() }}</i>
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -24,10 +27,13 @@
 | 
			
		|||
<script>
 | 
			
		||||
import filesize from 'filesize'
 | 
			
		||||
import moment from 'moment'
 | 
			
		||||
import webdav from '../webdav.js'
 | 
			
		||||
import page from '../page.js'
 | 
			
		||||
import array from '../array.js'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'item',
 | 
			
		||||
  props: ['name', 'isDir', 'url', 'type', 'size', 'modified'],
 | 
			
		||||
  props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'selected'],
 | 
			
		||||
  methods: {
 | 
			
		||||
    icon: function () {
 | 
			
		||||
      if (this.isDir) return 'folder'
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +50,76 @@ export default {
 | 
			
		|||
    },
 | 
			
		||||
    base64: function () {
 | 
			
		||||
      return window.btoa(this.name)
 | 
			
		||||
    },
 | 
			
		||||
    dragStart: function (event) {
 | 
			
		||||
      let el = event.target
 | 
			
		||||
 | 
			
		||||
      for (let i = 0; i < 5; i++) {
 | 
			
		||||
        if (!el.classList.contains('item')) {
 | 
			
		||||
          el = el.parentElement
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      event.dataTransfer.setData('name', el.querySelector('.name').innerHTML)
 | 
			
		||||
      event.dataTransfer.setData('obj-url', this.url)
 | 
			
		||||
    },
 | 
			
		||||
    dragOver: function (event) {
 | 
			
		||||
      if (!this.isDir) return
 | 
			
		||||
 | 
			
		||||
      event.preventDefault()
 | 
			
		||||
      let el = event.target
 | 
			
		||||
 | 
			
		||||
      for (let i = 0; i < 5; i++) {
 | 
			
		||||
        if (!el.classList.contains('item')) {
 | 
			
		||||
          el = el.parentElement
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      el.style.opacity = 1
 | 
			
		||||
    },
 | 
			
		||||
    drop: function (event) {
 | 
			
		||||
      if (!this.isDir) return
 | 
			
		||||
      event.preventDefault()
 | 
			
		||||
 | 
			
		||||
      let url = event.dataTransfer.getData('obj-url')
 | 
			
		||||
      let name = event.dataTransfer.getData('name')
 | 
			
		||||
 | 
			
		||||
      if (name === '' || url === '' || url === this.url) return
 | 
			
		||||
 | 
			
		||||
      webdav.move(url, this.url + name)
 | 
			
		||||
        .then(() => page.reload())
 | 
			
		||||
        .catch(error => console.log(error))
 | 
			
		||||
    },
 | 
			
		||||
    unselectAll: function () {
 | 
			
		||||
      let items = document.getElementsByClassName('item')
 | 
			
		||||
      Array.from(items).forEach(link => {
 | 
			
		||||
        link.setAttribute('aria-selected', false)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      this.selected.length = 0
 | 
			
		||||
 | 
			
		||||
      // listing.handleSelectionChange()
 | 
			
		||||
      return false
 | 
			
		||||
    },
 | 
			
		||||
    click: function (event) {
 | 
			
		||||
      let el = event.currentTarget
 | 
			
		||||
 | 
			
		||||
      if (this.selected.length !== 0) event.preventDefault()
 | 
			
		||||
      if (this.selected.indexOf(el.id) === -1) {
 | 
			
		||||
        if (!event.ctrlKey && !this.multiple) this.unselectAll()
 | 
			
		||||
 | 
			
		||||
        el.setAttribute('aria-selected', true)
 | 
			
		||||
        this.selected.push(el.id)
 | 
			
		||||
      } else {
 | 
			
		||||
        el.setAttribute('aria-selected', false)
 | 
			
		||||
        this.selected = array.remove(this.selected, el.id)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // this.handleSelectionChange()
 | 
			
		||||
      return false
 | 
			
		||||
    },
 | 
			
		||||
    open: function (event) {
 | 
			
		||||
      page.open(this.url)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,23 +1,23 @@
 | 
			
		|||
<template>
 | 
			
		||||
    <div id="previewer">
 | 
			
		||||
        <div class="bar">
 | 
			
		||||
            <button class="action" aria-label="Close Preview" id="close">
 | 
			
		||||
            <button @click="back" class="action" aria-label="Close Preview" id="close">
 | 
			
		||||
                <i class="material-icons">close</i>
 | 
			
		||||
            </button>
 | 
			
		||||
            <!-- TODO: add more buttons -->
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="preview">
 | 
			
		||||
            <img v-if="Data.Type == 'image'" :src="raw()">
 | 
			
		||||
            <audio v-else-if="Data.Type == 'audio'" :src="raw()" controls></audio>
 | 
			
		||||
            <video v-else-if="Data.Type == 'video'" :src="raw()" controls>
 | 
			
		||||
            <img v-if="type == 'image'" :src="raw()">
 | 
			
		||||
            <audio v-else-if="type == 'audio'" :src="raw()" controls></audio>
 | 
			
		||||
            <video v-else-if="type == 'video'" :src="raw()" controls>
 | 
			
		||||
                Sorry, your browser doesn't support embedded videos,
 | 
			
		||||
                but don't worry, you can <a href="?download=true">download it</a>
 | 
			
		||||
                and watch it with your favorite video player!
 | 
			
		||||
            </video>
 | 
			
		||||
            <object v-else-if="Data.Extension == '.pdf'" class="pdf" :data="raw()"></object>
 | 
			
		||||
            <a v-else-if="Data.Type == 'blob'" href="?download=true"><h2 class="message">Download <i class="material-icons">file_download</i></h2></a>
 | 
			
		||||
            <pre v-else >{{ Data.Content }}</pre>
 | 
			
		||||
            <object v-else-if="extension == '.pdf'" class="pdf" :data="raw()"></object>
 | 
			
		||||
            <a v-else-if="type == 'blob'" href="?download=true"><h2 class="message">Download <i class="material-icons">file_download</i></h2></a>
 | 
			
		||||
            <pre v-else >{{ content }}</pre>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -26,11 +26,14 @@
 | 
			
		|||
export default {
 | 
			
		||||
  name: 'preview',
 | 
			
		||||
  data: function () {
 | 
			
		||||
    return window.page
 | 
			
		||||
    return window.info.page.data
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    raw: function () {
 | 
			
		||||
      return this.Data.URL + '?raw=true'
 | 
			
		||||
      return this.url + '?raw=true'
 | 
			
		||||
    },
 | 
			
		||||
    back: function (event) {
 | 
			
		||||
      window.history.back()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,6 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
// Remove the last directory of an url
 | 
			
		||||
var removeLastDirectoryPartOf = function (url) {
 | 
			
		||||
  var arr = url.split('/')
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +25,7 @@ var removeLastDirectoryPartOf = function (url) {
 | 
			
		|||
  return (arr.join('/'))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var data = window.data
 | 
			
		||||
var user = window.info.user
 | 
			
		||||
var ssl = window.ssl
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,8 +47,8 @@ export default {
 | 
			
		|||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    reset: function () {
 | 
			
		||||
      if (data.user.AllowCommands && data.user.Commands.length > 0) {
 | 
			
		||||
        this.box.innerHTML = `Search or use one of your supported commands: ${data.user.Commands.join(', ')}.`
 | 
			
		||||
      if (user.allowCommands && user.commands.length > 0) {
 | 
			
		||||
        this.box.innerHTML = `Search or use one of your supported commands: ${user.commands.join(', ')}.`
 | 
			
		||||
      } else {
 | 
			
		||||
        this.box.innerHTML = 'Type and press enter to search.'
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -58,8 +57,8 @@ export default {
 | 
			
		|||
      let value = this.input.value
 | 
			
		||||
      let pieces = value.split(' ')
 | 
			
		||||
 | 
			
		||||
      for (let i = 0; i < data.user.Commands.length; i++) {
 | 
			
		||||
        if (pieces[0] === data.user.Commands[0]) {
 | 
			
		||||
      for (let i = 0; i < user.commands.length; i++) {
 | 
			
		||||
        if (pieces[0] === user.commands[0]) {
 | 
			
		||||
          return true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +77,7 @@ export default {
 | 
			
		|||
        return
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!this.supported() || !data.user.AllowCommands) {
 | 
			
		||||
      if (!this.supported() || !user.allowCommands) {
 | 
			
		||||
        this.box.innerHTML = 'Press enter to search.'
 | 
			
		||||
      } else {
 | 
			
		||||
        this.box.innerHTML = 'Press enter to execute.'
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +95,7 @@ export default {
 | 
			
		|||
 | 
			
		||||
      let protocol = ssl ? 'wss:' : 'ws:'
 | 
			
		||||
 | 
			
		||||
      if (this.supported() && data.user.AllowCommands) {
 | 
			
		||||
      if (this.supported() && user.allowCommands) {
 | 
			
		||||
        let conn = new window.WebSocket(`${protocol}//${url}?command=true`)
 | 
			
		||||
 | 
			
		||||
        conn.onopen = () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
export default function getRule (rules) {
 | 
			
		||||
  for (let i = 0; i < rules.length; i++) {
 | 
			
		||||
    rules[i] = rules[i].toLowerCase()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let result = null
 | 
			
		||||
  let find = Array.prototype.find
 | 
			
		||||
 | 
			
		||||
  find.call(document.styleSheets, styleSheet => {
 | 
			
		||||
    result = find.call(styleSheet.cssRules, cssRule => {
 | 
			
		||||
      let found = false
 | 
			
		||||
 | 
			
		||||
      if (cssRule instanceof window.CSSStyleRule) {
 | 
			
		||||
        for (let i = 0; i < rules.length; i++) {
 | 
			
		||||
          if (cssRule.selectorText.toLowerCase() === rules[i]) {
 | 
			
		||||
            found = true
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return found
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return result != null
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return result
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,12 +2,40 @@
 | 
			
		|||
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
 | 
			
		||||
import Vue from 'vue'
 | 
			
		||||
import App from './App'
 | 
			
		||||
 | 
			
		||||
window.data = (window.data || window.alert('Something is wrong, please refresh!'))
 | 
			
		||||
window.ssl = (window.location.protocol === 'https:')
 | 
			
		||||
// simport page from './page.js'
 | 
			
		||||
 | 
			
		||||
Vue.config.productionTip = false
 | 
			
		||||
 | 
			
		||||
window.info = (window.info || window.alert('Something is wrong, please refresh!'))
 | 
			
		||||
window.ssl = (window.location.protocol === 'https:')
 | 
			
		||||
 | 
			
		||||
// TODO: keep this here?
 | 
			
		||||
document.title = window.info.page.name
 | 
			
		||||
 | 
			
		||||
// TODO: keep this here?
 | 
			
		||||
window.addEventListener('popstate', (event) => {
 | 
			
		||||
  event.preventDefault()
 | 
			
		||||
  event.stopPropagation()
 | 
			
		||||
 | 
			
		||||
  window.info.page.kind = ''
 | 
			
		||||
 | 
			
		||||
  let request = new window.XMLHttpRequest()
 | 
			
		||||
  request.open('GET', event.state.url, true)
 | 
			
		||||
  request.setRequestHeader('Accept', 'application/json')
 | 
			
		||||
 | 
			
		||||
  request.onload = () => {
 | 
			
		||||
    if (request.status === 200) {
 | 
			
		||||
      window.info.page = JSON.parse(request.responseText)
 | 
			
		||||
      document.title = event.state.name
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(request.responseText)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  request.onerror = (error) => { console.log(error) }
 | 
			
		||||
  request.send()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/* eslint-disable no-new */
 | 
			
		||||
new Vue({
 | 
			
		||||
  el: '#app',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
function open (url, history) {
 | 
			
		||||
  window.info.page.kind = ''
 | 
			
		||||
 | 
			
		||||
  let request = new window.XMLHttpRequest()
 | 
			
		||||
  request.open('GET', url, true)
 | 
			
		||||
  request.setRequestHeader('Accept', 'application/json')
 | 
			
		||||
 | 
			
		||||
  request.onload = () => {
 | 
			
		||||
    if (request.status === 200) {
 | 
			
		||||
      window.info.page = JSON.parse(request.responseText)
 | 
			
		||||
 | 
			
		||||
      if (history) {
 | 
			
		||||
        window.history.pushState({
 | 
			
		||||
          name: window.info.page.name,
 | 
			
		||||
          url: url
 | 
			
		||||
        }, window.info.page.name, url)
 | 
			
		||||
 | 
			
		||||
        document.title = window.info.page.name
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(request.responseText)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  request.onerror = (error) => { console.log(error) }
 | 
			
		||||
  request.send()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  reload: () => {
 | 
			
		||||
    open(window.location.pathname, false)
 | 
			
		||||
  },
 | 
			
		||||
  open: (url) => {
 | 
			
		||||
    open(url, true)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
var info = window.info
 | 
			
		||||
 | 
			
		||||
function convertURL (url) {
 | 
			
		||||
  return window.location.origin + url.replace(info.baseURL + '/', info.webdavURL + '/')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function move (oldLink, newLink) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
 | 
			
		||||
    oldLink = convertURL(oldLink)
 | 
			
		||||
    newLink = newLink.replace(info.baseURL + '/', info.webdavURL + '/')
 | 
			
		||||
    newLink = window.location.origin + newLink.substring(info.baseURL.length)
 | 
			
		||||
 | 
			
		||||
    request.open('MOVE', oldLink, true)
 | 
			
		||||
    request.setRequestHeader('Destination', newLink)
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 201 || request.status === 204) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function put (link, body, headers = {}) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open('PUT', convertURL(link), true)
 | 
			
		||||
 | 
			
		||||
    for (let key in headers) {
 | 
			
		||||
      request.setRequestHeader(key, headers[key])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 201) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send(body)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function propfind (link, body, headers = {}) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open('PROPFIND', convertURL(link), true)
 | 
			
		||||
 | 
			
		||||
    for (let key in headers) {
 | 
			
		||||
      request.setRequestHeader(key, headers[key])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status < 300) {
 | 
			
		||||
        resolve(request.responseText)
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send(body)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function trash (link) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open('DELETE', convertURL(link), true)
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 204) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function create (link) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let request = new window.XMLHttpRequest()
 | 
			
		||||
    request.open((link.endsWith('/') ? 'MKCOL' : 'PUT'), convertURL(link), true)
 | 
			
		||||
    request.onload = () => {
 | 
			
		||||
      if (request.status === 201) {
 | 
			
		||||
        resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        reject(request.statusText)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    request.onerror = () => reject(request.statusText)
 | 
			
		||||
    request.send()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  create: create,
 | 
			
		||||
  trash: trash,
 | 
			
		||||
  propfind: propfind,
 | 
			
		||||
  put: put,
 | 
			
		||||
  move: move
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,16 +10,21 @@ import (
 | 
			
		|||
var m *filemanager.FileManager
 | 
			
		||||
 | 
			
		||||
func handler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	_, err := m.ServeHTTP(w, r)
 | 
			
		||||
	// TODO: review return codes and return 0 when everything works.
 | 
			
		||||
 | 
			
		||||
	code, err := m.ServeHTTP(w, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Print(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if code != 0 {
 | 
			
		||||
		w.WriteHeader(code)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	m = filemanager.New("D:\\TEST")
 | 
			
		||||
	m.SetBaseURL("/vaca")
 | 
			
		||||
	m.AllowEdit = false
 | 
			
		||||
	m.Commands = []string{"git"}
 | 
			
		||||
	http.HandleFunc("/", handler)
 | 
			
		||||
	http.ListenAndServe(":8080", nil)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										40
									
								
								file.go
								
								
								
								
							
							
						
						
									
										40
									
								
								file.go
								
								
								
								
							| 
						 | 
				
			
			@ -31,48 +31,48 @@ type fileInfo struct {
 | 
			
		|||
	// Used to store the file's content temporarily.
 | 
			
		||||
	content []byte
 | 
			
		||||
	// The name of the file.
 | 
			
		||||
	Name string
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	// The Size of the file.
 | 
			
		||||
	Size int64
 | 
			
		||||
	Size int64 `json:"size"`
 | 
			
		||||
	// The absolute URL.
 | 
			
		||||
	URL string
 | 
			
		||||
	URL string `json:"url"`
 | 
			
		||||
	// The extension of the file.
 | 
			
		||||
	Extension string
 | 
			
		||||
	Extension string `json:"extension"`
 | 
			
		||||
	// The last modified time.
 | 
			
		||||
	ModTime time.Time
 | 
			
		||||
	ModTime time.Time `json:"modified"`
 | 
			
		||||
	// The File Mode.
 | 
			
		||||
	Mode os.FileMode
 | 
			
		||||
	Mode os.FileMode `json:"mode"`
 | 
			
		||||
	// Indicates if this file is a directory.
 | 
			
		||||
	IsDir bool
 | 
			
		||||
	IsDir bool `json:"isDir"`
 | 
			
		||||
	// Absolute path.
 | 
			
		||||
	Path string
 | 
			
		||||
	Path string `json:"path"`
 | 
			
		||||
	// Relative path to user's virtual File System.
 | 
			
		||||
	VirtualPath string
 | 
			
		||||
	VirtualPath string `json:"virtualPath"`
 | 
			
		||||
	// Indicates the file content type: video, text, image, music or blob.
 | 
			
		||||
	Type string
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
	// Stores the content of a text file.
 | 
			
		||||
	Content string
 | 
			
		||||
	Content string `json:"content"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A listing is the context used to fill out a template.
 | 
			
		||||
type listing struct {
 | 
			
		||||
	// The name of the directory (the last element of the path).
 | 
			
		||||
	Name string
 | 
			
		||||
	Name string `json:"-"`
 | 
			
		||||
	// The full path of the request relatively to a File System.
 | 
			
		||||
	Path string
 | 
			
		||||
	Path string `json:"-"`
 | 
			
		||||
	// The items (files and folders) in the path.
 | 
			
		||||
	Items []fileInfo
 | 
			
		||||
	Items []fileInfo `json:"items"`
 | 
			
		||||
	// The number of directories in the listing.
 | 
			
		||||
	NumDirs int
 | 
			
		||||
	NumDirs int `json:"numDirs"`
 | 
			
		||||
	// The number of files (items that aren't directories) in the listing.
 | 
			
		||||
	NumFiles int
 | 
			
		||||
	NumFiles int `json:"numFiles"`
 | 
			
		||||
	// Which sorting order is used.
 | 
			
		||||
	Sort string
 | 
			
		||||
	Sort string `json:"sort"`
 | 
			
		||||
	// And which order.
 | 
			
		||||
	Order string
 | 
			
		||||
	Order string `json:"order"`
 | 
			
		||||
	// If ≠0 then Items have been limited to that many elements.
 | 
			
		||||
	ItemsLimitedTo int
 | 
			
		||||
	Display        string
 | 
			
		||||
	ItemsLimitedTo int    `json:"ItemsLimitedTo"`
 | 
			
		||||
	Display        string `json:"display"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getInfo gets the file information and, in case of error, returns the
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,12 +69,12 @@ type User struct {
 | 
			
		|||
	StyleSheet string `json:"-"`
 | 
			
		||||
 | 
			
		||||
	// These indicate if the user can perform certain actions.
 | 
			
		||||
	AllowNew      bool // Create files and folders
 | 
			
		||||
	AllowEdit     bool // Edit/rename files
 | 
			
		||||
	AllowCommands bool // Execute commands
 | 
			
		||||
	AllowNew      bool `json:"allowNew"`      // Create files and folders
 | 
			
		||||
	AllowEdit     bool `json:"allowEdit"`     // Edit/rename files
 | 
			
		||||
	AllowCommands bool `json:"allowCommands"` // Execute commands
 | 
			
		||||
 | 
			
		||||
	// Commands is the list of commands the user can execute.
 | 
			
		||||
	Commands []string
 | 
			
		||||
	Commands []string `json:"commands"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rule is a dissalow/allow rule.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								page.go
								
								
								
								
							
							
						
						
									
										8
									
								
								page.go
								
								
								
								
							| 
						 | 
				
			
			@ -35,10 +35,10 @@ type page struct {
 | 
			
		|||
	BaseURL   string `json:"-"`
 | 
			
		||||
	WebDavURL string `json:"-"`
 | 
			
		||||
 | 
			
		||||
	Name string
 | 
			
		||||
	Path string
 | 
			
		||||
	Kind string // listing, editor or preview
 | 
			
		||||
	Data interface{}
 | 
			
		||||
	Name string      `json:"name"`
 | 
			
		||||
	Path string      `json:"path"`
 | 
			
		||||
	Kind string      `json:"kind"` // listing, editor or preview
 | 
			
		||||
	Data interface{} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// breadcrumbItem contains the Name and the URL of a breadcrumb piece.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								serve.go
								
								
								
								
							
							
						
						
									
										6
									
								
								serve.go
								
								
								
								
							| 
						 | 
				
			
			@ -95,14 +95,14 @@ func serveListing(c *requestContext, w http.ResponseWriter, r *http.Request) (in
 | 
			
		|||
		listing.ItemsLimitedTo = limit
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listing.Display = displayMode(w, r, cookieScope)
 | 
			
		||||
	c.pg.Data = listing
 | 
			
		||||
 | 
			
		||||
	// If it's a JSON request, print only the items... in JSON! (such a surprise -_-)
 | 
			
		||||
	if strings.Contains(r.Header.Get("Accept"), "application/json") {
 | 
			
		||||
		c.pg.Data = listing.Items
 | 
			
		||||
		return c.pg.PrintJSON(w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listing.Display = displayMode(w, r, cookieScope)
 | 
			
		||||
	c.pg.Data = listing
 | 
			
		||||
	return c.pg.PrintHTML(w, c.fm.templates)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue