Skip to content

Commit

Permalink
feat: conditionally add async tag in page editor clientlibs (#19)
Browse files Browse the repository at this point in the history
* feat: conditionally add async tag in page editor clientlibs

Co-authored-by: Rachit Kumar <[email protected]>
Co-authored-by: Cezary Czernecki <[email protected]>
Co-authored-by: Artur Kudlacz <[email protected]>
Co-authored-by: grubyak <[email protected]>
  • Loading branch information
5 people authored Sep 22, 2020
1 parent f8d2f4f commit 9c3e86b
Show file tree
Hide file tree
Showing 8 changed files with 591 additions and 80 deletions.
114 changes: 114 additions & 0 deletions src/AuthoringUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import Constants, { AEM_MODE, TAG_ATTR, TAG_TYPE } from './Constants';
import { PathUtils } from './PathUtils';
import MetaProperty from './MetaProperty';

export class AuthoringUtils {
private _apiDomain: string | null;

constructor(domain: string | null) {
this._apiDomain = domain;
}

getApiDomain(): string | null {
return this._apiDomain;
}

/**
* Returns all the tags for requested state.
* @returns Serialized tags.
*/
public getTagsForState(state: string): string {
let tags = '';

if (state === Constants.STATE_AUTHORING) {
const clientLibs = this.generateClientLibUrls();

tags = clientLibs.map(resource => {
if (resource.endsWith('.js')) {
return this.generateElementString(TAG_TYPE.JS, TAG_ATTR.SRC, resource);
} else if (resource.endsWith('.css')) {
return this.generateElementString(TAG_TYPE.STYLESHEET, TAG_ATTR.HREF, resource);
}
}).join('');
}

return tags;
}

/**
* Returns string value of all the concatenated tags.
* @returns Concatenated tags.
*/
private generateElementString(tagType: string, attr: string, attrValue: string): string {
let tag = '';

if (tagType === TAG_TYPE.JS) {
tag = `<script ${attr}="${attrValue}"></script>`;
} else if (tagType === TAG_TYPE.STYLESHEET) {
tag = `<link ${attr}="${attrValue}"/>`;
}

return tag;
}

/**
* Checks status of requested state.
* @returns `true` if requested state is active.
*/
public static isStateActive(state: string): boolean {
if (state === Constants.STATE_AUTHORING) {
const viaMetaProperty = PathUtils.getMetaPropertyValue(MetaProperty.WCM_MODE) === AEM_MODE.EDIT;
const viaQueryParam = PathUtils.isBrowser() && (AuthoringUtils.getAemMode() === AEM_MODE.EDIT);

return viaMetaProperty || viaQueryParam;
}

return false;
}

/**
* Checks AEM mode.
* @returns AEM mode or `null`.
*/
public static getAemMode(): string | null {
let url: URL;

try {
url = new URL(PathUtils.getCurrentURL());
return url.searchParams.get(Constants.AEM_MODE_KEY);
} catch (e) {
// invalid url
}

return null;
}

/**
* Generates urls to authoring clientlibs.
* @returns Clientlib URLs.
*/
public generateClientLibUrls(): string[] {
const result: string[] = [];
const domain = this.getApiDomain();

if (domain) {
Constants.AUTHORING_LIBRARIES.forEach((library) => {
result.push(`${domain}${Constants.EDITOR_CLIENTLIB_PATH}${library}`);
});
}

return result;
}
}
52 changes: 50 additions & 2 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

/**
* Useful variables for interacting with CQ/AEM components.
*
* @namespace Constants
*/
export class Constants {
Expand Down Expand Up @@ -51,9 +50,58 @@ export class Constants {
*/
public static readonly JCR_CONTENT = 'jcr:content';

/**
* AEM state `authoring`.
*/
public static readonly STATE_AUTHORING = 'authoring';

/**
* Flag name (query parameter) to determine AEM mode.
*/
public static readonly AEM_MODE_KEY = 'aemmode';

/**
* Base path for editor clientlibs.
*/
public static readonly EDITOR_CLIENTLIB_PATH = '/etc.clientlibs/cq/gui/components/authoring/editors/clientlibs/internal/';

/**
* Authoring libraries.
*/
public static readonly AUTHORING_LIBRARIES = [
'page.js',
'page.css',
'pagemodel/messaging.js',
'messaging.js'
];

private constructor() {
// hide constructor
}
}

export default Constants;
/**
* AEM modes.
*/
export enum AEM_MODE {
EDIT = 'edit',
PREVIEW = 'preview'
}

/**
* Supported tag types.
*/
export enum TAG_TYPE {
JS = 'script',
STYLESHEET = 'stylesheet'
}

/**
* Supported tag attributes.
*/
export enum TAG_ATTR {
SRC = 'src',
HREF = 'href'
}

export default Constants;
9 changes: 6 additions & 3 deletions src/MetaProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
*/

/**
* Names of the meta properties associated with the PageModelProvider and ModelRouter
*
* @type {{PAGE_MODEL_ROOT_URL: string, PAGE_MODEL_ROUTE_FILTERS: string, PAGE_MODEL_ROUTER: string}}
* Meta property names associated with the PageModelProvider and ModelRouter.
*/
export class MetaProperty {
/**
Expand All @@ -31,6 +29,11 @@ export class MetaProperty {
*/
public static readonly PAGE_MODEL_ROUTER = 'cq:pagemodel_router';

/**
* Meta property pointing to wcm mode.
*/
public static readonly WCM_MODE = 'cq:wcmmode';

private constructor() {
// hide constructor
}
Expand Down
15 changes: 11 additions & 4 deletions src/ModelClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@ export class ModelClient {

/**
* @constructor
* @param {string} [apiHost] - Http host of the API
* @param [apiHost] Http host of the API.
*/
constructor(apiHost?: string) {
this._apiHost = apiHost || '';
this._apiHost = apiHost || null;
this._fetchPromises = {};
}

/**
* Returns http host of the API.
* @returns API host or `null`.
*/
get apiHost(): string | null {
return this._apiHost;
}

/**
* Fetches a model using the given a resource path.
*
* @param {string} modelPath - Absolute path to the model.
* @param modelPath Absolute path to the model.
* @return {*}
*/
public fetch<M extends Model>(modelPath: string): Promise<M> {
Expand Down
18 changes: 8 additions & 10 deletions src/ModelManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Model } from './Model';
import { ModelClient } from './ModelClient';
import { ModelStore } from './ModelStore';
import { PathUtils } from './PathUtils';
import { AuthoringUtils } from './AuthoringUtils';

/**
* Does the provided model object contains an entry for the given child path
Expand All @@ -38,12 +39,10 @@ function hasChildOfPath(model: any, childPath: string): boolean {
}

/**
* Does the provided page path correspond to the model root path
*
* @param {string} pagePath - path of the page model
* @param {string} modelRootPath - current model root path
* @return {boolean}
*
* Checks whether provided path corresponds to model root path.
* @param pagePath Page model path.
* @param modelRootPath Model root path.
* @returns `true` if provided page path is root
* @private
*/
function isPageURLRoot(pagePath: string, modelRootPath: string): boolean {
Expand All @@ -66,6 +65,7 @@ export class ModelManager {
private _fetchPromises: { [key: string]: Promise<Model> } = {};
private _initPromise: any;
private _editorClient: EditorClient | undefined;
private _clientlibUtil: AuthoringUtils | undefined;

public get modelClient() {
if (!this._modelClient) {
Expand Down Expand Up @@ -138,12 +138,11 @@ export class ModelManager {
this._modelClient = new ModelClient();
}

this._clientlibUtil = new AuthoringUtils(this.modelClient.apiHost);
this._editorClient = new EditorClient(this);
this._modelStore = (initialModel) ? new ModelStore(rootModelPath, initialModel) : new ModelStore(rootModelPath);


this._initPromise = this._checkDependencies().then(() => {

const data = this.modelStore.getData(rootModelPath);

if (data && (Object.keys(data).length > 0)) {
Expand Down Expand Up @@ -182,8 +181,7 @@ export class ModelManager {

/**
* Returns the path of the data model root.
*
* @return {string}
* @returns Page model root path.
*/
public get rootPath(): string {
return this.modelStore.rootPath;
Expand Down
8 changes: 8 additions & 0 deletions src/PathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,14 @@ export class PathUtils {
return this.isBrowser() ? window.location.pathname : null;
}

/**
* Returns empty string or current URL if called in the browser.
* @returns Current URL.
*/
public static getCurrentURL(): string {
return this.isBrowser() ? window.location.href : '';
}

/**
* Dispatches a custom event on the window object, when in the browser context.
*
Expand Down
Loading

0 comments on commit 9c3e86b

Please sign in to comment.