/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.servlet.taglib.aui; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.Mergeable; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import java.io.IOException; import java.io.Serializable; import java.io.Writer; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author Brian Wing Shun Chan * @author Shuyang Zhou */ public class ScriptData implements Mergeable<ScriptData>, Serializable { public void append( String portletId, String content, String modules, ModulesType modulesType) { PortletData portletData = _getPortletData(portletId); portletData.append(content, modules, modulesType); } public void append( String portletId, StringBundler contentSB, String modules, ModulesType modulesType) { PortletData portletData = _getPortletData(portletId); portletData.append(contentSB, modules, modulesType); } public void mark() { for (PortletData portletData : _portletDataMap.values()) { portletData.mark(); } } @Override public ScriptData merge(ScriptData scriptData) { if ((scriptData != null) && (scriptData != this)) { _portletDataMap.putAll(scriptData._portletDataMap); } return this; } public void reset() { for (PortletData portletData : _portletDataMap.values()) { portletData.reset(); } } @Override public ScriptData split() { return new ScriptData(); } public void writeTo(Writer writer) throws IOException { writer.write("<script type=\"text/javascript\">\n// <![CDATA[\n"); StringBundler auiModulesSB = new StringBundler(_portletDataMap.size()); Set<String> auiModulesSet = new HashSet<>(); StringBundler es6ModulesSB = new StringBundler(_portletDataMap.size()); Set<String> es6ModulesSet = new HashSet<>(); for (PortletData portletData : _portletDataMap.values()) { portletData._rawSB.writeTo(writer); if (!portletData._auiModulesSet.isEmpty()) { auiModulesSB.append(portletData._auiCallbackSB); } if (!portletData._es6ModulesSet.isEmpty()) { es6ModulesSB.append(portletData._es6CallbackSB); } auiModulesSet.addAll(portletData._auiModulesSet); es6ModulesSet.addAll(portletData._es6ModulesSet); } if ((auiModulesSB.index() == 0) && (es6ModulesSB.index() == 0)) { writer.write("\n// ]]>\n</script>"); return; } if (!es6ModulesSet.isEmpty()) { writer.write("Liferay.Loader.require("); Iterator<String> iterator = es6ModulesSet.iterator(); while (iterator.hasNext()) { writer.write(StringPool.APOSTROPHE); writer.write(iterator.next()); writer.write(StringPool.APOSTROPHE); if (iterator.hasNext()) { writer.write(StringPool.COMMA_AND_SPACE); } } writer.write(StringPool.COMMA_AND_SPACE); writer.write("function("); iterator = es6ModulesSet.iterator(); Set<String> variableNames = new HashSet<>(es6ModulesSet.size()); while (iterator.hasNext()) { writer.write(_generateVariable(iterator.next(), variableNames)); if (iterator.hasNext()) { writer.write(StringPool.COMMA_AND_SPACE); } } writer.write(") {\n"); es6ModulesSB.writeTo(writer); writer.write("},\nfunction(error) {\nconsole.error(error);\n});"); } if (!auiModulesSet.isEmpty()) { writer.write("AUI().use("); for (String use : auiModulesSet) { writer.write(StringPool.APOSTROPHE); writer.write(use); writer.write(StringPool.APOSTROPHE); writer.write(StringPool.COMMA_AND_SPACE); } writer.write("function(A) {"); auiModulesSB.writeTo(writer); writer.write("});"); } writer.write("\n// ]]>\n</script>"); } public static enum ModulesType { AUI, ES6 } private String _generateVariable(String name, Set<String> names) { StringBuilder sb = new StringBuilder(name.length()); char c = name.charAt(0); boolean modified = true; if ((CharPool.LOWER_CASE_A <= c) && (c <= CharPool.LOWER_CASE_Z) || (c == CharPool.UNDERLINE)) { sb.append(c); modified = false; } else if ((CharPool.UPPER_CASE_A <= c) && (c <= CharPool.UPPER_CASE_Z)) { sb.append((char)(c + 32)); } else { sb.append(CharPool.UNDERLINE); } boolean startNewWord = false; for (int i = 1; i < name.length(); i++) { c = name.charAt(i); if ((CharPool.LOWER_CASE_A <= c) && (c <= CharPool.LOWER_CASE_Z)) { if (startNewWord) { sb.append((char)(c - 32)); startNewWord = false; } else { sb.append(c); } } else if ((CharPool.UPPER_CASE_A <= c) && (c <= CharPool.UPPER_CASE_Z) || ((CharPool.NUMBER_0 <= c) && (c <= CharPool.NUMBER_9)) || (c == CharPool.UNDERLINE)) { sb.append(c); startNewWord = false; } else { modified = true; startNewWord = true; } } String safeName = name; if (modified) { safeName = sb.toString(); name = safeName; } int i = 1; while (!names.add(name)) { name = safeName.concat(String.valueOf(i++)); } return name; } private PortletData _getPortletData(String portletId) { if (Validator.isNull(portletId)) { portletId = StringPool.BLANK; } PortletData portletData = _portletDataMap.get(portletId); if (portletData == null) { portletData = new PortletData(); PortletData oldPortletData = _portletDataMap.putIfAbsent( portletId, portletData); if (oldPortletData != null) { portletData = oldPortletData; } } return portletData; } private static final long serialVersionUID = 1L; private final ConcurrentMap<String, PortletData> _portletDataMap = new ConcurrentHashMap<>(); private static class PortletData implements Serializable { public void append( String content, String modules, ModulesType modulesType) { if (Validator.isNull(modules)) { _rawSB.append(content); } else { String[] modulesArray = StringUtil.split(modules); if (modulesType == ModulesType.AUI) { _auiCallbackSB.append("(function() {"); _auiCallbackSB.append(content); _auiCallbackSB.append("})();"); for (String module : modulesArray) { _auiModulesSet.add(StringUtil.trim(module)); } } else { _es6CallbackSB.append("(function() {"); _es6CallbackSB.append(content); _es6CallbackSB.append("})();"); for (String module : modulesArray) { _es6ModulesSet.add(StringUtil.trim(module)); } } } } public void append( StringBundler contentSB, String modules, ModulesType modulesType) { if (Validator.isNull(modules)) { _rawSB.append(contentSB); } else { String[] modulesArray = StringUtil.split(modules); if (modulesType == ModulesType.AUI) { _auiCallbackSB.append("(function() {"); _auiCallbackSB.append(contentSB); _auiCallbackSB.append("})();"); for (String module : modulesArray) { _auiModulesSet.add(StringUtil.trim(module)); } } else { _es6CallbackSB.append("(function() {"); _es6CallbackSB.append(contentSB); _es6CallbackSB.append("})();"); for (String module : modulesArray) { _es6ModulesSet.add(StringUtil.trim(module)); } } } } public void mark() { _auiCallbackSBIndex = _auiCallbackSB.index(); _es6CallbackSBIndex = _es6CallbackSB.index(); _rawSBIndex = _rawSB.index(); } public void reset() { if (_auiCallbackSBIndex >= 0) { _auiCallbackSB.setIndex(_auiCallbackSBIndex); } if (_es6CallbackSBIndex >= 0) { _es6CallbackSB.setIndex(_es6CallbackSBIndex); } if (_rawSBIndex >= 0) { _rawSB.setIndex(_rawSBIndex); } } private static final long serialVersionUID = 1L; private final StringBundler _auiCallbackSB = new StringBundler(); private int _auiCallbackSBIndex = -1; private final Set<String> _auiModulesSet = new HashSet<>(); private final StringBundler _es6CallbackSB = new StringBundler(); private int _es6CallbackSBIndex = -1; private final Set<String> _es6ModulesSet = new HashSet<>(); private final StringBundler _rawSB = new StringBundler(); private int _rawSBIndex = -1; } }