001/**
002 * BBjJavaScriptExecutor.bbj
003 * @author ndecker
004 *
005 * The <code>BBjJavaScriptExecutor</code> is a BBj Custom Class that manages JavaScript code execution in a BBjHtmlView
006 * control, providing the ability to queue and debounce JavaScript execution. It is meant to be used with a BBj plug-in
007 * that extends the BBjWidget plug-in class. It is based off of the BBjGridExWidget's GxExecutor class by Hyyan Abo Fakher.
008 *
009 * For the full copyright and license information, please view the LICENSE file distributed with this source code.
010 *
011 * @since BBj 21.00
012*/
013package BBjCalendarWidget;
014import org.apache.commons.collections4.map.ListOrderedMap;
015/**
016 * The <code>BBjJavaScriptExecutor</code> is a BBj Custom Class that manages JavaScript code execution in a BBjHtmlView control, providing the ability to queue and debounce JavaScript execution.
017 * <p>
018 * <h3>Definition of terms:</h3>
019 * <p>
020 * - <b>Debouncing</b>: instead of immediately firing off several events in quick succession, if you choose to debounce the scripts then they will be batched together to improve performance.
021 * The batch size depends on how many scripts are enqueued, the amount of time that passes between their enqueing, and the defined Rate. Do NOT debounce scripts from which you are expecting
022 * a return value, as the return value will be associated with the entire batch, not an individual line of JavaScript. If you do need a valid return value, use the <code>executeImmediately()</code> methods.
023 * <p>
024 * - <b>Rate</b>: the number of milliseconds that must expire without enqueing any more scripts before the executor flushes the enqueued scripts on the client. The default Rate is 200ms,
025 * although you can override this by using the constructor that specifies a Rate time.
026 * <p>
027 * - <b>Execute Key</b>: The script's unique id. If you attempt to queue two scripts with the same key then the second script will overwrite the first and the first script will not be executed.
028 * You do not have to specify a key value with several of the execute() methods. The main point of the key is that if you think that you may be executing the exact same script several times,
029 * then reusing the same key is a way to have only the last instance of the script be executed. For example, if you execute a redraw() JavaScript function whenever the user resizes a window,
030 * then using the script as the key ensures that it won't get called dozens of times sequentially as the user is resizing the window.
031 * <p>
032 * - <b>Execute Priority</b>: The script's priority in the queue, starting from 0 and incrementing by ones. If you specify a priority, it will be used as the script's index in the queue (a ListOrderedMap).
033 * This MUST start at 0 and increment, or you will get an indexing error on the queue. If you use an execute() method that does not specify a priority, then the scripts will be added sequentually
034 * to the queue and executed in the order in which they were added.
035 * <p>
036 * Although this class offers several execute() methods for the most flexibility, the most common usage will be the <code>execute(BBjString script!, BBjNumber debounced!)</code> signature, as
037 * it reuses the provided script for the key, you can control debouncing, and sequential execution is desired (without having to keep track of priority levels).
038 * <p>
039 * If scripts are queued for execution, they will eventually be executed in the BBjHtmlView control either when the widget code explicitly calls the executor's flush() method,
040 * or after the Rate timer has expired, which is 200 milliseconds by default. The executor will continue to flush the queue whenever it is needed.
041 * <p>
042 * Note that every time you queue a new script it will reset the Rate timer. That means that if you are queueing up scripts for execution in quick succession, then
043 * they will all be executed in a batch after the last script is queued. If you are queueing up scripts occasionally, meaning longer than the Rate timer, then
044 * each script will end up being executed before the next one has been queued.
045 * <p>
046 * <b>Note</b>: The executor handles JSNI automatically so you don't have to check for BUI and adjust the window and document JavaScript objects.
047 * <p>
048 * <b>Example use cases:</b>
049 * <blockquote><pre><code><div style = 'background:#E1E6EA; border:1px solid #000; border-radius:1em; padding:0 1em;'>
050 *
051 * <i><span style = 'color:#080;'>rem Create a new executor object specifying the rate time of 200ms where #this! is an
052 * rem instance of the custom class (assuming the application is object-oriented)</span></i>
053 * executor! = new BBjJavaScriptExecutor(#this!, 200)
054 * <span></span>
055 * <i><span style = 'color:#080;'>rem Have the executor enqueue then execute the JavaScript stored in the script! variable.
056 * rem The "1" indicates that it should be debounced, or batched.</span></i>
057 * executor!.execute(script!, 1)
058 * <span></span>
059 * <i><span style = 'color:#080;'>rem Have the executor immediately execute the provided JavaScript and return the result
060 * rem in a BBjString or Object</span></i>
061 * viewType! = executor!.executeImmediately("window.calendar.view.type;")
062 * </div></code></pre></blockquote>
063 * Since this class is meant to be used with a BBjWidget, then it will automatically queue up scripts if the BBjWidget's getIsReady() returns false.
064 *
065 * @since BBj 21.00
066*/
067public class BBjJavaScriptExecutor {
068/** The number of milliseconds before the executor tries to flush the enqueued scripts on the client. */
069
070public BBjNumber Rate!;
071/**
072 * A BBjNumber acting as a boolean that determines whether the executor should override script debouncing in BUI. Defaults to "0", meaning scripts will be batched instead of executed immediately.
073 * Setting this to "0" may help to increase the communication performance between BBj and the JavaScript library/object inside the BBjHtmlView control in BUI.
074*/
075public BBjNumber SuppressBuiDebouncing!;
076/**
077 * A BBjNumber acting as a boolean that determines whether the executor should override script debouncing in GUI. Defaults to "0", meaning scripts will be batched instead of executed immediately.
078 * Setting this to "0" will help to increase the communication performance between BBj and the JavaScript library/object inside the BBjHtmlView control in GUI.
079*/
080public BBjNumber SuppressGuiDebouncing!;
081/** The widget instance. */
082
083protected BBjWidget Widget!;
084/** The BBjHtmlView control that will execute the JavaScript. */
085
086public BBjHtmlView HTMLView!;
087/** A boolean that determines whether the plug-in should run in debug mode - when true it will use unminified Javascript and display the debugger in GUI and log messages to the console. */
088
089public BBjNumber Debug!;
090/** A BBjNumber acting as a boolean that indicates whether the executor is currently flushing or not. */
091
092protected BBjNumber IsFlushing!;
093/** A BBjString that contains the Widget's ID which is only used for debugging. If it's advanced BBjWidget built from JavaScript, it will be a UUID. */
094
095protected BBjString Id!;
096/** A map of enqueued scripts to be executed. */
097
098protected ListOrderedMap Queue!;
099/**
100 * A constructor that creates a new BBjJavaScriptExecutor object to be used with a BBj plug-in that extends the BBjWidget plug-in class, specifying the BBjWidget and taking the default flush rate of 200ms.
101 *
102 * @param BBjWidget widget! The BBjWidget instance.
103*/
104public BBjJavaScriptExecutor(BBjWidget widget!) { }
105/**
106 * A constructor that creates a new BBjJavaScriptExecutor object to be used with a BBj plug-in that extends the BBjWidget plug-in class, specifying the BBjWidget and the flush rate in milliseconds.
107 *
108 * @param BBjWidget widget! The BBjWidget instance.
109 * @param BBjNumber rate! The number of milliseconds that should elapse before the executor flushes the JavaScript code on the client.
110*/
111public BBjJavaScriptExecutor(BBjWidget widget!, BBjNumber rate!) { }
112/**
113 * Executes the provided JavaScript code on the client, specifying all available parameters.
114 *
115 * @param BBjString key! The script's unique id. If you attempt to queue two scripts with the same key then the second script will overwrite the first, and the first script will not be executed.
116 * @param BBjString script! The JavaScript code to execute.
117 * @param BBjNumber debounced! A BBjNumber acting as a boolean that determines whether the executor will debounce the script (1) or execute it immediately (0).
118 * @param BBjNumber priority! The script's priority value that serves as its index in the queue. This MUST start at 0 and increment, or you'll get an indexing error on the queue.
119 *
120 * @return Object The result of the JavaScript code execution if the script returns a value.
121*/
122public Object execute(BBjString key!, BBjString script!, BBjNumber debounced!, BBjNumber priority!) { }
123/**
124 * If the script should not be debounced, then this means that the execution might return a value.
125 * In this case we check if the widget is ready and if so, then we execute the script directly on
126 * the BBjHtmlView control and return the result. Otherwise, we continue to the next step.
127 *
128 * An example of calls which not be debounced would be something like:
129 * grid!.getSelectedRow() -> we expect a value, so it should not be debounced.
130 * grid!.clearData() -> it should be executed immediately because other calls might depend on it.
131*/
132/**
133 * This point is reached in two scenarios:
134 *
135 * 1. The script SHOULD be debounced
136 * 2. The script SHOULD NOT be debounced but the widget is not ready yet so debouncing is enforced until the widget is ready
137 *
138 * We check if debouncing is enabled for the current platform (BUI, GUI) then
139 *
140 * Debouncing is ENABLED :
141 * 1. If the widget is READY we execute the script directly
142 * 2. If the widget is NOT READY we add the script to the queue for later execution
143 *
144 * Debouncing is NOT ENABLED:
145 * 1. add the script to the queue for later execution
146 * 2. Iin case the grid is ready start a count down before executing the code
147 * in the BBjHtmlView control. the countdown will be restated if the developer tries
148 * to execute anything new before the counted down reaches 0.
149 *
150*/
151/**
152 * Executes the provided JavaScript code on the client and uses the script itself as a key with the specified priority.
153 *
154 * @param BBjString script! The JavaScript code to execute.
155 * @param BBjNumber debounced! A BBjNumber acting as a boolean that determines whether the executor will debounce the script (1) or execute it immediately (0).
156 * @param BBjNumber priority! The script's priority value that serves as its index in the queue. This MUST start at 0 and increment, or you'll get an indexing error on the queue.
157 *
158 * @return Object The result of the JavaScript code execution if the script returns a value.
159*/
160public Object execute(BBjString script!, BBjNumber debounced!, BBjNumber priority!) { }
161/**
162 * Executes the provided JavaScript code on the client without a priority, which is a more typical use case.
163 *
164 * @param BBjString key! The script's unique id. If you attempt to queue two scripts with the same key then the second script will overwrite the first, and the first script will not be executed.
165 * @param BBjString script! The JavaScript code to execute.
166 * @param BBjNumber debounced! A BBjNumber acting as a boolean that determines whether the executor will debounce the script (1) or execute it immediately (0).
167 *
168 * @return Object The result of the JavaScript code execution if the script returns a value.
169*/
170public Object execute(BBjString key!, BBjString script!, BBjNumber debounced!) { }
171/**
172 * Executes the provided JavaScript code on the client using the script itself as a key and without priority, which is the typical use case.
173 *
174 * @param BBjString script! The JavaScript code to execute.
175 * @param BBjNumber debounced! A BBjNumber acting as a boolean that determines whether the executor will debounce the script (1) or execute it immediately (0).
176 *
177 * @return Object The result of the JavaScript code execution if the script returns a value.
178*/
179public Object execute(BBjString script!, BBjNumber debounced!) { }
180/**
181 * Executes <i>and debounces</i> the provided JavaScript code on the client without a priority.
182 *
183 * @param BBjString key! The script's unique id. If you attempt to queue two scripts with the same key then the second script will overwrite the first, and the first script will not be executed.
184 * @param BBjString script! The JavaScript code to execute.
185 *
186 * @return Object The result of the JavaScript code execution if the script returns a value.
187*/
188public Object execute(BBjString key!, BBjString script!) { }
189/**
190 * Executes <i>and debounces</i> the provided JavaScript code on the client using the script itself as a key and without a priority.
191 *
192 * @param BBjString script! The JavaScript code to execute.
193 *
194 * @return Object The result of the JavaScript code execution if the script returns a value.
195*/
196public Object execute(BBjString script!) { }
197/**
198 * Executes the provided JavaScript directly in the BBjHtmlView control without debouncing so that it can return a valid return object as a generic <code>Object</code>.
199 *
200 * @param BBjString script$ The JavaScript code to execute.
201 *
202 * @return Object The result of the JavaScript code execution if the script returns a value.
203*/
204public Object executeImmediately(BBjString script!) { }
205/**
206 * Flushes the executor's queue by iterating over the enqueued scripts, combining them into a single batch, executing them on the BBjHtmlView control.
207 *
208 * The widget should call this method once when it is ready to begin the execution process. This is normally this done after the 'ON_PAGE_LOADED' event has fired.
209 * If this method isn't explicitly called by the widget, then it will be called automatically after the Rate timer has expired, which is 200 milliseconds
210 * by default. The executor will continue to flush the queue whenever it is needed.
211 *
212 * @return BBjJavaScriptExecutor the executor instance.
213*/
214public BBjJavaScriptExecutor flush() { }
215/**
216 * An internal method serving as a BBjTimerEvent callback which will be invoked when the executor timer's count down reaches 0 to flush the enqueued scripts.
217 *
218 * @param BBjTimerEvent event!
219*/
220public void onFlush(BBjTimerEvent event!) { }
221/**
222 * Returns a boolean that indicates whether the widget is running in BUI or not.
223 *
224 * @return BBjNumber Returns true when the platform is BUI and false if it's GUI.
225*/
226protected boolean isBui() { }
227/**
228 * Enqueues a script for execution at a later time.
229 *
230 * @param BBjString key! The script's unique id. If you attempt to queue two scripts with the same key then the second script will overwrite the first, and the first script will not be executed.
231 * @param BBjString script! The JavaScript code to execute.
232 * @param BBjNumber priority! The script's priority value that serves as its index in the queue.
233 *
234 * @return BBjJavaScriptExecutor The executor instance.
235*/
236protected BBjJavaScriptExecutor enqueue(BBjString key!, BBjString script!, BBjNumber priority!) { }
237/**
238 * Executes the provided JavaScript directly in the BBjHtmlView control without enqueuing, called by the flush method with a flag that indicates if it's executing a queue of scripts or not.
239 *
240 * @param BBjString script! The JavaScript code to execute.
241 * @param BBjString isDebounced! A BBjNumber acting as a boolean that indicates whether the script is coming from the queue or not.
242 *
243 * @return Object The result of the JavaScript code execution if the script returns a value.
244*/
245protected Object executeInHtmlView(BBjString script!, BBjNumber isDebounced!) { }
246/**
247 * Starts the count down timer. When the timer reaches 0 then the queue will be flushed.
248 *
249 * @return BBjJavaScriptExecutor The executor instance so that the method may be chained.
250*/
251protected BBjJavaScriptExecutor startTimer() { }
252/**
253 * Stops the last created timer if it exists, preventing the executor from reaching the point where it needs to flush its content.
254 *
255 * @return BBjJavaScriptExecutor The executor instance so that the method may be chained.
256*/
257protected BBjJavaScriptExecutor endTimer() { }
258/**
259 * Returns a boolean that indicates whether the widget is ready to be interacted with or not (usually after the ON_PAGE_LOADED event).
260 *
261 * @return BBjNumber Returns True when the platform is BUI and false if it's GUI.
262*/
263protected boolean IsReady() { }
264/**
265 * Sets the BBjWidget with which this executor will be used.
266 *
267 * @param BBjJavaScriptExecutor The executor instance.
268*/
269protected void setWidget(BBjWidget widget!) { }
270/**
271 * Returns the widget instance as a BBjWidget.
272 *
273 * @return BBjWidget The widget instance.
274*/
275protected BBjWidget getWidget() { }
276}