PageSourceSearch

https://www.multisport-witten.de/valhalla-js-3.5/data.js

js multisport-witten.de collected 2026-10-04 03:43:53 UTC 142,337 bytes, 3,751 lines download raw bytes

1//# sourceURL=data.js
2//# COPYRIGHT Kraneburg Productions
3
4$(document).on('newField', function(){
5    console.log('NEW FIELD');
6    
7})
8
9//console.log('data.js loading');
10Storage.prototype.setObj = function(key, obj) {
11    return this.setItem(key, JSON.stringify(obj))
12};
13
14Storage.prototype.getObj = function(key) {
15    var resultObj = JSON.parse(this.getItem(key));
16    if (resultObj == null) return undefined;
17    else return resultObj;
18}
19
20
21
22
23STATUS_UNCHANGED = 0;
24STATUS_CHANGED = 1;
25STATUS_NEW = 2;
26STATUS_CHANGING = 3;
27STATUS_DELETED = 6;
28STATUS_INVALID = 10;    
29
30// Storage for Data in DOM 
31// ++ ADD XML FILE SUPPORT
32XMLStorage = new Object();
33XMLStorage.getEntity = function(view, storage) {
34	// storage is the XML element containing data
35        // view might have xmlRoot value where all its entities are stored       
36        
37	var entity = new Entity();
38	entity.setView( Model.views[view.name] );
39	entity.storage = XMLStorage;
40	entity.storageSource = storage;
41	
42        entity.reload = function() {
43            if (typeof entity.storageSource === 'undefined') return;    // NEw ENTITY, NOT STORED YET, DOES NOT HAVE XML TAG YET, USES DEFAULT VALUES FROM VIEW
44            for (var fieldName in view.fields) {
45		if ( isset(view.fields[fieldName].xmlStorage) ) {
46			
47			var storageParams = view.fields[fieldName].xmlStorage;
48
49			if (isset(storageParams)) {
50				
51				//alert(entity.fields[fieldName].status());
52				//alert(entity.view.name);
53				
54				if (isset(storageParams.selector) == false || storageParams.selector == '' || storageParams.selector == 'this')
55					var element = $(entity.storageSource);
56				else var element = $(entity.storageSource).find(storageParams.selector);
57
58
59                                if (typeof storageParams.type !== 'undefined' && storageParams.type == 'tag' ) {
60                                    var outerHTML = element[0].outerHTML;
61                                    var oldTag = outerHTML.split(' ')[0];
62                                    entity.data[fieldName] = convertValue( oldTag.substr(1) , 'string', view.fields[fieldName].type );	
63                                } 
64                                else if ( isset(storageParams.attribute) ) {
65					entity.data[fieldName] = convertValue( element.attr(storageParams.attribute) , 'string', view.fields[fieldName].type );										
66				} 
67				else if ( isset(storageParams.toggleAttribute) ) {
68					entity.data[fieldName] = convertValue( element.hasAttr(storageParams.toggleAttribute) , 'boolean', view.fields[fieldName].type );											
69				}				
70				else if ( isset(storageParams.toggleClass) ) {
71					entity.data[fieldName] = convertValue( element.hasClass(storageParams.toggleClass) , 'boolean', view.fields[fieldName].type );										
72				}
73                                else entity.data[fieldName] = convertValue( element.html() , 'string', view.fields[fieldName].type );
74
75			}						
76		}		
77            }            
78            
79        }
80	entity.reload();
81        	
82	return entity;	
83}
84
85XMLStorage.store = function( entity ) {
86        if ( entity.trash() == true ) {
87            if (typeof entity.storageSource !== 'undefined') {
88                entity.storageSource.remove();                
89            } // else NEw ENTITY, NOT STORED YET, DOES NOT HAVE XML TAG YET
90            $(entity).triggerHandler('deleted');
91            $(document).triggerHandler('entityDeleted', entity);
92//				self = undefined;
93            entity.status(STATUS_DELETED);
94            delete(entity);
95            return;
96        }
97	
98	if ( entity.status() == STATUS_UNCHANGED ) return true;
99        if (typeof entity.storageSource == 'undefined') {
100            // NEw ENTITY, NOT STORED YET, DOES NOT HAVE XML TAG YET
101            entity.storageSource = $('<' + entity.view.xmlEntityTag + '></' + entity.view.xmlEntityTag + '>');
102            $(entity.view.xmlRoot).append( entity.storageSource );
103        }
104        
105        
106	for (var fieldName in entity.fields) {
107		if (entity.fields[fieldName].status() != STATUS_UNCHANGED) {
108			var storageParams = entity.fields[fieldName].attribute('xmlStorage');
109			/*
110			console.log(fieldName);
111			console.log(entity.fields[fieldName].status());
112			console.log(storageParams);
113*/
114			
115			if (isset(storageParams)) {
116                            if (isset(storageParams.selector) == false || storageParams.selector == '' || storageParams.selector == 'this')
117                                    var element = $(entity.storageSource);
118                            else {
119                                var element = $(entity.storageSource).find(storageParams.selector);
120                                if (element.length == 0) {
121                                    // ELEMENT NOT FOUND, TRY TO CREATE
122                                    // selector MUST LOOK LIKE >div>ul>li
123                                    // ">" REQUIRED FOR CREATION
124                                    var parts = storageParams.selector.split('>');
125                                    var element = $(entity.storageSource);
126                                    for (var index in parts) {
127                                        if ( parts[ index ] != ''  ) {
128                                            var newElement = $('<' + parts[ index ] + '></' + parts[ index ] + '>');
129                                            element.append( newElement );
130                                            element = newElement;
131                                        }
132                                    }
133                                }
134                            }
135
136                            if (typeof storageParams.type !== 'undefined' && storageParams.type == 'tag' ) {
137                                var outerHTML = element[0].outerHTML;
138                                var oldTag = outerHTML.split(' ')[0];
139                                outerHTML = '<' + entity.get(fieldName, 'string') + outerHTML.substr( oldTag.length, outerHTML.length - oldTag.length - oldTag.length -2) + '</' + entity.get(fieldName, 'string') +'>';
140                                element[0].outerHTML = outerHTML;
141                            }                                
142                            else if (isset(storageParams.attribute)) {
143                                if (entity.get(fieldName, 'string') == '' ) { 
144                                    if ( storageParams.keep != true ) element.removeAttr( storageParams.attribute );
145                                    else element.attr( storageParams.attribute, '');
146                                }
147                                else if (entity.fields[fieldName].type == 'boolean' && entity.get(fieldName) == false)   element.removeAttr( storageParams.attribute );
148                                        else element.attr( storageParams.attribute, entity.get(fieldName, 'string'));					
149                            }                                
150                            else if (isset(storageParams.toggleAttribute)) {
151                                if (entity.get(fieldName) == 0) element.removeAttr(storageParams.toggleAttribute);
152                                else element.attr(storageParams.toggleAttribute, '');	
153                            }				
154                            else if (isset(storageParams.toggleClass)){
155                                if (entity.get(fieldName) == 0) element.removeClass(storageParams.toggleClass);
156                                    else element.addClass(storageParams.toggleClass);	
157                            }
158                            else //  SET VALUE
159                            {
160                                // REMOVE EMPTY FIELD
161                                if ( entity.get(fieldName) == '' ) {
162                                    element.remove();
163                                }
164                                // OR SET VALUE
165                                else element.html( entity.get(fieldName, 'string') );
166                            }
167                                                                    
168                                
169                                
170			}
171			
172		}
173	}
174	entity.status(STATUS_UNCHANGED); 
175	$(entity).trigger( 'stored' );
176	return true;
177	
178}
179
180
181
182
183// Storage for Data in Object 
184ObjectStorage = new Object();
185
186
187
188ObjectStorage.store = function( entity ) {
189        if ( entity.trash() == true ) {
190            if (typeof entity.storageSource !== 'undefined') {
191                entity.storageSource.remove();                
192            } // else NEw ENTITY, NOT STORED YET, DOES NOT HAVE XML TAG YET
193            $(entity).triggerHandler('deleted');
194            $(document).triggerHandler('entityDeleted', entity);
195//				self = undefined;
196            entity.status(STATUS_DELETED);
197            delete(entity);
198            return;
199        }
200	
201	if ( entity.status() == STATUS_UNCHANGED ) return true;
202        if (typeof entity.storageSource == 'undefined') {
203            // NEw ENTITY, NOT STORED YET, DOES NOT HAVE XML TAG YET
204            entity.storageSource = $('<' + entity.view.xmlEntityTag + '></' + entity.view.xmlEntityTag + '>');
205            $(entity.view.xmlRoot).append( entity.storageSource );
206        }
207        
208        
209	for (var fieldName in entity.fields) {
210		if (entity.fields[fieldName].status() != STATUS_UNCHANGED) {
211			var storageParams = entity.fields[fieldName].attribute('xmlStorage');
212			/*
213			console.log(fieldName);
214			console.log(entity.fields[fieldName].status());
215			console.log(storageParams);
216*/
217			
218			if (isset(storageParams)) {
219                            if (isset(storageParams.selector) == false || storageParams.selector == '' || storageParams.selector == 'this')
220                                    var element = $(entity.storageSource);
221                            else {
222                                var element = $(entity.storageSource).find(storageParams.selector);
223                                if (element.length == 0) {
224                                    // ELEMENT NOT FOUND, TRY TO CREATE
225                                    // selector MUST LOOK LIKE >div>ul>li
226                                    // ">" REQUIRED FOR CREATION
227                                    var parts = storageParams.selector.split('>');
228                                    var element = $(entity.storageSource);
229                                    for (var index in parts) {
230                                        if ( parts[ index ] != ''  ) {
231                                            var newElement = $('<' + parts[ index ] + '></' + parts[ index ] + '>');
232                                            element.append( newElement );
233                                            element = newElement;
234                                        }
235                                    }
236                                }
237                            }
238
239                            if (typeof storageParams.type !== 'undefined' && storageParams.type == 'tag' ) {
240                                var outerHTML = element[0].outerHTML;
241                                var oldTag = outerHTML.split(' ')[0];
242                                outerHTML = '<' + entity.get(fieldName, 'string') + outerHTML.substr( oldTag.length, outerHTML.length - oldTag.length - oldTag.length -2) + '</' + entity.get(fieldName, 'string') +'>';
243                                element[0].outerHTML = outerHTML;
244                            }                                
245                            else if (isset(storageParams.attribute)) {
246                                if (entity.get(fieldName, 'string') == '' ) { 
247                                    if ( storageParams.keep != true ) element.removeAttr( storageParams.attribute );
248                                    else element.attr( storageParams.attribute, '');
249                                }
250                                else if (entity.fields[fieldName].type == 'boolean' && entity.get(fieldName) == false)   element.removeAttr( storageParams.attribute );
251                                        else element.attr( storageParams.attribute, entity.get(fieldName, 'string'));					
252                            }                                
253                            else if (isset(storageParams.toggleAttribute)) {
254                                if (entity.get(fieldName) == 0) element.removeAttr(storageParams.toggleAttribute);
255                                else element.attr(storageParams.toggleAttribute, '');	
256                            }				
257                            else if (isset(storageParams.toggleClass)){
258                                if (entity.get(fieldName) == 0) element.removeClass(storageParams.toggleClass);
259                                    else element.addClass(storageParams.toggleClass);	
260                            }
261                            else //  SET VALUE
262                            {
263                                // REMOVE EMPTY FIELD
264                                if ( entity.get(fieldName) == '' ) {
265                                    element.remove();
266                                }
267                                // OR SET VALUE
268                                else element.html( entity.get(fieldName, 'string') );
269                            }
270                                                                    
271                                
272                                
273			}
274			
275		}
276	}
277	entity.status(STATUS_UNCHANGED); 
278	$(entity).trigger( 'stored' );
279	return true;
280	
281}
282
283
284
285
286
287
288FileCache = new Object();
289FileCache.load = function(params)
290{
291    try {
292    	var fileStorageName = 'File::' + params.url;
293	if (isset('APP_NAME')) fileStorageName = 'File::' + APP_NAME + '_' + APP_VERSION + '_' + params.url;
294	var tempDataType = params.dataType;
295	var localFileData = window.localStorage.getItem(fileStorageName);
296	
297	if (isset(localFileData) && localFileData != null) 
298	{
299		//console.log('RESTORING'); 
300		if (tempDataType == 'xml') localFileData = $.parseXML( localFileData );
301		//console.log(localFileData); 
302		params.done(localFileData); 	
303		return;
304	}
305	
306	
307	params.dataType = 'text';
308	$.ajax( params 	).done( function(data) {
309		window.localStorage.setItem(fileStorageName, data);		
310		params.dataType = tempDataType;
311		if 			(tempDataType == 'xml') 	data = $.parseXML( data );
312			else if	(tempDataType == 'json') 	data = $.parseJSON( data );
313		if (typeof params.done == 'function' ) params.done(data); 
314	});
315    } catch ( e ) { 
316        
317        console.log('Error - loading file :' + params.url);
318        throw e;
319//        alert(params.url);
320//        if (typeof params.fail == 'function' ) params.fail(); 
321    }
322
323	
324}
325
326function Query (queryString) {	
327	// ###
328	
329	this.view = undefined;
330        
331	this.host = undefined;      // retrieve data from different server, keeping filter and string provider/data/view/... 
332	this.path = undefined;      // becomes "'provider/plugins/data/view/VIEWNAME/' if empty 
333	
334	this.filters = [];
335	this.sorters = [];	
336	this.limiters = [];	
337        this.relations = [];
338
339	/*
340	this.toXML = function() {
341		var xml = '';
342		foreach (index in this.filters) {
343			var filter = this.filters[filter];
344			xml += filter.key + ' ' + filter.operator + ' "' + filter.value + '"';
345		}
346		return xml;
347	};
348	*/
349	this.toURL = function() {
350            
351            
352            var hostURL = 'provider/plugins/data/';           
353            if (typeof this.path !== 'undefined' && this.path != '') {
354                hostURL = this.path;
355            };
356            
357            if (typeof this.host !== 'undefined' && this.host != '') hostURL = this.host + hostURL;
358            if (typeof this.relationString !== 'undefined') return (hostURL + '?query=' + this.relationString);
359                
360            if (typeof this.path === 'undefined' || this.path == '') {
361                hostURL = hostURL + 'view/' + this.view + '/';
362            };
363
364
365		var url = '';
366		
367		for (var index in this.filters) {
368			var filter = this.filters[index];
369			if (url != '')  url += ' ' + filter.connector + ' ';
370			url += filter.toURL();
371		}
372		var first = true;
373		if (this.sorters.length > 0)
374		{
375			if (url != '')  url += ' ';
376			url += 'ORDER BY ';
377			for (var index in this.sorters) {
378				var sorter = this.sorters[index];
379				if (!first)  url += ' ORDER BY ';
380				first = false;
381				url += sorter.field + ' ' + sorter.direction;			
382			}
383			
384			//url += filter.field + ' ' + filter.comparator + ' "' + filter.value + '"';
385		
386		}
387                
388		if (this.limiters.length > 0)
389		{
390			if (url != '')  url += ' ';
391			url += 'LIMIT ';
392                        // BUT ONLY ONE LIMIT MAKES SENSE
393			for (var index in this.limiters) {
394				var limiter = this.limiters[index];
395//				if (!first)  url += ', ';  
396				first = false;
397				url += limiter.from + ', ' + limiter.amount;			
398			}
399			
400			//url += filter.field + ' ' + filter.comparator + ' "' + filter.value + '"';
401		
402		}
403                
404                if (this.relations.length > 0)
405		{
406			if (url != '')  url += ' RELATIONS ';
407                        url += this.relations.join(', ');				
408		}
409                
410                url = hostURL + '?list[filters]=' + url;
411                if (typeof DATA_URL != 'undefined') url = DATA_URL + url;
412		return url;
413	};
414
415    	this.toString = function() {
416            if (typeof this.relationString !== 'undefined') {
417                return this.relationString;
418            }
419		
420		var url = 'VIEW ' + this.view;
421		
422		var first = true;
423		for (var index in this.filters) {
424			var filter = this.filters[index];
425                        if ( first ) {
426                            url += ' WHERE ';
427                            first = false;
428                        } else url += ' ' + filter.connector + ' ';
429			url += filter.toURL();
430		}
431                first = true;
432		if (this.sorters.length > 0)
433		{
434			if (url != '')  url += ' ';
435			url += 'ORDER BY ';
436			for (var index in this.sorters) {
437				var sorter = this.sorters[index];
438				if (!first)  url += ' ORDER BY ';
439				first = false;
440				url += sorter.field + ' ' + sorter.direction;			
441			}
442			
443			//url += filter.field + ' ' + filter.comparator + ' "' + filter.value + '"';
444		
445		}
446                
447		if (this.limiters.length > 0)
448		{
449			if (url != '')  url += ' ';
450			url += 'LIMIT ';
451                        // BUT ONLY ONE LIMIT MAKES SENSE
452			for (var index in this.limiters) {
453				var limiter = this.limiters[index];
454//				if (!first)  url += ', ';  
455				first = false;
456				url += limiter.from + ', ' + limiter.amount;			
457			}
458			
459			//url += filter.field + ' ' + filter.comparator + ' "' + filter.value + '"';
460		
461		}                                
462		return url;
463	};
464
465	this.parseString = function(queryString) {
466            
467            if (queryString.substring(0,8) == "RELATION") {
468                // SEARCH FOR LAST RELATION, WHICH IS VIEW NAME
469                var parts = queryString.split('RELATION ');
470                var words = parts[parts.length-1].split(' ');
471                this.view = words[0];
472                this.relationString = queryString;
473                return;
474            };
475            
476
477		var words = queryString.split(' ');
478		var pos = 0;
479		
480		if (words.length == 1) this.view = words[0];
481		if (words.length <= 1) return;
482		
483		//console.log(words.length);
484		
485		while (words.length > pos) {
486			var action = words[pos].toUpperCase();
487			
488			if (action == 'VIEW') {
489				this.view = words[pos + 1];
490				pos += 2;
491			}
492
493
494                        else if (action == 'RELATIONS' ) {
495                                pos ++;
496//                                if ( typeof this.relations === 'undefined' ) this.relations = [];
497                                do {
498					var field = words[pos];
499					if (field.charAt(field.length - 1) == ',') field = field.slice(0, field.length-1);
500					this.relations.push(field);
501					pos++;					
502				} while (words[pos - 1].charAt(words[pos - 1].length - 1) == ',');
503                        }
504			
505
506			else if (action == 'ORDER' && words[pos + 1].toUpperCase() == 'BY') {
507				pos += 2;
508				
509				do {
510					
511					var field = words[pos];
512					if (field.charAt(field.length - 1) == ',') field = field.slice(0, field.length-1);
513					pos++;
514					
515					var direction = 'ASC';
516					if (words.length > pos) {
517						var tmpDirection = words[pos].toUpperCase();
518						if (tmpDirection.charAt(tmpDirection.length - 1) == ',') tmpDirection = tmpDirection.slice(0, field.length-1);
519						if (tmpDirection == 'ASC' || tmpDirection == 'ASC,') { pos++; }
520						if (tmpDirection == 'DESC' || tmpDirection == 'DESC,') { direction = 'DESC'; pos++; }					
521					} 
522					
523					
524					var sorter = new Sorter({'field': field, 'direction': direction });
525					this.sorters.push(sorter);
526				} while (words[pos - 1].charAt(words[pos - 1].length - 1) == ',');
527				
528			}
529                        
530                        else if (action == 'LIMIT' ) {
531				pos++;
532				
533                                        var from = words[pos];
534					if (from.charAt(from.length - 1) == ',') from = from.slice(0, from.length-1);
535                                        from = parseInt(from);
536					pos++;
537					
538					var amount = parseInt( words[pos] );
539					pos++;
540					
541					var limiter = new Limiter({'from': from, 'amount': amount });
542					this.limiters.push(limiter);
543				
544			}
545
546			else {
547			
548				if (action == 'WHERE') pos++;
549				
550				var continueWhereParsing = true;
551				
552				do {
553					var connector = 'AND';
554					
555					if (pos > 0 && words[pos-1].toUpperCase() == 'OR') connector = 'OR';
556					
557					var field = words[pos];
558					pos++;
559					var comparator = words[pos];
560					pos++;
561	
562					var value = '';
563					
564                                        if (comparator.toUpperCase() == 'IS') {
565                                            
566                                            if (words[pos].toUpperCase() == 'NOT') {
567                                                comparator = 'IS NOT';
568                                                pos++;
569                                            } else if (words[pos].toUpperCase() == 'IN') {
570                                                comparator = 'IS IN';
571                                                pos++;
572                                            } 
573                                            
574                                            value = words[pos];   // NULL
575                                            pos++;
576                                            // NOT SUPPORTED: IS IN, IS NOT, ...
577                                            
578                                        } else {                                        
579                                            do {  // SEARCH END OF STRING IN "
580                                                    if (words.length == pos) {
581                                                        alert('Error in Query String. WHERE value must be enclosed by "..."');
582                                                        console.log('ERROR - UNEXPECTED PARSE END'); 
583                                                        debugger;
584                                                        return;
585                                                    }						
586                                                    value += ' ' + words[pos];
587                                                    pos++;
588
589                                            } while (value.charAt(value.length - 1) != '"');
590                                            value = value.slice(2, value.length-1); // CLEAN " CHARS
591                                            
592                                            
593                                            //console.log (value);
594                                        }
595					
596					var filter = new Filter({'field': field, 'comparator': comparator, 'value': value, 'connector': connector});
597					this.filters.push(filter);
598					
599					if (words.length <= pos) continueWhereParsing = false;
600					else {
601						var tmpConnector = words[pos].toUpperCase();
602						if (tmpConnector == 'AND' || tmpConnector == 'OR') pos++;
603							else continueWhereParsing = false;
604					}
605				} while (continueWhereParsing);				
606			}
607			
608
609			
610/*			
611			else {
612				console.log ('ERROR - UNABLE TO PARSE: '+ words[pos]);
613				pos++;
614			}
615*/			
616		}
617		
618	}
619	
620	this.merge = function(query) {
621		if ( $.isArray(query) ) {
622			// query is Array of Queries
623			// merge each query
624			for ( var index in query) this.merge(query[index]);
625		} else
626		{
627			console.log(query);
628				this.filters = this.filters.concat(query.filters);
629				this.sorters = this.sorters.concat(query.sorters);
630				if (isset(this.view) == false) this.view = query.view;
631		}		
632	}
633	
634	if (isset(queryString) == false) return this
635		else if (typeof queryString == 'string')  this.parseString(queryString);
636			else return this.merge(queryString);
637	
638}
639
640
641
642function Filter(iniParams) {
643	this.field;
644	this.value;
645	this.comparator = '=';
646	this.connector = 'AND';
647
648	$.extend( this, iniParams );	
649
650	this.getField = function() { 
651		// Can be overwritten
652		return this.field; 
653	};
654	this.getValue = function() { 
655		// Can be overwritten
656		return this.value;
657	};
658	
659	this.set = function(field, value) {
660		this.field = field;
661		this.value = value;
662		$(this).triggerHandler('changed');
663	}
664	
665	this.toURL = function() {
666            if ( this.comparator.toUpperCase() == 'IS') return this.getField() + ' ' + this.comparator + ' ' + this.getValue();	
667                else if ( this.comparator.toUpperCase() == 'IS NOT') return this.getField() + ' ' + this.comparator + ' ' + this.getValue();	
668                    else if ( this.comparator.toUpperCase() == 'IS IN') return this.getField() + ' ' + this.comparator + ' ' + this.getValue();	            
669        		else return this.getField() + ' ' + this.comparator + ' "' + this.getValue() + '"';		
670	};
671	
672	this.validate = function ( entity ) {
673		
674		
675		// check if entity data corresponds to filter
676		// Filter Value is STRING!
677		// und wird umgewandelt: FILTER TYPE von STRING nach ENTITY TYPE um
678//		console.log(this);
679//                console.log(entity);
680//                console.log(this.getField());
681		
682                var field = entity.fields[ this.getField() ];                
683                if (typeof field === 'undefined') return false;
683     // FIELD DOES NOT EXIST, RETURN FALSE;
684                
685		var entityValue = field.get();
686		var filterValue = convertValue( this.getValue(), 'string', field.type );
687		
688		if ( isTypeLikeString (field.type) ) {
689                        if (typeof entityValue === 'undefined') entityValue = '';
690			entityValue = entityValue.toUpperCase();
691			filterValue = filterValue.toUpperCase();
692		}
693		
694		
695		
696		if (isset(this.comparator) == false || this.comparator == '=')
697		{ 
698			/*
699			alert(typeof this.getValue());
700			alert(typeof this.value);
701			alert(typeof entity.get(this.getField()));
702			alert(typeof convertValue(this.getValue(), 'string', 'binary'));
703			*/
704			
705			return entityValue == filterValue;
706		}
707		else if (this.comparator == "!=" || this.comparator == "<>")
708		{
709			return entityValue != filterValue;
710		}
711
712		else if (this.comparator == "<")
713		{
714			return entityValue < filterValue;
715		}
716		
717		else if (this.comparator == ">")
718		{
719			if (isTypeLikeString (field.type)) filterValue += '_';
720			return entityValue > filterValue;
721		}
722
723		else if (this.comparator == "<=")
724		{
725			if (isTypeLikeString (field.type)) filterValue += '_';
726			return entityValue <= filterValue;
727		}
728		else if (this.comparator == ">=")
729		{
730			return entityValue >= filterValue;
731		}		
732		else if (this.comparator == "*")  // LIKE
733		{
734			return  entity.get( this.getField(), 'string' ).toLowerCase().indexOf( this.getValue().toLowerCase() ) !== -1; 
735		}		
736	
737		
738/*		
739		if ($filter["comparator"] == "LIKE")
740		{
741			// ENTFERNE % AM ENDE UND AM ANFANG
742			$needle = substr($filter["field2"], 1, -1); 
743			if (stripos($entity->get($filter["field1"]), $needle) !== false) $result = true; 	
744		}
745
746	*/
747	}
748		
749}
750
751
752Sorter = function(iniParams) {
753	this.direction = 'ASC';
754	$.extend( this, iniParams );	
755}
756
757Limiter = function(iniParams) {
758	this.from = 0;
759        this.amount = 0;
760	$.extend( this, iniParams );	
761}
762
763
764
765
766
767View = function(url) {
768	this.name = 'undefined';
769	this.fields = new Object();
770        this.resolutions = new Object();
771        
772		
773	this.getBlankEntity = function( forcedFilters )
774	{
775                var entity;
776                
777                if ( typeof this.xmlRoot !== 'undefined' )  entity = XMLStorage.getEntity( this );
778                else  if ( typeof this.data !== 'undefined' )  entity = ObjectStorage.getEntity( this );
779                    else  entity = new Entity();
780		entity.isNew = true;
781		entity.setView(this);
782		
783		for (var index in this.fields)
784		{
785			//console.log(this.fields[index]);
786			if (isset(this.fields[index]['default'])) { 
787                            entity.data[index] = entity.fields[index].getDefault();
788                            entity.fields[index].dataStatus = STATUS_CHANGED;
789//                            entity.data[index] = convertValue(this.fields[index]['default'], 'string', this.fields[index]['type']);
790                        
791                        }
792			else if (this.fields[index]['type'] == 'boolean' ) entity.data[index] = false;
793			else if (this.fields[index]['type'] == 'string'  || this.fields[index]['type'] == 'html'  || this.fields[index]['type'] == 'varchar') entity.data[index] = '';
794		}
795		
796		entity.forceFilters( forcedFilters );
797		//entity.dataStatus = STATUS_NEW;
798		entity.getPrimaryField().set(Model.nextBlankId());
799		
800		
801		//console.log('NEW ENTITY');
802		//console.log(entity);
803		
804		//??
805		entity.status(STATUS_NEW);
806		// $(document).trigger('entityDisplayReady', entity);
807		$(document).triggerHandler('entityReady_' + entity.viewName, entity);
808		$(document).triggerHandler('entityReady', entity);				
809		
810		$(document).triggerHandler('blankEntity_' + entity.viewName, entity);
811		$(document).triggerHandler('blankEntity', entity);				
812		return entity;
813	}
814	
815        this.getTitle = function() {
816            if ( typeof this.title == 'string') return this.title;
817                else return this.name;
818        }
819
820        this.getFieldTitle = function( fieldName ) {
821            if ( typeof this.fields[ fieldName ].title === 'string') return this.fields[ fieldName ].title;
822                else return this.fields[ fieldName ].name.toLowerCase().replace(/(^| )(\w)/g, function(x) {  return x.toUpperCase() }).replace('_', ' ');
823            
824
825                
826                
827        }
828
829       
830    
831	this.getPrimaryField = function getPrimaryField() {
832		for (var index in this.fields) {
833			if (isset(this.fields[index]['index']) && this.fields[index]['index'].toUpperCase() == 'PRIMARY') return this.fields[index];
834		}		
835	}
836	
837	this.getPrimaryEntity = function( id, callback, type, async) {
838		//console.log('GET');
839		//console.log(this);
840                if (typeof async === 'undefined') async = true;
841
842		var primaryField = this.getPrimaryField();
843		if (isset( type )) id = convertValue(id, type, 'string');
844//		var set = 
845                new EntitySet({query: new Query('VIEW ' + this.name + ' WHERE '+ primaryField['name'] +' = "' + id + '"'), callback: function( set ){ 
846//                    alert('AS');
847                    for (var index in set.entities) {
848                         EntityCache.add(set.entities[index]);
849                         if (isset(callback)) callback(set.entities[index]);
850                     }
851                }}, async);
852//		console.log(set);
853
854//		$(set).on('ready', function() {			
855//                                
856//			for (var index in set.entities) {
857//				EntityCache.add(set.entities[index]);
858//                                if (isset(callback)) callback(set.entities[index]);
859//
860//
861//			}
862//		});	
863
864	}
865	
866	/*
867	this.getData = function(additionalFilters)
868	{
869		// LOCAL ONLY
870	}
871	*/
872	
873	
874	
875	this.loadXML = function(url, callback, async)
876	{
877		if (typeof async === 'undefined') async = true;
878                
879		var view = this;
880                
881		
882		FileCache.load({
883			async: async,
884			url: url,
885			context: this,
886			dataType: 'xml',
887			
888			done:  function(data){
889				
890				// parse XML
891				
892				var xml = $(data);
893		
894				xml.find('>view').children().each(function() {
895
896					// Parse View Params
897					
898					var xmlTag = $(this);
899					var viewPropertyName = xmlTag.prop("tagName");
900					
901					if (viewPropertyName != 'fields' && viewPropertyName != 'resolutions')
902					{
903						/*
904						console.log('yyyy');
905						console.log(viewPropertyName);
906						console.log(xmlTag.text());
907						//console.log(xmlTag[0].innerHTML);
908						//console.log(xmlTag.html());
909						console.log('yyyy2');
910						 */
911						
912                                                
913//                                              var viewPropertyValue = xmlTag.html();  // VERKACKT IM IE!
914                                                var viewPropertyValue = xmlTag.text();
915                                                view[viewPropertyName] = viewPropertyValue;
916                                                
917
918					}
919					
920				});
921
922
923				xml.find('field').each(function(){
924
925					// Parse Field Params
926
927					var field = new Object();
928					var xmlField = $(this);
929					
930					xmlField.children().each(function(e1, e2) {
931						var xmlTag = $(this);
932						var fieldPropertyName = xmlTag.prop("tagName");
933						//var fieldPropertyValue = xmlTag.html();
934						var fieldPropertyValue = xmlTag.text();
935						
936						field[fieldPropertyName] = fieldPropertyValue;
937					});
938					view.fields[field['name']] = field;                                        
939                                        
940                                        
941//                                        field.getTitle = function( ) {
942//                                            if ( typeof field.title === 'string') return field.title;
943//                                                else return field.name;
944//                                        }
945
946				});
947                                
948                                xml.find('resolution').each(function(){
949
950					// Parse resolution Params
951
952					var resolution = new Object();
953					var xmlField = $(this);
954					
955					xmlField.children().each(function(e1, e2) {
956						var xmlTag = $(this);
957						var fieldPropertyName = xmlTag.prop("tagName");
958						//var fieldPropertyValue = xmlTag.html();
959						var fieldPropertyValue = xmlTag.text();
960                                                
961                                                if ( fieldPropertyName == 'width' ) fieldPropertyValue = parseInt( fieldPropertyValue );
962						
963						resolution[fieldPropertyName] = fieldPropertyValue;
964					});
965					view.resolutions[resolution['name']] = resolution;                                        
966				});
967                                
968                                
969
970				$(view).trigger('ready');
971				$(document).triggerHandler('viewReady', view);
972				if (typeof callback == 'function') callback();
973				
974			}})
975	};
976	
977	if (isset(url)) this.loadXML(url);
978}
979
980function Relation() {
981	this.viewName;
982	this.relationViewName;	
983	var fieldName = '## PRIMARY ##';
984	var relationFieldName = '## PRIMARY ##';
985	this.fieldName = function(name) {	
986		if (isset( name )) fieldName = name;
987		else {
988			if (fieldName == '## PRIMARY ##') return Model.views[this.viewName].getPrimaryField().name;
989			else return fieldName;
990		}
991	}
992
993	this.relationFieldName = function(name) {          
994		if (isset( name )) relationFieldName = name;
995		else {
996			
997			// if ( typeof Model.views[this.relationViewName] === 'undefined' ) throw('Relation: VIEW NOT LOADED ' + this.relationViewName);
998			if ( typeof Model.views[ this.relationViewName ] === 'undefined' ) Model.loadView(  this.relationViewName , undefined, false );
999
1000			if (relationFieldName == '## PRIMARY ##') return Model.views[this.relationViewName].getPrimaryField().name;
1001			else return relationFieldName;
1002		}
1003	}
1004}
1005
1006
1007Model = new Object();
1008Model.lastBlankId = 0;
1009Model.nextBlankId = function()
1010{
1011	Model.lastBlankId -= 1;
1012	return Model.lastBlankId;
1013}
1014Model.views = {};
1015Model.views_loading = {};
1016Model.loadView = function(url, callback, async ) {
1017	// READ XML FROM URL OR VIEWNAME
1018        
1019        
1020    
1021    var job = new Job();
1022    job.info = 'loadView';
1023    
1024//    job.hire( () => {        
1025//        new EntitySet().loadFromURL( 'provider/plugins/data/view/' + viewName + '/?list[filters]=*PRIMARY* = "' + idAsString + '"', function( set ){      
1026//            for (var index in set.entities) {
1027//                EntityCache.add(set.entities[index]);
1028//                if ( typeof callback === 'function') callback(set.entities[index]);   
1029//                console.log( set.entities[index] );
1030//                
1031//                job.doSuccess( set.entities[index] );
1032//                return;
1033////                                return ( set.entities[index] );
1034//             }
1035//             job.failed( new Error('Primary Entity "'+ idAsString +'"not found.') );         
1036//             return;
1037//
1038//        }, async);        
1039//    });       
1040//    
1041//    job.do();
1042    
1043    
1044        
1045        
1046        
1047        
1048        
1049
1050        if (typeof Model.views[ url ]  !== 'undefined') {
1051            if (typeof callback === 'function') callback();
1052            job.doSuccess();
1053            return Model.views[ url ];
1054        }
1055
1056        if (typeof Model.views_loading[ url ]  === 'undefined') {
1057            Model.views_loading[ url ] = [];
1058            if (typeof callback === 'function') Model.views_loading[ url ].push( callback ); 
1059
1060            if (url.substr(url.lastIndexOf('.') + 1).toLowerCase() == 'xml'){
1061                var newView = new View();
1062                $(newView).on('ready', function(){Model.views[newView.name] = newView});
1063                newView.loadXML(url, callback);
1064                return newView;            
1065            };
1066            
1067            var viewLoader =  new EntitySet();
1068            viewLoader.loadFromURL( 'provider/plugins/data/view/' + url + '/?list[filters]=false', function(){
1069    //            callback();     
1070                for (var index in  Model.views_loading[ url ]){
1071                    Model.views_loading[ url ][ index ]();
1072                }
1073                
1074                delete Model.views_loading[ url ];
1075                job.doSuccess();            
1076            }, async);
1077            return job;
1078        }             
1079        if (typeof callback === 'function') Model.views_loading[ url ].push( callback ); 
1080               
1081    
1082
1083    return job;
1084
1085}
1086
1087Model.addView = function( view ){
1088    // TRIGGERS READY
1089    // NEEDED IF CHANGES TO FIELDS ARE MADE BEFORE ENTITIES ARE CREATED
1090    $(view).trigger('beforeReady');
1091    $(Model).trigger('beforeViewReady', view);
1092    $(Model).trigger('beforeViewReady_' + view.name, view);
1093    $(document).triggerHandler('beforeViewReady', view);    // LEGACY
1094    Model.views[view.name] = view;
1095    $(view).trigger('ready');
1096    $(Model).trigger('viewReady', view);
1097    $(Model).trigger('viewReady_' + view.name, view);
1098    $(document).triggerHandler('viewReady', view);  // LEGACY    
1099}
1100
1101
1102
1103Model.getPrimaryEntity = function( viewName, idAsString, callback, async){
1104    // WORKS WITHOUT LOADING VIEW FIRST
1105    // ID MUST BE STRING
1106    if (typeof async === 'undefined') async = true;
1107    
1108    var job = new Job();
1109//    console.clear();
1110//    console.log( job );
1111//    job.success.hire(function(){alert('test')});
1112    job.info = 'getPrimaryEntity';
1113    
1114//    job.hire( () => {        
1115        new EntitySet().loadFromURL( 'provider/plugins/data/view/' + viewName + '/?list[filters]=*PRIMARY* = "' + idAsString + '"', function( set ){      
1116            for (var index in set.entities) {
1117                EntityCache.add(set.entities[index]);
1118                if ( typeof callback === 'function') callback(set.entities[index]);   
1119                console.log( set.entities[index] );
1120                job.result = set.entities[index];
1121                job.doSuccess( job.result );
1122                return;
1123//                                return ( set.entities[index] );
1124             }
1125             job.result = new Error('Primary Entity "'+ idAsString +'"not found.');
1126             job.doFail( job.result );         
1127             return;
1128
1129        }, async);        
1130//    });           
1131//    job.do();
1132    return job;    
1133}
1134
1135
1136Model.relations = {};
1137$(document).on('viewReady', function(event, view) {
1138	if (isset(Model.relations[view.name]) == false) Model.relations[view.name] = {};
1139	
1140	for (var index in view.fields) {
1141		if (isset(view.fields[index].foreign)) {
1142			// create relation
1143			var relation = new Relation();
1144			relation.viewName = view.name;
1145			relation.fieldName( index );
1146			relation.relationViewName = view.fields[index].foreign;
1147			relation.relationFieldName( '## PRIMARY ##' );
1148			if (isset( view.fields[index].foreign_name )) relation.name = view.fields[index].foreign_name;
1149				else relation.name = view.fields[index].foreign;
1150			Model.relations[view.name][relation.name] = relation;
1151			
1152			var invertedRelation = new Relation();
1153
1154			invertedRelation.name = view.name;
1155/*			if (isset( view.fields[index].foreign_name )) invertedRelation.name = view.fields[index].foreign_name;
1156				else invertedRelation.name = view.name;
1157//				else invertedRelation.name = view.fields[index].foreign;
1158*/			
1159		
1160			invertedRelation.relationViewName = view.name;
1161			invertedRelation.relationFieldName( index );
1162			invertedRelation.viewName = view.fields[index].foreign;
1163			invertedRelation.fieldName('## PRIMARY ##');
1164			
1165			if (isset(Model.relations[invertedRelation.viewName]) == false) Model.relations[invertedRelation.viewName] = {};
1166			Model.relations[invertedRelation.viewName][invertedRelation.name] = invertedRelation;
1167			
1168			
1169		}
1170	}
1171	
1172});
1173
1174
1175
1176
1177EntityStorage = new Object();
1178
1179//EntityStorage.sets = new Object();
1180EntityStorage.sets = undefined;
1181
1182EntityStorage.baseName = 'Entity::';
1183if (isset('APP_NAME')) EntityStorage.baseName = 'Entity::' + APP_NAME + '_';
1184
1185
1186
1187EntityStorage.store = function (entity)
1188{
1189	console.log('Store Entity');
1190	localStorage.setObj(this.baseName + ':::' + entity.viewName +':::'+ '_'+ entity.getPrimary('string'), entity.data);
1191	if (isset(EntityStorage.sets[entity.viewName]) == false) EntityStorage.sets[entity.viewName] = new EntitySet();
1192	EntityStorage.sets[entity.viewName].add(entity);
1193
1194}
1195
1196EntityStorage.load = function()
1197{
1198	var sets = {};
1199	for (var key in localStorage){
1200		if (key.substring(0, EntityStorage.baseName.length) == EntityStorage.baseName) 
1201		{
1202			var viewName = key.split(":::")[1];
1203
1204			if (isset(sets[viewName]) == false) sets[viewName] = new EntitySet();
1205			
1206			console.log(key);
1207			var entity = Model.views[viewName].getBlankEntity();
1208			//var entity = new Entity();
1209			entity.data = localStorage.getObj(key);
1210			console.log(entity);
1211			sets[viewName].add(entity);
1212		}
1213	}
1214	EntityStorage.sets = sets;
1215//	return sets;
1216}
1217
1218EntityCache = new Object();
1219EntityCache.entityCache = {};
1220EntityCache.setCache = {};
1221
1222EntityCache.add = function ( entity )
1223{
1224	if ( isset( entity.viewName) == false) entity.viewName = 'unknown';
1225	if ( isset( EntityCache.entityCache[ entity.viewName ]) == false ) 
1226	{
1227		EntityCache.entityCache[entity.viewName] = new EntitySet();
1228		EntityCache.entityCache[entity.viewName].name  = entity.viewName;
1229		
1230		$(EntityCache).trigger('setAdded', EntityCache.entityCache[entity.viewName]);
1231	}
1232//	console.log('ADDED ENTITY ' + entity.getPrimary());
1233
1234        // CHECK IF CACHE ALREADY CONTAINS ENTITY
1235        if (typeof EntityCache.entityCache[ entity.viewName ].entities[entity.getPrimary()] === 'undefined') {
1236            EntityCache.entityCache[ entity.viewName ].add( entity );
1237        }
1238}
1239
1240EntityCache.restore = function()
1241{
1242	console.log('restoreAll');	
1243	for (var index in EntityCache.entityCache)
1244		EntityCache.entityCache[index].restore();
1245}
1246
1247EntityCache.store = function( )
1248{
1249	// params.callback
1250	// params.progress
1251	
1252	console.log('storeAll');	
1253	console.log( EntityCache.entityCache);
1254	var counterEntities = 0;
1255	var counterCompleted = 0;
1256        var counterDeleted = 0;
1257        var counterChanged = 0;
1258	var fails = 0;
1259        
1260        var arrStore = [];
1261        var arrDelete = [];
1262        
1263        
1264        var saveJob = new Job();
1265        saveJob.info = 'EntityCache.store';
1266        
1267
1268/*
1269
1270        for (var index in EntityCache.entityCache) {
1271			for (var id in EntityCache.entityCache[index].trash) {		
1272//                           saveJob.hire( EntityCache.entityCache[index].trash[id].store() );
1273//                           saveJob.hire( EntityCache.entityCache[index].entities[id].jobs('store') ); 
1274						saveJob.hire( EntityCache.entityCache[index].trash[id].store, true ); 
1275			}
1276			for (var id in EntityCache.entityCache[index].entities) {			
1277				if (EntityCache.entityCache[index].entities[id].status() != STATUS_UNCHANGED) {    
1278								
1279	//            liefert storeBefore Job!               
1280	//            saveJob.hire( EntityCache.entityCache[index].entities[id].store() );            
1281	//            saveJob.hire( EntityCache.entityCache[index].entities[id].jobs('store') ); 
1282				saveJob.hire( EntityCache.entityCache[index].entities[id].store, true ); 
1283                            
1284                            
1285                            
1286                            
1287                            
1288//                            EntityCache.entityCache[index].entities[id].store() ;     
1289//             saveJob.hire( EntityCache.entityCache[index].entities[id].events.get('store') );
1290                            
1291            	}                       
1292			}
1293		}
1294*/        
1295
1296		var changes = EntityCache.getChanges();
1297		for (var index in changes ){
1298			saveJob.hire( changes[index].store, true ); 
1299		}
1300        
1301        saveJob.doSuccess();        
1302        console.log( saveJob );
1303
1304        return saveJob;        
1305        
1306        /* OLD 
1307        
1308	var callbackOfEntities = function( result ) {
1309		counterCompleted++;
1310		console.log( 'result' );		
1311		console.log( counterCompleted +' - ' + counterEntities );		
1312		console.log( result );	
1313		if ( result.success == false ) {
1314                    fails++;
1315                } else {
1316                    if (result.action == 'store') counterChanged++;
1317                    else  if (result.action == 'delete') counterDeleted++;
1318                }
1319		if (typeof params.progress === "function") 	params.progress ( { 'fails': fails, 'completed': counterCompleted, 'changed': counterChanged, 'deleted': counterDeleted, 'amount': counterEntities} );
1320		
1321		if ( counterEntities == counterCompleted ) {
1322			if (typeof params.callback === "function") 	params.callback( { 'fails': fails, 'completed': counterCompleted, 'changed': counterChanged, 'deleted': counterDeleted,  'amount': counterEntities} );			
1323		}
1324		
1325	};
1326	
1327	// CHECK HOW MANY ENTITIES HAVE BEEN CHANGED
1328	for (var index in EntityCache.entityCache) {
1329//                counterEntities +=  EntityCache.entityCache[index].countTrash;
1330                for (var id in EntityCache.entityCache[index].trash) {		
1331                           counterEntities++;
1332                            arrStore.push( EntityCache.entityCache[index].trash[id] );
1333		}
1334		for (var id in EntityCache.entityCache[index].entities) {			
1335			if (EntityCache.entityCache[index].entities[id].status() != STATUS_UNCHANGED) {
1336                            counterEntities++;
1337                            arrStore.push( EntityCache.entityCache[index].entities[id] );
1338                        }                       
1339		}
1340	}
1341	
1342	if (counterEntities == 0 && typeof params.callback === "function") 	params.callback( { 'fails': fails, 'completed': counterCompleted, 'changed': counterChanged, 'deleted': counterDeleted, 'amount': counterEntities} );
1343	
1344        console.log(arrStore);
1345//        alert('sotre');
1346	
1347        for (var index in arrStore) {
1348		arrStore[index].store( { callback: callbackOfEntities });
1349	}
1350        */
1351    
1352	
1353}
1354
1355
1356
1357
1358EntityCache.getChanges = function( ) {
1359    var result = [];
1360    for (var index in EntityCache.entityCache) {
1361		for (var id in EntityCache.entityCache[index].trash) {		
1362			result.push( EntityCache.entityCache[index].trash[id] );
1363		}
1364	for (var id in EntityCache.entityCache[index].entities) {			
1365			if (EntityCache.entityCache[index].entities[id].status() != STATUS_UNCHANGED) {
1366				result.push( EntityCache.entityCache[index].entities[id] );
1367			}
1368        }
1369    }
1370    return result;    
1371}
1372
1373
1374
1375
1376
1377
1378$(document).on('readyOnce', function() {
1379	
1380	if (typeof READY_FLAG_DATA != 'undefined') return;  READY_FLAG_DATA = 1;
1381	
1382        // PARSING PAGES ITEMS ONLY!
1383        // OTHER FIELDS AND ENTITIES DISABLED
1384        
1385        
1386	console.log('scanning html');
1387	$('[data-structure="field"][data-view="pages_items"]').each(function(){
1388//	$('[data-structure="field"]').each(function(){
1389
1390//            var entity = Model.views['pages_items'].getBlankEntity();
1391
1392
1393		var id;
1394		var view;
1395		var fieldName;
1396		var entity; 
1397		var type;
1398		var value;
1399		var format = '';
1400		
1401		//console.log('display');
1402		//console.log($(this).html() + '  ' + $(this).attr('data-field'));
1403
1404//console.log(this);
1405		
1406		if (isset($(this).attr('data-raw'))) value = $(this).attr('data-raw');
1407			else value =$(this).html();
1408	
1409		
1410		if (isset($(this).attr('data-id'))) id = $(this).attr('data-id');
1411			else view = $(this).parent().attr('data-id');
1412		 
1413		if (isset($(this).attr('data-view'))) view = $(this).attr('data-view');
1414			else view = $(this).parent().attr('data-view');
1415		 
1416		if (isset($(this).attr('data-field'))) fieldName = $(this).attr('data-field');
1417			else view = $(this).parent().attr('data-field');
1418
1419		if (isset($(this).attr('data-type'))) type = $(this).attr('data-type');
1420			else type = $(this).parent().attr('data-type');
1421
1422		if (isset($(this).attr('data-format'))) format = $(this).attr('data-format');
1423			else if (isset($(this).parent().attr('data-format'))) format = $(this).parent().attr('data-format');
1424
1425		if (isset(EntityCache.entityCache[view]) && isset(EntityCache.entityCache[view].entities[id])) entity = EntityCache.entityCache[view].entities[id];
1426			else {
1427				entity = new Entity(id);
1428				entity.addField('id', id, 'string');
1429				entity.viewName = $(this).attr('data-view');
1430				EntityCache.add(entity);
1431			}		
1432
1433		var field = entity.addField(fieldName, convertValue(value, 'html', type), type);
1434		//field.attribute('format', format);
1435		field.format = format;
1436
1437		var display = $(this);
1438		
1439		display.data('entity', entity);
1440		display.data('field', field);
1441		
1442		$(field).on('changed', display, field.defaultOnChangeHandler);
1443		
1444		
1445		if (field.format != '') display.html(field.getForWeb());
1446		
1447                
1448		$(document).trigger('fieldDisplayReady', display);
1449				
1450	});
1451
1452	
1453	$(EntityCache).trigger('parseCompleted');
1454	
1455	
1456	/*
1457	$('[data-structure="entity"]').each(function(){
1458		var display = $(this);
1459		
1460		var viewName = display.attr('data-view');
1461		
1462		if (isset( EntityCache.entityCache[viewName] && isset( EntityCache.entityCache[viewName].entities[display.attr('data-id')])) )
1463		{
1464			display.data('entity', EntityCache.entityCache[viewName].entities[display.attr('data-id')]);
1465	
1466			//$(DataCache).triggerHandler('entityReady', DataCache.sets[set].entities[id]);
1467			
1468			$(EntityCache.entityCache[viewName]).triggerHandler('entityAdded', EntityCache.entityCache[viewName].entities[display.attr('data-id')]);
1469			
1470			if (isset(display.data('entity').view)) $(document).trigger('entityDisplayReady_' + display.data('entity').view.name, display);
1471			$(document).trigger('entityDisplayReady', display);
1472		
1473		}
1474		
1475	
1476	});
1477        */
1478		
1479});
1480
1481
1482
1483
1484
1485EntityField = function(entity, name) {
1486	this.entity = entity;
1487	this.name = name;
1488	this.format = '';
1489
1490	this.dataStatus = STATUS_UNCHANGED;
1491        
1492        this.syncFields = [];
1493	
1494	this.status = function(newStatus)
1495	{
1496		if (isset(newStatus) == false) return this.dataStatus;
1497		this.dataStatus = newStatus;
1498	}
1499	
1500	
1501	
1502	this.get = function(outputType, format)
1503	{
1504		/*
1505		if (this.name == 'text') {
1506			console.log('FIELD: ');
1507			console.log(this);
1508			alert(this.attribute('type')+ '  - ' + this.entity.data[this.name]);
1509		}
1510		*/
1511                var value = convertValue(this.entity.data[this.name], this.attribute('type'), outputType, format);
1512//                if ( isNaN(value) ) {
1513//                    value = this.getDefault();
1514//                }                 
1515		return value;
1516	};	
1517	
1518	this.getForWeb = function(format)
1519	{
1520	
1521		if (isset(format) == false)
1522		{
1523			format = this.attribute('format');
1524		}
1525		
1526		//console.log('FORMAT:');
1527		//console.log(this.attribute('format'));
1528		//console.log(format);
1529//alert(this.get('html', format));
1530		return this.get('html', format);
1531	};	
1532	
1533
1534
1535	this.set = function(value, inputType, inputFormat, silent)
1536	{
1537                // IF value = Entity - set Primary to field value
1538                if (value instanceof Entity ) {                    
1539                    inputType = this.attribute('type');
1540                    value = value.getPrimary( this.attribute('type') );
1541                }
1542            
1543            
1544                if ( (inputFormat == 'HH:mm' || inputFormat == 'HH:mm:ss') && this.attribute('type') == 'datetime' ) {
1545                    // SPECIAL CASE
1546                    // SETTING TIME TO DATETIME KEEPS OLD DATE
1547                    var dateString = this.get('string', 'YYYY-MM-DD');
1548                    value = dateString + ' ' + value;
1549                    inputFormat = 'YYYY-MM-DD ' + inputFormat;
1550                    console.log( value );
1551                    value = convertValue(value, inputType, this.attribute('type'), null, inputFormat);
1552                    if (value == 'Invalid Date') value = convertValue(dateString, inputType, this.attribute('type'), null, 'YYYY-MM-DD');
1553                    console.log( value );
1554                }
1555
1556		value = convertValue(value, inputType, this.attribute('type'), null, inputFormat);
1557
1558                // CHECK IF INVALID NUMBER (NaN) 
1559                // !!! IMPORTANT NaN === NaN;  // false !!!
1560                // In that case use default value
1561                if ( value !== value) {
1562                    if (typeof this.attribute('default') !== 'undefined') value = this.getDefault();
1563                } 
1564                
1565                var oldValue = this.entity.data[this.name];
1566                
1567		// LEAVE AND DO NOT TRIGGER CHANGE IF VALUE UNCHANGED. CANNOT DETERMINE CHANGES ON DATE OBJECT
1568                if ( oldValue == value && value instanceof Date == false ) return;
1569
1570
1571		if (this.dataStatus == STATUS_CHANGING) return;  // PREVENT EVENT CIRCUITS
1572				
1573		if (isset(silent) == false) silent = false;
1574		
1575                var oldStatus = this.status();
1576		this.status(STATUS_CHANGING);
1577                
1578                var params = { field: this, oldValue: oldValue, newValue: value };
1579
1580		if (silent == false) $(this.entity).trigger( "beforeChanged", this.name );
1581                if (silent == false) { 
1582                    $(this).trigger("beforeChanged", params );
1583                    if ( value != params.newValue) {
1584                        value = params.newValue;
1585                        if (value == oldValue) {
1586                            this.status( oldStatus );
1587                            return;
1588                        }
1589                    }
1590                    
1591
1592                }
1593               
1594                
1595		//this.dataStatus = STATUS_CHANGING;
1596
1597		//console.log('TRIGGER');
1598		//console.log(silent);
1599
1600		
1601		if (isset(this.entity.dataArchive[this.name]) == false) 
1602		{
1603			this.entity.dataArchive[this.name] = [];
1604		}
1605		this.entity.dataArchive[this.name].push(this.entity.data[this.name]);
1606
1607		
1608		//console.log(this.entity.dataArchive);
1609		
1610		this.entity.data[this.name] = value;
1611                
1612                // ENTITY STATUS MUST BE CHANGED TO STATUS_CHANGED
1613                // OTHERWISE ENTIY STATUS IS NOT SET CORRECTLY IN TRIGGERED FIELD CHANGED METHODS
1614                // THEREFORE STORE CURRENT ENTITY STATUS. SET QUIET. RESET. UPDATE WITH TRIGGER
1615
1616                var oldEntityStatus = this.entity.status();
1617                this.entity.dataStatus = STATUS_CHANGED;                        //SET QUIET
1618                                                            
1619                if (silent == false) $(this).trigger("changed", { field: this, oldValue: oldValue, newValue: value } );
1620                
1621		if (silent == false) $(this.entity).trigger( "changed", this.name );
1622		if (silent == false) $(document).trigger( "entityChanged", { entity: this.entity, field: this.name, oldValue: oldValue, newValue: value  } );
1623		
1624		this.status(STATUS_CHANGED);                                    // ENTIY STATUS WAS SET TO CHANGED QUIETLY BEFORE
1625                
1626                this.entity.dataStatus = oldEntityStatus;                       // RESET           
1627		this.entity.status(STATUS_CHANGED);                             // UPDATE WITH TRIGGER
1628		
1629	};	
1630        
1631        this.getDefault = function() {
1632            if (typeof this.attribute('default') !== 'undefined') return convertValue(this.attribute('default'), 'string', this.attribute('type'));
1633            return;
1634        }
1635	
1636	
1637	this.attribute = function(attributeName, value)
1638	{
1639            var view;
1640            if (typeof this.entity.view !== 'undefined') view = this.entity.view;
1641                else if (typeof this.entity.viewName !== 'undefined') view = Model.views[ this.entity.viewName ];
1642            
1643            if (typeof view == 'undefined')
1644            {
1645//			console.log('No VIEW');
1646            //	console.log(attributeName);
1647            //	console.log(this[attributeName]);
1648                    return this[attributeName];
1649            }
1650
1651            //console.log(this.entity.view.fields[this.name][attributeName]);
1652            //console.log(attributeName);
1653            //console.log(this.entity.view.fields);
1654            /*
1655            console.log('---   VIEW');
1656            console.log(this.entity.view);
1657            console.log(value);
1658            console.log(attributeName);
1659            console.log(this.entity.view.fields[name][attributeName]);
1660            */
1661
1662            if (typeof(value) !== 'undefined')  view.fields[name][attributeName] = value;
1663                    else return view.fields[name][attributeName];
1664		
1665	}
1666        
1667        this.getRelationViewName = function(){
1668            
1669            if (typeof this.attribute('foreign') === 'undefined') return false;
1670            return this.attribute('foreign');
1671        }
1672        
1673        this.getRelationView = function(){
1674            
1675            if (typeof this.attribute('foreign') === 'undefined') return false;
1676            // VIEW MUST BE LOADED BEFORE RETURN
1677//            alert( this.attribute('foreign') );
1678            if (typeof Model.views[ this.attribute('foreign') ] === 'undefined') {
1679                Model.loadView( this.attribute('foreign'), null, false);
1680            }
1681            return Model.views[ this.attribute('foreign') ];
1682        }
1683        
1684        this.getForeignView = function(){
1685            // LEGACY
1686            return this.getRelationView();
1687        }
1688        
1689        
1690        this.toAttribute = function( element, attributeName, ){
1691            var field = this;
1692            if ( typeof attributeName === 'undefined' ) attributeName = 'data-field-' + field.name;
1693            $(this).on('changed', function( event ){
1694                $(element).attr( attributeName, field.get('string' ));
1695            });  
1696            $(element).attr( attributeName, field.get('string' ));
1697        }
1698	
1699	
1700	this.toDisplay = function( options )
1701	{
1702                var display = null;
1703                var tagName;
1704                var label;
1705                var format;
1706                var type;
1707                var field = this;
1708                
1709		if (typeof (options) == 'undefined') tagName = 'div';	
1710                    else {
1711                            if (typeof options == 'object') {
1712                                if (options instanceof jQuery) display = options;       // options = element = display
1713                                else {
1714                                    tagName = options.tagName;
1715                                    format = options.format;
1716                                    label = options.label;
1717                                    type = options.type;
1718                                }
1719                            } else tagName = options;
1720                    }
1721                    
1722                if ( label === true ) label = this.getLabel();
1723            
1724            
1725                if (typeof (tagName) == 'undefined')    tagName = 'div';	    
1726                
1727                if (typeof (type) == 'undefined') {
1728                    if ( typeof display !== 'undefined' && display != null && display.hasAttr('data-type') ) type = display.attr('data-type');
1729                        else type = this.attribute('type');
1730                }                    
1731                
1732                if (typeof (format) == 'undefined') {
1733                    if (typeof display !== 'undefined' && display != null && display.hasAttr('data-format') ) format = display.attr('data-format');
1734                        else format = this.attribute('format');
1735                }
1736                
1737                if (display == null) {
1738                    var displayString = '<' + tagName + '></' + tagName + '>';
1739//                    alert(displayString);
1740                    display =  $(jQuery.parseHTML(displayString));                    
1741                } 
1742                
1743                display.attr('data-structure', 'field');
1744                
1745                display.attr('data-type', type);
1746                
1747                display.attr('data-field', this.name);  
1748                
1749                display.attr('data-format', format);     
1750                
1751                
1752                
1753                
1754
1755                                // Get FORMAT from Attribute
1756                                // Use FORMAT presets for special types
1757                                // element type might differ from entity field type
1758                                if (typeof format === 'undefined') {
1759                                    var attrType = display.attr('data-type');
1760                                    if ( attrType == 'date') format = 'L';
1761                                    else if ( attrType == 'datetime') format = 'L LT';
1762                                    else if ( attrType == 'time') format = 'HH:mm';
1763                                }
1764                         
1765//                                if ( field.type == 'datetime' ) display.attr('data-value', field.get('string', 'YYYY-MM-DD HH:mm:ss'));      
1766//                                else if ( field.type == 'time' ) display.attr('data-value', field.get('string', 'HH:mm:ss'));                                     
1767//                                else display.attr('data-value', field.getForWeb());
1768                                
1769//                                display.html(field.getForWeb( format ));
1770            
1771//                                alert( format + ' ' + value );
1772                                
1773                
1774                    
1775                var value; // CONTENT THAT IS DISPLAYED                
1776                if ( type == 'boolean') {
1777                    if (this.get() == 1) value = '<span data-icon="1"></span>';
1778                        else  value = '<span data-icon="0"></span>';
1779                } 
1780                else if ( type == 'password') {
1781                    value = '';
1782                    for (var i=0; i<field.get('string').length; i++) value += '&#8226;'
1783                } 
1784                
1785                else if ( type == 'link' ) {
1786                    var linkContent = options.content || this.getForWeb();
1787                    var url = this.get('string').trim();
1788                    if ( isEmail(url) ) url =  'mailto:' + url;
1789                    else {
1790                        if (url.substring(0, 4).toLowerCase() == 'www.') url = 'http://' + url;
1791                    } 
1792                    var target = '';
1793                    if (typeof options.target !== 'undefined') target = ' target="'+options.target +'"';
1794                    value = '<a href="'+ url + '"'+target+'>' + linkContent + '</a>';
1795                    if (url == '') value = '';
1796                }
1797                else value = this.getForWeb( format );      
1798                
1799                 
1800                if( display.prop("tagName") == 'INPUT' ) display.val( value );
1801                    else display.html( value );             
1802                    
1803            
1804            
1805            
1806            
1807            
1808            
1809            
1810            
1811                display.attr('data-label', label || this.attribute('label'));
1812                display.attr('data-id', this.entity.getPrimary('string'));
1813                display.attr('data-view', this.entity.viewName);
1814//                 console.log(this.name);
1815//                 console.log(this);
1816                 
1817                // Get Additional Field Attributes from VIEW 
1818                for (var index in Model.views[this.entity.viewName].fields[name] ){
1819                    var fieldAttribute = Model.views[this.entity.viewName].fields[ name ][ index ];
1820                    if (index != 'name' && index != 'type'  && index != 'title' && index != 'size' && typeof fieldAttribute != 'function' && typeof fieldAttribute != 'object' &&  display.hasAttr('data-' + index) == false) {                        
1821    //                        console.log('data-' + index);
1822    //                        console.log(fieldAttribute);
1823                        display.attr('data-' + index ,  fieldAttribute);
1824                    }
1825                    
1826                    
1827                }
1828                
1829                
1830                display.attr('data-value', this.getForWeb('plain'));
1831
1832		display.data('entity', this.entity);
1833		display.data('field', this);		
1834                
1835                if( display.prop('tagName') == 'SELECT') {
1836                            display.find('[value]:not([value="' + field.getForWeb() + '"])').removeAttr('selected');
1837                            display.find('[value="' + field.getForWeb() + '"]').attr('selected', 'selected');
1838                }
1839
1840
1841
1842                this.defaultOnChangeHandler = function(event){
1843//                        var display = $(event.data[0]);
1844//                        var field = this;
1845
1846                        console.log('refresh');
1847                        //console.log(event);
1848                        //console.log(display);
1849
1850                        var autoRefresh = display.triggerHandler('needRefresh', event);
1851                        if (isset(autoRefresh) == false) autoRefresh = true;
1852
1853                        if (autoRefresh) 
1854                        {       
1855                                // Get FORMAT from Attribute
1856                                // Use FORMAT presets for special types
1857                                // element type might differ from entity field type
1858                                format = display.attr('data-format');
1859//                                alert(format);
1860                                if (typeof format === 'undefined') {
1861                                    var attrType = display.attr('data-type');
1862                                    if ( attrType == 'date') format = 'L';
1863                                    else if ( attrType == 'datetime') format = 'L LT';
1864                                    else if ( attrType == 'time') format = 'HH:mm';
1865                                }
1866                         
1867//                                if ( field.type == 'datetime' ) display.attr('data-value', field.get('string', 'YYYY-MM-DD HH:mm:ss'));      
1868//                                else if ( field.type == 'time' ) display.attr('data-value', field.get('string', 'HH:mm:ss'));                                     
1869//                                else 
1870//                                    
1871                                display.attr('data-value', field.getForWeb('plain'));
1872                                
1873                                
1874                                if ( display.attr('data-type') == 'link') {
1875                                    var linkContent = field.getForWeb();
1876                                    var url = field.get().trim();
1877                                    if ( isEmail(url) ) url =  'mailto:' + url;
1878                                    var link = display.find('>a');
1879                                    link.attr('href', url);
1880                                    link.html( field.getForWeb() );
1881                                }
1882                                else 
1883                                    if ( display.attr('data-type') == 'boolean') {
1884                                    if (field.get() == 1) display.html('<span data-icon="1"></span>');
1885                                        else  display.html('<span data-icon="0"></span>');
1886                                } else if ( type == 'password') {
1887                                    var text = '';
1888                                    for (var i=0; i<field.get('string').length; i++) text += '&#8226;'
1889                                    display.html( text );                                 
1890                                }
1891                                else if( display.prop('tagName') == 'SELECT') {
1892                                    display.find('[value]:not([value="' + field.getForWeb() + '"])').removeAttr('selected');
1893                                    display.find('[value="' + field.getForWeb() + '"]').attr('selected', 'selected');
1894                                }                                 
1895                                else {                                    
1896                                    display.html(field.getForWeb( format ));
1897                                }
1898                                                             
1899                            
1900
1901                        }
1902                        display.triggerHandler('doneRefresh', event);
1903
1904                };   
1905
1906
1907                if ( type == 'picture') {
1908//                    return;
1909//                    console.log(this);
1910//                    console.log(this.get());
1911//                    alert(this.attribute('foreign') );
1912//            alert(this.get('string'));
1913            
1914                    display.html('');
1915                    
1916                    var path = '';
1917                    
1918                    if (typeof this.attribute('foreign') != 'undefined') {
1919                        console.log(this);
1920                        console.log(this.attribute('foreign'));
1921//                        if (typeof Model.views[ this.attribute('foreign') ] == 'undefined') console.log('ERROR: Picture - foreign view not loaded - ' + this.attribute('foreign') );
1922//                        path = Model.views[ this.attribute('foreign') ].path;
1923                    }
1924                    
1925                    // Timeout required for CSS to apply 
1926                    setTimeout(function(){var module = PictureModule( { element: display, field: field } );}, 1);
1927                    
1928
1929                } 
1930                
1931                
1932                
1933                else if ( type == 'file') {
1934                    
1935                    var path = '';                    
1936                    if (typeof this.attribute('foreign') != 'undefined') {
1937//                        console.log(this);
1938                        path = Model.views[ this.attribute('foreign') ].path;
1939                    }
1940                    
1941                    var module = FileModule( { element: display, fileView: Model.views[ this.attribute('foreign') ], presentation: 'filename'  } );
1942                    if ( this.get() != '' ) {
1943//                        ignoreTrigger = true;
1944                        module.setFile( path + this.get() );
1945//                         console.log(this);
1946                    }
1947//                    console.log (module);
1948                    
1949                    $(module).on('pictureSelected', function( event, url ) {
1950//                        console.log( 'SELECTED');
1951//                        console.log( url.substring( path.length ) );
1952//                        console.log( url );
1953//                        console.log( module.pictureEntity.get('id'));
1954//                        console.log( module.pictureEntity.get('filename'));
1955
1956                          field.set( url.substring( path.length ) );       // remove path from url;
1957
1958                    } );
1959                    
1960                     $(this).on('changed', display, function( event ){
1961                         if ( this.get() != '' ) module.setPicture
1961( path + this.get() );
1962                     });                
1963
1964                } 
1965                
1966                else  $(this).on('changed', display, this.defaultOnChangeHandler);
1967                
1968		
1969		//console.log('FIELD');
1970		//console.log(display);
1971                
1972                
1973             
1974                
1975                
1976                $(document).trigger('fieldDisplayReady', display);
1977		
1978		return display;
1979	}
1980	
1981	this.restore = function()
1982	{
1983		if (isset(this.entity.dataArchive[this.name]))
1984		{
1985			console.log('restore');
1986			console.log(this.entity.dataArchive[this.name]);
1987			
1988				this.set(this.entity.dataArchive[this.name][0]);
1989				$(this).trigger('restored', this);
1990		}		
1991		
1992	};
1993	
1994		
1995	
1996	
1997	this.getLabel = function() {
1998//            console.log(this);
1999//            return this.entity.view.fields[this.name].getTitle();
2000            return this.entity.view.getFieldTitle( this.name );
2001	}
2002	
2003        
2004        this.isPrimary = function(){
2005            return this.name == this.entity.getPrimaryFieldName();
2006        }
2007	
2008        
2009        this.desyncWithField = function( fieldOrOptions ) {
2010            // REMOVES SYNC CONNECTION BETWEEN TWO FIELDS
2011		var field;
2012		if (typeof fieldOrOptions.field !== 'undefined') {
2013			field = fieldOrOptions.field;
2014		} else field = fieldOrOptions;
2015		
2016		var thisField = this;
2017                var syncData;
2018                for (var index = this.syncFields.length-1; index >= 0; index--) {
2019                    if ( this.syncFields[index].sourceField == field ) {
2020                        syncData = this.syncFields[index];
2021                        // remove all events
2022                        for (var eventIndex = 0; eventIndex < this.syncFields[index].eventList.length; eventIndex++) {
2023                            this.syncFields[index].eventList[eventIndex].off();
2024                        }
2025                        // remove syncField;
2026                        this.syncFields.splice(index, 1);
2027                    } 
2028                }
2029                return syncData;
2030        }
2031
2032	this.syncWithField = function( fieldOrOptions ) {
2033		// Connects the fields of two different entities
2034		// Changing one field value also triggers a change of the other
2035		// If bidirectional = false or field values are different, THIS field value is changed 
2036		// PRIMARY FIELDS ARE NEVER CHANGED BY SYNC!!!
2037                
2038                var syncData = {};
2039                
2040		var bidirectional = true;
2041		var field;
2042		if (typeof fieldOrOptions.field !== 'undefined') {
2043			field = fieldOrOptions.field;
2044			if (typeof fieldOrOptions.bidirectional !== 'undefined') {
2045				bidirectional = fieldOrOptions.bidirectional;
2046			}
2047
2048		} else field = fieldOrOptions;
2049		
2050		var thisField = this;
2051                
2052                // PREVENT MULTIPLE SYNCS WITH THE SAME ENTITY.FIELD
2053                for (var index = 0; index < this.syncFields.length; index++) {
2054                    if ( this.syncFields[index].sourceField == field ) return this.syncFields[index]; 
2055                }
2056                
2057                syncData.sourceField = field;
2058                syncData.bidirectional = bidirectional;
2059                syncData.eventList = [];                        // eventList used to unbind events later
2060                this.syncFields.push(syncData);
2061		// Make equal
2062		if ( thisField.get() != field.get() )
2063			thisField.set( field.get() );
2064		
2065		
2066		// trigger store if parent field was stored
2067
2068		// field.entity.store.onSuccess( () => {
2069		// 	thisField.entity.store( );
2070		// });	
2071
2072/*
2073		syncData.eventList.push( $(field.entity).onEvent('stored', function(event, data) {		
2074			if (thisField.entity.status() == STATUS_CHANGED) thisField.entity.store( );				
2075		}) );		
2076*/
2077		
2078		
2079		// IF PARENT FIELD IS PRIMARY WAIT FOR IT TO BE STORED BEFORE STORING SELF
2080		// REQUIRED FOR PRIMARY TO BE SET BY DATABASE
2081//		if ( field.entity.getPrimaryFieldName() == field.name ) {		
2082		if ( field.isPrimary() ) {	
2083			thisField.entity.store.onBefore.hire( () => {
2084				if ( field.entity.status() == STATUS_CHANGED || field.entity.status() == STATUS_NEW || field.entity.trash( ) ) return field.entity.store; 
2085				else return true;
2086			});
2087			
2088/*
2089			syncData.eventList.push( $(thisField.entity).onEvent('beforeStored', function(event, data) {
2090//                            alert(field.name + '  ' + field.get() + '  ' + field.entity.status());
2091					data.preventStore = (field.entity.status() == STATUS_CHANGED || field.entity.status() == STATUS_NEW );
2092			}) );	
2093*/
2094		};		
2095		
2096		
2097		syncData.eventList.push( $(field).onEvent('changed', function(event){
2098			thisField.set(field.get());
2099		}) );
2100		
2101                
2102		if (bidirectional) {
2103			syncData.eventList.push( $(thisField).onEvent('changed', function(event){
2104                            if (field.isPrimary() == false) field.set(thisField.get());
2105			}) );			
2106		}
2107                
2108                return syncData;
2109	}
2110};
2111
2112
2113function Entity(initialId) {
2114	var self = this;
2115	this.class = 'Entity';
2116	this.data = {};
2117	this.dataArchive = {};
2118	this.fields = {};
2119	this.dataStatus = STATUS_UNCHANGED;
2120	this.view = undefined;
2121	this.viewName = 'unknown';
2122	this.entitySet = undefined;
2123	this.isNew = false;
2124	
2125//	var privateId = initialId;
2126	var privateTrash = false;
2127	/*
2128	this.id = function(newId)
2129	{
2130		if (isset(newId)) privateId = newId;
2131			else return privateId;
2132	}
2133	*/
2134	this.status = function(newStatus)
2135	{
2136		if (isset(newStatus) == false) return this.dataStatus;
2137		if (this.dataStatus != newStatus) {
2138			this.dataStatus = newStatus;
2139			$(this).trigger('statusChange', newStatus);
2140		}
2141	}
2142        
2143	this.trash = function(trashStatus)
2144	{	
2145		if (isset(trashStatus) == false) return privateTrash;
2146//		console.log('TRASH');
2147		if (privateTrash == trashStatus) return;
2148		privateTrash = trashStatus;
2149		
2150		$(this).trigger("trashStatus", this);		
2151		$(this).trigger("changed", this);		
2152	}
2153
2154	this.set = function (fieldName, value, type, format, silent)
2155	{
2156//		console.log(fieldName);
2157		if (typeof fieldName != 'string') {
2158                        // FIRST VALUE IS OBJECT
2159			value = fieldName.value;
2160			type = fieldName.type;
2161			format = fieldName.format;
2162			silent = fieldName.silent;
2163			fieldName = fieldName.field;
2164		}
2165
2166		// check if fieldName is in other view - then search for Relation
2167		var parts = fieldName.split('.');
2168		if ( parts.length > 1 ){
2169			if (typeof Model.relations[ this.view.name ] != 'undefined' && typeof Model.relations[ this.view.name ][ parts[0] ] != 'undefined' ) {
2170				fieldName = Model.relations[ this.view.name ][ parts[0] ].fieldName();
2171			} else console.log ('ERROR: VIEW "' + this.viewName + '" does not have FIELD OR RELATION "' + fieldName + '"', this, value);
2172		}
2173
2174		if (isset(this.fields[fieldName]) == false) console.log ('ERROR: VIEW "' + this.viewName + '" does not have FIELD "' + fieldName + '"', value);
2175		this.fields[fieldName].set(value, type, format, silent);		
2176	};
2177	
2178	this.get = function (fieldName, type, format)
2179	{
2180		//console.log(fieldName);
2181		//console.log(this);
2182		if (isset(this.fields[fieldName]) == false) console.log ('ERROR: VIEW "' + this.viewName + '" does not have FIELD "' + fieldName + '"');
2183		return this.fields[fieldName].get(type, format);		
2184	};
2185	
2186	this.getForWeb = function (fieldName, format)
2187	{
2188		if (isset(this.fields[fieldName]) == false) console.log ('ERROR: VIEW "' + this.viewName + '" does not have FIELD "' + fieldName + '"');
2189		return this.fields[fieldName].getForWeb(format);	
2190	};
2191	
2192	this.getPrimary = function(type)
2193	{
2194		if ( this.getPrimaryFieldName() == false ) return false; 
2195		return this.get(this.getPrimaryFieldName() , type);
2196	}
2197
2198	this.getPrimaryField = function( )
2199	{
2200//            console.log(this);
2201
2202            if (typeof this.view == 'undefined' ) return this.fields[ 'id' ];
2203            var primaryField = this.view.getPrimaryField();
2204            if (typeof primaryField == 'undefined' ) return this.fields[ 'id' ];
2205            return this.fields[ primaryField.name ];
2206//		return this.fields['id'];
2207	}
2208
2209	
2210	this.getPrimaryFieldName = function()
2211	{
2212            var field = this.getPrimaryField();
2213            if (typeof field === 'undefined' ) return false;
2214            return field.name;
2215	}
2216        
2217        this.getTitle = function() {
2218            var titleField = this.getTitleField();
2219            if (titleField == false) return '';
2220            return titleField.get('string');
2221        }
2222        
2223        this.getTitleField = function( )
2224	{
2225            if (typeof this.view === 'undefined') false;
2226            if (typeof this.view.displayField !== 'undefined' &&  this.view.displayField != '' ) return this.fields[ this.view.displayField ];
2227            return false;
2228	}
2229        
2230//	
2231	
2232	this.validate = function( filterOrArrayOrQuery ) {
2233            
2234                if (typeof filterOrArrayOrQuery === 'undefined' ) return true;
2235                if (typeof filterOrArrayOrQuery === 'function' ) return filterOrArrayOrQuery( this );
2236                
2237		
2238		// check if entity data corresponds to all filters		
2239		var filters;
2240		var thisEntity = this;
2241		if (filterOrArrayOrQuery instanceof Query) filters = filterOrArrayOrQuery.filters;
2242		else if ( typeof filterOrArrayOrQuery.comparator !== 'undefined') filters = [ filterOrArrayOrQuery ];
2243		else if ( Array.isArray( filterOrArrayOrQuery ) ) filters = filterOrArrayOrQuery;
2244		if (isset( filters ) == false) alert('ERROR setting entity filters. Filter object not given.\nTYPE: '+ typeof filterOrArrayOrQuery);
2245		
2246		var valid = true;
2247		$.each(filters, function( index, filter ) {
2248			if ( filter.validate( thisEntity ) == false ) {
2249                            valid = false;
2250                        }
2251		});
2252		return valid;
2253	}
2254	
2255	
2256	this.forceFilters = function ( filters )
2257	{
2258		// Sets entity values according to filter
2259		// ++ parameter filters can be filterArray OR query
2260
2261		console.log('FORCE FILTERS');		
2262
2263		if (isset(filters) == false) return;
2264//		console.log(filters.constructor.name);		
2265//		console.log(filters);		
2266		var filterArr = [];
2267//		if (isset(filters.getValue)) filterArr.push(filters); // SINGLE FILTER
2268		if (filters.constructor.name == 'Filter') filterArr.push(filters); // SINGLE FILTER
2269		else filterArr = filters.filters;		
2270		
2271//		console.log(filterArr);		
2272		
2273		
2274		for (var index in filterArr) {			
2275			var filter = filterArr[index];
2276			if (filter.comparator == '=') this.set(filter.getField(), filter.getValue(), 'string');
2277		}		
2278	}
2279	
2280	this.getRelationSet = function( options )
2281	{
2282            // ONLY SUPPORTS NEXT RELATION, NO DEEPER RELATIONS
2283            // SYNCHRONIZES PARENT FIELD
2284            
2285		var parentEntity = this;
2286                
2287		var params;
2288		if (typeof options === 'string' || options instanceof String || options.constructor.name == 'Query') params = { query: options };
2289			else params = options;
2290		
2291		var query = this.getRelationQuery(params.query);
2292		
2293		var relation = Model.relations[this.viewName][query.relation];
2294//		var relation = Model.relations[this.viewName][query.view];
2295		
2296		var relationCacheName;
2297                
2298                if (typeof params.cacheName !== 'undefined' ) relationCacheName = this.viewName + '_' + this.getPrimary('string') + '_' + params.cacheName;
2299                    else relationCacheName = relationCacheName = this.viewName + '_' + this.getPrimary('string') + '_' + relation.relationViewName;
2300//		if ( isset( this.relationCachePrefix ) == false ) this.relationCachePrefix = this.viewName + '_' + this.getPrimary('string') + '_';
2301//		if (isset (params.cacheName) ) relationCacheName = this.relationCachePrefix + params.cacheName;
2302//                
2303                
2304                
2305//                $(parentEntity.getPrimaryField()).on('changed', function() {
2306//                   alert('new relation anem needed' + parentEntity.data[parentEntity.getPrimaryFieldName()]); 
2307//                    relationCacheName ='';
2308//                    if ( isset( parentEntity.relationCachePrefix ) == false ) parentEntity.relationCachePrefix = parentEntity.viewName + '_' + parentEntity.data[parentEntity.getPrimaryFieldName()] + '_';
2309//                    if (isset (params.cacheName) ) relationCacheName = parentEntity.relationCachePrefix + params.cacheName;
2310//                   alert(relationCacheName); 
2311//                   
2312//                });
2313
2314//		alert( params.cacheName );
2315//		alert( 'cacheName: ' + relationCacheName );
2316//		
2317
2318/*		
2319		if (false) {
2320			var entitySet = new EntitySet({ 'cacheName': relationCacheName });
2321			entitySet.hookFilters(query);
2322			entitySet.view = Model.views[query.view];
2323		}		
2324		else entitySet = new EntitySet({ 'cacheName': relationCacheName, 'query': query });
2325*/
2326                if (this.isNew) {
2327                    var entitySet;
2328                    // CHECK IF RELATIONSET ALREADY CACHED
2329                    if ( typeof EntityCache.setCache[relationCacheName] !== 'undefined' ) {
2330                                          //  alert(relationCacheName);
2331                        // ASSIGN CACHED SET
2332                        entitySet = new EntitySet( { 'cacheName': relationCacheName });                        
2333                    } else {
2334                        // APPLY QUERY ON ENTITY CACHE THEN CACHE RELATIONSET
2335//                        console.log(query);
2336//                        console.log(options);
2337                        if (typeof EntityCache.entityCache[ query.view ] !== 'undefined') {
2338                            entitySet = EntityCache.entityCache[ query.view ].applyQuery( query );
2339                            EntityCache.setCache[ relationCacheName ] = entitySet;
2340    //                        console.log(entitySet);        
2341    //                        alert('NEW');
2342                        } else entitySet = new EntitySet({ 'cacheName': relationCacheName, 'query': query });
2343                    }
2344                    
2345
2346                    setTimeout( function( ){ 
2347                        entitySet.status('ready');
2348			$(entitySet).trigger( 'ready' );
2349                        if (typeof options.callback !== 'undefined') options.callback( entitySet ); 
2350                    }, 100);
2351                    
2352                    
2353                } else {                    
2354                    entitySet = new EntitySet({ 'cacheName': relationCacheName, 'query': query, 'callback': options.callback}, options.async );
2355                }		
2356                entitySet.view = Model.views[query.view];
2357                entitySet.query = query;			
2358                
2359                
2360//		console.log(query);
2361//		console.log(entitySet);
2362
2363		
2364		// STORE PARENTENTITY BEFORE ENTITY
2365		// TO DETERMINE PRIMARY
2366		$(entitySet).on('entityAdded', function(event, entity){			
2367/*
2368			if (entity.isNew) {	
2369				entity.set(relation.relationFieldName(), parentEntity.get(relation.fieldName()));
2370			}
2371
2372			$(parentEntity).on('stored', function(event, data) {
2373				console.log('STATUS');				
2374				console.log(entity.status());				
2375				console.log(entity.status() == STATUS_CHANGED);			
2376				if (entity.status() == STATUS_CHANGED) entity.store();				
2377			});		
2378			
2379			$(entity).on('beforeStored', function(event, data) {
2380					data.preventStore = (parentEntity.status() == STATUS_CHANGED);
2381			});		
2382			
2383			*/
2384                       
2385			entity.fields[relation.relationFieldName()].syncWithField( parentEntity.fields[relation.fieldName()] );
2386			
2387                        
2388                        
2389/*
2390			$(parentEntity.getPrimaryField()).on('changed', function(event){
2391				entity.set(relation.relationFieldName(), parentEntity.get(relation.fieldName()));
2392			});
2393*/			
2394			
2395			
2396		});		
2397		
2398		
2399		return entitySet;
2400		
2401		
2402	}
2403	
2404	
2405	this.getRelationQuery = function( params1 ) {
2406            
2407                // DOES NOT LINK RELATED FIELDS, USE getRelationSet instead!
2408            
2409		var query = new Query(params1);
2410                var thisEntity = this;
2411		if (typeof  Model.relations[this.viewName][query.view] === 'undefined') console.log('ERROR: View '+ this.viewName + ' does not have a relation to View ' + query.view);
2412		var relation = Model.relations[this.viewName][query.view];
2413		
2414		// query.view is NAME of relation!
2415		// therefore overwrite query.view by relation.relationViewName
2416		query.view = relation.relationViewName;
2417		query.relation = relation.name;
2418		
2419		
2420		var filter = new Filter();
2421		filter.set(relation.relationFieldName(), thisEntity.get(relation.fieldName())) ;   
2422		
2423//		$(thisEntity.fields[relation.fieldName()]).on('changed', function() {
2424//			filter.set(relation.relationFieldName(), thisEntity.get(relation.fieldName())) ;   
2425//		});
2426		query.filters.push(filter);
2427                return query;
2428	}
2429        
2430        this.loadRelations = function(relationString, callback, async) {		
2431                //STRING EXAMPLE: 'RELATION invoice id = "xy" RELATION invoice_items amount > 2 RELATION products');
2432            var es = new EntitySet();
2433            es.loadRelations('RELATION ' + this.view.name + ' ' + this.view.getPrimaryField().name  + ' = "'+ this.getPrimary('string') + '" ' + relationString, callback, async);
2434//            es.loadRelations('RELATION ' + this.view.name + ' ' + this.view.getPrimaryField().name  + ' = "'+ this.getPrimary('string') + '" RELATION ordered_webinars RELATION orders RELATION users', callback, async);
2435            return es;
2436        }
2437        
2438	
2439	this.setView = function(view)
2440	{
2441
2442		this.viewName = view.name;
2443		this.view = view;
2444
2445		for (var fieldName in view.fields)
2446		{
2447
2448                    this.addField(fieldName, this.data[ fieldName ], view.fields[ fieldName ].t
2448ype, view.fields[ fieldName ].format);
2449                    
2450                    /*
2451			var field = new EntityField(this, fieldName);
2452			//field.entity = this;
2453			field.type = view.fields[fieldName].type;
2454			field.format = view.fields[fieldName].format;			
2455			this.fields[fieldName] = field;                        
2456//			$(document).trigger('newField', field);
2457                    */
2458		}			
2459	}
2460	
2461	
2462	/*
2463	this.getHTML = function(fieldNames, tagName) {
2464		
2465		if (isset(fieldNames) == false) 
2466		{
2467			fieldNames = Object.keys(this.fields);
2468		}
2469		if (isset(tagName) == false) tagName = 'div';
2470		
2471		var result = '<li data-structure="entity" data-id="' + this.id() + '">';
2472
2473		for (var index in fieldNames) {
2474			var fieldName = fieldNames[index];
2475			result += '<' + tagName + ' data-structure="field" data-type="' + this.fields[fieldName].type + '" data-field="' + fieldName + '" data-id="' + this.id() + '" data-view="' + this.viewName + '">' + this.getForWeb(fieldName) + '</' + tagName + '>';
2476		}
2477		
2478		result += '</li>';
2479
2480		return result;
2481	};
2482	*/
2483	
2484	this.toDisplay = function(options) {
2485		if (typeof options == 'string') alert('ERROR: toDisplay Function outdated, use OPTION object');
2486		// OPTIONS { fieldNames, tagName, additionalFunction, display }
2487		
2488		if (isset(options) == false) options = {};		
2489		if (isset(options.fieldNames) == false) 
2490		{
2491			//fieldNames = [];
2492			//for(var k in this.fields){console.log(k); fieldNames.push(k)};
2493			options.fieldNames = Object.keys(this.fields);
2494		}
2495		
2496		if (isset(options.tagName) == false) options.tagName = 'div';
2497		
2498		//console.log(this.fields);
2499		//console.log(fieldNames);
2500		//var obj = $(jQuery.parseHTML(this.getHTML(fieldNames, tagName)));		
2501		if (isset(options.display)) 
2502			var obj = options.display.element || options.display;		
2503		else var obj = $(jQuery.parseHTML('<' + options.tagName + ' data-structure="entity" data-view="'+ this.viewName +'" data-id="' + this.getPrimary('string') + '"></' + options.tagName + '>'));		
2504		
2505		for (var index in options.fieldNames) {
2506			//if (isset(this.fields[fieldNames[index]].attribute('function')) == false)
2507			obj.append(this.fields[options.fieldNames[index]].toDisplay( ));
2508		}
2509		
2510		
2511		if (isset(options.additionalFunction)) options.additionalFunction(obj);
2512		
2513		obj.data('entity', this);
2514		obj.find('[data-structure="field"]').data('entity', this);
2515		/*
2516		obj.find('[data-structure="field"]').on('changed',  function(event, fieldName) {
2517			display.html(this.get(fieldName))
2518				.css('color', 'blue');
2519		});	
2520		*/
2521		/*
2522		$(obj.entity).on('changed', function(e, fieldName){
2523			console.log('changed');
2524			console.log(obj);
2525			obj.css('color', 'red');
2526		})
2527		*/
2528               
2529               
2530                // ++ REMOVE entityDisplayReady EVENT IF POSSIBLE
2531                // ++ USE displayInserted EVENT INSTEAD
2532                
2533		if (isset(obj.data('entity').view)) $(document).trigger('entityDisplayReady_' + obj.data('entity').view.name, obj);
2534		$(document).trigger('entityDisplayReady', obj);
2535		
2536		return obj;	
2537	};
2538
2539	
2540	this.clone = function() {
2541		var newEntity = this.view.getBlankEntity();
2542		var primaryFieldName = this.getPrimaryFieldName();
2543		for (var fieldName in this.fields) {
2544			if (fieldName != primaryFieldName) {
2545				newEntity.set({field: fieldName, value: this.get(fieldName), silent: true});
2546			}
2547		}
2548		EntityCache.add(newEntity);
2549		return newEntity;
2550	}
2551	
2552	this.cloneWithChildren  = function( fullDepth ) {
2553		
2554		if ( typeof fullDepth === 'undefined' ) fullDepth = true;
2555		
2556		var originalEntity = this;
2557		var newEntity = this.clone();
2558		var primaryFieldName = this.getPrimaryFieldName();
2559		console.log('clone');
2560		console.log(newEntity);
2561		$.each(Model.relations[this.view.name], function() {
2562//			console.log(Model.relations[this.view.name][index]);
2563//			console.log(Model.relations[this.view.name][index].relationFieldName());
2564//			console.log(Model.relations[this.view.name][index].fieldName());
2565			
2566
2567			console.log(this);
2568			console.log(this.viewName);
2569
2570			var relationFieldName = this.relationFieldName();
2571			console.log(relationFieldName);
2572			if ( primaryFieldName == this.fieldName() ) {
2573				var relatedSet = originalEntity.getRelationSet( { query: 'VIEW ' + this.relationViewName, callback: function(entitySet) {
2574					console.log(newEntity.getPrimary() );
2575						console.log(entitySet);
2576						$.each(entitySet.entities, function() {
2577							var clonedChild;
2578							if ( fullDepth) clonedChild = this.cloneWithChildren();
2579								else clonedChild = this.clone();								
2580							clonedChild.fields[relationFieldName].syncWithField( newEntity.getPrimaryField() );
2581
2582//							clonedChild.set( relationFieldName, newEntity.getPrimary() );
2583							console.log(clonedChild);
2584						});
2585				} });
2586				
2587			}
2588		});
2589
2590		return newEntity;
2591		
2592	}
2593	
2594	this.toArrayOfStrings = function( changesOnly ){
2595            // NOT REALLY ARRAY, IS OBJECT OF KEY / VALUES, CAN BE STRINGIFIED
2596            if ( typeof changesOnly === 'undefined' ) changesOnly = false;
2597          
2598            var dataArr = {};
2599            for (fieldName in this.fields) 
2600                    if (changesOnly == false || this.fields[fieldName].status() != STATUS_UNCHANGED) {
2601                            if (this.fields[fieldName].attribute('type') == 'html')
2602                                    dataArr[fieldName] = this.get(fieldName, 'html'); 
2603                            else if (this.fields[fieldName].attribute('type') == 'currency')        // PROBLEMS WITH , and . OTHERWISE
2604                                    dataArr[fieldName] = this.get(fieldName).toString();
2605                            else dataArr[fieldName] = this.get(fieldName, 'string');
2606                    }
2607            var entityArr = {'type': 'entity', 'view' : this.viewName, 'id' : this.getPrimary('string'), 'new' : this.isNew, 'status' : this.status(), 'data' : dataArr};
2608            return entityArr;
2609        };
2610	
2611	
2612	this.select = function()
2613	{
2614		$(this).trigger('selected');
2615	}
2616	
2617	this.delete = function() {
2618		this.trash(true);
2619		this.store();
2620	}
2621	
2622        
2623        
2624        
2625        EventMaker( this, 'store');
2626        
2627        
2628	this.onStore( (options) => {
2629            
2630//            console.log ('store', options, this);
2631            
2632            
2633                // returns job
2634                // otherJob.hire( entity.store() ) executes store() once and immediately
2635                // otherJob.hire( entity.store ) hires the "store" function and executes each time on otherJob.do()
2636                
2637            
2638                $(this).triggerHandler('storeRequested');     // triggert even if no STORE needed. Required for other entities depending on this one
2639
2640		if (isset( this.storage )) {
2641                    return this.storage.store(this);			
2642		}
2643		        
2644		if (options == EntityStorage)
2645		{
2646                    return EntityStorage.store(this);			
2647		}
2648                
2649                var storeJob = new Job();
2650                storeJob.isDoing;
2651//             storeJob.isDoing = true;   
2652//            setTimeout( function(){
2653//                storeJob.doFail( );
2654//                
2655//            }, 2000);
2656//            return storeJob;
2657                
2658                
2659                storeJob.info = 'store entity - ' + this.viewName;
2660                
2661//                alert( storeJob.stateSelf);
2662
2663
2664		var dataStore;
2665		var thisEntity = this;
2666
2667		if (isset(options) == false) {
2668			options = {};
2669		}
2670		if (isset(options.dataStore) == false) {
2671			dataStore = 'provider/plugins/data/' + this.viewName + '/';
2672			if (typeof DATA_URL != 'undefined') dataStore = DATA_URL + dataStore;
2673		} else if (isString(options)) {
2674			dataStore = options;
2675		} 
2676		
2677		var url = dataStore;
2678		
2679		
2680		
2681		if (this.trash())
2682		{
2683			if (this.isNew) {
2684				$(self).triggerHandler('deleted');
2685				$(document).triggerHandler('entityDeleted', self);
2686//				self = undefined;
2687				self.status(STATUS_DELETED);
2688                                
2689                                if (typeof options.success === "function") 	options.success( { entity: this, success: true, action: 'delete'  } );
2690				if (typeof options.callback === "function")	options.callback( { entity: this, success: true, action: 'delete'  });
2691				
2692				delete(self);
2693//				console.log(self);
2694//				console.log(this);
2695				return storeJob.doSuccess();
2696			}
2697			
2698			$.ajax({
2699				url: url,
2700				type: 'POST',
2701				context: this,
2702				data: {'items':[{'type': 'entity', 'view' : this.viewName, 'id' : this.getPrimary('string'), 'delete' : true, 'status' : this.status()}]},
2703				//data: JSON.stringify(dataArr, null, 2),
2704				//dataType: 'json',
2705			}).done(function(data) {
2706				
2707				console.log('DELETED');
2708				$(self).triggerHandler('deleted');
2709				$(document).triggerHandler('entityDeleted', self);
2710				self.status(STATUS_DELETED);
2711                                
2712                                if (typeof options.success === "function") 	options.success( { entity: self, success: true, action: 'delete'  } );
2713				if (typeof options.callback === "function")	options.callback( { entity: self, success: true, action: 'delete'  });
2714				
2715                                
2716				self = undefined;
2717				console.log(self);
2718                                
2719				storeJob.doSuccess();
2720				
2721				//$(this).trigger( "stored" );
2722			});	
2723			
2724			return storeJob;	
2725		}
2726		
2727		if (this.status() == STATUS_UNCHANGED) {
2728                    if (typeof options.success === "function") 	options.success( { entity: this, success: true, action: 'none'  } );
2729                    if (typeof options.callback === "function")	options.callback( { entity: this, success: true, action: 'none'  });
2730
2731                    return storeJob.doSuccess();
2732                }
2733
2734//                    console.log(this);
2735//                    alert('x ' + this.get('id'));
2736
2737		var dataArr = this.toArrayOfStrings( true );
2738                
2739		var eventData = { 
2740			preventStore : false,
2741			storeJob: storeJob
2742		 };
2743
2744
2745		
2746
2747
2748
2749
2750		$(this).triggerHandler('beforeStored', eventData);
2751		if (eventData.preventStore) {
2752			//alert('prevented');
2753			// storeJob.doFail();
2754			console.log('store prevented ' + this.getPrimary('string'));
2755			$(this).triggerHandler('storePrevented');
2756                        
2757                        if (typeof options.callback === "function")	options.callback( { entity: this, success: false, action: 'store', error: 'prevented'  });
2758
2759						return storeJob.doFail();
2760                        return storeJob;
2761		}
2762		
2763		//console.log(JSON.stringify(dataArr, null, 2));
2764		//console.log(dataArr);
2765		$.ajax({
2766			url: url,
2767			type: 'POST',
2768			context: this,
2769			data: {'items':[ dataArr ]},
2770			//data: JSON.stringify(dataArr, null, 2),
2771			dataType: 'json',
2772		}).done(function(data) {
2773			
2774			// Confirm entity was stored
2775			// Retrieve id of new entity
2776			// ++ check changes of other fields by server
2777			
2778			if (isset(data['data'][0]))  {
2779				console.log(thisEntity.getPrimary('string'));
2780				
2781				// COMPAIR FIELDS (id and others)
2782				for (var fieldName in data['data'][0]) {
2783					
2784					if (isset(thisEntity.fields[fieldName]) && thisEntity.fields[fieldName].attribute('type') == 'html' )
2785					{
2786						if (thisEntity.data[fieldName] != data['data'][0][fieldName]) {
2787						thisEntity.set(fieldName, data['data'][0][fieldName], 'string');
2788//						alert('DIF - field: ' + fieldName + ' Value: '+data['data'][0][fieldName]);
2789						}
2790					}  else if (isset(thisEntity.fields[fieldName]) && thisEntity.get(fieldName, 'string') != data['data'][0][fieldName]) {
2791						thisEntity.set(fieldName, data['data'][0][fieldName], 'string');
2792//						alert('DIF - field: ' + fieldName + ' Value: '+data['data'][0][fieldName]);
2793					}
2794					
2795				}
2796				
2797				
2798				if (thisEntity.isNew) {
2799//					thisEntity.set(thisEntity.getPrimaryFieldName(), data['data'][0][thisEntity.getPrimaryFieldName()], 'string');
2800
2801					//thisEntity.privateId = thisEntity.getPrimary();
2802					//thisEntity.id(thisEntity.getPrimary());
2803					//console.log(thisEntity.getPrimary());
2804					//console.log(thisEntity.id());
2805					thisEntity.isNew = false;					
2806				}
2807				
2808			}
2809                        
2810                        thisEntity.status(STATUS_UNCHANGED);
2811                        for (var fieldName in thisEntity.fields)
2812                        {
2813                            thisEntity.fields[fieldName].status( STATUS_UNCHANGED );
2814                        }
2815                        
2816                        
2817			console.log('STORED ENTITY');
2818                        console.log(thisEntity);
2819                        if (typeof options.success === "function") 	options.success( { entity: this, success: true, action: 'store'  } );
2820                        if (typeof options.callback === "function")	options.callback( { entity: this, success: true, action: 'store'  });
2821                        $(thisEntity).trigger( "stored" );
2822
2823                        storeJob.doSuccess();
2824			
2825		}).error(function(data, textStatus, errorThrown ) {
2826			console.log('Error storing entity!');
2827			console.log(data);
2828			console.log(textStatus);
2829			console.log(errorThrown);
2830//			alert('Error storing entity!');
2831			if (typeof options.error === "function") 	options.error( { entity: this, success: false, action: 'store', error: textStatus + ' - ' + errorThrown  } );
2832			if (typeof options.callback === "function")	options.callback( { entity: this, success: false, action: 'store', error: textStatus + ' - ' + errorThrown  } );
2833			console.log( storeJob );
2834                        storeJob.doFail( { error: textStatus } );
2835		});			
2836		
2837		return storeJob;
2838		
2839		
2840		/*
2841		var ns= $.initNamespaceStorage('ns_nameXX' + this.data['id']); // Namespace in localStorage
2842		var storage = ns.localStorage;
2843		storage.set('xxx', this.data );
2844		*/
2845	});
2846        
2847        
2848        
2849        
2850        
2851        
2852        
2853        
2854        
2855        
2856        
2857        
2858	
2859	
2860	this.restore = function() {
2861		
2862		for (var index in this.fields) 
2863		{
2864			this.fields[index].restore();
2865		}
2866		this.status( STATUS_UNCHANGED );
2867		
2868		/*
2869		var ns= $.initNamespaceStorage('ns_nameXX' + this.data['id']); // Namespace in localStorage
2870		var storage = ns.localStorage;
2871		storage.set('xxx', this.data );
2872		*/
2873	};
2874	
2875	
2876	this.addField = function(fieldName, value, type, format)
2877	{
2878		if (typeof value !== 'undefined')  this.data[fieldName] = value;
2879		var field = new EntityField(this, fieldName);
2880		field.entity = this;
2881		if (typeof type !== 'undefined')  field.type = type;
2882		if (typeof format !== 'undefined')  field.format = format;
2883		this.fields[fieldName] = field;
2884//		$(document).trigger('newField', field);
2885
2886
2887                // DEFINE GETTER AND SETTER
2888                // DO NOT CREATE IF OTHER PROPERY WITH THAT NAME EXISTS ALREADY
2889//                if (typeof this[fieldName] === 'undefined') => typeof result, does not show if property is defined
2890                if ( Object.getOwnPropertyDescriptor(this, fieldName) === undefined )                    
2891                    Object.defineProperty( this, [ fieldName ], {
2892                        set: function( value ) {
2893                            this.set( fieldName, value );
2894                        },
2895                        get: function() { 
2896                            return this.get( fieldName );
2897                        }
2898                    });
2899
2900		return field;
2901	}
2902	
2903	
2904};
2905
2906
2907
2908
2909function EntitySet( options, async ) {
2910	var thisEntitySet = this;
2911	
2912	if (isString(options)) options = { query: new Query( options )};
2913
2914	if (isset( options ) == false ) options =  {};
2915
2916	if (options.constructor.name == 'Query') options.query = options;
2917	
2918	this.query = options.query || undefined;
2919
2920	var status = 'new';
2921	
2922	this.entities = {};
2923	this.trash = {};
2924	this.name = options.name || 'unknown';
2925	this.cacheName = options.cacheName;
2926	this.fields = {};
2927        this.async = true;
2928        if (typeof async !== 'undefined') this.async = async;
2929        
2930        this.count = 0;
2931	this.countTrash = 0;
2932        this.countUnlimited = 0;
2933	this.streaming = false;
2934	this.forceStreaming = options.forceStreaming || false;
2935        this.keys = [];
2936        this.streamingOrder = {};
2937        
2938        if (isset( this.cacheName) ) {
2939//        if (false ) {
2940
2941// this.cacheName += 'xx';
2942//            alert( this.cacheName);
2943		if (isset (EntityCache.setCache[ this.cacheName ])) {
2944//			if (typeof options.callback === "function") setTimeout( function() { options.callback() }, 10);
2945//alert(this.cacheName);
2946                        var cachedSet = EntityCache.setCache[ this.cacheName ];
2947                        cachedSet.status('ready');
2948                        setTimeout( function( ){ 
2949                            
2950//                              Ready Trigger Deactivated on EntitySet from Cache. Use status check!
2951//                            $(cachedSet).trigger( "ready" );
2952                            if (typeof options.callback === 'function') options.callback( cachedSet );                      
2953                        }, 100);
2954                        
2955                        return cachedSet;
2956//			return EntityCache.setCache[ this.cacheName ];
2957		}
2958//		EntityCache.setCache[ this.cacheName ] = this;
2959	}
2960
2961
2962
2963	/*
2964	if (isset(url)) {		
2965		this.loadFromURL(url);
2966	}*/	
2967	
2968
2969
2970//	if (isset(options)) {		
2971//	}
2972
2973
2974	this.status = function(newStatus) {
2975            if (typeof newStatus !== 'undefined' && newStatus != status) {
2976                status = newStatus;
2977                $(this).trigger( status );  // for example "ready"              
2978            }
2979            return status;
2980//		if (isset(newStatus)) status = newStatus;
2981//		else return status;
2982	}
2983	
2984	this.remove = function( entity ) {
2985		// REMOVE DOES NOT MEAN DELETE!
2986                // ONLY REMOVES ENTIY FROM SET AND TRIGGERS REMOVE EVENT
2987                var entityId = entity.getPrimary();
2988                
2989		if ( this.entities[entityId] == entity) {                    
2990//                    if (entity.trash()) ???
2991                    if (typeof this.streamingOrder !== 'undefined') delete this.streamingOrder[  entity.getPrimary('string') ];
2992                    this.count--;
2993                    delete this.entities[entityId];                
2994                    $(this).triggerHandler('entityRemoved', entity);
2995                    return true;
2996                } else {
2997//                    alert('error');
2998                    return false
2999                }
3000	}
3001        
3002        this.removeAll = function() {
3003		// REMOVE DOES NOT MEAN DELETE!
3004                // ONLY REMOVES ENTIY FROM SET AND TRIGGERS REMOVE EVENT                
3005                $.each(this.entities, function( key, entity ) {
3006                    thisEntitySet.remove( entity );
3007//                    var entityId = entity.getPrimary();		
3008//                    delete thisEntitySet.entities[entityId];
3009//                    $(thisEntitySet).triggerHandler('entityRemoved', entity);                                                                  
3010                });
3011	}
3012        
3013        
3014        this.getEntity = function( index ) {
3015            if (typeof index === 'undefined') index = 0;
3016            return this.entities[ Object.keys(this.entities)[index]];
3017        }
3018	
3019        this.getEntitiesByField = function( fieldName, valueOrEntity, type ) {
3020            // RETURNS ARRAY OF entities WITH fieldName = fieldValue
3021            // CREATES KEY LISTS FOR LOOKUP
3022            
3023            if (typeof this.view.fields[ fieldName ] === 'undefined') {
3024                alert('ERROR: View ' + this.view.name + ' does not have field ' + fieldName );
3025                return result;
3026            }            
3027            
3028            if (typeof type === 'undefined') type = this.view.fields[ fieldName ].type; 
3029            var result = [];
3030            if (valueOrEntity instanceof Entity) {
3031                var fieldValue = valueOrEntity.getPrimary( type );
3032            } else var fieldValue = valueOrEntity;
3033            fieldValue = convertValue( fieldValue, type, this.view.fields[ fieldName ].type)
3034            // Now fieldValue is of the same type as this
3035            
3036            
3037            // IF PRIMARY FIELD DO NOT GO THROUGH ALL ENTITIES, USE INDEX INSTEAD, FASTER
3038            if (fieldName == this.view.getPrimaryField().name) {
3039                if ( typeof this.entities[ fieldValue ] === 'undefined' ) return [];
3040                else return [ this.entities[ fieldValue ] ];
3041            }
3042                        
3043            
3044            
3045            for (var index in this.entities) {
3046                    var entity = this.entities[index];
3047                    if (entity.get(fieldName) == fieldValue) result.push( entity );
3048                    // INCLUDES TRASHED ENTITIES                    
3049//                    if (entity.trash() == false && entity.get(fieldName) == fieldValue) result.push( entity );
3050            }
3051            
3052            return result;
3053            
3054            
3055            
3056            
3057            // OLD VERSION WITH LOOK OF ARRAY "KEYS"
3058            // FASTER BUT NOT USED ANYMORE BECAUSE IT DOES NOT UPDATE ON CHANGES
3059
3060            /*
3061            // RETURN PRIMARY VALUE WITHOUT EXTRA KEY LIST
3062            
3063            if (fieldName == this.view.getPrimaryField().name) {
3064                if ( typeof this.entities[ fieldValue ] === 'undefined' ) return [];
3065                else return [ this.entities[ fieldValue ] ];
3066            }
3067            
3068            
3069            var result = [];
3070            
3071            if (typeof this.view.fields[ fieldName ] === 'undefined') {
3072                alert('ERROR: View ' + this.view.name + ' does not have field ' + fieldName );
3073                return result;
3074            }
3075            
3076            if ( typeof this.keys[ fieldName ] === 'undefined' ){
3077                // CREATE KEY
3078                this.keys[ fieldName ] = {} ;
3079                for (var index in this.entities) {
3080                    var entity = this.entities[index];
3081                    console.log(entity.get(fieldName, 'string'));
3082                    if ( typeof thisEntitySet.keys[ fieldName ][ entity.get(fieldName, 'string')] === 'undefined' ) thisEntitySet.keys[ fieldName ][ entity.get(fieldName, 'string')] = [];
3083                    thisEntitySet.keys[ fieldName ][ entity.get(fieldName, 'string')].push( entity );
3084                }
3085            } 
3086            
3087            // RETURN KEY
3088            if (typeof this.keys[ fieldName ][ fieldValue ] === 'undefined') return [];
3089            return this.keys[ fieldName ][ fieldValue ];
3090            
3091            */
3092        }
3093        
3094        this.getEntityByField = function( fieldName, valueOrEntity, type ){
3095            // RETURNS FIRST MATCHING ENTITY OR FALSE;            
3096            var result = this.getEntitiesByField(fieldName, valueOrEntity, type )
3097            if ( result.length == 0 ) return false;
3098                else return result[0];
3099        }
3100        
3101        this.getPrimaryEntity = function( primary ){
3102            // RETURNS ONLY EXISTING ENTITIES, DOES NOT LOAD FROM SERVER!
3103            return this.getEntityByField(this.view.getPrimaryField().name, primary );
3104        }
3105        
3106        
3107        
3108        
3109	
3110	this.add = function(entity, index)
3111	{
3112		if (isset(index) == false) index = entity.getPrimary();
3113			else entity.getPrimaryField().set(index);
3114		entity.entitySet = this;
3115                
3116                
3117		var entityId = entity.getPrimary();
3118		
3119                
3120                if ( typeof this.entities[entityId] !== 'undefined' ){
3121                    return false;
3122                }
3123                
3124		this.entities[entityId] = entity;
3125		
3126		//console.log('ADDED _ ' + entity.id());
3127		
3128		var set = this;
3129		
3130                EntityCache.add(entity);
3131                
3132                
3133/*		
3134		if (entity.isNew) {
3135			alert('NEW ENTITY');
3136			entity.forceFilters(this.)
3137			
3138		}
3139	*/	
3140		
3141		$(entity).on('selected', function(event){$(set).trigger('selected', entity)});
3142
3143		$(entity).on('statusChange', function(event, status){
3144			$(set).triggerHandler('entityStatusChange', {entity: entity, status: status});
3145		});
3146
3147		$(entity).on('changed', function(event){$(set).trigger('entityChanged', entity ) });
3148
3149
3150		$(entity).on('deleted', function(event){
3151//			alert('DLETED ' + entity.status() + '  -  ' + entity.getPrimary());
3152			if (entity.trash()) {
3153                            delete set.trash[entity.getPrimary()];
3154                            $(set).triggerHandler('countChanged');
3155                            set.countTrash -= 1;                            
3156                        } else {
3157                            delete set.entities[entity.getPrimary()];
3158                            $(set).triggerHandler('countChanged');
3159                            set.count -= 1;
3160                        }
3161		});
3162		
3163		
3164		$(entity.getPrimaryField()).on('changed', function(event){
3165			// PRIMARY HAS CHANGED -> CHANGE ITS KEY IN EntitySet
3166			if (entity.trash())
3167			{
3168				set.trash[entity.getPrimary()] = entity;
3169				delete set.trash[entityId];
3170			}
3171			else
3172			{
3173				set.entities[entity.getPrimary()] = entity;
3174				delete set.entities[entityId];
3175			}
3176//			alert('FIELD CHANGED ' + entity.status() + '  -  ' + entity.getPrimary() + '  -  ' + entityId);
3177		});
3178		
3179		
3180		
3181		$(entity).on('trashStatus', function(event){
3182			if (entity.trash())
3183			{
3184                            // REMOVE ENTIY FROM KEYS
3185                            for (var key in set.keys ){
3186                                var index = set.keys[ key ][ entity.get( key )].indexOf( entity );
3187                                if (index > -1) {
3188                                    set.keys[ key ][ entity.get( key )].splice( index, 1 );
3189                                }
3190                            }
3191
3192                            // INSERT ENTITY TO TRASH
3193                            set.trash[entity.getPrimary()] = entity;
3194                            // REMOVE ENTITY FROM ENTITIES
3195                            delete set.entities[entity.getPrimary()];
3196
3197                            $(set).triggerHandler('countChanged');
3198                            set.count -= 1;
3199                            set.countTrash += 1;
3200                                
3201
3202			}
3203			else
3204			{
3205                            // INSERT ENTIY FROM KEYS
3206                            for (var key in set.keys ){
3207                                if ( typeof set.keys[ key ][ entity.get(key, 'string')] === 'undefined' ) thisEntitySet.keys[ key ][ entity.get(key, 'string')] = [];
3208                                set.keys[ key ][ entity.get(key, 'string')].push( entity );
3209                            }
3210                            
3211                            // INSERT ENTITY TO ENTITIES
3212                            set.entities[entity.getPrimary()] = entity;
3213                            // REMOVE ENTITY FROM TRASH
3214                            delete set.trash[entity.getPrimary()];
3215
3216                            set.count += 1;
3217                            set.countTrash -= 1;                                
3218                            $(set).triggerHandler('countChanged');
3219
3220			}
3221		});
3222		
3223			
3224		
3225                for (var key in set.keys ){
3226                    if ( typeof set.keys[ key ][ entity.get(key, 'string')] === 'undefined' ) thisEntitySet.keys[ key ][ entity.get(key, 'string')] = [];
3227                    set.keys[ key ][ entity.get(key, 'string')].push( entity );
3228                }
3229                            
3230                            
3231
3232                
3233                
3234                this.count += 1;                
3235                $(this).triggerHandler('countChanged');
3236                $(this).trigger('entityAdded', entity);
3237                return true;
3238	}
3239	
3240	/*
3241	this.getBlankEntity = function()
3242	{
3243		var entity = new Entity();
3244		entity.dataStatus = STATUS_NEW;
3245		
3246		console.log('NEW ENTITY');
3247		console.log(entity);
3248		this.add(entity);
3249		
3250		return entity;
3251	}
3252	*/
3253
3254
3255	this.hookFilters = function(objectWithFilters) {
3256		
3257		if (objectWithFilters.constructor.name == 'Query') for (var index in objectWithFilters.filters) {
3258				$(objectWithFilters.filters[index]).on('changed', function() {
3259					thisEntitySet.forceFilters(objectWithFilters.filters[index]);
3260				});
3261		} else if ($.isArray(objectWithFilters)) for (var index in objectWithFilters) {
3262				$(objectWithFilters[index]).on('changed', function() {
3263					thisEntitySet.forceFilters(objectWithFilters[index]);
3264				});
3265		} else if (objectWithFilters.constructor.name == 'Filter') {
3266				$(objectWithFilters).on('changed', function() {
3267					thisEntitySet.forceFilters(objectWithFilters);
3268				});
3269		} else {
3270			console.log('ERROR: NO FILTER GIVEN');
3271			console.log(objectWithFilters);
3272		}
3273		
3274	}	
3275
3276	this.loadFromQuery = function(query, callback, async) {
3277	 	
3278		// ++ REPLACE DATA EXCHANGE CODE
3279                if ( typeof Model.views[ query.view ]!== 'undefined' &&  typeof Model.views[ query.view ].xmlRoot !== 'undefined')
3280                    this.loadFromXML( query, callback, async );
3281                    else if ( typeof Model.views[ query.view ]!== 'undefined' && typeof Model.views[ query.view ].data !== 'undefined')  this.loadFromObject( query, callback, async );
3282                    else this.loadFromURL( query.toURL(), callback, async );
3283		this.query = query;		
3284		if (this.name == 'unknown') this.name = query.view;
3285		this.hookFilters(query);
3286	}
3287	
3288	this.loadRelations = function(string, callback, async) {		
3289                //STRING EXAMPLE: 'RELATION invoice id = "xy" RELATION invoice_items amount > 2 RELATION products');
3290                this.query = new Query(string);           
3291		this.loadFromURL( 'provider/plugins/data/?query=' + string, callback, async );
3292	}
3293        
3294        
3295        this.loadFromXML = function( query, callback, async) {
3296            var entitySet = this;
3297            var query = new Query(query);
3298            var view = Model.views[ query.view ];
3299            var element = view.xmlRoot;
3300
3301            entitySet.streaming  = false;
3302           
3303//            console.log( view );
3304
3305            if (typeof this.view == 'undefined' ) this.view = view;
3306            
3307//            alert( view.xmlRoot );
3308//            for (var index in element){
3309            $( element ).find('>' + view.xmlEntityTag ).each( function(){
3310//                alert( this );
3311                var entity = XMLStorage.getEntity(view, this);
3312//                console.log( entity );
3313                entitySet.add( entity );
3314            });
3315            entitySet.status('ready');
3316            
3317            console.log(query);
3318            if (typeof callback == 'function') setTimeout( function(){ callback() }, 1);
3319            
3320        }
3321        
3322        
3323        this.loadFromObject = ( query, callback, async) => {           
3324            var entitySet = this;
3325            var query = new Query(query);
3326            var view = Model.views[ query.view ];
3327            console.log( view.data );
3328            
3329            entitySet.streaming  = false;
3330           
3331            if (typeof this.view == 'undefined' ) this.view = view;
3332            
3333            for (var index in view.data ) {
3334                console.log( index );
3335                var entity = new Entity();
3336                entity.data[ 'key' ] = index;
3337                entity.data[ 'value' ] = view.data[ index ];
3338                entity.setView( view );
3339                entity.storage = ObjectStorage;                
3340                entitySet.add( entity );
3341            };
3342//            entitySet.applyQuery( query );            
3343            entitySet.status('ready');
3344            
3345            console.log(query);
3346            if (typeof callback == 'function') setTimeout( function(){ callback( entitySet.applyQuery( query ) ) }, 1);
3347            
3348        }
3349        
3350        
3351
3352	
3353        this.loadFromJSON = function( data ){
3354            
3355            if (typeof data['data'] == 'undefined'){
3356                // No entityset data given, perhaps data contains multiple EntitySets
3357                if ( typeof data[ this.name ] !== 'undefined' ) data = data[ this.name ];  // found EntitySet with this name, choose this data
3358            }
3359            
3360                        
3361                        var viewName;
3362			this.status('loading');
3363//                        console.log(this);
3364//                        console.log(data);
3365                        if( typeof( data['_view'] ) !== 'undefined' ) {
3366                            // ENTITY DATA ONLY, NO ADDITIONAL INFORMATION     
3367                            data = { viewName: data['_view'], data: [ data ] };         
3368                            
3369                        }
3370                        
3371                        
3372                        if ( typeof data['viewName'] !== 'undefined') {
3373                            viewName = data['viewName'];
3374                            if (typeof Model.views[ viewName ] == 'undefined') { 
3375                                var thisEntitySet = this;
3376                                Model.loadView( viewName, function(){ }, false );                                
3377                            }
3378                            this.view = Model.views[ viewName ];
3379                        } 
3380                        else if ( typeof data['view'] === 'undefined' ||  data['view'] === null || typeof data['view']['name'] === 'undefined' ) 
3381                        {
3382                                console.log('ERROR: No view data returned.');
3383                                console.log(data);
3384                                return;
3385                        }
3386                        else {
3387                             viewName = data['view']['name'];
3388                        }
3389                            
3390                        if (typeof Model.views[ viewName ] == 'undefined')  {
3391
3392					var newView = new View();
3393                                       
3394					newView.name = viewName;					
3395					newView.title = data['view']['title'];
3396					newView.displayField = data['view']['displayField'];					
3397					if (typeof data['view']['arrangeField'] !== 'undefined' && data['view']['arrangeField'] != null )newView.arrangeField = data['view']['arrangeField'];					
3398					newView.fields = data['view']['fields'];
3399                                        newView.resolutions = data['view']['resolutions'];
3400                                        newView.type = data['view']['type'];
3401                                        if (typeof data['view']['path'] !== 'undefined'){
3402                                            newView.path = data['view']['path'];                                            
3403                                        }
3404                                        
3405                                        
3406                                        
3407                                        $.each(newView.fields, function( key, field ) {
3408                                            field.getTitle = function( ) {
3409                                            if ( typeof field.title === 'string') return field.title;
3410                                                else return field.name;
3411                                            }
3412                                        });
3413					
3414					Model.addView( newView );
3415                                        
3416			} else {
3417//                            console.log(data);
3418//                            console.log(this.view);
3419//                            alert(data);
3420                        }
3421			
3422			if (isset(this.view) == false) this.view = Model.views[ viewName ];
3423//                      
3424                        if ( typeof data['unlimited'] !== 'undefined' ) {
3425                            this.countUnlimited = parseInt(data['unlimited']);
3426                            if ( data['data'].length != this.countUnlimited || this.forceStreaming == true) this.streaming = true;
3427                        }
3428                        else {
3429                                this.countUnlimited = data['data'].length;
3430                        }
3431                        
3432                        if ( typeof data['from'] !== 'undefined' ) this.from = parseInt( data['from'] ); else this.from = 0;
3433                        
3434                        // TRIGGER WHEN VIEW AND STREAMING SIZE DONE 
3435                        // AND BEFORE ADDING ENTITIES
3436                        // NEEDED FOR DISPLAYSET PLACEHOLDER PREPARATION
3437                        $(this).trigger('addingEntities'); 
3438                        
3439			var counter = 0;
3440			for (var index in data['data']){
3441				
3442				var entity = new Entity();
3443                                
3444                                
3445				if (isset(viewName)) 
3446				{
3447					entity.viewName = viewName;
3448					if (isset(Model.views[entity.viewName]))  {
3449						entity.view = Model.views[entity.viewName];
3450					}
3451				}
3452				
3453				if (typeof this.view === "undefined" ) console.log('ERROR: VIEW NOT LOADED');
3454				for (var fieldName in this.view.fields)
3455				{
3456					// TYPE HTML IS STORED AS STRING IN ENTITY DATA
3457//                                        console.log(this.view);
3458                                        
3459					if (this.view.fields[fieldName].type == 'html') 
3460						entity.addField(fieldName, data['data'][index][fieldName]);
3461					else  
3462                                            entity.addField(fieldName, convertValue(data['data'][index][fieldName], 'string', entity.view.fields[fieldName].type), entity.view.fields[fieldName].type);
3463                                        
3464//					entity.addField(fieldName, convertValue(data['data'][index][fieldName], 'string', entity.view.fields[fieldName].type), entity.view.fields[fieldName].type);
3465				}
3466
3467				//if (isset(data['view']['fields']['id'])) entity.id(data['data'][index]['id']);
3468//					alert('xxx ' + data['view']['name'] + '  -  ' + entity.getPrimary());	
3469
3470				if (isset(EntityCache.entityCache[viewName]) && isset(EntityCache.entityCache[viewName].entities[entity.getPrimary()])) {
3471                                    // ENTITY ALREADY EXISTS IN ENTITYCACHE
3472                                    // USE ENTITY IN ENTITYCACHE
3473                                    // AND UPDATE !
3474                                    // PREVIOUS CHANGES ARE NOT OVERWRITTEN
3475
3476
3477                                    var cacheEntity = EntityCache.entityCache[viewName].entities[entity.getPrimary()];
3478
3479
3480                                    // COMPARE FIELDS (id and others)
3481
3482                                    var hasChangedFields = false;   // determine if fields were changed locally. do not overwrite these fields
3483
3484                                    for (var fieldName in cacheEntity.fields) {
3485                                            // Compare strings, not objects
3486                                            if (typeof cacheEntity.fields[fieldName] != 'undefined' && cacheEntity.fields[fieldName].status() == STATUS_UNCHANGED && cacheEntity.get(fieldName, 'string') != entity.get(fieldName, 'string') ){
3487//console.log(cacheEntity);
3488//                                                console.log(fieldName);
3489//                                                console.log(cacheEntity.fields[fieldName].status());
3490//
3491//                                                console.log(cacheEntity.data[fieldName]);
3492//                                                console.log(entity.data[fieldName]);
3493                                                
3494                                                cacheEntity.set(fieldName, entity.data[fieldName]); 
3495//                                                cacheEntity.set(fieldName, entity.data[fieldName], null, null,  true); 
3496                                                cacheEntity.fields[fieldName].status( STATUS_UNCHANGED );
3497                                                
3498                                            } else if (typeof cacheEntity.fields[fieldName] != 'undefined' && cacheEntity.fields[fieldName].status() == STATUS_CHANGED){
3499                                                hasChangedFields = true;
3500                                            }
3501
3502                                    }
3503
3504
3505                                    if (cacheEntity.isNew) {
3506    //					cacheEntity.set(cacheEntity.getPrimaryFieldName(), data['data'][0][cacheEntity.getPrimaryFieldName()], 'string');
3507
3508                                            //cacheEntity.privateId = cacheEntity.getPrimary();
3509                                            //cacheEntity.id(cacheEntity.getPrimary());
3510                                            //console.log(cacheEntity.getPrimary());
3511                                            //console.log(cacheEntity.id());
3512                                            cacheEntity.isNew = false;					
3513                                    }
3514
3515                                    if ( hasChangedFields == false ) cacheEntity.status(STATUS_UNCHANGED);
3516
3517                                    entity = cacheEntity;
3518                                        
3519                                        
3520                                        
3521                                        
3522				} else {
3523					$(document).triggerHandler('entityReady_' + entity.viewName, entity);
3524					$(document).triggerHandler('entityReady', entity);
3525				}
3526                                
3527                                
3528                                if (this.streaming) {
3529//                                    entity.stream = counter + this.from;
3530                                    this.streamingOrder[ entity.getPrimary('string') ] = counter + this.from;
3531                                }
3532                                counter++
3533                                
3534                                this.add(entity);
3535			}
3536                        
3537                        if ( this.streaming == false  && typeof this.cacheName !== 'undefined') EntityCache.setCache[ this.cacheName ] = this;
3538                                                
3539			this.status('ready');            
3540        },
3541        
3542	this.loadFromURL = function( url, callback, async ) {
3543            if (typeof async === 'undefined') async = true;
3544		$.ajax({
3545                    async: async,
3546			url: url,
3547			type: 'POST',
3548			dataType: 'json',
3549			context: this,
3550                        error: function( data ) {
3551                            this.ajaxResult = data;
3552                            if (typeof callback === "function") callback( false );
3553                        }
3554		}).done(function(data) {
3555
3556                        this.ajaxResult = data;
3557                        
3558                        this.loadFromJSON( data );
3559                        
3560			if (typeof callback === "function") callback( this );			
3561                        
3562		});
3563                
3564                
3565	};
3566        
3567        this.loadStream = function( from, amount, callback ){
3568                                        
3569                                        var streamQuery = new Query( this.query.toString() );
3570                                        
3571                                        streamQuery.limiters = new Array( new Limiter( {from: from, amount: amount }) ); 
3572                                        
3573                                        var streamEntitySet = new EntitySet( { query: streamQuery, forceStreaming: true, callback: function(){
3574                                                console.log( streamEntitySet);
3575//                                                var i = 0;
3576//                                                for ( var entityIndex in streamEntitySet.entities) {
3577//                                                    
3578//                                                    var entity = streamEntitySet.entities[entityIndex] ;
3579//                                                    
3580//                                                    
3581//                                                    entity.stream = from + i;
3582//                                                    thisEntitySet.add( entity );
3583//                                                    
3584//                                                    console.log('### ' + i  + '  --  ' + entityIndex);
3585//console.log( entity.get('title') );
3586//console.log( streamEntitySet.entities );
3587//
3588//                                                    
3589//                                                    i++;
3590//
3591//                                                }
3592//                                                
3593                                            }
3594                                        } );
3595                                        
3596                                        var i = 0;
3597                                        
3598                                        $(streamEntitySet).on('entityAdded', function(event, entity){ 
3599//                                            entity.stream = from + i;
3600//console.log(entity);
3601                                            i++;
3602                                            var id = entity.getPrimary('string');
3603                                            thisEntitySet.streamingOrder[ id ] = streamEntitySet.streamingOrder[ id ];
3604                                            thisEntitySet.add( entity );
3605                                            
3606                                        });
3607                                        
3608            
3609        };
3610        
3611        
3612        
3613	
3614        this.downloadExcel = function(){
3615            if (typeof this.query === 'undefined') return false;
3616            var excelQuery = new Query( this.query.toString() );
3617//            console.log( this.query);
3618//            console.log( this.query.toString() );
3619//            console.log( excelQuery );
3620            excelQuery.limiters = [];
3621            var url = excelQuery.toURL() +'&format=xls';
3622            window.open(url, '_blank');
3623        }
3624        
3625        
3626	this.getHTML = function(fields)	{
3627		var result = '<ul data-structure="set">';
3628		for (var index in this.entities) result += this.entities[index].getHTML();
3629		result += '</ul>';
3630		return result;		
3631	};
3632	
3633	this.applyQuery = function (variableQuery, returnCopy) {
3634		// Filter Entities in this EntitySet
3635		// Return a copy or this without filtered Entities
3636				
3637		
3638		var query = new Query( variableQuery );
3639		var result = this;
3640		
3641		if (isset( returnCopy ) == false) returnCopy = true;
3642		if ( returnCopy ) result = new EntitySet();
3643
3644		$.each(this.entities, function( key, entity ) {
3645			if (entity.validate( query )) {
3646				if ( returnCopy ) result.add( entity );				
3647			} else {
3648				if ( returnCopy == false ) delete result.entities[key];
3649			}
3650		});
3651		return result;
3652	}
3653	
3654	
3655	this.getBlankEntity = function ( additionalQuery ) {
3656		var filters = [];
3657		console.log(this);
3658		console.log('________________ FILTER');
3659		console.log(additionalQuery);
3660		
3661		if (isset( additionalQuery ) && typeof additionalQuery == 'string') additionalQuery = new Query(additionalQuery); 
3662		
3663		console.log(additionalQuery);
3664		
3665		if (isset( additionalQuery )) filters = filters.concat(additionalQuery.filters);
3666		if (isset( this.query )) filters = filters.concat(this.query.filters);
3667		console.log(this);
3668		console.log(filters);
3669
3670		var entity = this.view.getBlankEntity( {'filters': filters} );
3671		
3672//		for (var index in this.query.filters) {
3673//			this.query.filters[index].hook(entity);
3674//		}
3675		
3676		$(this).triggerHandler('blankEntity', entity);	
3677                
3678                this.add( entity );                
3679                
3680		return entity;
3681	}
3682	
3683	this.forceFilters = function( filters ) {
3684		for (var index in this.entities) {
3685			this.entities[index].forceFilters(filters);
3686		}
3687		for (var index in this.trash) {
3688			this.trash[index].forceFilters(filters);
3689		}
3690	}
3691	
3692	this.store = function(dataStore) {
3693		for (var index in this.trash) {
3694			this.trash[index].store(dataStore);
3695		}
3696		for (var index in this.entities) {
3697			this.entities[index].store(dataStore);
3698		}
3699	};
3700	
3701	this.restore = function()
3702	{
3703		for (var index in this.entities) 
3704		{
3705			this.entities[index].restore();
3706		}
3707		
3708	}
3709	
3710 	this.treatAllEntities = function(handlerFunction) {
3711 		// handlerFunction is triggered for all Entities 
3712 		// ON entityAdded
3713 		// AND for all existing entities (from cache)
3714
3715 		for (var index in this.entities) {
3716 			handlerFunction( null, this.entities[index] );
3717 		}
3718 		
3719 		for (var index in this.trash) {
3720 			handlerFunction( null, this.trash[index] );
3721 		}
3722 		
3723 		$(this).on('entityAdded', function(event, entity){ handlerFunction(event, entity) });
3724 		
3725 	}
3726	
3727//        this.treatAllEntities( function(event, entity){  EntityCache.add(entity)  });
3728
3729	if (isset( this.query )) this.loadFromQuery(this.query, options.callback, this.async);
3730//	if (isset( this.query )) this.loadFromURL(this.query.toURL());
3731
3732	
3733	
3734};
3735
3736
3737
3738ObjectView = function( viewName, data ){    
3739        // CREATES AND RETURNS A NEW VIEW BASED ON A KEY VALUE OBJECT
3740	var view = new View();
3741	view.name = viewName;
3742
3743	view.fields['key']             = {'name': 'key',              'type': 'string',   'objectStorage': { 'type': 'key' }, 'index': 'primary' }; 
3744	view.fields['value'] 		= {'name': 'value', 		'type': 'string',   'objectStorage': { 'type': 'value' } };
3745        
3746        view.displayField = 'key';
3747        view.data = data;
3748	
3749        Model.addView( view );	 
3750	viewToEditor ( { 'viewName': view.name, 'navigation': false } );	 
3751}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.