|
|
|
@ -10,13 +10,16 @@ import blur from "../../../helpers/blur";
@@ -10,13 +10,16 @@ import blur from "../../../helpers/blur";
|
|
|
|
|
import { deferredPromise } from "../../../helpers/cancellablePromise"; |
|
|
|
|
import { attachClickEvent } from "../../../helpers/dom"; |
|
|
|
|
import findUpClassName from "../../../helpers/dom/findUpClassName"; |
|
|
|
|
import { requestFile } from "../../../helpers/files"; |
|
|
|
|
import highlightningColor from "../../../helpers/highlightningColor"; |
|
|
|
|
import { copy } from "../../../helpers/object"; |
|
|
|
|
import { AccountWallPapers, WallPaper } from "../../../layer"; |
|
|
|
|
import sequentialDom from "../../../helpers/sequentialDom"; |
|
|
|
|
import { AccountWallPapers, PhotoSize, WallPaper } from "../../../layer"; |
|
|
|
|
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager"; |
|
|
|
|
import appDownloadManager from "../../../lib/appManagers/appDownloadManager"; |
|
|
|
|
import appImManager from "../../../lib/appManagers/appImManager"; |
|
|
|
|
import appStateManager, { STATE_INIT } from "../../../lib/appManagers/appStateManager"; |
|
|
|
|
import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; |
|
|
|
|
import appStateManager, { Theme, STATE_INIT } from "../../../lib/appManagers/appStateManager"; |
|
|
|
|
import apiManager from "../../../lib/mtproto/mtprotoworker"; |
|
|
|
|
import rootScope from "../../../lib/rootScope"; |
|
|
|
|
import Button from "../../button"; |
|
|
|
@ -26,43 +29,45 @@ import { SliderSuperTab } from "../../slider";
@@ -26,43 +29,45 @@ import { SliderSuperTab } from "../../slider";
|
|
|
|
|
import { wrapPhoto } from "../../wrappers"; |
|
|
|
|
import AppBackgroundColorTab from "./backgroundColor"; |
|
|
|
|
|
|
|
|
|
let uploadTempId = 0; |
|
|
|
|
|
|
|
|
|
export default class AppBackgroundTab extends SliderSuperTab { |
|
|
|
|
private grid: HTMLElement; |
|
|
|
|
private tempId = 0; |
|
|
|
|
private theme: Theme; |
|
|
|
|
private clicked: Set<string> = new Set(); |
|
|
|
|
private blurCheckboxField: CheckboxField; |
|
|
|
|
|
|
|
|
|
init() { |
|
|
|
|
this.container.classList.add('background-container', 'background-image-container'); |
|
|
|
|
this.setTitle('ChatBackground'); |
|
|
|
|
|
|
|
|
|
this.theme = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme); |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
const container = generateSection(this.scrollable); |
|
|
|
|
|
|
|
|
|
//const uploadButton = Button('btn-primary btn-transparent', {icon: 'cameraadd', text: 'ChatBackground.UploadWallpaper', disabled: true});
|
|
|
|
|
const uploadButton = Button('btn-primary btn-transparent', {icon: 'cameraadd', text: 'ChatBackground.UploadWallpaper'}); |
|
|
|
|
const colorButton = Button('btn-primary btn-transparent', {icon: 'colorize', text: 'SetColor'}); |
|
|
|
|
const resetButton = Button('btn-primary btn-transparent', {icon: 'favourites', text: 'Appearance.Reset'}); |
|
|
|
|
|
|
|
|
|
attachClickEvent(uploadButton, this.onUploadClick, {listenerSetter: this.listenerSetter}); |
|
|
|
|
|
|
|
|
|
attachClickEvent(colorButton, () => { |
|
|
|
|
new AppBackgroundColorTab(this.slider).open(); |
|
|
|
|
}, {listenerSetter: this.listenerSetter}); |
|
|
|
|
|
|
|
|
|
attachClickEvent(resetButton, () => { |
|
|
|
|
const defaultTheme = STATE_INIT.settings.themes.find(t => t.name === theme.name); |
|
|
|
|
if(defaultTheme) { |
|
|
|
|
++tempId; |
|
|
|
|
theme.background = copy(defaultTheme.background); |
|
|
|
|
appStateManager.pushToState('settings', rootScope.settings); |
|
|
|
|
appImManager.applyCurrentTheme(undefined, undefined, true); |
|
|
|
|
blurCheckboxField.setValueSilently(theme.background.blur); |
|
|
|
|
} |
|
|
|
|
}, {listenerSetter: this.listenerSetter}); |
|
|
|
|
attachClickEvent(resetButton, this.onResetClick, {listenerSetter: this.listenerSetter}); |
|
|
|
|
|
|
|
|
|
const theme = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme); |
|
|
|
|
const blurCheckboxField = new CheckboxField({ |
|
|
|
|
const blurCheckboxField = this.blurCheckboxField = new CheckboxField({ |
|
|
|
|
text: 'ChatBackground.Blur', |
|
|
|
|
name: 'blur', |
|
|
|
|
checked: theme.background.blur, |
|
|
|
|
checked: this.theme.background.blur, |
|
|
|
|
withRipple: true |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.listenerSetter.add(blurCheckboxField.input, 'change', () => { |
|
|
|
|
theme.background.blur = blurCheckboxField.input.checked; |
|
|
|
|
this.theme.background.blur = blurCheckboxField.input.checked; |
|
|
|
|
appStateManager.pushToState('settings', rootScope.settings); |
|
|
|
|
|
|
|
|
|
const active = grid.querySelector('.active') as HTMLElement; |
|
|
|
@ -70,104 +75,135 @@ export default class AppBackgroundTab extends SliderSuperTab {
@@ -70,104 +75,135 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|
|
|
|
|
|
|
|
|
// * wait for animation end
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
setBackgroundDocument(active.dataset.slug, appDocsManager.getDoc(active.dataset.docId)); |
|
|
|
|
this.setBackgroundDocument(active.dataset.slug, appDocsManager.getDoc(active.dataset.docId)); |
|
|
|
|
}, 100); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
container.append(/* uploadButton, */colorButton, resetButton, blurCheckboxField.label); |
|
|
|
|
container.append(uploadButton, colorButton, resetButton, blurCheckboxField.label); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const grid = document.createElement('div'); |
|
|
|
|
grid.classList.add('grid'); |
|
|
|
|
rootScope.on('background_change', this.setActive); |
|
|
|
|
|
|
|
|
|
const saveToCache = (slug: string, url: string) => { |
|
|
|
|
fetch(url).then(response => { |
|
|
|
|
appDownloadManager.cacheStorage.save('backgrounds/' + slug, response); |
|
|
|
|
apiManager.invokeApiHashable('account.getWallPapers').then((accountWallpapers) => { |
|
|
|
|
const wallpapers = (accountWallpapers as AccountWallPapers.accountWallPapers).wallpapers as WallPaper.wallPaper[]; |
|
|
|
|
wallpapers.forEach((wallpaper) => { |
|
|
|
|
this.addWallPaper(wallpaper); |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let tempId = 0; |
|
|
|
|
const setBackgroundDocument = (slug: string, doc: MyDocument) => { |
|
|
|
|
let _tempId = ++tempId; |
|
|
|
|
const middleware = () => _tempId === tempId; |
|
|
|
|
|
|
|
|
|
const download = appDocsManager.downloadDoc(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0); |
|
|
|
|
|
|
|
|
|
const deferred = deferredPromise<void>(); |
|
|
|
|
deferred.addNotifyListener = download.addNotifyListener; |
|
|
|
|
deferred.cancel = download.cancel; |
|
|
|
|
//console.log(accountWallpapers);
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
download.then(() => { |
|
|
|
|
if(!middleware()) { |
|
|
|
|
deferred.resolve(); |
|
|
|
|
return; |
|
|
|
|
const grid = this.grid = document.createElement('div'); |
|
|
|
|
grid.classList.add('grid'); |
|
|
|
|
attachClickEvent(grid, this.onGridClick, {listenerSetter: this.listenerSetter}); |
|
|
|
|
this.scrollable.append(grid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
|
|
|
|
const onReady = (url: string) => { |
|
|
|
|
//const perf = performance.now();
|
|
|
|
|
averageColor(url).then(pixel => { |
|
|
|
|
if(!middleware()) { |
|
|
|
|
deferred.resolve(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
private onUploadClick = () => { |
|
|
|
|
requestFile('image/x-png,image/png,image/jpeg').then(file => { |
|
|
|
|
const id = 'wallpaper-upload-' + ++uploadTempId; |
|
|
|
|
|
|
|
|
|
const thumb = { |
|
|
|
|
_: 'photoSize', |
|
|
|
|
h: 0, |
|
|
|
|
w: 0, |
|
|
|
|
location: {} as any, |
|
|
|
|
size: file.size, |
|
|
|
|
type: 'full', |
|
|
|
|
url: URL.createObjectURL(file) |
|
|
|
|
} as PhotoSize.photoSize; |
|
|
|
|
let document: MyDocument = { |
|
|
|
|
_: 'document', |
|
|
|
|
access_hash: '', |
|
|
|
|
attributes: [], |
|
|
|
|
dc_id: 0, |
|
|
|
|
file_reference: [], |
|
|
|
|
id, |
|
|
|
|
mime_type: file.type, |
|
|
|
|
size: file.size, |
|
|
|
|
downloaded: true, |
|
|
|
|
date: Date.now() / 1000, |
|
|
|
|
url: thumb.url, |
|
|
|
|
pFlags: {}, |
|
|
|
|
thumbs: [thumb], |
|
|
|
|
file_name: file.name |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const hsla = highlightningColor(Array.from(pixel) as any); |
|
|
|
|
//console.log(doc, hsla, performance.now() - perf);
|
|
|
|
|
document = appDocsManager.saveDoc(document); |
|
|
|
|
|
|
|
|
|
background.slug = slug; |
|
|
|
|
background.type = 'image'; |
|
|
|
|
background.highlightningColor = hsla; |
|
|
|
|
appStateManager.pushToState('settings', rootScope.settings); |
|
|
|
|
const docThumb = appPhotosManager.getDocumentCachedThumb(document.id); |
|
|
|
|
docThumb.downloaded = thumb.size; |
|
|
|
|
docThumb.url = thumb.url; |
|
|
|
|
|
|
|
|
|
saveToCache(slug, url); |
|
|
|
|
appImManager.applyCurrentTheme(slug, url).then(deferred.resolve); |
|
|
|
|
}); |
|
|
|
|
let wallpaper: WallPaper.wallPaper = { |
|
|
|
|
_: 'wallPaper', |
|
|
|
|
access_hash: '', |
|
|
|
|
document: document, |
|
|
|
|
id, |
|
|
|
|
slug: id, |
|
|
|
|
pFlags: {} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if(background.blur) { |
|
|
|
|
setTimeout(() => { |
|
|
|
|
blur(doc.url, 12, 4) |
|
|
|
|
.then(url => { |
|
|
|
|
if(!middleware()) { |
|
|
|
|
deferred.resolve(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
const upload = appDownloadManager.upload(file, file.name); |
|
|
|
|
|
|
|
|
|
onReady(url); |
|
|
|
|
}); |
|
|
|
|
}, 200); |
|
|
|
|
} else { |
|
|
|
|
onReady(doc.url); |
|
|
|
|
const deferred = deferredPromise<void>(); |
|
|
|
|
deferred.addNotifyListener = upload.addNotifyListener; |
|
|
|
|
deferred.cancel = upload.cancel; |
|
|
|
|
|
|
|
|
|
upload.then(inputFile => { |
|
|
|
|
apiManager.invokeApi('account.uploadWallPaper', { |
|
|
|
|
file: inputFile, |
|
|
|
|
mime_type: file.type, |
|
|
|
|
settings: { |
|
|
|
|
_: 'wallPaperSettings' |
|
|
|
|
} |
|
|
|
|
}).then(_wallpaper => { |
|
|
|
|
const newDoc = (_wallpaper as WallPaper.wallPaper).document as MyDocument; |
|
|
|
|
newDoc.downloaded = document.downloaded; |
|
|
|
|
newDoc.url = document.url; |
|
|
|
|
|
|
|
|
|
wallpaper = _wallpaper as WallPaper.wallPaper; |
|
|
|
|
wallpaper.document = appDocsManager.saveDoc(wallpaper.document); |
|
|
|
|
|
|
|
|
|
container.dataset.docId = wallpaper.document.id; |
|
|
|
|
container.dataset.slug = wallpaper.slug; |
|
|
|
|
|
|
|
|
|
this.setBackgroundDocument(wallpaper.slug, wallpaper.document).then(deferred.resolve, deferred.reject); |
|
|
|
|
}, deferred.reject); |
|
|
|
|
}, deferred.reject); |
|
|
|
|
|
|
|
|
|
deferred.then(() => { |
|
|
|
|
this.clicked.delete(wallpaper.document.id); |
|
|
|
|
}, (err) => { |
|
|
|
|
container.remove(); |
|
|
|
|
//console.error('i saw the body drop', err);
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return deferred; |
|
|
|
|
}; |
|
|
|
|
const preloader = new ProgressivePreloader({ |
|
|
|
|
isUpload: true, |
|
|
|
|
cancelable: true, |
|
|
|
|
tryAgainOnFail: false |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
const setActive = () => { |
|
|
|
|
const active = grid.querySelector('.active'); |
|
|
|
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
|
|
|
|
const target = background.type === 'image' ? grid.querySelector(`.grid-item[data-slug="${background.slug}"]`) : null; |
|
|
|
|
if(active === target) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
const container = this.addWallPaper(wallpaper, false); |
|
|
|
|
this.clicked.add(wallpaper.document.id); |
|
|
|
|
|
|
|
|
|
if(active) { |
|
|
|
|
active.classList.remove('active'); |
|
|
|
|
} |
|
|
|
|
preloader.attach(container, false, deferred); |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if(target) { |
|
|
|
|
target.classList.add('active'); |
|
|
|
|
private onResetClick = () => { |
|
|
|
|
const defaultTheme = STATE_INIT.settings.themes.find(t => t.name === this.theme.name); |
|
|
|
|
if(defaultTheme) { |
|
|
|
|
++this.tempId; |
|
|
|
|
this.theme.background = copy(defaultTheme.background); |
|
|
|
|
appStateManager.pushToState('settings', rootScope.settings); |
|
|
|
|
appImManager.applyCurrentTheme(undefined, undefined, true); |
|
|
|
|
this.blurCheckboxField.setValueSilently(this.theme.background.blur); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
rootScope.on('background_change', setActive); |
|
|
|
|
|
|
|
|
|
apiManager.invokeApiHashable('account.getWallPapers').then((accountWallpapers) => { |
|
|
|
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
|
|
|
|
const wallpapers = (accountWallpapers as AccountWallPapers.accountWallPapers).wallpapers as WallPaper.wallPaper[]; |
|
|
|
|
wallpapers.forEach((wallpaper) => { |
|
|
|
|
private addWallPaper(wallpaper: WallPaper.wallPaper, append = true) { |
|
|
|
|
if(wallpaper.pFlags.pattern || (wallpaper.document as MyDocument).mime_type.indexOf('application/') === 0) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
@ -177,37 +213,43 @@ export default class AppBackgroundTab extends SliderSuperTab {
@@ -177,37 +213,43 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|
|
|
|
const container = document.createElement('div'); |
|
|
|
|
container.classList.add('grid-item'); |
|
|
|
|
|
|
|
|
|
const media = document.createElement('div'); |
|
|
|
|
media.classList.add('grid-item-media'); |
|
|
|
|
|
|
|
|
|
const wrapped = wrapPhoto({ |
|
|
|
|
photo: wallpaper.document, |
|
|
|
|
message: null, |
|
|
|
|
container: container, |
|
|
|
|
container: media, |
|
|
|
|
boxWidth: 0, |
|
|
|
|
boxHeight: 0, |
|
|
|
|
withoutPreloader: true |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
[wrapped.images.thumb, wrapped.images.full].filter(Boolean).forEach(image => { |
|
|
|
|
image.classList.add('grid-item-media'); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
container.dataset.docId = wallpaper.document.id; |
|
|
|
|
container.dataset.slug = wallpaper.slug; |
|
|
|
|
|
|
|
|
|
if(background.type === 'image' && background.slug === wallpaper.slug) { |
|
|
|
|
if(this.theme.background.type === 'image' && this.theme.background.slug === wallpaper.slug) { |
|
|
|
|
container.classList.add('active'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grid.append(container); |
|
|
|
|
(wrapped.loadPromises.thumb || wrapped.loadPromises.full).then(() => { |
|
|
|
|
sequentialDom.mutate(() => { |
|
|
|
|
container.append(media); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
let clicked: Set<string> = new Set(); |
|
|
|
|
attachClickEvent(grid, (e) => { |
|
|
|
|
this.grid[append ? 'append' : 'prepend'](container); |
|
|
|
|
|
|
|
|
|
return container; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private onGridClick = (e: MouseEvent | TouchEvent) => { |
|
|
|
|
const target = findUpClassName(e.target, 'grid-item') as HTMLElement; |
|
|
|
|
if(!target) return; |
|
|
|
|
|
|
|
|
|
const {docId, slug} = target.dataset; |
|
|
|
|
if(clicked.has(docId)) return; |
|
|
|
|
clicked.add(docId); |
|
|
|
|
if(this.clicked.has(docId)) return; |
|
|
|
|
this.clicked.add(docId); |
|
|
|
|
|
|
|
|
|
const preloader = new ProgressivePreloader({ |
|
|
|
|
cancelable: true, |
|
|
|
@ -217,8 +259,8 @@ export default class AppBackgroundTab extends SliderSuperTab {
@@ -217,8 +259,8 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|
|
|
|
const doc = appDocsManager.getDoc(docId); |
|
|
|
|
|
|
|
|
|
const load = () => { |
|
|
|
|
const promise = setBackgroundDocument(slug, doc); |
|
|
|
|
if(!doc.url || background.blur) { |
|
|
|
|
const promise = this.setBackgroundDocument(slug, doc); |
|
|
|
|
if(!doc.url || this.theme.background.blur) { |
|
|
|
|
preloader.attach(target, true, promise); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
@ -237,11 +279,86 @@ export default class AppBackgroundTab extends SliderSuperTab {
@@ -237,11 +279,86 @@ export default class AppBackgroundTab extends SliderSuperTab {
|
|
|
|
|
load(); |
|
|
|
|
|
|
|
|
|
//console.log(doc);
|
|
|
|
|
}, {listenerSetter: this.listenerSetter}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//console.log(accountWallpapers);
|
|
|
|
|
private saveToCache = (slug: string, url: string) => { |
|
|
|
|
fetch(url).then(response => { |
|
|
|
|
appDownloadManager.cacheStorage.save('backgrounds/' + slug, response); |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
this.scrollable.append(grid); |
|
|
|
|
private setBackgroundDocument = (slug: string, doc: MyDocument) => { |
|
|
|
|
let _tempId = ++this.tempId; |
|
|
|
|
const middleware = () => _tempId === this.tempId; |
|
|
|
|
|
|
|
|
|
const download = appDocsManager.downloadDoc(doc, appImManager.chat.bubbles ? appImManager.chat.bubbles.lazyLoadQueue.queueId : 0); |
|
|
|
|
|
|
|
|
|
const deferred = deferredPromise<void>(); |
|
|
|
|
deferred.addNotifyListener = download.addNotifyListener; |
|
|
|
|
deferred.cancel = download.cancel; |
|
|
|
|
|
|
|
|
|
download.then(() => { |
|
|
|
|
if(!middleware()) { |
|
|
|
|
deferred.resolve(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const background = this.theme.background; |
|
|
|
|
const onReady = (url: string) => { |
|
|
|
|
//const perf = performance.now();
|
|
|
|
|
averageColor(url).then(pixel => { |
|
|
|
|
if(!middleware()) { |
|
|
|
|
deferred.resolve(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const hsla = highlightningColor(Array.from(pixel) as any); |
|
|
|
|
//console.log(doc, hsla, performance.now() - perf);
|
|
|
|
|
|
|
|
|
|
background.slug = slug; |
|
|
|
|
background.type = 'image'; |
|
|
|
|
background.highlightningColor = hsla; |
|
|
|
|
appStateManager.pushToState('settings', rootScope.settings); |
|
|
|
|
|
|
|
|
|
this.saveToCache(slug, url); |
|
|
|
|
appImManager.applyCurrentTheme(slug, url).then(deferred.resolve); |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if(background.blur) { |
|
|
|
|
setTimeout(() => { |
|
|
|
|
blur(doc.url, 12, 4) |
|
|
|
|
.then(url => { |
|
|
|
|
if(!middleware()) { |
|
|
|
|
deferred.resolve(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onReady(url); |
|
|
|
|
}); |
|
|
|
|
}, 200); |
|
|
|
|
} else { |
|
|
|
|
onReady(doc.url); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return deferred; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private setActive = () => { |
|
|
|
|
const active = this.grid.querySelector('.active'); |
|
|
|
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
|
|
|
|
const target = background.type === 'image' ? this.grid.querySelector(`.grid-item[data-slug="${background.slug}"]`) : null; |
|
|
|
|
if(active === target) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(active) { |
|
|
|
|
active.classList.remove('active'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(target) { |
|
|
|
|
target.classList.add('active'); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|