1 /*
  2  * Standalone Wait Dialog (extracted from jQuery Mobile 1.4.3)
  3  * 
  4  * stdlne-wait-dlg
  5  *
  6  * <div class="stdlne-wait-dlg stdlne-style-b stdlne-wait-dlg-verbose"><span class="stdlne-icon"></span><h1>title</h1></div>
  7  * 
  8  * version 0.2
  9  * Copyright (C) 2015 russa, DFKI GmbH
 10  * MIT license
 11  * 
 12  * Dependencies:
 13  *   * document (DOM): body, head
 14  *     * createElement: div, span, h1
 15  *     * appendChild
 16  *     * classList
 17  *     * className
 18  *     * childNodes
 19  */
 20 
 21 //(function(module){
 22 define(['module'], function(module){
 23 
 24 //configurable via requirejs' module:
 25 // * activeClass: (String) the CSS for setting the dialog to 'active' state
 26 //                DEFAULT: "stdlne-active"
 27 // * fileName: (String) the name of the CSS styles file
 28 //                DEFAULT: "stdlne-wait-dlg.css"
 29 // * defaultType: (String) default type for the wait-dialog: ["small" | "verbose"]
 30 //                DEFAULT: "verbose"
 31 // * defaultTheme: (String) default theme for the wait-dialog: ["a" | "b"]
 32 //                DEFAULT: "a"
 33 // * defaultTitle: (String) default title / caption for the wait-dialog
 34 //                DEFAULT: ""
 35 
 36 /** 
 37  * Temporary variable for retrieving configuration values.
 38  * 
 39  * @private
 40  * @memberOf StandaloneWaitDialog.prototype
 41  */
 42 var tmpConfig = module.config().activeClass;
 43 
 44 /** 
 45  * @type String
 46  * @private
 47  * @memberOf StandaloneWaitDialog.prototype
 48  */
 49 var activatorClass = tmpConfig? tmpConfig : 'stdlne-active';
 50 
 51 tmpConfig = module.config().fileName;
 52 /** 
 53  * @type String
 54  * @private
 55  * @memberOf StandaloneWaitDialog.prototype
 56  */
 57 var defaultStyleUrl = tmpConfig? tmpConfig : 'stdlne-wait-dlg.css';
 58 
 59 
 60 
 61 /** 
 62  * @constant
 63  * @private
 64  * @memberOf StandaloneWaitDialog.prototype
 65  */
 66 var types = {
 67 		'small':   'stdlne-wait-dlg-small',
 68 		'verbose': 'stdlne-wait-dlg-verbose'
 69 };
 70 
 71 //set default type
 72 tmpConfig = module.config().defaultType;
 73 if(!tmpConfig){
 74 	tmpConfig = 'stdlne-wait-dlg-verbose';
 75 } else {
 76 	tmpConfig = types[tmpConfig];
 77 }
 78 types['default'] = tmpConfig;
 79 
 80 
 81 
 82 /** 
 83  * @constant
 84  * @private
 85  * @memberOf StandaloneWaitDialog.prototype
 86  */
 87 var themes = {
 88 		'a': 'stdlne-style-a',
 89 		'b': 'stdlne-style-b'
 90 };
 91 
 92 //set default theme
 93 tmpConfig = module.config().defaultTheme;
 94 if(!tmpConfig){
 95 	tmpConfig = 'stdlne-style-a';
 96 } else {
 97 	tmpConfig = themes[tmpConfig];
 98 }
 99 themes['default'] = tmpConfig;
100 
101 
102 //setting up the default title (used in StandaloneWaitDialog class)
103 /** 
104  * @private
105  * @type String
106  * @memberOf StandaloneWaitDialog.prototype
107  */
108 var tmpDefaultTitle = module.config().defaultTitle;
109 
110 if(!tmpDefaultTitle){
111 	tmpDefaultTitle = '';
112 }
113 
114 
115 
116 tmpConfig = module.config().defaultId;
117 /** 
118  * ID for default wait dialog (i.e. when show() is used without ID argument).
119  * 
120  * @type String
121  * @private
122  * @memberOf StandaloneWaitDialog.prototype
123  */
124 var defaultDialogId = tmpConfig? tmpConfig : 'default-stdlne-wait-dlg';
125 
126 
127 /**
128  * HELPER set type, theme classes to a wait-dialog DOM element
129  *  
130  * @private
131  * @memberOf StandaloneWaitDialog.prototype
132  */
133 function _applyClasses(domEl, typeCl, themeCl, isActivate){
134 
135 	if(typeCl){
136 		
137 		if(typeCl !== types['small']){
138 			domEl.classList.remove(types['small']);
139 		} else if(typeCl !== types['verbose']){
140 			domEl.classList.remove(types['verbose']);
141 		}
142 		domEl.classList.add(typeCl);
143 	}
144 	
145 	if(themeCl){
146 		
147 		if(themeCl !== themes['a']){
148 			domEl.classList.remove(themes['a']);
149 		} else if(themeCl !== themes['b']){
150 			domEl.classList.remove(themes['b']);
151 		}
152 		
153 		domEl.classList.add(themeCl);
154 	}
155 
156 	//apply 'active' class
157 	if(isActivate){
158 		domEl.classList.add(activatorClass);
159 	}
160 }
161 
162 /**
163  * Default options for wait dialog.
164  * 
165  * <p>
166  * Different wait dialogs are managed 
167  * in the <code>defaultOptions</code> map
168  * (map-key is their ID).
169  * 
170  * @class
171  * @see StandaloneWaitDialog#defaultOptions
172  */
173 function Options(options, owner){
174 	this.set(options, owner);
175 }
176 
177 Options.prototype = {
178 	
179 	set: function(options, owner){
180 		
181 		if(owner){
182 			this._owner = owner;
183 		}
184 		
185 		if(options.theme){
186 			this._theme = themes[options.theme];
187 		}
188 		if(options.type){
189 			this._type = types[options.type];
190 		}
191 		
192 		if(options.style){
193 			this._style = options.style;
194 		}
195 		if(options.classes){
196 			var cl = options.classes;
197 			if(typeof cl === 'string'){
198 				cl = cl.split(' ');
199 			}
200 			this._classes = cl;
201 		}
202 	},
203 	getTheme: function(){
204 		if(typeof this._theme !== 'undefined'){
205 			return this._theme;
206 		}
207 		return this._owner.defaultTheme;
208 	},
209 	getType: function(){
210 		if(typeof this._type !== 'undefined'){
211 			return this._type;
212 		}
213 		return this._owner.defaultType;
214 	},
215 	getTitle: function(){
216 		if(typeof this._title !== 'undefined'){
217 			return this._title;
218 		}
219 		return this._owner.defaultTitle;
220 	},
221 	getClasses: function(){
222 		if(typeof this._classes !== 'undefined'){
223 			return this._classes;
224 		}
225 		return;
226 	},
227 	getStyle: function(){
228 		if(typeof this._style !== 'undefined'){
229 			return this._style;
230 		}
231 		return;
232 	}
233 };
234 
235 /**
236  * The Wait Dialog interface.
237  * 
238  * <p>
239  * Allows to create multiple wait dialogs.
240  * 
241  * @class
242  * @singleton
243  */
244 function StandaloneWaitDialog(){
245 	if(typeof window !== 'undefined' && this === window){
246 		return new StandaloneWaitDialog();
247 	}
248 	return this;
249 };
250 
251 StandaloneWaitDialog.prototype = {
252 		defaultTitle: tmpDefaultTitle,
253 		defaultTheme: themes['default'],
254 		defaultType:  types['default'],
255 		styleUrl:     defaultStyleUrl,
256 		_isCssLoaded: false,
257 		_getDom: function(id){//returns: Array
258 			
259 			if(id){//if id: only return one element (in an array)
260 				var element = document.getElementById(id);
261 				return element? [element] : [];
262 			}
263 			return document.getElementsByClassName('stdlne-wait-dlg');
264 			
265 		},
266 		_loadStyle: function(url, isForceReloading){
267 			
268 			if(typeof url === 'boolean'){
269 				isForceReloading = url;
270 				url = void(0);
271 			}
272 			
273 			if(this._isCssLoaded && !isForceReloading){
274 				return;
275 			}
276 
277 			//NOTE do not use onload for LINK:
278 			//     not supported by all browsers, so no
279 			//     reliable detection via onload possible.
280 			//     -> just use simple FLAG
281 			//     (application must deal with possible loading-problems)
282 			this._isCssLoaded = true;
283 			
284 			if(!url){
285 				url = this.styleUrl;
286 			}
287 			
288 			var link = document.createElement("link");
289 			link.type = "text/css";
290 			link.rel  = "stylesheet";
291 			link.href = url;
292 			document.getElementsByTagName("head")[0].appendChild(link);
293 			
294 		},
295 		_getDefaults: function(id){
296 			return defaultOptions.get(id);
297 		},
298 		setDefaults: function(id, options){//NOTE not allowed for default-wait-dialog (-> set properties on StandaloneWaitDialog instance itself!)
299 			
300 			if(!id || !options){
301 				throw new Error('Invalid argument(s): '+(!id? 'missing ID (got: '+id+')':'')+(!options? 'missing options (got: '+options+')':''));
302 			}
303 			
304 			var defs = defaultOptions.get(id);
305 			if(!defs){
306 				defs = new Options(options, this);
307 				defaultOptions.set(id, defs);
308 			}
309 			else {
310 				defs.set(options);
311 			}
312 		},
313 		create: function(id, options){
314 			
315 			var _id = id;
316 			
317 			var container = document.createElement("div");
318 			container.classList.add('stdlne-wait-dlg');
319 			
320 			if(_id){
321 				
322 				container.id = _id;
323 				
324 				if(options){
325 					this.setDefaults(_id, options);
326 				}
327 			}
328 			
329 			var icon = document.createElement("span");
330 			icon.className = 'stdlne-icon';
331 			
332 			var caption = document.createElement("h1");
333 //			caption.className = 'stdlne-caption';
334 			
335 			container.appendChild(icon);
336 			container.appendChild(caption);
337 			
338 			return container;
339 		},
340 		/**
341 		 * 
342 		 * @param {String|Object} [title] OPTIONAL
343 		 * 			if String: the tile to show in the dialog (NOTE: only visible, if dialog-style is "verbose")
344 		 * 			if Object: an options object with (OPTIONAL) properties:
345 		 * 				* option.title {String} the title<br>
346 		 *				* option.id {String} the ID attribute of the dialog to show<br>
347 		 *				* option.type  {String} the type for the dialog to show: ["small" | "verbose"], default: "verbose"<br>
348 		 *				* option.theme {String} the theme (style) for the dialog to show: ["a" | "b"], default: "a"<br>
349 		 * @param {String} [id] OPTIONAL
350 		 * 				an ID for the dialog to show (if omitted the default dialog will be shown)
351 		 */
352 		show: function(title, id, options){
353 			
354 			var _title, _id, _type, _theme, _defaults;
355 			
356 			var _style, _classes, _elStyle;
357 			
358 			//re-map argument: is 2nd argument the options object?
359 			if(!options && id !== null && typeof id === 'object'){
360 				options = id;
361 				id = void(0);
362 			}
363 
364 			if(id){
365 				_id = id;
366 			}
367 			
368 			if(title){
369 				
370 				if(typeof title === 'string'){
371 					_title = title;
372 				}
373 				else {
374 					_title = title.title;
375 					//NOTE if there is also an options object (3rd argument),
376 					//     it will be overwritten by the first-argument's options!
377 					_type  = typeof title.type  !== 'undefined'? types[title.type]   : _type;
378 					_theme = typeof title.theme !== 'undefined'? themes[title.theme] : _theme;
379 					_id    = typeof title.id    !== 'undefined'? title.id            : id;
380 				}
381 				
382 			} else {
383 				
384 				_title = this.defaultTitle;
385 			}
386 			
387 			if(_id){
388 				
389 				if(options){
390 					this.setDefaults(_id, options);
391 				}
392 				
393 				_defaults = this._getDefaults(_id);
394 				if(_defaults){
395 					if(_defaults.getType() && typeof _type === 'undefined'){
396 						_type = _defaults.getType();
397 					}
398 					if(_defaults.getTheme() && typeof _theme === 'undefined'){
399 						_theme = _defaults.getTheme();
400 					}
401 					if(_defaults.getTitle() && typeof _title === 'undefined'){
402 						_title = _defaults.getTitle();
403 					}
404 
405 					if(_defaults.getStyle() && typeof _style === 'undefined'){
406 						_style = _defaults.getStyle();
407 					}
408 					if(_defaults.getClasses() && typeof _classes === 'undefined'){
409 						_classes = _defaults.getClasses();
410 					}
411 				}
412 			}
413 			
414 			if(typeof _type === 'undefined'){
415 				_type = this.defaultType;
416 			}
417 			if(typeof _theme === 'undefined'){
418 				_theme = this.defaultTheme;
419 			}
420 			if(typeof _title === 'undefined'){
421 				_title = this.defaultTitle;
422 			}
423 			
424 			if(typeof _id === 'undefined'){
425 				_id = defaultDialogId;
426 			}
427 			
428 			var list = this._getDom(_id);
429 			var size = list.length;
430 			if(size < 1){
431 				list = [this.create(_id, options)];
432 				size = 1;
433 				document.body.appendChild(list[0]);
434 			}
435 			
436 			var curr;
437 			for(var i=0; i < size; ++i){
438 				curr = list[i];
439 				_applyClasses(curr, _type, _theme, true);
440 				curr.childNodes.item(1).textContent = _title;
441 				
442 				if(typeof _style !== 'undefined'){
443 					//TODO should this just overwrite the complete style-attribute?
444 					//     ...because now, the removal (see hide()) is somewhat hacked 
445 					//        and also may "overlook" added semicolon...
446 					_elStyle = curr.getAttribute('style');
447 					if(!_elStyle){
448 						_elStyle = _style;
449 					}
450 					else  {
451 						_elStyle +=';' + _style;
452 					}
453 					curr.setAttribute('style', _elStyle);
454 				}
455 				if(typeof _classes !== 'undefined'){
456 					curr.classList.add.apply(curr.classList, _classes);
457 				}
458 			}
459 		},
460 		/**
461 		 * 
462 		 * @param {String} [id] OPTIONAL
463 		 * 			the ID for the dialog element (if omitted all dialogs will be hidden)
464 		 */
465 		hide: function(id){
466 			var list = this._getDom(id), curr, defs, currStyle;
467 			for(var i=0,size = list.length; i < size; ++i){
468 				curr = list[i];
469 				curr.classList.remove(activatorClass);
470 				
471 				if(curr.id){
472 					
473 					defs = this._getDefaults(curr.id);
474 					
475 					//remove style and classes from defaults
476 					if(defs){
477 						currStyle = curr.getAttribute('style');
478 						if(defs.getStyle() && currStyle){
479 							curr.setAttribute('style', currStyle.replace(defs.getStyle(), '') );
480 						}
481 						if(defs.getClasses()){
482 							curr.classList.removeapply(curr.classList, defs.getClasses());
483 						}
484 					}
485 				}
486 			}
487 		}
488 };
489 //module.waitDialog = dlg;
490 
491 /** 
492  * @private
493  * @memberOf StandaloneWaitDialog.prototype
494  */
495 var dlg = new StandaloneWaitDialog();
496 //dlg.newInstance = StandaloneWaitDialog;
497 
498 /** 
499  * @private
500  * @memberOf StandaloneWaitDialog.prototype
501  */
502 var defaultOptions = {
503 	get: function(id){
504 		if(id){
505 			return this[_getKey(id)];
506 		}
507 		return this['$'];
508 	},
509 	set: function(id, options){
510 		if(!id){
511 			id = '$';
512 		}
513 		this[_getKey(id)] = options;
514 	},
515 	'$': new Options({}, dlg)
516 };
517 
518 /** 
519  * @private
520  * @memberOf StandaloneWaitDialog.prototype
521  */
522 function _getKey(id){
523 	return '$$'+id;
524 }
525 
526 return dlg;
527 });
528 
529 //})(window);
530