add linter and apply linter

This commit is contained in:
Simon Vieille 2021-06-15 14:16:07 +02:00
parent 99e7969c5c
commit 631281dc3c
29 changed files with 2477 additions and 818 deletions

19
.eslintrc.json Normal file
View File

@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": [
"plugin:vue/essential",
"standard"
],
"parserOptions": {
"ecmaVersion": 12
},
"plugins": [
"vue"
],
"rules": {
}
}

View File

@ -1,22 +1,22 @@
import '../../css/admin.scss'; import '../../css/admin.scss'
require('../../../node_modules/bootstrap/dist/js/bootstrap.min.js'); require('../../../node_modules/bootstrap/dist/js/bootstrap.min.js')
require('./modules/table-fixed.js')(); require('./modules/table-fixed.js')()
require('./modules/form-confirm.js')(); require('./modules/form-confirm.js')()
require('./modules/form.js')(); require('./modules/form.js')()
require('./modules/dbclick.js')(); require('./modules/dbclick.js')()
require('./modules/toast.js')(); require('./modules/toast.js')()
require('./modules/modal.js')(); require('./modules/modal.js')()
require('./modules/push-state.js')(); require('./modules/push-state.js')()
require('./modules/password.js')(); require('./modules/password.js')()
require('./modules/tooltip.js')(); require('./modules/tooltip.js')()
require('./modules/editor.js')(); require('./modules/editor.js')()
require('./modules/panel.js')(); require('./modules/panel.js')()
require('./modules/choices.js')(); require('./modules/choices.js')()
require('./modules/checkbox-checker.js')(); require('./modules/checkbox-checker.js')()
require('./modules/rest-choices.js')(); require('./modules/rest-choices.js')()
require('./modules/form-collection.js')(); require('./modules/form-collection.js')()
require('./modules/datepicker.js')(); require('./modules/datepicker.js')()
require('./modules/sortable.js')(); require('./modules/sortable.js')()
require('./modules/batch.js')(); require('./modules/batch.js')()
require('./modules/file-manager.js')(); require('./modules/file-manager.js')()

View File

@ -4,42 +4,42 @@
<script> <script>
const map = { const map = {
'fa-file-pdf': ['application/pdf'], 'fa-file-pdf': ['application/pdf'],
'fa-file-image': ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'], 'fa-file-image': ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'],
'fa-file-audio': ['application/ogg', 'audio/mp3', 'audio/mpeg', 'audio/wav'], 'fa-file-audio': ['application/ogg', 'audio/mp3', 'audio/mpeg', 'audio/wav'],
'fa-file-archive': ['application/zip', 'multipart/x-zip', 'application/rar', 'application/x-rar-compressed', 'application/x-zip-compressed', 'application/tar', 'application/x-tar'], 'fa-file-archive': ['application/zip', 'multipart/x-zip', 'application/rar', 'application/x-rar-compressed', 'application/x-zip-compressed', 'application/tar', 'application/x-tar'],
'fa-file-alt': ['application/rtf'], 'fa-file-alt': ['application/rtf'],
'fa-file-excel': ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'], 'fa-file-excel': ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
'fa-file-powerpoint': ['application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'], 'fa-file-powerpoint': ['application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
'fa-file-video': ['video/x-msvideo', 'video/mpeg'], 'fa-file-video': ['video/x-msvideo', 'video/mpeg']
} }
export default { export default {
name: 'FileIcon', name: 'FileIcon',
methods: { methods: {
getIcon(mime) { getIcon (mime) {
let icons = ['fa'] const icons = ['fa']
let iconFound = false let iconFound = false
for (let icon in map) { for (const icon in map) {
if (map[icon].indexOf(mime) !== -1) { if (map[icon].indexOf(mime) !== -1) {
iconFound = true iconFound = true
icons.push(icon) icons.push(icon)
} }
} }
if (!iconFound) { if (!iconFound) {
icons.push('fa-file') icons.push('fa-file')
} }
return icons return icons
}, }
}, },
props: { props: {
mime: { mime: {
type: String, type: String,
required: true required: true
}, }
}, }
} }
</script> </script>

View File

@ -15,9 +15,9 @@
import Files from './Files' import Files from './Files'
export default { export default {
name: "FileManager", name: 'FileManager',
components: { components: {
Files, Files
} }
} }
</script> </script>

View File

@ -165,121 +165,120 @@ tr {
</style> </style>
<script> <script>
import Routing from '../../../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js'
import FileIcon from './FileIcon'
const axios = require('axios').default const axios = require('axios').default
const routes = require('../../../../../public/js/fos_js_routes.json') const routes = require('../../../../../public/js/fos_js_routes.json')
import Routing from '../../../../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js';
import FileIcon from './FileIcon';
Routing.setRoutingData(routes);
export default { export default {
name: "Files", name: 'Files',
components: { components: {
FileIcon, FileIcon
}, },
data() { data () {
return { return {
view: 'list', view: 'list',
directory: null, directory: null,
directories: [], directories: [],
breadcrumb: [], breadcrumb: [],
files: [], files: [],
parent: null, parent: null
}
},
methods: {
setDirectory(directory) {
this.directory = directory
},
setView(view) {
this.view = view
localStorage.setItem('file-manager.view', view)
},
generateInfoLink(item, directory) {
if (directory) {
return Routing.generate('admin_file_manager_info', {
file: item.path
})
} else {
return Routing.generate('admin_file_manager_info', {
file: item.path + '/' + item.basename
})
}
},
generateUploadLink(directory) {
return Routing.generate('admin_file_manager_upload', {
file: directory
})
},
generateNewDirectoryLink(directory) {
return Routing.generate('admin_file_manager_directory_new', {
file: directory
})
},
buildBreadcrum(elements) {
let path = '/'
this.breadcrumb = []
for (let i in elements) {
const element = elements[i]
if (element !== '/') {
path = path + '/' + element
this.breadcrumb.push({
path: path,
label: element,
})
} else {
this.breadcrumb.push({
path: '/',
label: 'Files',
})
}
}
}
},
mounted() {
let view = localStorage.getItem('file-manager.view')
if (['grid', 'list'].indexOf(view) !== -1) {
this.view = view
}
const query = new URLSearchParams(window.location.search)
if (query.has('path')) {
this.setDirectory(query.get('path'))
} else {
this.setDirectory('/')
}
},
watch: {
directory(directory) {
axios.get(Routing.generate('admin_file_manager_api_directory', {
directory: this.directory
}))
.then((response) => {
this.buildBreadcrum(response.data.breadcrumb)
this.parent = response.data.parent
this.directories = response.data.directories
this.files = response.data.files
const query = new URLSearchParams(window.location.search)
query.set('path', directory)
history.pushState(
null,
'',
window.location.pathname + '?' + query.toString()
)
})
.catch(() => {
alert('An error occured')
})
}
} }
},
methods: {
setDirectory (directory) {
this.directory = directory
},
setView (view) {
this.view = view
localStorage.setItem('file-manager.view', view)
},
generateInfoLink (item, directory) {
if (directory) {
return Routing.generate('admin_file_manager_info', {
file: item.path
})
} else {
return Routing.generate('admin_file_manager_info', {
file: item.path + '/' + item.basename
})
}
},
generateUploadLink (directory) {
return Routing.generate('admin_file_manager_upload', {
file: directory
})
},
generateNewDirectoryLink (directory) {
return Routing.generate('admin_file_manager_directory_new', {
file: directory
})
},
buildBreadcrum (elements) {
let path = '/'
this.breadcrumb = []
for (const i in elements) {
const element = elements[i]
if (element !== '/') {
path = path + '/' + element
this.breadcrumb.push({
path: path,
label: element
})
} else {
this.breadcrumb.push({
path: '/',
label: 'Files'
})
}
}
}
},
mounted () {
Routing.setRoutingData(routes)
const view = localStorage.getItem('file-manager.view')
if (['grid', 'list'].indexOf(view) !== -1) {
this.view = view
}
const query = new URLSearchParams(window.location.search)
if (query.has('path')) {
this.setDirectory(query.get('path'))
} else {
this.setDirectory('/')
}
},
watch: {
directory (directory) {
axios.get(Routing.generate('admin_file_manager_api_directory', {
directory: this.directory
}))
.then((response) => {
this.buildBreadcrum(response.data.breadcrumb)
this.parent = response.data.parent
this.directories = response.data.directories
this.files = response.data.files
const query = new URLSearchParams(window.location.search)
query.set('path', directory)
history.pushState(
null,
'',
window.location.pathname + '?' + query.toString()
)
})
.catch(() => {
alert('An error occured')
})
}
}
} }
</script> </script>

View File

@ -1,23 +1,23 @@
const $ = require('jquery') const $ = require('jquery')
module.exports = () => { module.exports = () => {
$('th.crud-batch-column input').change((e) => { $('th.crud-batch-column input').change((e) => {
$('td.crud-batch-column input').prop('checked', $(e.target).is(':checked')); $('td.crud-batch-column input').prop('checked', $(e.target).is(':checked'))
}); })
const form = $('#form-batch') const form = $('#form-batch')
form.submit((e) => { form.submit((e) => {
e.preventDefault(); e.preventDefault()
const route = form.attr('action') const route = form.attr('action')
const datas = form.serialize() const datas = form.serialize()
form.addClass('is-loading'); form.addClass('is-loading')
$.post(route, datas) $.post(route, datas)
.always(() => { .always(() => {
document.location.href = document.location.href document.location.href = document.location.href
}); })
}); })
} }

View File

@ -1,31 +1,31 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('*[data-checkbox-ckecker]').click(function() { $('*[data-checkbox-ckecker]').click(function () {
const wrapperName = $(this).attr('data-checkbox-ckecker'); const wrapperName = $(this).attr('data-checkbox-ckecker')
if (!wrapperName) { if (!wrapperName) {
return; return
} }
const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]'); const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]')
$(checkboxes).each(function(i, v) { $(checkboxes).each(function (i, v) {
$(v).prop('checked', true); $(v).prop('checked', true)
})
}) })
})
$('*[data-checkbox-unckecker]').click(function() { $('*[data-checkbox-unckecker]').click(function () {
const wrapperName = $(this).attr('data-checkbox-unckecker'); const wrapperName = $(this).attr('data-checkbox-unckecker')
if (!wrapperName) { if (!wrapperName) {
return; return
} }
const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]'); const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]')
$(checkboxes).each(function(i, v) { $(checkboxes).each(function (i, v) {
$(v).prop('checked', false); $(v).prop('checked', false)
})
}) })
}; })
}

View File

@ -1,8 +1,8 @@
const Choices = require('choices.js'); const Choices = require('choices.js')
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('*[data-jschoice]').each(function(key, item) { $('*[data-jschoice]').each(function (key, item) {
new Choices(item); new Choices(item)
}); })
} }

View File

@ -1,26 +1,26 @@
const Datepicker = require('vanillajs-datepicker') const Datepicker = require('vanillajs-datepicker')
const isDateSupported = () => { const isDateSupported = () => {
const input = document.createElement('input'); const input = document.createElement('input')
const value = 'a'; const value = 'a'
input.setAttribute('type', 'date'); input.setAttribute('type', 'date')
input.setAttribute('value', value); input.setAttribute('value', value)
return input.value !== value; return input.value !== value
} }
module.exports = () => { module.exports = () => {
if (isDateSupported()) { if (isDateSupported()) {
return return
} }
const inputs = document.querySelectorAll('input[type="date"]') const inputs = document.querySelectorAll('input[type="date"]')
const size = inputs.length const size = inputs.length
for (var i = 0, c = inputs.length; i < c; i++) { for (let i = 0, c = inputs.length; i < c; i++) {
new Datepicker.Datepicker(inputs[i], { new Datepicker.Datepicker(inputs[i], {
format: 'yyyy-mm-dd' format: 'yyyy-mm-dd'
}) })
} }
} }

View File

@ -1,7 +1,7 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('*[data-dblclick]').dblclick(function(e) { $('*[data-dblclick]').dblclick(function (e) {
document.location.href = $(this).attr('data-dblclick'); document.location.href = $(this).attr('data-dblclick')
}) })
}; }

View File

@ -1,43 +1,43 @@
const $ = require('jquery'); const $ = require('jquery')
let DocumentSelector = () => { const DocumentSelector = () => {
let forms = $('.document-selector-form'); const forms = $('.document-selector-form')
let btnSubmit = $('#download-archive-form button'); const btnSubmit = $('#download-archive-form button')
let handler = function() { const handler = function () {
forms.each((fi, f) => { forms.each((fi, f) => {
let form = $(f); const form = $(f)
let ids = form.find('.document-selector-ids'); const ids = form.find('.document-selector-ids')
let btn = form.find('.document-selector-button'); const btn = form.find('.document-selector-button')
ids.html(''); ids.html('')
let hasSelection = false; let hasSelection = false
$('*[data-documents] *[data-selectable-row] input[data-selectable-checkbox]').each((i, c) => { $('*[data-documents] *[data-selectable-row] input[data-selectable-checkbox]').each((i, c) => {
let checkbox = $(c); const checkbox = $(c)
if (checkbox.is(':checked')) { if (checkbox.is(':checked')) {
ids.append(checkbox[0].outerHTML); ids.append(checkbox[0].outerHTML)
hasSelection = true; hasSelection = true
} }
}); })
if (hasSelection && btn.length) { if (hasSelection && btn.length) {
btn.removeAttr('disabled'); btn.removeAttr('disabled')
ids.find('input').prop('checked', true); ids.find('input').prop('checked', true)
} else { } else {
btn.attr('disabled', 'disabled'); btn.attr('disabled', 'disabled')
} }
}) })
} }
$('*[data-documents] *[data-selectable-row]').click(function() { $('*[data-documents] *[data-selectable-row]').click(function () {
window.setTimeout(handler, 100) window.setTimeout(handler, 100)
}); })
$('*[data-documents] *[data-selectable-row]').on('clicked', function() { $('*[data-documents] *[data-selectable-row]').on('clicked', function () {
window.setTimeout(handler, 100) window.setTimeout(handler, 100)
}); })
} }
module.exports = DocumentSelector; module.exports = DocumentSelector

View File

@ -1,100 +1,100 @@
const $ = require('jquery') const $ = require('jquery')
if (typeof tinymce !== 'undefined') { if (typeof tinymce !== 'undefined') {
tinymce.murph = tinymce.murph || {} tinymce.murph = tinymce.murph || {}
tinymce.murph.selector = tinymce.murph.selector || '*[data-tinymce]' tinymce.murph.selector = tinymce.murph.selector || '*[data-tinymce]'
tinymce.murph.configurationBase = tinymce.murph.configurationBase || { tinymce.murph.configurationBase = tinymce.murph.configurationBase || {
base_url: '/vendor/tinymce/', base_url: '/vendor/tinymce/',
cache_suffix: '?v=4.1.6', cache_suffix: '?v=4.1.6',
importcss_append: true, importcss_append: true,
image_caption: true, image_caption: true,
noneditable_noneditable_class: "mceNonEditable", noneditable_noneditable_class: 'mceNonEditable',
toolbar_drawer: 'sliding', toolbar_drawer: 'sliding',
spellchecker_dialog: true, spellchecker_dialog: true,
tinycomments_mode: 'embedded', tinycomments_mode: 'embedded',
convert_urls: false, convert_urls: false,
init_instance_callback: function (editor) { init_instance_callback: function (editor) {
editor.on('SetContent', () => { editor.on('SetContent', () => {
tinymce.triggerSave(false, true); tinymce.triggerSave(false, true)
}); })
editor.on('Change', () => { editor.on('Change', () => {
tinymce.triggerSave(false, true); tinymce.triggerSave(false, true)
}); })
}
} }
}
tinymce.murph.modes = tinymce.murph.modes || {} tinymce.murph.modes = tinymce.murph.modes || {}
tinymce.murph.modes.default = tinymce.murph.modes.default || { tinymce.murph.modes.default = tinymce.murph.modes.default || {
plugins: 'print preview importcss searchreplace visualblocks visualchars fullscreen template table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars link image code autoresize', plugins: 'print preview importcss searchreplace visualblocks visualchars fullscreen template table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars link image code autoresize',
menubar: 'file edit view insert format tools table tc help', menubar: 'file edit view insert format tools table tc help',
toolbar: 'undo redo | bold italic underline strikethrough | link image | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap | fullscreen preview', toolbar: 'undo redo | bold italic underline strikethrough | link image | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap | fullscreen preview',
quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable', quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
contextmenu: "link image imagetools table configurepermanentpen", contextmenu: 'link image imagetools table configurepermanentpen'
} }
tinymce.murph.modes.light = tinymce.murph.modes.light || { tinymce.murph.modes.light = tinymce.murph.modes.light || {
contextmenu: "link image imagetools table configurepermanentpen", contextmenu: 'link image imagetools table configurepermanentpen',
quickbars_selection_toolbar: 'bold italic', quickbars_selection_toolbar: 'bold italic',
toolbar: 'undo redo | bold italic underline', toolbar: 'undo redo | bold italic underline'
} }
} }
const buildConfiguration = (conf) => { const buildConfiguration = (conf) => {
return Object.assign({}, tinymce.murph.configurationBase, conf); return Object.assign({}, tinymce.murph.configurationBase, conf)
} }
const makeId = () => { const makeId = () => {
let result = ''; let result = ''
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
const charactersLength = characters.length; const charactersLength = characters.length
for ( var i = 0; i < 20; i++ ) { for (let i = 0; i < 20; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength)); result += characters.charAt(Math.floor(Math.random() * charactersLength))
} }
return 'tinymce-' + result; return 'tinymce-' + result
} }
const doInitEditor = () => { const doInitEditor = () => {
$(tinymce.murph.selector).each((i, v) => { $(tinymce.murph.selector).each((i, v) => {
const element = $(v); const element = $(v)
let id = null let id = null
if (element.attr('id')) { if (element.attr('id')) {
id = element.attr('id') id = element.attr('id')
} else { } else {
id = makeId() id = makeId()
element.attr('id', makeId) element.attr('id', makeId)
}
let mode = element.attr('data-tinymce')
if (!mode) {
mode = 'default';
}
if (!tinymce.murph.modes.hasOwnProperty(mode)) {
return;
}
let conf = buildConfiguration(tinymce.murph.modes[mode])
conf.mode = 'exact'
conf.elements = id
tinymce.init(conf);
})
}
module.exports = function() {
if (typeof tinymce === 'undefined') {
return;
} }
const observer = new MutationObserver(doInitEditor); let mode = element.attr('data-tinymce')
const config = {attributes: false, childList: true, subtree: true};
observer.observe(document.querySelector('body'), config);
doInitEditor(); if (!mode) {
}; mode = 'default'
}
if (!tinymce.murph.modes.hasOwnProperty(mode)) {
return
}
const conf = buildConfiguration(tinymce.murph.modes[mode])
conf.mode = 'exact'
conf.elements = id
tinymce.init(conf)
})
}
module.exports = function () {
if (typeof tinymce === 'undefined') {
return
}
const observer = new MutationObserver(doInitEditor)
const config = { attributes: false, childList: true, subtree: true }
observer.observe(document.querySelector('body'), config)
doInitEditor()
}

View File

@ -4,15 +4,15 @@ const Vue = require('vue').default
const FileManager = require('../components/file-manager/FileManager').default const FileManager = require('../components/file-manager/FileManager').default
module.exports = () => { module.exports = () => {
if (!document.getElementById('file-manager')) { if (!document.getElementById('file-manager')) {
return return
} }
new Vue({ new Vue({
el: '#file-manager', el: '#file-manager',
template: '<FileManager />', template: '<FileManager />',
components: { components: {
FileManager FileManager
} }
}); })
} }

View File

@ -1,84 +1,84 @@
const $ = require('jquery'); const $ = require('jquery')
const DeleteHandler = (e) => { const DeleteHandler = (e) => {
e.stopPropagation() e.stopPropagation()
const target = e.target; const target = e.target
let button = $(target); let button = $(target)
if (button.is('[data-collection-delete-container]')) { if (button.is('[data-collection-delete-container]')) {
button = button.find('*[data-collection-delete]').first() button = button.find('*[data-collection-delete]').first()
} }
const id = button.attr('data-collection-delete'); const id = button.attr('data-collection-delete')
const collection = button.parents('[data-collection]') const collection = button.parents('[data-collection]')
const item = collection.find('*[data-collection-item="' + id + '"]') const item = collection.find('*[data-collection-item="' + id + '"]')
if (confirm('Validez-vous la suppression ?')) { if (confirm('Validez-vous la suppression ?')) {
item.remove(); item.remove()
collection.trigger('collection.update'); collection.trigger('collection.update')
} }
} }
const CollectionInitilizedAndUpdated = (e) => { const CollectionInitilizedAndUpdated = (e) => {
const target = $(e.target) const target = $(e.target)
target.find('*[data-collection-empty]').toggleClass( target.find('*[data-collection-empty]').toggleClass(
'd-none', 'd-none',
target.find('*[data-collection-item]').length !== 0 target.find('*[data-collection-item]').length !== 0
); )
target.find('*[data-collection-nonempty]').toggleClass( target.find('*[data-collection-nonempty]').toggleClass(
'd-none', 'd-none',
target.find('*[data-collection-item]').length === 0 target.find('*[data-collection-item]').length === 0
); )
} }
const FormCollection = () => { const FormCollection = () => {
$('*[data-collection]').on( $('*[data-collection]').on(
'collection.update', 'collection.update',
CollectionInitilizedAndUpdated CollectionInitilizedAndUpdated
); )
$('*[data-collection]').on( $('*[data-collection]').on(
'collection.init', 'collection.init',
CollectionInitilizedAndUpdated CollectionInitilizedAndUpdated
); )
$('body').on( $('body').on(
'click', 'click',
'*[data-collection-delete], *[data-collection-delete-container]', '*[data-collection-delete], *[data-collection-delete-container]',
DeleteHandler DeleteHandler
); )
$('body').on('click', '*[data-collection-add]', (e) => { $('body').on('click', '*[data-collection-add]', (e) => {
e.stopPropagation() e.stopPropagation()
const collectionId = $(e.target).attr('data-collection-add') const collectionId = $(e.target).attr('data-collection-add')
const collectionContainer = $('*[data-collection="' + collectionId + '"]') const collectionContainer = $('*[data-collection="' + collectionId + '"]')
const prototypeContent = $('#' + collectionId).html() const prototypeContent = $('#' + collectionId).html()
let name = 0 let name = 0
collectionContainer.find('*[data-collection-item]').each(function() { collectionContainer.find('*[data-collection-item]').each(function () {
var n = parseInt($(this).attr('data-collection-item')) const n = parseInt($(this).attr('data-collection-item'))
if (n >= name) { if (n >= name) {
name = n + 1 name = n + 1
} }
}) })
collectionContainer.append(prototypeContent) collectionContainer.append(prototypeContent)
const item = collectionContainer.children('*[data-collection-item]:last-child') const item = collectionContainer.children('*[data-collection-item]:last-child')
const deleteBtn = $('<span data-collection-delete="__name__" class="fa fa-trash"></span>') const deleteBtn = $('<span data-collection-delete="__name__" class="fa fa-trash"></span>')
item.find('*[data-collection-delete-container]').first().append(deleteBtn) item.find('*[data-collection-delete-container]').first().append(deleteBtn)
item.html(item.html().replace(/__name__/g, name)) item.html(item.html().replace(/__name__/g, name))
item.attr('data-collection-item', name) item.attr('data-collection-item', name)
collectionContainer.trigger('collection.update'); collectionContainer.trigger('collection.update')
}); })
$('*[data-collection]').trigger('collection.init'); $('*[data-collection]').trigger('collection.init')
} }
module.exports = FormCollection; module.exports = FormCollection

View File

@ -1,15 +1,15 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('body').on('submit', '*[data-form-confirm]', function(e) { $('body').on('submit', '*[data-form-confirm]', function (e) {
let message = $(this).attr('data-form-confirm'); let message = $(this).attr('data-form-confirm')
if (!message) { if (!message) {
message = 'Confimez-vous cette action ?'; message = 'Confimez-vous cette action ?'
} }
if (!confirm(message)) { if (!confirm(message)) {
e.preventDefault(); e.preventDefault()
} }
}) })
}; }

View File

@ -1,31 +1,31 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('body').on('change', '.custom-file-input', function(event) { $('body').on('change', '.custom-file-input', function (event) {
let inputFile = event.currentTarget; const inputFile = event.currentTarget
$(inputFile).parent() $(inputFile).parent()
.find('.custom-file-label') .find('.custom-file-label')
.html(inputFile.files[0].name); .html(inputFile.files[0].name)
}); })
$('.nav a').each(function() { $('.nav a').each(function () {
const link = $(this) const link = $(this)
const href = link.attr('href') const href = link.attr('href')
if (href.substr(0, 1) !== '#') { if (href.substr(0, 1) !== '#') {
return return
} }
const tab = $('.tab-pane ' + href) const tab = $('.tab-pane ' + href)
if (!tab.length) { if (!tab.length) {
return return
} }
if (tab.find('.form-error-message').length) { if (tab.find('.form-error-message').length) {
link.addClass('border border-danger') link.addClass('border border-danger')
link.click() link.click()
} }
}) })
}; }

View File

@ -1,56 +1,56 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
let click = 0; let click = 0
$('body').on('click', '*[data-modal]', (e) => { $('body').on('click', '*[data-modal]', (e) => {
e.preventDefault(); e.preventDefault()
e.stopPropagation(); e.stopPropagation()
++click; ++click
window.setTimeout(() => { window.setTimeout(() => {
if (click !== 1) { if (click !== 1) {
click = 0; click = 0
return; return
} }
click = 0; click = 0
let container = $('#modal-container'); let container = $('#modal-container')
const body = $('body') const body = $('body')
if (!container.length) { if (!container.length) {
container = $('<div id="modal-container" class="modal">'); container = $('<div id="modal-container" class="modal">')
body.append(container); body.append(container)
} }
const loader = $('<div style="position: absolute; top: 25vh; left: 50vw; z-index: 2000">'); const loader = $('<div style="position: absolute; top: 25vh; left: 50vw; z-index: 2000">')
loader.html('<div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div>'); loader.html('<div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div>')
body.append(loader); body.append(loader)
container.html(''); container.html('')
let url = $(e.target).attr('data-modal'); let url = $(e.target).attr('data-modal')
if (!url) { if (!url) {
url = $(e.target).parents('*[data-modal]').first().attr('data-modal'); url = $(e.target).parents('*[data-modal]').first().attr('data-modal')
} }
$(container).modal('show'); $(container).modal('show')
container.load(url, function() { container.load(url, function () {
loader.remove() loader.remove()
}); })
}, 250) }, 250)
}); })
const urlParams = new URLSearchParams(window.location.search) const urlParams = new URLSearchParams(window.location.search)
const dataModal = urlParams.get('data-modal') const dataModal = urlParams.get('data-modal')
if (dataModal) { if (dataModal) {
$('*[data-modal="' + dataModal + '"]').first().click(); $('*[data-modal="' + dataModal + '"]').first().click()
} }
} }

View File

@ -1,47 +1,47 @@
const $ = require('jquery'); const $ = require('jquery')
let Pannel = () => { const Pannel = () => {
let panels = $('.panel'); const panels = $('.panel')
panels.each((i, p) => { panels.each((i, p) => {
let panel = $(p); const panel = $(p)
let content = panel.find('.panel-content').first(); const content = panel.find('.panel-content').first()
let togglers = panel.find('.panel-toggler'); const togglers = panel.find('.panel-toggler')
togglers.each((k, t) => { togglers.each((k, t) => {
let toggler = $(t); const toggler = $(t)
if (!toggler.is('.fa')) { if (!toggler.is('.fa')) {
return; return
} }
if (content.is('.active')) { if (content.is('.active')) {
toggler.removeClass('fa-arrow-down'); toggler.removeClass('fa-arrow-down')
toggler.addClass('fa-arrow-up'); toggler.addClass('fa-arrow-up')
} else { } else {
toggler.removeClass('fa-arrow-up'); toggler.removeClass('fa-arrow-up')
toggler.addClass('fa-arrow-down'); toggler.addClass('fa-arrow-down')
} }
}) })
togglers.click(function(e) { togglers.click(function (e) {
e.stopPropagation(); e.stopPropagation()
content.toggleClass('active'); content.toggleClass('active')
togglers.each((k, t) => { togglers.each((k, t) => {
let toggler = $(t); const toggler = $(t)
if (!toggler.is('.fa')) { if (!toggler.is('.fa')) {
return; return
} }
toggler toggler
.toggleClass('fa-arrow-down') .toggleClass('fa-arrow-down')
.toggleClass('fa-arrow-up'); .toggleClass('fa-arrow-up')
}) })
}); })
}); })
} }
module.exports = Pannel; module.exports = Pannel

View File

@ -1,82 +1,82 @@
const $ = require('jquery'); const $ = require('jquery')
const zxcvbn = require('zxcvbn'); const zxcvbn = require('zxcvbn')
let scoreColors = [ const scoreColors = [
'danger', 'danger',
'danger', 'danger',
'warning', 'warning',
'warning', 'warning',
'success', 'success'
]; ]
let scoreInfos = { const scoreInfos = {
"This is a top-10 common password": "Parmis le top 10 des mots de passes communs", 'This is a top-10 common password': 'Parmis le top 10 des mots de passes communs',
"This is a top-100 common password": "Parmis le top 100 des mots de passes communs", 'This is a top-100 common password': 'Parmis le top 100 des mots de passes communs',
"This is a very common password": "Mot de passe vraiment trop commun", 'This is a very common password': 'Mot de passe vraiment trop commun',
"This is similar to a commonly used password": "Similaire à un mot de passe commun", 'This is similar to a commonly used password': 'Similaire à un mot de passe commun',
"A word by itself is easy to guess": "Ce mot est trop simple à deviner", 'A word by itself is easy to guess': 'Ce mot est trop simple à deviner',
"Names and surnames by themselves are easy to guess": "Les noms ou les surnoms sont simples à deviner", 'Names and surnames by themselves are easy to guess': 'Les noms ou les surnoms sont simples à deviner',
"Common names and surnames are easy to guess": "Les noms ou les surnoms sont simples à deviner", 'Common names and surnames are easy to guess': 'Les noms ou les surnoms sont simples à deviner',
"Straight rows of keys are easy to guess": "Combinaison de touches trop simple", 'Straight rows of keys are easy to guess': 'Combinaison de touches trop simple',
"Short keyboard patterns are easy to guess": "Combinaison de touches trop simple", 'Short keyboard patterns are easy to guess': 'Combinaison de touches trop simple',
"Repeats like \"aaa\" are easy to guess'": "Les répétitions comme \"aaa\" sont simples à deviner", "Repeats like \"aaa\" are easy to guess'": 'Les répétitions comme "aaa" sont simples à deviner',
"Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": "Les répétitions comme \"abcabcabc\" sont simples à deviner", 'Repeats like "abcabcabc" are only slightly harder to guess than "abc"': 'Les répétitions comme "abcabcabc" sont simples à deviner',
"Sequences like abc or 6543 are easy to guess": "Les séquences comme \"abc\" ou \"6543\" sont simples à deviner", 'Sequences like abc or 6543 are easy to guess': 'Les séquences comme "abc" ou "6543" sont simples à deviner',
"Recent years are easy to guess": "Les années sont simples à deviner", 'Recent years are easy to guess': 'Les années sont simples à deviner',
"Dates are often easy to guess": "Les dates sont souvent simples à deviner", 'Dates are often easy to guess': 'Les dates sont souvent simples à deviner'
} }
let checkPassword = function(password, confirmation, indicator, submit) { const checkPassword = function (password, confirmation, indicator, submit) {
let result = zxcvbn(password.val()); const result = zxcvbn(password.val())
let score = result.score; const score = result.score
let cols = indicator.children('.col-sm'); const cols = indicator.children('.col-sm')
let info = indicator.children('.password-strenth-info'); const info = indicator.children('.password-strenth-info')
info.text(''); info.text('')
cols.attr('class', 'col-sm'); cols.attr('class', 'col-sm')
for (var u = 0; u <= 5; u++) { for (let u = 0; u <= 5; u++) {
let col = cols.eq(u); const col = cols.eq(u)
if (u <= score) { if (u <= score) {
col.addClass('bg-' + scoreColors[score]); col.addClass('bg-' + scoreColors[score])
} else {
col.addClass('bg-light');
}
}
console.log(result)
info.text(scoreInfos[result.feedback.warning]);
info.attr('class', 'col-12 password-strenth-info text-' + scoreColors[score]);
if (score < 4 || confirmation.val() !== password.val()) {
submit.attr('disabled', 'disabled');
} else { } else {
submit.removeAttr('disabled'); col.addClass('bg-light')
} }
}
console.log(result)
info.text(scoreInfos[result.feedback.warning])
info.attr('class', 'col-12 password-strenth-info text-' + scoreColors[score])
if (score < 4 || confirmation.val() !== password.val()) {
submit.attr('disabled', 'disabled')
} else {
submit.removeAttr('disabled')
}
} }
module.exports = function() { module.exports = function () {
let passwordNew = $('#form-password-new'); const passwordNew = $('#form-password-new')
let passwordConfirmation = $('#form-password-confirmation'); const passwordConfirmation = $('#form-password-confirmation')
let passwordSubmit = $('#form-password-submit'); const passwordSubmit = $('#form-password-submit')
let passwordStrength = $('#form-password-strength'); const passwordStrength = $('#form-password-strength')
if (passwordStrength.length) { if (passwordStrength.length) {
passwordNew.keyup(function() { passwordNew.keyup(function () {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit); checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit)
}); })
passwordNew.change(function() { passwordNew.change(function () {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit); checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit)
}); })
passwordConfirmation.keyup(function() { passwordConfirmation.keyup(function () {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit); checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit)
}); })
passwordConfirmation.change(function() { passwordConfirmation.change(function () {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit); checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit)
}); })
} }
}; }

View File

@ -1,44 +1,44 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('*[data-pushstate]').click((e) => { $('*[data-pushstate]').click((e) => {
var url = $(e.target).attr('data-pushstate'); const url = $(e.target).attr('data-pushstate')
history.pushState({url: url}, null, url); history.pushState({ url: url }, null, url)
history.replaceState({url: url}, null, url); history.replaceState({ url: url }, null, url)
}); })
let forms = $('form[data-formpushstate]'); const forms = $('form[data-formpushstate]')
let checkAndUsePushState = () => { const checkAndUsePushState = () => {
let state = [window.location.pathname, window.location.search].join(''); const state = [window.location.pathname, window.location.search].join('')
$('*[data-pushstate]').each((i, v) => { $('*[data-pushstate]').each((i, v) => {
let method = 'compare'; let method = 'compare'
if ($(v).is('[data-pushstate-method]')) { if ($(v).is('[data-pushstate-method]')) {
method = $(v).attr('data-pushstate-method') method = $(v).attr('data-pushstate-method')
} }
var isThisOne = false; let isThisOne = false
if (method === 'compare' && $(v).attr('data-pushstate') === state) { if (method === 'compare' && $(v).attr('data-pushstate') === state) {
isThisOne = true; isThisOne = true
} }
if (method === 'indexOf' && state.indexOf($(v).attr('data-pushstate')) !== -1) { if (method === 'indexOf' && state.indexOf($(v).attr('data-pushstate')) !== -1) {
isThisOne = true; isThisOne = true
} }
if (isThisOne) { if (isThisOne) {
$(v).click(); $(v).click()
forms.attr('action', state); forms.attr('action', state)
} }
}); })
} }
checkAndUsePushState(); checkAndUsePushState()
$(window).on('statechange', checkAndUsePushState, false); $(window).on('statechange', checkAndUsePushState, false)
} }

View File

@ -1,27 +1,27 @@
const $ = require('jquery'); const $ = require('jquery')
const Choices = require('choices.js'); const Choices = require('choices.js')
module.exports = function() { module.exports = function () {
$('*[data-rest-choices]').each(function(key, item) { $('*[data-rest-choices]').each(function (key, item) {
const url = $(this).attr('data-rest-choices'); const url = $(this).attr('data-rest-choices')
new Choices(item, { new Choices(item, {
searchPlaceholderValue: 'Chercher', searchPlaceholderValue: 'Chercher'
}).setChoices(function() { }).setChoices(function () {
return fetch(url) return fetch(url)
.then(function(response) { .then(function (response) {
return response.json(); return response.json()
}) })
.then(function(data) { .then(function (data) {
return data.map(function(d) { return data.map(function (d) {
return { return {
label: d.label, label: d.label,
value: d.value value: d.value
}; }
}); })
});
}) })
.then(function(instance) {
});
}) })
}; .then(function (instance) {
})
})
}

View File

@ -2,32 +2,32 @@ const $ = require('jquery')
const Sortable = require('sortablejs').Sortable const Sortable = require('sortablejs').Sortable
module.exports = () => { module.exports = () => {
$('*[data-sortable]').each((i, list) => { $('*[data-sortable]').each((i, list) => {
const element = $(list) const element = $(list)
const route = element.attr('data-sortable-route') const route = element.attr('data-sortable-route')
new Sortable(list, { new Sortable(list, {
handle: '*[data-sortable-item]', handle: '*[data-sortable-item]',
sort: true, sort: true,
animation: 150, animation: 150,
fallbackTolerance: 3, fallbackTolerance: 3,
onEnd: (e) => { onEnd: (e) => {
if (!route) { if (!route) {
return; return
} }
const items = element.find('*[data-sortable-item]') const items = element.find('*[data-sortable-item]')
let datas = {items: []} const datas = { items: [] }
items.each((order, v) => { items.each((order, v) => {
datas.items[$(v).attr('data-sortable-item')] = order + 1; datas.items[$(v).attr('data-sortable-item')] = order + 1
}) })
$.post(route, datas) $.post(route, datas)
.always((data) => { .always((data) => {
document.location.href = document.location.href document.location.href = document.location.href
}) })
} }
}); })
}); })
} }

View File

@ -1,24 +1,24 @@
const $ = require('jquery'); const $ = require('jquery')
let resizeTbody = (tbody) => { const resizeTbody = (tbody) => {
tbody.height($(window).height() - tbody.offset().top - 20); tbody.height($(window).height() - tbody.offset().top - 20)
} }
let tableFixed = () => { const tableFixed = () => {
let tables = $('table[data-table-fixed], *[data-table-fixed] > table'); const tables = $('table[data-table-fixed], *[data-table-fixed] > table')
tables.each((i, t) => { tables.each((i, t) => {
let table = $(t); const table = $(t)
table.addClass('table-fixed'); table.addClass('table-fixed')
let tbody = table.find('tbody'); const tbody = table.find('tbody')
resizeTbody(tbody); resizeTbody(tbody)
$(window).resize(function() { $(window).resize(function () {
resizeTbody(tbody); resizeTbody(tbody)
}); })
}); })
} }
module.exports = tableFixed; module.exports = tableFixed

View File

@ -1,122 +1,121 @@
const $ = require('jquery'); const $ = require('jquery')
const selectedClass = 'table-primary-light'; const selectedClass = 'table-primary-light'
let toggleRow = (row, checkbox, checkboxIsClicked) => { const toggleRow = (row, checkbox, checkboxIsClicked) => {
row.toggleClass(selectedClass); row.toggleClass(selectedClass)
if (checkboxIsClicked) { if (checkboxIsClicked) {
checkbox.prop('checked', checkbox.prop('checked')); checkbox.prop('checked', checkbox.prop('checked'))
return; return
} }
if (checkbox.length) { if (checkbox.length) {
checkbox.prop('checked', !checkbox.prop('checked')); checkbox.prop('checked', !checkbox.prop('checked'))
} }
} }
let unactiveRow = (row, checkbox) => { const unactiveRow = (row, checkbox) => {
row.removeClass(selectedClass); row.removeClass(selectedClass)
if (checkbox.length) { if (checkbox.length) {
checkbox.prop('checked', false); checkbox.prop('checked', false)
}
}
const activeRow = (row, checkbox) => {
row.addClass(selectedClass)
if (checkbox.length) {
checkbox.prop('checked', true)
}
}
const tableSelectable = () => {
const tables = $('*[data-selectable]')
tables.each((i, t) => {
const table = $(t)
const rows = table.find('*[data-selectable-row]')
let selectedIndex = null
const tbody = table.find('tbody')
const resizer = () => {
tbody.height($(window).height() - tbody.offset().top - 20)
} }
window.setInterval(resizer, 1000)
resizer()
$(window).resize(resizer);
((rows) => {
rows.each((i, r) => {
const row = $(r)
const checkbox = row.find('*[data-selectable-checkbox]')
const selectors = row.find('*[data-selectable-selector]');
((row, selectors, checkbox, index) => {
selectors.click((e) => {
if (event.target.nodeName === 'INPUT') {
e.stopPropagation()
checkbox.trigger('clicked')
return toggleRow(row, checkbox, true)
}
if (window.event.ctrlKey) {
e.preventDefault()
return toggleRow(row, checkbox)
}
if (window.event.button === 0) {
if (!window.event.ctrlKey && !window.event.shiftKey) {
rows.each((z, r2) => {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'))
})
toggleRow(row, checkbox)
if (row.hasClass(selectedClass)) {
selectedIndex = index
} else {
selectedIndex = null
}
return
}
if (window.event.shiftKey) {
if (selectedIndex !== null) {
rows.each((z, r2) => {
if (selectedIndex <= index) {
if (z >= selectedIndex && z <= index) {
activeRow($(r2), $(r2).find('*[data-selectable-checkbox]'))
} else {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'))
}
} else {
if (z <= selectedIndex && z >= index) {
activeRow($(r2), $(r2).find('*[data-selectable-checkbox]'))
} else {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'))
}
}
})
// selectedIndex = index;
}
}
}
})
})(row, selectors, checkbox, i)
})
})(rows)
})
} }
let activeRow = (row, checkbox) => { module.exports = tableSelectable
row.addClass(selectedClass);
if (checkbox.length) {
checkbox.prop('checked', true);
}
}
let tableSelectable = () => {
let tables = $('*[data-selectable]');
tables.each((i, t) => {
var table = $(t);
var rows = table.find('*[data-selectable-row]');
let selectedIndex = null;
var tbody = table.find('tbody');
var resizer = () => {
tbody.height($(window).height() - tbody.offset().top - 20);
}
window.setInterval(resizer, 1000);
resizer();
$(window).resize(resizer);
((rows) => {
rows.each((i, r) => {
let row = $(r);
let checkbox = row.find('*[data-selectable-checkbox]');
let selectors = row.find('*[data-selectable-selector]');
((row, selectors, checkbox, index) => {
selectors.click((e) => {
if (event.target.nodeName === 'INPUT') {
e.stopPropagation();
checkbox.trigger('clicked');
return toggleRow(row, checkbox, true);
}
if (window.event.ctrlKey) {
e.preventDefault();
return toggleRow(row, checkbox);
}
if (window.event.button === 0) {
if (!window.event.ctrlKey && !window.event.shiftKey) {
rows.each((z, r2) => {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
});
toggleRow(row, checkbox);
if (row.hasClass(selectedClass)) {
selectedIndex = index;
} else {
selectedIndex = null;
}
return;
}
if (window.event.shiftKey) {
if (selectedIndex !== null) {
rows.each((z, r2) => {
if (selectedIndex <= index) {
if (z >= selectedIndex && z <= index) {
activeRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
} else {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
}
} else {
if (z <= selectedIndex && z >= index) {
activeRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
} else {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
}
}
});
//selectedIndex = index;
}
}
}
});
})(row, selectors, checkbox, i);
});
})(rows);
});
}
module.exports = tableSelectable;

View File

@ -1,11 +1,11 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('.toast').toast({ $('.toast').toast({
animation: true, animation: true,
autohide: true, autohide: true,
delay: 5000, delay: 5000
}); })
$('.toast').toast('show'); $('.toast').toast('show')
}; }

View File

@ -1,5 +1,5 @@
const $ = require('jquery'); const $ = require('jquery')
module.exports = function() { module.exports = function () {
$('*[data-toggle="tooltip"]').tooltip(); $('*[data-toggle="tooltip"]').tooltip()
}; }

981
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,12 @@
"@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1",
"@vue/babel-preset-jsx": "^1.2.4", "@vue/babel-preset-jsx": "^1.2.4",
"core-js": "^3.0.0", "core-js": "^3.0.0",
"eslint": "^7.28.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-vue": "^7.11.1",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"node-sass": "^4.13.1", "node-sass": "^4.13.1",
"regenerator-runtime": "^0.13.2", "regenerator-runtime": "^0.13.2",

691
yarn.lock

File diff suppressed because it is too large Load Diff