mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-29 18:30:09 +00:00
made the keyhandler mode-aware...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
093ff72d5c
commit
d9a59440fd
@ -177,9 +177,9 @@ Priority work
|
|||||||
[X] 100% actions
|
[X] 100% actions
|
||||||
[X] bug: shifting up to new ribbon pushes the current row down...
|
[X] bug: shifting up to new ribbon pushes the current row down...
|
||||||
| before starting on a fix, need to cleanup the code from old hacks and workarounds...
|
| before starting on a fix, need to cleanup the code from old hacks and workarounds...
|
||||||
[_] 37% Preview II (optional features)
|
[_] 39% Preview II (optional features)
|
||||||
[_] 0% make things modular and reusable
|
[_] 14% make things modular and reusable
|
||||||
[_] make the keyboard handler local to selector (mode-aware)
|
[X] make the keyboard handler local to selector (mode-aware)
|
||||||
[_] prefix an ID to all selectors to make their actions "local"
|
[_] prefix an ID to all selectors to make their actions "local"
|
||||||
[_] avoid use of id html attr
|
[_] avoid use of id html attr
|
||||||
[_] avoid use of globals
|
[_] avoid use of globals
|
||||||
@ -204,7 +204,6 @@ Priority work
|
|||||||
[_] .dblclick(...) does not work...
|
[_] .dblclick(...) does not work...
|
||||||
[_] .dragable(...) does not work...
|
[_] .dragable(...) does not work...
|
||||||
[_] slideshow...
|
[_] slideshow...
|
||||||
[_] make keyboard handler mode-aware...
|
|
||||||
| this is needed to disable navigation keys in setup-mode, for example...
|
| this is needed to disable navigation keys in setup-mode, for example...
|
||||||
[X] 100% serialization/deserialization
|
[X] 100% serialization/deserialization
|
||||||
[X] JSON loader/unloader
|
[X] JSON loader/unloader
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
// - are the benefits worth the code bloat?
|
// - are the benefits worth the code bloat?
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
var ImageGrid = {
|
var ImageGrid = {
|
||||||
// this can be serialized...
|
// this can be serialized...
|
||||||
// NOTE: to load a serialized set of options use ImageGrid.set(options)...
|
// NOTE: to load a serialized set of options use ImageGrid.set(options)...
|
||||||
@ -524,6 +525,8 @@ function toKeyName(code){
|
|||||||
// XXX this must create it's own overlay...
|
// XXX this must create it's own overlay...
|
||||||
function showInOverlay(obj){
|
function showInOverlay(obj){
|
||||||
obj.click(function(){ return false })
|
obj.click(function(){ return false })
|
||||||
|
// XXX
|
||||||
|
$('.viewer').addClass('overlay-mode')
|
||||||
// clean things up...
|
// clean things up...
|
||||||
$('.overlay .content').children().remove()
|
$('.overlay .content').children().remove()
|
||||||
// put it in the overlay...
|
// put it in the overlay...
|
||||||
@ -536,6 +539,7 @@ function showInOverlay(obj){
|
|||||||
$('.overlay .content')
|
$('.overlay .content')
|
||||||
.children()
|
.children()
|
||||||
.remove()
|
.remove()
|
||||||
|
$('.overlay-mode').removeClass('overlay-mode')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.fadeIn()
|
.fadeIn()
|
||||||
@ -1113,10 +1117,10 @@ function setupEvents(){
|
|||||||
// keyboard...
|
// keyboard...
|
||||||
if(DEBUG){
|
if(DEBUG){
|
||||||
$(document)
|
$(document)
|
||||||
.keydown(makeKeyboardHandler(keybindings, ignorekeys, function(k){alert(k)}))
|
.keydown(makeKeyboardHandler(keybindings, function(k){alert(k)}))
|
||||||
} else {
|
} else {
|
||||||
$(document)
|
$(document)
|
||||||
.keydown(makeKeyboardHandler(keybindings, ignorekeys))
|
.keydown(makeKeyboardHandler(keybindings))
|
||||||
}
|
}
|
||||||
// swipe...
|
// swipe...
|
||||||
$('.viewer')
|
$('.viewer')
|
||||||
@ -1474,52 +1478,58 @@ var KEYBOARD_HANDLER_PROPAGATE = false
|
|||||||
*
|
*
|
||||||
* XXX might need to add meta information to generate sensible help...
|
* XXX might need to add meta information to generate sensible help...
|
||||||
*/
|
*/
|
||||||
function makeKeyboardHandler(keybindings, ignore, unhandled){
|
function makeKeyboardHandler(keybindings, unhandled){
|
||||||
if(unhandled == null){
|
if(unhandled == null){
|
||||||
unhandled = function(){return false}
|
unhandled = function(){return false}
|
||||||
}
|
}
|
||||||
return function(evt){
|
return function(evt){
|
||||||
var key = evt.keyCode
|
for(var mode in keybindings){
|
||||||
if(ignore != null && ignore.indexOf(key) != -1){
|
if($(mode).length > 0){
|
||||||
return true
|
var bindings = keybindings[mode]
|
||||||
}
|
|
||||||
// XXX ugly...
|
|
||||||
var modifers = evt.ctrlKey ? 'ctrl' : ''
|
|
||||||
modifers += evt.altKey ? (modifers != '' ? '+alt' : 'alt') : ''
|
|
||||||
modifers += evt.shiftKey ? (modifers != '' ? '+shift' : 'shift') : ''
|
|
||||||
|
|
||||||
var handler = keybindings[key]
|
var key = evt.keyCode
|
||||||
|
if(bindings.ignore != null && bindings.ignore.indexOf(key) != -1){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// XXX ugly...
|
||||||
|
var modifers = evt.ctrlKey ? 'ctrl' : ''
|
||||||
|
modifers += evt.altKey ? (modifers != '' ? '+alt' : 'alt') : ''
|
||||||
|
modifers += evt.shiftKey ? (modifers != '' ? '+shift' : 'shift') : ''
|
||||||
|
|
||||||
// alias...
|
var handler = bindings[key]
|
||||||
while (typeof(handler) == typeof(123)) {
|
|
||||||
handler = keybindings[handler]
|
// alias...
|
||||||
}
|
while (typeof(handler) == typeof(123)) {
|
||||||
// no handler...
|
handler = bindings[handler]
|
||||||
if(handler == null){
|
}
|
||||||
return unhandled(key)
|
// no handler...
|
||||||
}
|
if(handler == null){
|
||||||
// Array, lisp style with docs...
|
return unhandled(key)
|
||||||
// XXX for some odd reason in chrome typeof([]) == typeof({})!!!
|
}
|
||||||
if(typeof(handler) == typeof([]) && handler.constructor.name == 'Array'){
|
// Array, lisp style with docs...
|
||||||
// we do not care about docs here, so just get the handler...
|
// XXX for some odd reason in chrome typeof([]) == typeof({})!!!
|
||||||
handler = handler[0]
|
if(typeof(handler) == typeof([]) && handler.constructor.name == 'Array'){
|
||||||
}
|
// we do not care about docs here, so just get the handler...
|
||||||
// complex handler...
|
handler = handler[0]
|
||||||
if(typeof(handler) == typeof({})){
|
}
|
||||||
var callback = handler[modifers]
|
// complex handler...
|
||||||
if(callback == null){
|
if(typeof(handler) == typeof({})){
|
||||||
callback = handler['default']
|
var callback = handler[modifers]
|
||||||
|
if(callback == null){
|
||||||
|
callback = handler['default']
|
||||||
|
}
|
||||||
|
if(callback != null){
|
||||||
|
var res = callback()
|
||||||
|
return KEYBOARD_HANDLER_PROPAGATE&&res?true:false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// simple callback...
|
||||||
|
var res = handler()
|
||||||
|
return KEYBOARD_HANDLER_PROPAGATE&&res?true:false
|
||||||
|
}
|
||||||
|
return unhandled(key)
|
||||||
}
|
}
|
||||||
if(callback != null){
|
|
||||||
var res = callback()
|
|
||||||
return KEYBOARD_HANDLER_PROPAGATE&&res?true:false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// simple callback...
|
|
||||||
var res = handler()
|
|
||||||
return KEYBOARD_HANDLER_PROPAGATE&&res?true:false
|
|
||||||
}
|
}
|
||||||
return unhandled(key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1642,6 +1652,7 @@ ImageGrid.GROUP('Configuration and Help',
|
|||||||
},
|
},
|
||||||
function(e){return e.click_handler})
|
function(e){return e.click_handler})
|
||||||
}),
|
}),
|
||||||
|
// XXX do not use global keybindings...
|
||||||
ImageGrid.ACTION({
|
ImageGrid.ACTION({
|
||||||
title: 'Keyboard configuration',
|
title: 'Keyboard configuration',
|
||||||
doc: 'Show keyboard configuration interface.',
|
doc: 'Show keyboard configuration interface.',
|
||||||
@ -1649,39 +1660,52 @@ ImageGrid.GROUP('Configuration and Help',
|
|||||||
function showKeyboardBindings(){
|
function showKeyboardBindings(){
|
||||||
// build reverse key index...
|
// build reverse key index...
|
||||||
var bindings = {}
|
var bindings = {}
|
||||||
for(var k in keybindings){
|
for(var m in keybindings){
|
||||||
var id
|
var mode_bindings = keybindings[m]
|
||||||
var v = keybindings[k]
|
|
||||||
|
|
||||||
// alias...
|
// XXX do the doc for the mode...
|
||||||
while (typeof(v) == typeof(123)) {
|
// XXX
|
||||||
v = keybindings[v]
|
|
||||||
}
|
for(var k in mode_bindings){
|
||||||
// Array, lisp style with docs...
|
// XXX skip doc attrs...
|
||||||
if(typeof(v) == typeof([]) && v.constructor.name == 'Array'){
|
if(k == 'title' || k == 'doc' || k == 'ignore'){
|
||||||
// XXX what do we do here???
|
continue
|
||||||
}
|
|
||||||
// function...
|
|
||||||
if(typeof(v) == typeof(function(){})){
|
|
||||||
id = v.id != null ? v.id : v.name
|
|
||||||
}
|
|
||||||
// complex handler...
|
|
||||||
// NOTE: this can contain several key bindings...
|
|
||||||
if(typeof(v) == typeof({})){
|
|
||||||
for(var m in v){
|
|
||||||
id = v[m].id != null ? v[m].id : v[m].name
|
|
||||||
if(bindings[id] == null){
|
|
||||||
bindings[id] = []
|
|
||||||
}
|
|
||||||
bindings[id].push((m=='default'?'':m+'+') + toKeyName(k))
|
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
|
var id
|
||||||
|
var v = mode_bindings[k]
|
||||||
|
|
||||||
|
// alias...
|
||||||
|
while (typeof(v) == typeof(123)) {
|
||||||
|
v = mode_bindings[v]
|
||||||
|
}
|
||||||
|
// Array, lisp style with docs...
|
||||||
|
if(typeof(v) == typeof([]) && v.constructor.name == 'Array'){
|
||||||
|
// XXX what do we do here???
|
||||||
|
}
|
||||||
|
// function...
|
||||||
|
if(typeof(v) == typeof(function(){})){
|
||||||
|
id = v.id != null ? v.id : v.name
|
||||||
|
}
|
||||||
|
// complex handler...
|
||||||
|
// NOTE: this can contain several key bindings...
|
||||||
|
if(typeof(v) == typeof({})){
|
||||||
|
for(var m in v){
|
||||||
|
id = v[m].id != null ? v[m].id : v[m].name
|
||||||
|
if(bindings[id] == null){
|
||||||
|
bindings[id] = []
|
||||||
|
}
|
||||||
|
bindings[id].push((m=='default'?'':m+'+') + toKeyName(k))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bindings[id] == null){
|
||||||
|
bindings[id] = []
|
||||||
|
}
|
||||||
|
bindings[id].push(toKeyName(k))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bindings[id] == null){
|
|
||||||
bindings[id] = []
|
|
||||||
}
|
|
||||||
bindings[id].push(toKeyName(k))
|
|
||||||
}
|
}
|
||||||
showOptionsUI(ImageGrid.actions,
|
showOptionsUI(ImageGrid.actions,
|
||||||
function(e){
|
function(e){
|
||||||
|
|||||||
@ -1,110 +1,136 @@
|
|||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
// NOTE: use String.fromCharCode(code)...
|
// NOTE: use String.fromCharCode(code)...
|
||||||
// list of keys to be ignored by handler but still handled by the browser...
|
// list of keys to be ignored by handler but still handled by the browser...
|
||||||
var ignorekeys = [
|
|
||||||
116, // F5
|
|
||||||
123, // F12
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
var keybindings = {
|
var keybindings = {
|
||||||
// togglable modes and options...
|
'.overlay-mode': {
|
||||||
191: {
|
title: 'Overlay mode',
|
||||||
'default': ImageGrid.showKeyboardBindings, // ?
|
doc: 'overlay mode key bindings.',
|
||||||
'ctrl': ImageGrid.showSetup, // ctrl+?
|
|
||||||
|
ignore: [
|
||||||
|
37, // Left
|
||||||
|
39, // Right
|
||||||
|
36, // Home
|
||||||
|
32, // Space
|
||||||
|
35, // End
|
||||||
|
38, // Up
|
||||||
|
40, // Down
|
||||||
|
],
|
||||||
|
|
||||||
|
27: ImageGrid.closeOverlay, // Esc
|
||||||
},
|
},
|
||||||
80: ImageGrid.showSetup, // p
|
|
||||||
70: ImageGrid.toggleSingleImageMode, // f
|
|
||||||
13: 70, // Enter
|
|
||||||
83: ImageGrid.toggleSingleRibbonMode, // s
|
|
||||||
84: ImageGrid.toggleSingleImageModeTransitions, // t
|
|
||||||
65: ImageGrid.toggleTransitions, // a
|
|
||||||
9: ImageGrid.toggleControls, // tab
|
|
||||||
66: ImageGrid.toggleBackgroundModes, // b
|
|
||||||
73: ImageGrid.toggleCurrentRibbonOpacity, // i
|
|
||||||
77: toggleMarkers, // m
|
|
||||||
|
|
||||||
87: ImageGrid.saveState, // w
|
|
||||||
|
|
||||||
27: ImageGrid.closeOverlay, // Esc
|
//'*': {
|
||||||
|
// everything except overlays...
|
||||||
|
'.viewer *:not(.overlay-mode *)': {
|
||||||
|
title: 'ALL',
|
||||||
|
doc: 'global key bindings.',
|
||||||
|
|
||||||
// zooming...
|
ignore: [
|
||||||
187: ImageGrid.scaleContainerUp, // +
|
116, // F5
|
||||||
189: ImageGrid.scaleContainerDown, // -
|
123, // F12
|
||||||
// zoom presets...
|
],
|
||||||
48: {
|
|
||||||
'default': ImageGrid.centerCurrentImage, // 0
|
// togglable modes and options...
|
||||||
|
191: {
|
||||||
|
'default': ImageGrid.showKeyboardBindings, // ?
|
||||||
|
'ctrl': ImageGrid.showSetup, // ctrl+?
|
||||||
|
},
|
||||||
|
80: ImageGrid.showSetup, // p
|
||||||
|
70: ImageGrid.toggleSingleImageMode, // f
|
||||||
|
13: 70, // Enter
|
||||||
|
83: ImageGrid.toggleSingleRibbonMode, // s
|
||||||
|
84: ImageGrid.toggleSingleImageModeTransitions, // t
|
||||||
|
65: ImageGrid.toggleTransitions, // a
|
||||||
|
9: ImageGrid.toggleControls, // tab
|
||||||
|
66: ImageGrid.toggleBackgroundModes, // b
|
||||||
|
73: ImageGrid.toggleCurrentRibbonOpacity, // i
|
||||||
|
77: toggleMarkers, // m
|
||||||
|
|
||||||
|
87: ImageGrid.saveState, // w
|
||||||
|
|
||||||
|
27: ImageGrid.closeOverlay, // Esc
|
||||||
|
|
||||||
|
// zooming...
|
||||||
|
187: ImageGrid.scaleContainerUp, // +
|
||||||
|
189: ImageGrid.scaleContainerDown, // -
|
||||||
|
// zoom presets...
|
||||||
|
48: {
|
||||||
|
'default': ImageGrid.centerCurrentImage, // 0
|
||||||
|
// XXX make this into a real action...
|
||||||
|
'ctrl': ImageGrid.fitImage, // ctrl+0
|
||||||
|
},
|
||||||
|
49: ImageGrid.fitImage, // 1
|
||||||
|
50: ImageGrid.fitTwoImages, // 2
|
||||||
|
51: ImageGrid.fitThreeImages, // 3
|
||||||
|
52: ImageGrid.fitFourImages, // 4
|
||||||
|
53: ImageGrid.fitFiveImages, // 5
|
||||||
|
54: ImageGrid.fitSixImages, // 6
|
||||||
|
55: ImageGrid.fitSevenImages, // 7
|
||||||
|
56: ImageGrid.fitEightImages, // 8
|
||||||
|
57: ImageGrid.fitNineImages, // 9
|
||||||
|
|
||||||
|
|
||||||
|
// navigation...
|
||||||
|
36: ImageGrid.firstImage, // Home
|
||||||
|
219: 36, // [
|
||||||
|
35: ImageGrid.lastImage, // End
|
||||||
|
221: 35, // ]
|
||||||
|
37: {
|
||||||
|
'default': ImageGrid.prevImage, // Right
|
||||||
|
'ctrl': ImageGrid.prevScreenImages, // ctrl-Right
|
||||||
|
'alt': ImageGrid.prevScreenImages, // alt-Right
|
||||||
|
},
|
||||||
|
8: 37, // BkSp
|
||||||
|
188: 37, // <
|
||||||
|
39: {
|
||||||
|
'default': ImageGrid.nextImage, // Left
|
||||||
|
'ctrl': ImageGrid.nextScreenImages, // ctrl-Left
|
||||||
|
'alt': ImageGrid.nextScreenImages, // alt-Left
|
||||||
|
},
|
||||||
|
32: 39, // Space
|
||||||
|
190: 39, // >
|
||||||
|
186: ImageGrid.prevScreenImages, // ;
|
||||||
|
222: ImageGrid.nextScreenImages, // '
|
||||||
|
// move view...
|
||||||
|
// XXX should these be s-up, s-down, ... ??
|
||||||
|
75: ImageGrid.moveViewUp, // k
|
||||||
|
74: ImageGrid.moveViewDown, // j
|
||||||
|
72: ImageGrid.moveViewLeft, // h
|
||||||
|
76: ImageGrid.moveViewRight, // l
|
||||||
|
// XXX use this to open...
|
||||||
|
//79: ImageGrid.centerCurrentImage, // o
|
||||||
|
|
||||||
|
|
||||||
|
// combined navigation with actions..
|
||||||
|
38: {
|
||||||
|
'default': ImageGrid.focusAboveRibbon, // Up
|
||||||
|
'shift': ImageGrid.shiftImageUp, // shift-Up
|
||||||
|
'ctrl+shift': ImageGrid.shiftImageUpNewRibbon // ctrl-shift-Up
|
||||||
|
},
|
||||||
|
40: {
|
||||||
|
'default': ImageGrid.focusBelowRibbon, // Down
|
||||||
|
'shift': ImageGrid.shiftImageDown, // shift-Down
|
||||||
|
'ctrl+shift': ImageGrid.shiftImageDownNewRibbon // ctrl-shift-Down
|
||||||
|
},
|
||||||
|
|
||||||
|
// misc actions...
|
||||||
|
82: ImageGrid.reverseImageOrder, // r
|
||||||
|
|
||||||
|
|
||||||
|
// ignore the modifiers (shift, alt, ctrl, caps)...
|
||||||
|
16: function(){},
|
||||||
|
17: 16,
|
||||||
|
18: 16,
|
||||||
|
20: 16, // Caps Lock
|
||||||
|
|
||||||
|
// refresh...
|
||||||
// XXX make this into a real action...
|
// XXX make this into a real action...
|
||||||
'ctrl': ImageGrid.fitImage, // ctrl+0
|
116: function(){ return DEBUG?true:false }, // F5
|
||||||
},
|
112: 116, // F12
|
||||||
49: ImageGrid.fitImage, // 1
|
}
|
||||||
50: ImageGrid.fitTwoImages, // 2
|
|
||||||
51: ImageGrid.fitThreeImages, // 3
|
|
||||||
52: ImageGrid.fitFourImages, // 4
|
|
||||||
53: ImageGrid.fitFiveImages, // 5
|
|
||||||
54: ImageGrid.fitSixImages, // 6
|
|
||||||
55: ImageGrid.fitSevenImages, // 7
|
|
||||||
56: ImageGrid.fitEightImages, // 8
|
|
||||||
57: ImageGrid.fitNineImages, // 9
|
|
||||||
|
|
||||||
|
|
||||||
// navigation...
|
|
||||||
36: ImageGrid.firstImage, // Home
|
|
||||||
219: 36, // [
|
|
||||||
35: ImageGrid.lastImage, // End
|
|
||||||
221: 35, // ]
|
|
||||||
37: {
|
|
||||||
'default': ImageGrid.prevImage, // Right
|
|
||||||
'ctrl': ImageGrid.prevScreenImages, // ctrl-Right
|
|
||||||
'alt': ImageGrid.prevScreenImages, // alt-Right
|
|
||||||
},
|
|
||||||
8: 37, // BkSp
|
|
||||||
188: 37, // <
|
|
||||||
39: {
|
|
||||||
'default': ImageGrid.nextImage, // Left
|
|
||||||
'ctrl': ImageGrid.nextScreenImages, // ctrl-Left
|
|
||||||
'alt': ImageGrid.nextScreenImages, // alt-Left
|
|
||||||
},
|
|
||||||
32: 39, // Space
|
|
||||||
190: 39, // >
|
|
||||||
186: ImageGrid.prevScreenImages, // ;
|
|
||||||
222: ImageGrid.nextScreenImages, // '
|
|
||||||
// move view...
|
|
||||||
// XXX should these be s-up, s-down, ... ??
|
|
||||||
75: ImageGrid.moveViewUp, // k
|
|
||||||
74: ImageGrid.moveViewDown, // j
|
|
||||||
72: ImageGrid.moveViewLeft, // h
|
|
||||||
76: ImageGrid.moveViewRight, // l
|
|
||||||
// XXX use this to open...
|
|
||||||
//79: ImageGrid.centerCurrentImage, // o
|
|
||||||
|
|
||||||
|
|
||||||
// combined navigation with actions..
|
|
||||||
40: {
|
|
||||||
'default': ImageGrid.focusBelowRibbon, // Down
|
|
||||||
'shift': ImageGrid.shiftImageDown, // shift-Down
|
|
||||||
'ctrl+shift': ImageGrid.shiftImageDownNewRibbon // ctrl-shift-Down
|
|
||||||
},
|
|
||||||
38: {
|
|
||||||
'default': ImageGrid.focusAboveRibbon, // Up
|
|
||||||
'shift': ImageGrid.shiftImageUp, // shift-Up
|
|
||||||
'ctrl+shift': ImageGrid.shiftImageUpNewRibbon // ctrl-shift-Up
|
|
||||||
},
|
|
||||||
|
|
||||||
// misc actions...
|
|
||||||
82: ImageGrid.reverseImageOrder, // r
|
|
||||||
|
|
||||||
|
|
||||||
// ignore the modifiers (shift, alt, ctrl, caps)...
|
|
||||||
16: function(){},
|
|
||||||
17: 16,
|
|
||||||
18: 16,
|
|
||||||
20: 16, // Caps Lock
|
|
||||||
|
|
||||||
// refresh...
|
|
||||||
// XXX make this into a real action...
|
|
||||||
116: function(){ return DEBUG?true:false }, // F5
|
|
||||||
112: 116, // F12
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user