1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 
 25 /**
 26  * Creates a composite that is populated from a message pattern.
 27  * @constructor
 28  * @class
 29  * This class allows you to create a composite that is populated from
 30  * a message pattern and inserts controls at the appropriate places.
 31  * For example, say that the message <code>MyMsg.repeatTimes</code> is
 32  * defined as the following:
 33  * <pre>
 34  * MyMsg.repeatTimes = "Repeat: {0} times";
 35  * </pre>
 36  * and you want to replace "{0}" with an input field or perhaps a
 37  * drop-down menu that enumerates a specific list of choices as part of
 38  * the application. To do this, you just create a
 39  * {@link DwtMessageComposite} and set the message format, like so:
 40  * <pre>
 41  * var comp = new DwtMessageComposite(parent);
 42  * comp.setFormat(MyMsg.repeatTimes);
 43  * </pre>
 44  * <p>
 45  * The message composite instantiates an {@link AjxMessageFormat}
 46  * from the specified message pattern. Then, for each segment it creates
 47  * static text or a {@link DwtInputField} for replacement segments
 48  * such as "{0}".
 49  * <p>
 50  * To have more control over the controls that are created and inserted
 51  * into the resulting composite, you can pass a callback object to the
 52  * method. Each time that a replacement segment is found in the
 53  * message pattern, the callback is called with the following parameters:
 54  * <ul>
 55  * <li>a reference to this message composite object;
 56  * <li>a reference to the segment object.
 57  * <li>the index at which the segment was found in the message pattern; and
 58  * </ul>
 59  * The segment object will be an instance of
 60  * <code>AjxMessageFormat.MessageSegment</code> and has the following
 61  * methods of interest:
 62  * <ul>
 63  * <li>toSubPattern
 64  * <li>getIndex
 65  * <li>getType
 66  * <li>getStyle
 67  * <li>getSegmentFormat
 68  * </ul>
 69  * <p>
 70  * The callback can use this information to determine whether or not
 71  * a custom control should be created for the segment. If the callback
 72  * returns <code>null</code>, a standard {@link DwtInputField} is
 73  * created and inserted. Note: if the callback returns a custom control,
 74  * it <em>must</em> be an instance of {@link AjxControl}.
 75  * <p>
 76  * Here is an example of a message composite created with a callback
 77  * that generates a custom control for each replacement segment:
 78  * <pre>
 79  * function createCustomControl(parent, segment, i) {
 80  *     return new DwtInputField(parent);
 81  * }
 82  *
 83  * var compParent = ...;
 84  * var comp = new DwtMessageComposite(compParent);
 85  *
 86  * var message = MyMsg.repeatTimes;
 87  * var callback = new AjxCallback(null, createCustomControl);
 88  * comp.setFormat(message, callback);
 89  * </pre>
 90  *
 91  * @author Andy Clark
 92  *
 93  * @param {Object}		params		hash of params:
 94  * @param {DwtComposite}	parent    the parent widget.
 95  * @param {string}	className 	the CSS class
 96  * @param {constant}	posStyle  		the position style (see {@link DwtControl})
 97  * @param {DwtComposite}	parent    the parent widget.
 98  * @param {string}	format   the message that defines the text and controls within this composite control
 99  * @param {AjxCallback}	[controlCallback]   the callback to create UI components (only used with format specified)
100  * @param {AjxCallback}	[hintsCallback]   the callback to provide display hints for the container element of the UI component (only used with format specified)
101  * 
102  * @extends		DwtComposite
103  */
104 DwtMessageComposite = function(params) {
105 	if (arguments.length == 0) return;
106 
107 	params = Dwt.getParams(arguments, DwtMessageComposite.PARAMS);
108 
109 	if (!params.className) {
110 		params.className = "DwtMessageComposite";
111 	}
112 
113 	DwtComposite.call(this, params);
114 
115 	this._tabGroup = new DwtTabGroup("DwtMessageComposite");
116 
117 	if (params.format) {
118 		this.setFormat(params.format,
119 		               params.controlCallback,
120 		               params.hintsCallback);
121 	}
122 }
123 
124 DwtMessageComposite.PARAMS = ['parent', 'className', 'posStyle'];
125 
126 DwtMessageComposite.prototype = new DwtComposite;
127 DwtMessageComposite.prototype.constructor = DwtMessageComposite;
128 DwtMessageComposite.prototype.isDwtMessageComposite = true;
129 
130 DwtMessageComposite.prototype.toString =
131 function() {
132 	return "DwtMessageComposite";
133 }
134 
135 // Public methods
136 
137 /**
138  * Sets the format.
139  * 
140  * @param {string}	message   the message that defines the text and controls that comprise this composite
141  * @param {AjxCallback}	[callback]   the callback to create UI components
142  * @param {AjxCallback}	[hintsCallback]   the callback to provide display hints for the container element of the UI component
143  */
144 DwtMessageComposite.prototype.setFormat =
145 function(message, callback, hintsCallback) {
146     // create formatter
147     this._formatter = new AjxMessageFormat(message);
148     this._controls = {};
149 
150     // create HTML
151     var id = this._htmlElId;
152     this.getHtmlElement().innerHTML = "<table class='DwtCompositeTable' border='0' cellspacing='0' cellpadding='0'><tr valign='center'></tr></table>";
153     var row = this.getHtmlElement().firstChild.rows[0];
154 
155     var segments = this._formatter.getSegments();
156     for (var i = 0; i < segments.length; i++) {
157         var segment = segments[i];
158         var isMsgSegment = segment instanceof AjxMessageFormat.MessageSegment;
159 
160         var cid = [id,i].join("_");
161         var cell = document.createElement('TD');
162 
163         cell.id = cid;
164         cell.className = 'DwtCompositeCell';
165         row.appendChild(cell);
166 
167         if (isMsgSegment) {
168             cell.className += ' MessageControl' + segment.getIndex();
169             var control = callback ? callback.run(this, segment, i) : null;
170             if (!control) {
171                 control = new DwtInputField({parent:this, parentElement: cell});
172             } else {
173                 control.reparentHtmlElement(cell);
174             }
175             this._tabGroup.addMember(control.getTabGroupMember());
176             if (hintsCallback) {
177                 var hints = hintsCallback.run(this, segment, i);
178 
179                 AjxUtil.hashUpdate(control.getHtmlElement(), hints, true);
180             }
181 
182             var sindex = segment.getIndex();
183             this._controls[sindex] = this._controls[sindex] || control;
184         }
185         else {
186             control = new DwtText({parent:this, parentElement: cell});
187             control.setText(segment.toSubPattern());
188             this._tabGroup.addMember(control);
189         }
190     }
191 };
192 
193 /**
194  * Gets the format.
195  * 
196  * @return	{string}	the format
197  */
198 DwtMessageComposite.prototype.format = function() {
199     var args = [];
200     for (var sindex in this._controls) {
201         args[sindex] = this._controls[sindex].getValue();
202     }
203     return this._formatter.format(args);
204 };
205 
206 DwtMessageComposite.prototype.getTabGroupMember = function() {
207 	return this._tabGroup;
208 };
209