sideshow 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.bowerrc +3 -0
- data/.csslintrc +37 -0
- data/.editorconfig +27 -0
- data/.gitattributes +1 -0
- data/.gitignore +23 -0
- data/BUILDING.md +4 -0
- data/CHANGELOG.md +47 -0
- data/Gulpfile.js +404 -0
- data/LICENSE +191 -0
- data/README.md +342 -0
- data/VERSION +1 -0
- data/bower.json +61 -0
- data/distr/dependencies/jazz.min.js +8 -0
- data/distr/dependencies/jquery.min.js +5 -0
- data/distr/dependencies/pagedown.min.js +1 -0
- data/distr/fonts/open-sans-family/opensans-bold.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-bold.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-bold.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-bold.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-bolditalic.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-bolditalic.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-bolditalic.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-bolditalic.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-extrabold.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-extrabold.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-extrabold.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-extrabold.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-extrabolditalic.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-extrabolditalic.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-extrabolditalic.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-extrabolditalic.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-italic.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-italic.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-italic.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-italic.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-light.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-light.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-light.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-light.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-lightitalic.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-lightitalic.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-lightitalic.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-lightitalic.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-regular.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-regular.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-regular.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-regular.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-semibold.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-semibold.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-semibold.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-semibold.woff +0 -0
- data/distr/fonts/open-sans-family/opensans-semibolditalic.eot +0 -0
- data/distr/fonts/open-sans-family/opensans-semibolditalic.svg +1825 -0
- data/distr/fonts/open-sans-family/opensans-semibolditalic.ttf +0 -0
- data/distr/fonts/open-sans-family/opensans-semibolditalic.woff +0 -0
- data/distr/fonts/sideshow-fontface.min.css +1 -0
- data/distr/fonts/sideshow-icons/sideshow-icons.eot +0 -0
- data/distr/fonts/sideshow-icons/sideshow-icons.svg +16 -0
- data/distr/fonts/sideshow-icons/sideshow-icons.ttf +0 -0
- data/distr/fonts/sideshow-icons/sideshow-icons.woff +0 -0
- data/distr/sideshow.js +2510 -0
- data/distr/sideshow.min.js +10 -0
- data/distr/stylesheets/sideshow.min.css +1 -0
- data/docs/api.js +29 -0
- data/docs/assets/css/external-small.png +0 -0
- data/docs/assets/css/logo.png +0 -0
- data/docs/assets/css/main.css +783 -0
- data/docs/assets/favicon.png +0 -0
- data/docs/assets/img/spinner.gif +0 -0
- data/docs/assets/index.html +10 -0
- data/docs/assets/js/api-filter.js +52 -0
- data/docs/assets/js/api-list.js +251 -0
- data/docs/assets/js/api-search.js +98 -0
- data/docs/assets/js/apidocs.js +370 -0
- data/docs/assets/js/yui-prettify.js +17 -0
- data/docs/assets/vendor/prettify/CHANGES.html +130 -0
- data/docs/assets/vendor/prettify/COPYING +202 -0
- data/docs/assets/vendor/prettify/README.html +203 -0
- data/docs/assets/vendor/prettify/prettify-min.css +1 -0
- data/docs/assets/vendor/prettify/prettify-min.js +1 -0
- data/docs/classes/Arrow.html +541 -0
- data/docs/classes/Arrows.html +805 -0
- data/docs/classes/ControlVariables.html +1005 -0
- data/docs/classes/DetailsPanel.html +672 -0
- data/docs/classes/FadableItem.html +613 -0
- data/docs/classes/HidableItem.html +495 -0
- data/docs/classes/Mask.CloseButton.html +706 -0
- data/docs/classes/Mask.CompositeMask.html +721 -0
- data/docs/classes/Mask.CornerPart.html +613 -0
- data/docs/classes/Mask.Part.html +395 -0
- data/docs/classes/Mask.Polling.html +809 -0
- data/docs/classes/Mask.Subject.html +199 -0
- data/docs/classes/Mask.SubjectMask.html +417 -0
- data/docs/classes/Mask.WizardMenu.html +1401 -0
- data/docs/classes/SS.html +267 -0
- data/docs/classes/SSException.html +203 -0
- data/docs/classes/Screen.html +363 -0
- data/docs/classes/StepDescription.html +1025 -0
- data/docs/classes/StepDescriptionNextButton.html +746 -0
- data/docs/classes/VisualItem.html +339 -0
- data/docs/classes/Wizard.html +967 -0
- data/docs/files/c +0 -0
- data/docs/index.html +162 -0
- data/example.html +81 -0
- data/examples/images/clemenza.jpg +0 -0
- data/examples/images/doc_brown.png +0 -0
- data/examples/images/forkme.png +0 -0
- data/examples/images/fortes-logo.png +0 -0
- data/examples/images/sideshow-logo.png +0 -0
- data/examples/images/sideshow-logo.svg +155 -0
- data/examples/scripts/sideshow.config.js +2 -0
- data/examples/scripts/tutorials/introducing_sideshow.js +259 -0
- data/examples/stylesheets/example.min.css +1 -0
- data/examples/stylesheets/styl/example.styl +272 -0
- data/gulp/config.js +0 -0
- data/gulp/extensions/gulp-html-extend.js +151 -0
- data/gulp/tasks/.gitkeep +0 -0
- data/icons/iconfont.zip +0 -0
- data/package.json +56 -0
- data/sideshow.gemspec +20 -0
- data/sideshow.nuspec +72 -0
- data/sideshow.sublime-project +22 -0
- data/src/copyright_info.js +8 -0
- data/src/general/config.js +42 -0
- data/src/general/dictionary.js +42 -0
- data/src/general/exception.js +15 -0
- data/src/general/global_object.js +287 -0
- data/src/general/polling.js +151 -0
- data/src/general/screen.js +39 -0
- data/src/general/utility_functions.js +88 -0
- data/src/general/variables.js +42 -0
- data/src/interface_itens/fadable_item.js +55 -0
- data/src/interface_itens/hidable_item.js +32 -0
- data/src/interface_itens/visual_item.js +42 -0
- data/src/main.js +52 -0
- data/src/mask/composite_mask.js +193 -0
- data/src/mask/composite_mask_corner_part.js +105 -0
- data/src/mask/composite_mask_part.js +51 -0
- data/src/mask/mask.js +6 -0
- data/src/mask/subject_mask.js +34 -0
- data/src/step/arrow.js +88 -0
- data/src/step/arrows.js +155 -0
- data/src/step/step_description.js +165 -0
- data/src/step/step_description_next_button.js +55 -0
- data/src/step/step_details_panel.js +87 -0
- data/src/step/subject.js +100 -0
- data/src/wizard/wizard.js +395 -0
- data/src/wizard/wizard_control_variables.js +95 -0
- data/src/wizard/wizard_menu.js +101 -0
- data/stylesheets/_animations.styl +87 -0
- data/stylesheets/_font-face.styl +135 -0
- data/stylesheets/_icons.styl +27 -0
- data/stylesheets/_layout.styl +362 -0
- data/stylesheets/_mixins.styl +52 -0
- data/stylesheets/_variables.styl +35 -0
- data/stylesheets/sideshow-fontface.styl +4 -0
- data/stylesheets/sideshow.styl +7 -0
- data/yuidoc.json +15 -0
- metadata +246 -0
data/distr/sideshow.js
ADDED
@@ -0,0 +1,2510 @@
|
|
1
|
+
/**
|
2
|
+
@license
|
3
|
+
Sideshow - An incredible Javascript interactive help Library
|
4
|
+
Version: 0.4.1
|
5
|
+
Date: 2014-11-27
|
6
|
+
Author: Alcides Queiroz [alcidesqueiroz(at)gmail(dot)com]
|
7
|
+
Available under Apache License 2.0 (https://raw2.github.com/fortesinformatica/sideshow/master/LICENSE)
|
8
|
+
**/
|
9
|
+
|
10
|
+
;
|
11
|
+
(function (global, $, jazz, markdown) {
|
12
|
+
(function (name, module) {
|
13
|
+
var ss = module();
|
14
|
+
|
15
|
+
if (typeof define === 'function' && define.amd) {
|
16
|
+
define(module);
|
17
|
+
} else {
|
18
|
+
global[name] = ss;
|
19
|
+
}
|
20
|
+
})('sideshow', function () {
|
21
|
+
//jQuery is needed
|
22
|
+
if ($ === undefined) throw new SSException("2", "jQuery is required for Sideshow to work.");
|
23
|
+
|
24
|
+
//Jazz is needed
|
25
|
+
if (jazz === undefined) throw new SSException("3", "Jazz is required for Sideshow to work.");
|
26
|
+
|
27
|
+
//Pagedown (the Markdown parser used by Sideshow) is needed
|
28
|
+
if (markdown === undefined) throw new SSException("4", "Pagedown (the Markdown parser used by Sideshow) is required for Sideshow to work.");
|
29
|
+
var globalObjectName = "Sideshow",
|
30
|
+
|
31
|
+
|
32
|
+
$window,
|
33
|
+
|
34
|
+
$body,
|
35
|
+
|
36
|
+
$document,
|
37
|
+
|
38
|
+
pollingDuration = 150,
|
39
|
+
|
40
|
+
|
41
|
+
longAnimationDuration = 600,
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
/**
|
48
|
+
|
49
|
+
The main class for Sideshow
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
@class SS
|
54
|
+
|
55
|
+
@static
|
56
|
+
|
57
|
+
**/
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
SS = {
|
62
|
+
|
63
|
+
/**
|
64
|
+
|
65
|
+
The current Sideshow version
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
@property VERSION
|
70
|
+
|
71
|
+
@type String
|
72
|
+
|
73
|
+
**/
|
74
|
+
|
75
|
+
get VERSION() {
|
76
|
+
|
77
|
+
return "0.4.1";
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
},
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
controlVariables = [],
|
87
|
+
|
88
|
+
|
89
|
+
flags = {
|
90
|
+
|
91
|
+
lockMaskUpdate: false,
|
92
|
+
|
93
|
+
changingStep: false,
|
94
|
+
|
95
|
+
skippingStep: false,
|
96
|
+
|
97
|
+
running: false
|
98
|
+
|
99
|
+
},
|
100
|
+
|
101
|
+
|
102
|
+
wizards = [],
|
103
|
+
|
104
|
+
|
105
|
+
currentWizard,
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
/**
|
111
|
+
|
112
|
+
Possible statuses for an animation
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
@@enum AnimationStatus
|
117
|
+
|
118
|
+
**/
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
AnimationStatus = jazz.Enum("VISIBLE", "FADING_IN", "FADING_OUT", "NOT_DISPLAYED", "NOT_RENDERED", "TRANSPARENT");
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
/**
|
128
|
+
A custom exception class for Sideshow
|
129
|
+
|
130
|
+
@class SSException
|
131
|
+
@extends Error
|
132
|
+
@param {String} code The error code
|
133
|
+
@param {String} message The error message
|
134
|
+
**/
|
135
|
+
|
136
|
+
function SSException(code, message) {
|
137
|
+
this.name = "SSException";
|
138
|
+
this.message = "[SIDESHOW_E#" + ("00000000" + code).substr(-8) + "] " + message;
|
139
|
+
}
|
140
|
+
|
141
|
+
SSException.prototype = new Error();
|
142
|
+
SSException.prototype.constructor = SSException;
|
143
|
+
|
144
|
+
/**
|
145
|
+
Shows a warning in a pre-defined format
|
146
|
+
|
147
|
+
@@function showWarning
|
148
|
+
@param {String} code The warning code
|
149
|
+
@param {String} message The warning message
|
150
|
+
**/
|
151
|
+
|
152
|
+
function showWarning(code, message) {
|
153
|
+
console.warn("[SIDESHOW_W#" + ("00000000" + code).substr(-8) + "] " + message);
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
Parses a string in the format "#px" in a number
|
158
|
+
|
159
|
+
@@function parsePxValue
|
160
|
+
@param {String} value A value with/without a px unit
|
161
|
+
@return Number The number value without unit
|
162
|
+
**/
|
163
|
+
|
164
|
+
function parsePxValue(value) {
|
165
|
+
if (value.constructor !== String) return value;
|
166
|
+
var br = value === "" ? "0" : value;
|
167
|
+
return +br.replace("px", "");
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
Gets a string from the dictionary in the current language
|
172
|
+
|
173
|
+
@@function getString
|
174
|
+
@param {Object} stringKeyValuePair A string key-value pair in dictionary
|
175
|
+
@return String The string value in the current language
|
176
|
+
**/
|
177
|
+
|
178
|
+
function getString(stringKeyValuePair) {
|
179
|
+
if (!(SS.config.language in stringKeyValuePair)) {
|
180
|
+
showWarning("2001", "String not found for the selected language, getting the first available.");
|
181
|
+
return stringKeyValuePair[Object.keys(stringKeyValuePair)[0]];
|
182
|
+
}
|
183
|
+
|
184
|
+
return stringKeyValuePair[SS.config.language];
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
Registers hotkeys to be used when running Sideshow
|
189
|
+
|
190
|
+
@@function registerInnerHotkeys
|
191
|
+
**/
|
192
|
+
|
193
|
+
function registerInnerHotkeys() {
|
194
|
+
$document.keyup(innerHotkeysListener);
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
Unregisters hotkeys used when running Sideshow
|
199
|
+
|
200
|
+
@@function Unregisters
|
201
|
+
**/
|
202
|
+
|
203
|
+
function unregisterInnerHotkeys() {
|
204
|
+
$document.unbind("keyup", innerHotkeysListener);
|
205
|
+
}
|
206
|
+
|
207
|
+
function innerHotkeysListener(e) {
|
208
|
+
//Esc or F1
|
209
|
+
if (e.keyCode == 27 || e.keyCode == 112) SS.close();
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
Registers global hotkeys
|
214
|
+
|
215
|
+
@@function registerGlobalHotkeys
|
216
|
+
**/
|
217
|
+
|
218
|
+
function registerGlobalHotkeys() {
|
219
|
+
$document.keyup(function (e) {
|
220
|
+
//F2
|
221
|
+
if (e.keyCode == 113) {
|
222
|
+
if (e.shiftKey) SS.start({
|
223
|
+
listAll: true
|
224
|
+
});
|
225
|
+
else SS.start();
|
226
|
+
}
|
227
|
+
});
|
228
|
+
}
|
229
|
+
|
230
|
+
/**
|
231
|
+
Removes nodes created by Sideshow (except mask, which remains due to performance reasons when recalling Sideshow)
|
232
|
+
|
233
|
+
@@function removeDOMGarbage
|
234
|
+
**/
|
235
|
+
|
236
|
+
function removeDOMGarbage() {
|
237
|
+
$("[class*=\"sideshow\"]").not(".sideshow-mask-part, .sideshow-mask-corner-part, .sideshow-subject-mask").remove();
|
238
|
+
}
|
239
|
+
|
240
|
+
/**
|
241
|
+
Strings Dictionary
|
242
|
+
|
243
|
+
@@object strings
|
244
|
+
**/
|
245
|
+
var strings = {
|
246
|
+
availableWizards: {
|
247
|
+
"en": "Available Tutorials",
|
248
|
+
"pt-br": "Tutoriais Disponíveis",
|
249
|
+
"es": "Tutoriales Disponibles"
|
250
|
+
},
|
251
|
+
relatedWizards: {
|
252
|
+
"en": "Related Wizards",
|
253
|
+
"pt-br": "Tutoriais Relacionados",
|
254
|
+
"es": "Tutoriales Relacionados"
|
255
|
+
},
|
256
|
+
noAvailableWizards: {
|
257
|
+
"en": "There's no tutorials available.",
|
258
|
+
"pt-br": "Não há tutoriais disponíveis para esta tela.",
|
259
|
+
"es": "No hay tutoriales disponibles."
|
260
|
+
},
|
261
|
+
close: {
|
262
|
+
"en": "Close",
|
263
|
+
"pt-br": "Fechar",
|
264
|
+
"es": "Cerrar"
|
265
|
+
},
|
266
|
+
estimatedTime: {
|
267
|
+
"en": "Estimated Time",
|
268
|
+
"pt-br": "Tempo Estimado",
|
269
|
+
"es": "Tiempo Estimado"
|
270
|
+
},
|
271
|
+
next: {
|
272
|
+
"en": "Next",
|
273
|
+
"pt-br": "Continuar",
|
274
|
+
"es": "Continuar"
|
275
|
+
},
|
276
|
+
finishWizard: {
|
277
|
+
"en": "Finish Wizard",
|
278
|
+
"pt-br": "Concluir Tutorial",
|
279
|
+
"es": "Concluir Tutorial"
|
280
|
+
}
|
281
|
+
};
|
282
|
+
/**
|
283
|
+
Sideshow Settings
|
284
|
+
|
285
|
+
@@object config
|
286
|
+
**/
|
287
|
+
SS.config = {};
|
288
|
+
|
289
|
+
/**
|
290
|
+
Application route to persists user preferences
|
291
|
+
|
292
|
+
@@field userPreferencesRoute
|
293
|
+
@type String
|
294
|
+
@@unused
|
295
|
+
@@todo Implement persistence logic
|
296
|
+
**/
|
297
|
+
SS.config.userPreferencesRoute = null;
|
298
|
+
|
299
|
+
/**
|
300
|
+
Logged in user
|
301
|
+
|
302
|
+
@@field loggedInUser
|
303
|
+
@type String
|
304
|
+
@@unused
|
305
|
+
**/
|
306
|
+
SS.config.loggedInUser = null;
|
307
|
+
|
308
|
+
/**
|
309
|
+
Chosen language for sideshow interface
|
310
|
+
|
311
|
+
@@field language
|
312
|
+
@type String
|
313
|
+
**/
|
314
|
+
SS.config.language = "en";
|
315
|
+
|
316
|
+
/**
|
317
|
+
Defines if the intro screen (the tutorial list) will be skipped when there's just one
|
318
|
+
tutorial available. This way, when Sideshow is invoked, the first step is directly shown.
|
319
|
+
|
320
|
+
@@field autoSkipIntro
|
321
|
+
@type boolean
|
322
|
+
**/
|
323
|
+
SS.config.autoSkipIntro = false;
|
324
|
+
|
325
|
+
/**
|
326
|
+
Stores the variables used in step evaluators
|
327
|
+
|
328
|
+
@class ControlVariables
|
329
|
+
@static
|
330
|
+
**/
|
331
|
+
SS.ControlVariables = {};
|
332
|
+
|
333
|
+
/**
|
334
|
+
Sets a variable value
|
335
|
+
|
336
|
+
@method set
|
337
|
+
@param {String} name The variable name
|
338
|
+
@param {String} value The variable value
|
339
|
+
@return {String} A formatted key=value pair representing the defined variable
|
340
|
+
**/
|
341
|
+
SS.ControlVariables.set = function (name, value) {
|
342
|
+
var variable = {};
|
343
|
+
if (this.isDefined(name)) {
|
344
|
+
variable = this.getNameValuePair(name);
|
345
|
+
} else controlVariables.push(variable);
|
346
|
+
|
347
|
+
variable.name = name;
|
348
|
+
variable.value = value;
|
349
|
+
return name + "=" + value;
|
350
|
+
};
|
351
|
+
|
352
|
+
/**
|
353
|
+
Sets a variable if not defined yet
|
354
|
+
|
355
|
+
@method setIfUndefined
|
356
|
+
@param {String} name The variable name
|
357
|
+
@param {String} value The variable value
|
358
|
+
@return {String} A formatted key=value pair representing the defined variable
|
359
|
+
**/
|
360
|
+
SS.ControlVariables.setIfUndefined = function (name, value) {
|
361
|
+
if (!this.isDefined(name)) return this.set(name, value);
|
362
|
+
};
|
363
|
+
|
364
|
+
/**
|
365
|
+
Checks if some variable is already defined
|
366
|
+
|
367
|
+
@method isDefined
|
368
|
+
@param {String} name The variable name
|
369
|
+
@return {boolean} A boolean indicating if the variable is already defined
|
370
|
+
**/
|
371
|
+
SS.ControlVariables.isDefined = function (name) {
|
372
|
+
return this.getNameValuePair(name) !== undefined;
|
373
|
+
};
|
374
|
+
|
375
|
+
/**
|
376
|
+
Gets a variable value
|
377
|
+
|
378
|
+
@method get
|
379
|
+
@param {String} name The variable name
|
380
|
+
@return {any} The variable value
|
381
|
+
**/
|
382
|
+
SS.ControlVariables.get = function (name) {
|
383
|
+
var pair = this.getNameValuePair(name);
|
384
|
+
return pair ? pair.value : undefined;
|
385
|
+
};
|
386
|
+
|
387
|
+
/**
|
388
|
+
Gets a pair with name and value
|
389
|
+
|
390
|
+
@method getNameValuePair
|
391
|
+
@param {String} name The variable name
|
392
|
+
@return {Object} A pair with the variable name and value
|
393
|
+
**/
|
394
|
+
SS.ControlVariables.getNameValuePair = function (name) {
|
395
|
+
for (var i = 0; i < controlVariables.length; i++) {
|
396
|
+
var variable = controlVariables[i];
|
397
|
+
if (variable.name === name) return variable;
|
398
|
+
}
|
399
|
+
};
|
400
|
+
|
401
|
+
/**
|
402
|
+
Remove some variable from the control variables collection
|
403
|
+
|
404
|
+
@method remove
|
405
|
+
@param {String} name The variable name
|
406
|
+
@return {Object} A pair with the removed variable name and value
|
407
|
+
**/
|
408
|
+
SS.ControlVariables.remove = function (name) {
|
409
|
+
return controlVariables.splice(controlVariables.indexOf(this.getNameValuePair(name)), 1);
|
410
|
+
};
|
411
|
+
|
412
|
+
/**
|
413
|
+
Clear the control variables collection
|
414
|
+
|
415
|
+
@method clear
|
416
|
+
**/
|
417
|
+
SS.ControlVariables.clear = function () {
|
418
|
+
controlVariables = [];
|
419
|
+
};
|
420
|
+
|
421
|
+
/**
|
422
|
+
A visual item
|
423
|
+
|
424
|
+
@class VisualItem
|
425
|
+
@@abstract
|
426
|
+
**/
|
427
|
+
var VisualItem = jazz.Class().abstract;
|
428
|
+
|
429
|
+
/**
|
430
|
+
The jQuery wrapped DOM element for the visual item
|
431
|
+
|
432
|
+
@@field $el
|
433
|
+
@type Object
|
434
|
+
**/
|
435
|
+
VisualItem.field("$el");
|
436
|
+
|
437
|
+
/**
|
438
|
+
The jQuery wrapped DOM element for the visual item
|
439
|
+
|
440
|
+
@@field $el
|
441
|
+
@type AnimationStatus
|
442
|
+
**/
|
443
|
+
VisualItem.field("status", AnimationStatus.NOT_RENDERED);
|
444
|
+
|
445
|
+
/**
|
446
|
+
Renders the item's DOM object
|
447
|
+
|
448
|
+
@method render
|
449
|
+
**/
|
450
|
+
VisualItem.method("render", function ($parent) {
|
451
|
+
($parent || $body).append(this.$el);
|
452
|
+
this.status = AnimationStatus.NOT_DISPLAYED;
|
453
|
+
});
|
454
|
+
|
455
|
+
/**
|
456
|
+
Destroys the item's DOM object
|
457
|
+
|
458
|
+
@method destroy
|
459
|
+
**/
|
460
|
+
VisualItem.method("destroy", function () {
|
461
|
+
this.$el.remove();
|
462
|
+
});
|
463
|
+
/**
|
464
|
+
A visual item which can be shown and hidden
|
465
|
+
|
466
|
+
@class HidableItem
|
467
|
+
@@abstract
|
468
|
+
@extends VisualItem
|
469
|
+
**/
|
470
|
+
var HidableItem = jazz.Class().extending(VisualItem).abstract;
|
471
|
+
|
472
|
+
/**
|
473
|
+
Shows the visual item
|
474
|
+
|
475
|
+
@method show
|
476
|
+
@param {boolean} displayButKeepTransparent The item will hold space but keep invisible
|
477
|
+
**/
|
478
|
+
HidableItem.method("show", function (displayButKeepTransparent) {
|
479
|
+
if (!this.$el) this.render();
|
480
|
+
if (!displayButKeepTransparent) this.$el.removeClass("sideshow-invisible");
|
481
|
+
this.$el.removeClass("sideshow-hidden");
|
482
|
+
this.status = AnimationStatus.VISIBLE;
|
483
|
+
});
|
484
|
+
|
485
|
+
/**
|
486
|
+
Hides the visual item
|
487
|
+
|
488
|
+
@method hide
|
489
|
+
**/
|
490
|
+
HidableItem.method("hide", function (keepHoldingSpace) {
|
491
|
+
if (!keepHoldingSpace) this.$el.addClass("sideshow-hidden");
|
492
|
+
this.$el.addClass("sideshow-invisible");
|
493
|
+
this.status = AnimationStatus.NOT_DISPLAYED;
|
494
|
+
});
|
495
|
+
/**
|
496
|
+
A visual item which holds fading in and out capabilities
|
497
|
+
|
498
|
+
@class FadableItem
|
499
|
+
@@abstract
|
500
|
+
@extends HidableItem
|
501
|
+
**/
|
502
|
+
var FadableItem = jazz.Class().extending(HidableItem).abstract;
|
503
|
+
|
504
|
+
/**
|
505
|
+
Does a fade in transition for the visual item
|
506
|
+
|
507
|
+
@method fadeIn
|
508
|
+
**/
|
509
|
+
FadableItem.method("fadeIn", function (callback, linearTimingFunction) {
|
510
|
+
var item = this;
|
511
|
+
item.status = AnimationStatus.FADING_IN;
|
512
|
+
|
513
|
+
if (!item.$el) this.render();
|
514
|
+
if (linearTimingFunction) item.$el.css("animation-timing-function", "linear");
|
515
|
+
item.$el.removeClass("sideshow-hidden");
|
516
|
+
|
517
|
+
//Needed hack to get CSS transition to work properly
|
518
|
+
setTimeout(function () {
|
519
|
+
item.$el.removeClass("sideshow-invisible");
|
520
|
+
|
521
|
+
setTimeout(function () {
|
522
|
+
item.status = AnimationStatus.VISIBLE;
|
523
|
+
if (linearTimingFunction) item.$el.css("animation-timing-function", "ease");
|
524
|
+
if (callback) callback();
|
525
|
+
}, longAnimationDuration);
|
526
|
+
}, 20); //<-- Yeap, I'm really scheduling a timeout for 20 milliseconds... this is a dirty trick =)
|
527
|
+
});
|
528
|
+
|
529
|
+
/**
|
530
|
+
Does a fade out transition for the visual item
|
531
|
+
|
532
|
+
@method fadeOut
|
533
|
+
**/
|
534
|
+
FadableItem.method("fadeOut", function (callback, linearTimingFunction) {
|
535
|
+
var item = this;
|
536
|
+
if (item.status != AnimationStatus.NOT_RENDERED) {
|
537
|
+
item.status = AnimationStatus.FADING_OUT;
|
538
|
+
|
539
|
+
if (linearTimingFunction) item.$el.css("animation-timing-function", "linear");
|
540
|
+
item.$el.addClass("sideshow-invisible");
|
541
|
+
|
542
|
+
setTimeout(function () {
|
543
|
+
item.$el.addClass("sideshow-hidden");
|
544
|
+
item.status = AnimationStatus.NOT_DISPLAYED;
|
545
|
+
if (linearTimingFunction) item.$el.css("animation-timing-function", "ease");
|
546
|
+
if (callback) callback();
|
547
|
+
}, longAnimationDuration);
|
548
|
+
}
|
549
|
+
});
|
550
|
+
|
551
|
+
/**
|
552
|
+
Represents a tutorial
|
553
|
+
|
554
|
+
@class Wizard
|
555
|
+
@@initializer
|
556
|
+
@param {Object} wizardConfig The wizard configuration object
|
557
|
+
**/
|
558
|
+
var Wizard = jazz.Class(function (wizardConfig) {
|
559
|
+
this.name = wizardConfig.name;
|
560
|
+
this.title = wizardConfig.title;
|
561
|
+
this.description = wizardConfig.description;
|
562
|
+
this.estimatedTime = wizardConfig.estimatedTime;
|
563
|
+
this.affects = wizardConfig.affects;
|
564
|
+
this.preparation = wizardConfig.preparation;
|
565
|
+
this.listeners = wizardConfig.listeners;
|
566
|
+
this.showStepPosition = wizardConfig.showStepPosition;
|
567
|
+
this.relatedWizards = wizardConfig.relatedWizards;
|
568
|
+
});
|
569
|
+
|
570
|
+
/**
|
571
|
+
A function to prepare the environment for running a wizard (e.g. redirecting to some screen)
|
572
|
+
|
573
|
+
@@field preparation
|
574
|
+
@type Function
|
575
|
+
**/
|
576
|
+
Wizard.field("preparation");
|
577
|
+
|
578
|
+
/**
|
579
|
+
An object with listeners to this wizard (e.g. beforeWizardStarts, afterWizardEnds)
|
580
|
+
|
581
|
+
@@field listeners
|
582
|
+
@type Object
|
583
|
+
**/
|
584
|
+
Wizard.field("listeners");
|
585
|
+
|
586
|
+
/**
|
587
|
+
A configuration flag that defines if the step position (e.g. 2/10, 3/15, 12/12) will be shown
|
588
|
+
|
589
|
+
@@field showStepPosition
|
590
|
+
@type boolean
|
591
|
+
**/
|
592
|
+
Wizard.field("showStepPosition");
|
593
|
+
|
594
|
+
/**
|
595
|
+
An array with related wizards names. These wizards are listed after the ending of the current wizard.
|
596
|
+
|
597
|
+
@@field relatedWizards
|
598
|
+
@type Array
|
599
|
+
**/
|
600
|
+
Wizard.field("relatedWizards");
|
601
|
+
|
602
|
+
/**
|
603
|
+
The wizard unique name (used internally as an identifier)
|
604
|
+
|
605
|
+
@@field name
|
606
|
+
@type String
|
607
|
+
**/
|
608
|
+
Wizard.field("name");
|
609
|
+
|
610
|
+
/**
|
611
|
+
The wizard title (will be shown in the list of available wizards)
|
612
|
+
|
613
|
+
@@field title
|
614
|
+
@type String
|
615
|
+
**/
|
616
|
+
Wizard.field("title");
|
617
|
+
|
618
|
+
/**
|
619
|
+
The wizard description (will be shown in the list of available wizards)
|
620
|
+
|
621
|
+
@@field description
|
622
|
+
@type String
|
623
|
+
**/
|
624
|
+
Wizard.field("description");
|
625
|
+
|
626
|
+
/**
|
627
|
+
The wizard estimated completion time (will be shown in the list of available wizards)
|
628
|
+
|
629
|
+
@@field estimatedTime
|
630
|
+
@type String
|
631
|
+
**/
|
632
|
+
Wizard.field("estimatedTime");
|
633
|
+
|
634
|
+
/**
|
635
|
+
A collection of rules to infer whether a wizard should be available in a specific screen
|
636
|
+
|
637
|
+
@@field affects
|
638
|
+
@type Array
|
639
|
+
**/
|
640
|
+
Wizard.field("affects");
|
641
|
+
|
642
|
+
/**
|
643
|
+
The sequence of steps for this wizard
|
644
|
+
|
645
|
+
@@field storyline
|
646
|
+
@private
|
647
|
+
@type Object
|
648
|
+
**/
|
649
|
+
Wizard.field("_storyline");
|
650
|
+
|
651
|
+
/**
|
652
|
+
Points to the current step object in a playing wizard
|
653
|
+
|
654
|
+
@@field currentStep
|
655
|
+
@type Object
|
656
|
+
**/
|
657
|
+
Wizard.field("currentStep");
|
658
|
+
|
659
|
+
/**
|
660
|
+
Sets the storyline for the wizard
|
661
|
+
|
662
|
+
@method storyLine
|
663
|
+
**/
|
664
|
+
Wizard.method("storyLine", function (storyline) {
|
665
|
+
this._storyline = storyline;
|
666
|
+
});
|
667
|
+
|
668
|
+
/**
|
669
|
+
Runs the wizard
|
670
|
+
|
671
|
+
@method play
|
672
|
+
**/
|
673
|
+
Wizard.method("play", function () {
|
674
|
+
var wiz = this;
|
675
|
+
|
676
|
+
Polling.enqueue("check_composite_mask_subject_changes", function () {
|
677
|
+
Mask.CompositeMask.singleInstance.pollForSubjectChanges();
|
678
|
+
});
|
679
|
+
|
680
|
+
Polling.enqueue("check_arrow_changes", function () {
|
681
|
+
Arrows.pollForArrowsChanges(true);
|
682
|
+
});
|
683
|
+
|
684
|
+
//Checks if the wizard has a storyline
|
685
|
+
if (!this._storyline) throw new SSException("201", "A wizard needs to have a storyline.");
|
686
|
+
var steps = this._storyline.steps;
|
687
|
+
|
688
|
+
//Checks if the storyline has at least one step
|
689
|
+
if (steps.length === 0) throw new SSException("202", "A storyline must have at least one step.");
|
690
|
+
|
691
|
+
DetailsPanel.singleInstance.render();
|
692
|
+
|
693
|
+
StepDescription.singleInstance.render();
|
694
|
+
|
695
|
+
var listeners = this.listeners;
|
696
|
+
if (listeners && listeners.beforeWizardStarts) listeners.beforeWizardStarts();
|
697
|
+
|
698
|
+
flags.changingStep = true;
|
699
|
+
this.showStep(steps[0], function () {
|
700
|
+
//Releases the polling for checking any changes in the current subject
|
701
|
+
//flags.lockMaskUpdate = false;
|
702
|
+
|
703
|
+
//Register the function that checks the completing of a step in the polling queue
|
704
|
+
Polling.enqueue("check_completed_step", function () {
|
705
|
+
wiz.pollForCheckCompletedStep();
|
706
|
+
});
|
707
|
+
});
|
708
|
+
|
709
|
+
Mask.CompositeMask.singleInstance.fadeIn();
|
710
|
+
});
|
711
|
+
|
712
|
+
/**
|
713
|
+
Shows a specific step
|
714
|
+
|
715
|
+
@method showStep
|
716
|
+
@param {Object} step The step to be shown
|
717
|
+
@param {Function} callback A callback function to be called
|
718
|
+
**/
|
719
|
+
Wizard.method("showStep", function (step, callback) {
|
720
|
+
var wizard = this;
|
721
|
+
flags.skippingStep = false;
|
722
|
+
|
723
|
+
Arrows.clear();
|
724
|
+
|
725
|
+
if (this.currentStep && this.currentStep.listeners && this.currentStep.listeners.afterStep) this.currentStep.listeners.afterStep();
|
726
|
+
|
727
|
+
function skipStep(wiz) {
|
728
|
+
flags.skippingStep = true;
|
729
|
+
wizard.next();
|
730
|
+
}
|
731
|
+
|
732
|
+
if (step && step.listeners && step.listeners.beforeStep) step.listeners.beforeStep();
|
733
|
+
|
734
|
+
//The shown step is, of course, the current
|
735
|
+
this.currentStep = step;
|
736
|
+
|
737
|
+
//If the step has a skipIf evaluator and it evaluates to true, we'll skip to the next step!
|
738
|
+
if (step.skipIf && step.skipIf()) skipStep(this);
|
739
|
+
|
740
|
+
if (flags.changingStep && !flags.skippingStep) {
|
741
|
+
//Sets the current subject and updates its dimension and position
|
742
|
+
if (step.subject) SS.setSubject(step.subject);
|
743
|
+
else SS.setEmptySubject();
|
744
|
+
//Updates the mask
|
745
|
+
Mask.CompositeMask.singleInstance.update(Subject.position, Subject.dimension, Subject.borderRadius);
|
746
|
+
|
747
|
+
var sm = Mask.SubjectMask.singleInstance;
|
748
|
+
sm.fadeOut(function () {
|
749
|
+
if (step.lockSubject) sm.show(true);
|
750
|
+
});
|
751
|
+
//The details panel (that wraps the step description and arrow) is shown
|
752
|
+
DetailsPanel.singleInstance.show();
|
753
|
+
//Repositionate the details panel depending on the remaining space in the screen
|
754
|
+
DetailsPanel.singleInstance.positionate();
|
755
|
+
//Sets the description properties (text, title and step position)
|
756
|
+
var description = StepDescription.singleInstance;
|
757
|
+
var text = step.text;
|
758
|
+
text = text instanceof Function ? SS.heredoc(text) : text;
|
759
|
+
if (step.format == "markdown") {
|
760
|
+
description.setHTML(new markdown.Converter().makeHtml(text));
|
761
|
+
} else description.setText(text);
|
762
|
+
|
763
|
+
description.setTitle(step.title);
|
764
|
+
description.setStepPosition((this.getStepPosition() + 1) + "/" + this._storyline.steps.length);
|
765
|
+
//If this step doesn't have its own passing conditions/evaluators, or the flag "showNextButton" is true, then, the button is visible
|
766
|
+
if (step.showNextButton || step.autoContinue === false || !(step.completingConditions && step.completingConditions.length > 0)) {
|
767
|
+
var nextStep = this._storyline.steps[this.getStepPosition() + 1];
|
768
|
+
if (nextStep) {
|
769
|
+
description.nextButton.setText(getString(strings.next) + ": " + this._storyline.steps[this.getStepPosition() + 1].title);
|
770
|
+
} else {
|
771
|
+
description.nextButton.setText(getString(strings.finishWizard));
|
772
|
+
}
|
773
|
+
description.nextButton.show();
|
774
|
+
|
775
|
+
if (step.autoContinue === false) description.nextButton.disable();
|
776
|
+
} else {
|
777
|
+
description.nextButton.hide();
|
778
|
+
}
|
779
|
+
|
780
|
+
if (step.targets && step.targets.length > 0) {
|
781
|
+
Arrows.setTargets(step.targets);
|
782
|
+
Arrows.render();
|
783
|
+
Arrows.positionate();
|
784
|
+
Arrows.fadeIn();
|
785
|
+
}
|
786
|
+
|
787
|
+
//Step Description is shown, but is transparent yet (since we need to know its dimension to positionate it properly)
|
788
|
+
description.show(true);
|
789
|
+
if (!Mask.CompositeMask.singleInstance.scrollIfNecessary(Subject.position, Subject.dimension)) {
|
790
|
+
description.positionate();
|
791
|
+
//Do a simple fade in for the description box
|
792
|
+
description.fadeIn();
|
793
|
+
}
|
794
|
+
|
795
|
+
|
796
|
+
//If a callback is passed, call it
|
797
|
+
if (callback) callback();
|
798
|
+
flags.changingStep = false;
|
799
|
+
}
|
800
|
+
});
|
801
|
+
|
802
|
+
/**
|
803
|
+
Shows the next step of the wizard
|
804
|
+
|
805
|
+
@method next
|
806
|
+
@param {Function} callback A callback function to be called
|
807
|
+
**/
|
808
|
+
Wizard.method("next", function (callback, nextStep) {
|
809
|
+
if (!flags.changingStep || flags.skippingStep) {
|
810
|
+
flags.changingStep = true;
|
811
|
+
var currentStep = this.currentStep;
|
812
|
+
nextStep = nextStep || this._storyline.steps[this.getStepPosition(this.currentStep) + 1];
|
813
|
+
var self = this;
|
814
|
+
|
815
|
+
this.hideStep(function () {
|
816
|
+
if (nextStep) self.showStep(nextStep, function () {
|
817
|
+
if (callback) callback();
|
818
|
+
});
|
819
|
+
else {
|
820
|
+
if (currentStep && currentStep.listeners && currentStep.listeners.afterStep) currentStep.listeners.afterStep();
|
821
|
+
|
822
|
+
var completedWizard = currentWizard;
|
823
|
+
currentWizard = null;
|
824
|
+
var listeners = self.listeners;
|
825
|
+
if (listeners && listeners.afterWizardEnds) listeners.afterWizardEnds();
|
826
|
+
|
827
|
+
if (!SS.showRelatedWizardsList(completedWizard)) SS.close();
|
828
|
+
}
|
829
|
+
});
|
830
|
+
}
|
831
|
+
});
|
832
|
+
|
833
|
+
/**
|
834
|
+
Hides the step
|
835
|
+
|
836
|
+
@method hideStep
|
837
|
+
@param {Function} callback A callback function to be called in the ending of the hiding process
|
838
|
+
**/
|
839
|
+
Wizard.method("hideStep", function (callback) {
|
840
|
+
StepDescription.singleInstance.fadeOut(function () {
|
841
|
+
DetailsPanel.singleInstance.hide();
|
842
|
+
});
|
843
|
+
Arrows.fadeOut();
|
844
|
+
Mask.SubjectMask.singleInstance.update(Subject.position, Subject.dimension, Subject.borderRadius);
|
845
|
+
Mask.SubjectMask.singleInstance.fadeIn(callback);
|
846
|
+
});
|
847
|
+
|
848
|
+
/**
|
849
|
+
Returns the position of the step passed as argument or (by default) the current step
|
850
|
+
|
851
|
+
@method getStepPosition
|
852
|
+
@param {Object} step The step object to get position
|
853
|
+
**/
|
854
|
+
Wizard.method("getStepPosition", function (step) {
|
855
|
+
return this._storyline.steps.indexOf(step || this.currentStep);
|
856
|
+
});
|
857
|
+
|
858
|
+
/**
|
859
|
+
Checks if a wizard should be shown in the current context (running each evaluator defined for this wizard)
|
860
|
+
|
861
|
+
@method isEligible
|
862
|
+
@return {boolean} A boolean indicating if this wizard should be available in the current context
|
863
|
+
**/
|
864
|
+
Wizard.method("isEligible", function () {
|
865
|
+
var l = global.location;
|
866
|
+
|
867
|
+
function isEqual(a, b, caseSensitive) {
|
868
|
+
return (caseSensitive) ? a === b : a.toLowerCase() === b.toLowerCase();
|
869
|
+
}
|
870
|
+
|
871
|
+
for (var c = 0; c < this.affects.length; c++) {
|
872
|
+
var condition = this.affects[c];
|
873
|
+
if (condition instanceof Function) {
|
874
|
+
if (condition()) return true;
|
875
|
+
} else if (condition instanceof Object) {
|
876
|
+
if ("route" in condition) {
|
877
|
+
var route = l.pathname + l.search + l.hash;
|
878
|
+
if (isEqual(route, condition.route, condition.caseSensitive)) return true;
|
879
|
+
}
|
880
|
+
|
881
|
+
if ("hash" in condition) {
|
882
|
+
if (isEqual(location.hash, condition.hash, condition.caseSensitive)) return true;
|
883
|
+
}
|
884
|
+
|
885
|
+
if ("url" in condition) {
|
886
|
+
if (isEqual(location.href, condition.url, condition.caseSensitive)) return true;
|
887
|
+
}
|
888
|
+
}
|
889
|
+
}
|
890
|
+
return false;
|
891
|
+
});
|
892
|
+
|
893
|
+
/**
|
894
|
+
Checks if the current user already watched this wizard
|
895
|
+
|
896
|
+
@method isAlreadyWatched
|
897
|
+
@return {boolean} A boolean indicating if the user watched this wizard
|
898
|
+
@@todo Implement this method...
|
899
|
+
**/
|
900
|
+
Wizard.method("isAlreadyWatched", function () {
|
901
|
+
//ToDo
|
902
|
+
return false;
|
903
|
+
});
|
904
|
+
|
905
|
+
/**
|
906
|
+
A Polling function to check if the current step is completed
|
907
|
+
|
908
|
+
@method pollForCheckCompletedStep
|
909
|
+
**/
|
910
|
+
Wizard.method("pollForCheckCompletedStep", function () {
|
911
|
+
var conditions = this.currentStep.completingConditions;
|
912
|
+
if (conditions && conditions.length > 0 && !flags.skippingStep) {
|
913
|
+
var completed = true;
|
914
|
+
for (var fn = 0; fn < conditions.length; fn++) {
|
915
|
+
var completingCondition = conditions[fn];
|
916
|
+
if (!completingCondition()) completed = false;
|
917
|
+
}
|
918
|
+
|
919
|
+
if (completed) {
|
920
|
+
if (this.currentStep.autoContinue === false) StepDescription.singleInstance.nextButton.enable();
|
921
|
+
else currentWizard.next();
|
922
|
+
}
|
923
|
+
}
|
924
|
+
});
|
925
|
+
|
926
|
+
|
927
|
+
Wizard.method("prepareAndPlay", function () {
|
928
|
+
currentWizard = this;
|
929
|
+
|
930
|
+
if (!this.isEligible()) {
|
931
|
+
if (this.preparation) this.preparation(function () {
|
932
|
+
currentWizard.play();
|
933
|
+
});
|
934
|
+
else throw new SSException("203", "This wizard is not eligible neither has a preparation function.");
|
935
|
+
} else this.play();
|
936
|
+
});
|
937
|
+
|
938
|
+
/**
|
939
|
+
The panel that holds step description, is positionated over the biggest remaining space among the four parts of a composite mask
|
940
|
+
|
941
|
+
@class DetailsPanel
|
942
|
+
@@singleton
|
943
|
+
@extends FadableItem
|
944
|
+
**/
|
945
|
+
var DetailsPanel = jazz.Class().extending(FadableItem).singleton;
|
946
|
+
|
947
|
+
/**
|
948
|
+
An object holding dimension information for the Details Panel
|
949
|
+
|
950
|
+
@@field dimension
|
951
|
+
@type Object
|
952
|
+
**/
|
953
|
+
DetailsPanel.field("dimension", {});
|
954
|
+
|
955
|
+
/**
|
956
|
+
An object holding positioning information for the Details Panel
|
957
|
+
|
958
|
+
@@field position
|
959
|
+
@type Object
|
960
|
+
**/
|
961
|
+
DetailsPanel.field("position", {});
|
962
|
+
|
963
|
+
/**
|
964
|
+
Renders the Details Panel
|
965
|
+
|
966
|
+
@method render
|
967
|
+
**/
|
968
|
+
DetailsPanel.method("render", function () {
|
969
|
+
this.$el = $("<div>").addClass("sideshow-details-panel").addClass("sideshow-hidden");
|
970
|
+
this.callSuper("render");
|
971
|
+
});
|
972
|
+
|
973
|
+
/**
|
974
|
+
Positionates the panel automatically, calculating the biggest available area and putting the panel over there
|
975
|
+
|
976
|
+
@method positionate
|
977
|
+
**/
|
978
|
+
DetailsPanel.method("positionate", function () {
|
979
|
+
var parts = Mask.CompositeMask.singleInstance.parts;
|
980
|
+
|
981
|
+
//Considering the four parts surrounding the current subject, gets the biggest one
|
982
|
+
var sortedSides = [
|
983
|
+
[parts.top, "height"],
|
984
|
+
[parts.right, "width"],
|
985
|
+
[parts.bottom, "height"],
|
986
|
+
[parts.left, "width"]
|
987
|
+
].sort(function (a, b) {
|
988
|
+
return a[0].dimension[a[1]] - b[0].dimension[b[1]];
|
989
|
+
});
|
990
|
+
|
991
|
+
var biggestSide = sortedSides.slice(-1)[0];
|
992
|
+
|
993
|
+
for (var i = 2; i > 0; i--) {
|
994
|
+
var side = sortedSides[i];
|
995
|
+
var dimension = side[0].dimension;
|
996
|
+
if (dimension.width > 250 && dimension.height > 250) {
|
997
|
+
if ((dimension.width + dimension.height) > ((biggestSide[0].dimension.width + biggestSide[0].dimension.height) * 2)) biggestSide = side;
|
998
|
+
}
|
999
|
+
}
|
1000
|
+
|
1001
|
+
if (biggestSide[1] == "width") {
|
1002
|
+
this.$el.css("left", biggestSide[0].position.x).css("top", 0).css("height", Screen.dimension.height).css("width", biggestSide[0].dimension.width);
|
1003
|
+
} else {
|
1004
|
+
this.$el.css("left", 0).css("top", biggestSide[0].position.y).css("height", biggestSide[0].dimension.height).css("width", Screen.dimension.width);
|
1005
|
+
}
|
1006
|
+
|
1007
|
+
this.dimension = {
|
1008
|
+
width: parsePxValue(this.$el.css("width")),
|
1009
|
+
height: parsePxValue(this.$el.css("height"))
|
1010
|
+
};
|
1011
|
+
|
1012
|
+
this.position = {
|
1013
|
+
x: parsePxValue(this.$el.css("left")),
|
1014
|
+
y: parsePxValue(this.$el.css("top"))
|
1015
|
+
};
|
1016
|
+
});
|
1017
|
+
|
1018
|
+
|
1019
|
+
/**
|
1020
|
+
Class representing all the current shown arrows
|
1021
|
+
|
1022
|
+
@class Arrows
|
1023
|
+
@static
|
1024
|
+
**/
|
1025
|
+
var Arrows = {};
|
1026
|
+
|
1027
|
+
Arrows.arrows = [];
|
1028
|
+
|
1029
|
+
/**
|
1030
|
+
Clear the currently defined arrows
|
1031
|
+
|
1032
|
+
@method clear
|
1033
|
+
@static
|
1034
|
+
**/
|
1035
|
+
Arrows.clear = function () {
|
1036
|
+
this.arrows = [];
|
1037
|
+
};
|
1038
|
+
|
1039
|
+
/**
|
1040
|
+
Sets the targets for arrows to point
|
1041
|
+
|
1042
|
+
@method setTargets
|
1043
|
+
@static
|
1044
|
+
**/
|
1045
|
+
Arrows.setTargets = function (targets, targetsChanged) {
|
1046
|
+
if (targets.constructor === String) targets = $(targets);
|
1047
|
+
|
1048
|
+
if (targets instanceof $ && targets.length > 0) {
|
1049
|
+
targets.each(function () {
|
1050
|
+
var arrow = Arrow.build();
|
1051
|
+
arrow.target.$el = $(this);
|
1052
|
+
if (arrow.target.$el.is(":visible")) {
|
1053
|
+
Arrows.arrows.push(arrow);
|
1054
|
+
arrow.onceVisible = true;
|
1055
|
+
}
|
1056
|
+
});
|
1057
|
+
}
|
1058
|
+
else if (!targetsChanged) throw new SSException("150", "Invalid targets.");
|
1059
|
+
};
|
1060
|
+
|
1061
|
+
Arrows.recreateDOMReferences = function () {
|
1062
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1063
|
+
var arrow = this.arrows[a];
|
1064
|
+
arrow.$el.remove();
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
Arrows.clear();
|
1068
|
+
Arrows.setTargets(currentWizard.currentStep.targets, true);
|
1069
|
+
Arrows.render();
|
1070
|
+
Arrows.positionate();
|
1071
|
+
Arrows.show();
|
1072
|
+
};
|
1073
|
+
|
1074
|
+
/**
|
1075
|
+
Iterates over the arrows collection showing each arrow
|
1076
|
+
|
1077
|
+
@method show
|
1078
|
+
@static
|
1079
|
+
**/
|
1080
|
+
Arrows.show = function () {
|
1081
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1082
|
+
var arrow = this.arrows[a];
|
1083
|
+
arrow.show();
|
1084
|
+
}
|
1085
|
+
};
|
1086
|
+
|
1087
|
+
/**
|
1088
|
+
Iterates over the arrows collection hiding each arrow
|
1089
|
+
|
1090
|
+
@method hide
|
1091
|
+
@static
|
1092
|
+
**/
|
1093
|
+
Arrows.hide = function () {
|
1094
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1095
|
+
var arrow = this.arrows[a];
|
1096
|
+
arrow.hide();
|
1097
|
+
}
|
1098
|
+
};
|
1099
|
+
|
1100
|
+
/**
|
1101
|
+
Iterates over the arrows collection fading in each arrow
|
1102
|
+
|
1103
|
+
@method fadeIn
|
1104
|
+
@static
|
1105
|
+
**/
|
1106
|
+
Arrows.fadeIn = function () {
|
1107
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1108
|
+
var arrow = this.arrows[a];
|
1109
|
+
arrow.fadeIn();
|
1110
|
+
}
|
1111
|
+
};
|
1112
|
+
|
1113
|
+
/**
|
1114
|
+
Iterates over the arrows collection fading out each arrow
|
1115
|
+
|
1116
|
+
@method fadeOut
|
1117
|
+
@static
|
1118
|
+
**/
|
1119
|
+
Arrows.fadeOut = function () {
|
1120
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1121
|
+
var arrow = this.arrows[a];
|
1122
|
+
registerFadeOut(arrow);
|
1123
|
+
}
|
1124
|
+
|
1125
|
+
function registerFadeOut(arrow) {
|
1126
|
+
arrow.fadeOut(function () {
|
1127
|
+
arrow.destroy();
|
1128
|
+
});
|
1129
|
+
}
|
1130
|
+
};
|
1131
|
+
|
1132
|
+
/**
|
1133
|
+
Iterates over the arrows collection repositionating each arrow
|
1134
|
+
|
1135
|
+
@method positionate
|
1136
|
+
@static
|
1137
|
+
**/
|
1138
|
+
Arrows.positionate = function () {
|
1139
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1140
|
+
var arrow = this.arrows[a];
|
1141
|
+
arrow.positionate();
|
1142
|
+
}
|
1143
|
+
};
|
1144
|
+
|
1145
|
+
/**
|
1146
|
+
Iterates over the arrows collection rendering each arrow
|
1147
|
+
|
1148
|
+
@method render
|
1149
|
+
@static
|
1150
|
+
**/
|
1151
|
+
Arrows.render = function () {
|
1152
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1153
|
+
var arrow = this.arrows[a];
|
1154
|
+
arrow.render();
|
1155
|
+
}
|
1156
|
+
};
|
1157
|
+
|
1158
|
+
/**
|
1159
|
+
A Polling function to check if arrows coordinates has changed
|
1160
|
+
|
1161
|
+
@method pollForArrowsChanges
|
1162
|
+
**/
|
1163
|
+
Arrows.pollForArrowsChanges = function () {
|
1164
|
+
var brokenReference = false;
|
1165
|
+
for (var a = 0; a < this.arrows.length; a++) {
|
1166
|
+
var arrow = this.arrows[a];
|
1167
|
+
if (arrow.hasChanged()) arrow.positionate();
|
1168
|
+
if (arrow.onceVisible && !arrow.target.$el.is(":visible")) brokenReference = true;
|
1169
|
+
}
|
1170
|
+
|
1171
|
+
if (brokenReference) this.recreateDOMReferences();
|
1172
|
+
};
|
1173
|
+
|
1174
|
+
/**
|
1175
|
+
A single arrow for pointing individual items in current subject
|
1176
|
+
|
1177
|
+
@class Arrow
|
1178
|
+
**/
|
1179
|
+
var Arrow = jazz.Class().extending(FadableItem);
|
1180
|
+
|
1181
|
+
/**
|
1182
|
+
The jQuery wrapped object which will be pointed by this arrow
|
1183
|
+
|
1184
|
+
@@field target
|
1185
|
+
@type Object
|
1186
|
+
**/
|
1187
|
+
Arrow.field("target", {});
|
1188
|
+
|
1189
|
+
/**
|
1190
|
+
Flag created to set if the arrow was visible once, this is used for recreating references to the targets DOM objects
|
1191
|
+
|
1192
|
+
@@field onceVisible
|
1193
|
+
@type Object
|
1194
|
+
**/
|
1195
|
+
Arrow.field("onceVisible", false);
|
1196
|
+
|
1197
|
+
/**
|
1198
|
+
Renders the Arrow
|
1199
|
+
|
1200
|
+
@method render
|
1201
|
+
**/
|
1202
|
+
Arrow.method("render", function () {
|
1203
|
+
this.$el = $("<div>").addClass("sideshow-subject-arrow").addClass("sideshow-hidden").addClass("sideshow-invisible");
|
1204
|
+
this.callSuper("render");
|
1205
|
+
});
|
1206
|
+
|
1207
|
+
/**
|
1208
|
+
Positionates the Arrow according to its target
|
1209
|
+
|
1210
|
+
@method positionate
|
1211
|
+
**/
|
1212
|
+
Arrow.method("positionate", function () {
|
1213
|
+
var target = this.target;
|
1214
|
+
target.position = {
|
1215
|
+
x: target.$el.offset().left - $window.scrollLeft(),
|
1216
|
+
y: target.$el.offset().top - $window.scrollTop()
|
1217
|
+
};
|
1218
|
+
target.dimension = {
|
1219
|
+
width: target.$el.outerWidth(),
|
1220
|
+
height: target.$el.outerHeight()
|
1221
|
+
};
|
1222
|
+
|
1223
|
+
this.$el.css("top", target.position.y - 30 + "px").css("left", target.position.x + (parsePxValue(target.dimension.width) / 2) - 12 + "px");
|
1224
|
+
});
|
1225
|
+
|
1226
|
+
/**
|
1227
|
+
Shows the Arrow
|
1228
|
+
|
1229
|
+
@method show
|
1230
|
+
**/
|
1231
|
+
Arrow.method("show", function () {
|
1232
|
+
this.callSuper("show");
|
1233
|
+
this.positionate();
|
1234
|
+
});
|
1235
|
+
|
1236
|
+
/**
|
1237
|
+
Does a fade in transition in the Arrow
|
1238
|
+
|
1239
|
+
@method fadeIn
|
1240
|
+
**/
|
1241
|
+
Arrow.method("fadeIn", function () {
|
1242
|
+
this.callSuper("fadeIn");
|
1243
|
+
this.positionate();
|
1244
|
+
});
|
1245
|
+
|
1246
|
+
/**
|
1247
|
+
Checks if the arrow's target position or dimension has changed
|
1248
|
+
|
1249
|
+
@method hasChanged
|
1250
|
+
@return boolean
|
1251
|
+
**/
|
1252
|
+
Arrow.method("hasChanged", function () {
|
1253
|
+
return (this.target.dimension.width !== this.target.$el.outerWidth() || this.target.dimension.height !== this.target.$el.outerHeight() || this.target.position.y !== (this.target.$el.offset().top - $window.scrollTop()) || this.target.position.x !== (this.target.$el.offset().left - $window.scrollLeft()));
|
1254
|
+
});
|
1255
|
+
/**
|
1256
|
+
Represents a panel holding the step description
|
1257
|
+
|
1258
|
+
@class StepDescription
|
1259
|
+
@extends FadableItem
|
1260
|
+
@@initializer
|
1261
|
+
**/
|
1262
|
+
var StepDescription = jazz.Class(function () {
|
1263
|
+
this.nextButton = StepDescriptionNextButton.build();
|
1264
|
+
}).extending(FadableItem).singleton;
|
1265
|
+
|
1266
|
+
/**
|
1267
|
+
The step description text content
|
1268
|
+
|
1269
|
+
@@field text
|
1270
|
+
@type String
|
1271
|
+
**/
|
1272
|
+
StepDescription.field("text", "");
|
1273
|
+
|
1274
|
+
/**
|
1275
|
+
The title text for the step description panel
|
1276
|
+
|
1277
|
+
@@field title
|
1278
|
+
@type String
|
1279
|
+
**/
|
1280
|
+
StepDescription.field("title", "");
|
1281
|
+
|
1282
|
+
/**
|
1283
|
+
An object holding dimension information for the Step Description panel
|
1284
|
+
|
1285
|
+
@@field dimension
|
1286
|
+
@type Object
|
1287
|
+
**/
|
1288
|
+
StepDescription.field("dimension", {});
|
1289
|
+
|
1290
|
+
/**
|
1291
|
+
An object holding positioning information for the Step Description panel
|
1292
|
+
|
1293
|
+
@@field position
|
1294
|
+
@type Object
|
1295
|
+
**/
|
1296
|
+
StepDescription.field("position", {});
|
1297
|
+
|
1298
|
+
/**
|
1299
|
+
An object representing the next button for a step description panel
|
1300
|
+
|
1301
|
+
@@field nextButton
|
1302
|
+
@type Object
|
1303
|
+
**/
|
1304
|
+
StepDescription.field("nextButton");
|
1305
|
+
|
1306
|
+
/**
|
1307
|
+
Sets the text for the step description panel
|
1308
|
+
|
1309
|
+
@method setText
|
1310
|
+
@param {String} text The text for the step description panel
|
1311
|
+
**/
|
1312
|
+
StepDescription.method("setText", function (text) {
|
1313
|
+
this.text = text;
|
1314
|
+
this.$el.find(".sideshow-step-text").text(text);
|
1315
|
+
});
|
1316
|
+
|
1317
|
+
/**
|
1318
|
+
Sets the HTML content for the step description panel
|
1319
|
+
|
1320
|
+
@method setHTML
|
1321
|
+
@param {String} text The HTML content for step description panel
|
1322
|
+
**/
|
1323
|
+
StepDescription.method("setHTML", function (text) {
|
1324
|
+
this.text = text;
|
1325
|
+
this.$el.find(".sideshow-step-text").html(text);
|
1326
|
+
});
|
1327
|
+
|
1328
|
+
/**
|
1329
|
+
Sets the title for the step description panel
|
1330
|
+
|
1331
|
+
@method setTitle
|
1332
|
+
@param {String} title The text for the step description panel
|
1333
|
+
**/
|
1334
|
+
StepDescription.method("setTitle", function (title) {
|
1335
|
+
this.title = title;
|
1336
|
+
this.$el.find("h2:first").text(title);
|
1337
|
+
});
|
1338
|
+
|
1339
|
+
/**
|
1340
|
+
Sets the title for the step description panel
|
1341
|
+
|
1342
|
+
@method setStepPosition
|
1343
|
+
@param {String} title The text for the step description panel
|
1344
|
+
**/
|
1345
|
+
StepDescription.method("setStepPosition", function (stepPosition) {
|
1346
|
+
this.stepPosition = stepPosition;
|
1347
|
+
this.$el.find(".sideshow-step-position").text(stepPosition);
|
1348
|
+
});
|
1349
|
+
|
1350
|
+
/**
|
1351
|
+
Renders the step description panel
|
1352
|
+
|
1353
|
+
@method render
|
1354
|
+
**/
|
1355
|
+
StepDescription.method("render", function () {
|
1356
|
+
this.$el = $("<div>").addClass("sideshow-step-description").addClass("sideshow-hidden").addClass("sideshow-invisible");
|
1357
|
+
|
1358
|
+
var stepPosition = $("<span>").addClass("sideshow-step-position");
|
1359
|
+
this.$el.append(stepPosition);
|
1360
|
+
if (currentWizard.showStepPosition === false) stepPosition.hide();
|
1361
|
+
|
1362
|
+
this.$el.append($("<h2>"));
|
1363
|
+
this.$el.append($("<div>").addClass("sideshow-step-text"));
|
1364
|
+
this.nextButton.render(this.$el);
|
1365
|
+
this.nextButton.$el.click(function () {
|
1366
|
+
currentWizard.next();
|
1367
|
+
});
|
1368
|
+
DetailsPanel.singleInstance.$el.append(this.$el);
|
1369
|
+
});
|
1370
|
+
|
1371
|
+
/**
|
1372
|
+
Shows the step description panel
|
1373
|
+
|
1374
|
+
@method show
|
1375
|
+
**/
|
1376
|
+
StepDescription.method("show", function (displayButKeepTransparent) {
|
1377
|
+
this.callSuper("show", displayButKeepTransparent);
|
1378
|
+
//this.positionate();
|
1379
|
+
});
|
1380
|
+
|
1381
|
+
/**
|
1382
|
+
Positionates the step description panel
|
1383
|
+
|
1384
|
+
@method positionate
|
1385
|
+
**/
|
1386
|
+
StepDescription.method("positionate", function () {
|
1387
|
+
var dp = DetailsPanel.singleInstance;
|
1388
|
+
|
1389
|
+
if (dp.dimension.width >= 900) this.dimension.width = 900;
|
1390
|
+
else this.dimension.width = dp.dimension.width * 0.9;
|
1391
|
+
|
1392
|
+
this.$el.css("width", this.dimension.width);
|
1393
|
+
|
1394
|
+
var paddingLeftRight = (parsePxValue(this.$el.css("padding-left")) + parsePxValue(this.$el.css("padding-right"))) / 2;
|
1395
|
+
var paddingTopBottom = (parsePxValue(this.$el.css("padding-top")) + parsePxValue(this.$el.css("padding-bottom"))) / 2;
|
1396
|
+
|
1397
|
+
this.dimension.height = parsePxValue(this.$el.outerHeight());
|
1398
|
+
|
1399
|
+
//Checks if the description dimension overflow the available space in the details panel
|
1400
|
+
if (this.dimension.height > dp.dimension.height || this.dimension.width < 400) {
|
1401
|
+
this.dimension.width = $window.width() * 0.9;
|
1402
|
+
this.$el.css("width", this.dimension.width);
|
1403
|
+
this.dimension.height = parsePxValue(this.$el.outerHeight());
|
1404
|
+
|
1405
|
+
this.position.x = ($window.width() - this.dimension.width) / 2;
|
1406
|
+
this.position.y = ($window.height() - this.dimension.height) / 2;
|
1407
|
+
} else {
|
1408
|
+
this.position.x = (dp.dimension.width - this.dimension.width) / 2;
|
1409
|
+
this.position.y = (dp.dimension.height - this.dimension.height) / 2;
|
1410
|
+
}
|
1411
|
+
|
1412
|
+
this.$el.css("left", this.position.x - paddingLeftRight);
|
1413
|
+
this.$el.css("top", this.position.y - paddingTopBottom);
|
1414
|
+
});
|
1415
|
+
|
1416
|
+
/**
|
1417
|
+
Step next button
|
1418
|
+
|
1419
|
+
@class StepDescriptionNextButton
|
1420
|
+
@extends HidableItem
|
1421
|
+
**/
|
1422
|
+
var StepDescriptionNextButton = jazz.Class().extending(HidableItem);
|
1423
|
+
|
1424
|
+
/**
|
1425
|
+
The text for the next button
|
1426
|
+
|
1427
|
+
@@field _text
|
1428
|
+
@private
|
1429
|
+
**/
|
1430
|
+
StepDescriptionNextButton.field("_text");
|
1431
|
+
|
1432
|
+
/**
|
1433
|
+
Disables the next button
|
1434
|
+
|
1435
|
+
@method disable
|
1436
|
+
**/
|
1437
|
+
StepDescriptionNextButton.method("disable", function () {
|
1438
|
+
this.$el.attr("disabled", "disabled");
|
1439
|
+
});
|
1440
|
+
|
1441
|
+
/**
|
1442
|
+
Enables the next button
|
1443
|
+
|
1444
|
+
@method enable
|
1445
|
+
**/
|
1446
|
+
StepDescriptionNextButton.method("enable", function () {
|
1447
|
+
this.$el.attr("disabled", null);
|
1448
|
+
});
|
1449
|
+
|
1450
|
+
/**
|
1451
|
+
Sets the text for the next button
|
1452
|
+
|
1453
|
+
@method setText
|
1454
|
+
@param {String} text The text for the next button
|
1455
|
+
**/
|
1456
|
+
StepDescriptionNextButton.method("setText", function (text) {
|
1457
|
+
this._text = text;
|
1458
|
+
this.$el.text(text);
|
1459
|
+
});
|
1460
|
+
|
1461
|
+
/**
|
1462
|
+
Renders the Next Button
|
1463
|
+
|
1464
|
+
@method render
|
1465
|
+
@param {Object} $stepDescriptionEl The jQuery wrapped DOM element for the Step Description panel
|
1466
|
+
**/
|
1467
|
+
StepDescriptionNextButton.method("render", function ($stepDescriptionEl) {
|
1468
|
+
this.$el = $("<button>").addClass("sideshow-next-step-button");
|
1469
|
+
this.callSuper("render", $stepDescriptionEl);
|
1470
|
+
});
|
1471
|
+
/**
|
1472
|
+
Represents the current available area in the browser
|
1473
|
+
|
1474
|
+
@class Screen
|
1475
|
+
@static
|
1476
|
+
**/
|
1477
|
+
var Screen = {};
|
1478
|
+
|
1479
|
+
/**
|
1480
|
+
Object holding dimension information for the screen
|
1481
|
+
|
1482
|
+
@@field
|
1483
|
+
@static
|
1484
|
+
@type Object
|
1485
|
+
**/
|
1486
|
+
Screen.dimension = {};
|
1487
|
+
|
1488
|
+
/**
|
1489
|
+
Checks if the screen dimension information has changed
|
1490
|
+
|
1491
|
+
@method hasChanged
|
1492
|
+
@static
|
1493
|
+
@return boolean
|
1494
|
+
**/
|
1495
|
+
Screen.hasChanged = function () {
|
1496
|
+
return ($window.width() !== this.dimension.width) || ($window.height() !== this.dimension.height);
|
1497
|
+
};
|
1498
|
+
|
1499
|
+
/**
|
1500
|
+
Updates the dimension information for the screen
|
1501
|
+
|
1502
|
+
@method updateInfo
|
1503
|
+
@static
|
1504
|
+
**/
|
1505
|
+
Screen.updateInfo = function () {
|
1506
|
+
this.dimension.width = $window.width();
|
1507
|
+
this.dimension.height = $window.height();
|
1508
|
+
};
|
1509
|
+
/**
|
1510
|
+
The current subject (the object being shown by the current wizard)
|
1511
|
+
|
1512
|
+
@class Subject
|
1513
|
+
@static
|
1514
|
+
**/
|
1515
|
+
var Subject = {};
|
1516
|
+
|
1517
|
+
/**
|
1518
|
+
The current subject jQuery wrapped DOM element
|
1519
|
+
|
1520
|
+
@@field obj
|
1521
|
+
@static
|
1522
|
+
@type Object
|
1523
|
+
**/
|
1524
|
+
Subject.obj = null;
|
1525
|
+
|
1526
|
+
/**
|
1527
|
+
The current subject dimension information
|
1528
|
+
|
1529
|
+
@@field position
|
1530
|
+
@static
|
1531
|
+
@type Object
|
1532
|
+
**/
|
1533
|
+
Subject.dimension = {};
|
1534
|
+
|
1535
|
+
/**
|
1536
|
+
The current subject positioning information
|
1537
|
+
|
1538
|
+
@@field position
|
1539
|
+
@static
|
1540
|
+
@type Object
|
1541
|
+
**/
|
1542
|
+
Subject.position = {};
|
1543
|
+
|
1544
|
+
/**
|
1545
|
+
The current subject border radius information
|
1546
|
+
|
1547
|
+
@@field borderRadius
|
1548
|
+
@static
|
1549
|
+
@type Object
|
1550
|
+
**/
|
1551
|
+
Subject.borderRadius = {};
|
1552
|
+
|
1553
|
+
/**
|
1554
|
+
Checks if the object has changed since the last checking
|
1555
|
+
|
1556
|
+
@method hasChanged
|
1557
|
+
@return boolean
|
1558
|
+
**/
|
1559
|
+
Subject.hasChanged = function () {
|
1560
|
+
if (!this.obj) return false;
|
1561
|
+
|
1562
|
+
return (this.obj.offset().left - $window.scrollLeft() !== this.position.x) || (this.obj.offset().top - $window.scrollTop() !== this.position.y) || (this.obj.outerWidth() !== this.dimension.width) || (this.obj.outerHeight() !== this.dimension.height) || (parsePxValue(this.obj.css("border-top-left-radius")) !== this.borderRadius.leftTop) || (parsePxValue(this.obj.css("border-top-right-radius")) !== this.borderRadius.rightTop) || (parsePxValue(this.obj.css("border-bottom-left-radius")) !== this.borderRadius.leftBottom) || (parsePxValue(this.obj.css("border-bottom-right-radius")) !== this.borderRadius.rightBottom) || Screen.hasChanged();
|
1563
|
+
};
|
1564
|
+
|
1565
|
+
/**
|
1566
|
+
Updates the information about the suject
|
1567
|
+
|
1568
|
+
@method updateInfo
|
1569
|
+
@param {Object} config Dimension, positioning and border radius information
|
1570
|
+
**/
|
1571
|
+
Subject.updateInfo = function (config) {
|
1572
|
+
if (config === undefined) {
|
1573
|
+
this.position.x = this.obj.offset().left - $window.scrollLeft();
|
1574
|
+
this.position.y = this.obj.offset().top - $window.scrollTop();
|
1575
|
+
this.dimension.width = this.obj.outerWidth();
|
1576
|
+
this.dimension.height = this.obj.outerHeight();
|
1577
|
+
this.borderRadius.leftTop = parsePxValue(this.obj.css("border-top-left-radius"));
|
1578
|
+
this.borderRadius.rightTop = parsePxValue(this.obj.css("border-top-right-radius"));
|
1579
|
+
this.borderRadius.leftBottom = parsePxValue(this.obj.css("border-bottom-left-radius"));
|
1580
|
+
this.borderRadius.rightBottom = parsePxValue(this.obj.css("border-bottom-right-radius"));
|
1581
|
+
} else {
|
1582
|
+
this.position.x = config.position.x;
|
1583
|
+
this.position.y = config.position.y;
|
1584
|
+
this.dimension.width = config.dimension.width;
|
1585
|
+
this.dimension.height = config.dimension.height;
|
1586
|
+
this.borderRadius.leftTop = config.borderRadius.leftTop;
|
1587
|
+
this.borderRadius.rightTop = config.borderRadius.rightTop;
|
1588
|
+
this.borderRadius.leftBottom = config.borderRadius.leftBottom;
|
1589
|
+
this.borderRadius.rightBottom = config.borderRadius.rightBottom;
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
Screen.updateInfo();
|
1593
|
+
};
|
1594
|
+
|
1595
|
+
Subject.isSubjectVisible = function (position, dimension) {
|
1596
|
+
if ((position.y + dimension.height) > $window.height() || position.y < 0) {
|
1597
|
+
return false;
|
1598
|
+
}
|
1599
|
+
return true;
|
1600
|
+
};
|
1601
|
+
/**
|
1602
|
+
Namespace to hold classes for mask control
|
1603
|
+
|
1604
|
+
@namespace Mask
|
1605
|
+
**/
|
1606
|
+
var Mask = {};
|
1607
|
+
/**
|
1608
|
+
Controls the mask that covers the subject during a step transition
|
1609
|
+
|
1610
|
+
@class SubjectMask
|
1611
|
+
@@singleton
|
1612
|
+
**/
|
1613
|
+
Mask.SubjectMask = jazz.Class().extending(FadableItem).singleton;
|
1614
|
+
|
1615
|
+
/**
|
1616
|
+
Renders the subject mask
|
1617
|
+
|
1618
|
+
@method render
|
1619
|
+
**/
|
1620
|
+
Mask.SubjectMask.method("render", function () {
|
1621
|
+
this.$el = $("<div>").addClass("sideshow-subject-mask");
|
1622
|
+
this.callSuper("render");
|
1623
|
+
});
|
1624
|
+
|
1625
|
+
/**
|
1626
|
+
Updates the dimension, positioning and border radius of the subject mask
|
1627
|
+
|
1628
|
+
@method update
|
1629
|
+
@param {Object} position The positioning information
|
1630
|
+
@param {Object} dimension The dimension information
|
1631
|
+
@param {Object} borderRadius The border radius information
|
1632
|
+
**/
|
1633
|
+
Mask.SubjectMask.method("update", function (position, dimension, borderRadius) {
|
1634
|
+
this.$el.css("left", position.x).css("top", position.y).css("width", dimension.width).css("height", dimension.height).css("border-radius", borderRadius.leftTop + "px " + borderRadius.rightTop + "px " + borderRadius.leftBottom + "px " + borderRadius.rightBottom + "px ");
|
1635
|
+
});
|
1636
|
+
/**
|
1637
|
+
Controls the mask surrounds the subject (the step focussed area)
|
1638
|
+
|
1639
|
+
@class CompositeMask
|
1640
|
+
@@singleton
|
1641
|
+
**/
|
1642
|
+
Mask.CompositeMask = jazz.Class().extending(FadableItem).singleton;
|
1643
|
+
|
1644
|
+
/**
|
1645
|
+
Initializes the composite mask
|
1646
|
+
|
1647
|
+
@method init
|
1648
|
+
**/
|
1649
|
+
Mask.CompositeMask.method("init", function () {
|
1650
|
+
var mask = this;
|
1651
|
+
["top", "left", "right", "bottom"].forEach(function (d) {
|
1652
|
+
mask.parts[d] = Mask.CompositeMask.Part.build();
|
1653
|
+
});
|
1654
|
+
["leftTop", "rightTop", "leftBottom", "rightBottom"].forEach(function (d) {
|
1655
|
+
mask.parts[d] = Mask.CompositeMask.CornerPart.build();
|
1656
|
+
});
|
1657
|
+
});
|
1658
|
+
|
1659
|
+
/**
|
1660
|
+
The parts composing the mask
|
1661
|
+
|
1662
|
+
@@field parts
|
1663
|
+
@type Object
|
1664
|
+
**/
|
1665
|
+
Mask.CompositeMask.field("parts", {});
|
1666
|
+
|
1667
|
+
/**
|
1668
|
+
Renders the composite mask
|
1669
|
+
|
1670
|
+
@method render
|
1671
|
+
**/
|
1672
|
+
Mask.CompositeMask.method("render", function () {
|
1673
|
+
var mask = this;
|
1674
|
+
for (var p in this.parts) {
|
1675
|
+
var part = this.parts[p];
|
1676
|
+
if (part.render) part.render();
|
1677
|
+
}
|
1678
|
+
this.$el = $(".sideshow-mask-part, .sideshow-mask-corner-part");
|
1679
|
+
// if(!this.$el || this.$el.length === 0) this.$el = $(".sideshow-mask-part, .sideshow-mask-corner-part");
|
1680
|
+
Mask.SubjectMask.singleInstance.render();
|
1681
|
+
["leftTop", "rightTop", "leftBottom", "rightBottom"].forEach(function (d) {
|
1682
|
+
mask.parts[d].$el.addClass(d);
|
1683
|
+
});
|
1684
|
+
this.status = AnimationStatus.NOT_DISPLAYED;
|
1685
|
+
});
|
1686
|
+
|
1687
|
+
/**
|
1688
|
+
Checks if the subject is fully visible, if not, scrolls 'til it became fully visible
|
1689
|
+
|
1690
|
+
@method scrollIfNecessary
|
1691
|
+
@param {Object} position An object representing the positioning info for the mask
|
1692
|
+
@param {Object} dimension An object representing the dimension info for the mask
|
1693
|
+
**/
|
1694
|
+
Mask.CompositeMask.method("scrollIfNecessary", function (position, dimension) {
|
1695
|
+
function doSmoothScroll(scrollTop, callback) {
|
1696
|
+
$("body,html").animate({
|
1697
|
+
scrollTop: scrollTop
|
1698
|
+
}, 300, callback);
|
1699
|
+
}
|
1700
|
+
|
1701
|
+
if (!Subject.isSubjectVisible(position, dimension)) {
|
1702
|
+
var description = StepDescription.singleInstance;
|
1703
|
+
var y = dimension.height > ($window.height() - 50) ? position.y : position.y - 25;
|
1704
|
+
y += $window.scrollTop();
|
1705
|
+
|
1706
|
+
doSmoothScroll(y, function () {
|
1707
|
+
setTimeout(function () {
|
1708
|
+
DetailsPanel.singleInstance.positionate();
|
1709
|
+
description.positionate();
|
1710
|
+
description.fadeIn();
|
1711
|
+
}, 300);
|
1712
|
+
});
|
1713
|
+
|
1714
|
+
return true;
|
1715
|
+
}
|
1716
|
+
return false;
|
1717
|
+
});
|
1718
|
+
|
1719
|
+
/**
|
1720
|
+
Updates the positioning and dimension of each part composing the whole mask, according to the subject coordinates
|
1721
|
+
|
1722
|
+
@method update
|
1723
|
+
@param {Object} position An object representing the positioning info for the mask
|
1724
|
+
@param {Object} dimension An object representing the dimension info for the mask
|
1725
|
+
@param {Object} borderRadius An object representing the borderRadius info for the mask
|
1726
|
+
**/
|
1727
|
+
Mask.CompositeMask.method("update", function (position, dimension, borderRadius) {
|
1728
|
+
Mask.SubjectMask.singleInstance.update(position, dimension, borderRadius);
|
1729
|
+
//Aliases
|
1730
|
+
var left = position.x,
|
1731
|
+
top = position.y,
|
1732
|
+
width = dimension.width,
|
1733
|
+
height = dimension.height,
|
1734
|
+
br = borderRadius;
|
1735
|
+
|
1736
|
+
//Updates the divs surrounding the subject
|
1737
|
+
this.parts.top.update({
|
1738
|
+
x: 0,
|
1739
|
+
y: 0
|
1740
|
+
}, {
|
1741
|
+
width: $window.width(),
|
1742
|
+
height: top
|
1743
|
+
});
|
1744
|
+
this.parts.left.update({
|
1745
|
+
x: 0,
|
1746
|
+
y: top
|
1747
|
+
}, {
|
1748
|
+
width: left,
|
1749
|
+
height: height
|
1750
|
+
});
|
1751
|
+
this.parts.right.update({
|
1752
|
+
x: left + width,
|
1753
|
+
y: top
|
1754
|
+
}, {
|
1755
|
+
width: $window.width() - (left + width),
|
1756
|
+
height: height
|
1757
|
+
});
|
1758
|
+
this.parts.bottom.update({
|
1759
|
+
x: 0,
|
1760
|
+
y: top + height
|
1761
|
+
}, {
|
1762
|
+
width: $window.width(),
|
1763
|
+
height: $window.height() - (top + height)
|
1764
|
+
});
|
1765
|
+
|
1766
|
+
//Updates the Rounded corners
|
1767
|
+
this.parts.leftTop.update({
|
1768
|
+
x: left,
|
1769
|
+
y: top
|
1770
|
+
}, br.leftTop);
|
1771
|
+
this.parts.rightTop.update({
|
1772
|
+
x: left + width - br.rightTop,
|
1773
|
+
y: top
|
1774
|
+
}, br.rightTop);
|
1775
|
+
this.parts.leftBottom.update({
|
1776
|
+
x: left,
|
1777
|
+
y: top + height - br.leftBottom
|
1778
|
+
}, br.leftBottom);
|
1779
|
+
this.parts.rightBottom.update({
|
1780
|
+
x: left + width - br.rightBottom,
|
1781
|
+
y: top + height - br.rightBottom
|
1782
|
+
}, br.rightBottom);
|
1783
|
+
});
|
1784
|
+
|
1785
|
+
/**
|
1786
|
+
A Polling function to check if subject coordinates has changed
|
1787
|
+
|
1788
|
+
@method pollForSubjectChanges
|
1789
|
+
**/
|
1790
|
+
Mask.CompositeMask.method("pollForSubjectChanges", function () {
|
1791
|
+
if (!flags.lockMaskUpdate) {
|
1792
|
+
if (currentWizard && currentWizard.currentStep.subject) {
|
1793
|
+
var subject = $(currentWizard.currentStep.subject);
|
1794
|
+
if (Subject.obj[0] !== subject[0]) SS.setSubject(subject, true);
|
1795
|
+
}
|
1796
|
+
|
1797
|
+
if (Subject.hasChanged()) {
|
1798
|
+
Subject.updateInfo();
|
1799
|
+
this.update(Subject.position, Subject.dimension, Subject.borderRadius);
|
1800
|
+
}
|
1801
|
+
}
|
1802
|
+
});
|
1803
|
+
|
1804
|
+
/**
|
1805
|
+
A Polling function to check if screen dimension has changed
|
1806
|
+
|
1807
|
+
@method pollForScreenChanges
|
1808
|
+
**/
|
1809
|
+
Mask.CompositeMask.method("pollForScreenChanges", function () {
|
1810
|
+
if (Screen.hasChanged()) {
|
1811
|
+
Screen.updateInfo();
|
1812
|
+
this.update(Subject.position, Subject.dimension, Subject.borderRadius);
|
1813
|
+
}
|
1814
|
+
});
|
1815
|
+
|
1816
|
+
/**
|
1817
|
+
A part composing the mask
|
1818
|
+
|
1819
|
+
@class Part
|
1820
|
+
@@initializer
|
1821
|
+
@param {Object} position The positioning information
|
1822
|
+
@param {Object} dimension The dimension information
|
1823
|
+
**/
|
1824
|
+
Mask.CompositeMask.Part = jazz.Class(function (position, dimension) {
|
1825
|
+
this.position = position;
|
1826
|
+
this.dimension = dimension;
|
1827
|
+
}).extending(VisualItem);
|
1828
|
+
|
1829
|
+
|
1830
|
+
/**
|
1831
|
+
@@alias Part
|
1832
|
+
@@to Mask.CompositeMask.Part
|
1833
|
+
**/
|
1834
|
+
var Part = Mask.CompositeMask.Part;
|
1835
|
+
|
1836
|
+
/**
|
1837
|
+
An object holding positioning information for the mask part
|
1838
|
+
|
1839
|
+
@@field position
|
1840
|
+
@type Object
|
1841
|
+
**/
|
1842
|
+
Part.field("position", {});
|
1843
|
+
|
1844
|
+
/**
|
1845
|
+
An object holding dimension information for the mask part
|
1846
|
+
|
1847
|
+
@@field position
|
1848
|
+
@type Object
|
1849
|
+
**/
|
1850
|
+
Part.field("dimension", {});
|
1851
|
+
|
1852
|
+
/**
|
1853
|
+
Renders the mask part
|
1854
|
+
|
1855
|
+
@method render
|
1856
|
+
**/
|
1857
|
+
Part.method("render", function () {
|
1858
|
+
this.$el = $("<div>").addClass("sideshow-mask-part").addClass("sideshow-hidden").addClass("sideshow-invisible");
|
1859
|
+
this.callSuper("render");
|
1860
|
+
});
|
1861
|
+
|
1862
|
+
/**
|
1863
|
+
Updates the dimension and positioning of the subject mask part
|
1864
|
+
|
1865
|
+
@method update
|
1866
|
+
@param {Object} position The positioning information
|
1867
|
+
@param {Object} dimension The dimension information
|
1868
|
+
**/
|
1869
|
+
Part.method("update", function (position, dimension) {
|
1870
|
+
this.position = position;
|
1871
|
+
this.dimension = dimension;
|
1872
|
+
this.$el.css("left", position.x).css("top", position.y).css("width", dimension.width).css("height", dimension.height);
|
1873
|
+
});
|
1874
|
+
/**
|
1875
|
+
A corner part composing the mask
|
1876
|
+
|
1877
|
+
@class CornerPart
|
1878
|
+
@@initializer
|
1879
|
+
@param {Object} position The positioning information
|
1880
|
+
@param {Object} dimension The dimension information
|
1881
|
+
**/
|
1882
|
+
Mask.CompositeMask.CornerPart = jazz.Class().extending(VisualItem);
|
1883
|
+
|
1884
|
+
/**
|
1885
|
+
@@alias CornerPart
|
1886
|
+
@@to Mask.CompositeMask.CornerPart
|
1887
|
+
**/
|
1888
|
+
var CornerPart = Mask.CompositeMask.CornerPart;
|
1889
|
+
|
1890
|
+
/**
|
1891
|
+
An object holding positioning information for the mask corner part
|
1892
|
+
|
1893
|
+
@@field position
|
1894
|
+
@type Object
|
1895
|
+
**/
|
1896
|
+
CornerPart.field("position", {});
|
1897
|
+
|
1898
|
+
/**
|
1899
|
+
An object holding dimension information for the mask corner part
|
1900
|
+
|
1901
|
+
@@field position
|
1902
|
+
@type Object
|
1903
|
+
**/
|
1904
|
+
CornerPart.field("dimension", {});
|
1905
|
+
|
1906
|
+
/**
|
1907
|
+
An object holding border radius information for the mask corner part
|
1908
|
+
|
1909
|
+
@@field borderRadius
|
1910
|
+
@type Object
|
1911
|
+
**/
|
1912
|
+
CornerPart.field("borderRadius", 0);
|
1913
|
+
|
1914
|
+
/**
|
1915
|
+
Formats the SVG path for the corner part
|
1916
|
+
|
1917
|
+
@method SVGPathPointsTemplate
|
1918
|
+
@param {Number} borderRadius The corner part border radius
|
1919
|
+
@static
|
1920
|
+
**/
|
1921
|
+
CornerPart.static.SVGPathPointsTemplate = function (borderRadius) {
|
1922
|
+
return "m 0,0 0," + borderRadius + " C 0," + borderRadius * 0.46 + " " + borderRadius * 0.46 + ",0 " + borderRadius + ",0";
|
1923
|
+
};
|
1924
|
+
|
1925
|
+
/**
|
1926
|
+
Renders the SVG for the corner part
|
1927
|
+
|
1928
|
+
@method buildSVG
|
1929
|
+
@param {Number} borderRadius The corner part border radius
|
1930
|
+
@static
|
1931
|
+
**/
|
1932
|
+
CornerPart.static.buildSVG = function (borderRadius) {
|
1933
|
+
function SVG(nodeName) {
|
1934
|
+
return document.createElementNS("http://www.w3.org/2000/svg", nodeName);
|
1935
|
+
}
|
1936
|
+
|
1937
|
+
var bezierPoints = this.SVGPathPointsTemplate(borderRadius);
|
1938
|
+
var $svg = $(SVG("svg"));
|
1939
|
+
var $path = $(SVG("path"));
|
1940
|
+
|
1941
|
+
$path.attr("d", bezierPoints);
|
1942
|
+
$svg.append($path);
|
1943
|
+
|
1944
|
+
return $svg[0];
|
1945
|
+
};
|
1946
|
+
|
1947
|
+
/**
|
1948
|
+
Renders the mask corner part
|
1949
|
+
|
1950
|
+
@method render
|
1951
|
+
@return {Object} The corner part jQuery wrapped DOM element
|
1952
|
+
**/
|
1953
|
+
CornerPart.prototype.render = function () {
|
1954
|
+
this.$el = $("<div>").addClass("sideshow-mask-corner-part").addClass("sideshow-hidden").addClass("sideshow-invisible");
|
1955
|
+
this.$el.append(CornerPart.buildSVG(this.borderRadius));
|
1956
|
+
$body.append(this.$el);
|
1957
|
+
return this.$el;
|
1958
|
+
};
|
1959
|
+
|
1960
|
+
/**
|
1961
|
+
Updates the positioning and border radius of the mask corner part
|
1962
|
+
|
1963
|
+
@method update
|
1964
|
+
@param {Object} position The positioning information
|
1965
|
+
@param {Object} borderRadius The border radius information
|
1966
|
+
**/
|
1967
|
+
CornerPart.prototype.update = function (position, borderRadius) {
|
1968
|
+
this.$el.css("left", position.x).css("top", position.y).css("width", borderRadius).css("height", borderRadius);
|
1969
|
+
|
1970
|
+
$(this.$el).find("path").attr("d", CornerPart.SVGPathPointsTemplate(borderRadius));
|
1971
|
+
};
|
1972
|
+
/**
|
1973
|
+
Controls the polling functions needed by Sideshow
|
1974
|
+
|
1975
|
+
@class Polling
|
1976
|
+
@static
|
1977
|
+
**/
|
1978
|
+
var Polling = {};
|
1979
|
+
|
1980
|
+
/**
|
1981
|
+
The polling functions queue
|
1982
|
+
|
1983
|
+
@@field queue
|
1984
|
+
@type Object
|
1985
|
+
@static
|
1986
|
+
**/
|
1987
|
+
Polling.queue = [];
|
1988
|
+
|
1989
|
+
/**
|
1990
|
+
A flag that controls if the polling is locked
|
1991
|
+
|
1992
|
+
@@field lock
|
1993
|
+
@type boolean
|
1994
|
+
@static
|
1995
|
+
**/
|
1996
|
+
Polling.lock = false;
|
1997
|
+
|
1998
|
+
/**
|
1999
|
+
Pushes a polling function in the queue
|
2000
|
+
|
2001
|
+
@method enqueue
|
2002
|
+
@static
|
2003
|
+
**/
|
2004
|
+
Polling.enqueue = function () {
|
2005
|
+
var firstArg = arguments[0];
|
2006
|
+
var fn;
|
2007
|
+
var name = "";
|
2008
|
+
|
2009
|
+
if (typeof firstArg == "function") fn = firstArg;
|
2010
|
+
else {
|
2011
|
+
name = arguments[0];
|
2012
|
+
fn = arguments[1];
|
2013
|
+
}
|
2014
|
+
|
2015
|
+
if (this.getFunctionIndex(fn) < 0 && (name === "" || this.getFunctionIndex(name) < 0)) {
|
2016
|
+
this.queue.push({
|
2017
|
+
name: name,
|
2018
|
+
fn: fn,
|
2019
|
+
enabled: true
|
2020
|
+
});
|
2021
|
+
} else throw new SSException("301", "The function is already in the polling queue.");
|
2022
|
+
};
|
2023
|
+
|
2024
|
+
/**
|
2025
|
+
Removes a polling function from the queue
|
2026
|
+
|
2027
|
+
@method dequeue
|
2028
|
+
@static
|
2029
|
+
**/
|
2030
|
+
Polling.dequeue = function () {
|
2031
|
+
this.queue.splice(this.getFunctionIndex(arguments[0]), 1);
|
2032
|
+
};
|
2033
|
+
|
2034
|
+
/**
|
2035
|
+
Enables an specific polling function
|
2036
|
+
|
2037
|
+
@method enable
|
2038
|
+
@static
|
2039
|
+
**/
|
2040
|
+
Polling.enable = function () {
|
2041
|
+
this.queue[this.getFunctionIndex(arguments[0])].enabled = true;
|
2042
|
+
}
|
2043
|
+
|
2044
|
+
/**
|
2045
|
+
Disables an specific polling function, but preserving it in the polling queue
|
2046
|
+
|
2047
|
+
@method disable
|
2048
|
+
@static
|
2049
|
+
**/
|
2050
|
+
Polling.disable = function () {
|
2051
|
+
this.queue[this.getFunctionIndex(arguments[0])].enabled = false;
|
2052
|
+
}
|
2053
|
+
|
2054
|
+
/**
|
2055
|
+
Gets the position of a polling function in the queue based on its name or the function itself
|
2056
|
+
|
2057
|
+
@method getFunctionIndex
|
2058
|
+
@static
|
2059
|
+
**/
|
2060
|
+
Polling.getFunctionIndex = function () {
|
2061
|
+
var firstArg = arguments[0];
|
2062
|
+
|
2063
|
+
if (typeof firstArg == "function") return this.queue.map(function (p) {
|
2064
|
+
return p.fn;
|
2065
|
+
}).indexOf(firstArg);
|
2066
|
+
else if (typeof firstArg == "string") return this.queue.map(function (p) {
|
2067
|
+
return p.name;
|
2068
|
+
}).indexOf(firstArg);
|
2069
|
+
|
2070
|
+
throw new SSException("302", "Invalid argument for getFunctionIndex method. Expected a string (the polling function name) or a function (the polling function itself).");
|
2071
|
+
}
|
2072
|
+
|
2073
|
+
/**
|
2074
|
+
Unlocks the polling and starts the checking process
|
2075
|
+
|
2076
|
+
@method start
|
2077
|
+
@static
|
2078
|
+
**/
|
2079
|
+
Polling.start = function () {
|
2080
|
+
this.lock = false;
|
2081
|
+
this.doPolling();
|
2082
|
+
};
|
2083
|
+
|
2084
|
+
/**
|
2085
|
+
Stops the polling process
|
2086
|
+
|
2087
|
+
@method stop
|
2088
|
+
@static
|
2089
|
+
**/
|
2090
|
+
Polling.stop = function () {
|
2091
|
+
this.lock = true;
|
2092
|
+
};
|
2093
|
+
|
2094
|
+
/**
|
2095
|
+
Clear the polling queue
|
2096
|
+
|
2097
|
+
@method clear
|
2098
|
+
@static
|
2099
|
+
**/
|
2100
|
+
Polling.clear = function () {
|
2101
|
+
var lock = this.lock;
|
2102
|
+
|
2103
|
+
this.lock = true;
|
2104
|
+
this.queue = [];
|
2105
|
+
this.lock = lock;
|
2106
|
+
};
|
2107
|
+
|
2108
|
+
/**
|
2109
|
+
Starts the polling process
|
2110
|
+
|
2111
|
+
@method doPolling
|
2112
|
+
@static
|
2113
|
+
**/
|
2114
|
+
Polling.doPolling = function () {
|
2115
|
+
if (!this.lock) {
|
2116
|
+
//Using timeout to avoid the queue to not complete in a cycle
|
2117
|
+
setTimeout(function () {
|
2118
|
+
for (var fn = 0; fn < Polling.queue.length; fn++) {
|
2119
|
+
var pollingFunction = Polling.queue[fn];
|
2120
|
+
pollingFunction.enabled && pollingFunction.fn();
|
2121
|
+
}
|
2122
|
+
Polling.doPolling();
|
2123
|
+
}, pollingDuration);
|
2124
|
+
}
|
2125
|
+
};
|
2126
|
+
/**
|
2127
|
+
The main menu, where the available wizards are listed
|
2128
|
+
|
2129
|
+
@class WizardMenu
|
2130
|
+
@static
|
2131
|
+
**/
|
2132
|
+
var WizardMenu = {};
|
2133
|
+
|
2134
|
+
/**
|
2135
|
+
Renders the wizard menu
|
2136
|
+
|
2137
|
+
@method render
|
2138
|
+
@param {Array} wizards The wizards list
|
2139
|
+
@static
|
2140
|
+
**/
|
2141
|
+
WizardMenu.render = function (wizards) {
|
2142
|
+
var $menu = $("<div>").addClass("sideshow-wizard-menu");
|
2143
|
+
this.$el = $menu;
|
2144
|
+
var $title = $("<h1>").addClass("sideshow-wizard-menu-title");
|
2145
|
+
$menu.append($title);
|
2146
|
+
|
2147
|
+
if (wizards.length > 0) {
|
2148
|
+
var $wizardsList = $("<ul>");
|
2149
|
+
|
2150
|
+
//Extracting this function to avoid the JSHint warning W083
|
2151
|
+
|
2152
|
+
function setClick($wiz, wizard) {
|
2153
|
+
$wiz.click(function () {
|
2154
|
+
WizardMenu.hide(function () {
|
2155
|
+
wizard.prepareAndPlay();
|
2156
|
+
});
|
2157
|
+
});
|
2158
|
+
}
|
2159
|
+
|
2160
|
+
for (var w = 0; w < wizards.length; w++) {
|
2161
|
+
var wiz = wizards[w];
|
2162
|
+
var $wiz = $("<li>");
|
2163
|
+
var $wizTitle = $("<h2>").text(wiz.title);
|
2164
|
+
|
2165
|
+
var description = wiz.description;
|
2166
|
+
description.length > 100 && (description = description.substr(0, 100) + "...");
|
2167
|
+
|
2168
|
+
var $wizDescription = $("<span>").addClass("sideshow-wizard-menu-item-description").text(description);
|
2169
|
+
var $wizEstimatedTime = $("<span>").addClass("sideshow-wizard-menu-item-estimated-time").text(wiz.estimatedTime);
|
2170
|
+
$wiz.append($wizEstimatedTime, $wizTitle, $wizDescription);
|
2171
|
+
$wizardsList.append($wiz);
|
2172
|
+
|
2173
|
+
setClick($wiz, wiz);
|
2174
|
+
}
|
2175
|
+
$menu.append($wizardsList);
|
2176
|
+
} else {
|
2177
|
+
$("<div>").addClass("sideshow-no-wizards-available").text(getString(strings.noAvailableWizards)).appendTo($menu);
|
2178
|
+
}
|
2179
|
+
|
2180
|
+
$body.append($menu);
|
2181
|
+
};
|
2182
|
+
|
2183
|
+
/**
|
2184
|
+
Shows the wizard menu
|
2185
|
+
|
2186
|
+
@method show
|
2187
|
+
@param {Array} wizards The wizards list
|
2188
|
+
@static
|
2189
|
+
**/
|
2190
|
+
WizardMenu.show = function (wizards, title) {
|
2191
|
+
if (wizards.length == 1 && SS.config.autoSkipIntro) wizards[0].prepareAndPlay();
|
2192
|
+
else {
|
2193
|
+
SS.setEmptySubject();
|
2194
|
+
Mask.CompositeMask.singleInstance.update(Subject.position, Subject.dimension, Subject.borderRadius);
|
2195
|
+
Mask.CompositeMask.singleInstance.fadeIn();
|
2196
|
+
|
2197
|
+
WizardMenu.render(wizards);
|
2198
|
+
|
2199
|
+
if (title) this.setTitle(title);
|
2200
|
+
else this.setTitle(getString(strings.availableWizards));
|
2201
|
+
}
|
2202
|
+
};
|
2203
|
+
|
2204
|
+
/**
|
2205
|
+
Hides the wizard menu
|
2206
|
+
|
2207
|
+
@method hide
|
2208
|
+
@param {Function} callback The callback to be called after hiding the menu
|
2209
|
+
@static
|
2210
|
+
**/
|
2211
|
+
WizardMenu.hide = function (callback) {
|
2212
|
+
var menu = this,
|
2213
|
+
$el = menu.$el;
|
2214
|
+
|
2215
|
+
$el && $el.addClass("sideshow-menu-closed");
|
2216
|
+
setTimeout(function () {
|
2217
|
+
$el && $el.hide();
|
2218
|
+
if (callback) callback();
|
2219
|
+
}, longAnimationDuration);
|
2220
|
+
};
|
2221
|
+
|
2222
|
+
WizardMenu.setTitle = function (title) {
|
2223
|
+
this.$el.find(".sideshow-wizard-menu-title").text(title);
|
2224
|
+
};
|
2225
|
+
|
2226
|
+
/**
|
2227
|
+
Initializes Sideshow
|
2228
|
+
|
2229
|
+
@method init
|
2230
|
+
@static
|
2231
|
+
**/
|
2232
|
+
SS.init = function () {
|
2233
|
+
$window = $(global);
|
2234
|
+
$document = $(global.document);
|
2235
|
+
$body = $("body", global.document);
|
2236
|
+
registerGlobalHotkeys();
|
2237
|
+
Polling.start();
|
2238
|
+
Mask.CompositeMask.singleInstance.init();
|
2239
|
+
flags.lockMaskUpdate = true;
|
2240
|
+
Mask.CompositeMask.singleInstance.render();
|
2241
|
+
};
|
2242
|
+
|
2243
|
+
/**
|
2244
|
+
Receives a function with just a multiline comment as body and converts to a here-document string
|
2245
|
+
|
2246
|
+
@method heredoc
|
2247
|
+
@param {Function} A function without body but a multiline comment
|
2248
|
+
@return {String} A multiline string
|
2249
|
+
@static
|
2250
|
+
**/
|
2251
|
+
SS.heredoc = function (fn) {
|
2252
|
+
return fn.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1];
|
2253
|
+
}
|
2254
|
+
|
2255
|
+
/**
|
2256
|
+
Stops and Closes Sideshow
|
2257
|
+
|
2258
|
+
@method closes
|
2259
|
+
@static
|
2260
|
+
**/
|
2261
|
+
SS.close = function () {
|
2262
|
+
if (!currentWizard) WizardMenu.hide();
|
2263
|
+
|
2264
|
+
DetailsPanel.singleInstance.fadeOut();
|
2265
|
+
|
2266
|
+
this.CloseButton.singleInstance.fadeOut();
|
2267
|
+
Arrows.fadeOut();
|
2268
|
+
|
2269
|
+
setTimeout(function () {
|
2270
|
+
if (Mask.CompositeMask.singleInstance.status === AnimationStatus.VISIBLE || Mask.CompositeMask.singleInstance.status === AnimationStatus.FADING_IN) Mask.CompositeMask.singleInstance.fadeOut();
|
2271
|
+
|
2272
|
+
Mask.SubjectMask.singleInstance.fadeOut();
|
2273
|
+
|
2274
|
+
}, longAnimationDuration);
|
2275
|
+
|
2276
|
+
removeDOMGarbage();
|
2277
|
+
Polling.clear();
|
2278
|
+
SS.ControlVariables.clear();
|
2279
|
+
unregisterInnerHotkeys();
|
2280
|
+
currentWizard = null;
|
2281
|
+
flags.running = false;
|
2282
|
+
};
|
2283
|
+
|
2284
|
+
SS.runWizard = function (name) {
|
2285
|
+
var wiz = wizards.filter(function (w) {
|
2286
|
+
return w.name === name
|
2287
|
+
})[0];
|
2288
|
+
currentWizard = wiz;
|
2289
|
+
if (wiz) {
|
2290
|
+
if (wiz.isEligible()) wiz.play();
|
2291
|
+
else if (wiz.preparation) wiz.preparation(function () {
|
2292
|
+
setTimeout(function () {
|
2293
|
+
wiz.play();
|
2294
|
+
}, 1000);
|
2295
|
+
});
|
2296
|
+
else throw new SSException("204", "This wizard hasn't preparation.");
|
2297
|
+
} else throw new SSException("204", "There's no wizard with name " + name + ".");
|
2298
|
+
};
|
2299
|
+
|
2300
|
+
SS.gotoStep = function () {
|
2301
|
+
var firstArg = arguments[0],
|
2302
|
+
steps = currentWizard._storyline.steps,
|
2303
|
+
destination;
|
2304
|
+
|
2305
|
+
flags.skippingStep = true;
|
2306
|
+
|
2307
|
+
//First argument is the step position (1-based)
|
2308
|
+
if (typeof firstArg == "number") {
|
2309
|
+
if (firstArg <= steps.length) destination = steps[firstArg - 1];
|
2310
|
+
else throw new SSException("401", "There's no step in the storyline with position " + firstArg + ".");
|
2311
|
+
} //First argument is the step name
|
2312
|
+
else if (typeof firstArg == "string") {
|
2313
|
+
destination = steps.filter(function (i) {
|
2314
|
+
return i.name === firstArg;
|
2315
|
+
})[0];
|
2316
|
+
|
2317
|
+
if (!destination) throw new SSException("401", "There's no step in the storyline with name " + firstArg + ".");
|
2318
|
+
}
|
2319
|
+
setTimeout(function () {
|
2320
|
+
currentWizard.next(null, destination);
|
2321
|
+
}, 100);
|
2322
|
+
};
|
2323
|
+
|
2324
|
+
/**
|
2325
|
+
A trick to use the composite mask to simulate the behavior of a solid mask, setting an empty subject
|
2326
|
+
|
2327
|
+
@method setEmptySubject
|
2328
|
+
@static
|
2329
|
+
**/
|
2330
|
+
SS.setEmptySubject = function () {
|
2331
|
+
flags.lockMaskUpdate = true;
|
2332
|
+
Subject.obj = null;
|
2333
|
+
Subject.updateInfo({
|
2334
|
+
dimension: {
|
2335
|
+
width: 0,
|
2336
|
+
height: 0
|
2337
|
+
},
|
2338
|
+
position: {
|
2339
|
+
x: 0,
|
2340
|
+
y: 0
|
2341
|
+
},
|
2342
|
+
borderRadius: {
|
2343
|
+
leftTop: 0,
|
2344
|
+
rightTop: 0,
|
2345
|
+
leftBottom: 0,
|
2346
|
+
rightBottom: 0
|
2347
|
+
}
|
2348
|
+
});
|
2349
|
+
};
|
2350
|
+
|
2351
|
+
/**
|
2352
|
+
Sets the current subject
|
2353
|
+
|
2354
|
+
@method setSubject
|
2355
|
+
@param {Object} subj
|
2356
|
+
@static
|
2357
|
+
**/
|
2358
|
+
SS.setSubject = function (subj, subjectChanged) {
|
2359
|
+
if (subj.constructor === String) subj = $(subj);
|
2360
|
+
|
2361
|
+
if (subj instanceof $ && subj.length > 0) {
|
2362
|
+
if (subj.length === 1) {
|
2363
|
+
Subject.obj = subj;
|
2364
|
+
Subject.updateInfo();
|
2365
|
+
flags.lockMaskUpdate = false;
|
2366
|
+
} else throw new SSException("101", "A subject must have only one element. Multiple elements by step will be supported in future versions of Sideshow.");
|
2367
|
+
}
|
2368
|
+
else if (subjectChanged) SS.setEmptySubject();
|
2369
|
+
else throw new SSException("100", "Invalid subject.");
|
2370
|
+
};
|
2371
|
+
|
2372
|
+
/**
|
2373
|
+
Registers a wizard
|
2374
|
+
|
2375
|
+
@method registerWizard
|
2376
|
+
@param {Object} wizardConfig
|
2377
|
+
@return {Object} The wizard instance
|
2378
|
+
@static
|
2379
|
+
**/
|
2380
|
+
SS.registerWizard = function (wizardConfig) {
|
2381
|
+
var wiz = Wizard.build(wizardConfig);
|
2382
|
+
wizards.push(wiz);
|
2383
|
+
return wiz;
|
2384
|
+
};
|
2385
|
+
|
2386
|
+
/**
|
2387
|
+
Registers a wizard
|
2388
|
+
|
2389
|
+
@method registerWizard
|
2390
|
+
@param {boolean} onlyNew Checks only recently added wizards
|
2391
|
+
@return {Array} The eligible wizards list
|
2392
|
+
@static
|
2393
|
+
**/
|
2394
|
+
SS.getElegibleWizards = function (onlyNew) {
|
2395
|
+
var eligibleWizards = [];
|
2396
|
+
var somethingNew = false;
|
2397
|
+
for (var w = 0; w < wizards.length; w++) {
|
2398
|
+
var wiz = wizards[w];
|
2399
|
+
if (wiz.isEligible()) {
|
2400
|
+
if (!wiz.isAlreadyWatched()) somethingNew = true;
|
2401
|
+
eligibleWizards.push(wiz);
|
2402
|
+
}
|
2403
|
+
}
|
2404
|
+
|
2405
|
+
return !onlyNew || somethingNew ? eligibleWizards : [];
|
2406
|
+
};
|
2407
|
+
|
2408
|
+
/**
|
2409
|
+
Checks if there are eligible wizards, if exists, shows the wizard menu
|
2410
|
+
|
2411
|
+
@method showWizardsList
|
2412
|
+
@param {boolean} onlyNew Checks only recently added wizards
|
2413
|
+
@return {boolean} Returns a boolean indicating whether there is some wizard available
|
2414
|
+
@static
|
2415
|
+
**/
|
2416
|
+
SS.showWizardsList = function () {
|
2417
|
+
var firstArg = arguments[0];
|
2418
|
+
var title = arguments[1];
|
2419
|
+
var onlyNew = typeof firstArg == "boolean" ? false : firstArg;
|
2420
|
+
var wizards = firstArg instanceof Array ? firstArg : this.getElegibleWizards(onlyNew);
|
2421
|
+
|
2422
|
+
WizardMenu.show(wizards, title);
|
2423
|
+
|
2424
|
+
return wizards.length > 0;
|
2425
|
+
};
|
2426
|
+
|
2427
|
+
/**
|
2428
|
+
Shows a list with the related wizards
|
2429
|
+
|
2430
|
+
@method showRelatedWizardsList
|
2431
|
+
@param {Object} completedWizard The recently completed wizard
|
2432
|
+
@return {boolean} Returns a boolean indicating whether there is some related wizard available
|
2433
|
+
@static
|
2434
|
+
**/
|
2435
|
+
SS.showRelatedWizardsList = function (completedWizard) {
|
2436
|
+
var relatedWizardsNames = completedWizard.relatedWizards;
|
2437
|
+
if (!relatedWizardsNames) return false;
|
2438
|
+
|
2439
|
+
//Gets only related tutorials which are eligible or have a preparation function
|
2440
|
+
var relatedWizards = wizards.filter(function (w) {
|
2441
|
+
return relatedWizardsNames.indexOf(w.name) > -1 && (w.isEligible() || w.preparation);
|
2442
|
+
});
|
2443
|
+
if (relatedWizards.length == 0) return false;
|
2444
|
+
|
2445
|
+
Polling.clear();
|
2446
|
+
SS.ControlVariables.clear();
|
2447
|
+
SS.showWizardsList(relatedWizards, getString(strings.relatedWizards));
|
2448
|
+
|
2449
|
+
return true;
|
2450
|
+
};
|
2451
|
+
|
2452
|
+
/**
|
2453
|
+
The close button for the wizard
|
2454
|
+
|
2455
|
+
@class CloseButton
|
2456
|
+
@@singleton
|
2457
|
+
@extends FadableItem
|
2458
|
+
**/
|
2459
|
+
SS.CloseButton = jazz.Class().extending(FadableItem).singleton;
|
2460
|
+
|
2461
|
+
/**
|
2462
|
+
Renders the close button
|
2463
|
+
|
2464
|
+
@method render
|
2465
|
+
**/
|
2466
|
+
SS.CloseButton.method("render", function () {
|
2467
|
+
this.$el = $("<button>").addClass("sideshow-close-button").text(getString(strings.close));
|
2468
|
+
this.$el.click(function () {
|
2469
|
+
SS.close();
|
2470
|
+
});
|
2471
|
+
this.callSuper("render");
|
2472
|
+
});
|
2473
|
+
|
2474
|
+
/**
|
2475
|
+
Starts Sideshow
|
2476
|
+
|
2477
|
+
@method start
|
2478
|
+
@param {Object} config The config object for Sideshow
|
2479
|
+
**/
|
2480
|
+
SS.start = function (config) {
|
2481
|
+
config = config || {};
|
2482
|
+
if (!flags.running) {
|
2483
|
+
var onlyNew = "onlyNew" in config && !! config.onlyNew;
|
2484
|
+
var listAll = "listAll" in config && !! config.listAll;
|
2485
|
+
|
2486
|
+
if (listAll) SS.showWizardsList(wizards.filter(function (w) {
|
2487
|
+
return w.isEligible() || w.preparation;
|
2488
|
+
}));
|
2489
|
+
else SS.showWizardsList(onlyNew);
|
2490
|
+
|
2491
|
+
this.CloseButton.singleInstance.render();
|
2492
|
+
this.CloseButton.singleInstance.fadeIn();
|
2493
|
+
|
2494
|
+
registerInnerHotkeys();
|
2495
|
+
flags.running = true;
|
2496
|
+
|
2497
|
+
Polling.enqueue("check_composite_mask_screen_changes", function () {
|
2498
|
+
Mask.CompositeMask.singleInstance.pollForScreenChanges();
|
2499
|
+
});
|
2500
|
+
}
|
2501
|
+
};
|
2502
|
+
|
2503
|
+
|
2504
|
+
|
2505
|
+
//Tries to register the Global Access Point
|
2506
|
+
if (global[globalObjectName] === undefined) {
|
2507
|
+
global[globalObjectName] = SS;
|
2508
|
+
} else throw new SSException("1", "The global access point \"Sideshow\" is already being used.");
|
2509
|
+
});
|
2510
|
+
})(this, jQuery, Jazz, Markdown);
|