import { Injectable , Inject } from "@angular/core" ; import { VisitedRoute } from "./structures/visited-route" ; import { Subject } from "rxjs" ; import { Router , NavigationStart , NavigationEnd } from "@angular/router" ; import { UserClick } from "./structures/click" ; /**
 * Session flow service. Track information about user activity, such as:
 * 1. Session id
 * 2. Device id
 * 3. User id
 * 3. Is mobile user
 * 4. Refferer url
 * 5. Saves visited routes
 * 6. Saves user clicks
 *
 * To enable `Session Flow` for tracking inject `DAL` and `Session Flow` into root `AppComponent`. If you want to track user click also add HostListener on click:
 * ```
 * export class AppComponent {
 *
 * \@HostListener('click', ['$event']) onClick(e){
 *    this.sessionFlow.addUserClick(e);
 * }
 *
 *
 * constructor(
 *            private dal: DbAbstractionLayer,
 *            private sessionFlow: SessionFlow
 *            ) {
 * }
 *
 * ngOnInit() {
 *    this.dal.checkOldSessionFlow(this.sessionFlow.deviceId);
 *    this.dal.connectSessionFlowToDB(this.sessionFlow, this.sessionFlow.deviceId, this.sessionFlow.sessionId);
 * }
 * ```
 *
 */ @ Injectable ( ) export class SessionFlow { /**
     * Current session id
     */ sessionId : string ; /**
     * Current device id
     */ deviceId : string ; /**
     * Is mobile user
     */ isMobile : boolean ; /**
     * Refferer url
     */ reffererUrl : string = '' ; /**
     * RxJs subject for visited routes
     */ visitedRoute : Subject < VisitedRoute > = new Subject < VisitedRoute > ( ) ; /**
     * RxJs subject for user clicks
     */ click : Subject < UserClick > = new Subject < UserClick > ( ) ; /**
     * Current user id
     */ userId : string ; /**
     * Last route. For default "/"
     */ private lastRoute : string = '/' ; /**
     * Start time of enter for last route
     */ private lastRouteTimeStart : Date = new Date ( ) ; constructor ( protected router , @ Inject ( 'isBrowser' ) protected isBrowser ) { this . userId = 'guest' ; if ( isBrowser ) { this . reffererUrl = document . referrer ; } this . createDeviceId ( ) ; this . createSessionId ( ) ; this . trackRouteChange ( ) ; } /**
     * Create new visited route
     */ createVisitedRoute ( ) { let visitedRoute = new VisitedRoute ( ) ; visitedRoute . route = this . lastRoute ; visitedRoute . timeStart = this . lastRouteTimeStart ; visitedRoute . timeEnd = new Date ( ) ; visitedRoute . calcDuration ( ) ; this . visitedRoute . next ( visitedRoute ) ; } /**
     * Tracks router changes
     */ private trackRouteChange ( ) { this . router . events . subscribe ( ( event ) => { if ( event instanceof NavigationStart ) { event = event as NavigationStart ; this . createVisitedRoute ( ) ; } if ( event instanceof NavigationEnd ) { event = event as NavigationEnd ; this . lastRoute = event . url ; this . lastRouteTimeStart = new Date ( ) ; } } ) ; } /**
     * Create session id
     */ private createSessionId ( ) { this . sessionId = this . guid ( ) ; this . isMobile = this . detectMobileDevice ( ) ; this . setCookie ( 'mobile' , this . detectMobileDevice ( ) + "" , 0 ) ; this . setCookie ( 'session_id' , this . sessionId , 0 ) ; } /**
     * Create guid for session id and device id
     */ private guid ( ) { function s4 ( ) { return Math . floor ( ( 1 + Math . random ( ) ) * 0x10000 ) . toString ( 16 ) . substring ( 1 ) ; } return s4 ( ) + s4 ( ) + '-' + s4 ( ) + '-' + s4 ( ) + '-' + s4 ( ) + '-' + s4 ( ) + s4 ( ) + s4 ( ) ; } /**
     * Return true if user use mobile device
     */ private detectMobileDevice ( ) { var check = false ; if ( this . isBrowser ) { ( ( a ) => { if ( /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i . test ( a ) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i . test ( a . substr ( 0 , 4 ) ) ) check = true ; } ) ( navigator . userAgent || navigator . vendor ) ; } return check ; } param { string } cname param { string } cvalue param { integer } exdays private setCookie ( cname , cvalue , exdays ) { if ( this . isBrowser ) { let d = new Date ( ) ; d . setTime ( d . getTime ( ) + ( exdays * 24 * 60 * 60 * 1000 ) ) ; let expires = "expires=" + d . toUTCString ( ) ; document . cookie = cname + "=" + cvalue + "; " ; } } param { string } cname private getCookie ( cname ) { if ( this . isBrowser ) { var name = cname + "=" ; var ca = document . cookie . split ( ';' ) ; for ( var i = 0 ; i < ca . length ; i ++ ) { var c = ca [ i ] ; while ( c . charAt ( 0 ) == ' ' ) { c = c . substring ( 1 ) ; } if ( c . indexOf ( name ) == 0 ) { return c . substring ( name . length , c . length ) ; } } } return "" ; } /**
     * Create device id
     */ private createDeviceId ( ) { if ( this . getCookie ( "device_id" ) === "" ) { this . deviceId = this . guid ( ) ; this . setCookie ( "device_id" , this . deviceId , 100 ) ; } else { this . deviceId = this . getCookie ( "device_id" ) ; } } param e addUserClick ( e ) { let userClick = new UserClick ( ) ; let targetValues = [ 'id' , 'name' , 'className' , 'innerText' ] ; userClick . selectorName = "undefined" ; for ( let i = 0 ; i < targetValues . length ; i ++ ) { let targetVal = targetValues [ i ] ; if ( e . target [ targetVal ] ) { if ( typeof e . target [ targetVal ] === 'object' ) { userClick . selectorName = e . target [ targetVal ] . name ; } else { userClick . selectorName = e . target [ targetVal ] ; } break ; } } userClick . time = new Date ( ) ; userClick . route = this . router . url ; this . click . next ( userClick ) ; } }