/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.core.gui.components.form.flexible.impl; import java.util.Iterator; import java.util.Set; import org.apache.commons.io.IOUtils; import org.json.JSONException; import org.json.JSONObject; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.render.StringOutput; import org.olat.core.logging.OLATRuntimeException; /** * Description:<br> * * <P> * Initial Date: 11.01.2007 <br> * * @author patrickb */ public class FormJSHelper { private static final String[] EXTJSACTIONS = { "dblclick", "click", "change" }; /** * create for example an * <code>onclick="o_ffEvent('ofo_1377','ofo_1377_dispatchuri','o_fi1399','ofo_1377_eventval','1')"</code> * * @param form * @param id * @param actions * @return */ public static StringBuilder getRawJSFor(Form form, String id, int actions) { StringBuilder sb = new StringBuilder(64); // find correct action! only one action supported for (int i = FormEvent.ON_DOTDOTDOT.length - 1; i >= 0; i--) { if (actions - FormEvent.ON_DOTDOTDOT[i] > 0) throw new AssertionError("only one actions supported here"); if (actions - FormEvent.ON_DOTDOTDOT[i] == 0) { sb.append(" on" + EXTJSACTIONS[i]);// javascript action sb.append("=\""); sb.append(getJSFnCallFor(form, id, i)); sb.append("\""); break; // actions = actions - FormEvent.ON_DOTDOTDOT[i]; } } return sb; } public static String getJSFnCallFor(Form form, String id, int actionIndex) { String content = "o_ffEvent('"; content += form.getFormName() + "','"; content += form.getDispatchFieldId() + "','"; content += id + "','"; content += form.getEventFieldId() + "','"; content += (FormEvent.ON_DOTDOTDOT[actionIndex]); content += ("')"); return content; } /** * Build the javascript method to send a flexi form event. * * @param item The form item * @param dirtyCheck If false, the dirty check is by passed * @param pushState If true, the state (visible url in browser) will be pushed to the browser * @param pairs Additional name value pairs send by the link * @return */ public static String getXHRFnCallFor(FormItem item, boolean dirtyCheck, boolean pushState, NameValuePair... pairs) { return getXHRFnCallFor(item.getRootForm(), item.getFormDispatchId(), 1, dirtyCheck, pushState, false, pairs); } /** * Build the javascript method to send a flexi form event. * * @param form The form object * @param id The id of the element * @param actionIndex The type of event (click...) * @param dirtyCheck If false, the dirty check is by passed * @param pushState If true, the state (visible url in browser) will be pushed to the browser * @param pairs Additional name value pairs send by the link * @return */ public static String getXHRFnCallFor(Form form, String id, int actionIndex, boolean dirtyCheck, boolean pushState, NameValuePair... pairs) { return getXHRFnCallFor(form, id, actionIndex, dirtyCheck, pushState, false, pairs); } /** * Build the javascript method to send a flexi form event with all possible settings. * * @param form The form object * @param id The id of the element * @param actionIndex The type of event (click...) * @param dirtyCheck If false, the dirty check is by passed * @param pushState If true, the state (visible url in browser) will be pushed to the browser * @param submit If true, the form will be submitted but it only works for none multi part forms. * @param pairs Additional name value pairs send by the link * @return */ public static String getXHRFnCallFor(Form form, String id, int actionIndex, boolean dirtyCheck, boolean pushState, boolean submit, NameValuePair... pairs) { StringOutput sb = new StringOutput(128); sb.append("o_ffXHREvent('") .append(form.getFormName()).append("','") .append(form.getDispatchFieldId()).append("','") .append(id).append("','") .append(form.getEventFieldId()).append("','") .append(FormEvent.ON_DOTDOTDOT[actionIndex]) .append("',").append(dirtyCheck) .append(",").append(pushState) .append(",").append(submit); if(pairs != null && pairs.length > 0) { for(NameValuePair pair:pairs) { sb.append(",'").append(pair.getName()).append("','").append(pair.getValue()).append("'"); } } sb.append(")"); IOUtils.closeQuietly(sb); return sb.toString(); } public static String getXHRNFFnCallFor(Form form, String id, int actionIndex, NameValuePair... pairs) { StringOutput sb = new StringOutput(128); sb.append("o_ffXHRNFEvent('") .append(form.getFormName()).append("','") .append(form.getDispatchFieldId()).append("','") .append(id).append("','") .append(form.getEventFieldId()).append("','") .append(FormEvent.ON_DOTDOTDOT[actionIndex]) .append("'"); if(pairs != null && pairs.length > 0) { for(NameValuePair pair:pairs) { sb.append(",'").append(pair.getName()).append("','").append(pair.getValue()).append("'"); } } sb.append(")"); IOUtils.closeQuietly(sb); return sb.toString(); } public static String generateXHRFnCallVariables(Form form, String id, int actionIndex) { StringBuilder sb = new StringBuilder(128); sb.append("var formNam = '").append(form.getFormName()).append("';\n") .append("var dispIdField = '").append(form.getDispatchFieldId()).append("';\n") .append("var dispId = '").append(id).append("';\n") .append("var eventIdField = '").append(form.getEventFieldId()).append("';\n") .append("var eventInt = ").append(FormEvent.ON_DOTDOTDOT[actionIndex]).append(";\n"); return sb.toString(); } public static String getXHRSubmit(Form form, NameValuePair... pairs) { StringOutput sb = new StringOutput(128); sb.append("o_ffXHRNFEvent('") .append(form.getFormName()).append("'"); if(pairs != null && pairs.length > 0) { for(NameValuePair pair:pairs) { sb.append(",'").append(pair.getName()).append("','").append(pair.getValue()).append("'"); } } sb.append(")"); IOUtils.closeQuietly(sb); return sb.toString(); } /** * * @param sb * @param jsonRenderInstruction * @param acceptedInstructions */ public static void appendRenderInstructions(StringOutput sb, String jsonRenderInstruction, Set<String> acceptedInstructions) { JSONObject instr; try { instr = new JSONObject(jsonRenderInstruction); sb.append(" ");// ensure blank before appending instructions -> ' // '... for (Iterator<String> iter = acceptedInstructions.iterator(); iter .hasNext();) { String accepted = iter.next(); if (instr.get(accepted) != null) { // generates i.e. 'class=\"thevalueclass\" ' sb.append(accepted);// accepted key is also use as attribute sb.append("=\""); sb.append(instr.getString(accepted)); sb.append("\" "); } } } catch (JSONException e) { throw new OLATRuntimeException( "error retrieving JSON style render instruction", e); } } /** * @param rootForm * @param id * @return */ public static String getJSSubmitRegisterFn(Form form, String id) { String content = "o_ffRegisterSubmit('"; content += form.getDispatchFieldId() + "','"; content += id + "');"; return content; } public static String getJSStartWithVarDeclaration(String id){ StringBuilder sb = new StringBuilder(150); sb.append(" <script type=\"text/javascript\">\n /* <![CDATA[ */ \n"); // Execute code within an anonymous function (closure) to not leak // variables to global scope (OLAT-5755) sb.append("(function() {"); sb.append("var ").append(id).append(" = jQuery('#").append(id).append("'); "); return sb.toString(); } public static String getJSStart(){ // Execute code within an anonymous function (closure) to not leak // variables to global scope (OLAT-5755) return " <script type=\"text/javascript\">\n /* <![CDATA[ */ \n (function() {"; } public static String getJSEnd(){ // Execute anonymous function (closure) now (OLAT-5755) return "})();\n /* ]]> */ \n</script>"; } // Execute code within an anonymous function (closure) to not leak // variables to global scope (OLAT-5755) public static StringOutput appendFlexiFormDirty(StringOutput sb, Form form, String id) { return appendFlexiFormDirtyOn(sb, form, "change keypress", id); } public static StringOutput appendFlexiFormDirtyForCheckbox(StringOutput sb, Form form, String formDispatchId) { return appendFlexiFormDirtyOn(sb, form, "change mouseup", formDispatchId); } public static StringOutput appendFlexiFormDirtyForClick(StringOutput sb, Form form, String formDispatchId) { return appendFlexiFormDirtyOn(sb, form, "click", formDispatchId); } /** * * @param sb The output * @param form The form containing the button to be dirty * @param events A list of space separated javascript events * @param formDispatchId * @return */ public static StringOutput appendFlexiFormDirtyOn(StringOutput sb, Form form, String events, String formDispatchId) { sb.append(" <script type=\"text/javascript\">\n /* <![CDATA[ */ \n") .append("(function() { jQuery('#").append(formDispatchId).append("').on('").append(events).append("', {formId:\"").append(form.getDispatchFieldId()).append("\", hideMessage:").append(form.isHideDirtyMarkingMessage()).append("}, setFlexiFormDirtyByListener);") .append("})();\n /* ]]> */ \n</script>"); return sb; } /** * This is an hack because it use a timeout of 500ms to be executed after * o_afterserver() method * * @param sb * @param form * @return */ public static StringOutput setFlexiFormDirtyOnLoad(StringOutput sb, Form form) { sb.append("<script type=\"text/javascript\">\n /* <![CDATA[ */ \n") .append(" setTimeout(function(){ setFlexiFormDirty(\"").append(form.getDispatchFieldId()).append("\",").append(form.isHideDirtyMarkingMessage()).append(");}, 500);") .append("\n/* ]]> */ \n</script>"); return sb; } public static String getSetFlexiFormDirtyFnCallOnly(Form form){ if(form.isDirtyMarking()){ return "setFlexiFormDirty('"+form.getDispatchFieldId()+"');"; }else{ return " "; } } /** * creates the JS fragment needed for the {@link #getInlineEditOkCancelHTML(StringOutput, String, String, String)} HTML fragment. * @param sb where to append * @param id formItemId of the InlineEdit FormItem * @param oldHtmlValue escaped HTML value TODO:2009-09-26:pb: escaped values appear as ' and are not escaped back. * @param rootForm to extract the ID of the Form where to submit to. */ public static void getInlineEditOkCancelJS(StringOutput sb, String id, String oldHtmlValue, Form rootForm) { /* * yesFn emulates a click on the input field, which in turn "submits" to the inlineElement to extract the value */ sb.append("var ").append(id).append("=jQuery('#").append(id).append("');") .append(id).append(".focus(1);")//defer focus .append("var o_ff_inline_yesFn = function(e){") .append(FormJSHelper.getJSFnCallFor(rootForm, id, FormEvent.ONCLICK)).append(";};") .append("jQuery('#").append(id).append("').on('blur',o_ff_inline_yesFn);"); /* * noFn replaces the old value in the input field, and then "submits" to the inlineElement via yesFn */ sb.append("var o_ff_inline_noFn = function(e){ jQuery('#").append(id).append("').val('").append(oldHtmlValue).append("'); o_ff_inline_yesFn(e); };") .append("jQuery('#").append(id).append("').keydown(function(e) {") .append(" if(e.which == 27) {") .append(" o_ff_inline_noFn();") .append(" } else if(e.which == 10 || e.which == 13) {") .append(" o_ff_inline_yesFn();") .append(" }") .append("});"); } /** * submits a form when the enter key is pressed. * TextAreas are handled special and do not propagate the enter event to the outer world * @param formName * @return */ public static String submitOnKeypressEnter(String formName) { StringBuilder sb = new StringBuilder(); sb.append(getJSStart()) .append("jQuery('#").append(formName).append("').keypress(function(event) {\n") .append(" if (13 == event.keyCode) {\n") .append(" event.preventDefault();\n") .append(" if (this.onsubmit()) { this.submit(); }\n") .append(" }\n") .append("});\n") .append(getJSEnd()); return sb.toString(); } }