<commit_before>

function cloneSelectedLayers() {
    var clones=[];
    for(var i=0;i<selection.count();i++) {
        var layer=selection.objectAtIndex(i);
        clones.push(layer.duplicate());
        layer.setIsSelected(false);
    }
    return clones;
}

function deselectAllLayers() {
    for (var i=0; i<selection.count(); i++) {
        selection[i].setIsSelected(false);
    }
}

function duplicate(direction,paddings) {
    if(selection.count()<1) return;

    var direction = direction || "right";
    var paddings = paddings || 10;

    var bounds=getSelectionBounds();
    var clones = cloneSelectedLayers();

    // deselectAllLayers();

    var w=bounds.maxX-bounds.minX;
    var h=bounds.maxY-bounds.minY;

    var paddings=10;

    for(var i=0;i<clones.length;i++) {
        var layer=clones[i];
        var rect=clones[i].frame();

        [layer select:true byExpandingSelection:true ];
        // layer.setIsSelected(true);

        if(direction=="above") {
            rect.subtractY(h+paddings);
        } else if(direction=="below") {
            rect.addY(h+paddings);
        } else if(direction=="right") {
            rect.addX(w+paddings);
        } else if(direction=="left") {
            rect.subtractX(w+paddings);
        }

    }

    // doc.currentView().refresh();
}

function getSelectionBounds() {
    var minX,minY,maxX,maxY;
    minX=minY=10000000;
    maxX=maxY=-10000000;

    for(var i=0;i<selection.count();i++) {
        var rect=selection.objectAtIndex(i).frame();
        if(rect.minX()<minX) minX=rect.minX();
        if(rect.minY()<minY) minY=rect.minY();
        if(rect.maxX()>maxX) maxX=rect.maxX();
        if(rect.maxY()>maxY) maxY=rect.maxY();

    }

    return {
        minX: minX,
        minY: minY,
        maxX: maxX,
        maxY: maxY
    };
}
<commit_msg>Switched from cloning layers using MSLayer:duplicate to MSCanvasActions:duplicate method. No more weird bugs anymore! :)<commit_after>

function duplicate(direction,showOptionsAlert) {
    if(selectionContainsArtboards()) {
        doc.displayMessage("🚫🚫🚫 Can't duplicate Artboards! Please remove all the artboards from the selection. 🚫🚫🚫");
        return;
    }

    if(selection.count()<1) {
        doc.displayMessage("🚫🚫🚫 Can't duplicate an emptiness! Please select some layers and try again. 🚫🚫🚫");
        return;
    }

    var showOptionsAlert=showOptionsAlert || false;

    var padding = 10;
    var times = 1;

    for(var n=0;n<times;n++) {

        var action=doc.actionsController().actionWithName("MSCanvasActions");
        action.duplicate(nil);

        var sel=doc.findSelectedLayers();
        log(sel);

        var size=getSelectionSize(sel);

        for(var i=0;i<sel.count();i++) {
            var layer=sel.objectAtIndex(i);
            var rect=layer.frame();

            if(direction=="above") {
                rect.subtractY(size.h+padding);
            } else if(direction=="below") {
                rect.addY(size.h+padding);
            } else if(direction=="right") {
                rect.addX(size.w+padding);
            } else if(direction=="left") {
                rect.subtractX(size.w+padding);
            }
        }
    }
}

function selectionContainsArtboards() {
    for(var i=0;i<selection.count();i++) {
        var layer=selection.objectAtIndex(i);
        if(layer.className()=="MSArtboardGroup") return true;
    }

    return false;
}

function getSelectionSize(sel) {
    var minX,minY,maxX,maxY;
    minX=minY=Number.MAX_VALUE;
    // maxX=maxY=Number.MIN_VALUE; // Have no idea why it doesn't work.. :(
    maxX=maxY=-0xFFFFFFFF;

    for(var i=0;i<sel.count();i++) {
        var rect=sel.objectAtIndex(i).frame();

        minX=Math.min(minX,rect.minX());
        minY=Math.min(minY,rect.minY());
        maxX=Math.max(maxX,rect.maxX());
        maxY=Math.max(maxY,rect.maxY());
    }

    return {
        w: maxX-minX,
        h: maxY-minY
    };
}
<|endoftext|>


<commit_before>app.controller('UsersPasswordCtrl', ['$state', '$scope', 'users', '$timeout', 'SweetAlert', 'toaster', '$http', function ($state, $scope, users, $timeout, SweetAlert, toaster) {
     //Init input addForm variable
     //create users
     $scope.process = false;
 if ($scope.dataUser.level == 0 ) {
         $state.go("app.dashboard")
     }
     $scope.master = $scope.myModel;
     $scope.form = {

         submit: function (form) {
             var firstError = null;
             if (form.$invalid) {

                 var field = null, firstError = null;
                 for (field in form) {
                     if (field[0] != '$') {
                         if (firstError === null && !form[field].$valid) {
                             firstError = form[field].$name;
                         }

                         if (form[field].$pristine) {
                             form[field].$dirty = true;
                         }
                     }
                 }
                 angular.element('.ng-invalid[name=' + firstError + ']').focus();
                 SweetAlert.swal("The form cannot be submitted because it contains validation errors!", "Errors are marked with a red, dashed border!", "error");
                 return;

             } else {
                 SweetAlert.swal("Good job!", "Your form is ready to be submitted!", "success");
                 //your code for submit
             }

         },
         reset: function (form) {

             $scope.myModel = angular.copy($scope.master);
             form.$setPristine(true);
         }

     };
     $scope.closeAlert = function (index) {
         $scope.alerts.splice(index, 1);
     };

     $scope.submitData = function (isBack) {
         $scope.alerts = [];
         //Set process status
         $scope.process = true;
         //Close Alert

         //Check validation status
         if ($scope.Form.$valid) {
             //run Ajax
             users.updatepassword($scope.myModel)
                 .success(function (data) {
                     if (data.updated == true) {
                         //If back to list after submitting
                         if (isBack == true) {
                             $state.go('app.users');
                             $scope.toaster = {
                                 type: 'success',
                                 title: 'Sukses',
                                 text: 'Simpan Data Berhasil!'
                             };
                             toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                         } else {
                             $scope.clearInput();
                             $scope.sup();
                             $scope.alerts.push({
                                 type: 'success',
                                 msg: 'Simpan Data Berhasil!'
                             });
                             $scope.process = false;
                             $scope.toaster = {
                                 type: 'success',
                                 title: 'Sukses',
                                 text: 'Simpan Data Berhasil!'
                             };
                             toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                         }
                         //Clear Input
                     }

                 })
                 .error(function (data, status) {
                     // unauthorized
                     if (status === 401) {
                         //redirect to login
                         $scope.redirect();
                     }
                     $scope.sup();
                     // Stop Loading
                     $scope.process = false;
                     $scope.alerts.push({
                         type: 'danger',
                         msg: data.validation
                     });
                     $scope.toaster = {
                         type: 'error',
                         title: 'Gagal',
                         text: 'Simpan Data Gagal!'
                     };
                     toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                 });
         }
     };

}]);<commit_msg>password<commit_after>app.controller('UsersPasswordCtrl', ['$state', '$scope', 'users', '$timeout', 'SweetAlert', 'toaster', '$http', function ($state, $scope, users, $timeout, SweetAlert, toaster) {
     //Init input addForm variable
     //create users
     $scope.process = false;
 if ($scope.dataUser.level == 0 ) {
         $state.go("app.dashboard")
     }
     $scope.master = $scope.myModel;
     $scope.form = {

         submit: function (form) {
             var firstError = null;
             if (form.$invalid) {

                 var field = null, firstError = null;
                 for (field in form) {
                     if (field[0] != '$') {
                         if (firstError === null && !form[field].$valid) {
                             firstError = form[field].$name;
                         }

                         if (form[field].$pristine) {
                             form[field].$dirty = true;
                         }
                     }
                 }
                 angular.element('.ng-invalid[name=' + firstError + ']').focus();
                 SweetAlert.swal("The form cannot be submitted because it contains validation errors!", "Errors are marked with a red, dashed border!", "error");
                 return;

             } else {
                 SweetAlert.swal("Good job!", "Your form is ready to be submitted!", "success");
                 //your code for submit
             }

         },
         reset: function (form) {

             $scope.myModel = angular.copy($scope.master);
             form.$setPristine(true);
         }

     };
     $scope.closeAlert = function (index) {
         $scope.alerts.splice(index, 1);
     };

     $scope.submitData = function (isBack) {
         $scope.alerts = [];
         //Set process status
         $scope.process = true;
         //Close Alert

         //Check validation status
         if ($scope.Form.$valid) {
             //run Ajax
             users.updatepassword($scope.myModel)
                 .success(function (data) {
                     if (data.updated == true) {
                         //If back to list after submitting
                         if (isBack == true) {
                             $state.go('app.users');
                             $scope.toaster = {
                                 type: 'success',
                                 title: 'Sukses',
                                 text: 'Simpan Data Berhasil!'
                             };
                             toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                         } else {
                             $scope.clearInput();
                             $scope.sup();
                             $scope.alerts.push({
                                 type: 'success',
                                 msg: 'Simpan Data Berhasil!'
                             });
                             $scope.process = false;
                             $scope.toaster = {
                                 type: 'success',
                                 title: 'Sukses',
                                 text: 'Simpan Data Berhasil!'
                             };
                             toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                         }
                         //Clear Input
                     }
                    else{
                        $scope.process = false;
                            $scope.toaster = {
                                type: 'error',
                                title: 'CekData',
                                text: data.result.message
                            };
                            toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                    }

                 })
                 .error(function (data, status) {
                     // unauthorized
                     if (status === 401) {
                         //redirect to login
                         $scope.redirect();
                     }
                     $scope.sup();
                     // Stop Loading
                     $scope.process = false;
                     $scope.alerts.push({
                         type: 'danger',
                         msg: data.validation
                     });
                     $scope.toaster = {
                         type: 'error',
                         title: 'Gagal',
                         text: 'Simpan Data Gagal!'
                     };
                     toaster.pop($scope.toaster.type, $scope.toaster.title, $scope.toaster.text);
                 });
         }
     };

}]);<|endoftext|>


<commit_before>/*globals angular:false*/
(function (angular) {
  'use strict';

  var app;
  app = angular.module('dacos');
  app.config(function ($routeProvider) {
    $routeProvider.when('/matriculas/:enrollmentCode/disciplinas', {'templateUrl' : 'enrollment-requirement/list.html'});
    $routeProvider.when('/matriculas/:enrollmentCode/disciplinas/:requirementCode', {'templateUrl' : 'enrollment-requirement/details.html'});

    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas', {'templateUrl' : 'enrollment-requirement/manage-list.html'});
    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas/criar', {'templateUrl' : 'enrollment-requirement/manage-create.html'});
    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas/:requirementCode', {'templateUrl' : 'enrollment-requirement/manage-details.html'});
    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas/:requirementCode/editar', {'templateUrl' : 'enrollment-requirement/manage-update.html'});
  });

  app.controller('EnrollmentRequirementListController', function ($routeParams, $controller, EnrollmentRequirement) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.requirements = EnrollmentRequirement.query($routeParams);
  });

  app.controller('EnrollmentRequirementDetailsController', function ($routeParams, $controller, EnrollmentRequirement) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.requirement = EnrollmentRequirement.get($routeParams);
  });

  app.controller('EnrollmentRequirementDeleteController', function ($routeParams, $controller, $route) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.remove = function (enrollmentRequirement) {
      var params;
      params = angular.copy($routeParams);
      params.enrollmentCode = this.enrollment.year + '-' + this.enrollment.period;
      params.requirementCode = enrollmentRequirement.discipline + '-' + enrollmentRequirement.offering;
      enrollmentRequirement.$delete(params, $route.reload);
    };
  });

  app.controller('EnrollmentRequirementCreateController', function ($scope, $routeParams, $controller, $location, Discipline, Offering, EnrollmentRequirement) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.requirement = new EnrollmentRequirement($routeParams);

    $scope.disciplines = Discipline.query($routeParams);

    $scope.selectDisciplineChanged = function (discipline) {
      var params;
      params = angular.copy($routeParams);
      params.disciplineCode = discipline;
      $scope.offerings = Offering.query(params);
    };

    this.save = function () {
      this.requirement.$save($routeParams, $location.parent(1));
    };
  });

  app.controller('EnrollmentRequirementUpdateController', function ($scope, $routeParams, $controller, $location, Discipline, Offering) {
    angular.extend(this, $controller('EnrollmentRequirementDetailsController'));

    $scope.disciplines = Discipline.query($routeParams);

    $scope.selectDisciplineChanged = function (discipline) {
      var params;
      params = angular.copy($routeParams);
      params.disciplineCode = discipline;
      $scope.offerings = Offering.query(params);
    }.bind(this);

    this.save = function () {
      this.requirement.$update($routeParams, $location.parent(2));
    }.bind(this);
  });
})(angular);<commit_msg>Fix enrollment<commit_after>/*globals angular:false*/
(function (angular) {
  'use strict';

  var app;
  app = angular.module('dacos');
  app.config(function ($routeProvider) {
    $routeProvider.when('/matriculas/:enrollmentCode/disciplinas', {'templateUrl' : 'enrollment-requirement/list.html'});
    $routeProvider.when('/matriculas/:enrollmentCode/disciplinas/:requirementCode', {'templateUrl' : 'enrollment-requirement/details.html'});

    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas', {'templateUrl' : 'enrollment-requirement/manage-list.html'});
    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas/criar', {'templateUrl' : 'enrollment-requirement/manage-create.html'});
    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas/:requirementCode', {'templateUrl' : 'enrollment-requirement/manage-details.html'});
    $routeProvider.when('/gerenciar-matriculas/:enrollmentCode/disciplinas/:requirementCode/editar', {'templateUrl' : 'enrollment-requirement/manage-update.html'});
  });

  app.controller('EnrollmentRequirementListController', function ($routeParams, $controller, EnrollmentRequirement) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.requirements = EnrollmentRequirement.query($routeParams);
  });

  app.controller('EnrollmentRequirementDetailsController', function ($routeParams, $controller, EnrollmentRequirement) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.requirement = EnrollmentRequirement.get($routeParams);
  });

  app.controller('EnrollmentRequirementDeleteController', function ($routeParams, $controller, $route) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.remove = function (enrollmentRequirement) {
      var params;
      params = angular.copy($routeParams);
      params.enrollmentCode = this.enrollment.year + '-' + this.enrollment.period;
      params.requirementCode = enrollmentRequirement.discipline + '-' + enrollmentRequirement.offering;
      enrollmentRequirement.$delete(params, $route.reload);
    };
  });

  app.controller('EnrollmentRequirementCreateController', function ($scope, $routeParams, $controller, $location, Discipline, Offering, EnrollmentRequirement) {
    angular.extend(this, $controller('EnrollmentDetailsController'));
    this.requirement = new EnrollmentRequirement($routeParams);

    $scope.disciplines = Discipline.query($routeParams);

    $scope.selectDisciplineChanged = function (discipline) {
      var params;
      params = angular.copy($routeParams);
      params.disciplineCode = discipline;
      $scope.offerings = Offering.query(params);
    };

    this.save = function () {
      this.requirement.$save($routeParams, $location.parent(1));
    };
  });

  app.controller('EnrollmentRequirementUpdateController', function ($scope, $routeParams, $controller, $location, Discipline, Offering) {
    angular.extend(this, $controller('EnrollmentRequirementDetailsController'));

    $scope.disciplines = Discipline.query($routeParams);

    $scope.selectDisciplineChanged = function (discipline) {
      var params;
      params = angular.copy($routeParams);
      params.disciplineCode = discipline;
      $scope.offerings = Offering.query(params);
    }.bind(this);

    this.requirement.$promise.then(function() {
      $scope.selectDisciplineChanged(this.requirement.discipline)
    }.bind(this));

    this.save = function () {
      this.requirement.$update($routeParams, $location.parent(2));
    }.bind(this);
  });
})(angular);<|endoftext|>


<commit_before>var p = Ent.Project.create('bolt-kernel', 'external');
p.setVersion(1, 1, 0);
p.setConfig({
    command: ["make"],
    dist: "gen",
    distInclude: "**/*"
});

<commit_msg>removed dent and intellij<commit_after><|endoftext|>


<commit_before>if (document.getElementById("speedy-delete") !== null) {
	var path = mw.config.get('wgArticlePath');
	var node = document.createElement("span");
	node.id = "delete-requester";
	node.style = "margin-left: 5px;";
	node.innerHTML = '提刪者：未取得';
	document.getElementsByClassName("mw-indicators mw-body-content")[0].appendChild(node);
	$.ajax({
		type: "GET",
		url: "https://zh.wikipedia.org/w/api.php",
		data: {
			action: "query",
			format: "json",
			prop: "revisions",
			titles: mw.config.get('wgPageName'),
			rvprop: "user|tags",
			rvlimit: "50"
		},
		success: function success(data) {
			var message = "找不到提刪者";
			var path = mw.config.get('wgArticlePath');
			for (var id in data.query.pages) {
				var page = data.query.pages[id];
				var requester = [];
				for (var i = 0; i < page.revisions.length; i++) {
					if (page.revisions[i].tags.indexOf("添加刪除模板") !== -1) {
						var user = page.revisions[i].user;
						requester.push('' + user + '');
					}
				}
				if (requester.length != 0) {
					message = requester.join("、");
				}
			}
			delrequester.innerHTML = message;
		},
		error: function error(e) {
			delrequester.innerHTML = "抓取錯誤";
		}
	});
}
if (mw.config.get('wgNamespaceNumber') !== -1 && (mw.config.get('wgAction') === "view" || mw.config.get('wgAction') === "edit")) {
	var path = mw.config.get('wgArticlePath');
	var node = document.createElement("span");
	node.id = "delete-log";
	node.style = "margin-left: 5px;";
	node.innerHTML = '刪除紀錄：未取得';
	document.getElementsByClassName("mw-indicators mw-body-content")[0].appendChild(node);
	$.ajax({
		type: "GET",
		url: "https://zh.wikipedia.org/w/api.php",
		data: {
			action: "query",
			format: "json",
			list: "logevents",
			leprop: "comment|type",
			letype: "delete",
			letitle: mw.config.get('wgPageName')
		},
		success: function success(data) {
			var message = "沒有";
			var path = mw.config.get('wgArticlePath');
			var log = [];
			for (var i = 0; i < data.query.logevents.length; i++) {
				var comment = data.query.logevents[i].comment;
				if (data.query.logevents[i].action === "restore") {
					comment = "還原";
				} else if (data.query.logevents[i].action === "revision") {
					continue;
				} else if (comment === "") {
					comment = "空";
				} else if (comment.match("删除以便移动") !== null) {
					continue;
				} else {
					comment = comment.replace(/.*\[\[:?(?:WP|Wikipedia)\:CSD\#([^|\]]+).*/g, "$1");
					comment = comment.replace(/.*?(Wikipedia:頁面存廢討論\/記錄\/\d{4}\/\d{2}\/\d{2}).*/g, '存廢');
					comment = comment.replace(/根據投票結果刪除.*/, "存廢");
					comment = comment.replace("列入[[WP:CV|侵权验证页面]]超过七日", "侵權");
					comment = comment.replace(/侵犯版权.*/, "侵權");
					comment = comment.replace(/侵犯版權.*/, "侵權");
					comment = comment.replace(/存廢討論通過.*/, "存廢");
					comment = comment.replace(/.*\[\[WP:CV.*/, "侵權");
					comment = comment.replace(/^内容为：.+/, "空");
					comment = comment.replace(/^content was.+/, "空");
					comment = comment.replace(/^make space.*/, "G8");
					comment = comment.replace(/^大量删除\[\[Special:Contributions\/.+/, "批刪");
					comment = comment.replace(/.*?([AGOF]\d{1,2}).*/i, "$1");
				}
				log.push(comment);
			}
			if (log.length != 0) {
				message = log.join("、");
			}
			dellog.innerHTML = message;
		},
		error: function error(e) {
			dellog.innerHTML = "抓取錯誤";
		}
	});
}
<commit_msg>delete-status add file<commit_after>if (document.getElementById("speedy-delete") !== null) {
	var path = mw.config.get('wgArticlePath');
	var node = document.createElement("span");
	node.id = "delete-requester";
	node.style = "margin-left: 5px;";
	node.innerHTML = '提刪者：未取得';
	document.getElementsByClassName("mw-indicators mw-body-content")[0].appendChild(node);
	$.ajax({
		type: "GET",
		url: "https://zh.wikipedia.org/w/api.php",
		data: {
			action: "query",
			format: "json",
			prop: "revisions",
			titles: mw.config.get('wgPageName'),
			rvprop: "user|tags",
			rvlimit: "50"
		},
		success: function success(data) {
			var message = "找不到提刪者";
			var path = mw.config.get('wgArticlePath');
			for (var id in data.query.pages) {
				var page = data.query.pages[id];
				var requester = [];
				for (var i = 0; i < page.revisions.length; i++) {
					if (page.revisions[i].tags.indexOf("添加刪除模板") !== -1) {
						var user = page.revisions[i].user;
						requester.push('' + user + '');
					}
				}
				if (requester.length != 0) {
					message = requester.join("、");
				}
			}
			delrequester.innerHTML = message;
		},
		error: function error(e) {
			delrequester.innerHTML = "抓取錯誤";
		}
	});
}
if (mw.config.get('wgNamespaceNumber') !== -1 && (mw.config.get('wgAction') === "view" || mw.config.get('wgAction') === "edit")) {
	var path = mw.config.get('wgArticlePath');
	var node = document.createElement("span");
	node.id = "delete-log";
	node.style = "margin-left: 5px;";
	node.innerHTML = '刪除紀錄：未取得';
	document.getElementsByClassName("mw-indicators mw-body-content")[0].appendChild(node);
	$.ajax({
		type: "GET",
		url: "https://zh.wikipedia.org/w/api.php",
		data: {
			action: "query",
			format: "json",
			list: "logevents",
			leprop: "comment|type",
			letype: "delete",
			letitle: mw.config.get('wgPageName')
		},
		success: function success(data) {
			var message = "沒有";
			var path = mw.config.get('wgArticlePath');
			var log = [];
			for (var i = 0; i < data.query.logevents.length; i++) {
				var comment = data.query.logevents[i].comment;
				if (data.query.logevents[i].action === "restore") {
					comment = "還原";
				} else if (data.query.logevents[i].action === "revision") {
					continue;
				} else if (comment === "") {
					comment = "空";
				} else if (comment.match("删除以便移动") !== null) {
					continue;
				} else {
					comment = comment.replace(/.*\[\[:?(?:WP|Wikipedia)\:CSD\#([^|\]]+).*/g, "$1");
					comment = comment.replace(/.*?(Wikipedia:(頁面|檔案)存廢討論\/記錄\/\d{4}\/\d{2}\/\d{2}).*/g, '存廢');
					comment = comment.replace(/根據投票結果刪除.*/, "存廢");
					comment = comment.replace("列入[[WP:CV|侵权验证页面]]超过七日", "侵權");
					comment = comment.replace(/侵犯版权.*/, "侵權");
					comment = comment.replace(/侵犯版權.*/, "侵權");
					comment = comment.replace(/存廢討論通過.*/, "存廢");
					comment = comment.replace(/.*\[\[WP:CV.*/, "侵權");
					comment = comment.replace(/^内容为：.+/, "空");
					comment = comment.replace(/^content was.+/, "空");
					comment = comment.replace(/^make space.*/, "G8");
					comment = comment.replace(/^大量删除\[\[Special:Contributions\/.+/, "批刪");
					comment = comment.replace(/.*?([AGOF]\d{1,2}).*/i, "$1");
				}
				log.push(comment);
			}
			if (log.length != 0) {
				message = log.join("、");
			}
			dellog.innerHTML = message;
		},
		error: function error(e) {
			dellog.innerHTML = "抓取錯誤";
		}
	});
}
<|endoftext|>


<commit_before><commit_msg>remove ignore file<commit_after><|endoftext|>


<commit_before>const PRIORITY_EVENT_TITLE_PREFIXES = ['-', '1.', '2.', '3.', '4.', '5.', null];
/* const */ DEBUG = false;

function dispatchEvent(obj, event) {
    var evt = new Event(
        event,
        {
            target: obj,
            bubbles: true,
        }
    );
    obj.dispatchEvent(evt);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function insertButtons(referenceNode, onclickAction, where, alternateReferenceNode) {
    var insertTarget = referenceNode;
    var buttonLabels = [
        // buttonLabel, eventTitlePrefix, buttonClassNames.
        ['-',       '-',  ['jfk-button', 'jfk-button-standard']],
        ['1',       '1.', ['jfk-button', 'jfk-button-standard']],
        ['2',       '2.', ['jfk-button', 'jfk-button-standard']],
        ['3',       '3.', ['jfk-button', 'jfk-button-standard']],
        ['4',       '4.', ['jfk-button', 'jfk-button-standard']],
        ['5',       '5.', ['jfk-button', 'jfk-button-standard']],
        ['X',       null, ['jfk-button', 'jfk-button-standard']],
        ['DONE',    '✓',  ['jfk-button', 'jfk-button-default' ]],
        ['NOPE',    '✗',  ['jfk-button', 'jfk-button-standard']],
        ['OKAY',    '▣',  ['jfk-button', 'jfk-button-standard']],
        ['AWESOME', 'ツ', ['jfk-button', 'jfk-button-standard']],
    ];
    for (var i in buttonLabels) {
        var buttonLabel = buttonLabels[i][0];
        DEBUG && console.log('buttonLabel:', buttonLabel);

        var eventTitlePrefix = buttonLabels[i][1];
        DEBUG && console.log('eventTitlePrefix:', eventTitlePrefix);

        var buttonClassNames = buttonLabels[i][2];
        DEBUG && console.log('buttonClassNames:', buttonClassNames);

        var button = document.createElement('button');
        button.innerText = buttonLabel;
        button.classList.add(...buttonClassNames);

        (function(myButton, myEventTitlePrefix) {
            myButton.onclick = function() {
                DEBUG && console.log('myButton.onclick');
                onclickAction(myButton, myEventTitlePrefix);
            };
        }(button, eventTitlePrefix));

        DEBUG && console.log('inserting button', button, where, insertTarget);
        if (where === 'after') {
            insertAfter(button, insertTarget);
            insertTarget = button;
        } else if (where === 'inside') {
            if (alternateReferenceNode && !PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
                alternateReferenceNode.appendChild(button);
            } else {
                referenceNode.appendChild(button);
            }
        }
    }
}

function clickButton(clickedData) {
    DEBUG && console.group('clickButton');
    DEBUG && console.log('clickedData:', clickedData);
    DEBUG && console.log('button clicked');

    // Today.
    var date = new Date();
    var monthName = date.toLocaleString('en-us', { 'month': 'short' });
    var todayFormattedDate = monthName + ' ' + date.getDate() + ', ' + date.getFullYear();

    // Event date.
    var startDateInput = document.querySelector('[aria-label="Start date"]');
    var endDateInput = document.querySelector('[aria-label="End date"]');
    var eventDate = startDateInput.value;
    if (endDateInput.value !== startDateInput.value) {
        eventDate += ' - ' + endDateInput.value;
    }
    DEBUG && console.log('eventDate:', eventDate);

    var eventTitle = document.querySelector('[aria-label="Title"]');
    var calendarEventTitle = eventTitle.value;
    DEBUG && console.log('before calendarEventTitle:', calendarEventTitle);

    // Remove leading number (e.g. "1. " in "1. My Calendar Event").
    calendarEventTitle = calendarEventTitle.replace(/^\d+\. /, '');
    // Remove leading ! character.
    calendarEventTitle = calendarEventTitle.replace(/^! /, '');
    // Remove leading - character.
    calendarEventTitle = calendarEventTitle.replace(/^- /, '');
    // Remove leading ~ character.
    calendarEventTitle = calendarEventTitle.replace(/^~ /, '');

    var buttonLabel = clickedData.button.innerText;
    DEBUG && console.log('buttonLabel:', buttonLabel);
    if (buttonLabel === 'DONE') {
        // Remove leading "Tentative: ".
        calendarEventTitle = calendarEventTitle.replace(/^Tentative: /, '');
    }

    DEBUG && console.log(' after calendarEventTitle:', calendarEventTitle);

    // "✓ My Event; Dec 31, 2015; event date: Jan 1, 2016"
    // "1. My Event"
    // "My Event"
    var eventTitlePrefix = clickedData['eventTitlePrefix'];
    var newCalendarEventTitle = calendarEventTitle;
    if (eventTitlePrefix !== null) {
        newCalendarEventTitle = eventTitlePrefix + ' ' + calendarEventTitle;
        if (! PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
            newCalendarEventTitle += ';' +
                ' ' + todayFormattedDate + ';' +
                ' event date: ' + eventDate;
        }
    }
    DEBUG && console.log('newCalendarEventTitle:', newCalendarEventTitle);
    eventTitle.value = newCalendarEventTitle;

    setTimeout(function() {
        dispatchEvent(eventTitle, 'input');
    }, 200);

    var saveButton = document.querySelector('[aria-label="Save"]');
    setTimeout(function() {
        saveButton.click();
    }, 500);

    DEBUG && console.groupEnd();
}

var buttonClickedData;
(function(){
    var pathname = window.location.pathname;
    setInterval(function() {
        if (pathname !== window.location.pathname) {
            DEBUG && console.log('pathname changed');
            pathname = window.location.pathname;
            if (pathname.match(/^\/calendar\/r\/eventedit\//)) {
                DEBUG && console.log('on event edit page');

                if (buttonClickedData) {
                    clickButton(buttonClickedData);
                    buttonClickedData = null;
                } else {
                    // Add action buttons to calendar event edit page.
                    DEBUG && console.log('adding buttons');
                    var onclickAction = function(myButton, myEventTitlePrefix) {
                        buttonClickedData = {
                            'button': myButton,
                            'eventTitlePrefix': myEventTitlePrefix,
                        };
                        clickButton(buttonClickedData);
                        buttonClickedData = null;
                    };

                    var saveButton = document.querySelector('[aria-label="Save"]');

                    var buttonWrapper = document.createElement('div');
                    buttonWrapper.style.left = '350px';
                    buttonWrapper.style.position = 'absolute';
                    buttonWrapper.style.top = '65px';
                    buttonWrapper.style.width = '350px';
                    buttonWrapper.style.zIndex = '1';

                    insertAfter(buttonWrapper, saveButton);
                    insertButtons(buttonWrapper, onclickAction, 'inside');
                }
            }
        }
    }, 1000);
})();

document.onclick = function(event) {
    DEBUG && console.group('element clicked');
    DEBUG && console.log('element clicked:', event.target);

    var target = event.target;
    if (!target) {
        return;
    }
    var eventIdFound = false;
    for (var i = 0; i < 5; i++) {
        // Ignore event bubble.
        if (target.getAttribute('id') === 'xDetDlg') {
            break;
        } else if (target.getAttribute('data-eventid')) {
            eventIdFound = true;
            break;
        }
        target = target.parentElement;
    }
    DEBUG && console.log('target:', target);
    if (eventIdFound) {
        var eventId = target.getAttribute('data-eventid');
        DEBUG && console.log('event id found:', eventId);

        function modifyEventBubble(attempt) {
            attempt = attempt || 1;
            if (attempt >= 3) {
                return;
            }
            attempt += 1;

            var eventBubble = document.querySelector('#xDetDlg[data-eventid="' + eventId + '"]');
            DEBUG && console.log('eventBubble:', eventBubble);
            if (!eventBubble) {
                setTimeout(modifyEventBubble, 500, attempt);
                return;
            }

            var editEventButton = eventBubble.querySelector('[data-tooltip="Edit event"]');
            var eventEditable = editEventButton ? true : false;
            if (!eventEditable) {
                return;
            }

            var lastEventBubbleMetaItem = document.querySelector('#xDtlDlgCt > div:last-child');

            var newEventBubbleMetaItem = document.createElement('div');
            newEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(newEventBubbleMetaItem, lastEventBubbleMetaItem);

            var lastNewEventBubbleMetaItem = document.createElement('div');
            lastNewEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(lastNewEventBubbleMetaItem, newEventBubbleMetaItem);

            var onclickAction = function(myButton, myEventTitlePrefix) {
                buttonClickedData = {
                    'button': myButton,
                    'eventTitlePrefix': myEventTitlePrefix,
                };

                var editEventButton = document.querySelector('[aria-label="Edit event"]');
                DEBUG && console.log('editEventButton:', editEventButton);
                editEventButton.click();
            };
            insertButtons(newEventBubbleMetaItem, onclickAction, 'inside', lastNewEventBubbleMetaItem);
        }
        setTimeout(modifyEventBubble, 100);
    }
    DEBUG && console.groupEnd();
};
<commit_msg>Fix .getAttribute failing when target is null<commit_after>const PRIORITY_EVENT_TITLE_PREFIXES = ['-', '1.', '2.', '3.', '4.', '5.', null];
/* const */ DEBUG = false;

function dispatchEvent(obj, event) {
    var evt = new Event(
        event,
        {
            target: obj,
            bubbles: true,
        }
    );
    obj.dispatchEvent(evt);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function insertButtons(referenceNode, onclickAction, where, alternateReferenceNode) {
    var insertTarget = referenceNode;
    var buttonLabels = [
        // buttonLabel, eventTitlePrefix, buttonClassNames.
        ['-',       '-',  ['jfk-button', 'jfk-button-standard']],
        ['1',       '1.', ['jfk-button', 'jfk-button-standard']],
        ['2',       '2.', ['jfk-button', 'jfk-button-standard']],
        ['3',       '3.', ['jfk-button', 'jfk-button-standard']],
        ['4',       '4.', ['jfk-button', 'jfk-button-standard']],
        ['5',       '5.', ['jfk-button', 'jfk-button-standard']],
        ['X',       null, ['jfk-button', 'jfk-button-standard']],
        ['DONE',    '✓',  ['jfk-button', 'jfk-button-default' ]],
        ['NOPE',    '✗',  ['jfk-button', 'jfk-button-standard']],
        ['OKAY',    '▣',  ['jfk-button', 'jfk-button-standard']],
        ['AWESOME', 'ツ', ['jfk-button', 'jfk-button-standard']],
    ];
    for (var i in buttonLabels) {
        var buttonLabel = buttonLabels[i][0];
        DEBUG && console.log('buttonLabel:', buttonLabel);

        var eventTitlePrefix = buttonLabels[i][1];
        DEBUG && console.log('eventTitlePrefix:', eventTitlePrefix);

        var buttonClassNames = buttonLabels[i][2];
        DEBUG && console.log('buttonClassNames:', buttonClassNames);

        var button = document.createElement('button');
        button.innerText = buttonLabel;
        button.classList.add(...buttonClassNames);

        (function(myButton, myEventTitlePrefix) {
            myButton.onclick = function() {
                DEBUG && console.log('myButton.onclick');
                onclickAction(myButton, myEventTitlePrefix);
            };
        }(button, eventTitlePrefix));

        DEBUG && console.log('inserting button', button, where, insertTarget);
        if (where === 'after') {
            insertAfter(button, insertTarget);
            insertTarget = button;
        } else if (where === 'inside') {
            if (alternateReferenceNode && !PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
                alternateReferenceNode.appendChild(button);
            } else {
                referenceNode.appendChild(button);
            }
        }
    }
}

function clickButton(clickedData) {
    DEBUG && console.group('clickButton');
    DEBUG && console.log('clickedData:', clickedData);
    DEBUG && console.log('button clicked');

    // Today.
    var date = new Date();
    var monthName = date.toLocaleString('en-us', { 'month': 'short' });
    var todayFormattedDate = monthName + ' ' + date.getDate() + ', ' + date.getFullYear();

    // Event date.
    var startDateInput = document.querySelector('[aria-label="Start date"]');
    var endDateInput = document.querySelector('[aria-label="End date"]');
    var eventDate = startDateInput.value;
    if (endDateInput.value !== startDateInput.value) {
        eventDate += ' - ' + endDateInput.value;
    }
    DEBUG && console.log('eventDate:', eventDate);

    var eventTitle = document.querySelector('[aria-label="Title"]');
    var calendarEventTitle = eventTitle.value;
    DEBUG && console.log('before calendarEventTitle:', calendarEventTitle);

    // Remove leading number (e.g. "1. " in "1. My Calendar Event").
    calendarEventTitle = calendarEventTitle.replace(/^\d+\. /, '');
    // Remove leading ! character.
    calendarEventTitle = calendarEventTitle.replace(/^! /, '');
    // Remove leading - character.
    calendarEventTitle = calendarEventTitle.replace(/^- /, '');
    // Remove leading ~ character.
    calendarEventTitle = calendarEventTitle.replace(/^~ /, '');

    var buttonLabel = clickedData.button.innerText;
    DEBUG && console.log('buttonLabel:', buttonLabel);
    if (buttonLabel === 'DONE') {
        // Remove leading "Tentative: ".
        calendarEventTitle = calendarEventTitle.replace(/^Tentative: /, '');
    }

    DEBUG && console.log(' after calendarEventTitle:', calendarEventTitle);

    // "✓ My Event; Dec 31, 2015; event date: Jan 1, 2016"
    // "1. My Event"
    // "My Event"
    var eventTitlePrefix = clickedData['eventTitlePrefix'];
    var newCalendarEventTitle = calendarEventTitle;
    if (eventTitlePrefix !== null) {
        newCalendarEventTitle = eventTitlePrefix + ' ' + calendarEventTitle;
        if (! PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
            newCalendarEventTitle += ';' +
                ' ' + todayFormattedDate + ';' +
                ' event date: ' + eventDate;
        }
    }
    DEBUG && console.log('newCalendarEventTitle:', newCalendarEventTitle);
    eventTitle.value = newCalendarEventTitle;

    setTimeout(function() {
        dispatchEvent(eventTitle, 'input');
    }, 200);

    var saveButton = document.querySelector('[aria-label="Save"]');
    setTimeout(function() {
        saveButton.click();
    }, 500);

    DEBUG && console.groupEnd();
}

var buttonClickedData;
(function(){
    var pathname = window.location.pathname;
    setInterval(function() {
        if (pathname !== window.location.pathname) {
            DEBUG && console.log('pathname changed');
            pathname = window.location.pathname;
            if (pathname.match(/^\/calendar\/r\/eventedit\//)) {
                DEBUG && console.log('on event edit page');

                if (buttonClickedData) {
                    clickButton(buttonClickedData);
                    buttonClickedData = null;
                } else {
                    // Add action buttons to calendar event edit page.
                    DEBUG && console.log('adding buttons');
                    var onclickAction = function(myButton, myEventTitlePrefix) {
                        buttonClickedData = {
                            'button': myButton,
                            'eventTitlePrefix': myEventTitlePrefix,
                        };
                        clickButton(buttonClickedData);
                        buttonClickedData = null;
                    };

                    var saveButton = document.querySelector('[aria-label="Save"]');

                    var buttonWrapper = document.createElement('div');
                    buttonWrapper.style.left = '350px';
                    buttonWrapper.style.position = 'absolute';
                    buttonWrapper.style.top = '65px';
                    buttonWrapper.style.width = '350px';
                    buttonWrapper.style.zIndex = '1';

                    insertAfter(buttonWrapper, saveButton);
                    insertButtons(buttonWrapper, onclickAction, 'inside');
                }
            }
        }
    }, 1000);
})();

document.onclick = function(event) {
    DEBUG && console.group('element clicked');
    DEBUG && console.log('element clicked:', event.target);

    var target = event.target;
    if (!target) {
        return;
    }
    var eventIdFound = false;
    for (var i = 0; i < 5; i++) {
        if (!target) {
            break;
        // Ignore event bubble.
        } else if (target.getAttribute('id') === 'xDetDlg') {
            break;
        } else if (target.getAttribute('data-eventid')) {
            eventIdFound = true;
            break;
        }
        target = target.parentElement;
    }
    DEBUG && console.log('target:', target);
    if (eventIdFound) {
        var eventId = target.getAttribute('data-eventid');
        DEBUG && console.log('event id found:', eventId);

        function modifyEventBubble(attempt) {
            attempt = attempt || 1;
            if (attempt >= 3) {
                return;
            }
            attempt += 1;

            var eventBubble = document.querySelector('#xDetDlg[data-eventid="' + eventId + '"]');
            DEBUG && console.log('eventBubble:', eventBubble);
            if (!eventBubble) {
                setTimeout(modifyEventBubble, 500, attempt);
                return;
            }

            var editEventButton = eventBubble.querySelector('[data-tooltip="Edit event"]');
            var eventEditable = editEventButton ? true : false;
            if (!eventEditable) {
                return;
            }

            var lastEventBubbleMetaItem = document.querySelector('#xDtlDlgCt > div:last-child');

            var newEventBubbleMetaItem = document.createElement('div');
            newEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(newEventBubbleMetaItem, lastEventBubbleMetaItem);

            var lastNewEventBubbleMetaItem = document.createElement('div');
            lastNewEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(lastNewEventBubbleMetaItem, newEventBubbleMetaItem);

            var onclickAction = function(myButton, myEventTitlePrefix) {
                buttonClickedData = {
                    'button': myButton,
                    'eventTitlePrefix': myEventTitlePrefix,
                };

                var editEventButton = document.querySelector('[aria-label="Edit event"]');
                DEBUG && console.log('editEventButton:', editEventButton);
                editEventButton.click();
            };
            insertButtons(newEventBubbleMetaItem, onclickAction, 'inside', lastNewEventBubbleMetaItem);
        }
        setTimeout(modifyEventBubble, 100);
    }
    DEBUG && console.groupEnd();
};
<|endoftext|>


<commit_before>// Declare a proxy to reference the hub.
//var url = window.location.href;
//trace('url = ' + url);
//var scripts = document.getElementsByTagName("script"),
//    src = toLocation(scripts[scripts.length - 1].src).origin;
//$.connection.hub.url = src + "/signalr";
//trace('connection url = ' + $.connection.hub.url);
//var chat = $.connection.chatHub;

//function toLocation(url) {
//    var a = document.createElement('a');
//    a.href = url;
//    return a;
//};

$.connection.hub.url = "https://chatroomone.azurewebsites.net/signalr";
//$.connection.hub.url = "http://localhost:52527/signalr";
var chat = $.connection.chatHub;

function starting() {

    // Create a function that the hub can call to broadcast messages.
    chat.client.broadcastMessage = function (priv, name, message) {
        // Html encode display name and message.
        var encodedName = $('
').text(name).html();
        var encodedMsg = $('
').text(message).html();
        // Add the message to the page.
        if (priv) {
            message = "[" + priv + "]  " + message;
        };
        message = message + "    " + getTime() + "";
        $discussion.prepend('
' + encodedName
            + ':  ' + message + '
');

        var audio = new Audio('/sound/page-flip-01a.mp3');
        audio.play();
    };

    chat.client.showUsersOnLine = function (keys, connection, browsers, medias) {
        console.trace('keys = ' + keys);
        console.trace('connection = ' + connection);
        console.trace('browsers = ' + browsers);
        console.trace('medias = ' + medias);
        var keysarray = keys.toString().split(',');
        var conarray = connection.toString().split(',');
        var browserarray = browsers.toString().split(',');
        var mediaarray = medias.toString().split(',');
        var number = keysarray.indexOf($displayname.val());
        keysarray.splice(number, 1);
        conarray.splice(number, 1);
        browserarray.splice(number, 1);
        mediaarray.splice(number, 1);
        if (keysarray[0] != null) {
            var audio = new Audio('/sound/bottle-open-1.mp3');
            audio.play();
            var i;
            $users.empty();
            $users.append('<input type="radio" value= "public" name="user" checked>Public
');
            for (i = 0; i < keysarray.length; i++) {
                var connectionId = conarray[i];
                var media = mediaarray[i];
                console.trace('media = ' + media);
                var med = " ";
                switch (media) {
                    case "0":
                        med = " ";
                        break;
                    case "1":
                        med = "WebCam";
                        break;
                    case "2":
                        med = "Mic";
                        break;
                    default:
                        med = "Nothing";
                        break;
                }
                console.trace('med = ' + med);

                $users.append('<input type="radio" value= connectionId name="user" checked>' + keysarray[i] + '   /' + browserarray[i] + '/   ' + med + '
');
                $('input[name="user"]:checked').val(conarray[i]);
            }
            $('input[name="user"][value="public"]').prop('checked', true);
        }
        else {
            $users.empty();
        }
        $callButton.prop('disabled', true);
    };

    chat.client.hangUpVideo = function () {
        hangup();
    };

    chat.client.sendOffer = function (desc) {
        trace('Offer sent ' + desc);
        answer(JSON.parse(desc));
    };

    chat.client.sendIce = function (desc) {
        trace('Ice sent ' + desc);
        addIceCandidate(JSON.parse(desc));
    };

    chat.client.sendAnswer = function (desc) {
        trace('Answer sent ' + desc);
        getAnswer(JSON.parse(desc));
    };

    getUserName();
    startHub();
};

function getUserName() {
    // Get the user name and store it to prepend to messages.
    var name = "";
    name = getUrlVars()["user"];
    //trace('user1 = ' + name);
    if (!(name) || name == "undefined") {
        var name = $("#user").val();
    }
    name = $.trim(name);
    console.trace('user2 = ' + name);
    if (!(name) || name == null) {
        name = generateQuickGuid();
        //trace('user = ' + name);
    }
    console.trace('user = ' + name);
    console.trace('browser = ' + webrtcDetectedBrowser);
    $myname.val(name);
    $displayname.val(name);
    // Set initial focus to message input box.
    $message.focus();
    // Start the connection.
    $.connection.hub.qs = "userName=" + name + "&browser=" + webrtcDetectedBrowser;
    //$.connection.hub.qs = "browser=" + webrtcDetectedBrowser;
};

function startHub() {
    $.connection.hub.start().done(function () {
        $sendmessage.click(function () {
            // Call the Send method on the hub.
            var conn = $('input[name="user"]:checked').val();
            var conname = $('input[name="user"]:checked').next().text().split(' ')[0];
            console.trace('conn = ' + conn + '/' + conname);
            if (conn == "public") {
                chat.server.send($displayname.val(), $message.val());
            }
            else {
                chat.server.sendToUser(conname, conn, $displayname.val(), $message.val());
            }
            // Clear text box and reset focus for next comment.
            $message.val('').focus();
        });

        $message.keypress(function (e) {
            if (e.which == 13) {//Enter key pressed
                $sendmessage.click();
            }
        });
        $clearMessages.click(function () {
            $discussion.empty();
        });
    });
};

   <commit_msg>user online fixing<commit_after>// Declare a proxy to reference the hub.
//var url = window.location.href;
//trace('url = ' + url);
//var scripts = document.getElementsByTagName("script"),
//    src = toLocation(scripts[scripts.length - 1].src).origin;
//$.connection.hub.url = src + "/signalr";
//trace('connection url = ' + $.connection.hub.url);
//var chat = $.connection.chatHub;

//function toLocation(url) {
//    var a = document.createElement('a');
//    a.href = url;
//    return a;
//};

$.connection.hub.url = "https://chatroomone.azurewebsites.net/signalr";
//$.connection.hub.url = "http://localhost:52527/signalr";
var chat = $.connection.chatHub;

function starting() {

    // Create a function that the hub can call to broadcast messages.
    chat.client.broadcastMessage = function (priv, name, message) {
        // Html encode display name and message.
        var encodedName = $('
').text(name).html();
        var encodedMsg = $('
').text(message).html();
        // Add the message to the page.
        if (priv) {
            message = "[" + priv + "]  " + message;
        };
        message = message + "    " + getTime() + "";
        $discussion.prepend('
' + encodedName
            + ':  ' + message + '
');

        var audio = new Audio('/sound/page-flip-01a.mp3');
        audio.play();
    };

    chat.client.showUsersOnLine = function (keys, connection, browsers, medias) {
        console.trace('keys = ' + keys);
        console.trace('connection = ' + connection);
        console.trace('browsers = ' + browsers);
        console.trace('medias = ' + medias);
        var keysarray = keys.toString().split(',');
        var conarray = connection.toString().split(',');
        var browserarray = browsers.toString().split(',');
        var mediaarray = medias.toString().split(',');
        var number = keysarray.indexOf($displayname.val());
        keysarray.splice(number, 1);
        conarray.splice(number, 1);
        browserarray.splice(number, 1);
        mediaarray.splice(number, 1);
        if (keysarray[0] != null) {
            var audio = new Audio('/sound/bottle-open-1.mp3');
            audio.play();
            var i;
            $users.empty();
            $users.append('<input type="radio" value= "public" name="user" checked>Public
');
            for (i = 0; i < keysarray.length; i++) {
                var connectionId = conarray[i];
                var media = mediaarray[i];
                console.trace('media = ' + media);
                var med = " ";
                switch (media) {
                    case "0":
                        med = " ";
                        break;
                    case "1":
                        med = "WebCam";
                        break;
                    case "2":
                        med = "Mic";
                        break;
                    default:
                        med = "Nothing";
                        break;
                }
                console.trace('med = ' + med);

                $users.append('<input type="radio" value= connectionId name="user" checked>' + keysarray[i] + '   /' + browserarray[i] + '/   ' + med + '

');
                $('input[name="user"]:checked').val(conarray[i]);
            }
            $('input[name="user"][value="public"]').prop('checked', true);
        }
        else {
            $users.empty();
        }
        $callButton.prop('disabled', true);
    };

    chat.client.hangUpVideo = function () {
        hangup();
    };

    chat.client.sendOffer = function (desc) {
        trace('Offer sent ' + desc);
        answer(JSON.parse(desc));
    };

    chat.client.sendIce = function (desc) {
        trace('Ice sent ' + desc);
        addIceCandidate(JSON.parse(desc));
    };

    chat.client.sendAnswer = function (desc) {
        trace('Answer sent ' + desc);
        getAnswer(JSON.parse(desc));
    };

    getUserName();
    startHub();
};

function getUserName() {
    // Get the user name and store it to prepend to messages.
    var name = "";
    name = getUrlVars()["user"];
    //trace('user1 = ' + name);
    if (!(name) || name == "undefined") {
        var name = $("#user").val();
    }
    name = $.trim(name);
    console.trace('user2 = ' + name);
    if (!(name) || name == null) {
        name = generateQuickGuid();
        //trace('user = ' + name);
    }
    console.trace('user = ' + name);
    console.trace('browser = ' + webrtcDetectedBrowser);
    $myname.val(name);
    $displayname.val(name);
    // Set initial focus to message input box.
    $message.focus();
    // Start the connection.
    $.connection.hub.qs = "userName=" + name + "&browser=" + webrtcDetectedBrowser;
    //$.connection.hub.qs = "browser=" + webrtcDetectedBrowser;
};

function startHub() {
    $.connection.hub.start().done(function () {
        $sendmessage.click(function () {
            // Call the Send method on the hub.
            var conn = $('input[name="user"]:checked').val();
            var conname = $('input[name="user"]:checked').next().text().split(' ')[0];
            console.trace('conn = ' + conn + '/' + conname);
            if (conn == "public") {
                chat.server.send($displayname.val(), $message.val());
            }
            else {
                chat.server.sendToUser(conname, conn, $displayname.val(), $message.val());
            }
            // Clear text box and reset focus for next comment.
            $message.val('').focus();
        });

        $message.keypress(function (e) {
            if (e.which == 13) {//Enter key pressed
                $sendmessage.click();
            }
        });
        $clearMessages.click(function () {
            $discussion.empty();
        });
    });
};

   <|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import MicroContainer from 'react-micro-container';
import {createHttpClient} from './http';
import Alert from './components/Alert';
import Select from './components/Select';
import {sprintf} from 'sprintf-js';

class SettingView extends MicroContainer {

  constructor(props) {
    super(props);
    this.state = {
      alert: {
        visible: false,
        kind: '',
        message: '',
      },
      email: '',
      timeSpan: {
        editable: false,
        timeSpans: [], // {fromHour:23, fromMinute:0, toHour:23, toMinute:30}
      },
    };
  }

  componentDidMount() {
    this.subscribe({
      showAlert: this.handleShowAlert,
      // Email
      hideAlert: this.handleHideAlert,
      onChangeEmail: this.handleOnChangeEmail,
      updateEmail: this.handleUpdateEmail,
      // TimeSpan
      setTimeSpanEditable: this.handleSetTimeSpanEditable,
      addTimeSpan: this.handleAddTimeSpan,
      deleteTimeSpan: this.handleDeleteTimeSpan,
      updateTimeSpan: this.handleUpdateTimeSpan,
      onChangeTimeSpan: this.handleOnChangeTimeSpan,
    });

    this.fetch();
  }

  render() {
    return (

        <Alert dispatch={this.dispatch} {...this.state.alert}/>
        <EmailForm dispatch={this.dispatch} value={this.state.email}/>
        <NotificationTimeSpanForm dispatch={this.dispatch} {...this.state.timeSpan}
        />

    );
  }

  fetch() {
    const client = createHttpClient();
    client.get('/api/v1/me')
      .then((response) => {
        console.log(response.data);
        const timeSpans = response.data['notificationTimeSpans'] ? response.data['notificationTimeSpans'] : [];
        this.setState({
          email: response.data['email'],
          timeSpan: {
            editable: this.state.timeSpan.editable,
            timeSpans: timeSpans,
          },
        })
      })
      .catch((error) => {
        console.log(error);
        this.handleShowAlert('danger', 'システムエラーが発生しました');
      });
  }

  handleShowAlert(kind, message) {
    this.setState({
      alert: {visible: true, kind: kind, message: message}
    })
  }

  handleHideAlert() {
    this.setState({
      alert: {visible: false}
    })
  }

  handleOnChangeEmail(email) {
    this.setState({email: email})
  }

  handleUpdateEmail(email) {
    //alert('email is ' + email);
    const client = createHttpClient();
    client.post('/api/v1/me/email', {
      email: email,
    })
      .then((response) => {
        this.handleShowAlert('success', 'メールアドレスを更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しいメールアドレスを入力してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });
  }

  handleSetTimeSpanEditable(value) {
    this.setState({
      timeSpan: {
        editable: value,
        timeSpans: this.state.timeSpan.timeSpans,
      }
    })
  }

  handleAddTimeSpan() {
    if (this.state.timeSpan.timeSpans.length === 3) {
      return;
    }
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: [...this.state.timeSpan.timeSpans, {fromHour: 0, fromMinute: 0, toHour: 0, toMinute: 0}],
      }
    });
  }

  handleDeleteTimeSpan(index) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    if (index >= timeSpans.length) {
      return;
    }
    timeSpans.splice(index, 1);
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      },
    });
  }

  handleOnChangeTimeSpan(name, index, value) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    timeSpans[index][name] = value;
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      }
    });
  }

  handleUpdateTimeSpan() {
    const timeSpans = [];
    for (const timeSpan of this.state.timeSpan.timeSpans) {
      for (const [k, v] of Object.entries(timeSpan)) {
        timeSpan[k] = parseInt(v);
      }
      if (timeSpan.fromHour === 0
        && timeSpan.fromMinute === 0
        && timeSpan.toHour === 0
        && timeSpan.toMinute === 0) {
        // Ignore zero value
        continue;
      }
      timeSpans.push(timeSpan);
    }

    const client = createHttpClient();
    client.post('/api/v1/me/notificationTimeSpan', {
      notificationTimeSpans: timeSpans,
    })
      .then((response) => {
        this.handleShowAlert('success', '通知対象の時間帯を更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しい通知対象の時間帯を選択してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });

    this.setState({
      timeSpan: {
        editable: false,
        timeSpans: timeSpans,
      }
    });
  }
}

class EmailForm extends React.Component {

  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
  }

  onChange(e) {
    this.props.dispatch('onChangeEmail', e.target.value);
  }

  render() {
    return (
      <form className="form-horizontal">

            Email address

            <input
              type="email" className="form-control" name="email" id="email"
              placeholder="Email" required autoFocus autoComplete="on"
              value={this.props.value} onChange={this.onChange}/>

            <button
              type="button"
              disabled={!this.props.value}
              className="btn btn-primary"
              onClick={() => this.props.dispatch('updateEmail', this.props.value)}
            >
              変更
            </button>

      </form>
    );
  }
}

class NotificationTimeSpanForm extends React.Component {
  constructor(props) {
    super(props);
    this.onClickPlus = this.onClickPlus.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  onClickPlus(e) {
    e.preventDefault();
    this.props.dispatch('addTimeSpan');
  }

  onClickMinus(e, index) {
    e.preventDefault();
    this.props.dispatch('deleteTimeSpan', index);
  }

  onChange(event) {
    const a = event.target.name.split('_');
    const name = a[0];
    const index = a[1];
    const timeSpans = this.props.timeSpans.slice();
    timeSpans[index][name] = event.target.value;
    this.props.dispatch('onChangeTimeSpan', name, index, event.target.value);
  }

  createTimeSpanRow(timeSpan, index) {
    let hourOptions = [];
    for (let i = 0; i <= 23; i++) {
      hourOptions.push({value: i, label: i})
    }
    let minuteOptions = [];
    for (const i of [0, 30]) {
      minuteOptions.push({value: i, label: sprintf('%02d', i)});
    }

    return (

        <Select
          name={'fromHour_' + index}
          value={timeSpan.fromHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'fromMinute_' + index}
          value={timeSpan.fromMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

          〜

        <Select
          name={'toHour_' + index}
          value={timeSpan.toHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'toMinute_' + index}
          value={timeSpan.toMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

        <button type="button" className="btn btn-link btn-xs" onClick={() => this.onClickPlus(event)}>

        </button>
        <button type="button" className="btn btn-link btn-xs" onClick={() => this.onClickMinus(event, index)}>

        </button>

    );
  }

  render() {
    let content = [];
    if (this.props.editable) {
      if (this.props.timeSpans.length > 0) {
        this.props.timeSpans.map((timeSpan, i) => {
          content.push(this.createTimeSpanRow(timeSpan, i));
        });
      } else {
        this.props.dispatch('addTimeSpan');
      }
    } else {
      if (this.props.timeSpans.length > 0) {
        for (let timeSpan of this.props.timeSpans) {
          content.push(

{timeSpan.fromHour}:{sprintf('%02d', timeSpan.fromMinute)} 〜 {timeSpan.toHour}:{sprintf('%02d', timeSpan.toMinute)}
);
        }
      } else {
        content.push(

データがありません。編集ボタンで追加できます。

        );
      }
    }

    let updateButton;
    if (this.props.editable) {
      updateButton =
        <button
          type="button" className="btn btn-primary"
          onClick={() => this.props.dispatch('updateTimeSpan', false)}
        >
          更新
        </button>;
    } else {
      updateButton =
        <button
          type="button" className="btn btn-default"
          onClick={() => this.props.dispatch('setTimeSpanEditable', true)}
        >
          編集
        </button>;
    }

    return (
      <form className="form-horizontal">

            通知対象の時間帯

            {content}

            {updateButton}

      </form>
    );
  }
}

ReactDOM.render(
  <SettingView/>,
  document.getElementById('root')
);
<commit_msg>Add help link<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import MicroContainer from 'react-micro-container';
import {createHttpClient} from './http';
import Alert from './components/Alert';
import Select from './components/Select';
import {sprintf} from 'sprintf-js';

class SettingView extends MicroContainer {

  constructor(props) {
    super(props);
    this.state = {
      alert: {
        visible: false,
        kind: '',
        message: '',
      },
      email: '',
      timeSpan: {
        editable: false,
        timeSpans: [], // {fromHour:23, fromMinute:0, toHour:23, toMinute:30}
      },
    };
  }

  componentDidMount() {
    this.subscribe({
      showAlert: this.handleShowAlert,
      // Email
      hideAlert: this.handleHideAlert,
      onChangeEmail: this.handleOnChangeEmail,
      updateEmail: this.handleUpdateEmail,
      // TimeSpan
      setTimeSpanEditable: this.handleSetTimeSpanEditable,
      addTimeSpan: this.handleAddTimeSpan,
      deleteTimeSpan: this.handleDeleteTimeSpan,
      updateTimeSpan: this.handleUpdateTimeSpan,
      onChangeTimeSpan: this.handleOnChangeTimeSpan,
    });

    this.fetch();
  }

  render() {
    return (

        <Alert dispatch={this.dispatch} {...this.state.alert}/>
        <EmailForm dispatch={this.dispatch} value={this.state.email}/>
        <NotificationTimeSpanForm dispatch={this.dispatch} {...this.state.timeSpan}
        />

    );
  }

  fetch() {
    const client = createHttpClient();
    client.get('/api/v1/me')
      .then((response) => {
        console.log(response.data);
        const timeSpans = response.data['notificationTimeSpans'] ? response.data['notificationTimeSpans'] : [];
        this.setState({
          email: response.data['email'],
          timeSpan: {
            editable: this.state.timeSpan.editable,
            timeSpans: timeSpans,
          },
        })
      })
      .catch((error) => {
        console.log(error);
        this.handleShowAlert('danger', 'システムエラーが発生しました');
      });
  }

  handleShowAlert(kind, message) {
    this.setState({
      alert: {visible: true, kind: kind, message: message}
    })
  }

  handleHideAlert() {
    this.setState({
      alert: {visible: false}
    })
  }

  handleOnChangeEmail(email) {
    this.setState({email: email})
  }

  handleUpdateEmail(email) {
    //alert('email is ' + email);
    const client = createHttpClient();
    client.post('/api/v1/me/email', {
      email: email,
    })
      .then((response) => {
        this.handleShowAlert('success', 'メールアドレスを更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しいメールアドレスを入力してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });
  }

  handleSetTimeSpanEditable(value) {
    this.setState({
      timeSpan: {
        editable: value,
        timeSpans: this.state.timeSpan.timeSpans,
      }
    })
  }

  handleAddTimeSpan() {
    if (this.state.timeSpan.timeSpans.length === 3) {
      return;
    }
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: [...this.state.timeSpan.timeSpans, {fromHour: 0, fromMinute: 0, toHour: 0, toMinute: 0}],
      }
    });
  }

  handleDeleteTimeSpan(index) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    if (index >= timeSpans.length) {
      return;
    }
    timeSpans.splice(index, 1);
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      },
    });
  }

  handleOnChangeTimeSpan(name, index, value) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    timeSpans[index][name] = value;
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      }
    });
  }

  handleUpdateTimeSpan() {
    const timeSpans = [];
    for (const timeSpan of this.state.timeSpan.timeSpans) {
      for (const [k, v] of Object.entries(timeSpan)) {
        timeSpan[k] = parseInt(v);
      }
      if (timeSpan.fromHour === 0
        && timeSpan.fromMinute === 0
        && timeSpan.toHour === 0
        && timeSpan.toMinute === 0) {
        // Ignore zero value
        continue;
      }
      timeSpans.push(timeSpan);
    }

    const client = createHttpClient();
    client.post('/api/v1/me/notificationTimeSpan', {
      notificationTimeSpans: timeSpans,
    })
      .then((response) => {
        this.handleShowAlert('success', '通知対象の時間帯を更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しい通知対象の時間帯を選択してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });

    this.setState({
      timeSpan: {
        editable: false,
        timeSpans: timeSpans,
      }
    });
  }
}

class EmailForm extends React.Component {

  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
  }

  onChange(e) {
    this.props.dispatch('onChangeEmail', e.target.value);
  }

  render() {
    return (
      <form className="form-horizontal">

            Email address

            <input
              type="email" className="form-control" name="email" id="email"
              placeholder="Email" required autoFocus autoComplete="on"
              value={this.props.value} onChange={this.onChange}/>

            <button
              type="button"
              disabled={!this.props.value}
              className="btn btn-primary"
              onClick={() => this.props.dispatch('updateEmail', this.props.value)}
            >
              変更
            </button>

      </form>
    );
  }
}

class NotificationTimeSpanForm extends React.Component {
  constructor(props) {
    super(props);
    this.onClickPlus = this.onClickPlus.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  onClickPlus(e) {
    e.preventDefault();
    this.props.dispatch('addTimeSpan');
  }

  onClickMinus(e, index) {
    e.preventDefault();
    this.props.dispatch('deleteTimeSpan', index);
  }

  onChange(event) {
    const a = event.target.name.split('_');
    const name = a[0];
    const index = a[1];
    const timeSpans = this.props.timeSpans.slice();
    timeSpans[index][name] = event.target.value;
    this.props.dispatch('onChangeTimeSpan', name, index, event.target.value);
  }

  createTimeSpanRow(timeSpan, index) {
    let hourOptions = [];
    for (let i = 0; i <= 23; i++) {
      hourOptions.push({value: i, label: i})
    }
    let minuteOptions = [];
    for (const i of [0, 30]) {
      minuteOptions.push({value: i, label: sprintf('%02d', i)});
    }

    return (

        <Select
          name={'fromHour_' + index}
          value={timeSpan.fromHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'fromMinute_' + index}
          value={timeSpan.fromMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

          〜

        <Select
          name={'toHour_' + index}
          value={timeSpan.toHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'toMinute_' + index}
          value={timeSpan.toMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

        <button type="button" className="btn btn-link btn-xs" onClick={() => this.onClickPlus(event)}>

        </button>
        <button type="button" className="btn btn-link btn-xs" onClick={() => this.onClickMinus(event, index)}>

        </button>

    );
  }

  render() {
    let content = [];
    if (this.props.editable) {
      if (this.props.timeSpans.length > 0) {
        this.props.timeSpans.map((timeSpan, i) => {
          content.push(this.createTimeSpanRow(timeSpan, i));
        });
      } else {
        this.props.dispatch('addTimeSpan');
      }
    } else {
      if (this.props.timeSpans.length > 0) {
        for (let timeSpan of this.props.timeSpans) {
          content.push(

{timeSpan.fromHour}:{sprintf('%02d', timeSpan.fromMinute)} 〜 {timeSpan.toHour}:{sprintf('%02d', timeSpan.toMinute)}
);
        }
      } else {
        content.push(

データがありません。編集ボタンで追加できます。

        );
      }
    }

    let updateButton;
    if (this.props.editable) {
      updateButton =
        <button
          type="button" className="btn btn-primary"
          onClick={() => this.props.dispatch('updateTimeSpan', false)}
        >
          更新
        </button>;
    } else {
      updateButton =
        <button
          type="button" className="btn btn-default"
          onClick={() => this.props.dispatch('setTimeSpanEditable', true)}
        >
          編集
        </button>;
    }

    return (
      <form className="form-horizontal">

            通知対象の時間帯

            {content}

            {updateButton}

      </form>
    );
  }
}

ReactDOM.render(
  <SettingView/>,
  document.getElementById('root')
);
<|endoftext|>


<commit_before>$(window).keyup(function (event) {
    if (event.keyCode === 27 && $('#contextmenu').length) {
        $('#context-menu-blocker, #contextmenu').remove();
    }
});

function showContextMenu(buttons, x, y) {
    var contextmenu = $('
');

    $.each(buttons, function(i, item) {
        var $li = $('
')
            .text(item.title)
            .data('args', item.args)
            .data('callback', item.callback)
            .click(function() {
                $(this).data('callback')($(this).data('args'));
                $('#context-menu-blocker, #contextmenu').remove();
            });
        if (item.cssClass) {
            $li.addClass(item.cssClass);
        }
        $li.appendTo(contextmenu);
    });

    // Set up a blocker div that closes the menu when clicked
    var blocker = $('

').mousedown(function(event) {
        $('#context-menu-blocker, #contextmenu').remove();
        event.stopPropagation();
    });

    blocker.appendTo('body');
    contextmenu.appendTo('body');

	// Make sure contextmenu does not reach outside the window
	if ((y + contextmenu.height()) > $(window).height()) {
		y -= contextmenu.height();
	}
    if ((x + contextmenu.width()) > $(window).width()) {
        x -= contextmenu.width();
    }
    contextmenu.css({
        'top': y,
        'left': x
    });

    // Finally, prevent contextmenu on our contextmenu :)
    $('#context-menu-blocker, #contextmenu, #contextmenu li.option').bind('contextmenu', function (event) {
        event.preventDefault();
    });
}

function showPlaylistContextMenu(event) {
    $('#left .selected').removeClass('selected');
    $(this).addClass('selected');
    event.preventDefault();

    var buttons = [];
    buttons.push({
        title: TranslationSystem.get('Rename'),
        cssClass: 'rename',
        args: $(this),
        callback: function(li) {
            var playlist = null,
                input = $('<input type="text"/>')
                .addClass('rename')
                .val(li.text())
                .data('li', li)
                .blur(function(event) {
                    li.find('.title').show();
                    input.remove();
                })
                .keyup(function(event) {
                    var playlist;
                    switch (event.keyCode) {
                        case 13: // RETURN
                            playlist = li.data('model');
                            playlist.rename(input.val());
                            playlistManager.save();
                            $(this).blur();
                            break;
                        case 27: // ESC
                            $(this).blur();
                            break;
                    }
                    event.stopPropagation();
                });

            li.find('.title').hide();
            li.append(input);
            input.focus().select();
        }
    });
    if (logged_in && $(this).data('model').remoteId) {
        buttons.push({
            title: TranslationSystem.get('Share'),
            cssClass: 'share',
            args: $(this),
            callback: function(li) {
                new ShareTrackDialog(li.data('model')).show();
            }
        });
    }
    buttons.push({
        title: TranslationSystem.get('Import from Spotify'),
        cssClass: 'import',
        args: $(this),
        callback: function(li) {
            li.arrowPopup('#spotify-importer', 'left');
        }
    });
    buttons.push({
        title: TranslationSystem.get('Remove duplicate videos'),
        cssClass: 'remove-duplicates',
        args: $(this),
        callback: function(li) {
            var index = li.index(),
                playlist = playlistManager.getPlaylist(index);
            if (confirm(TranslationSystem.get('This will delete duplicate videos from your playlist. Continue?'))) {
                Notifications.append(playlist.removeDuplicates() + TranslationSystem.get(' duplicates removed.'));
            }
        }
    });

    if (logged_in && !$(this).data('model').remoteId) {
        buttons.push({
            title: TranslationSystem.get('Sync'),
            cssClass: 'sync',
            args: $(this),
            callback: function(li) {
                li.data('model').sync(function() {
                    playlistManager.save();
                    li.addClass('remote');
                });
            }
        });
    }

    if (logged_in && $(this).data('model').remoteId) {
        buttons.push({
            title: TranslationSystem.get('Unsync'),
            cssClass: 'unsync',
            args: $(this),
            callback: function(li) {
                li.data('model').unsync();
                playlistManager.save();
                li.removeClass('remote');
            }
        });
    }

    if (ON_DEV) {
        buttons.push({
            title: 'View JSON',
            cssClass: 'json',
            args: $(this),
            callback: function(li) {
                var playlist = li.data('model');
                alert(JSON.stringify(playlist.toJSON()));
                console.log(playlist.toJSON());
            }
        });
    }

    buttons.push({
        title: TranslationSystem.get('Delete/Unsubscribe'),
        cssClass: 'delete',
        args: $(this),
        callback: function(li) {
            var index = li.index(),
                playlist = playlistManager.getPlaylist(index);

            if (confirm("Are you sure you want to delete this playlist (" + playlist.title + ")?")) {
                playlistManager.deletePlaylist(index);
                playlistManager.save();
            }
        }
    });

    showContextMenu(buttons, event.pageX, event.pageY);
}

function showResultsItemContextMenu(event, videoElem) {
    var li = videoElem || $(this);
    var allSelectedVideos = $(this).parent().find('.video.selected');
    var video = li.data('model');

    var videos = [];
    if (allSelectedVideos.length === 0) {
        videos.push(video);
    } else {
        $.each(allSelectedVideos, function(index, li) {
            var video = $(li).data('model');
            if (video) {
                videos.push(video);
            }
        });
    }

    if (!$(li).hasClass('selected')) {
        li.parent().find('.selected').removeClass('selected');
        li.addClass('selected');
    }

    var buttons = [
        {
            title: TranslationSystem.get('Play'),
            cssClass: 'play',
            args: li,
            callback: function(elem) {
                elem.data('model').play();
            }
        },
		{
            title: TranslationSystem.get('Queue'),
            cssClass: 'queue',
            args: allSelectedVideos,
            callback: function(allSelectedVideos) {
                $.each(allSelectedVideos, function(index, li) {
                    var model = $(li).data('model');
                    if (model) {
                        Queue.addManual(model);
                    }
                });
            }
        },
		{
            title: TranslationSystem.get('Add to playlist'),
            cssClass: 'add-to-playlist',
            args: allSelectedVideos,
            callback: function() {
                new AddToPlaylistDialog(videos).show();
            }
        },
		{
			title: TranslationSystem.get('Share'),
            cssClass: 'share',
			args: li,
			callback: function(elem) {
                var video = $(elem).data('model');
                new ShareTrackDialog(video).show();
			}
		}
    ];

    if (ON_DEV) {
        buttons.push({
            title: 'View JSON',
            cssClass: 'json',
            args: li,
            callback: function(li) {
                alert(JSON.stringify(video.toJSON()));
                console.log(video.toJSON());
            }
        });
    }

    if (li.data('additionalMenuButtons')) {
        buttons = $.merge(buttons, li.data('additionalMenuButtons'));
    }

    showContextMenu(buttons, (event.pageX || li.offset().left + li.width()) , (event.pageY || li.offset().top));

    return false;
}
<commit_msg>Re added context menu. Closes #180<commit_after>$(window).keyup(function (event) {
    if (event.keyCode === 27 && $('#contextmenu').length) {
        $('#context-menu-blocker, #contextmenu').remove();
    }
});

function showContextMenu(buttons, x, y) {
    var contextmenu = $('
');

    $.each(buttons, function(i, item) {
        var $li = $('
')
            .text(item.title)
            .data('args', item.args)
            .data('callback', item.callback)
            .click(function() {
                $(this).data('callback')($(this).data('args'));
                $('#context-menu-blocker, #contextmenu').remove();
            });
        if (item.cssClass) {
            $li.addClass(item.cssClass);
        }
        $li.appendTo(contextmenu);
    });

    // Set up a blocker div that closes the menu when clicked
    var blocker = $('

').mousedown(function(event) {
        $('#context-menu-blocker, #contextmenu').remove();
        event.stopPropagation();
    });

    blocker.appendTo('body');
    contextmenu.appendTo('body');

	// Make sure contextmenu does not reach outside the window
	if ((y + contextmenu.height()) > $(window).height()) {
		y -= contextmenu.height();
	}
    if ((x + contextmenu.width()) > $(window).width()) {
        x -= contextmenu.width();
    }
    contextmenu.css({
        'top': y,
        'left': x
    });

    // Finally, prevent contextmenu on our contextmenu :)
    $('#context-menu-blocker, #contextmenu, #contextmenu li.option').bind('contextmenu', function (event) {
        event.preventDefault();
    });
}

function showPlaylistContextMenu(event) {
    $('#left .selected').removeClass('selected');
    $(this).addClass('selected');
    event.preventDefault();

    var buttons = [];
    buttons.push({
        title: TranslationSystem.get('Rename'),
        cssClass: 'rename',
        args: $(this),
        callback: function(li) {
            var playlist = null,
                input = $('<input type="text"/>')
                .addClass('rename')
                .val(li.text())
                .data('li', li)
                .blur(function(event) {
                    li.find('.title').show();
                    input.remove();
                })
                .keyup(function(event) {
                    var playlist;
                    switch (event.keyCode) {
                        case 13: // RETURN
                            playlist = li.data('model');
                            playlist.rename(input.val());
                            playlistManager.save();
                            $(this).blur();
                            break;
                        case 27: // ESC
                            $(this).blur();
                            break;
                    }
                    event.stopPropagation();
                });

            li.find('.title').hide();
            li.append(input);
            input.focus().select();
        }
    });
    if (logged_in && $(this).data('model').remoteId) {
        buttons.push({
            title: TranslationSystem.get('Share'),
            cssClass: 'share',
            args: $(this),
            callback: function(li) {
                new ShareTrackDialog(li.data('model')).show();
            }
        });
    }
    buttons.push({
        title: TranslationSystem.get('Import from Spotify'),
        cssClass: 'import',
        args: $(this),
        callback: function(li) {
            li.arrowPopup('#spotify-importer', 'left');
        }
    });
    buttons.push({
        title: TranslationSystem.get('Remove duplicate videos'),
        cssClass: 'remove-duplicates',
        args: $(this),
        callback: function(li) {
            var index = li.index(),
                playlist = playlistManager.getPlaylist(index);
            if (confirm(TranslationSystem.get('This will delete duplicate videos from your playlist. Continue?'))) {
                Notifications.append(playlist.removeDuplicates() + TranslationSystem.get(' duplicates removed.'));
            }
        }
    });

    if (logged_in && !$(this).data('model').remoteId) {
        buttons.push({
            title: TranslationSystem.get('Sync'),
            cssClass: 'sync',
            args: $(this),
            callback: function(li) {
                li.data('model').sync(function() {
                    playlistManager.save();
                    li.addClass('remote');
                });
            }
        });
    }

    if (logged_in && $(this).data('model').remoteId) {
        buttons.push({
            title: TranslationSystem.get('Unsync'),
            cssClass: 'unsync',
            args: $(this),
            callback: function(li) {
                li.data('model').unsync();
                playlistManager.save();
                li.removeClass('remote');
            }
        });
    }

    if (ON_DEV) {
        buttons.push({
            title: 'View JSON',
            cssClass: 'json',
            args: $(this),
            callback: function(li) {
                var playlist = li.data('model');
                alert(JSON.stringify(playlist.toJSON()));
                console.log(playlist.toJSON());
            }
        });
    }

    buttons.push({
        title: TranslationSystem.get('Delete/Unsubscribe'),
        cssClass: 'delete',
        args: $(this),
        callback: function(li) {
            var index = li.index(),
                playlist = playlistManager.getPlaylist(index);

            if (confirm("Are you sure you want to delete this playlist (" + playlist.title + ")?")) {
                playlistManager.deletePlaylist(index);
                playlistManager.save();
            }
        }
    });

    showContextMenu(buttons, event.pageX, event.pageY);
}

function showResultsItemContextMenu(event, videoElem) {
    var li = videoElem || $(this);
    var allSelectedVideos = $(this).parent().find('.video.selected');
    var video = li.data('model');

    var videos = [];
    if (allSelectedVideos.length === 0) {
        videos.push(video);
    } else {
        $.each(allSelectedVideos, function(index, li) {
            var video = $(li).data('model');
            if (video) {
                videos.push(video);
            }
        });
    }

    if (!$(li).hasClass('selected')) {
        li.parent().find('.selected').removeClass('selected');
        li.addClass('selected');
    }

    var buttons = [
        {
            title: TranslationSystem.get('Play'),
            cssClass: 'play',
            args: li,
            callback: function(elem) {
                elem.data('model').play();
            }
        },
		{
            title: TranslationSystem.get('Queue'),
            cssClass: 'queue',
            args: allSelectedVideos,
            callback: function(allSelectedVideos) {
                $.each(allSelectedVideos, function(index, li) {
                    var model = $(li).data('model');
                    if (model) {
                        Queue.addManual(model);
                    }
                });
            }
        },
		{
            title: TranslationSystem.get('Add to playlist'),
            cssClass: 'add-to-playlist',
            args: allSelectedVideos,
            callback: function() {
                new AddToPlaylistDialog(videos).show();
            }
        },
        {
			title: TranslationSystem.get('Share'),
            cssClass: 'share',
			args: li,
			callback: function(elem) {
                var video = $(elem).data('model');
                new ShareTrackDialog(video).show();
			}
		},
		{
			title: video.getExternalLink().label,
            cssClass: 'view',
			args: li,
			callback: function(elem) {
                var video = $(elem).data('model');
                window.open(video.getExternalLink().url);
			}
		}
    ];

    if (ON_DEV) {
        buttons.push({
            title: 'View JSON',
            cssClass: 'json',
            args: li,
            callback: function(li) {
                alert(JSON.stringify(video.toJSON()));
                console.log(video.toJSON());
            }
        });
    }

    if (li.data('additionalMenuButtons')) {
        buttons = $.merge(buttons, li.data('additionalMenuButtons'));
    }

    showContextMenu(buttons, (event.pageX || li.offset().left + li.width()) , (event.pageY || li.offset().top));

    return false;
}
<|endoftext|>