PageSourceSearch

https://asd.mx/wp-content/plugins/screets-cx/assets/js/scx.app.js?ver=2.1.1

js asd.mx collected 2026-10-07 06:59:53 UTC 13,648 bytes, 640 lines download raw bytes

1/*!
2 * Screets Chat X - Application
3 * Author: @screetscom
4 *
5 * COPYRIGHT © 2016 Screets d.o.o. All rights reserved.
6 * This  is  commercial  software,  only  users  who have purchased a valid
7 * license  and  accept  to the terms of the  License Agreement can install
8 * and use this program.
9 */
10
11;(function () {
12
13	var W = window,
14		D = document,
15		title = D.title,
16		root = this,
17		prev_chat_x = root.ChatX,
18		// jQuery style selector for more complex queries (slower than getElementById)
19		$ = function ( selector ) {
20			return D.querySelector( selector );
21		}
22
23	function ChatX( opts ) {
24
25		var self = this;
26
27		// Setup and establish options
28		this.opts = opts;
29
30		// Create real-time database
31		if( !opts.no_db ) {
32			this.db = new SCX_FB( this.opts ); // Firebase
33			
34			// Get reference
35			this.ref = this.db.ref;
36
37			// Bind data events
38			this._bind_data_events();
39		}
40
41
42		// User data
43		this.user = {};
44		this.user_id = '';
45		this.username = '';
46
47		// Useful variables
48		this.retina = this._is_retina();
49
50		// Sounds
51		this.sounds = {};
52		this._add_sound( 'connected' );
53		this._add_sound( 'disconnected' );
54		this._add_sound( 'new-msg' );
55		this._add_sound( 'new-ntf' );
56		this._add_sound( 'new-visitor' );
57
58		// Some useful regexes
59		this.url_pattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;
60		this.pseudo_url_pattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
61
62	}
63
64	//
65	// Internal methods
66	// ------------------
67
68	ChatX.prototype = {
69
70		/**
71		 * Bind data events
72		 */
73		_bind_data_events : function() {
74
75			this.db.on( 'user-update', this._on_user_update.bind( this ) );
76
77		},
78
79
80		/**
81		 * Listen user updates
82		 */
83		_on_user_update : function( user ) {
84
85			// User is deleted!
86			if( !user || !user.id ) {
87				
88				// Disconnect for 
89				if( !this.opts.is_frontend ) {
90					Firebase.goOffline();
91				}
92				
93				setTimeout( function() {
94					W.location.reload();
95				}, 0 );
96
97				return;
98			}
99
100			// Update our current user state and render latest user name
101			this.user = user;
102
103			// Update user id
104			this.user_id = user.id.toString();
105			this.username = user.name.toString();
106
107			// FIXME!
108			// Update our interface to reflect which users are muted or not.
109			/*
110			var mutedUsers = this._user.muted || {};
111			$('[data-event="firechat-user-mute-toggle"]').each(function(i, el) {
112				var userId = $(this).closest('[data-user-id]').data('user-id');
113				$(this).toggleClass('red', !!mutedUsers[userId]);
114			});
115
116			// Ensure that all messages from muted users are removed.
117			for (var userId in mutedUsers) {
118				$('.message[data-user-id="' + userId + '"]').fadeOut();
119			}
120			*/
121		},
122
123		/**
124		 * Setup UI for operators
125		 */
126		_setup_op_ui : function() {},
127
128		/**
129		 * Add sound
130		 */
131		_add_sound : function( name ) {
132			var source = D.createElement('source'),
133				filename = this.opts.plugin_url + '/assets/sounds/' + name;
134			
135			this.sounds[name] = D.createElement('audio');
136			
137			if ( this.sounds[name].canPlayType('audio/mpeg;' ) ) {
138				source.type= 'audio/mpeg';
139				source.src= filename + '.mp3';
140
141			} else {
142				source.type= 'audio/ogg';
143				source.src= filename + '.ogg';
144			}
145			this.sounds[name].appendChild(source);
146		},
147
148		/**
149		 * Is URL an image
150		 */
151		_is_retina : function( uri ) {
152			
153			if ( W.matchMedia ) {
154				var mq = W.matchMedia( "only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen  and (min-device-pixel-ratio: 1.3), only screen and (min-resolution: 1.3dppx)" );
155				return ( mq && mq.matches || ( W.devicePixelRatio > 1 ) ); 
156			}
157
158			return false;
159
160		},
161
162		/**
163		 * Is URL an image
164		 *
165		 * Source: http://stackoverflow.com/a/19395606/272478
166		 */
167		_is_img : function( uri ) {
168			
169			// Make sure we remove any nasty GET params 
170			uri = uri.split('?')[0];
171
172			// Moving on now
173			var parts = uri.split('.'),
174				extension = parts[parts.length-1],
175				imageTypes = ['jpg','jpeg','tiff','png','gif','bmp'];
176
177			if( imageTypes.indexOf( extension ) !== -1 ) {
178				return true;
179			}
180
181		},
182
183
184	};
185
186	// Run the script in "noConflict" mode
187	ChatX.noConflict = function noConflict() {
188		root.ChatX = prev_chat_x;
189		return ChatX;
190	};
191
192	// Export the object as global
193	root.ChatX = ChatX;
194
195	/**
196	 * Exposes internal chat bindings via this external interface
197	 */
198	ChatX.prototype.on = function( event_type, callback ) {
199		this.db.on( event_type, callback );
200	};
201
202	/**
203	 * Initialize an authenticated session with a user id and name.
204	 * This method assumes that the underlying Firebase reference has
205	 * already been authenticated
206	 */
207	ChatX.prototype.set_user = function( user_id, username ) {
208
209		var self = this;
210
211		// Initialize data events
212		self.db.set_user( user_id, username, function( user ) {
213
214			self.user = user;
215
216			// Bind operator UI events
217			if( self.db.is_op ) {
218				self._setup_op_ui();
219			}
220
221			// Resume session (don't resume when OP is in front-end)
222			if( !( self.opts.is_frontend && self.db.is_op ) ) {
223				self.db.resume_session();
224			}
225
226		});
227
228	};
229
230	/**
231	 * Add message into chat conversation
232	 */
233	ChatX.prototype.add_msg = function( chat_id, raw_msg, reverse_direction ) {
234		
235		var obj_id = ['scx-cnv-',chat_id].join(''),
236			$cnv = D.getElementById( obj_id ),
237			pos = ( reverse_direction ) ? 'afterbegin' : 'beforeend',
238			cls = [];
239
240		if( !$cnv ) return;
241
242		// Message data
243		var is_you = ( raw_msg.user_id == this.db.user_id ),
244		data = {
245			id              : raw_msg.id,
246			user_id         : raw_msg.user_id,
247			name            : raw_msg.name,
248			avatar          : this.render( 'avatar', raw_msg.avatar ),
249			type            : raw_msg.type,
250			time            : this.format_time( raw_msg.timestamp ),
251			msg             : raw_msg.msg || '',
252			self_msg        : is_you
253		};
254
255		// Sanitize the message
256		data.msg = this.sanitize( raw_msg.msg );
257
258		// Is it you?
259		var _msg_owner = (is_you ) ? 'scx-you' : 'scx-other';
260		cls.push( _msg_owner );
261
262		// Is unread message
263		if( raw_msg.unread ) {
264			cls.push( 'scx-new' );
265		}
266
267		// Include type
268		cls.push( 'scx-type-' + raw_msg.type );
269
270		data._cls = cls.join(' ');
271
272		$cnv.insertAdjacentHTML( pos, this.render( 'chat-msg', data ) );
273
274		// Scroll down
275		if( !reverse_direction ) {
276			$cnv.scrollTop = 10000;
277		}
278
279	};
280
281	/**
282	 * Play a sound
283	 */
284	ChatX.prototype.play = function( name ) {
285		this.sounds[name].play();
286	};
287
288	/**
289	 * Create notification on UI
290	 */
291	ChatX.prototype.ntf = function( id, msg, type, section, ico, duration ) {
292		
293		var self = this,
294			$ntf = '';
295
296		// Update section
297		section = section || 'main';
298		section = [ '.scx-ntf-', section ].join('');
299
300		var $all_ntf = D.querySelectorAll( section );
301
302		if( $all_ntf ) {
303
304			for( var i=0; i < $all_ntf.length; i++ ) {
305
306				$ntf = $all_ntf[i];
307
308				// Include id
309				$ntf.id = [ 'scx-ntf-', id ].join('');
310
311				// Get icon
312				if( ico ) {
313					ico = self.render( 'ico', ico );
314				}
315
316				// Render message
317				$ntf.innerHTML = self.render( 'ntf', {
318					msg: msg,
319					type: type,
320					ico: ico
321				});
322
323				// Set classes
324				$ntf.className += ' scx-active';
325
326				// Hide notification after a specific time
327				if( duration ) {
328					root.setTimeout( function() {
329						self.hide_ntf( id );
330
331					}, duration );
332				
333				// Hide chat for only main notifications in the console
334				} else if( !self.opts.is_frontend && section === '.scx-ntf-main' ) {
335					$ntf.addEventListener('click', function(e) {
336						self.remove_class( this, 'scx-active' );
337					});
338				}
339
340			}
341
342		}
343
344	};
345
346	/**
347	 * Create notification on UI
348	 */
349	ChatX.prototype.hide_ntf = function( id ) {
350
351		id = [ 'scx-ntf-', id ].join('');
352		$ntf = D.getElementById( id );
353
354		if( $ntf ) {
355			this.add_class( $ntf, 'scx-close' );
356			this.remove_class( $ntf, 'scx-active' );
357		}
358
359	};
360
361	/**
362	 * Remove a DOM object
363	 */
364	ChatX.prototype.remove_obj = function( id ) {
365
366		var $item = D.getElementById(id);
367
368		if( $item ) { 
369			$item.parentNode.removeChild( $item ); 
370		}
371
372	};
373
374	/**
375	 * Clear up UI from highlighted messages and counts
376	 */
377	ChatX.prototype.reset_ui = function() {
378		var $highlighted = D.getElementsByTagName('scx-highlighted');
379
380		if( $highlighted ) {
381			for( var i=0; i < $highlighted.length; i++ ) {
382				this.remove_class( $highlighted[i], 'scx-highlighted' );
383			}
384		}
385	};
386
387	/**
388	 * Convert HTML tags to text and replace plain URLs with links
389	 *
390	 * @param    {string}
391	 */
392	ChatX.prototype.sanitize = function( str ) {
393		
394		var links = str.match( this.url_pattern ) || [],
395			pseudo_links = str.match( this.pseudo_url_pattern ),
396			link = '',
397			href = '',
398			rx = '';
399
400		// Merge links with pseudos
401		if( pseudo_links ) {
402			for( var i in pseudo_links ) { links.push( pseudo_links[i].trim() ); }
403		}
404
405		// Convert HTML tags and new lines to text
406		str = String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace( /\n/g, '<br>');
407
408		// Twitter Emoji parse
409		str = twemoji.parse( str );
410
411		for( var i=0; i<links.length; ++i ) {
412			link = links[i];
413
414			// Clean http:// part from url
415			href = link.replace( /.*?:\/\//g, "" );
416
417			rx = new RegExp( link, 'g' );
418
419			str = str.replace( rx, this.render( 'msg-link', href ) );
420			
421		}
422
423		return str;
424		
425	};
426
427	/**
428	 * Get time difference between two dates
429	 *
430	 */
431	ChatX.prototype.time_diff = function( first, sec, return_type ) {
432
433		var diff = (first - sec); // milliseconds between two dates
434
435		switch( return_type ) {
436
437			case 'ms': // milliseconds
438				return diff;
439
440			case 'm': // minutes
441				return Math.round(((diff % 86400000) % 3600000) / 60000);
442				
443			case 'h': // hours
444				return Math.round((diff % 86400000) / 3600000);
445
446			case 'd': // days
447				return Math.round(diffMs / 86400000);
448
449		}
450	};
451
452	/**
453	 * Given a timestamp, format it in the form hh:mm am/pm. Defaults to now
454	 * if the timestamp is undefined.
455	 *
456	 * @param    {Number}    timestamp
457	 * @param    {string}    date
458	 * @return   {string}
459	 */
460	ChatX.prototype.format_time = function( timestamp, full ) {
461
462		var date = (timestamp) ? new Date(timestamp) : new Date(),
463			year = date.getFullYear(),
464			month = date.getMonth() + 1,
465			day = date.getDate(),
466			hours = date.getHours() || '0',
467			minutes = '' + date.getMinutes();
468
469		minutes = ( minutes.length < 2 ) ? '0' + minutes : minutes;
470		day = ( day.length < 2 ) ? '0' + day : day;
471		month = ( month.length < 2 ) ? '0' + month : month;
472
473		if( !full ) {
474			return '' + hours + ':' + minutes;
475		} else {
476			return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes;
477		}
478
479	};
480
481	/**
482	 * Add new class(es)
483	 */
484	ChatX.prototype.add_class = function( el, new_cls ) {
485
486		this.remove_class(el, new_cls );
487
488		var cls = [el.className, new_cls].join(' ');
489
490		el.className = cls;
491
492	};
493
494	/**
495	 * Remove class(es)
496	 */
497	ChatX.prototype.remove_class = function( el, remove ) {
498
499		if( !el ) return;
500				var classes = remove.split( ' ' ),
501			rx = '';
502
503		for( var i=0; i < classes.length; i++ ) {
504			rx = new RegExp( '(?:^|\\s)' + classes[i] + '(?!\\S)', 'g' );
505
506			// Update class
507			el.className = el.className.replace( rx , '' );
508
509		}
510
511	};
512
513	/**
514	 * Has class?
515	 */
516	ChatX.prototype.has_class = function( $el, selector ) {
517
518		var class_name = ' ' + selector + ' ';
519
520		if ( ( ' ' + $el.className + ' ' ).replace( /[\n\t]/g, ' ' ).indexOf( class_name ) > -1 ) {
521			return true;
522		}
523		return false;
524
525	};
526
527	/**
528	 * Check if a value exists in an array
529	 *
530	 * @param    {string}
531	 */
532	ChatX.prototype.in_array = function( val, array ) {
533		
534		var length = array.length;
535
536		for(var i = 0; i < length; i++) {
537			if( array[i] == val ) return true;
538		}
539
540		return false;
541	};
542
543	/**
544	 * Validate email field
545	 */
546	ChatX.prototype.is_email = function( email ) {
547
548		var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
549
550		return re.test( email );
551
552	};
553
554	/**
555	 * Send a post request to the server
556	 */
557	ChatX.prototype.post = function( url, data, callback ) {
558		
559		var self = this,
560			xhr = new XMLHttpRequest(),
561			fd = new FormData();
562
563		xhr.open( "POST", url, true );
564
565		// Handle response
566		xhr.onreadystatechange = function() {
567
568			if ( xhr.readyState == 4 ) {
569
570				// Perfect!
571				if( xhr.status == 200 ) {
572					if( callback ) { callback( JSON.parse( xhr.responseText ) ); }
573
574				// Something wrong!
575				} else {
576					if( callback ) { callback( null ); }
577				}
578			}
579		};
580		
581		// Get data
582		for( var k in data ) { fd.append( k, data[k] ) ; }
583
584		// Initiate a multipart/form-data upload
585		xhr.send( fd );
586
587	};
588
589	/**
590	 * Render template
591	 */
592	ChatX.prototype.render = function( template, p ) {
593		
594		var arr = [];
595
596		switch( template ) {
597
598			// Notification
599			case 'ntf':
600				arr = [ '<div class="scx-wrap scx-', p.type,'">', p.ico, p.msg, '</div>' ];
601				break;
602
603			// Admin notification
604			case 'admin-ntf':
605				arr = [ '<div class="scx-wrap scx-', p.type,'">', p.ico, p.msg, '</div>' ];
606				break;
607
608			// Icon
609			case 'ico':
610				arr = [ '<span class="scx-ico ',p,'"></span>' ];
611				break;
612
613			// Normal link :)
614			case 'msg-link':
615				arr = [ '<a href="http://',p,'" target="_blank" rel="nofollow">', p,'</a>' ];
616				break;
617
618			// Chat message
619			case 'chat-msg':
620				arr = [ '<div id="SCX-msg-',p.id,'" class="scx-msg scx-msg-',p.id,' ',p._cls,'">',p.avatar,'<div class="scx-msg-wrap"><span class="scx-msg-time">',p.time,'</span><span class="scx-msg-author">',p.name,'</span><span class="scx-msg-content">',p.msg,'</span></div></div>' ];
621				break;
622
623			// Avatar
624			case 'avatar':
625				arr = [ '<span class="scx-user-avatar"><img src="',p,'" alt="" /></span>' ];
626				break;
627
628			// Title
629			case 'title':
630				arr = [ '' ];
631				break;
632
633		}
634
635		return arr.join('');
636
637	};
638
639
640})();

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.