Skip to content

Commit 54e428c

Browse files
committed
Add JSDoc
1 parent 475c16d commit 54e428c

File tree

16 files changed

+3279
-4
lines changed

16 files changed

+3279
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Instead of the last step, you can also use `showRandomTip` to force it to evalua
5151

5252
Tips are specified as a list (array) of objects. Each object represents on tip and lists the constraints under which the tip may be shown or may not be shown. These are evaluated of this module, but only _after_ the global constraint(s), i.e. `showRandomTipIfWanted` (see [above](#using)), passed.
5353

54-
There are many constraints you can configure/set, so it would not be useful to list them here, but [you can look them up in the JSDOC](). Also, you can see the same [in the example source code](examples/Tips.js) of a tips specification. You can copy this to your `../data` dir (relative to this repo), but as the object is loaded via the `init` method, is it not required to be at any specific place.
54+
There are many constraints you can configure/set, so it would not be useful to list them here, but [you can look them up in the JSDOC](https://tinywebex.github.io/RandomTips/global.html#TipObject). Also, you can see the same [in the example source code](examples/Tips.js) of a tips specification. You can copy this to your `../data` dir (relative to this repo), but as the object is loaded via the `init` method, is it not required to be at any specific place.
5555

5656
### Context
5757

docs/RandomTips.js.html

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
5+
<meta charset="utf-8">
6+
<title>RandomTips.js - Documentation</title>
7+
8+
9+
<script src="scripts/prettify/prettify.js"></script>
10+
<script src="scripts/prettify/lang-css.js"></script>
11+
<!--[if lt IE 9]>
12+
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13+
<![endif]-->
14+
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
15+
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
17+
</head>
18+
<body>
19+
20+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
21+
<label for="nav-trigger" class="navicon-button x">
22+
<div class="navicon"></div>
23+
</label>
24+
25+
<label for="nav-trigger" class="overlay"></label>
26+
27+
<nav >
28+
29+
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-RandomTips.html">RandomTips</a><ul class='methods'><li data-type='method'><a href="module-RandomTips.html#.init">init</a></li><li data-type='method'><a href="module-RandomTips.html#.setContext">setContext</a></li><li data-type='method'><a href="module-RandomTips.html#.showRandomTip">showRandomTip</a></li><li data-type='method'><a href="module-RandomTips.html#.showRandomTipIfWanted">showRandomTipIfWanted</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html#tips">tips</a></li></ul>
30+
</nav>
31+
32+
<div id="main">
33+
34+
<h1 class="page-title">RandomTips.js</h1>
35+
36+
37+
38+
39+
40+
41+
42+
<section>
43+
<article>
44+
<pre class="prettyprint source linenums"><code>/**
45+
* Shows random tips to the user, if wanted.
46+
*
47+
* @module RandomTips
48+
* @requires ../lib/lodash/debounce
49+
* @requires ../AddonSettings
50+
* @requires ../MessageHandler/CustomMessages
51+
*/
52+
53+
// lodash
54+
import debounce from "../lodash/debounce.js";
55+
56+
import * as AddonSettings from "../AddonSettings/AddonSettings.js";
57+
import * as CustomMessages from "../MessageHandler/CustomMessages.js";
58+
59+
const TIP_MESSAGE_BOX_ID = "messageTips";
60+
const TIP_SETTING_STORAGE_ID = "randomTips";
61+
const GLOBAL_RANDOMIZE = 0.2; // (%)
62+
const DEBOUNCE_SAVING = 1000; // ms
63+
const MESSAGE_TIP_ID = "messageTip";
64+
65+
/** @see {@link config/tips.js} **/
66+
let tips;
67+
68+
let tipConfig = {
69+
tips: {}
70+
};
71+
72+
let tipShown = null;
73+
let context = null;
74+
75+
/**
76+
* Save the current config.
77+
*
78+
* @function
79+
* @name saveConfig
80+
* @private
81+
* @returns {void}
82+
*/
83+
let saveConfig = null; // will be assigned in init()
84+
85+
/**
86+
* Hook for the dismiss event.
87+
*
88+
* @function
89+
* @private
90+
* @param {Object} param
91+
* @returns {void}
92+
*/
93+
function messageDismissed(param) {
94+
const elMessage = param.elMessage;
95+
96+
const id = elMessage.dataset.tipId;
97+
if (tipShown.id !== id) {
98+
throw new Error("cached tip and dismissed tip differ");
99+
}
100+
101+
// update config
102+
tipConfig.tips[id].dismissedCount = (tipConfig.tips[id].dismissedCount || 0) + 1;
103+
saveConfig();
104+
105+
// cleanup values
106+
tipShown = null;
107+
delete elMessage.dataset.tipId;
108+
109+
console.info(`Tip ${id} has been dismissed.`);
110+
}
111+
112+
/**
113+
* Returns true or false at random. The passed procentage indicates how
114+
* much of the calls should return "true" on average.
115+
*
116+
* @function
117+
* @private
118+
* @param {number} percentage
119+
* @returns {bool}
120+
*/
121+
function randomizePassed(percentage) {
122+
return (Math.random() &lt; percentage);
123+
}
124+
125+
/**
126+
* Shows this tip.
127+
*
128+
* @function
129+
* @private
130+
* @param {Object} tipSpec
131+
* @returns {void}
132+
*/
133+
function showTip(tipSpec) {
134+
// default settings
135+
const allowDismiss = tipSpec.allowDismiss !== undefined ? tipSpec.allowDismiss : true;
136+
137+
const elMessage = CustomMessages.getHtmlElement(MESSAGE_TIP_ID);
138+
elMessage.dataset.tipId = tipSpec.id;
139+
CustomMessages.showMessage(MESSAGE_TIP_ID, tipSpec.text, allowDismiss, tipSpec.actionButton);
140+
141+
// update config
142+
tipConfig.tips[tipSpec.id].shownCount = (tipConfig.tips[tipSpec.id].shownCount || 0) + 1;
143+
tipConfig.tips[tipSpec.id].shownContext[context] = (tipConfig.tips[tipSpec.id].shownContext[context] || 0) + 1;
144+
saveConfig();
145+
146+
tipShown = tipSpec;
147+
}
148+
149+
/**
150+
* Returns whether the tip has already be shown enough times or may not
151+
* be shown, because of some other requirement.
152+
*
153+
* @function
154+
* @private
155+
* @param {Object} tipSpec
156+
* @returns {bool}
157+
*/
158+
function shouldBeShown(tipSpec) {
159+
// default settings
160+
const requiredTriggers = tipSpec.requiredTriggers !== undefined ? tipSpec.requiredTriggers : 10;
161+
162+
// create option if needed
163+
if (tipConfig.tips[tipSpec.id] === undefined) {
164+
tipConfig.tips[tipSpec.id] = {};
165+
tipConfig.tips[tipSpec.id].shownContext = {};
166+
saveConfig();
167+
}
168+
169+
// require some global triggers, if needed
170+
if (tipConfig.triggeredOpen &lt; requiredTriggers) {
171+
return false;
172+
}
173+
// require some additional randomness if needed
174+
if (tipSpec.randomizeDisplay) {
175+
// default value for tip is 50%
176+
const randomizeDisplay = tipSpec.randomizeDisplay !== true ? tipSpec.randomizeDisplay : 0.5;
177+
178+
// 1 : x -> if one number is not selected, do not display result
179+
if (!randomizePassed(randomizeDisplay)) {
180+
return false;
181+
}
182+
}
183+
184+
const tipShowCount = tipConfig.tips[tipSpec.id].shownCount || 0;
185+
const tipDismissed = tipConfig.tips[tipSpec.id].dismissedCount || 0;
186+
187+
// do not show if it has been dismissed enough times
188+
if (tipSpec.maximumDismiss &amp;&amp; tipDismissed >= tipSpec.maximumDismiss) {
189+
return false;
190+
}
191+
192+
// block when it is shown too much times in a given context
193+
if (tipSpec.maximumInContest) {
194+
if (context in tipSpec.maximumInContest) {
195+
const tipShownInCurrentContext = tipConfig.tips[tipSpec.id].shownContext[context] || 0;
196+
197+
if (tipShownInCurrentContext >= tipSpec.maximumInContest[context]) {
198+
return false;
199+
}
200+
}
201+
}
202+
203+
// NOTE: do not return true above this line (for obvious reasons)
204+
// or has it been shown enough times already?
205+
206+
// dismiss is shown enough times?
207+
let requiredDismissCount;
208+
if (Number.isFinite(tipSpec.requireDismiss)) {
209+
requiredDismissCount = tipSpec.requireDismiss;
210+
} else if (tipSpec.requireDismiss === true) { // bool
211+
requiredDismissCount = tipSpec.requiredShowCount;
212+
} else {
213+
// ignore dismiss count
214+
requiredDismissCount = null;
215+
}
216+
217+
// check context check if needed
218+
if (tipSpec.showInContext) {
219+
if (context in tipSpec.showInContext) {
220+
const tipShownInCurrentContext = tipConfig.tips[tipSpec.id].shownContext[context] || 0;
221+
222+
if (tipShownInCurrentContext &lt; tipSpec.showInContext[context]) {
223+
return true;
224+
}
225+
}
226+
}
227+
228+
return (tipSpec.requiredShowCount === null || tipShowCount &lt; tipSpec.requiredShowCount) // not already shown enough times already?
229+
|| (requiredDismissCount !== null &amp;&amp; tipDismissed &lt; requiredDismissCount); // not dismissed enough times?
230+
}
231+
232+
/**
233+
* Sets the context for the current session.
234+
*
235+
* @function
236+
* @param {string} newContext
237+
* @returns {void}
238+
*/
239+
export function setContext(newContext) {
240+
context = newContext;
241+
}
242+
243+
/**
244+
* Selects and shows a random tip.
245+
*
246+
* @function
247+
* @returns {void}
248+
*/
249+
export function showRandomTip() {
250+
// only try to select tip, if one is even available
251+
if (tips.length === 0) {
252+
console.info("no tips to show available anymore");
253+
return;
254+
}
255+
256+
// randomly select element
257+
const randomNumber = Math.floor(Math.random() * tips.length);
258+
const tipSpec = tips[randomNumber];
259+
260+
if (!shouldBeShown(tipSpec)) {
261+
// remove tip
262+
tips.splice(randomNumber, 1);
263+
264+
// retry random selection
265+
showRandomTip();
266+
return;
267+
}
268+
269+
console.info("selected tip to be shown:", randomNumber, tipSpec);
270+
271+
showTip(tipSpec);
272+
}
273+
274+
/**
275+
* Shows the random tip only randomly so the user is not annoyed.
276+
*
277+
* @function
278+
* @returns {void}
279+
*/
280+
export function showRandomTipIfWanted() {
281+
tipConfig.triggeredOpen = (tipConfig.triggeredOpen || 0) + 1;
282+
saveConfig();
283+
284+
// randomize tip showing in general
285+
if (!randomizePassed(GLOBAL_RANDOMIZE)) {
286+
console.info("show no random tip, because randomize did not pass");
287+
return;
288+
}
289+
290+
showRandomTip();
291+
}
292+
293+
/**
294+
* Initialises the module.
295+
*
296+
* @function
297+
* @param {TipObject[]} tipsToShow the tips object to init
298+
* @returns {Promise.&lt;void>}
299+
*/
300+
export function init(tipsToShow) {
301+
// use local shallow copy, so we can modify it
302+
// inner objects won't be modified, so we do not need to deep-clone it.
303+
tips = tipsToShow.slice();
304+
305+
// load function
306+
// We need to assign it here to make it testable.
307+
saveConfig = debounce(() => {
308+
AddonSettings.set(TIP_SETTING_STORAGE_ID, tipConfig);
309+
}, DEBOUNCE_SAVING);
310+
311+
// register HTMLElement
312+
CustomMessages.registerMessageType(MESSAGE_TIP_ID, document.getElementById(TIP_MESSAGE_BOX_ID));
313+
CustomMessages.setHook(MESSAGE_TIP_ID, "dismissStart", messageDismissed);
314+
315+
return AddonSettings.get(TIP_SETTING_STORAGE_ID).then((randomTips) => {
316+
tipConfig = randomTips;
317+
});
318+
}
319+
</code></pre>
320+
</article>
321+
</section>
322+
323+
324+
325+
326+
327+
328+
</div>
329+
330+
<br class="clear">
331+
332+
<footer>
333+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Sun Jan 20 2019 11:45:32 GMT+0100 (Mitteleuropäische Normalzeit) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
334+
</footer>
335+
336+
<script>prettyPrint();</script>
337+
<script src="scripts/linenumber.js"></script>
338+
339+
340+
</body>
341+
</html>

0 commit comments

Comments
 (0)