From caadf7d6afcdfef7cb066d80a798bbc7c7be740c Mon Sep 17 00:00:00 2001 From: Tony Air Date: Wed, 2 Sep 2020 05:12:38 +0700 Subject: [PATCH] Minor updates --- src/js/_components/_ui.ajax.js | 4 +- src/js/_components/_ui.form.croppie.js | 190 ++++++++++-------- src/js/_components/_ui.form.validate.field.js | 37 +++- src/js/_components/_ui.form.validate.js | 6 +- src/js/_events.js | 2 + src/js/_main.js | 36 +++- src/scss/_components/_ui.carousel.scss | 5 + src/scss/_components/_ui.main.scss | 14 +- 8 files changed, 179 insertions(+), 115 deletions(-) diff --git a/src/js/_components/_ui.ajax.js b/src/js/_components/_ui.ajax.js index 6795a4b..f4a1316 100755 --- a/src/js/_components/_ui.ajax.js +++ b/src/js/_components/_ui.ajax.js @@ -286,7 +286,7 @@ const AjaxUI = (($) => { for (const url in $.xhrPool.requests) { const jqXHR = $.xhrPool.requests[url]; $.ajax(jqXHR.opts); - console.log(`AJAX request is restored (${jqXHR.opts.url})`); + //console.log(`AJAX request is restored (${jqXHR.opts.url})`); } $.xhrPool.paused = false; @@ -297,7 +297,7 @@ const AjaxUI = (($) => { beforeSend: (jqXHR) => {}, // and connection to list complete: (jqXHR) => { if (!$.xhrPool.paused) { - console.log(`AJAX request is done (${jqXHR.opts.url})`); + //console.log(`AJAX request is done (${jqXHR.opts.url})`); delete $.xhrPool.requests[jqXHR.opts.url]; } }, diff --git a/src/js/_components/_ui.form.croppie.js b/src/js/_components/_ui.form.croppie.js index c08c801..ea4eeb3 100755 --- a/src/js/_components/_ui.form.croppie.js +++ b/src/js/_components/_ui.form.croppie.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; import $ from 'jquery'; @@ -9,7 +9,6 @@ import 'croppie/croppie.js'; import 'exif-js/exif.js'; const CroppieUI = (($) => { - const NAME = 'jsCroppieUI'; const DATA_KEY = NAME; @@ -28,11 +27,12 @@ const CroppieUI = (($) => { }; class CroppieUI { - constructor(element) { const ui = this; const $el = $(element); + console.log(`${NAME}: init ...`); + ui.$el = $el; $el.data(DATA_KEY, this); @@ -44,7 +44,7 @@ const CroppieUI = (($) => { $el.append( '
' + - '
' + '', ); //$el.append(ui.inputData); @@ -102,103 +102,127 @@ const CroppieUI = (($) => { ui.uploadCrop.show(); ui.uploadCropWrap.show(); ui.$btnRemove.show(); - } + }; reader.readAsDataURL(input.files[0]); + $form.off('submit'); $form.on('submit', (e) => { - //$(input).val(''); + console.log(`${NAME}: Processing submission ...`); + + e.preventDefault(); + + if ($form.data('locked')) { + console.warn(`${NAME}: Form#${$form.attr('id')} is locked.`); + return false; + } + + $form.data('locked', true); + SpinnerUI.show(); if (!ui.uploadCrop.hasClass('ready')) { return true; } - ui.uploadCrop.croppie('result', { - type: 'blob', - size: { - width: ui.width, - height: ui.height, - }, - format: 'png', - }).then((blob) => { - const form = e.currentTarget; - const data = new FormData(form); - const name = $(input).attr('name'); - - data.delete('BackURL'); - data.delete(name); - data.append(name, blob, `${name }-image.png`); - data.append('ajax', '1'); - - if (!$(form).data('jsFormValidate').validate()) { - return false; - } - - $.ajax({ - url: $(form).attr('action'), - data, - processData: false, - contentType: false, - type: $(form).attr('method'), - success: function(data) { - let IS_JSON = false; - let json = {}; - try { - IS_JSON = true; - json = $.parseJSON(data); - } catch (e) { - IS_JSON = false; - } - - if (IS_JSON) { - /*for (let k in json) { - $form.find('select[name="' + k + '"],input[name="' + k + '"],textarea[name="' + k + '"]').setError(true, json[k]); - }*/ - - if (typeof json['status'] !== 'undefined') { - if (json['status'] === 'success') { - MainUI.alert(json['message'], json['status']); - - if (typeof json['link'] !== 'undefined') { - setTimeout(() => { - G.location = json['link']; - }, 2000); - } else { - //G.location.reload(false); - } - } else if (json['status'] === 'error') { - MainUI.alert(json['message'], json['status']); - } - } - - if (typeof json['form'] !== 'undefined') { - $(form).replaceWith(json['form']); - } - } else { - $(form).replaceWith(data); - //G.location.reload(false); - } - - SpinnerUI.hide(); - $(G).trigger(Events.AJAX); + ui.uploadCrop + .croppie('result', { + type: 'blob', + size: { + width: ui.width, + height: ui.height, }, + format: 'png', + }) + .then((blob) => { + const form = e.currentTarget; + const data = new FormData(form); + const name = $(input).attr('name'); + + data.delete('BackURL'); + data.delete(name); + data.append(name, blob, `${name}-image.png`); + data.append('ajax', '1'); + + if (!$(form).data('jsFormValidate').validate()) { + return false; + } + + $.ajax({ + url: $(form).attr('action'), + data, + processData: false, + contentType: false, + type: $(form).attr('method'), + success: function (data) { + $form.data('locked', false); + + let IS_JSON = false; + let json = {}; + try { + IS_JSON = true; + json = $.parseJSON(data); + } catch (e) { + IS_JSON = false; + } + + if (IS_JSON) { + if (typeof json['status'] !== 'undefined') { + if (json['status'] === 'success') { + MainUI.alert(json['message'], json['status']); + + if (typeof json['link'] !== 'undefined') { + console.log( + `${NAME}: Finished submission > JSON ... Redirecting to ${json['link']}.`, + ); + + setTimeout(() => { + G.location = json['link']; + }, 2000); + } else { + console.warn( + `${NAME}: Finished submission > JSON no redirect link.`, + ); + } + } else if (json['status'] === 'error') { + MainUI.alert(json['message'], json['status']); + } + } + + if (typeof json['form'] !== 'undefined') { + console.log( + `${NAME}: Finished submission > JSON. Got new form response.`, + ); + + $(form).replaceWith(json['form']); + $(G).trigger(Events.AJAX); + } + } else { + console.log( + `${NAME}: Finished submission > DATA response.`, + ); + + $(form).replaceWith(data); + $(G).trigger(Events.AJAX); + //G.location.reload(false); + } + + SpinnerUI.hide(); + }, + }); + + //ui.inputData.val(data); }); - - //ui.inputData.val(data); - - }); - - e.preventDefault(); }); - } else { - console.log('Sorry - your browser doesn\'t support the FileReader API'); + console.log( + `${NAME}: Sorry - your browser doesn't support the FileReader API.`, + ); } } static dispose() { - console.log(`Destroying: ${NAME}`); + console.log(`${NAME}: destroying.`); } static _jQueryInterface() { diff --git a/src/js/_components/_ui.form.validate.field.js b/src/js/_components/_ui.form.validate.field.js index 8673f9e..df69125 100755 --- a/src/js/_components/_ui.form.validate.field.js +++ b/src/js/_components/_ui.form.validate.field.js @@ -5,7 +5,7 @@ const FormValidateField = (($) => { // Constants const NAME = 'jsFormValidateField'; const DATA_KEY = NAME; - const $Html = $('html, body'); + const $Body = $('body'); const FieldUI = 'jsFormFieldUI'; @@ -50,15 +50,21 @@ const FormValidateField = (($) => { const val = $el.val(); - // browser checks + required + // browser checks + if (!ui.$el[0].checkValidity()) { + valid = false; + console.warn(`Browser check validity is failed #${$el.attr('id')}`); + } + + // required if ( - !ui.$el[0].checkValidity() || - ($el.hasClass('required') && - (!val.length || - !val.trim().length || - (ui.isHtml(val) && !$(val).text().length))) + $el.hasClass('required') && + (!val.length || + !val.trim().length || + (ui.isHtml(val) && !$(val).text().length)) ) { valid = false; + console.warn(`Required field is missing #${$el.attr('id')}`); } // validate URL @@ -66,6 +72,7 @@ const FormValidateField = (($) => { valid = false; msg = 'URL must start with http:// or https://. For example: https://your-domain.com/'; + console.warn(`Wrong URL #${$el.attr('id')}`); } this.removeError(); @@ -73,7 +80,12 @@ const FormValidateField = (($) => { // extra checks if (extraChecks) { extraChecks.forEach((check) => { - valid = valid && check(); + const result = check(); + valid = valid && result; + if (!result) { + console.log(check); + console.warn(`Extra check is failed #${$el.attr('id')}`); + } }); } @@ -111,16 +123,19 @@ const FormValidateField = (($) => { const fieldUI = ui.$el.data(FieldUI); const $field = ui.$el.closest('.field'); - const pos = $field.offset().top; + const bodyScroll = $Body.scrollTop(); + const pos = $field[0].getBoundingClientRect().top; //$field.offset().top; + + const rowCorrection = parseInt($field.css('font-size')) * 4; ui.removeError(); $field.addClass('error'); if (msg) { fieldUI.addMessage(msg, 'alert-error alert-danger', scrollTo); - } else if (scrollTo) { + } else if (pos && scrollTo) { $field.focus(); - $Html.scrollTop(pos - 100); + $Body.scrollTop(bodyScroll + pos - rowCorrection); } } diff --git a/src/js/_components/_ui.form.validate.js b/src/js/_components/_ui.form.validate.js index 06db8ee..189c9e8 100755 --- a/src/js/_components/_ui.form.validate.js +++ b/src/js/_components/_ui.form.validate.js @@ -85,8 +85,6 @@ const FormValidate = (($) => { SpinnerUI.hide(); ui.$element.addClass('error'); - console.log('Invalid form data:'); - console.log($el); if (badCallback) { badCallback(); @@ -101,7 +99,7 @@ const FormValidate = (($) => { } static _jQueryInterface() { - return this.each(function() { + return this.each(function () { // attach functionality to element const $element = $(this); let data = $element.data(DATA_KEY); @@ -117,7 +115,7 @@ const FormValidate = (($) => { // jQuery interface $.fn[NAME] = FormValidate._jQueryInterface; $.fn[NAME].Constructor = FormValidate; - $.fn[NAME].noConflict = function() { + $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return FormValidate._jQueryInterface; }; diff --git a/src/js/_events.js b/src/js/_events.js index fa82736..5649c59 100755 --- a/src/js/_events.js +++ b/src/js/_events.js @@ -4,6 +4,8 @@ module.exports = { AJAX: 'ajax-load', + AJAXMAIN: 'ajax-main-load', + MAININIT: 'main-init', TABHIDDEN: 'tab-hidden', TABFOCUSED: 'tab-focused', OFFLINE: 'offline', diff --git a/src/js/_main.js b/src/js/_main.js index 16aad82..8dad6c6 100755 --- a/src/js/_main.js +++ b/src/js/_main.js @@ -327,6 +327,7 @@ const MainUI = (($) => { } $Body.data(NAME, this); + $(W).removeClass('lock-main-init'); } static detectBootstrapScreenSize() { @@ -467,6 +468,24 @@ const MainUI = (($) => { } }); + // replace img src + $Body + .find('[data-src-replace]') + .not('.loaded') + .each((i, el) => { + const $el = $(el); + const lazySrc = $el.data('src-replace'); + + if ($el.hasClass('loaded')) { + return; + } + + if (lazySrc && lazySrc.length) { + $el.addClass('loaded'); + $el.attr('src', lazySrc); + } + }); + // load defined images AjaxUI.preload($imgUrls).then(() => { $(W).trigger('images-loaded'); @@ -487,9 +506,20 @@ const MainUI = (($) => { } } - $(W).on(`${Events.AJAX} ${Events.LOADED}`, () => { - MainUI.init(); - }); + $(W).on( + `${Events.MAININIT} ${Events.AJAX} ${Events.AJAXMAIN} ${Events.LOADED}`, + () => { + const $w = $(W); + + if ($w.hasClass('lock-main-init')) { + console.warn('MainUI is locked'); + return; + } + + $w.addClass('lock-main-init'); + MainUI.init(); + }, + ); $(W).on(`${Events.RESIZE}`, () => { MainUI.detectBootstrapScreenSize(); diff --git a/src/scss/_components/_ui.carousel.scss b/src/scss/_components/_ui.carousel.scss index 9988244..53b7fd3 100755 --- a/src/scss/_components/_ui.carousel.scss +++ b/src/scss/_components/_ui.carousel.scss @@ -17,6 +17,7 @@ $carousel-controls-font-size: 3rem; $carousel-controls-zindex: 11 !default; $carousel-controls-shadow: 1px 1px $black !default; $carousel-controls-hover-bg: transparentize($black, 0.4) !default; +$carousel-slide-img-loading-max-height: 25vh !default; .carousel-slide { min-height: $carousel-slide-min-height; @@ -37,6 +38,10 @@ $carousel-controls-hover-bg: transparentize($black, 0.4) !default; display: block; width: 100%; } + + img.loading { + max-height: $carousel-slide-img-loading-max-height; + } } .carousel-control-prev, diff --git a/src/scss/_components/_ui.main.scss b/src/scss/_components/_ui.main.scss index cf79634..ad689ca 100755 --- a/src/scss/_components/_ui.main.scss +++ b/src/scss/_components/_ui.main.scss @@ -178,25 +178,15 @@ textarea, .container { position: relative; display: flex; - flex-direction: column; + flex-direction: row; align-items: center; justify-content: center; - @media (min-width: map-get($grid-breakpoints, 'sm')) { - flex-direction: row; - } } .typography { flex: 1 1; } .btn-close { - position: absolute; - top: 0; - right: 0; - - @media (min-width: map-get($grid-breakpoints, 'sm')) { - position: static; - margin-left: 1rem; - } + margin-left: 1rem; } }