<commit_before>var path = require('path');
var orm = require('../../config/orm.js');
var UserModel = require('../../model/user.js');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var orm = require('../../config/orm.js');

//Setting the strategy for Passport
passport.use(new LocalStrategy({passReqToCallback : true},
  function(req, userName, password, done) {
  	//Searching the ORM for the user in the database
  	orm.findUser(userName, function(err, user){
  		//use this to troubleshoot
  		//console.log('first', user);
  		user = user[0];

  		if (err) { return done(err); }

      if (!user) { return done(null, false); }

      //comparing user passwords - return if not a match
      if (password !== user.password) { return done(null, false);}

      return done(null, user);
  	});
  }
));

//These two methods are required to keep the user logged in via the session
passport.serializeUser(function(Users, done) {
  done(null, Users);
});

passport.deserializeUser(function(Users, done) {
  done(null, Users);
});

module.exports = function (app){

	// HTML GET Requests
	// Below code handles when users "visit" a page.
	// In each of the below cases the user is shown an HTML page of content
	// ---------------------------------------------------------------------------

	app.get('/', function(req, res){
		res.render('home', {
			title: 'Home',
			link: 'home',
			welcomeText: "Sign In",
			actionBtn: 'Sign Up',
			message: req.flash('error')[0],
			otherAction: "Sign In"
			});
	});

	app.get('/home', function(req, res){
		orm.getCrisisDetails(function(crisis_details) {
			res.render('home', {
				title: 'Home',
				link: 'home',
				crisis: crisis_details
			});
		});
	});

	app.get('/crisis', function(req, res){
		var user_id = parseInt(req.user.userID);

		res.render('crisis', {
			title: 'Crisis Manager',
			link: 'crisis',
			active_crisis: true,
			userID: user_id,
			username: req.user.userName,
		});
	});

	app.get('/signin', function(req, res){
		res.render('signin', {
			title: 'Sign In',
			link: 'dashboard',

		});

		// console.log('sign in succesfull ' + req.user.userName);
	});

	app.get('/signup', function(req, res){
		res.render('signup', {
			title: 'Sign Up',
			link: 'signin',
			welcomeText: "Sign Up",
			actionBtn: 'signup',
			otherAction: "signin"
		});
	});

	app.get('/tasks', function(req, res){
		var user_id = parseInt(req.user.userID);

		orm.allTasks(function(all_tasks) {
			res.render('tasks', {
				username: req.user.userName,
				title: 'Tasks',
				link: 'tasks',
				active_tasks: true,
				tasks: all_tasks,
				userID: user_id,
			});
		});
	});

	app.get('/task/:task_id', function(req, res){
		var task_id = parseInt(req.params.task_id);
		var user_id = parseInt(req.user.userID);

		orm.specificTask(task_id, function(the_task) {
			res.render('task', {
				username: req.user.userName,
				layout: 'subdir',
				title: 'Task',
				link: 'task',
				active_tasks: true,
				task: the_task,
				userID: user_id,
			});
		});
		// if (req.isAuthenticated()) {
		// 	res.render('/task/:task_id', {
		// 		username: req.user.username
		// 	})
		// } else {
		// 	res.redirect('/signin')
		// }
	});
	///:user_id
	app.get('/profile/:user_id', function(req, res){

		console.log('clicked on profile link' );

		var user_id = parseInt(req.user.userID);

		orm.memberProfile(user_id, function(memb) {
			orm.corpProfile(user_id, function(corp) {
				orm.memberTask(user_id, function(task) {

					if (req.isAuthenticated()){

						console.log('dynamic profile working, userID = ' + user_id)

						res.render('profile', {
						username: req.user.userName,
						layout: 'subdir',
						title: "Profile",
						link: 'profile',
						active_profile: true,
						member: memb,
						corporation: corp,
	 					userID: user_id,
	 					task: task
	 					});
					}else{
						console.log('fith place');
						res.redirect('/signin')
					}
				});
  			});
  		});
  	});

  	app.get('/dashboard', function(req, res){
  		var user_id = parseInt(req.user.userID);
		// if (req.isAuthenticated()) {
		// 	res.render('dashboard', {

		// 		// username: req.user.username
		// 	})
		// } else {
		// 	res.redirect('/signin')
		// }
		// get number of volunteers still needed
		orm.numVolsNeeded(function(num_vols) {
			orm.numVolsWhoHaveVolunteered(function(vols_volunteered) {
				orm.totalNumMembers(function(total_membs) {
					orm.totalVolPositions(function(vol_positions) {
						orm.totalCompletedTasks(function(comp_tasks) {
							orm.totalTasks(function(tot_tasks) {
								orm.dashboardTasksList(function(tasks_three) {
									if (req.isAuthenticated()) {
										console.log(req.user.userID);
									res.render('dashboard', {
										username: req.user.userName,
										title: 'Dashboard',
										link: 'dashboard',
										active_dashboard: true,
										vols: num_vols,
										vols_volun: vols_volunteered,
										membs: total_membs,
										vol_pos: vol_positions,
										completed_tasks: comp_tasks,
										total_tasks: tot_tasks,
										db_tasks: tasks_three,
										userID: req.user.userID
									});
								};
							});
						});
					});
				});
			})
		});
	});
});

	app.get('/corp', function(req, res){
		// get all corporate users
		orm.allCorpUsers(function(all_corps) {

			res.render('corp', {
				title: 'Corporation List', // breadcrumbs title
				link: 'corp', // link to pass to breadcrumbs
				active_crisis: true, // active class to display on admin nav
				corp_list: all_corps, // mysql data to pass to handlebars page
				userID: req.user.userID
			});
			if (req.isAuthenticated()) {
			res.render('/corp', {
				username: req.user.userName
			})
			} else {
				res.redirect('/signin')
			}
		});
	});

	app.get('/logout', function(req, res){
	  req.logout();
	  res.redirect('/');
	});

	// =============== POSTS ================= //

	app.post('/signin', passport.authenticate('local',{failureRedirect:'/', failureFlash:'Wrong Username or Password'}), function(req, res){

		res.redirect('/dashboard');
		console.log("post /sign: " + req.user.userName);
		});

	app.post('/signup', function(req, res){
		var user = new UserModel(req.body);
		UserModel.saveUser(Users, function(status){
			if(!status) {
				res.redirect('/signin')
				return false
			}
			res.redirect('/dashboard');
  		});
  	});

	// If no matching route is found default to home
	app.use(function(req, res){
		// res.sendFile(path.join(__dirname '/../public/home.html'));
		res.render('404', {
			title: 'CrisisCorps Not Found',
			link: '404'
		});
	});
}
<commit_msg>fixed corp not showing bug<commit_after>var path = require('path');
var orm = require('../../config/orm.js');
var UserModel = require('../../model/user.js');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var orm = require('../../config/orm.js');

//Setting the strategy for Passport
passport.use(new LocalStrategy({passReqToCallback : true},
  function(req, userName, password, done) {
  	//Searching the ORM for the user in the database
  	orm.findUser(userName, function(err, user){
  		//use this to troubleshoot
  		//console.log('first', user);
  		user = user[0];

  		if (err) { return done(err); }

      if (!user) { return done(null, false); }

      //comparing user passwords - return if not a match
      if (password !== user.password) { return done(null, false);}

      return done(null, user);
  	});
  }
));

//These two methods are required to keep the user logged in via the session
passport.serializeUser(function(Users, done) {
  done(null, Users);
});

passport.deserializeUser(function(Users, done) {
  done(null, Users);
});

module.exports = function (app){

	// HTML GET Requests
	// Below code handles when users "visit" a page.
	// In each of the below cases the user is shown an HTML page of content
	// ---------------------------------------------------------------------------

	app.get('/', function(req, res){
		res.render('home', {
			title: 'Home',
			link: 'home',
			welcomeText: "Sign In",
			actionBtn: 'Sign Up',
			message: req.flash('error')[0],
			otherAction: "Sign In"
			});
	});

	app.get('/home', function(req, res){
		orm.getCrisisDetails(function(crisis_details) {
			res.render('home', {
				title: 'Home',
				link: 'home',
				crisis: crisis_details
			});
		});
	});

	app.get('/crisis', function(req, res){
		var user_id = parseInt(req.user.userID);

		res.render('crisis', {
			title: 'Crisis Manager',
			link: 'crisis',
			active_crisis: true,
			userID: user_id,
			username: req.user.userName,
		});
	});

	app.get('/signin', function(req, res){
		res.render('signin', {
			title: 'Sign In',
			link: 'dashboard',

		});

		// console.log('sign in succesfull ' + req.user.userName);
	});

	app.get('/signup', function(req, res){
		res.render('signup', {
			title: 'Sign Up',
			link: 'signin',
			welcomeText: "Sign Up",
			actionBtn: 'signup',
			otherAction: "signin"
		});
	});

	app.get('/tasks', function(req, res){
		var user_id = parseInt(req.user.userID);

		orm.allTasks(function(all_tasks) {
			res.render('tasks', {
				username: req.user.userName,
				title: 'Tasks',
				link: 'tasks',
				active_tasks: true,
				tasks: all_tasks,
				userID: user_id,
			});
		});
	});

	app.get('/task/:task_id', function(req, res){
		var task_id = parseInt(req.params.task_id);
		var user_id = parseInt(req.user.userID);

		orm.specificTask(task_id, function(the_task) {
			res.render('task', {
				username: req.user.userName,
				layout: 'subdir',
				title: 'Task',
				link: 'task',
				active_tasks: true,
				task: the_task,
				userID: user_id,
			});
		});
		// if (req.isAuthenticated()) {
		// 	res.render('/task/:task_id', {
		// 		username: req.user.username
		// 	})
		// } else {
		// 	res.redirect('/signin')
		// }
	});
	///:user_id
	app.get('/profile/:user_id', function(req, res){

		console.log('clicked on profile link' );

		var user_id = parseInt(req.user.userID);

		orm.memberProfile(user_id, function(memb) {
			orm.corpProfile(user_id, function(corp) {
				orm.memberTask(user_id, function(task) {

					if (req.isAuthenticated()){

						console.log('dynamic profile working, userID = ' + user_id)

						res.render('profile', {
						username: req.user.userName,
						layout: 'subdir',
						title: "Profile",
						link: 'profile',
						active_profile: true,
						member: memb,
						corporation: corp,
	 					userID: user_id,
	 					task: task
	 					});
					}else{
						console.log('fith place');
						res.redirect('/signin')
					}
				});
  			});
  		});
  	});

  	app.get('/dashboard', function(req, res){
  		var user_id = parseInt(req.user.userID);
		// if (req.isAuthenticated()) {
		// 	res.render('dashboard', {

		// 		// username: req.user.username
		// 	})
		// } else {
		// 	res.redirect('/signin')
		// }
		// get number of volunteers still needed
		orm.numVolsNeeded(function(num_vols) {
			orm.numVolsWhoHaveVolunteered(function(vols_volunteered) {
				orm.totalNumMembers(function(total_membs) {
					orm.totalVolPositions(function(vol_positions) {
						orm.totalCompletedTasks(function(comp_tasks) {
							orm.totalTasks(function(tot_tasks) {
								orm.dashboardTasksList(function(tasks_three) {
									if (req.isAuthenticated()) {
										console.log(req.user.userID);
									res.render('dashboard', {
										username: req.user.userName,
										title: 'Dashboard',
										link: 'dashboard',
										active_dashboard: true,
										vols: num_vols,
										vols_volun: vols_volunteered,
										membs: total_membs,
										vol_pos: vol_positions,
										completed_tasks: comp_tasks,
										total_tasks: tot_tasks,
										db_tasks: tasks_three,
										userID: req.user.userID
									});
								};
							});
						});
					});
				});
			})
		});
	});
});

	app.get('/corp', function(req, res){
		// get all corporate users
		orm.allCorpUsers(function(all_corps) {

			if (req.isAuthenticated()) {
				res.render('corp', {
				title: 'Corporation List', // breadcrumbs title
				link: 'corp', // link to pass to breadcrumbs
				active_crisis: true, // active class to display on admin nav
				corp_list: all_corps, // mysql data to pass to handlebars page
				userID: req.user.userID,
				username: req.user.userName
			})
			} else {
				res.redirect('/signin')
			}
		});
	});

	app.get('/logout', function(req, res){
	  req.logout();
	  res.redirect('/');
	});

	// =============== POSTS ================= //

	app.post('/signin', passport.authenticate('local',{failureRedirect:'/', failureFlash:'Wrong Username or Password'}), function(req, res){

		res.redirect('/dashboard');
		console.log("post /sign: " + req.user.userName);
		});

	app.post('/signup', function(req, res){
		var user = new UserModel(req.body);
		UserModel.saveUser(Users, function(status){
			if(!status) {
				res.redirect('/signin')
				return false
			}
			res.redirect('/dashboard');
  		});
  	});

	// If no matching route is found default to home
	app.use(function(req, res){
		// res.sendFile(path.join(__dirname '/../public/home.html'));
		res.render('404', {
			title: 'CrisisCorps Not Found',
			link: '404'
		});
	});
}
<|endoftext|>


<commit_before>(function () {
    $(function(){
        FastClick.attach(document.body);

        var router = new Routy.Router();

        router.rootRegister({
            url: '/views/projects.html',
            context: ".container"
        }, PROJECT.ctrl);

        router.register('/projects', {
            url: '/views/projects.html',
            context: ".container"
        }, PROJECT.ctrl);

        router.register('pp', {
            url: '/views/pp.html',
            context: ".container"
        } ,function(){
            console.log('/pp');
        });

    })
})();
<commit_msg>include bootstrap module only when necessary<commit_after>(function () {
    $(function(){
        FastClick.attach(document.body);

        var router = new Routy.Router();

        router.rootRegister({
            url: '/views/projects.html',
            context: ".container"
        }, PROJECT.ctrl);

        router.register('/projects', {
            url: '/views/projects.html',
            context: ".container"
        }, PROJECT.ctrl);

        router.register('/pp', {
            url: '/views/pp.html',
            context: ".container"
        } ,function(){
            console.log('/pp');
        });

    })
})();
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Jason Ish
 * All rights reserved.
 */

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

app.controller("NavBarController", function ($routeParams, $scope, $modal,
    $location, Keyboard) {

    $scope.$routeParams = $routeParams;

    $scope.openConfig = function () {
        $modal.open({
            templateUrl: "views/config.html",
            controller: "ConfigController"
        });
    };

    $scope.openHelp = function () {
        $modal.open({
            templateUrl: "views/help.html",
            size: "lg"
        });
    };

    Keyboard.scopeBind($scope, "g i", function (e) {
        $scope.$apply(function () {
            $location.url("/inbox");
        });
    });

    Keyboard.scopeBind($scope, "g s", function (e) {
        $scope.$apply(function () {
            $location.url("/starred")
        });
    });

    Keyboard.scopeBind($scope, "g e", function (e) {
        $scope.$apply(function () {
            $location.url("/events");
        });
    });

    Keyboard.scopeBind($scope, "g c", function (e) {
        $scope.openConfig();
    });

    Keyboard.scopeBind($scope, "?", function (e) {
        $scope.openHelp();
    })
});

app.controller("ConfigController", function ($scope, $modalInstance, Config) {

    $scope.config = Config;

    $scope.ok = function () {
        Config.save();
        $modalInstance.close();
    };

    $scope.cancel = function () {
        $modalInstance.dismiss();
    };

});

app.controller("RecordController", function ($scope, $routeParams, Util,
    ElasticSearch) {

    // Export some functions to $scope.
    $scope.Util = Util;

    ElasticSearch.searchEventById($routeParams.id)
        .success(function (response) {
            $scope.response = response;
            $scope.hits = response.hits;
        });

});

app.controller("EventDetailController", function ($scope, Keyboard) {

    $scope.$on("$destroy", function () {
        Keyboard.resetScope($scope);
    });

    Keyboard.scopeBind($scope, ".", function () {
        $("#event-detail-more-button").first().dropdown('toggle');
    });

});

app.controller("ModalProgressController", function ($scope, jobs) {

    $scope.jobs = jobs;

});

app.controller("AggregatedController", function ($scope, $location, Keyboard,
    ElasticSearch, $modal, $routeParams) {

    console.log("AggregatedController");

    AggregatedController = $scope;

    $scope.activeRowIndex = 0;

    var getActiveRow = function () {
        return $scope.aggregations[$scope.activeRowIndex];
    };

    /**
     * Opens an aggregation in "flat" view by explicitly setting no aggregation
     * and constructing a query.
     */
    $scope.openAggregation = function (agg) {
        var searchParams = $location.search();
        searchParams.aggregateBy = "";
        searchParams.q = "q" in searchParams ? searchParams.q : "";
        searchParams.q += " +alert.signature.raw:\"" + agg.signature + "\"";
        if (agg.src_ip) {
            searchParams.q += " +src_ip.raw:\"" + agg.src_ip + "\"";
        }
        searchParams.q = searchParams.q.trim();

        $location.search(searchParams);
    };

    $scope.toggleActiveRow = function () {
        var row = getActiveRow();
        row.__selected = !row.__selected;
    };

    $scope.selectAll = function () {
        _.forEach($scope.aggregations, function (agg) {
            agg.__selected = true;
        });
    };

    $scope.deselectAll = function () {
        _.forEach($scope.aggregations, function (agg) {
            agg.__selected = false;
        });
    };

    $scope.selectedCount = function () {
        try {
            return _.filter($scope.aggregations, function (agg) {
                return agg.__selected;
            }).length;
        }
        catch (err) {
            return 0;
        }
    };

    $scope.totalEventCount = function () {
        return _.reduce($scope.aggregations, function (sum, agg) {
            return sum + agg.count;
        }, 0);
    };

    $scope.deleteRow = function (row) {
        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    }
                }
            }
        };

        query.query.filtered.filter = {
            "and": _.cloneDeep($scope.filters)
        };

        query.query.filtered.filter.and.push({
            "range": {
                "@timestamp": {
                    "lte": row.last_timestamp
                }
            }
        });

        query.query.filtered.filter.and.push({
            "term": {
                "alert.signature.raw": row.signature
            }
        });

        if (row.src_ip) {
            query.query.filtered.filter.and.push({
                "term": {
                    "src_ip.raw": row.src_ip
                }
            });
        }

        return ElasticSearch.deleteByQuery(query);

    };

    $scope.deleteSelected = function () {
        var selectedRows = _.filter($scope.aggregations, "__selected");

        if (!selectedRows) {
            return;
        }

        var row = selectedRows.pop();

        $scope.deleteRow(row)
            .success(function (response) {
                _.remove($scope.aggregations, row);
                if ($scope.activeRowIndex > 0 && _.indexOf($scope.aggregations, row) < $scope.activeRowIndex) {
                    $scope.activeRowIndex--;
                }
            })
            .finally(function () {
                $scope.deleteSelected();
            });
    };

    $scope.buildArchiveRowQuery = function (row) {
        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    },
                    filter: {
                        and: [
                            {
                                term: { tags: "inbox"}
                            },
                            {
                                range: {
                                    "@timestamp": {
                                        "lte": row.last_timestamp
                                    }
                                }
                            },
                            {
                                term: {
                                    "alert.signature.raw": row.signature
                                }
                            }
                        ]
                    }
                }
            },
            size: 1000,
            fields: ["_index", "_type", "_id"],
            sort: [
                {"@timestamp": {order: "desc"}}
            ]
        };

        if (row.src_ip) {
            query.query.filtered.filter.and.push({
                "term": {
                    "src_ip.raw": row.src_ip
                }
            });
        }

        return query;
    };

    $scope.archiveSelected = function () {
        var selectedRows = _.filter($scope.aggregations, "__selected");

        if (selectedRows.length == 0) {
            return $scope.displayErrorMessage("No events selected.");
        }

        var archiveJobs = selectedRows.map(function (row) {
            return {
                row: row,
                label: row.signature,
                query: $scope.buildArchiveRowQuery(row)
            };
        });

        var modal = $modal.open({
            templateUrl: "templates/modal-progress.html",
            controller: "ModalProgressController",
            resolve: {
                jobs: function () {
                    return archiveJobs;
                }
            }
        });

        var doArchiveJob = function (job) {

            ElasticSearch.search(job.query)
                .success(function (response) {
                    if (job.max === undefined) {
                        job.max = response.hits.total;
                        job.value = 0;
                    }
                    if (response.hits.hits.length > 0) {
                        ElasticSearch.bulkRemoveTag(response.hits.hits, "inbox")
                            .success(function (response) {
                                job.value += response.items.length;
                                doArchiveJob(job);
                            });
                    }
                    else {
                        var row = job.row;
                        var rowIndex = _.indexOf($scope.aggregations, row);
                        if (($scope.activeRowIndex > 0) && (rowIndex <= $scope.activeRowIndex)) {
                            $scope.activeRowIndex--;
                        }
                        _.remove($scope.aggregations, row);
                        _.remove(archiveJobs, job);
                        if (archiveJobs.length == 0) {
                            console.log("No archive jobs left; closing modal.");
                            modal.close();
                        }
                    }
                });

        };

        _.forEach(archiveJobs, doArchiveJob);

    };

    $scope.archiveByQuery = function () {
        if ($scope.response.hits.total == 0) {
            $scope.displayErrorMessage("No events to archive.");
            return;
        }

        var lastTimestamp = _.max($scope.aggregations, function (row) {
            return row.last_timestamp;
        }).last_timestamp;

        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    },
                    filter: {
                        and: [
                            {
                                term: { tags: "inbox" }
                            },
                            {
                                range: {
                                    "@timestamp": {
                                        "lte": lastTimestamp
                                    }
                                }
                            }
                        ]
                    }
                }
            },
            size: 1000,
            fields: ["_index", "_type", "_id"],
            sort: [
                {"@timestamp": {order: "desc"}}
            ]
        };

        $scope.doArchiveByQuery("Archiving...", query);
    };

    $scope.deleteByQuery = function () {
        if ($scope.response.hits.total == 0) {
            $scope.displayErrorMessage("No events to delete.");
            return;
        }

        var lastTimestamp = _.max($scope.aggregations, function (row) {
            return row.last_timestamp;
        }).last_timestamp;

        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    },
                    filter: {
                        and: [
                            {
                                range: {
                                    "@timestamp": {
                                        "lte": lastTimestamp
                                    }
                                }
                            }
                        ]
                    }
                }
            }
        };

        if ($routeParams.view == "inbox") {
            query.query.filtered.filter.and.push({term: {tags: "inbox"}});
        }

        ElasticSearch.deleteByQuery(query)
            .success(function (response) {
                $scope.page = 1;
                $scope.refresh();
            })
            .error(function (error) {
                console.log(error);
            })
    };

    $scope.changeSortBy = function (what) {
        if ($scope.sortBy == what) {
            $location.search("sortByOrder",
                $scope.sortByOrder == "desc" ? "asc" : "desc");
        }
        else {
            $location.search("sortBy", what);
        }
    };

    $scope.$on("$destroy", function () {
        Keyboard.resetScope($scope);
    });

    Keyboard.scopeBind($scope, "* a", function (e) {
        $scope.$apply($scope.selectAll());
    });

    Keyboard.scopeBind($scope, "* n", function (e) {
        $scope.$apply($scope.deselectAll());
    });

    Keyboard.scopeBind($scope, "o", function () {
        $scope.$apply(function () {
            var row = getActiveRow();
            $scope.openAggregation(row);
        });
    });

    Keyboard.scopeBind($scope, "x", function () {
        $scope.$apply(function () {
            $scope.toggleActiveRow();
        });
    });

    Keyboard.scopeBind($scope, "shift+j", function () {
        $scope.toggleActiveRow();
        Mousetrap.trigger("j");
    });

    Keyboard.scopeBind($scope, "shift+k", function () {
        $scope.toggleActiveRow();
        Mousetrap.trigger("k");
    });

    Keyboard.scopeBind($scope, "e", function () {
        $scope.$apply(function () {
            $scope.archiveSelected();
        });
    });

    Keyboard.scopeBind($scope, "#", function () {
        $scope.$apply(function () {
            $scope.deleteSelected();
        });
    });
});<commit_msg>In aggregate view, refresh if the archiving selected removes all aggregations.<commit_after>/*
 * Copyright (c) 2014 Jason Ish
 * All rights reserved.
 */

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

app.controller("NavBarController", function ($routeParams, $scope, $modal,
    $location, Keyboard) {

    $scope.$routeParams = $routeParams;

    $scope.openConfig = function () {
        $modal.open({
            templateUrl: "views/config.html",
            controller: "ConfigController"
        });
    };

    $scope.openHelp = function () {
        $modal.open({
            templateUrl: "views/help.html",
            size: "lg"
        });
    };

    Keyboard.scopeBind($scope, "g i", function (e) {
        $scope.$apply(function () {
            $location.url("/inbox");
        });
    });

    Keyboard.scopeBind($scope, "g s", function (e) {
        $scope.$apply(function () {
            $location.url("/starred")
        });
    });

    Keyboard.scopeBind($scope, "g e", function (e) {
        $scope.$apply(function () {
            $location.url("/events");
        });
    });

    Keyboard.scopeBind($scope, "g c", function (e) {
        $scope.openConfig();
    });

    Keyboard.scopeBind($scope, "?", function (e) {
        $scope.openHelp();
    })
});

app.controller("ConfigController", function ($scope, $modalInstance, Config) {

    $scope.config = Config;

    $scope.ok = function () {
        Config.save();
        $modalInstance.close();
    };

    $scope.cancel = function () {
        $modalInstance.dismiss();
    };

});

app.controller("RecordController", function ($scope, $routeParams, Util,
    ElasticSearch) {

    // Export some functions to $scope.
    $scope.Util = Util;

    ElasticSearch.searchEventById($routeParams.id)
        .success(function (response) {
            $scope.response = response;
            $scope.hits = response.hits;
        });

});

app.controller("EventDetailController", function ($scope, Keyboard) {

    $scope.$on("$destroy", function () {
        Keyboard.resetScope($scope);
    });

    Keyboard.scopeBind($scope, ".", function () {
        $("#event-detail-more-button").first().dropdown('toggle');
    });

});

app.controller("ModalProgressController", function ($scope, jobs) {

    $scope.jobs = jobs;

});

app.controller("AggregatedController", function ($scope, $location, Keyboard,
    ElasticSearch, $modal, $routeParams) {

    console.log("AggregatedController");

    AggregatedController = $scope;

    $scope.activeRowIndex = 0;

    var getActiveRow = function () {
        return $scope.aggregations[$scope.activeRowIndex];
    };

    /**
     * Opens an aggregation in "flat" view by explicitly setting no aggregation
     * and constructing a query.
     */
    $scope.openAggregation = function (agg) {
        var searchParams = $location.search();
        searchParams.aggregateBy = "";
        searchParams.q = "q" in searchParams ? searchParams.q : "";
        searchParams.q += " +alert.signature.raw:\"" + agg.signature + "\"";
        if (agg.src_ip) {
            searchParams.q += " +src_ip.raw:\"" + agg.src_ip + "\"";
        }
        searchParams.q = searchParams.q.trim();

        $location.search(searchParams);
    };

    $scope.toggleActiveRow = function () {
        var row = getActiveRow();
        row.__selected = !row.__selected;
    };

    $scope.selectAll = function () {
        _.forEach($scope.aggregations, function (agg) {
            agg.__selected = true;
        });
    };

    $scope.deselectAll = function () {
        _.forEach($scope.aggregations, function (agg) {
            agg.__selected = false;
        });
    };

    $scope.selectedCount = function () {
        try {
            return _.filter($scope.aggregations, function (agg) {
                return agg.__selected;
            }).length;
        }
        catch (err) {
            return 0;
        }
    };

    $scope.totalEventCount = function () {
        return _.reduce($scope.aggregations, function (sum, agg) {
            return sum + agg.count;
        }, 0);
    };

    $scope.deleteRow = function (row) {
        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    }
                }
            }
        };

        query.query.filtered.filter = {
            "and": _.cloneDeep($scope.filters)
        };

        query.query.filtered.filter.and.push({
            "range": {
                "@timestamp": {
                    "lte": row.last_timestamp
                }
            }
        });

        query.query.filtered.filter.and.push({
            "term": {
                "alert.signature.raw": row.signature
            }
        });

        if (row.src_ip) {
            query.query.filtered.filter.and.push({
                "term": {
                    "src_ip.raw": row.src_ip
                }
            });
        }

        return ElasticSearch.deleteByQuery(query);

    };

    $scope.deleteSelected = function () {
        var selectedRows = _.filter($scope.aggregations, "__selected");

        if (!selectedRows) {
            return;
        }

        var row = selectedRows.pop();

        $scope.deleteRow(row)
            .success(function (response) {
                _.remove($scope.aggregations, row);
                if ($scope.activeRowIndex > 0 && _.indexOf($scope.aggregations, row) < $scope.activeRowIndex) {
                    $scope.activeRowIndex--;
                }
            })
            .finally(function () {
                $scope.deleteSelected();
            });
    };

    $scope.buildArchiveRowQuery = function (row) {
        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    },
                    filter: {
                        and: [
                            {
                                term: { tags: "inbox"}
                            },
                            {
                                range: {
                                    "@timestamp": {
                                        "lte": row.last_timestamp
                                    }
                                }
                            },
                            {
                                term: {
                                    "alert.signature.raw": row.signature
                                }
                            }
                        ]
                    }
                }
            },
            size: 1000,
            fields: ["_index", "_type", "_id"],
            sort: [
                {"@timestamp": {order: "desc"}}
            ]
        };

        if (row.src_ip) {
            query.query.filtered.filter.and.push({
                "term": {
                    "src_ip.raw": row.src_ip
                }
            });
        }

        return query;
    };

    $scope.archiveSelected = function () {
        var selectedRows = _.filter($scope.aggregations, "__selected");

        if (selectedRows.length == 0) {
            return $scope.displayErrorMessage("No events selected.");
        }

        var archiveJobs = selectedRows.map(function (row) {
            return {
                row: row,
                label: row.signature,
                query: $scope.buildArchiveRowQuery(row)
            };
        });

        var modal = $modal.open({
            templateUrl: "templates/modal-progress.html",
            controller: "ModalProgressController",
            resolve: {
                jobs: function () {
                    return archiveJobs;
                }
            }
        });

        modal.result.then(function () {
            if ($scope.aggregations.length == 0) {
                $scope.refresh();
            }
        });

        var doArchiveJob = function (job) {

            ElasticSearch.search(job.query)
                .success(function (response) {
                    if (job.max === undefined) {
                        job.max = response.hits.total;
                        job.value = 0;
                    }
                    if (response.hits.hits.length > 0) {
                        ElasticSearch.bulkRemoveTag(response.hits.hits, "inbox")
                            .success(function (response) {
                                job.value += response.items.length;
                                doArchiveJob(job);
                            });
                    }
                    else {
                        var row = job.row;
                        var rowIndex = _.indexOf($scope.aggregations, row);
                        if (($scope.activeRowIndex > 0) && (rowIndex <= $scope.activeRowIndex)) {
                            $scope.activeRowIndex--;
                        }
                        _.remove($scope.aggregations, row);
                        _.remove(archiveJobs, job);
                        if (archiveJobs.length == 0) {
                            console.log("No archive jobs left; closing modal.");
                            modal.close();
                        }
                    }
                });

        };

        _.forEach(archiveJobs, doArchiveJob);

    };

    $scope.archiveByQuery = function () {
        if ($scope.response.hits.total == 0) {
            $scope.displayErrorMessage("No events to archive.");
            return;
        }

        var lastTimestamp = _.max($scope.aggregations, function (row) {
            return row.last_timestamp;
        }).last_timestamp;

        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    },
                    filter: {
                        and: [
                            {
                                term: { tags: "inbox" }
                            },
                            {
                                range: {
                                    "@timestamp": {
                                        "lte": lastTimestamp
                                    }
                                }
                            }
                        ]
                    }
                }
            },
            size: 1000,
            fields: ["_index", "_type", "_id"],
            sort: [
                {"@timestamp": {order: "desc"}}
            ]
        };

        $scope.doArchiveByQuery("Archiving...", query);
    };

    $scope.deleteByQuery = function () {
        if ($scope.response.hits.total == 0) {
            $scope.displayErrorMessage("No events to delete.");
            return;
        }

        var lastTimestamp = _.max($scope.aggregations, function (row) {
            return row.last_timestamp;
        }).last_timestamp;

        var query = {
            query: {
                filtered: {
                    query: {
                        query_string: {
                            query: $scope.userQuery || "*"
                        }
                    },
                    filter: {
                        and: [
                            {
                                range: {
                                    "@timestamp": {
                                        "lte": lastTimestamp
                                    }
                                }
                            }
                        ]
                    }
                }
            }
        };

        if ($routeParams.view == "inbox") {
            query.query.filtered.filter.and.push({term: {tags: "inbox"}});
        }

        ElasticSearch.deleteByQuery(query)
            .success(function (response) {
                $scope.page = 1;
                $scope.refresh();
            })
            .error(function (error) {
                console.log(error);
            })
    };

    $scope.changeSortBy = function (what) {
        if ($scope.sortBy == what) {
            $location.search("sortByOrder",
                    $scope.sortByOrder == "desc" ? "asc" : "desc");
        }
        else {
            $location.search("sortBy", what);
        }
    };

    $scope.$on("$destroy", function () {
        Keyboard.resetScope($scope);
    });

    Keyboard.scopeBind($scope, "* a", function (e) {
        $scope.$apply($scope.selectAll());
    });

    Keyboard.scopeBind($scope, "* n", function (e) {
        $scope.$apply($scope.deselectAll());
    });

    Keyboard.scopeBind($scope, "o", function () {
        $scope.$apply(function () {
            var row = getActiveRow();
            $scope.openAggregation(row);
        });
    });

    Keyboard.scopeBind($scope, "x", function () {
        $scope.$apply(function () {
            $scope.toggleActiveRow();
        });
    });

    Keyboard.scopeBind($scope, "shift+j", function () {
        $scope.toggleActiveRow();
        Mousetrap.trigger("j");
    });

    Keyboard.scopeBind($scope, "shift+k", function () {
        $scope.toggleActiveRow();
        Mousetrap.trigger("k");
    });

    Keyboard.scopeBind($scope, "e", function () {
        $scope.$apply(function () {
            $scope.archiveSelected();
        });
    });

    Keyboard.scopeBind($scope, "#", function () {
        $scope.$apply(function () {
            $scope.deleteSelected();
        });
    });
});<|endoftext|>


<commit_before>import AppConfig from 'scripts/config';
import Notifications from 'scripts/services/notifications';

export default class User extends Backbone.NestedModel {
  constructor(options) {
    this.urlRoot = `${AppConfig.apiPath}/sessions/sign_in`;
    this.validation = {
      'user.name': {
        required: true
      },
      'user.password': {
        required: true,
        minLength: 6
      }
    };

    super(options);

    this.listenTo(this, 'error', this.onErrorHandler);
    this.listenTo(this, 'validated:invalid', this.onInvalidValidation);
  }

  onErrorHandler(model, error) {
    if (error.status === 401 || error.status === 403) {
      if (error.responseJSON) Notifications.alert(error.responseJSON.error);
    }
  }

  onInvalidValidation(model, errors) {
    var message = _.chain(errors).values().join('\n').value();
    Notifications.error(message);
  }
}
<commit_msg>Alert fix<commit_after>import AppConfig from 'scripts/config';
import Notifications from 'scripts/services/notifications';

export default class User extends Backbone.NestedModel {
  constructor(options) {
    this.urlRoot = `${AppConfig.apiPath}/sessions/sign_in`;
    this.validation = {
      'user.name': {
        required: true
      },
      'user.password': {
        required: true,
        minLength: 6
      }
    };

    super(options);

    this.listenTo(this, 'error', this.onErrorHandler);
    this.listenTo(this, 'validated:invalid', this.onInvalidValidation);
  }

  onErrorHandler(model, error) {
    if (error.status === 401 || error.status === 403) {
      if (error.responseJSON) Notifications.error(error.responseJSON.error);
    }
  }

  onInvalidValidation(model, errors) {
    var message = _.chain(errors).values().join('\n').value();
    Notifications.error(message);
  }
}
<|endoftext|>


<commit_before>import Service from '@ember/service';
import {action} from '@ember/object';
import {observes} from '@ember-decorators/object';
import {inject as service} from '@ember/service';
import {set} from '@ember/object';
import {tracked} from '@glimmer/tracking';

const DEFAULT_SETTINGS = {
    expanded: {
        posts: true
    }
};

export default class NavigationService extends Service {
    @service session;

    @tracked settings;

    constructor() {
        super(...arguments);
        this.settings = Object.assign({}, DEFAULT_SETTINGS);
        this.updateSettings();
    }

    // eslint-disable-next-line ghost/ember/no-observers
    @observes('session.isAuthenticated', 'session.user.accessibility')
    async updateSettings() {
        // avoid fetching user before authenticated otherwise the 403 can fire
        // during authentication and cause errors during setup/signin
        if (!this.session.isAuthenticated) {
            return;
        }

        let user = await this.session.user;
        let userSettings = JSON.parse(user.get('accessibility')) || {};
        this.settings = userSettings.navigation || Object.assign({}, DEFAULT_SETTINGS);
    }

    @action
    async toggleExpansion(key) {
        if (!this.settings.expanded) {
            this.settings.expanded = {};
        }

        // set is still needed here because we're not tracking deep keys
        // and Ember picks up that our templates are dependent on them and
        // complains. TODO: can we avoid set?
        set(this.settings.expanded, key, !this.settings.expanded[key]);

        return await this._saveNavigationSettings();
    }

    async _saveNavigationSettings() {
        let user = await this.session.user;
        let userSettings = JSON.parse(user.get('accessibility'));
        userSettings.navigation = this.settings;
        user.set('accessibility', JSON.stringify(userSettings));
        return user.save();
    }
}
<commit_msg>Fixed saving of custom views expansion state<commit_after>import Service from '@ember/service';
import {action} from '@ember/object';
import {observes} from '@ember-decorators/object';
import {inject as service} from '@ember/service';
import {set} from '@ember/object';
import {tracked} from '@glimmer/tracking';

const DEFAULT_SETTINGS = {
    expanded: {
        posts: true
    }
};

export default class NavigationService extends Service {
    @service session;

    @tracked settings;

    constructor() {
        super(...arguments);
        this.settings = Object.assign({}, DEFAULT_SETTINGS);
        this.updateSettings();
    }

    // eslint-disable-next-line ghost/ember/no-observers
    @observes('session.isAuthenticated', 'session.user.accessibility')
    async updateSettings() {
        // avoid fetching user before authenticated otherwise the 403 can fire
        // during authentication and cause errors during setup/signin
        if (!this.session.isAuthenticated) {
            return;
        }

        let user = await this.session.user;
        let userSettings = JSON.parse(user.get('accessibility')) || {};
        this.settings = userSettings.navigation || Object.assign({}, DEFAULT_SETTINGS);
    }

    @action
    async toggleExpansion(key) {
        if (!this.settings.expanded) {
            this.settings.expanded = {};
        }

        // set is still needed here because we're not tracking deep keys
        // and Ember picks up that our templates are dependent on them and
        // complains. TODO: can we avoid set?
        set(this.settings.expanded, key, !this.settings.expanded[key]);

        return await this._saveNavigationSettings();
    }

    async _saveNavigationSettings() {
        let user = await this.session.user;
        let userSettings = JSON.parse(user.get('accessibility')) || {};
        userSettings.navigation = this.settings;
        user.set('accessibility', JSON.stringify(userSettings));
        return user.save();
    }
}
<|endoftext|>


<commit_before>var addSpaces = function(line, requiredSpaces) {

	if (line.length == 1) {
		return line;
	}

	var words = line.slice(0, line.length -1);
	var spacePositions = word.length - 1;

}

var justify = function(str, len) {
	var words = str.split(' ');
	var lines = [];

	var numberOfWords = words.length;
	for (var i = 0; i < numberOfWords; i++) {
		var line = [];
		var totalLineLength = 0;
		var currWordLength = 0;
		var wordCount = 0;

		for (var j = i; j < numberOfWords; j++) {
			if (words[j].length + totalLineLength < len) {
				var currWord = words[j];
				line.push(currWord);
				currWordLength += currWord.length;

				// Add space to each word
				totalLineLength += currWord.length + 1;
				wordCount++;
			} else {

				// Skip all added words
				i += wordCount - 1;
				break;
			}
		}
		if (i + 1 == numberOfWords) {
			lines.push(line.join(''));
		} else {
			line = addSpaces(line, len - currWordLength);
			lines.push(line.join(''));
		}

	}

	return lines.join('\n');
};
//var str = 'Benny wne go for away die new baby new majit';
var str = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sagittis dolor mauris, at elementum ligula tempor eget. In quis rhoncus nunc, at aliquet orci. Fusce at dolor sit amet felis suscipit tristique. Nam a imperdiet tellus. Nulla eu vestibulum urna. Vivamus tincidunt suscipit enim, nec ultrices nisi volutpat ac. Maecenas sit amet lacinia arcu, non dictum justo. Donec sed quam vel risus faucibus euismod. Suspendisse rhoncus rhoncus felis at fermentum. Donec lorem magna, ultricies a nunc sit amet, blandit fringilla nunc. In vestibulum velit ac felis rhoncus pellentesque. Mauris at tellus enim. Aliquam eleifend tempus dapibus. Pellentesque commodo, nisi sit amet hendrerit fringilla, ante odio porta lacus, ut elementum justo nulla et dolor.';

console.log(justify(str, 30));<commit_msg>change method name<commit_after>var justifyLine = function(line, requiredSpaces) {

	if (line.length == 1) {
		return line;
	}

	var words = line.slice(0, line.length -1);
	var numberOfSpacePositions = words.length;
	var spacePerPosition = Math.floor(requiredSpaces / numberOfSpacePositions);
	var remainingSpaces = requiredSpaces % numberOfSpacePositions;

	var wordsObject = [];
	words.forEach(function(word){
		wordsObject.push({spaces: []});
	});

	// Distribute first round of spaces
	wordsObject.forEach(function(o) {
		for (var i = 0; i < spacePerPosition; i++) {
			o.spaces.push(' ');
		}
	});

	// Distribute second round of spaces to avoid gaps
	if (remainingSpaces > 0) {
		for (var i = 0; i < remainingSpaces; i++) {
			wordsObject[i].spaces.push(' ');
		}
	}

	// Add spaces to create the justified line
	var justifiedLine = [];
	words.forEach(function(word, index) {
		justifiedLine.push(word);
		justifiedLine.push(wordsObject[index].spaces.join(''));
	});

	justifiedLine.push(line[words.length]);

	return justifiedLine.join('');
}

var justify = function(str, len) {
	var words = str.split(' ');
	var lines = [];

	var currLine = [];
	var currLineLength = 0;

	words.forEach(function(word, index) {
		if (currLineLength + word.length + 1 < len) {
			currLineLength += word.length + 1;
			currLine.push(word);
		} else {
			var requiredSpaces = len - (currLineLength - currLine.length);
			lines.push(justifyLine(currLine, requiredSpaces));
			currLine = [];
			currLine.push(word);
			currLineLength = word.length + 1;
		}
	});

	lines.push(currLine.join(' '));
	return lines.join('\n');
};
//var str = 'Benny wne go for away die new baby new majit';
var str = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sagittis dolor mauris, at elementum ligula tempor eget. In quis rhoncus nunc, at aliquet orci. Fusce at dolor sit amet felis suscipit tristique. Nam a imperdiet tellus. Nulla eu vestibulum urna. Vivamus tincidunt suscipit enim, nec ultrices nisi volutpat ac. Maecenas sit amet lacinia arcu, non dictum justo. Donec sed quam vel risus faucibus euismod. Suspendisse rhoncus rhoncus felis at fermentum. Donec lorem magna, ultricies a nunc sit amet, blandit fringilla nunc. In vestibulum velit ac felis rhoncus pellentesque. Mauris at tellus enim. Aliquam eleifend tempus dapibus. Pellentesque commodo, nisi sit amet hendrerit fringilla, ante odio porta lacus, ut elementum justo nulla et dolor.';

console.log(justify(str, 30));<|endoftext|>


<commit_before>var id_Generator = require('./id_generator');

var Chats_message_router = function(wscc) {
    this.wscc = wscc;
};

Chats_message_router.prototype = {

    get_chat_wscs: function(chat_id) {
        var chat_wscs = [];
        this.wscc.get_all_wsc().forEach(function(wsc) {
            if (wsc.readyState !== wsc.CLOSED && wsc.has_chat_id(chat_id)) {
                chat_wscs.push(wsc);
            }
        });
        return chat_wscs;
    },

    broadcast_chat_message: function(chat_id, responseData) {
        var active_wscs = [];
        this.get_chat_wscs(chat_id).forEach(function(wsc) {
            if (wsc.readyState !== wsc.CLOSED) {
                responseData.target_ws_device_id = wsc.ws_device_id;
                wsc.send(JSON.stringify(responseData));
                active_wscs.push(wsc);
            }
        });
        return active_wscs;
    },

    get_chat_wscs_device_ids: function(chat_id) {
        return this.get_chat_wscs(chat_id).filter(function(wsc) {
            return wsc.readyState !== wsc.CLOSED;
        }).map(function(wsc) {
            return wsc.get_ws_device_id();
        });
    },

    onDeviceCreateChat: function(cur_wsc, parsedMessageData) {
        var chat_id = id_Generator.generateId();
        cur_wsc.put_chat_id(chat_id);
        cur_wsc.put_user_id(parsedMessageData.user_id);

        var responseData = {
            type: 'chat_created',
            from_user_id: parsedMessageData.user_id,
            from_ws_device_id: cur_wsc.ws_device_id,
            chat_description: {
                chat_id: chat_id
            }
        };

        cur_wsc.send(JSON.stringify(responseData));

        console.log('Create chat from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'user_id = ' + parsedMessageData.user_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatJoin: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.user_id);

        var responseData = {
            type: 'chat_joined',
            from_user_id: parsedMessageData.user_id,
            from_ws_device_id: cur_wsc.ws_device_id,
            wscs_device_ids: this.get_chat_wscs_device_ids(parsedMessageData.chat_description.chat_id),
            chat_description: parsedMessageData.chat_description,
            restore_chat_state: parsedMessageData.restore_chat_state
        };

        this.broadcast_chat_message(responseData);

        console.log('Join chat from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'user_id = ' + responseData.user_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatOffer: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.user_id);

        var target_wsc = this.wscc.get_wsc_by_ws_dev_id(parsedMessageData.to_ws_device_id);
        if (!target_wsc) {
            cur_wsc.send_error(new Error('Target connection is not found!'), parsedMessageData);
            return;
        }

        var responseData = {
            type: 'notifyChat',
            notify_data: 'handleDevicePassive',
            from_ws_device_id: cur_wsc.ws_device_id,
            from_user_id: parsedMessageData.user_id,
            offerDescription: parsedMessageData.offerDescription,
            to_ws_device_id: target_wsc.ws_device_id,
            chat_description: parsedMessageData.chat_description
        };

        target_wsc.send(JSON.stringify(responseData));

        console.log(
            'Offer from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'to ws_device_id = ' + target_wsc.ws_device_id,
            'user_id = ' + responseData.user_id,
            'chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatAnswer: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.user_id);

        var target_wsc = this.wscc.get_ws_device_wsc(parsedMessageData.to_ws_device_id);
        if (!target_wsc) {
            cur_wsc.send_error(new Error('Target connection is not found!'), parsedMessageData);
            return;
        }

        var responseData = {
            type: 'notifyChat',
            notify_data: 'handleDeviceAnswer',
            from_ws_device_id: cur_wsc.ws_device_id,
            from_user_id: parsedMessageData.user_id,
            to_ws_device_id: target_wsc.ws_device_id,
            answerDescription: parsedMessageData.answerDescription,
            chat_description: parsedMessageData.chat_description
        };

        target_wsc.send(JSON.stringify(responseData));

        console.log(
            'Answer from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'to ws_device_id = ' + target_wsc.ws_device_id,
            'user_id = ' + responseData.user_id,
            'chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatAccept: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.user_id);

        var target_wsc = this.wscc.get_wsc_by_ws_dev_id(parsedMessageData.to_ws_device_id);
        if (!target_wsc) {
            cur_wsc.send_error(new Error('Target connection is not found!'), parsedMessageData);
            return;
        }

        var responseData = {
            type: 'notifyChat',
            notify_data: 'serverStoredAccept',
            from_ws_device_id: parsedMessageData.ws_device_id,
            from_user_id: parsedMessageData.user_id,
            to_ws_device_id: target_wsc.ws_device_id,
            chat_description: parsedMessageData.chat_description
        };

        target_wsc.send(JSON.stringify(responseData));

        console.log(
            'Accept from',
            'ws_device_id = ' + responseData.ws_device_id,
            'to ws_device_id = ' + target_wsc.ws_device_id,
            'user_id = ' + responseData.user_id,
            'chat_id = ' + responseData.chat_description.chat_id);
    }
};

module.exports = Chats_message_router;<commit_msg>Bugfixing on chat join approved<commit_after>var id_Generator = require('./id_generator');

var Chats_message_router = function(wscc) {
    this.wscc = wscc;
};

Chats_message_router.prototype = {

    get_chat_wscs: function(chat_id) {
        var chat_wscs = [];
        this.wscc.get_all_wsc().forEach(function(wsc) {
            if (wsc.readyState !== wsc.CLOSED && wsc.has_chat_id(chat_id)) {
                chat_wscs.push(wsc);
            }
        });
        return chat_wscs;
    },

    broadcast_chat_message: function(chat_id, responseData) {
        var active_wscs = [];
        this.get_chat_wscs(chat_id).forEach(function(wsc) {
            if (wsc.readyState !== wsc.CLOSED) {
                responseData.target_ws_device_id = wsc.ws_device_id;
                wsc.send(JSON.stringify(responseData));
                active_wscs.push(wsc);
            }
        });
        return active_wscs;
    },

    get_chat_wscs_device_ids: function(chat_id) {
        return this.get_chat_wscs(chat_id).filter(function(wsc) {
            return wsc.readyState !== wsc.CLOSED;
        }).map(function(wsc) {
            return wsc.get_ws_device_id();
        });
    },

    onDeviceCreateChat: function(cur_wsc, parsedMessageData) {
        var chat_id = id_Generator.generateId();
        cur_wsc.put_chat_id(chat_id);
        cur_wsc.put_user_id(parsedMessageData.from_user_id);

        var responseData = {
            type: 'chat_created',
            from_user_id: parsedMessageData.from_user_id,
            from_ws_device_id: cur_wsc.ws_device_id,
            chat_description: {
                chat_id: chat_id
            }
        };

        cur_wsc.send(JSON.stringify(responseData));

        console.log('Create chat from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'user_id = ' + parsedMessageData.from_user_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatJoin: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.from_user_id);

        var responseData = {
            type: 'chat_joined',
            from_user_id: parsedMessageData.from_user_id,
            from_ws_device_id: cur_wsc.ws_device_id,
            wscs_device_ids: this.get_chat_wscs_device_ids(parsedMessageData.chat_description.chat_id),
            chat_description: parsedMessageData.chat_description,
            restore_chat_state: parsedMessageData.restore_chat_state
        };

        this.broadcast_chat_message(responseData);

        console.log('Join chat from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'user_id = ' + responseData.from_user_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatOffer: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.from_user_id);

        var target_wsc = this.wscc.get_wsc_by_ws_dev_id(parsedMessageData.to_ws_device_id);
        if (!target_wsc) {
            cur_wsc.send_error(new Error('Target connection is not found!'), parsedMessageData);
            return;
        }

        var responseData = {
            type: 'notifyChat',
            notify_data: 'handleDevicePassive',
            from_ws_device_id: cur_wsc.ws_device_id,
            from_user_id: parsedMessageData.from_user_id,
            offerDescription: parsedMessageData.offerDescription,
            to_ws_device_id: target_wsc.ws_device_id,
            chat_description: parsedMessageData.chat_description
        };

        target_wsc.send(JSON.stringify(responseData));

        console.log(
            'Offer from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'user_id = ' + responseData.from_user_id,
            'to ws_device_id = ' + target_wsc.ws_device_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatAnswer: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.from_user_id);

        var target_wsc = this.wscc.get_ws_device_wsc(parsedMessageData.to_ws_device_id);
        if (!target_wsc) {
            cur_wsc.send_error(new Error('Target connection is not found!'), parsedMessageData);
            return;
        }

        var responseData = {
            type: 'notifyChat',
            notify_data: 'handleDeviceAnswer',
            from_ws_device_id: cur_wsc.ws_device_id,
            from_user_id: parsedMessageData.from_user_id,
            to_ws_device_id: target_wsc.ws_device_id,
            answerDescription: parsedMessageData.answerDescription,
            chat_description: parsedMessageData.chat_description
        };

        target_wsc.send(JSON.stringify(responseData));

        console.log(
            'Answer from',
            'ws_device_id = ' + cur_wsc.ws_device_id,
            'user_id = ' + responseData.from_user_id,
            'to ws_device_id = ' + target_wsc.ws_device_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    },

    onDeviceChatAccept: function(cur_wsc, parsedMessageData) {
        cur_wsc.put_chat_id(parsedMessageData.chat_description.chat_id);
        cur_wsc.put_user_id(parsedMessageData.from_user_id);

        var target_wsc = this.wscc.get_wsc_by_ws_dev_id(parsedMessageData.to_ws_device_id);
        if (!target_wsc) {
            cur_wsc.send_error(new Error('Target connection is not found!'), parsedMessageData);
            return;
        }

        var responseData = {
            type: 'notifyChat',
            notify_data: 'serverStoredAccept',
            from_ws_device_id: parsedMessageData.ws_device_id,
            from_user_id: parsedMessageData.from_user_id,
            to_ws_device_id: target_wsc.ws_device_id,
            chat_description: parsedMessageData.chat_description
        };

        target_wsc.send(JSON.stringify(responseData));

        console.log(
            'Accept from',
            'ws_device_id = ' + responseData.ws_device_id,
            'user_id = ' + responseData.from_user_id,
            'to ws_device_id = ' + target_wsc.ws_device_id,
            'to chat_id = ' + responseData.chat_description.chat_id);
    }
};

module.exports = Chats_message_router;<|endoftext|>


<commit_before>const path = require('path')
const setFrontmatter = require('./node_utils/setFrontmatter')
const getSidebarData = require('./node_utils/getSidebarData')
const {createPage, deletePage} = require('./node_utils/handlePage')
const chalk = require('chalk') // 命令行打印美化
const yaml = require('js-yaml') // yaml转js
const log = console.log

// md容器名
const CARD_LIST = 'cardList'
const CARD_IMG_LIST = 'cardImgList'

// Theme API.
module.exports = (options, ctx) => {
  const { sourceDir, themeConfig, siteConfig } = ctx

  // 自动设置front matter
  setFrontmatter(sourceDir, themeConfig)

  // 自动生成结构化侧边栏
  const sidebar = themeConfig.sidebar
  if(sidebar === 'structuring' || sidebar && sidebar.mode === 'structuring') {
    const collapsable = themeConfig.sidebar.collapsable === false ? false : true
    const sidebarData = getSidebarData(sourceDir, collapsable)
    if(sidebarData) {
      themeConfig.sidebar = sidebarData
      log(chalk.blue('tip ') + chalk.green('add sidebar data. 侧边栏数据成功生成。'))
    } else {
      themeConfig.sidebar = 'auto'
      log(chalk.yellow('warning: fail to add sidebar data, switch to "auto". 未能添加侧边栏数据，将切换为“auto”。'))
    }
  }

  // 分类页
  if (themeConfig.category !== false) {
    createPage(sourceDir, 'categoriesPage')
  } else {
    deletePage(sourceDir, 'categoriesPage')
  }

  // 标签页
  if (themeConfig.tag !== false) {
    createPage(sourceDir, 'tagsPage')
  } else {
    deletePage(sourceDir, 'tagsPage')
  }

  // 归档页
  if (themeConfig.archive !== false) {
    createPage(sourceDir, 'archivesPage')
  } else {
    deletePage(sourceDir, 'archivesPage')
  }

  // resolve algolia
  const isAlgoliaSearch = (
    themeConfig.algolia
    || Object
        .keys(siteConfig.locales && themeConfig.locales || {})
        .some(base => themeConfig.locales[base].algolia)
  )

  const enableSmoothScroll = themeConfig.smoothScroll === true

  return {
    alias () {
      return {
        '@AlgoliaSearchBox': isAlgoliaSearch
          ? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue')
          : path.resolve(__dirname, 'noopModule.js')
      }
    },

    plugins: [
      ['@vuepress/active-header-links', options.activeHeaderLinks],
      '@vuepress/search',
      '@vuepress/plugin-nprogress',
      ['smooth-scroll', enableSmoothScroll],

      ['container', {
        type: 'tip',
        defaultTitle: {
          '/': 'TIP',
          '/zh/': '提示'
        }
      }],
      ['container', {
        type: 'warning',
        defaultTitle: {
          '/': 'WARNING',
          '/zh/': '注意'
        }
      }],
      ['container', {
        type: 'danger',
        defaultTitle: {
          '/': 'WARNING',
          '/zh/': '警告'
        }
      }],
      ['container', {
        type: 'details',
        before: info => `<details class="custom-block details">${info ? `${info}` : ''}\n`,
        after: () => '\n'
      }],
      // 卡片列表
      [
        'container',
        {
          type: CARD_LIST,
          render:  (tokens, idx) => {
            // tokens 是整个md文件的虚拟dom结构数组
            // idx 是tokens中':::' 所在的索引，而且是当前指定type的':::'，分别有开始和结束两次的idx
            // if (tokens[idx].nesting === 1) { // 开头的 ':::' 标记
            // } else { // 结束的 ':::' 标记
            // }
            // 注意：修改这里面的代码后需要在md文件保存一下才会重新执行渲染
            return renderCardList(tokens, idx, CARD_LIST)
          }
        },
      ],

      // 图文卡片列表
      [
        'container',
        {
          type: CARD_IMG_LIST,
          render:  (tokens, idx) => {
            return renderCardList(tokens, idx, CARD_IMG_LIST)
          }
        },
      ],

    ]
  }
}

// 渲染md容器的卡片列表
function renderCardList(tokens, idx, type) {
  const END_TYPE = `container_${type}_close`,
        _tokens$idx = tokens[idx],
        nesting = _tokens$idx.nesting,
        info = _tokens$idx.info;

  if (nesting === 1) { // 渲染开头的 ':::' 标记
    let yamlStr = '';

    for (let i = idx; i < tokens.length; i++) {
      let _tokens$i = tokens[i],
          type = _tokens$i.type,
          content = _tokens$i.content,
          _info = _tokens$i.info;
      if (type === END_TYPE) break; // 遇到结束的 ':::' 时
      if (!content) continue;
      if (type === 'fence' && _info === 'yaml') { // 是代码块类型，并且是yaml代码
        yamlStr = content
      }
    }

    if (yamlStr) { // 正确解析出yaml字符串后
      const dataObj = yaml.safeLoad(yamlStr) // 将yaml字符串解析成js对象
      let dataList = []

      if (dataObj) { // 正确解析出数据对象
        dataList = Array.isArray(dataObj) ? dataObj : dataObj.list
      }

      if (dataList  && dataList.length) { // 有列表数据

        // 每行显示几个
        let row = Number(info.split(' ').pop())
        if (!row || row > 4 || row < 1) {
          row = 3 // 默认 3
        }

        let listDOM = ''
        if (type === CARD_LIST) { // 普通卡片列表
          listDOM = getCardListDOM(dataList, row)
        } else if (type === CARD_IMG_LIST) { // 卡片图片列表
          listDOM = getCardImgListDOM(dataList, row)
        }

        return `<div class="${type}Container"><div class="card-list">${ listDOM }</div>`
      }
    }
  } else { // 渲染':::' 结尾
    return '
'
  }
}

// 将数据解析成DOM结构 - 普通卡片列表
function getCardListDOM(dataList, row) {
  let listDOM = ''
  dataList.forEach(item => {
    listDOM += `
      <${item.link ? 'a href="' + item.link +'" target="_blank"' : 'span' } class="card-item ${ row ? 'row-' + row : '' }"
         style="${ item.bgColor ? 'background-color:' + item.bgColor + ';': '' }${ item.textColor ? 'color:' + item.textColor + ';': '' }"
      >
        ${ item.avatar ? '' : '' }

${item.name}

${item.desc}

      </${item.link ? 'a' : 'span'}>
    `
  })
  return listDOM
}

// 将数据解析成DOM结构 - 图文卡片列表
function getCardImgListDOM(dataList, row) {
  let listDOM = ''
  dataList.forEach(item => {
    listDOM += `

${item.name}

            ${item.desc ? `<p class="desc">${item.desc}</p>` : ''}

        ${item.avatar || item.author
          ? `

                ${item.avatar ? `<img src="${item.avatar}" class="no-zoom">` : ''}
                ${item.author ? `<span>${item.author}</span>` : ''}

`
          : ''
        }

    `
  })
  return listDOM
}<commit_msg>amend: amend container details defaultTitle<commit_after>const path = require('path')
const setFrontmatter = require('./node_utils/setFrontmatter')
const getSidebarData = require('./node_utils/getSidebarData')
const {createPage, deletePage} = require('./node_utils/handlePage')
const chalk = require('chalk') // 命令行打印美化
const yaml = require('js-yaml') // yaml转js
const log = console.log

// md容器名
const CARD_LIST = 'cardList'
const CARD_IMG_LIST = 'cardImgList'

// Theme API.
module.exports = (options, ctx) => {
  const { sourceDir, themeConfig, siteConfig } = ctx

  // 自动设置front matter
  setFrontmatter(sourceDir, themeConfig)

  // 自动生成结构化侧边栏
  const sidebar = themeConfig.sidebar
  if(sidebar === 'structuring' || sidebar && sidebar.mode === 'structuring') {
    const collapsable = themeConfig.sidebar.collapsable === false ? false : true
    const sidebarData = getSidebarData(sourceDir, collapsable)
    if(sidebarData) {
      themeConfig.sidebar = sidebarData
      log(chalk.blue('tip ') + chalk.green('add sidebar data. 侧边栏数据成功生成。'))
    } else {
      themeConfig.sidebar = 'auto'
      log(chalk.yellow('warning: fail to add sidebar data, switch to "auto". 未能添加侧边栏数据，将切换为“auto”。'))
    }
  }

  // 分类页
  if (themeConfig.category !== false) {
    createPage(sourceDir, 'categoriesPage')
  } else {
    deletePage(sourceDir, 'categoriesPage')
  }

  // 标签页
  if (themeConfig.tag !== false) {
    createPage(sourceDir, 'tagsPage')
  } else {
    deletePage(sourceDir, 'tagsPage')
  }

  // 归档页
  if (themeConfig.archive !== false) {
    createPage(sourceDir, 'archivesPage')
  } else {
    deletePage(sourceDir, 'archivesPage')
  }

  // resolve algolia
  const isAlgoliaSearch = (
    themeConfig.algolia
    || Object
        .keys(siteConfig.locales && themeConfig.locales || {})
        .some(base => themeConfig.locales[base].algolia)
  )

  const enableSmoothScroll = themeConfig.smoothScroll === true

  return {
    alias () {
      return {
        '@AlgoliaSearchBox': isAlgoliaSearch
          ? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue')
          : path.resolve(__dirname, 'noopModule.js')
      }
    },

    plugins: [
      ['@vuepress/active-header-links', options.activeHeaderLinks],
      '@vuepress/search',
      '@vuepress/plugin-nprogress',
      ['smooth-scroll', enableSmoothScroll],

      ['container', {
        type: 'tip',
        defaultTitle: {
          '/': 'TIP',
          '/zh/': '提示'
        }
      }],
      ['container', {
        type: 'warning',
        defaultTitle: {
          '/': 'WARNING',
          '/zh/': '注意'
        }
      }],
      ['container', {
        type: 'danger',
        defaultTitle: {
          '/': 'WARNING',
          '/zh/': '警告'
        }
      }],
      ['container', {
        type: 'details',
        before: info => `<details class="custom-block details">${info ? `${info}` : ''}\n`,
        after: () => '\n',
        defaultTitle: {
          '/': 'DETAILS',
          '/zh/': '点击查看'
        }
      }],
      // 卡片列表
      [
        'container',
        {
          type: CARD_LIST,
          render:  (tokens, idx) => {
            // tokens 是整个md文件的虚拟dom结构数组
            // idx 是tokens中':::' 所在的索引，而且是当前指定type的':::'，分别有开始和结束两次的idx
            // if (tokens[idx].nesting === 1) { // 开头的 ':::' 标记
            // } else { // 结束的 ':::' 标记
            // }
            // 注意：修改这里面的代码后需要在md文件保存一下才会重新执行渲染
            return renderCardList(tokens, idx, CARD_LIST)
          }
        },
      ],

      // 图文卡片列表
      [
        'container',
        {
          type: CARD_IMG_LIST,
          render:  (tokens, idx) => {
            return renderCardList(tokens, idx, CARD_IMG_LIST)
          }
        },
      ],

    ]
  }
}

// 渲染md容器的卡片列表
function renderCardList(tokens, idx, type) {
  const END_TYPE = `container_${type}_close`,
        _tokens$idx = tokens[idx],
        nesting = _tokens$idx.nesting,
        info = _tokens$idx.info;

  if (nesting === 1) { // 渲染开头的 ':::' 标记
    let yamlStr = '';

    for (let i = idx; i < tokens.length; i++) {
      let _tokens$i = tokens[i],
          type = _tokens$i.type,
          content = _tokens$i.content,
          _info = _tokens$i.info;
      if (type === END_TYPE) break; // 遇到结束的 ':::' 时
      if (!content) continue;
      if (type === 'fence' && _info === 'yaml') { // 是代码块类型，并且是yaml代码
        yamlStr = content
      }
    }

    if (yamlStr) { // 正确解析出yaml字符串后
      const dataObj = yaml.safeLoad(yamlStr) // 将yaml字符串解析成js对象
      let dataList = []

      if (dataObj) { // 正确解析出数据对象
        dataList = Array.isArray(dataObj) ? dataObj : dataObj.list
      }

      if (dataList  && dataList.length) { // 有列表数据

        // 每行显示几个
        let row = Number(info.split(' ').pop())
        if (!row || row > 4 || row < 1) {
          row = 3 // 默认 3
        }

        let listDOM = ''
        if (type === CARD_LIST) { // 普通卡片列表
          listDOM = getCardListDOM(dataList, row)
        } else if (type === CARD_IMG_LIST) { // 卡片图片列表
          listDOM = getCardImgListDOM(dataList, row)
        }

        return `<div class="${type}Container"><div class="card-list">${ listDOM }</div>`
      }
    }
  } else { // 渲染':::' 结尾
    return '
'
  }
}

// 将数据解析成DOM结构 - 普通卡片列表
function getCardListDOM(dataList, row) {
  let listDOM = ''
  dataList.forEach(item => {
    listDOM += `
      <${item.link ? 'a href="' + item.link +'" target="_blank"' : 'span' } class="card-item ${ row ? 'row-' + row : '' }"
         style="${ item.bgColor ? 'background-color:' + item.bgColor + ';': '' }${ item.textColor ? 'color:' + item.textColor + ';': '' }"
      >
        ${ item.avatar ? '' : '' }

${item.name}

${item.desc}

      </${item.link ? 'a' : 'span'}>
    `
  })
  return listDOM
}

// 将数据解析成DOM结构 - 图文卡片列表
function getCardImgListDOM(dataList, row) {
  let listDOM = ''
  dataList.forEach(item => {
    listDOM += `

${item.name}

            ${item.desc ? `<p class="desc">${item.desc}</p>` : ''}

        ${item.avatar || item.author
          ? `

                ${item.avatar ? `<img src="${item.avatar}" class="no-zoom">` : ''}
                ${item.author ? `<span>${item.author}</span>` : ''}

`
          : ''
        }

    `
  })
  return listDOM
}<|endoftext|>


<commit_before>var BINDING_RE = /\{\{(.+?)\}\}/

module.exports = {

    /**
     *  Parse a piece of text, return an array of tokens
     */
    parse: function (text) {
        if (!BINDING_RE.test(text)) return null
        var m, i, tokens = []
        do {
            m = text.match(BINDING_RE)
            if (!m) break
            i = m.index
            if (i > 0) tokens.push(text.slice(0, i))
            tokens.push({ key: m[1].trim() })
            text = text.slice(i + m[0].length)
        } while (true)
        if (text.length) tokens.push(text)
        return tokens
    }

}<commit_msg>simplify the while loop in text parser<commit_after>var BINDING_RE = /\{\{(.+?)\}\}/

module.exports = {

    /**
     *  Parse a piece of text, return an array of tokens
     */
    parse: function (text) {
        if (!BINDING_RE.test(text)) return null
        var m, i, tokens = []
        /* jshint boss: true */
        while (m = text.match(BINDING_RE)) {
            i = m.index
            if (i > 0) tokens.push(text.slice(0, i))
            tokens.push({ key: m[1].trim() })
            text = text.slice(i + m[0].length)
        }
        if (text.length) tokens.push(text)
        return tokens
    }

}<|endoftext|>


<commit_before>import { CharAnalyzer } from './char-analyzer.js';
import {
    TextPatch,
    SentenceTextPatch,
    WordTextPatch,
    LetterTextPatch,
} from './text-tagger-patches.js';
import { Counter } from './utils/counter.js';
import { merge } from './utils/merge.js';

/**
 * Convert HTML text to HTMLElement.
 * @private
 *
 * @param {string} text An HTML string.
 * @return {HTMLElement} The HTMLElement.
 */
function textToNode(text) {
    let parser = new DOMParser();
    let doc = parser.parseFromString(text, 'text/html');
    return doc.querySelector('html');
}

/**
 * Get a recursive list of text nodes in a Node.
 * @private
 *
 * @param {HTMLElement} node The parent to parse.
 * @return {Array} A recursive list of text nodes.
 */
function findAllTextNodes(node, options = {}) {
    let textNodes = [];
    let clone = Array.prototype.slice.call(node.childNodes, 0);
    clone.forEach((child) => {
        if (child.nodeType === Node.TEXT_NODE) {
            textNodes.push(child);
        } else if (child.nodeType === Node.ELEMENT_NODE &&
            !child.matches(options.excludeSelector)) {
            textNodes.push(...findAllTextNodes(child, options));
        }
    });
    return textNodes;
}

/**
 * Split a text node in single character chunks.
 * @private
 *
 * @param {Text} node The text node to split.
 * @return {Array} A list of chunks.
 */
function splitTextNode(node) {
    let text = node.textContent;
    let nodes = [];
    let last;
    for (let z = 0; z < text.length; z++) {
        let char = text[z];
        let nextChar = text[z + 1];
        if (nextChar && CharAnalyzer.isDiacritic(nextChar)) {
            char += nextChar;
            z++;
        }
        let textNode = document.createTextNode(char);
        nodes.push(textNode);
    }
    return nodes;
}

/**
 * Replace a text node with its single character chunks.
 * @private
 *
 * @param {Text} node The text node to replace.
 * @return {Array} A list of chunks.
 */
function replaceTextNode(node) {
    let nodes = splitTextNode(node);
    let parent = node.parentNode;
    let ref;
    nodes.forEach((child, index) => {
        if (index === 0) {
            parent.replaceChild(child, node);
            ref = child.nextSibling;
        } else {
            if (ref) {
                parent.insertBefore(child, ref);
            } else {
                parent.appendChild(child);
            }
        }
    });
    return nodes;
}

/**
 * Check if a text node is a valid letter.
 * @private
 *
 * @param {Text} textNode The text node to check.
 * @return {boolean}
 */
function isLetter(textNode) {
    let text = textNode.textContent;
    return !CharAnalyzer.isWhiteSpace(text) &&
        !CharAnalyzer.isPunctuation(text);
}

function isParent(node, parent) {
    while (node) {
        if (node === parent) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

function isLastBlockNode(node, options = {}) {
    let scope = node;
    if (!node.nextToken) {
        return true;
    }
    node = node.parentNode;
    while (node) {
        if (node.nodeType === Node.ELEMENT_NODE &&
            node.matches(options.blockSelector)) {
            if (isParent(scope.nextToken, node)) {
                return false;
            }
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

/**
 * Get a list of patches for the given node.
 * @private
 *
 * @param {HTMLElement} node The text node to analyze.
 * @return {Array} A list of TextPatch-es.
 */
function getPatches(node, options = {}) {
    let modes = options.modes;
    let textNodes = [];
    findAllTextNodes(node, options)
        .forEach((child) => {
            let children = replaceTextNode(child);
            textNodes.push(...children);
        });
    let last;
    textNodes.forEach((n, index) => {
        if (last) {
            last.nextToken = n;
        }
        n.indexToken = index;
        last = n;
    });
    let patches = [];
    if (modes.indexOf('sentence') !== -1) {
        let desc = new SentenceTextPatch(node);
        textNodes.forEach((child) => {
            if (!desc.start && isLetter(child)) {
                desc.setStart(child);
            }
            if (desc.start &&
                (CharAnalyzer.isStopPunctuation(child.textContent) ||
                isLastBlockNode(child, options) ||
                !child.nextToken)) {
                desc.setEnd(child);
                patches.push(desc);
                desc = new SentenceTextPatch(node);
            }
        });
    }
    if (modes.indexOf('word') !== -1) {
        let desc = new WordTextPatch(node);
        textNodes.forEach((child, index) => {
            let isLet = isLetter(child);
            let next = textNodes[index + 1];
            if (!desc.start && isLet) {
                desc.setStart(child);
            }
            if (desc.start &&
                (!next || !isLetter(next) || isLastBlockNode(child, options))) {
                desc.setEnd(child);
                patches.push(desc);
                desc = new WordTextPatch(node);
            }
        });
    }
    if (modes.indexOf('letter') !== -1) {
        textNodes.forEach((child) => {
            patches.push(new LetterTextPatch(node, child));
        });
    } else {
        if (modes.indexOf('space') !== -1) {
            textNodes.forEach((child) => {
                if (CharAnalyzer.isWhiteSpace(child.textContent)) {
                    patches.push(new LetterTextPatch(node, child));
                }
            });
        }
        if (modes.indexOf('puntuaction') !== -1) {
            textNodes.forEach((child) => {
                if (CharAnalyzer.isPunctuation(child.textContent)) {
                    patches.push(new LetterTextPatch(node, child));
                }
            });
        }
    }
    return patches;
}

/**
 * Tag a XML node content.
 * @private
 *
 * @param {HTMLElement} node The node to tag.
 * @return {string} The tagged XML content.
 */
function chunkNode(node, options = {}) {
    let counter = this.counter;
    let patches = getPatches(node, options).filter((patch) => patch.exec(options));
    if (options.setId) {
        patches.sort(TextPatch.sort).forEach((patch) => {
            let token = patch.wrapper;
            token.setAttribute(options.tokenIdAttr, options.id(counter));
            counter.increase();
        });
    }
    return node;
}

export class TextTagger {
    static get DEFAULTS() {
        return {
            setId: true,
            useClasses: false,
            modes: ['letter'],
            tokenIdAttr: 'data-token-id',
            tokenTag: 't:span',
            tokenClass: 'tagger--token',
            tokenLetter: 'tagger--letter',
            tokenWord: 'tagger--word',
            tokenSentence: 'tagger--sentence',
            puntuactionClass: 'tagger--token-puntuaction',
            sentenceStopClass: 'tagger--token-sentence-stop',
            whiteSpaceClass: 'tagger--token-whitespace',
            excludeSelector: 'head, title, meta, script, style, iframe, svg, .tagger--disable',
            blockSelector: 'p, li, ul, div, h1, h2, h3, h4, h5, h6',
            id: (index) => index,
        };
    }
    /**
     * A class for letter or words tagging in texts.
     * @class TextTagger
     *
     * @param {Object} options A set of options.
     * @property {Boolean} options.setId
     * Should set the data token id attribute to the token element.
     * @property {Boolean} options.useClasses
     * Should set the token class to the token element.
     * @property {String} options.mode The tag method ("letter" or "word").
     * @property {String} options.tokenTag The tag for the token element.
     * @property {String} options.tokenClass The class for the token element.
     * @property {String} options.puntuactionClass The class for the punctuation token element.
     * @property {String} options.sentenceStopClass
     * The class for the stop punctuation token element.
     * @property {String} options.whiteSpaceClass The class for the white space token element.
     * @property {String} options.excludeSelector A selector to ignore on tagging.
     */
    constructor(options = {}) {
        this.options = merge(TextTagger.DEFAULTS, options);
        this.counter = new Counter();
    }
    /**
     * Tag the text.
     *
     * @param {String} text The text to tag.
     * @param {Object} options Optional extra options.
     * @return {String} The tagged text.
     */
    tag(text, options = {}, getBody) {
        getBody = typeof getBody !== 'undefined' ?
            getBody :
            text.match(/<body[^>]*>/);
        options = merge(this.options, options);
        let n = textToNode(text);
        n = chunkNode.call(this, n, options);
        let html = '';
        let body = n;
        if (getBody) {
            let h = n.querySelector('head');
            let b = n.querySelector('body');
            if (h) {
                html += h.innerHTML;
            }
            if (b) {
                body = b;
            }
            html += body.innerHTML;
        } else {
            html = body.outerHTML;
        }
        return html;
    }
}

if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            let matches = (this.document || this.ownerDocument).querySelectorAll(s);
            let i = matches.length;
            // eslint-disable-next-line
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}
<commit_msg>fix: handle new line tags<commit_after>import { CharAnalyzer } from './char-analyzer.js';
import {
    TextPatch,
    SentenceTextPatch,
    WordTextPatch,
    LetterTextPatch,
} from './text-tagger-patches.js';
import { Counter } from './utils/counter.js';
import { merge } from './utils/merge.js';

/**
 * Convert HTML text to HTMLElement.
 * @private
 *
 * @param {string} text An HTML string.
 * @return {HTMLElement} The HTMLElement.
 */
function textToNode(text) {
    let parser = new DOMParser();
    let doc = parser.parseFromString(text, 'text/html');
    return doc.querySelector('html');
}

/**
 * Get a recursive list of text nodes in a Node.
 * @private
 *
 * @param {HTMLElement} node The parent to parse.
 * @return {Array} A recursive list of text nodes.
 */
function findAllTextNodes(node, options = {}) {
    let textNodes = [];
    let clone = Array.prototype.slice.call(node.childNodes, 0);
    clone.forEach((child) => {
        if (child.nodeType === Node.TEXT_NODE) {
            textNodes.push(child);
        } else if (child.nodeType === Node.ELEMENT_NODE &&
            !child.matches(options.excludeSelector)) {
            textNodes.push(...findAllTextNodes(child, options));
        }
    });
    return textNodes;
}

/**
 * Split a text node in single character chunks.
 * @private
 *
 * @param {Text} node The text node to split.
 * @return {Array} A list of chunks.
 */
function splitTextNode(node) {
    let text = node.textContent;
    let nodes = [];
    let last;
    for (let z = 0; z < text.length; z++) {
        let char = text[z];
        let nextChar = text[z + 1];
        if (nextChar && CharAnalyzer.isDiacritic(nextChar)) {
            char += nextChar;
            z++;
        }
        let textNode = document.createTextNode(char);
        nodes.push(textNode);
    }
    return nodes;
}

/**
 * Replace a text node with its single character chunks.
 * @private
 *
 * @param {Text} node The text node to replace.
 * @return {Array} A list of chunks.
 */
function replaceTextNode(node) {
    let nodes = splitTextNode(node);
    let parent = node.parentNode;
    let ref;
    nodes.forEach((child, index) => {
        if (index === 0) {
            parent.replaceChild(child, node);
            ref = child.nextSibling;
        } else {
            if (ref) {
                parent.insertBefore(child, ref);
            } else {
                parent.appendChild(child);
            }
        }
    });
    return nodes;
}

/**
 * Check if a text node is a valid letter.
 * @private
 *
 * @param {Text} textNode The text node to check.
 * @return {boolean}
 */
function isLetter(textNode) {
    let text = textNode.textContent;
    return !CharAnalyzer.isWhiteSpace(text) &&
        !CharAnalyzer.isPunctuation(text);
}

function isParent(node, parent) {
    while (node) {
        if (node === parent) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

function isLastBlockNode(node, options = {}) {
    let scope = node;
    if (!node.nextToken ||
        (node.nextSibling &&
        node.nextSibling.matches &&
        node.nextSibling.matches(options.newLineSelector))) {
        return true;
    }
    node = node.parentNode;
    while (node) {
        if (node.nodeType === Node.ELEMENT_NODE &&
            node.matches(options.blockSelector)) {
            if (isParent(scope.nextToken, node)) {
                return false;
            }
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

/**
 * Get a list of patches for the given node.
 * @private
 *
 * @param {HTMLElement} node The text node to analyze.
 * @return {Array} A list of TextPatch-es.
 */
function getPatches(node, options = {}) {
    let modes = options.modes;
    let textNodes = [];
    findAllTextNodes(node, options)
        .forEach((child) => {
            let children = replaceTextNode(child);
            textNodes.push(...children);
        });
    let last;
    textNodes.forEach((n, index) => {
        if (last) {
            last.nextToken = n;
        }
        n.indexToken = index;
        last = n;
    });
    let patches = [];
    if (modes.indexOf('sentence') !== -1) {
        let desc = new SentenceTextPatch(node);
        textNodes.forEach((child) => {
            if (!desc.start && isLetter(child)) {
                desc.setStart(child);
            }
            if (desc.start &&
                (CharAnalyzer.isStopPunctuation(child.textContent) ||
                isLastBlockNode(child, options) ||
                !child.nextToken)) {
                desc.setEnd(child);
                patches.push(desc);
                desc = new SentenceTextPatch(node);
            }
        });
    }
    if (modes.indexOf('word') !== -1) {
        let desc = new WordTextPatch(node);
        textNodes.forEach((child, index) => {
            let isLet = isLetter(child);
            let next = textNodes[index + 1];
            if (!desc.start && isLet) {
                desc.setStart(child);
            }
            if (desc.start &&
                (!next || !isLetter(next) || isLastBlockNode(child, options))) {
                desc.setEnd(child);
                patches.push(desc);
                desc = new WordTextPatch(node);
            }
        });
    }
    if (modes.indexOf('letter') !== -1) {
        textNodes.forEach((child) => {
            patches.push(new LetterTextPatch(node, child));
        });
    } else {
        if (modes.indexOf('space') !== -1) {
            textNodes.forEach((child) => {
                if (CharAnalyzer.isWhiteSpace(child.textContent)) {
                    patches.push(new LetterTextPatch(node, child));
                }
            });
        }
        if (modes.indexOf('puntuaction') !== -1) {
            textNodes.forEach((child) => {
                if (CharAnalyzer.isPunctuation(child.textContent)) {
                    patches.push(new LetterTextPatch(node, child));
                }
            });
        }
    }
    return patches;
}

/**
 * Tag a XML node content.
 * @private
 *
 * @param {HTMLElement} node The node to tag.
 * @return {string} The tagged XML content.
 */
function chunkNode(node, options = {}) {
    let counter = this.counter;
    let patches = getPatches(node, options).filter((patch) => patch.exec(options));
    if (options.setId) {
        patches.sort(TextPatch.sort).forEach((patch) => {
            let token = patch.wrapper;
            token.setAttribute(options.tokenIdAttr, options.id(counter));
            counter.increase();
        });
    }
    return node;
}

export class TextTagger {
    static get DEFAULTS() {
        return {
            setId: true,
            useClasses: false,
            modes: ['letter'],
            tokenIdAttr: 'data-token-id',
            tokenTag: 't:span',
            tokenClass: 'tagger--token',
            tokenLetter: 'tagger--letter',
            tokenWord: 'tagger--word',
            tokenSentence: 'tagger--sentence',
            puntuactionClass: 'tagger--token-puntuaction',
            sentenceStopClass: 'tagger--token-sentence-stop',
            whiteSpaceClass: 'tagger--token-whitespace',
            excludeSelector: 'head, title, meta, script, style, iframe, svg, .tagger--disable',
            blockSelector: 'p, li, ul, div, h1, h2, h3, h4, h5, h6',
            newLineSelector: 'br',
            id: (index) => index,
        };
    }
    /**
     * A class for letter or words tagging in texts.
     * @class TextTagger
     *
     * @param {Object} options A set of options.
     * @property {Boolean} options.setId
     * Should set the data token id attribute to the token element.
     * @property {Boolean} options.useClasses
     * Should set the token class to the token element.
     * @property {String} options.mode The tag method ("letter" or "word").
     * @property {String} options.tokenTag The tag for the token element.
     * @property {String} options.tokenClass The class for the token element.
     * @property {String} options.puntuactionClass The class for the punctuation token element.
     * @property {String} options.sentenceStopClass
     * The class for the stop punctuation token element.
     * @property {String} options.whiteSpaceClass The class for the white space token element.
     * @property {String} options.excludeSelector A selector to ignore on tagging.
     */
    constructor(options = {}) {
        this.options = merge(TextTagger.DEFAULTS, options);
        this.counter = new Counter();
    }
    /**
     * Tag the text.
     *
     * @param {String} text The text to tag.
     * @param {Object} options Optional extra options.
     * @return {String} The tagged text.
     */
    tag(text, options = {}, getBody) {
        getBody = typeof getBody !== 'undefined' ?
            getBody :
            text.match(/<body[^>]*>/);
        options = merge(this.options, options);
        let n = textToNode(text);
        n = chunkNode.call(this, n, options);
        let html = '';
        let body = n;
        if (getBody) {
            let h = n.querySelector('head');
            let b = n.querySelector('body');
            if (h) {
                html += h.innerHTML;
            }
            if (b) {
                body = b;
            }
            html += body.innerHTML;
        } else {
            html = body.outerHTML;
        }
        return html;
    }
}

if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            let matches = (this.document || this.ownerDocument).querySelectorAll(s);
            let i = matches.length;
            // eslint-disable-next-line
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}
<|endoftext|>