1404 lines
42 KiB
JavaScript
1404 lines
42 KiB
JavaScript
'use strict'
|
|
|
|
// const { urlencoded } = require('body-parser')
|
|
|
|
const d = document
|
|
, w = window
|
|
|
|
let ProductTemplate = d.createElement( 'ProductTemplate' )
|
|
, ThumbnailsRowTemplate = d.createElement( 'ThumbnailsRowTemplate' )
|
|
, ThumbnailTemplate = d.createElement( 'ThumbnailTemplate' )
|
|
, ProductTypesDatalist = d.getElementById( 'product_types' )
|
|
, searchField = d.getElementById( 'search' )
|
|
, searchForm = d.querySelector( 'nav form' )
|
|
, btnShowAll = d.getElementById( 'ShowAll' )
|
|
, btnShowWithoutPhotos = d.getElementById( 'ShowWithoutPhotos' )
|
|
, chkShowOnlyAvaliable = d.getElementById( 'ShowOnlyAvaliable' )
|
|
, txtCurrentCount = d.getElementById( 'CurrentCount' )
|
|
, ProgressBarElement = d.querySelector( 'ProgressBarElement' )
|
|
, Main = d.getElementById( 'photos' )
|
|
, Moving_Thumbnail = null
|
|
, unloadedProducts = []
|
|
, ProductTypes = new Set( )
|
|
, ProgressBar
|
|
, ProgressBarTotal
|
|
, MaxChunk = 100
|
|
, BaseURL = localStorage.getItem( 'BaseURL' ) || ''
|
|
, Accounts_Ordered = JSON.parse( localStorage.getItem( 'Accounts_Ordered' ) || '[ ]' )
|
|
, Default_Accounts = JSON.parse( localStorage.getItem( 'Default_Accounts' ) || '{ }' )
|
|
, _HardReload_in_3_2_ = false
|
|
, Products = Object.assign( { },
|
|
...Object.keys( localStorage ).map( SKU =>
|
|
( ![ 'lastUpdated', 'Accounts_Ordered', 'Default_Accounts', 'BaseURL' ].includes( SKU ) )
|
|
&& ({ [ SKU ]: JSON.parse( localStorage.getItem( SKU ) || '{ }' ) })
|
|
|| ({ })
|
|
) )
|
|
|
|
//console.log( Products )
|
|
|
|
const ceil_abs = N => Math.ceil( Math.abs( N ) )
|
|
const range = ( cnt_or_start = 0, stop = null, step = 1 ) =>
|
|
( null === stop )
|
|
&& [...Array( cnt_or_start ).keys( )]
|
|
|| [...Array( ceil_abs( ( stop - cnt_or_start ) / step ) )]
|
|
.map( ( _, i ) => cnt_or_start + i * step )
|
|
|
|
|
|
function PopulateSearchWithTypes( )
|
|
{ [...ProductTypes]
|
|
.sort( ( a,b ) => a.localeCompare( b ) )
|
|
.map( T =>
|
|
{ let O = document.createElement( 'option' )
|
|
O.value = T
|
|
ProductTypesDatalist.appendChild( O )
|
|
})
|
|
}
|
|
|
|
const getHashFromPhotoURL = PhotoURL =>
|
|
PhotoURL.match( /([^\/_]+)_?L\.jpg$/ )[ 1 ]
|
|
|
|
const unURIHash = ( Hash = w.location.hash ) =>
|
|
decodeURIComponent( Hash )
|
|
|
|
|
|
const Now = ( ) =>
|
|
new Date( ).toISOString( )
|
|
.slice( 0, 19 )
|
|
.replace( 'T', ' ' )
|
|
|
|
const Product_Error =
|
|
{ SKU: 'ERR-01-001'
|
|
, Title: 'Cant get Products from server'
|
|
, Variants: { _default: { 10:'', 1:'' } }
|
|
, Accounts: { }
|
|
}
|
|
|
|
const DefaultRequestParams =
|
|
{ ContentType: 'application/json'
|
|
, url: '/api/photos/'
|
|
, method: 'GET'
|
|
, body: ''
|
|
}
|
|
|
|
const Search_onKeypress = e =>
|
|
{ if( ( typeof e.key == 'undefined' )
|
|
|| ( e.key == null )
|
|
)
|
|
{ return false }
|
|
|
|
const regexValidKeys = new RegExp
|
|
( '^'
|
|
+ '[µßáäåæëíïðñóöøúüþœœ\\w\\d]'
|
|
+ '|Backspace|Delete|Clear|Cut|Paste|Undo|Redo'
|
|
+ '$'
|
|
, 'i'
|
|
)
|
|
|
|
if( regexValidKeys.test( e.key ) )
|
|
{ showOnlySearched( ) }
|
|
}
|
|
|
|
const setURL = path =>
|
|
{ let u = new URL( w.location.toString( ) )
|
|
|
|
u.pathname = '/photos/' + path
|
|
u.hash = ''
|
|
u.search = ''
|
|
d.title = path
|
|
history.pushState( { search: path }, path, u.toString( ) )
|
|
}
|
|
|
|
const AddClassFor600ms = ( _DOMnode, _Class ) =>
|
|
{ _DOMnode.classList.add( _Class )
|
|
setTimeout( ( ) => { _DOMnode.classList.remove( _Class ) }
|
|
, 600
|
|
)
|
|
}
|
|
|
|
const getPhotoURL = ({ SKU, Hash, Size }) =>
|
|
( SKU && Hash && Size )
|
|
&& ( BaseURL
|
|
+ GetID( SKU )
|
|
+ '/'
|
|
+ Hash
|
|
+ ( Hash.length>2 ? '_' : '' )
|
|
+ Size
|
|
+ '.jpg'
|
|
)
|
|
|| ''
|
|
|
|
|
|
const getSearchIndex = ( ...args ) =>
|
|
[ ...args ]
|
|
.join( '' )
|
|
.replace( /[^µßáäåæëíïðñóöøúüþœœ\w\d]/gi,'' )
|
|
.toUpperCase( )
|
|
|
|
const setProgressBarTotal = Total =>
|
|
{ ProgressBarTotal = Total
|
|
ProgressBar = -1
|
|
incrementProgressBar( )
|
|
}
|
|
|
|
function incrementProgressBar( )
|
|
{ ProgressBarElement.style.width =
|
|
( ++ProgressBar * 100 / ProgressBarTotal ) + '%'
|
|
|
|
if( ProgressBar == ProgressBarTotal )
|
|
{ Window_onScroll( ) }
|
|
}
|
|
|
|
function collapseAll( )
|
|
{ setURL( searchField.value )
|
|
d.querySelectorAll( 'ProductElement.targeted' )
|
|
.forEach( P => P.classList.remove( 'targeted' ) )
|
|
}
|
|
|
|
function unhideAll( )
|
|
{ d.querySelectorAll( 'ProductElement.hidden' )
|
|
.forEach( P => P.classList.remove( 'hidden' ) )
|
|
}
|
|
|
|
const UpdateCounter = Count =>
|
|
{ CurrentCount.textContent
|
|
= Count
|
|
|| d.querySelectorAll( 'ProductElement:not(.hidden):not(.zero)' )
|
|
.length
|
|
}
|
|
|
|
const ShowOnlyAvaliable = e =>
|
|
{ const NonZero = Boolean( chkShowOnlyAvaliable.checked )
|
|
d.querySelectorAll( 'ProductElement' )
|
|
.forEach( P =>
|
|
{ if( NonZero && Products[ P.dataset.sku ].Amount == 0 )
|
|
{ P.classList.add('zero') }
|
|
else
|
|
{ P.classList.remove('zero') }
|
|
})
|
|
UpdateCounter( )
|
|
}
|
|
|
|
const ScrollTo = P =>
|
|
{ w.scroll( 0, P.offsetTop + P.offsetParent.offsetTop - 50 )
|
|
Window_onScroll( )
|
|
}
|
|
|
|
const obj2URLSearchParams = obj =>
|
|
{ let u = new URLSearchParams( )
|
|
for( const key in obj )
|
|
{ u.set( key, obj[ key ] ) }
|
|
return u.toString( )
|
|
}
|
|
|
|
const request = Params =>
|
|
new Promise( ( resolve, reject ) =>
|
|
{ let { url, method, body, ContentType } = DefaultRequestParams
|
|
if( 'string' == typeof Params )
|
|
{ url = Params }
|
|
else
|
|
{ if( 'string' == typeof Params.url )
|
|
{ url = Params.url }
|
|
else
|
|
{ const { api, query } = Params
|
|
let u = new URL( w.location.toString( ) )
|
|
u.pathname = '/api/photos/' + api
|
|
u.search = obj2URLSearchParams( query || {} )
|
|
u.hash = ''
|
|
url = u.toString( )
|
|
}
|
|
|
|
method = Params.method || method
|
|
body = Params.body || body
|
|
ContentType = Params.ContentType || ContentType
|
|
}
|
|
|
|
const headers = Params.headers
|
|
|| { 'Content-Type' : ContentType }
|
|
|
|
fetch( url
|
|
, [ 'GET','HEAD' ].includes( method )
|
|
? { headers, method }
|
|
: { headers, method, body }
|
|
)
|
|
.then( res =>
|
|
{ if( !res || !res.ok )
|
|
reject( res && res.status || url )
|
|
if( res.statusCode >= 400 )
|
|
resolve( false )
|
|
|
|
resolve( res.text( ) )
|
|
})
|
|
.catch( err =>
|
|
{ reject({ err, Params })
|
|
console.error( 'rejected request::fetch', err )
|
|
})
|
|
|
|
}).catch( err => console.error( 'cant [request]', err ) )
|
|
|
|
|
|
const GetID = SKU =>
|
|
SKU.replace( /\s+/g,'' )
|
|
.replace( /[^\w\d\.\,_-]/g,'-' )
|
|
.toUpperCase( )
|
|
|
|
const SendFiles = ({ Form, SKU, Variant, Nr }) =>
|
|
{ let u = new URL( w.location.toString( ) )
|
|
u.pathname = '/api/photos'
|
|
u.search = obj2URLSearchParams({ SKU, Variant, Nr: Nr||'0' })
|
|
Form.action = u.toString( )
|
|
Form.submit( )
|
|
}
|
|
|
|
const ProcessPhotoFile = ({ PhotoFile, Nr }) =>
|
|
{ let canvas = d.createElement( 'canvas' )
|
|
, ctx = canvas.getContext( '2d' )
|
|
, reader = new FileReader( )
|
|
, Hash
|
|
|
|
return new Promise( ( resolve,reject ) =>
|
|
{ reader.onload = e =>
|
|
{ let img = new Image( )
|
|
img.onload = ( ) =>
|
|
{ canvas.width = 200
|
|
canvas.height = 200
|
|
ctx.drawImage( img, 0, 0, 200, 200 )
|
|
let imageData = ctx.getImageData(0, 0, 200, 200)
|
|
if( 10 == Nr )
|
|
{ let data = imageData.data
|
|
for( let i = 0; i < data.length; i += 4)
|
|
{ data[ i ] // red
|
|
= data[ i+1 ] // green
|
|
= data[ i+2 ] // blue
|
|
= 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]
|
|
}
|
|
ctx.putImageData( imageData, 0, 0 )
|
|
}
|
|
resolve({ imgData: canvas.toDataURL( 'image/jpeg' )
|
|
, imgWidth: img.naturalWidth
|
|
, imgHeight: img.naturalHeight
|
|
})
|
|
}
|
|
img.src = e.target.result
|
|
}
|
|
try
|
|
{ reader.readAsDataURL( PhotoFile ) }
|
|
catch( e )
|
|
{ reject( e ) }
|
|
} ).catch( err => console.error( 'cant [ProcessPhotoFile]', err ))
|
|
}
|
|
|
|
const UploadSinglePhoto = async e =>
|
|
{ let Form = e.target.parentNode
|
|
, T = Form.parentNode.parentNode
|
|
, R = T.parentNode
|
|
, P = R.parentNode.parentNode
|
|
, PhotoFile = e.target.files[ 0 ]
|
|
, SKU = P.dataset.sku
|
|
, Nr = T.dataset.nr
|
|
, Variant = R.dataset.variant
|
|
, Hash = T.dataset.hash
|
|
|
|
SendFiles({ Form, SKU, Variant, Nr })
|
|
|
|
T.dataset.src_big = getPhotoURL({ SKU, Hash, Size:'M' })
|
|
+ '?' + Date.now( )
|
|
const { imgData, imgWidth, imgHeight } = await ProcessPhotoFile({ PhotoFile, Nr })
|
|
T.dataset.src_small
|
|
= T.querySelector( 'img' ).src
|
|
= imgData
|
|
|
|
if( 1600 != imgWidth
|
|
|| 1600 != imgHeight
|
|
)
|
|
{ T.querySelector( 'WrongDimensions' ).innerText = imgWidth + 'x' + imgHeight }
|
|
|
|
updateAccounts( P )
|
|
}
|
|
|
|
const UploadMultiplePhotos = async e =>
|
|
{ let Form = e.target.parentNode
|
|
, R = Form.parentNode.parentNode.parentNode.parentNode
|
|
, P = R.parentNode.parentNode
|
|
, SKU = P.dataset.sku
|
|
, Variant = R.dataset.variant
|
|
, Photos = e.target.files
|
|
, TT = R.querySelectorAll( 'Thumbnail' )
|
|
, Nr = TT.length-1
|
|
|
|
SendFiles({ Form, SKU, Variant })
|
|
|
|
for( const i in Photos )
|
|
{ if( i != Number( i ) )
|
|
{ updateAccounts( P )
|
|
return true
|
|
}
|
|
Nr = ( 9 == Nr ? 11 : Nr + 1 )
|
|
|
|
if( 10 < Nr )
|
|
{ return false }
|
|
|
|
const Hash = ''// await getFilehash( Photos[i] )
|
|
, PhotoFile = Photos[i]
|
|
|
|
let T = createThumbnail(
|
|
{ SKU
|
|
, Nr
|
|
, Hash
|
|
, Variant
|
|
, PrevUUID: SKU + ':' + ( TT[ Nr-1 ] ? Nr-1 : TT.length-1 )
|
|
, NextUUID: SKU + ':' + ( Photos[ i+1 ] ? Nr+1 : 10 )
|
|
})
|
|
|
|
T.dataset.src_big = getPhotoURL({ SKU, Hash, Size:'M' })
|
|
const { imgData, imgWidth, imgHeight } =
|
|
await ProcessPhotoFile({ PhotoFile, Nr })
|
|
|
|
T.dataset.src_small
|
|
= T.querySelector( 'img' ).src
|
|
= imgData
|
|
|
|
if( 1600 != imgWidth
|
|
|| 1600 != imgHeight
|
|
)
|
|
{ T.querySelector( 'WrongDimensions' ).innerText = imgWidth + 'x' + imgHeight }
|
|
|
|
R.appendChild( T )
|
|
}
|
|
}
|
|
|
|
const VariantSelector_onChange = e =>
|
|
{ let S = e.target || e
|
|
, R = S.parentNode.parentNode
|
|
, P = R.parentNode.parentNode
|
|
|
|
const SKU = P.dataset.sku
|
|
, Account = R.dataset.account
|
|
, Variant = S.value
|
|
, Variants = Object.keys( Products[ SKU ].Variants )
|
|
|
|
Products[ SKU ].Accounts[ Account ] = Variant
|
|
|
|
request({ api: 'assignVariantToAccount'
|
|
, method: 'PUT'
|
|
, query: { SKU, Account, Variant }
|
|
})
|
|
|
|
R.parentNode
|
|
.replaceChild( createAccount(
|
|
{ SKU
|
|
, Account
|
|
, Variant
|
|
, Variants
|
|
, ProductElement: P
|
|
} )
|
|
, R )
|
|
}
|
|
|
|
const updateAccounts = ProductElement =>
|
|
{ ProductElement.querySelectorAll( 'Variants ThumbnailsRow' ).forEach( V =>
|
|
{ ProductElement.querySelectorAll( 'Accounts ThumbnailsRow' ).forEach( A =>
|
|
{ if( V.dataset.variant != A.dataset.variant )
|
|
{ return false }
|
|
A.querySelector( 'select' ).value = A.dataset.variant
|
|
A.querySelectorAll( 'Thumbnail' ).forEach( T => A.removeChild( T ) )
|
|
V.querySelectorAll( 'Thumbnail' ).forEach( T =>
|
|
{ let newT = A.appendChild( T.cloneNode( true ))
|
|
AddClassFor600ms( newT, 'updated' )
|
|
})
|
|
})
|
|
})
|
|
Window_onScroll( )
|
|
}
|
|
|
|
const openNewVariantDialog = e =>
|
|
{ let NewVariantDialog = e.target.parentNode.parentNode.parentNode
|
|
NewVariantDialog.querySelector( 'ConfirmationDialog' )
|
|
.classList.toggle( 'hidden' )
|
|
NewVariantDialog.querySelector( 'button' )
|
|
.classList.toggle( 'hidden' )
|
|
let VariantName =
|
|
NewVariantDialog.querySelector( `input[name='variant_name']` )
|
|
VariantName.value = Now( )
|
|
VariantName.select( )
|
|
VariantName.focus( )
|
|
}
|
|
|
|
const abortNewVariant = e =>
|
|
{ let NewVariantDialog = e.target.parentNode.parentNode.parentNode
|
|
NewVariantDialog.querySelector( 'ConfirmationDialog' )
|
|
.classList.toggle( 'hidden' )
|
|
NewVariantDialog.querySelector( 'button' )
|
|
.classList.toggle( 'hidden' )
|
|
}
|
|
|
|
const confirmNewVariant = e =>
|
|
{ let NewVariantDialog = e.target
|
|
&& e.target.parentNode.parentNode.parentNode
|
|
|| e.parentNode.parentNode
|
|
, P = NewVariantDialog.parentNode.parentNode
|
|
, VariantName = NewVariantDialog.querySelector( `input[name='variant_name']` )
|
|
|
|
NewVariantDialog.querySelector( 'ConfirmationDialog' )
|
|
.classList.toggle( 'hidden' )
|
|
NewVariantDialog.querySelector( 'button' )
|
|
.classList.toggle( 'hidden' )
|
|
|
|
if( !VariantName.value.trim( ) )
|
|
{ VariantName.value = Now( ) }
|
|
|
|
const SKU = P.dataset.sku
|
|
, Variant = VariantName.value
|
|
|
|
Products[ SKU ].Variants[ Variant ] = { }
|
|
Products[ SKU ].Variants[ Variant ][ 10 ] =
|
|
Products[ SKU ].Variants[ '_default' ][ 10 ] || ''
|
|
|
|
let V = P.querySelector( `Variants [data-variant='${Variant}']` )
|
|
if( V )
|
|
{ ScrollTo( V )
|
|
return false
|
|
}
|
|
|
|
request({ api: 'variant'
|
|
, method: 'POST'
|
|
, query: { SKU, Variant }
|
|
})
|
|
|
|
let newV = createVariant(
|
|
{ SKU
|
|
, Variant
|
|
, Photos: Products[ SKU ].Variants[ Variant ]
|
|
} )
|
|
newV.querySelectorAll( 'Thumbnail' ).forEach( T =>
|
|
{ AddClassFor600ms( T, 'updated' ) } )
|
|
|
|
ScrollTo( P.querySelector( 'Variants' ).appendChild( newV ))
|
|
|
|
P.querySelectorAll( 'Accounts select' ).forEach( S =>
|
|
{ let O = d.createElement( 'option' )
|
|
O.textContent = Variant
|
|
S.appendChild( O )
|
|
} )
|
|
}
|
|
|
|
const copyVariant = e =>
|
|
{ const R = e.target
|
|
&& e.target.parentNode.parentNode
|
|
|| e.parentNode.parentNode
|
|
, P = R.parentNode.parentNode
|
|
, SKU = P.dataset.sku
|
|
, CopyOf = R.dataset.variant
|
|
, Variant = 'Copy ' + Now( )
|
|
|
|
console.log({R,P, SKU, CopyOf, Variant})
|
|
|
|
Products[ SKU ].Variants[ Variant ] = Products[ SKU ].Variants[ CopyOf ]
|
|
|
|
request({ api: 'variant'
|
|
, method: 'POST'
|
|
, query: { SKU, Variant, CopyOf }
|
|
})
|
|
|
|
let newV = createVariant(
|
|
{ SKU
|
|
, Variant
|
|
, Photos: Products[ SKU ].Variants[ Variant ]
|
|
})
|
|
|
|
newV.querySelectorAll( 'Thumbnail' ).forEach( T =>
|
|
{ AddClassFor600ms( T, 'updated' ) } )
|
|
|
|
ScrollTo( P.querySelector( 'Variants' ).appendChild( newV ))
|
|
|
|
P.querySelectorAll( 'Accounts select' ).forEach( S =>
|
|
{ let O = d.createElement( 'option' )
|
|
O.textContent = Variant
|
|
S.appendChild( O )
|
|
})
|
|
}
|
|
|
|
|
|
|
|
const toggleProduct = e =>
|
|
{ e.preventDefault( )
|
|
|
|
let P = e.target.parentNode.parentNode.parentNode
|
|
, SKU = P.dataset.sku
|
|
|
|
if( P.classList.contains( 'targeted' ) )
|
|
{ collapseAll( )
|
|
P.querySelectorAll( 'Thumbnail' )
|
|
.forEach( T => T.classList.remove( 'updated' ) )
|
|
}
|
|
else
|
|
{ expandProduct( P ) }
|
|
return true
|
|
}
|
|
|
|
const expandProduct = P =>
|
|
{ unzoomAll( )
|
|
|
|
P.classList.remove( 'hidden' )
|
|
P.classList.add( 'targeted' )
|
|
ScrollTo( P )
|
|
|
|
const SKU = P.dataset.sku
|
|
setURL( '!'+SKU )
|
|
|
|
if( P.querySelector( 'Accounts ThumbnailsRow' ) )
|
|
{ return true }
|
|
|
|
let Product = Products[ SKU ] || { }
|
|
, Variants = []
|
|
, NewVariant = P.querySelector( 'NewVariant' )
|
|
, ConfirmNewVariant = NewVariant.querySelector( 'ConfirmNewVariant' )
|
|
, AbortNewVariant = NewVariant.querySelector( 'AbortNewVariant' )
|
|
, NewVariantName = NewVariant.querySelector( 'input[type=text]' )
|
|
|
|
NewVariant.querySelector( 'button' ).onclick = openNewVariantDialog
|
|
ConfirmNewVariant.onclick = confirmNewVariant
|
|
AbortNewVariant.onclick = abortNewVariant
|
|
NewVariantName.onkeypress = e =>
|
|
{ if( e.key == 'Escape' )
|
|
{ AbortNewVariant.click( )
|
|
return false
|
|
}
|
|
else if( e.key == 'Enter' )
|
|
{ confirmNewVariant( ConfirmNewVariant ) }
|
|
}
|
|
|
|
if( !Product.Variants )
|
|
{ Product.Variants = { _default: { } } }
|
|
|
|
for( const Variant in Product.Variants )
|
|
{ Variants.push( Variant )
|
|
if( Variant != '_default' )
|
|
{ P.querySelector( 'Variants' ).appendChild(
|
|
createVariant(
|
|
{ SKU
|
|
, Variant
|
|
, Photos: Product.Variants[Variant]
|
|
})
|
|
)
|
|
}
|
|
}
|
|
|
|
if( !Object.keys( Product.Accounts ).length )
|
|
{ Products[ SKU ].Accounts
|
|
= Product.Accounts
|
|
= Default_Accounts
|
|
}
|
|
|
|
Accounts_Ordered.map( Account =>
|
|
P.querySelector( 'Accounts' ).appendChild
|
|
( createAccount(
|
|
{ Account
|
|
, Variant: Product.Accounts[ Account ] || '_default'
|
|
, Variants
|
|
, ProductElement: P
|
|
, SKU
|
|
})
|
|
)
|
|
)
|
|
|
|
ScrollTo( P )
|
|
}
|
|
|
|
const Window_onScroll = e =>
|
|
{ const Window_Height = w.innerHeight
|
|
|| d.documentElement.clientHeight
|
|
|
|
d.querySelectorAll( 'ThumbnailsRow' )
|
|
.forEach( ( R, i, a ) =>
|
|
{ let Rect = R.getBoundingClientRect( )
|
|
if( ( 0 <= ( Rect.top + Rect.height ) )
|
|
&& ( Rect.top <= Window_Height )
|
|
)
|
|
{ // if( ( i == a.length-1 )
|
|
// && ( !d.querySelector( '.targeted' ) )
|
|
// )
|
|
// { showProducts( ) }
|
|
|
|
if( ( R.querySelector( 'Thumbnail img' ).src
|
|
== R.querySelector( 'Thumbnail' ).dataset.src_small
|
|
)
|
|
|| ( R.querySelector( 'Thumbnail img' ).src
|
|
== R.querySelector( 'Thumbnail' ).dataset.src_big
|
|
)
|
|
)
|
|
{ return false }
|
|
|
|
R.querySelectorAll( 'Thumbnail:not(.zoomed)' )
|
|
.forEach( T => T.querySelector( 'img' ).src = T.dataset.src_small )
|
|
}
|
|
})
|
|
}
|
|
|
|
const Window_onResize = e =>
|
|
{ d.getElementById( 'ZoomedStyle' ).innerText =
|
|
`.zoomed
|
|
{ top: ${ w.innerHeight/2 - 400 }px !important
|
|
; left: ${ w.innerWidth/2 - 400 }px !important
|
|
; position: fixed
|
|
}
|
|
`.replace( /\s\s+/g, '\n' )
|
|
Window_onScroll( )
|
|
}
|
|
|
|
const Window_onKeyDown = e =>
|
|
{ let Zoomed = d.querySelector( 'Thumbnail.zoomed' )
|
|
if( Zoomed )
|
|
{ if( e.key == 'Escape' )
|
|
{ unzoomAll( )
|
|
return true
|
|
}
|
|
if( /^(k|n|ArrowRight)$/i.test( e.key ))
|
|
Zoomed.querySelector( 'Next' ).click( )
|
|
if( /^(j|p|ArrowLeft)$/i.test( e.key ))
|
|
Zoomed.querySelector( 'Prev' ).click( )
|
|
}
|
|
else
|
|
{ if( e.key == 'Escape' )
|
|
{ collapseAll( )
|
|
unhideAll( )
|
|
showProducts( )
|
|
}
|
|
if( ( e.key == 'R' && ( e.ctrlKey || e.metaKey ) )
|
|
|| ( ( e.which || e.keyCode ) == 116 )
|
|
)
|
|
{ console.log( 'Hard Reload in 3.. 2.. ' )
|
|
_HardReload_in_3_2_ = true
|
|
}
|
|
}
|
|
}
|
|
|
|
const openThumbnailRemovalDialog = e =>
|
|
{ unzoomAll( )
|
|
let RemovalButton = e.target
|
|
, ConfirmationDialog = RemovalButton.querySelector( 'ConfirmationDialog' )
|
|
, Icon = RemovalButton.querySelector( 'Icon' )
|
|
|
|
if( !( ConfirmationDialog && Icon ))
|
|
{ return false }
|
|
ConfirmationDialog.classList.remove( 'hidden' )
|
|
Icon.classList.add( 'hidden' )
|
|
// RemovalButton.removeEventListener( 'click', openThumbnailRemovalDialog )
|
|
}
|
|
|
|
const abortThumbnailRemoval = e =>
|
|
{ let RemovalDialog = e.target.parentNode.parentNode.parentNode
|
|
RemovalDialog.querySelector( 'Icon' ).classList.remove( 'hidden' )
|
|
RemovalDialog.querySelector( 'ConfirmationDialog' ).classList.add( 'hidden' )
|
|
// RemovalDialog.addEventListener( 'click', openThumbnailRemovalDialog )
|
|
}
|
|
|
|
const confirmThumbnailRemoval = e =>
|
|
{ unzoomAll( )
|
|
let RemoveButton = e.target.parentNode.parentNode
|
|
, T = RemoveButton.parentNode
|
|
, R = T.parentNode
|
|
, P = R.parentNode.parentNode
|
|
|
|
const SKU = P.dataset.sku
|
|
, Variant = R.dataset.variant
|
|
, Nr = T.dataset.nr
|
|
, Hash = T.dataset.hash
|
|
|
|
console.log( 'removing photo:', SKU, Variant, Nr, Hash )
|
|
request({ api: ''
|
|
, method: 'DELETE'
|
|
, query: { SKU, Variant, Nr }
|
|
})
|
|
|
|
if( 10 == Nr )
|
|
{ delete Products[ SKU ].Variants[ Variant ][ 10 ] }
|
|
else
|
|
{ const newAmountOfRegularPhotos = Object
|
|
.keys( Products[ SKU ].Variants[ Variant ] )
|
|
.filter( k => 10 > k )
|
|
.length - 1
|
|
|
|
for( let _Nr = Nr; _Nr <= 9; _Nr++ )
|
|
{ if( _Nr <= newAmountOfRegularPhotos )
|
|
{ Products[ SKU ].Variants[ Variant ][ _Nr ] =
|
|
Products[ SKU ].Variants[ Variant ][ Number( _Nr ) + 1 ]
|
|
}
|
|
else
|
|
{ delete Products[ SKU ].Variants[ Variant ][ _Nr ] }
|
|
}
|
|
}
|
|
|
|
|
|
let V = createVariant(
|
|
{ SKU
|
|
, Variant
|
|
, Photos: Products[ SKU ].Variants[ Variant ]
|
|
})
|
|
|
|
R.parentNode.replaceChild( V, R )
|
|
AddClassFor600ms( V, 'changed_numeration' )
|
|
updateAccounts( P )
|
|
}
|
|
|
|
const openVariantRemovalDialog = e =>
|
|
{ unzoomAll( )
|
|
let RemovalButton = e.target
|
|
, ConfirmationDialog = RemovalButton.querySelector( 'ConfirmationDialog' )
|
|
, Icon = RemovalButton.querySelector( 'Icon' )
|
|
|
|
if( !( ConfirmationDialog && Icon ))
|
|
{ return false }
|
|
ConfirmationDialog.classList.remove( 'hidden' )
|
|
Icon.classList.add( 'hidden' )
|
|
// RemovalButton.removeEventListener( 'click', openVariantRemovalDialog )
|
|
}
|
|
const abortVariantRemoval = e =>
|
|
{ let RemovalDialog = e.target.parentNode.parentNode
|
|
RemovalDialog.querySelector( 'icon' ).classList.remove( 'hidden' )
|
|
RemovalDialog.querySelector( 'ConfirmationDialog' ).classList.add( 'hidden' )
|
|
// RemovalDialog.addEventListener( 'click', openVariantRemovalDialog )
|
|
}
|
|
const confirmVariantRemoval = e =>
|
|
{ const R = e.target.parentNode.parentNode.parentNode.parentNode
|
|
, P = R.parentNode.parentNode
|
|
, Variant = R.dataset.variant
|
|
, SKU = P.dataset.sku
|
|
|
|
console.log( 'removing', SKU, Variant )
|
|
|
|
request({ api: 'variant'
|
|
, method: 'DELETE'
|
|
, query: { SKU, Variant }
|
|
})
|
|
|
|
if( Products[ SKU ].Variants[ Variant ] )
|
|
{ delete Products[ SKU ].Variants[ Variant ]
|
|
localStorage.setItem( SKU, JSON.stringify( Products[ SKU ] ) )
|
|
}
|
|
|
|
// if( Variant == '_default' )
|
|
// let chkNewDefault = P.querySelector('input[type=checkbox]:not(:checked)')
|
|
// if( chkNewDefault )
|
|
// changeDefaultVariant( chkNewDefault )
|
|
// }
|
|
// }
|
|
|
|
R.parentNode.removeChild( R )
|
|
}
|
|
|
|
const unzoomAll = ( ) =>
|
|
{ d.querySelectorAll( 'Thumbnail.zoomed' ).forEach( z =>
|
|
{ //console.log(z)
|
|
z.querySelector( 'Prev' ).classList.add( 'hidden' )
|
|
z.querySelector( 'Next' ).classList.add( 'hidden' )
|
|
z.querySelector( 'Zoom i' ).classList.add( 'fa-search-plus' )
|
|
z.querySelector( 'Zoom i' ).classList.remove( 'fa-search-minus' )
|
|
z.classList.remove( 'zoomed' )
|
|
z.querySelector( 'img' ).src = z.dataset.src_small
|
|
})
|
|
}
|
|
|
|
const ThumbnailZoomToggle = T =>
|
|
{ if( T.classList.contains( 'zoomed' ) )
|
|
{ unzoomAll( ) }
|
|
else
|
|
{ ThumbnailZoomIn( T ) }
|
|
}
|
|
|
|
const ThumbnailZoomIn = T =>
|
|
{ unzoomAll( )
|
|
T.querySelector( 'Prev' ).classList.remove( 'hidden' )
|
|
T.querySelector( 'Next' ).classList.remove( 'hidden' )
|
|
T.querySelector( 'Zoom i' ).classList.remove( 'fa-search-plus' )
|
|
T.querySelector( 'Zoom i' ).classList.add( 'fa-search-minus' )
|
|
T.classList.add( 'zoomed' )
|
|
T.querySelector( 'img' ).src = T.dataset.src_big
|
|
}
|
|
|
|
const Moving_inProgress = Pos =>
|
|
{ Moving_Thumbnail.style.top = Pos.pageY+10 + 'px'
|
|
Moving_Thumbnail.style.left = Pos.pageX+5 + 'px'
|
|
Pos.preventDefault( )
|
|
}
|
|
|
|
const Moving_Stop = e =>
|
|
{ e.preventDefault( )
|
|
d.removeEventListener( 'mousemove', Moving_inProgress )
|
|
d.removeEventListener( 'mouseup', Moving_Stop )
|
|
|
|
if( Moving_Thumbnail != d.querySelector( 'Thumbnail.moving' ) )
|
|
{ return false }
|
|
|
|
let R = Moving_Thumbnail.parentNode
|
|
, P = R.parentNode.parentNode
|
|
, SKU = P.dataset.sku
|
|
, Variant = R.dataset.variant
|
|
, OldNr = Moving_Thumbnail.dataset.nr
|
|
|
|
R.querySelectorAll( 'MovePlaceholder' )
|
|
.forEach( p => p.classList.add( 'hidden' ) )
|
|
|
|
R.classList.remove( 'has_moving' )
|
|
Moving_Thumbnail.classList.remove( 'moving' )
|
|
Moving_Thumbnail.style = ''
|
|
|
|
let Place = d.querySelector( 'Thumbnail MovePlaceholder:hover' )
|
|
if( !Place
|
|
|| ( Place.parentNode.dataset.uuid
|
|
== Moving_Thumbnail.dataset.prevuuid
|
|
)
|
|
)
|
|
{ return false }
|
|
|
|
// rearrange photos
|
|
let NewNr = Number( Place.textContent )
|
|
console.log( 'reordering photos', OldNr, '>>', NewNr )
|
|
Place.parentNode.insertAdjacentElement( 'afterend', Moving_Thumbnail )
|
|
AddClassFor600ms( Moving_Thumbnail.querySelector( 'img' ), 'updated' )
|
|
|
|
R.querySelectorAll( 'Thumbnail' )
|
|
.forEach( ( T, i, a ) =>
|
|
{ const NrBefore = T.dataset.nr
|
|
, NrAfter = ( i<10 ? ( i==0 ? 10 : i ) : i+1 )
|
|
T.dataset.nr
|
|
= T.querySelector( 'ThumbnailCaption' ).textContent
|
|
= NrAfter
|
|
T.dataset.uuid
|
|
= T.title
|
|
= T.dataset.sku + ':' + NrAfter
|
|
|
|
T.dataset.prevuuid = T.dataset.sku + ':' + ( [ a.length, 10, ...range( 1,10 ) ][i] || i )
|
|
T.dataset.nextuuid = T.dataset.sku + ':' + ( i==a.length-1 ? 10 : ( i<9 ? i+1 : i+2 ) )
|
|
})
|
|
|
|
AddClassFor600ms( R, 'changed_numeration' )
|
|
request({ api: 'renumber'
|
|
, method: 'PUT'
|
|
, query: { SKU, Variant, OldNr, NewNr }
|
|
})
|
|
|
|
updateAccounts( P )
|
|
Moving_Thumbnail = null
|
|
}
|
|
|
|
const Move = e =>
|
|
{ if( e.button == 0 )
|
|
{ Moving_Thumbnail = e.target.parentNode
|
|
Moving_Thumbnail.classList.add( 'moving' )
|
|
Moving_Thumbnail.parentNode.classList.add( 'has_moving' )
|
|
|
|
let R = Moving_Thumbnail.parentNode
|
|
R.querySelectorAll( 'Thumbnail:not( .moving ) MovePlaceholder' )
|
|
.forEach( ( MovePlaceholder, i, a ) =>
|
|
{ MovePlaceholder.textContent = i+1
|
|
MovePlaceholder.classList.remove( 'hidden' )
|
|
} )
|
|
Moving_inProgress( e )
|
|
d.addEventListener( 'mousemove', Moving_inProgress )
|
|
d.addEventListener( 'mouseup', Moving_Stop )
|
|
e.preventDefault( )
|
|
}
|
|
}
|
|
|
|
const Prev = e =>
|
|
{ const T = e.target.parentNode
|
|
, R = T.parentNode
|
|
, PrevT = R.querySelector( `[data-uuid='${T.dataset.prevuuid}']` )
|
|
|
|
if( PrevT )
|
|
{ ThumbnailZoomIn( PrevT ) }
|
|
}
|
|
const Next = e =>
|
|
{ const T = e.target.parentNode
|
|
, R = T.parentNode
|
|
, NextT = R.querySelector( `[data-uuid='${T.dataset.nextuuid}']` )
|
|
|
|
if( NextT )
|
|
{ ThumbnailZoomIn( NextT ) }
|
|
}
|
|
|
|
const createThumbnail = ({ SKU, Nr, Hash, PrevUUID, NextUUID, Variant }) =>
|
|
{ let T = ThumbnailTemplate
|
|
.querySelector( 'Thumbnail' )
|
|
.cloneNode( true )
|
|
Hash = Hash || ''
|
|
|
|
T.dataset.hash = Hash
|
|
T.dataset.src_big = getPhotoURL({ SKU, Hash, Nr, Size:'M' })
|
|
T.dataset.src_small = getPhotoURL({ SKU, Hash, Nr, Size:'S' }) || '/placeholder.svg'
|
|
T.querySelector( 'img' ).src = '/placeholder.svg'
|
|
|
|
T.querySelector( 'ThumbnailCaption' ).textContent = Nr
|
|
T.querySelector( 'Upload input' ).onchange = UploadSinglePhoto
|
|
T.querySelector( 'Remove' ).addEventListener( 'click', openThumbnailRemovalDialog )
|
|
T.querySelector( 'Remove AbortRemoval' ).onclick = abortThumbnailRemoval
|
|
T.querySelector( 'Remove ConfirmRemoval' ).onclick = confirmThumbnailRemoval
|
|
T.querySelector( 'Move' ).onmousedown = Move
|
|
T.querySelector( 'Zoom' ).onclick = e => ThumbnailZoomToggle( e.target.parentNode )
|
|
T.querySelector( 'Prev' ).onclick = Prev
|
|
T.querySelector( 'Next' ).onclick = Next
|
|
T.dataset.prevuuid = PrevUUID
|
|
T.dataset.nextuuid = NextUUID
|
|
|
|
T.dataset.sku = SKU
|
|
T.dataset.nr = Nr
|
|
T.dataset.uuid
|
|
= T.title
|
|
= SKU+':'+Nr
|
|
|
|
return T
|
|
}
|
|
|
|
const changeDefaultVariant = e =>
|
|
{ const newDefaultCheckbox = e.target || e
|
|
, R = newDefaultCheckbox.parentNode.parentNode
|
|
, P = R.parentNode.parentNode
|
|
, SKU = P.dataset.sku
|
|
, NewDefaultVariant = R.dataset.variant
|
|
, oldDefaultRow = P.querySelector( `Variants ThumbnailsRow[data-variant='_default']` )
|
|
, oldDefaultCheckbox = oldDefaultRow.querySelector( `input[type='checkbox']` )
|
|
, oldVarinatRenamedTo = Now( )
|
|
|
|
oldDefaultRow.dataset.variant = oldVarinatRenamedTo
|
|
oldDefaultCheckbox.checked
|
|
= oldDefaultCheckbox.disabled
|
|
= false
|
|
newDefaultCheckbox.checked
|
|
= newDefaultCheckbox.disabled
|
|
= true
|
|
|
|
oldDefaultRow.querySelector('RowTitle').textContent = oldVarinatRenamedTo
|
|
Products[ SKU ].Variants[ oldVarinatRenamedTo ] =
|
|
Products[ SKU ].Variants[ '_default' ]
|
|
|
|
R.dataset.variant = '_default'
|
|
R.querySelector('RowTitle').textContent = ''
|
|
Products[ SKU ].Variants[ '_default' ] =
|
|
Products[ SKU ].Variants[ NewDefaultVariant ]
|
|
|
|
delete Products[ SKU ].Variants[ NewDefaultVariant ]
|
|
|
|
updateAccounts( P )
|
|
request({ api: 'setDefaultVariant'
|
|
, method: 'PUT'
|
|
, query: { SKU, Variant: NewDefaultVariant, oldVarinatRenamedTo }
|
|
})
|
|
|
|
AddClassFor600ms( R, 'updated' )
|
|
console.log( `set '${NewDefaultVariant}' as default of '${SKU}',`
|
|
, 'old default renamed to', oldVarinatRenamedTo
|
|
)
|
|
}
|
|
|
|
const createVariant = ({ SKU, Variant, Photos }) =>
|
|
{ let R = ThumbnailsRowTemplate
|
|
.querySelector( 'ThumbnailsRow' )
|
|
.cloneNode( true )
|
|
|
|
let isDefault = R.querySelector( 'RowDescription input[type=checkbox]' )
|
|
|
|
isDefault.onchange = changeDefaultVariant
|
|
isDefault.checked = ( Variant == '_default' )
|
|
isDefault.disabled = isDefault.checked
|
|
|
|
R.querySelector( 'RowTitle' ).textContent = Variant.replace( '_default', '' )
|
|
R.dataset.variant = Variant
|
|
|
|
R.querySelector( 'input[type=file]' ).onchange = UploadMultiplePhotos
|
|
R.querySelector( 'RowDescription Remove' ).addEventListener( 'click', openVariantRemovalDialog )
|
|
R.querySelector( 'RowDescription Remove AbortRemoval' ).onclick = abortVariantRemoval
|
|
R.querySelector( 'RowDescription Remove ConfirmRemoval' ).onclick = confirmVariantRemoval
|
|
R.querySelector( 'RowDescription Copy' ).onclick = copyVariant
|
|
|
|
const Photo_Set = [ ...( new Set([ '10', ...Object.keys( Photos ).sort( ) ]) ) ]
|
|
Photo_Set.forEach( ( Nr, i, a ) =>
|
|
{ R.appendChild( createThumbnail(
|
|
{ SKU
|
|
, Nr
|
|
, Hash: Photos[Nr] || ''
|
|
, Variant
|
|
, PrevUUID: SKU + ':' + ( a[i-1] ? a[i-1] : a[a.length-1] )
|
|
, NextUUID: SKU + ':' + ( a[i+1] ? a[i+1] : a[0] )
|
|
} ) )
|
|
})
|
|
|
|
return R
|
|
}
|
|
|
|
const createAccount = ({ Account, Variant, Variants, ProductElement, SKU }) =>
|
|
{ const VariantRowSelector = `Variants ThumbnailsRow[data-variant='${Variant}']`
|
|
, DefaultRowSelector = `Variants ThumbnailsRow`
|
|
|
|
let R = ( ProductElement.querySelector( VariantRowSelector )
|
|
|| ProductElement.querySelector( DefaultRowSelector )
|
|
).cloneNode( true )
|
|
|
|
R.querySelector( 'RowDescription input[type=checkbox]' )
|
|
.classList.add( 'hidden' )
|
|
|
|
R.dataset.account
|
|
= R.querySelector( 'RowTitle' ).textContent
|
|
= Account
|
|
|
|
let S = R.querySelector( `select[name='VariantSelector']` )
|
|
S.onchange = VariantSelector_onChange
|
|
Variants.forEach( V =>
|
|
{ let O = d.createElement( 'option' )
|
|
O.textContent = V
|
|
O.selected = ( V == Variant )
|
|
S.appendChild( O )
|
|
} )
|
|
|
|
return R
|
|
}
|
|
|
|
const createProduct = Product =>
|
|
{ if( d.querySelector( `ProductElement[id='${Product.SKU}']` ) )
|
|
{ return false }
|
|
|
|
let P = ProductTemplate.querySelector( 'ProductElement' )
|
|
.cloneNode( true )
|
|
|
|
P.dataset.searchindex = getSearchIndex( Product.SKU, Product.Title, Product.Type )
|
|
P.querySelector( 'ProductAmount' ).textContent = Product.Amount
|
|
P.querySelector( 'ProductSKU a' ).onclick = toggleProduct
|
|
P.querySelector( 'ProductSKU a' ).href = '/photos/!'+Product.SKU
|
|
P.querySelector( 'ProductSKU a' ).textContent
|
|
= P.dataset.sku
|
|
= P.id
|
|
= Product.SKU
|
|
|
|
P.querySelector( 'ProductTitle' ).innerHTML =
|
|
Product.Title.replace( /\[/, ' <hr>[' )
|
|
|
|
const Variant = '_default'
|
|
P.querySelector( 'Variants' ).appendChild( createVariant(
|
|
{ SKU: Product.SKU
|
|
, Variant
|
|
, Photos: Product.Variants[ Variant ]
|
|
}))
|
|
|
|
return P
|
|
}
|
|
|
|
const ShowSKUs = Desired =>
|
|
new Promise( (resolve, reject) =>
|
|
{ setProgressBarTotal( Desired.length ) // Desired.length >= MaxChunk
|
|
// && MaxChunk
|
|
// || Desired.length
|
|
|
|
Desired.forEach( ( P, i, a ) =>
|
|
{ setTimeout( ( )=>
|
|
{ unloadedProducts.splice( unloadedProducts.indexOf( P ), 1 )
|
|
incrementProgressBar( )
|
|
const newP = Main.appendChild( createProduct( Products[P] ) )
|
|
// if( i==0 )
|
|
// ScrollTo( newP )
|
|
// }
|
|
if( i == 10 )
|
|
{ Window_onScroll( ) }
|
|
|
|
if( i==a.length-1 )
|
|
{ resolve( true ) }
|
|
}, 1 )
|
|
})
|
|
}).catch( err => console.error( 'cant [ShowSKUs]', err ) )
|
|
|
|
const onSearch = async e =>
|
|
{ if( e ) e.preventDefault( )
|
|
searchField.value = searchField.value.trim( )
|
|
chkShowOnlyAvaliable.checked = false
|
|
UpdateCounter('')
|
|
setURL( searchField.value )
|
|
|
|
if( searchField.value === '*' )
|
|
{ // show all
|
|
unzoomAll( )
|
|
collapseAll( )
|
|
unhideAll( )
|
|
showProducts( 1e10 )
|
|
return true
|
|
}
|
|
if( searchField.value[0] === '-' )
|
|
{ // show w/o photos
|
|
unzoomAll( )
|
|
collapseAll( )
|
|
unhideAll( )
|
|
const AbsentPhoto = Number( searchField.value
|
|
.replace(/^\-/, '')
|
|
.trim( )
|
|
|| 1
|
|
)
|
|
let Desired = []
|
|
for( const SKU in Products )
|
|
{ const P = Products[ SKU ]
|
|
if( !P.Variants[ '_default' ][ AbsentPhoto ] )
|
|
{ Desired.push( SKU ) }
|
|
}
|
|
|
|
d.querySelectorAll( 'ProductElement' )
|
|
.forEach( P =>
|
|
{ if( !Desired.includes( P.dataset.sku ) )
|
|
{ P.classList.add( 'hidden' ) }
|
|
})
|
|
|
|
await ShowSKUs( Desired )
|
|
|
|
d.querySelectorAll( 'ProductElement' )
|
|
.forEach( P =>
|
|
{ if( !Desired.includes( P.dataset.sku ) )
|
|
{ P.classList.add( 'hidden' ) }
|
|
})
|
|
|
|
UpdateCounter( )
|
|
return true
|
|
}
|
|
|
|
const SearchString = getSearchIndex( searchField.value )
|
|
if( SearchString )
|
|
{ const Desired = unloadedProducts.filter( P =>
|
|
getSearchIndex( P, Products[P].Title, Products[P].Type )
|
|
.includes( SearchString )
|
|
)
|
|
if( Desired.length )
|
|
{ await ShowSKUs( Desired ) }
|
|
showOnlySearched({ expandSoloProduct: true })
|
|
UpdateCounter( )
|
|
}
|
|
else
|
|
{ unhideAll( )
|
|
collapseAll( )
|
|
UpdateCounter( )
|
|
}
|
|
}
|
|
|
|
const showProducts = ( Amount = MaxChunk ) =>
|
|
{ setProgressBarTotal
|
|
( ( unloadedProducts.length >= Amount )
|
|
&& Amount
|
|
|| unloadedProducts.length
|
|
)
|
|
|
|
unloadedProducts.every( ( SKU, i, a ) =>
|
|
{ let P = createProduct( Products[ SKU ] )
|
|
if( P )
|
|
{ setTimeout( ( )=>
|
|
{ incrementProgressBar( )
|
|
Main.appendChild( P )
|
|
if( i>=Amount-1 || i==a.length-1 )
|
|
{ Window_onScroll( ) }
|
|
}, 1 )
|
|
}
|
|
|
|
if( i>=Amount-1 || i==a.length-1 )
|
|
{ unloadedProducts.splice( 0, Amount )
|
|
return false
|
|
}
|
|
return true
|
|
} )
|
|
}
|
|
|
|
const showOnlySearched = ({ expandSoloProduct }) =>
|
|
{ searchField.value = searchField.value.trim( )
|
|
|
|
const SearchString = getSearchIndex( searchField.value )
|
|
, SKU = searchField.value.replace( /^[!\?\*]+/, '' )
|
|
, ForceSKU = ( searchField.value[0] == '!' )
|
|
|
|
if( ForceSKU && Products[ SKU ] )
|
|
{ d.querySelectorAll( `ProductElement:not([data-sku='${ SKU }'])` )
|
|
.forEach( P => P.classList.add( 'hidden' ) )
|
|
|
|
let ShownProduct = d.querySelector( `ProductElement[data-sku='${ SKU }']` )
|
|
ShownProduct.classList.remove( 'hidden' )
|
|
expandProduct( ShownProduct )
|
|
|
|
return true
|
|
}
|
|
|
|
if( !SearchString )
|
|
{ unhideAll( )
|
|
Window_onScroll( )
|
|
return false
|
|
}
|
|
|
|
d.querySelectorAll( `ProductElement:not([data-searchindex*='${SearchString}'])` )
|
|
.forEach( P => P.classList.add( 'hidden' ) )
|
|
|
|
let ShownProducts =
|
|
d.querySelectorAll( `ProductElement[data-searchindex*='${SearchString}']` )
|
|
ShownProducts.forEach( P => P.classList.remove( 'hidden' ) )
|
|
|
|
if( expandSoloProduct && ShownProducts.length==1 )
|
|
{ expandProduct( ShownProducts[0] ) }
|
|
else if( ShownProducts.length > 1 )
|
|
{ collapseAll( )
|
|
ScrollTo( ShownProducts[0] )
|
|
}
|
|
|
|
Window_onScroll( )
|
|
}
|
|
|
|
const loadData = ( ) =>
|
|
new Promise( async ( resolve, reject ) =>
|
|
{ BaseURL = await request({ api: 'baseURL' })
|
|
localStorage.setItem( 'BaseURL', BaseURL )
|
|
console.log( 'BaseURL:', BaseURL )
|
|
|
|
Accounts_Ordered = ( await request({ api: 'accounts' }) )
|
|
.split( /[\r\n]+/ )
|
|
localStorage.setItem( 'Accounts_Ordered', JSON.stringify( Accounts_Ordered ) )
|
|
console.log( 'Accounts:', JSON.stringify( Accounts_Ordered, false, 2 ) )
|
|
|
|
const Default_Accounts = Object.assign
|
|
( { }, ...Accounts_Ordered.map( A => ({[ A ] : '_default' }) ) )
|
|
localStorage.setItem( 'Default_Accounts', JSON.stringify( Default_Accounts, false, 2 ) )
|
|
|
|
let AllPhotos = []
|
|
try
|
|
{ console.log( 'getting Photos...' )
|
|
AllPhotos = JSON.parse( await request(
|
|
{ api: ''
|
|
, query: { since: localStorage.getItem( 'lastUpdated' ) || 0 }
|
|
}) )
|
|
console.log
|
|
( 'new rows in DB.Photos_Ordered ( since'
|
|
, localStorage.getItem( 'lastUpdated' ) || 0
|
|
,'):'
|
|
, AllPhotos.length
|
|
)
|
|
}
|
|
catch( err )
|
|
{ console.error( err )
|
|
Products = { 'ERR-01-001': Product_Error }
|
|
reject( false )
|
|
return false
|
|
}
|
|
|
|
localStorage.setItem( 'lastUpdated', Now( ) )
|
|
console.log( 'lastUpdated:', localStorage.getItem( 'lastUpdated' ) )
|
|
|
|
|
|
AllPhotos.forEach( P =>
|
|
{ const { SKU, Variant } = P
|
|
if( !Products[ SKU ] )
|
|
{ Products[ SKU ] =
|
|
{ SKU
|
|
, Title: P.Title || ''
|
|
, Type: P.Type || ''
|
|
, Amount: P.Amount || 0
|
|
, id: P.id
|
|
, Variants: { '_default': { } }
|
|
, Accounts: { }
|
|
}
|
|
|
|
if( Products[ SKU ].Type.trim( )
|
|
&& !ProductTypes.has( Products[ SKU ].Type.trim( ) )
|
|
)
|
|
{ ProductTypes.add( Products[ SKU ].Type.trim( ) ) }
|
|
}
|
|
|
|
if( P.Accounts )
|
|
{ P.Accounts
|
|
.split(/[\r\n]+/)
|
|
.filter( A => A && A.trim( ) && A != 'undefined' )
|
|
.forEach( Account =>
|
|
{ Products[ SKU ].Accounts[ Account ] = Variant })
|
|
}
|
|
|
|
if( Variant )
|
|
{ if( !Products[ SKU ].Variants[ Variant ] )
|
|
{ Products[ SKU ].Variants[ Variant ] = { } }
|
|
for( let Nr = 1; Nr < 11; Nr++ )
|
|
{ if( P[ Nr ] )
|
|
{ Products[ SKU ].Variants[ Variant ][ Nr ]
|
|
= getHashFromPhotoURL( P[ Nr ] )
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|
|
|
|
for( let SKU in Products )
|
|
{ //console.log( SKU, Products[ SKU ] )
|
|
if( SKU )
|
|
{ localStorage.setItem
|
|
( SKU, JSON.stringify( Products[ SKU ] ) )
|
|
}
|
|
}
|
|
|
|
resolve( true )
|
|
|
|
}).catch( err => console.error( 'cant [loadData]', err ) )
|
|
|
|
|
|
const init = async ( ) =>
|
|
{ w.onkeydown = Window_onKeyDown
|
|
w.onscroll = Window_onScroll
|
|
w.onresize = Window_onResize
|
|
Window_onResize( )
|
|
|
|
searchForm.onsubmit = onSearch
|
|
searchField.focus( )
|
|
// searchField.onchange = Search_onKeypress
|
|
// searchField.onkeypress = Search_onKeypress
|
|
|
|
btnShowAll.onclick = e =>
|
|
{ if( e ) e.preventDefault( )
|
|
searchField.value = '*'
|
|
e.target.blur( )
|
|
onSearch( )
|
|
return false
|
|
}
|
|
btnShowWithoutPhotos.onclick = e =>
|
|
{ if( e ) e.preventDefault( )
|
|
searchField.value = '-1'
|
|
e.target.blur( )
|
|
onSearch( )
|
|
return false
|
|
}
|
|
chkShowOnlyAvaliable.onchange = ShowOnlyAvaliable
|
|
|
|
|
|
ProductTemplate.innerHTML =
|
|
d.getElementById( 'ProductTemplate' ).innerHTML
|
|
ThumbnailsRowTemplate.innerHTML =
|
|
d.getElementById( 'ThumbnailsRowTemplate' ).innerHTML
|
|
ThumbnailTemplate.innerHTML =
|
|
d.getElementById( 'ThumbnailTemplate' ).innerHTML
|
|
|
|
if( !localStorage.getItem( 'lastUpdated' )
|
|
|| !Object.keys( Products ).length
|
|
)
|
|
{ await loadData( ) }
|
|
else
|
|
{ loadData( ) }
|
|
|
|
PopulateSearchWithTypes( )
|
|
|
|
unloadedProducts = Object.keys( Products )
|
|
unloadedProducts.sort( (a,b) => Products[a].id - Products[b].id )
|
|
// console.log( 'active Products:', unloadedProducts )
|
|
|
|
let locHash = unURIHash( )
|
|
if( w.location.search || locHash )
|
|
{ let u = new URLSearchParams( w.location.search )
|
|
MaxChunk = Number( u.get( 'max' ) || 100 )
|
|
let s = locHash
|
|
searchField.value = s.toUpperCase( )
|
|
onSearch( )
|
|
}
|
|
else
|
|
{ showProducts( MaxChunk ) }
|
|
}
|
|
|
|
|
|
w.addEventListener( 'DOMContentLoaded', init( ), true )
|
|
w.addEventListener( 'beforeunload', e =>
|
|
{
|
|
if( _HardReload_in_3_2_ )
|
|
{ console.warn( 'CLEAR!!' )
|
|
localStorage.clear( )
|
|
}
|
|
})
|