This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch commons-thumbnails
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git


The following commit(s) were added to refs/heads/commons-thumbnails by this 
push:
     new da69612  A mostly working implementation of transformations
da69612 is described below

commit da69612d8d89226e15640bb1fc4d0e46f41f6c09
Author: Dan Klco <[email protected]>
AuthorDate: Sun Jul 25 22:59:59 2021 -0400

    A mostly working implementation of transformations
---
 .../reference/components/general/image/edit.json   |  11 +-
 .../reference/components/general/image/image.jsp   |   9 +-
 .../components/general/image/transformations.jsp   |   8 +-
 ui/src/main/frontend/js/cms.fields.js              | 245 +++++++++++++--------
 ui/src/main/frontend/js/cms.pathfield.js           |  39 ++--
 .../components/editor/fields/base/base.jsp         |   7 +-
 .../components/editor/fields/path/path.jsp         |   9 +-
 .../components/editor/scripts/transformations.jsp  |   4 +-
 8 files changed, 217 insertions(+), 115 deletions(-)

diff --git 
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
 
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
index ef07361..4b3cf95 100644
--- 
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
+++ 
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
@@ -13,7 +13,11 @@
             "label": "Image Source",
             "name": "src",
             "titleProperty": "jcr:content/jcr:title",
-            "required": true
+            "required": true,
+            "events": {
+                "input": "const srcValue = 
document.querySelector('input[name=src]').value;\r\nconst transformationField = 
document.querySelector('select[name=transformation]').closest('.field');\r\ntransformationField.disabled=true;\r\nconst
 url = 
`${transformationField.dataset.path}.html${location.pathname.replace('\/cms\/editor\/edit.html','')}?src=${srcValue}`;\r\nfetch(url).then(res
 => res.text()).then(html => {\r\n    const div = 
document.createElement('div');\r\n    div.innerHTML  [...]
+                "load": "const srcValue = 
document.querySelector('input[name=src]').value;\r\nconst transformationField = 
document.querySelector('select[name=transformation]').closest('.field');\r\ntransformationField.disabled=true;\r\nconst
 url = 
`${transformationField.dataset.path}.html${location.pathname.replace('\/cms\/editor\/edit.html','')}?src=${srcValue}`;\r\nfetch(url).then(res
 => res.text()).then(html => {\r\n    const div = 
document.createElement('div');\r\n    div.innerHTML = [...]
+            }
         },
         "transformation": {
             "jcr:primaryType": "nt:unstructured",
@@ -30,6 +34,11 @@
             "name": "transformationFormat",
             "required": false,
             "options": {
+                "default": {
+                    "jcr:primaryType": "nt:unstructured",
+                    "label": "Default Extension",
+                    "value": "default"
+                },
                 "png": {
                     "jcr:primaryType": "nt:unstructured",
                     "label": "PNG",
diff --git 
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
 
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
index f5d9d4c..58ac1c9 100644
--- 
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
+++ 
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
@@ -19,7 +19,14 @@
 <%@include file="/libs/sling-cms/global.jsp"%>
 <c:if test="${not empty properties.src}">
     <c:if test="${not empty properties.transformation}">
-        <c:set var="transform" 
value=".transform/${properties.transformation}.${properties.transformationFormat}"
 />
+        <c:choose>
+            <c:when test="${properties.transformationFormat == 'default'}">
+                <c:set var="transform" 
value=".transform/${properties.transformation}" />
+            </c:when>
+            <c:otherwise>
+                <c:set var="transform" 
value=".transform/${properties.transformation}.${properties.transformationFormat}"
 />
+            </c:otherwise>
+        </c:choose>
     </c:if>
     <img src="${properties.src}${transform}" alt="${properties.alt}" 
class="${properties.imageClass}" />
 </c:if>
\ No newline at end of file
diff --git 
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
 
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
index 50eb1e8..a69d9e0 100644
--- 
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
+++ 
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
@@ -17,10 +17,10 @@
  * under the License.
  */ --%>
  <%@include file="/libs/sling-cms/global.jsp"%>
-<sling:adaptTo adaptable="${slingRequest.requestPathInfo.suffixResource}" 
adaptTo="org.apache.sling.thumbnails.TransformationManager" 
var="transformationManager" />
+<sling:adaptTo adaptable="${slingRequest}" 
adaptTo="org.apache.sling.thumbnails.RenderedResource" var="rendered" />
 <option value="">None</option>
-<c:forEach var="transformation" 
items="${transformationManager.transformations}">
-    <option 
${slingRequest.requestPathInfo.suffixResource.valueMap.transformation == 
transformation.name ? 'selected' : ''} 
value="${sling:encode(transformation.name,'HTML_ATTR')}">
-        ${sling:encode(transformation.name,'HTML')}
+<c:forEach var="rendition" items="${rendered.supportedRenditions}">
+    <option 
${slingRequest.requestPathInfo.suffixResource.valueMap.transformation == 
rendition ? 'selected' : ''} value="${sling:encode(rendition,'HTML_ATTR')}">
+        ${sling:encode(rendition,'HTML')}
     </option>
 </c:forEach>
\ No newline at end of file
diff --git a/ui/src/main/frontend/js/cms.fields.js 
b/ui/src/main/frontend/js/cms.fields.js
index 11a9b8b..e759258 100644
--- a/ui/src/main/frontend/js/cms.fields.js
+++ b/ui/src/main/frontend/js/cms.fields.js
@@ -18,11 +18,11 @@
  */
 /* global wysihtml, wysihtmlParserRules */
 
-rava.bind('.file', {
+rava.bind(".file", {
   callbacks: {
     created() {
       const field = this;
-      const close = field.closest('form').querySelector('a.close');
+      const close = field.closest("form").querySelector("a.close");
 
       function setProgress(m, progress) {
         const meter = m;
@@ -37,89 +37,131 @@ rava.bind('.file', {
         formData.append("_charset_", "utf-8");
 
         const xhr = new XMLHttpRequest();
-        xhr.upload.addEventListener('loadstart', () => {
-          setProgress(meter, 0);
-        }, false);
-        xhr.upload.addEventListener('progress', (event) => {
-          const percent = (event.loaded / event.total) * 100;
-          setProgress(meter, percent);
-        }, false);
-        xhr.upload.addEventListener('load', () => {
-          meter.classList.add('is-info');
-        }, false);
-        xhr.addEventListener('readystatechange', (event) => {
-          let status; let text; let
-            readyState;
-          try {
-            readyState = event.target.readyState;
-            text = event.target.responseText;
-            status = event.target.status;
-          } catch (e) {
-            meter.classList.add('is-danger');
-          }
-          if (readyState === 4) {
-            meter.classList.remove('is-info');
-            if (status === 200 && text) {
-              meter.classList.add('is-success');
-            } else {
-              meter.classList.add('is-danger');
-              console.warn('Failed to upload %s, recieved message %s', 
file.name, text); // eslint-disable-line no-console
+        xhr.upload.addEventListener(
+          "loadstart",
+          () => {
+            setProgress(meter, 0);
+          },
+          false
+        );
+        xhr.upload.addEventListener(
+          "progress",
+          (event) => {
+            const percent = (event.loaded / event.total) * 100;
+            setProgress(meter, percent);
+          },
+          false
+        );
+        xhr.upload.addEventListener(
+          "load",
+          () => {
+            meter.classList.add("is-info");
+          },
+          false
+        );
+        xhr.addEventListener(
+          "readystatechange",
+          (event) => {
+            let status;
+            let text;
+            let readyState;
+            try {
+              readyState = event.target.readyState;
+              text = event.target.responseText;
+              status = event.target.status;
+            } catch (e) {
+              meter.classList.add("is-danger");
             }
-          }
-        }, false);
-        xhr.open('POST', action, true);
+            if (readyState === 4) {
+              meter.classList.remove("is-info");
+              if (status === 200 && text) {
+                meter.classList.add("is-success");
+              } else {
+                meter.classList.add("is-danger");
+                console.warn(
+                  "Failed to upload %s, recieved message %s",
+                  file.name,
+                  text
+                ); // eslint-disable-line no-console
+              }
+            }
+          },
+          false
+        );
+        xhr.open("POST", action, true);
         xhr.send(formData);
       }
       function handleFile(scope, file) {
-        const it = document.createElement('div');
-        const ctr = 
scope.closest('.control').querySelector('.file-item-container');
+        const it = document.createElement("div");
+        const ctr = scope
+          .closest(".control")
+          .querySelector(".file-item-container");
         let meter = null;
-        it.innerHTML = document.querySelector('.file-item-template').innerHTML;
-        meter = it.querySelector('.progress');
-        it.querySelector('.file-item-name').innerText = file.name;
-        ctr.classList.remove('is-hidden');
+        it.innerHTML = document.querySelector(".file-item-template").innerHTML;
+        meter = it.querySelector(".progress");
+        it.querySelector(".file-item-name").innerText = file.name;
+        ctr.classList.remove("is-hidden");
         ctr.appendChild(it);
-        uploadFile(meter, scope.closest('form').action, file);
+        uploadFile(meter, scope.closest("form").action, file);
       }
 
-      field.addEventListener('dragover', (event) => {
-        event.preventDefault();
-      }, false);
-      field.addEventListener('dragenter', (event) => {
-        event.preventDefault();
-        field.classList.add('is-primary');
-      }, false);
-      field.addEventListener('dragleave', (event) => {
-        event.preventDefault();
-        if(!field.contains(event.fromElement)){
-          field.classList.remove('is-primary');
-        }
-      }, false);
-      field.addEventListener('drop', (event) => {
-        event.preventDefault();
-        field.classList.remove('is-primary');
-        if (event.dataTransfer.items) {
-          const { items } = event.dataTransfer;
-          for (let i = 0; i < items.length; i++) { // eslint-disable-line 
no-plusplus
-            if (items[i].kind === 'file') {
-              handleFile(field, items[i].getAsFile());
-            }
+      field.addEventListener(
+        "dragover",
+        (event) => {
+          event.preventDefault();
+        },
+        false
+      );
+      field.addEventListener(
+        "dragenter",
+        (event) => {
+          event.preventDefault();
+          field.classList.add("is-primary");
+        },
+        false
+      );
+      field.addEventListener(
+        "dragleave",
+        (event) => {
+          event.preventDefault();
+          if (!field.contains(event.fromElement)) {
+            field.classList.remove("is-primary");
           }
-        } else {
-          const { files } = event.dataTransfer;
-          for (let i = 0; i < files.length; i++) { // eslint-disable-line 
no-plusplus
-            handleFile(field, files[i]);
+        },
+        false
+      );
+      field.addEventListener(
+        "drop",
+        (event) => {
+          event.preventDefault();
+          field.classList.remove("is-primary");
+          if (event.dataTransfer.items) {
+            const { items } = event.dataTransfer;
+            for (let i = 0; i < items.length; i++) {
+              // eslint-disable-line no-plusplus
+              if (items[i].kind === "file") {
+                handleFile(field, items[i].getAsFile());
+              }
+            }
+          } else {
+            const { files } = event.dataTransfer;
+            for (let i = 0; i < files.length; i++) {
+              // eslint-disable-line no-plusplus
+              handleFile(field, files[i]);
+            }
           }
-        }
-      }, false);
-      field.closest('form').querySelector('button[type=submit]').remove();
-      close.innerText = 'Done';
-      close.addEventListener('click', () => {
+        },
+        false
+      );
+      field.closest("form").querySelector("button[type=submit]").remove();
+      close.innerText = "Done";
+      close.addEventListener("click", () => {
         window.Sling.CMS.ui.reloadContext();
       });
-      field.querySelector('input').addEventListener('change', (event) => {
+      field.querySelector("input").addEventListener("change", (event) => {
         const { files } = event.target;
-        for (let i = 0; i < files.length; i++) { // eslint-disable-line 
no-plusplus
+        for (let i = 0; i < files.length; i++) {
+          // eslint-disable-line no-plusplus
           handleFile(field, files[i]);
         }
       });
@@ -128,37 +170,41 @@ rava.bind('.file', {
 });
 
 /* Support for updating the namehint when creating a component */
-rava.bind('.namehint', {
+rava.bind(".namehint", {
   callbacks: {
     created() {
       const field = this;
-      
this.closest('.Form-Ajax').querySelector('select[name="sling:resourceType"]').addEventListener('change',
 (evt) => {
-        const resourceType = evt.target.value.split('/');
-        field.value = resourceType[resourceType.length - 1];
-      });
+      this.closest(".Form-Ajax")
+        .querySelector('select[name="sling:resourceType"]')
+        .addEventListener("change", (evt) => {
+          const resourceType = evt.target.value.split("/");
+          field.value = resourceType[resourceType.length - 1];
+        });
     },
   },
 });
 
 /* Support for repeating form fields */
-rava.bind('.repeating', {
+rava.bind(".repeating", {
   callbacks: {
     created() {
       const ctr = this;
-      this.querySelectorAll('.repeating__add').forEach((el) => {
-        el.addEventListener('click', (event) => {
+      this.querySelectorAll(".repeating__add").forEach((el) => {
+        el.addEventListener("click", (event) => {
           event.stopPropagation();
           event.preventDefault();
-          const node = ctr.querySelector('.repeating__template > 
.repeating__item').cloneNode(true);
-          ctr.querySelector('.repeating__container').appendChild(node);
+          const node = ctr
+            .querySelector(".repeating__template > .repeating__item")
+            .cloneNode(true);
+          ctr.querySelector(".repeating__container").appendChild(node);
         });
       });
     },
   },
 });
-rava.bind('.repeating__item', {
+rava.bind(".repeating__item", {
   events: {
-    ':scope .repeating__remove': {
+    ":scope .repeating__remove": {
       click(event) {
         event.stopPropagation();
         event.preventDefault();
@@ -168,13 +214,38 @@ rava.bind('.repeating__item', {
   },
 });
 
-rava.bind('.rte', {
+rava.bind(".rte", {
   callbacks: {
     created() {
-      new wysihtml.Editor(this.querySelector('.rte-editor'), { // 
eslint-disable-line no-new, new-cap
-        toolbar: this.querySelector('.rte-toolbar'),
+      new wysihtml.Editor(this.querySelector(".rte-editor"), {
+        // eslint-disable-line no-new, new-cap
+        toolbar: this.querySelector(".rte-toolbar"),
         parserRules: wysihtmlParserRules,
       });
     },
   },
 });
+
+rava.bind('.field[data-events]:not([data-events=""])', {
+  callbacks: {
+    async created() {
+      const events = this.dataset.events.split(",").filter((e) => e !== "");
+      const res = await fetch(`${this.dataset.path}/events.json`, {
+        cache: "no-cache",
+        headers: {
+          Accept: "application/json",
+        },
+      });
+      const handlers = await res.json();
+      for (var event of events) {
+        this.querySelectorAll("input,select,textarea").forEach((el) => {
+          if (event === "load") {
+            Function(handlers[event])();
+          } else {
+            el.addEventListener(event, Function(handlers[event]));
+          }
+        });
+      }
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.pathfield.js 
b/ui/src/main/frontend/js/cms.pathfield.js
index e66b14d..fbf0f0c 100644
--- a/ui/src/main/frontend/js/cms.pathfield.js
+++ b/ui/src/main/frontend/js/cms.pathfield.js
@@ -19,24 +19,28 @@
 /* global autoComplete */
 Sling.CMS.pathfield = null;
 
-rava.bind('input.pathfield', {
+rava.bind("input.pathfield", {
   callbacks: {
     created() {
-      const { type } = this.dataset;
-      const { base } = this.dataset;
-
-      new autoComplete({ // eslint-disable-line no-new, new-cap
+      const { base, type } = this.dataset;
+      const field = this;
+      // eslint-disable-line no-new, new-cap
+      new autoComplete({
         minChars: 1,
         selector: this,
+        debounce: 100,
+        onSelect: function (e, term, item) {
+          setTimeout(() => field.dispatchEvent(new Event("input")), 500);
+        },
         source(t, response) {
           let term = t;
-          if (term === '/') {
+          if (term === "/") {
             term = base;
           }
           fetch(
             `/bin/cms/paths?path=${encodeURIComponent(
-              term,
-            )}&type=${encodeURIComponent(type)}`,
+              term
+            )}&type=${encodeURIComponent(type)}`
           )
             .then((resp) => resp.json())
             .then((data) => {
@@ -48,15 +52,15 @@ rava.bind('input.pathfield', {
   },
 });
 
-rava.bind('.search-button', {
+rava.bind(".search-button", {
   events: {
     click() {
-      Sling.CMS.pathfield = this.closest('.field').querySelector('.pathfield');
+      Sling.CMS.pathfield = this.closest(".field").querySelector(".pathfield");
     },
   },
 });
 
-rava.bind('.search-select-button', {
+rava.bind(".search-select-button", {
   events: {
     click() {
       const { path } = this.dataset;
@@ -65,23 +69,24 @@ rava.bind('.search-select-button', {
       } else {
         Sling.CMS.pathfield.postMessage(
           {
-            action: 'slingcms.setpath',
+            action: "slingcms.setpath",
             path,
           },
-          window.location.origin,
+          window.location.origin
         );
       }
-      this.closest('.modal').remove();
+      this.closest(".modal").remove();
     },
   },
 });
 
 window.addEventListener(
-  'message',
+  "message",
   (event) => {
-    if (event.data.action === 'slingcms.setpath') {
+    if (event.data.action === "slingcms.setpath") {
       Sling.CMS.pathfield.value = event.data.path;
+      Sling.CMS.pathfield.dispatchEvent(new Event("change"));
     }
   },
-  false,
+  false
 );
diff --git 
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
 
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
index e2b2031..a9f3246 100644
--- 
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
+++ 
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
@@ -48,7 +48,12 @@
         <c:set var="value" value="${editProperties[properties.name]}" 
scope="request" />
     </c:otherwise>
 </c:choose>
-<div class="field">
+<c:forEach var="event" 
items="${sling:getRelativeResource(resource,'./events').valueMap}">
+    <c:if test="${!fn:contains(event.key,':')}">
+        <c:set var="events" value="${events},${event.key}" />
+    </c:if>
+</c:forEach>
+<div class="field" data-events="${events}" data-path="${resource.path}">
     <c:if test="${not empty properties.label}">
         <label class="label" for="${properties.name}">
             <fmt:message key="${properties.label}" var="label" />
diff --git 
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
 
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
index f74ac86..9786855 100644
--- 
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
+++ 
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
@@ -55,9 +55,14 @@
         <c:if test="${properties.required}"><span 
class="has-text-danger">*</span></c:if>
     </label>
 </c:if>
+<c:forEach var="event" 
items="${sling:getRelativeResource(resource,'./events').valueMap}">
+    <c:if test="${!fn:contains(event.key,':')}">
+        <c:set var="events" value="${events},${event.key}" />
+    </c:if>
+</c:forEach>
 <c:choose>
     <c:when test="${properties.hidesearch != true}">
-        <div class="field has-addons">
+        <div class="field has-addons" data-events="${events}" 
data-path="${resource.path}">
           <div class="control is-expanded">
               <input class="input pathfield" type="text" 
id="${properties.name}" name="${properties.name}" value="${value}" ${required} 
${disabled} data-type="${properties.type}" data-base="${properties.basePath}" 
autocomplete="off" />
           </div>
@@ -72,7 +77,7 @@
          </div>
     </c:when>
     <c:otherwise>
-        <div class="field">
+        <div class="field" data-events="${events}" 
data-path="${resource.path}">
             <div class="control">
                 <input class="input pathfield" type="text" 
name="${properties.name}" value="${value}" ${required} ${disabled} 
data-type="${properties.type}" data-base="${properties.basePath}" 
autocomplete="off" />
             </div>
diff --git 
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
 
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
index c36aa39..7f9524e 100644
--- 
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
+++ 
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
@@ -17,9 +17,9 @@
  * under the License.
  */ --%>
  <%@include file="/libs/sling-cms/global.jsp"%>
-<sling:adaptTo adaptable="${slingRequest.requestPathInfo.suffixResource}" 
adaptTo="org.apache.sling.thumbnails.TransformationManager" 
var="transformationManager" />
+<sling:adaptTo adaptable="${slingRequest.requestPathInfo.suffixResource}" 
adaptTo="org.apache.sling.thumbnails.RenderedResource" var="rendered" />
 <option value="">None</option>
-<c:forEach var="transformation" 
items="${transformationManager.transformations}">
+<c:forEach var="transformation" items="${rendered.availableTransformations}">
     <option 
${slingRequest.requestPathInfo.suffixResource.valueMap.transformation == 
transformation.name ? 'selected' : ''} 
value="${sling:encode(transformation.path,'HTML_ATTR')}">
         ${sling:encode(transformation.name,'HTML')}
     </option>

Reply via email to