diff --git a/connect.py b/connect.py index b815d8898..760f484a0 100755 --- a/connect.py +++ b/connect.py @@ -3,6 +3,7 @@ import socket import sys import os +import ast import select import time from datetime import datetime @@ -24,8 +25,7 @@ django.setup() from juser.models import User from jasset.models import Asset from jlog.models import Log -from jumpserver.api import user_perm_asset_api, PyCrypt, BASE_DIR, CONF, CRYPTOR, KEY - +from jumpserver.api import * try: import termios import tty @@ -98,7 +98,13 @@ def log_record(username, host): today_connect_log_dir = os.path.join(connect_log_dir, today) log_filename = '%s_%s_%s.log' % (username, host, time_now) log_file_path = os.path.join(today_connect_log_dir, log_filename) + dept_name = User.objects.get(username=username).dept pid = os.getpid() + ip_list = [] + remote_ip = os.popen("who |grep `ps aux |gawk '{if ($2==%s) print $1}'` |gawk '{print $5}'|tr -d '()'" % pid).readlines() + for ip in remote_ip: + ip_list.append(ip.strip('\n')) + ip_list = ','.join(list(set(ip_list))) if not os.path.isdir(today_connect_log_dir): try: @@ -112,7 +118,7 @@ def log_record(username, host): except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=username, host=host, log_path=log_file_path, start_time=datetime.now(), pid=pid) + log = Log(user=username, host=host, remote_ip=ip_list, dept_name=dept_name, log_path=log_file_path, start_time=datetime.now(), pid=pid) log_file.write('Starttime is %s\n' % datetime.now()) log.save() return log_file, log @@ -173,6 +179,15 @@ def get_user_host(username): return hosts_attr +def get_user_hostgroup(username): + """Get the hostgroups of under the user control.""" + groups_attr = {} + group_all = user_perm_group_api(username) + for group in group_all: + groups_attr[group.name] = [group.id, group.comment] + return groups_attr + + def get_connect_item(username, ip): asset = get_object(Asset, ip=ip) @@ -222,8 +237,9 @@ def print_prompt(): msg = """\033[1;32m### Welcome Use JumpServer To Login. ### \033[0m 1) Type \033[32mIP ADDRESS\033[0m To Login. 2) Type \033[32mP/p\033[0m To Print The Servers You Available. - 3) Type \033[32mE/e\033[0m To Execute Command On Several Servers. - 4) Type \033[32mQ/q\033[0m To Quit. + 3) Type \033[32mG/g\033[0m To Print The Server Groups You Available. + 4) Type \033[32mE/e\033[0m To Execute Command On Several Servers. + 5) Type \033[32mQ/q\033[0m To Quit. """ print textwrap.dedent(msg) @@ -236,6 +252,13 @@ def print_user_host(username): print '%s -- %s' % (ip, hosts_attr[ip][1]) +def print_user_hostgroup(username): + group_attr = get_user_hostgroup(username) + groups = group_attr.keys() + for g in groups: + print '%s -- %s' % (g, group_attr[g][1]) + + def connect(username, password, host, port, login_name): """ Connect server. @@ -351,6 +374,9 @@ if __name__ == '__main__': if option in ['P', 'p']: print_user_host(LOGIN_NAME) continue + elif option in ['G', 'g']: + print_user_hostgroup(LOGIN_NAME) + continue elif option in ['E', 'e']: exec_cmd_servers(LOGIN_NAME) elif option in ['Q', 'q']: diff --git a/docs/AddUserAsset.py b/docs/AddUserAsset.py index 77de5371a..f8faea53a 100644 --- a/docs/AddUserAsset.py +++ b/docs/AddUserAsset.py @@ -13,7 +13,6 @@ django.setup() from juser.views import db_add_user, md5_crypt, CRYPTOR, db_add_group from jasset.models import Asset, IDC, BisGroup from juser.models import UserGroup, DEPT, User -from jasset.views import jasset_group_add from jperm.models import CmdGroup from jlog.models import Log @@ -123,18 +122,18 @@ def test_add_log(): end_time = datetime.datetime.now() log_path = '/var/log/jumpserver/test.log' host = '192.168.1.' + str(ip) - Log.objects.create(user=user, host=host, log_path=log_path, pid=168, start_time=start_time, + Log.objects.create(user=user, host=host, remote_ip='8.8.8.8', dept_name='运维部', log_path=log_path, pid=168, start_time=start_time, is_finished=1, log_finished=1, end_time=end_time) if __name__ == '__main__': - install() - test_add_dept() - test_add_group() - test_add_user() - test_add_idc() - test_add_asset_group() - test_add_asset() + #install() + #test_add_dept() + #test_add_group() + #test_add_user() + #test_add_idc() + #test_add_asset_group() + #test_add_asset() test_add_log() diff --git a/docs/install.py b/docs/install.py new file mode 100644 index 000000000..76a7da522 --- /dev/null +++ b/docs/install.py @@ -0,0 +1,135 @@ +#coding:utf-8 +import django +import os +import sys +import random +import datetime + +sys.path.append('../') +os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' +django.setup() + + +from juser.views import db_add_user, md5_crypt, CRYPTOR, db_add_group +from jasset.models import Asset, IDC, BisGroup +from juser.models import UserGroup, DEPT, User +from jasset.views import jasset_group_add +from jperm.models import CmdGroup +from jlog.models import Log + + +def install(): + IDC.objects.create(name='ALL', comment='ALL') + IDC.objects.create(name='默认', comment='默认') + DEPT.objects.create(name="默认", comment="默认部门") + DEPT.objects.create(name="超管部", comment="超级管理员部门") + dept = DEPT.objects.get(name='超管部') + dept2 = DEPT.objects.get(name='默认') + UserGroup.objects.create(name='ALL', dept=dept, comment='ALL') + UserGroup.objects.create(name='默认', dept=dept, comment='默认') + + BisGroup.objects.create(name='ALL', dept=dept, comment='ALL') + BisGroup.objects.create(name='默认', dept=dept, comment='默认') + + User(id=5000, username="admin", password=md5_crypt('admin'), + name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save() + User(id=5001, username="group_admin", password=md5_crypt('group_admin'), + name='group_admin', email='group_admin@jumpserver.org', role='DA', is_active=True, dept=dept2).save() + + +def test_add_idc(): + for i in range(1, 20): + name = 'IDC' + str(i) + IDC.objects.create(name=name, comment='') + print 'Add: %s' % name + + +def test_add_dept(): + for i in range(1, 100): + name = 'DEPT' + str(i) + print "Add: %s" % name + DEPT.objects.create(name=name, comment=name) + + +def test_add_group(): + dept_all = DEPT.objects.all() + for i in range(1, 100): + name = 'UserGroup' + str(i) + UserGroup.objects.create(name=name, dept=random.choice(dept_all), comment=name) + print 'Add: %s' % name + + +def test_add_cmd_group(): + for i in range(1, 20): + name = 'CMD' + str(i) + cmd = '/sbin/ping%s, /sbin/ifconfig/' % str(i) + CmdGroup.objects.create(name=name, cmd=cmd, comment=name) + print 'Add: %s' % name + + +def test_add_user(): + for i in range(1, 500): + username = "test" + str(i) + dept_all = DEPT.objects.all() + group_all = UserGroup.objects.all() + group_all_id = [group.id for group in group_all] + db_add_user(username=username, + password=md5_crypt(username), + dept=random.choice(dept_all), + name=username, email='%s@jumpserver.org' % username, + groups=[random.choice(group_all_id) for i in range(1, 4)], role='CU', + ssh_key_pwd=CRYPTOR.encrypt(username), + ldap_pwd=CRYPTOR.encrypt(username), + is_active=True, + date_joined=datetime.datetime.now()) + print "Add: %s" % username + + +def test_add_asset_group(): + dept = DEPT.objects.get(name='默认') + for i in range(1, 20): + name = 'AssetGroup' + str(i) + group = BisGroup(name=name, dept=dept, comment=name) + group.save() + print 'Add: %s' % name + + +def test_add_asset(): + idc_all = IDC.objects.all() + test_idc = random.choice(idc_all) + bis_group_all = BisGroup.objects.all() + dept_all = DEPT.objects.all() + for i in range(1, 500): + ip = '192.168.1.' + str(i) + asset = Asset(ip=ip, port=22, login_type='L', idc=test_idc, is_active=True, comment='test') + asset.save() + asset.bis_group = [random.choice(bis_group_all) for i in range(2)] + asset.dept = [random.choice(dept_all) for i in range(2)] + print "Add: %s" % ip + + +def test_add_log(): + li_date = [] + today = datetime.date.today() + oneday = datetime.timedelta(days=1) + for i in range(0, 7): + today = today-oneday + li_date.append(today) + user_list = ['马云', '马化腾', '丁磊', '周鸿祎', '雷军', '柳传志', '陈天桥', '李彦宏', '李开复', '罗永浩'] + for i in range(1, 1000): + user = random.choice(user_list) + ip = random.randint(1, 20) + start_time = random.choice(li_date) + end_time = datetime.datetime.now() + log_path = '/var/log/jumpserver/test.log' + host = '192.168.1.' + str(ip) + Log.objects.create(user=user, host=host, log_path=log_path, pid=168, start_time=start_time, + is_finished=1, log_finished=1, end_time=end_time) + + +if __name__ == '__main__': + install() + + + + diff --git a/jasset/views.py b/jasset/views.py index e399d8067..79c415cb3 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -1,5 +1,7 @@ # coding:utf-8 +import ast + from django.db.models import Q from django.http import HttpResponseRedirect from django.template import RequestContext @@ -9,8 +11,10 @@ from models import IDC, Asset, BisGroup from juser.models import UserGroup, DEPT from connect import PyCrypt, KEY from jlog.models import Log -from jumpserver.views import jasset_group_add, jasset_host_edit, pages +from jumpserver.views import jasset_host_edit, pages from jumpserver.api import asset_perm_api +from jumpserver.api import user_perm_group_api, require_login, require_super_user, \ + require_admin, is_group_admin, is_super_user, is_common_user, get_user_dept cryptor = PyCrypt(KEY) @@ -37,17 +41,13 @@ def f_add_host(ip, port, idc, jtype, group, dept, active, comment, username='', comment=comment) a.save() - jasset_group_add(ip, ip, 'P') all_group = BisGroup.objects.get(name='ALL') - private_group = BisGroup.objects.get(name=ip.strip()) for g in group: c = BisGroup.objects.get(name=g) groups.append(c) - groups.extend([all_group, private_group]) + groups.append(all_group) - print dept for d in dept: - print d p = DEPT.objects.get(name=d) depts.append(p) @@ -56,14 +56,20 @@ def f_add_host(ip, port, idc, jtype, group, dept, active, comment, username='', a.save() +@require_admin def add_host(request): - login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} + login_types = {'L': 'LDAP', 'M': 'MAP'} header_title, path1, path2 = u'添加主机', u'资产管理', u'添加主机' - eidc = IDC.objects.all() - edept = DEPT.objects.all() - egroup = BisGroup.objects.filter(type='A') - eusergroup = UserGroup.objects.all() - + eidc = IDC.objects.exclude(name='ALL') + if is_super_user(request): + edept = DEPT.objects.all() + egroup = BisGroup.objects.exclude(name='ALL') + eusergroup = UserGroup.objects.all() + elif is_group_admin(request): + dept_id = get_user_dept(request) + user_id = request.session.get('user_id') + edept = DEPT.objects.get(id=dept_id) + egroup = edept.bisgroup_set.all() if request.method == 'POST': j_ip = request.POST.get('j_ip') j_idc = request.POST.get('j_idc') @@ -89,21 +95,20 @@ def add_host(request): return render_to_response('jasset/host_add.html', locals(), context_instance=RequestContext(request)) +@require_admin def add_host_multi(request): header_title, path1, path2 = u'批量添加主机', u'资产管理', u'批量添加主机' - login_types = {'LDAP': 'L', 'SSH_KEY': 'S', 'PASSWORD': 'P', 'MAP': 'M'} - j_group = [] + login_types = {'LDAP': 'L', 'MAP': 'M'} + dept_id = get_user_dept(request) if request.method == 'POST': multi_hosts = request.POST.get('j_multi').split('\n') for host in multi_hosts: if host == '': break - j_ip, j_port, j_type, j_idc, j_groups, j_active, j_comment = host.split() + j_ip, j_port, j_type, j_idc, j_groups, j_depts, j_active, j_comment = host.split() j_type = login_types[j_type] - j_groups = j_groups.split(',') - for group in j_groups: - g = group.strip('[]').encode('utf-8').strip() - j_group.append(g) + j_group = ast.literal_eval(j_groups) + j_dept = ast.literal_eval(j_depts) if Asset.objects.filter(ip=str(j_ip)): emg = u'该IP %s 已存在!' % j_ip @@ -113,9 +118,9 @@ def add_host_multi(request): if j_type == 'M': j_user = request.POST.get('j_user') j_password = cryptor.encrypt(request.POST.get('j_password')) - f_add_host(j_ip, j_port, j_idc, j_type, j_group, j_active, j_comment, j_user, j_password) + f_add_host(j_ip, j_port, j_idc, j_type, j_group, j_dept, j_active, j_comment, j_user, j_password) else: - f_add_host(j_ip, j_port, j_idc, j_type, j_group, j_active, j_comment) + f_add_host(j_ip, j_port, j_idc, j_type, j_group, j_dept, j_active, j_comment) smg = u'批量添加添加成功' return HttpResponseRedirect('/jasset/host_list/') @@ -123,6 +128,7 @@ def add_host_multi(request): return render_to_response('jasset/host_add_multi.html', locals(), context_instance=RequestContext(request)) +@require_admin def batch_host_edit(request): if request.method == 'POST': len_table = request.POST.get('len_table') @@ -158,22 +164,34 @@ def batch_host_edit(request): return render_to_response('jasset/host_list.html') +@require_login def list_host(request): header_title, path1, path2 = u'查看主机', u'资产管理', u'查看主机' - login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} + login_types = {'L': 'LDAP', 'M': 'MAP'} keyword = request.GET.get('keyword', '') - if keyword: - posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | - Q(bis_group__name__contains=keyword) | Q(comment__contains=keyword)).distinct().order_by('ip') - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - - else: - posts = Asset.objects.all().order_by('ip') + dept_id = get_user_dept(request) + dept = DEPT.objects.get(id=dept_id) + if is_super_user(request): + if keyword: + posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | + Q(bis_group__name__contains=keyword) | Q(comment__contains=keyword)).distinct().order_by('ip') + else: + posts = Asset.objects.all().order_by('ip') + contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + elif is_group_admin(request): + if keyword: + posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | + Q(bis_group__name__contains=keyword) | Q(comment__contains=keyword)).filter(dept=dept).distinct().order_by('ip') + else: + posts = Asset.objects.all().filter(dept=dept).order_by('ip') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + elif is_common_user(request): + pass return render_to_response('jasset/host_list.html', locals(), context_instance=RequestContext(request)) +@require_admin def host_del(request, offset): if offset == 'multi': len_list = request.POST.get("len_list") @@ -191,13 +209,14 @@ def host_del(request, offset): return HttpResponseRedirect('/jasset/host_list/') +@require_admin def host_edit(request): actives = {1: u'激活', 0: u'禁用'} login_types = {'L': 'LDAP', 'M': 'MAP'} header_title, path1, path2 = u'修改主机', u'资产管理', u'修改主机' groups, e_group, e_dept, depts = [], [], [], [] eidc = IDC.objects.all() - egroup = BisGroup.objects.filter(type='A') + egroup = BisGroup.objects.all() edept = DEPT.objects.all() offset = request.GET.get('id') for g in Asset.objects.get(id=int(offset)).bis_group.all(): @@ -257,6 +276,7 @@ def host_edit(request): return render_to_response('jasset/host_edit.html', locals(), context_instance=RequestContext(request)) +@require_login def jlist_ip(request, offset): header_title, path1, path2 = u'主机详细信息', u'资产管理', u'主机详情' login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} @@ -266,6 +286,7 @@ def jlist_ip(request, offset): return render_to_response('jasset/jlist_ip.html', locals(), context_instance=RequestContext(request)) +@require_super_user def add_idc(request): header_title, path1, path2 = u'添加IDC', u'资产管理', u'添加IDC' if request.method == 'POST': @@ -281,6 +302,7 @@ def add_idc(request): return render_to_response('jasset/idc_add.html', locals(), context_instance=RequestContext(request)) +@require_admin def list_idc(request): header_title, path1, path2 = u'查看IDC', u'资产管理', u'查看IDC' keyword = request.GET.get('keyword', '') @@ -292,6 +314,7 @@ def list_idc(request): return render_to_response('jasset/idc_list.html', locals(), context_instance=RequestContext(request)) +@require_super_user def edit_idc(request): header_title, path1, path2 = u'编辑IDC', u'资产管理', u'编辑IDC' edit = 1 @@ -320,6 +343,7 @@ def edit_idc(request): return render_to_response('jasset/idc_add.html', locals(), context_instance=RequestContext(request)) +@require_super_user def del_idc(request, offset): if offset == 'multi': len_list = request.POST.get("len_list") @@ -333,10 +357,17 @@ def del_idc(request, offset): return HttpResponseRedirect('/jasset/idc_list/') +@require_admin def add_group(request): header_title, path1, path2 = u'添加主机组', u'资产管理', u'添加主机组' - posts = Asset.objects.all() - edept = DEPT.objects.all() + if is_super_user(request): + posts = Asset.objects.all() + edept = DEPT.objects.all() + elif is_group_admin(request): + dept_id = get_user_dept(request) + dept = DEPT.objects.get(id=dept_id) + posts = Asset.objects.filter(dept=dept) + edept = DEPT.objects.get(id=dept_id) if request.method == 'POST': j_group = request.POST.get('j_group') j_dept = request.POST.get('j_dept') @@ -348,9 +379,8 @@ def add_group(request): emg = u'该主机组已存在!' return render_to_response('jasset/group_add.html', locals(), context_instance=RequestContext(request)) else: - BisGroup.objects.create(name=j_group, comment=j_comment, type='A') + BisGroup.objects.create(name=j_group, dept=j_dept, comment=j_comment) group = BisGroup.objects.get(name=j_group) - group.dept = j_dept for host in j_hosts: g = Asset.objects.get(id=host) group.asset_set.add(g) @@ -359,25 +389,44 @@ def add_group(request): return render_to_response('jasset/group_add.html', locals(), context_instance=RequestContext(request)) +@require_admin def list_group(request): header_title, path1, path2 = u'查看主机组', u'资产管理', u'查看主机组' + dept_id = get_user_dept(request) + dept = DEPT.objects.get(id=dept_id) keyword = request.GET.get('keyword', '') - if keyword: - posts = BisGroup.objects.filter(Q(name__contains=keyword) | Q(comment__contains=keyword)) - else: - posts = BisGroup.objects.all().order_by('id') + if is_super_user(request): + if keyword: + posts = BisGroup.objects.exclude(name='ALL').filter(Q(name__contains=keyword) | Q(comment__contains=keyword)) + else: + posts = BisGroup.objects.exclude(name='ALL').order_by('id') + elif is_group_admin(request): + if keyword: + posts = BisGroup.objects.filter(Q(name__contains=keyword) | Q(comment__contains=keyword)).filter(dept=dept) + else: + posts = BisGroup.objects.filter(dept=dept).order_by('id') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jasset/group_list.html', locals(), context_instance=RequestContext(request)) +@require_admin def edit_group(request): header_title, path1, path2 = u'编辑主机组', u'资产管理', u'编辑主机组' group_id = request.GET.get('id') group = BisGroup.objects.get(id=group_id) all = Asset.objects.all() + dept_id = get_user_dept(request) eposts = contact_list = Asset.objects.filter(bis_group=group).order_by('ip') - posts = [g for g in all if g not in eposts] + + if is_super_user(request): + edept = DEPT.objects.all() + posts = [g for g in all if g not in eposts] + + elif is_group_admin(request): + dept = DEPT.objects.get(id=dept_id) + all_dept = Asset.objects.filter(dept=dept) + posts = [g for g in all_dept if g not in eposts] + if request.method == 'POST': j_group = request.POST.get('j_group') j_hosts = request.POST.getlist('j_hosts') @@ -394,30 +443,43 @@ def edit_group(request): return render_to_response('jasset/group_add.html', locals(), context_instance=RequestContext(request)) +@require_admin def detail_group(request): header_title, path1, path2 = u'主机组详情', u'资产管理', u'主机组详情' login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} + dept_id = get_user_dept(request) + dept = DEPT.objects.get(id=dept_id) group_id = request.GET.get('id') group_name = BisGroup.objects.get(id=group_id).name b = BisGroup.objects.get(id=group_id) - posts = Asset.objects.filter(bis_group=b).order_by('ip') - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + if is_super_user(request): + posts = Asset.objects.filter(bis_group=b).order_by('ip') + contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + elif is_group_admin(request): + posts = Asset.objects.filter(bis_group=b).filter(dept=dept).order_by('ip') + contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) return render_to_response('jasset/group_detail.html', locals(), context_instance=RequestContext(request)) def detail_idc(request): header_title, path1, path2 = u'IDC详情', u'资产管理', u'IDC详情' - login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} + login_types = {'L': 'LDAP', 'M': 'MAP'} idc_id = request.GET.get('id') idc_name = IDC.objects.get(id=idc_id).name b = IDC.objects.get(id=idc_id) - posts = Asset.objects.filter(idc=b).order_by('ip') + dept_id = get_user_dept(request) + dept = DEPT.objects.get(id=dept_id) + if is_super_user(request): + posts = Asset.objects.filter(idc=b).order_by('ip') + elif is_group_admin(request): + posts = Asset.objects.filter(idc=b).filter(dept=dept).order_by('ip') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) return render_to_response('jasset/idc_detail.html', locals(), context_instance=RequestContext(request)) +@require_admin def group_del_host(request, offset): if request.method == 'POST': group_name = request.POST.get('group_name') @@ -439,6 +501,7 @@ def group_del_host(request, offset): return HttpResponseRedirect('/jasset/%s_detail/?id=%s' % (offset, group.id)) +@require_admin def group_del(request, offset): if offset == 'multi': len_list = request.POST.get("len_list") @@ -454,10 +517,17 @@ def group_del(request, offset): def host_search(request): keyword = request.GET.get('keyword') - login_types = {'L': 'LDAP', 'S': 'SSH_KEY', 'P': 'PASSWORD', 'M': 'MAP'} - posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | - Q(bis_group__name__contains=keyword) | Q( - comment__contains=keyword)).distinct().order_by('ip') + login_types = {'L': 'LDAP', 'M': 'MAP'} + dept_id = get_user_dept(request) + dept = DEPT.objects.get(id=dept_id) + if is_super_user(request): + posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | + Q(bis_group__name__contains=keyword) | Q( + comment__contains=keyword)).distinct().order_by('ip') + elif is_group_admin(request): + posts = Asset.objects.filter(Q(ip__contains=keyword) | Q(idc__name__contains=keyword) | + Q(bis_group__name__contains=keyword) | Q( + comment__contains=keyword)).filter(dept=dept).distinct().order_by('ip') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) return render_to_response('jasset/host_search.html', locals(), context_instance=RequestContext(request)) diff --git a/jlog/models.py b/jlog/models.py index 3bb861569..0e22c5d07 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -4,6 +4,8 @@ from django.db import models class Log(models.Model): user = models.CharField(max_length=20, null=True) host = models.CharField(max_length=20, null=True) + remote_ip = models.CharField(max_length=100) + dept_name = models.CharField(max_length=20) log_path = models.CharField(max_length=100) start_time = models.DateTimeField(null=True) pid = models.IntegerField(max_length=10) diff --git a/jlog/views.py b/jlog/views.py index f6b3744aa..59a448b93 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -5,12 +5,15 @@ from datetime import datetime from django.db.models import Q from django.http import HttpResponse +from django.template import RequestContext from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from connect import BASE_DIR from jlog.models import Log from jumpserver.views import pages +from juser.models import User, DEPT +from jumpserver.api import get_user_dept, is_super_user, is_group_admin, is_common_user CONF = ConfigParser.ConfigParser() CONF.read('%s/jumpserver.conf' % BASE_DIR) @@ -20,30 +23,61 @@ def log_list_online(request): header_title, path1, path2 = u'查看日志', u'查看日志', u'在线用户' keyword = request.GET.get('keyword') web_socket_host = CONF.get('websocket', 'web_socket_host') - if keyword: - posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ - .filter(is_finished=0).order_by('-start_time') - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - else: - posts = Log.objects.filter(is_finished=0).order_by('-start_time') + dept_id = get_user_dept(request) + dept_name = DEPT.objects.get(id=dept_id).name + user_id = request.session.get('user_id') + username = User.objects.get(id=user_id).username + + if is_super_user(request): + if keyword: + posts = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=0).order_by('-start_time') + else: + posts = Log.objects.filter(is_finished=0).order_by('-start_time') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jlog/log_online.html', locals()) + elif is_group_admin(request): + if keyword: + posts = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=0).filter(dept_name=dept_name).order_by('-start_time') + else: + posts = Log.objects.filter(is_finished=0).filter(dept_name=dept_name).order_by('-start_time') + contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + + elif is_common_user(request): + posts = Log.objects.filter(is_finished=0).filter(user=username).order_by('-start_time') + + return render_to_response('jlog/log_online.html', locals(), context_instance=RequestContext(request)) def log_list_offline(request): header_title, path1, path2 = u'查看日志', u'查看日志', u'历史记录' keyword = request.GET.get('keyword') web_socket_host = CONF.get('websocket', 'web_socket_host') - if keyword: - posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ - .filter(is_finished=1).order_by('-start_time') - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - else: - posts = Log.objects.filter(is_finished=1).order_by('-start_time') + dept_id = get_user_dept(request) + dept_name = DEPT.objects.get(id=dept_id).name + user_id = request.session.get('user_id') + username = User.objects.get(id=user_id).username + if is_super_user(request): + if keyword: + posts = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=1).order_by('-start_time') + else: + posts = Log.objects.filter(is_finished=1).order_by('-start_time') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jlog/log_offline.html', locals()) + elif is_group_admin(request): + if keyword: + posts = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=1).filter(dept_name=dept_name).order_by('-start_time') + else: + posts = Log.objects.filter(is_finished=1).filter(dept_name=dept_name).order_by('-start_time') + contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + + elif is_common_user(request): + posts = Log.objects.filter(is_finished=1).filter(user=username).order_by('-start_time') + + return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) def log_kill(request, offset): @@ -51,7 +85,7 @@ def log_kill(request, offset): if pid: os.kill(int(pid), 9) Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.now()) - return HttpResponseRedirect('jlog/log_offline.html', locals()) + return HttpResponseRedirect('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) def log_history(request): @@ -69,13 +103,23 @@ def log_history(request): def log_search(request): keyword = request.GET.get('keyword') env = request.GET.get('env') - if env == 'online': - posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ - .filter(is_finished=0).order_by('-start_time') - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - elif env == 'offline': - posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ - .filter(is_finished=1).order_by('-start_time') + dept_id = get_user_dept(request) + dept_name = DEPT.objects.get(id=dept_id).name + if is_super_user(request): + if env == 'online': + posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=0).order_by('-start_time') + elif env == 'offline': + posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=1).order_by('-start_time') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jlog/log_search.html', locals()) + elif is_group_admin(request): + if env == 'online': + posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=0).filter(dept_name=dept_name).order_by('-start_time') + elif env == 'offline': + posts = contact_list = Log.objects.filter(Q(user__contains=keyword) | Q(host__contains=keyword)) \ + .filter(is_finished=1).filter(dept_name=dept_name).order_by('-start_time') + contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) + return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request)) diff --git a/jumpserver/api.py b/jumpserver/api.py index 70ba2b507..3034e8f52 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -29,6 +29,15 @@ LOGIN_NAME = getpass.getuser() LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') +# def user_perm_group_api(username): +# user = User.objects.get(username=username) +# if user: +# perm_list = [] +# user_group_all = user.group.all() +# for user_group in user_group_all: +# perm_list.extend(user_group.perm_set.all()) + + class LDAPMgmt(): def __init__(self, host_url, @@ -177,12 +186,27 @@ def is_super_user(request): def is_group_admin(request): + print request.session.get('role_id'), type(request.session.get('role_id')) if request.session.get('role_id') == 1: return True else: return False +def is_common_user(request): + if request.session.get('role_id') == 0: + return True + else: + return False + + +def get_user_dept(request): + user_id = request.session.get('user_id') + if user_id: + user_dept = User.objects.get(id=user_id).dept + return user_dept.id + + def api_user(request): hosts = Log.objects.filter(is_finished=0).count() users = Log.objects.filter(is_finished=0).values('user').distinct().count() diff --git a/jumpserver/context_processors.py b/jumpserver/context_processors.py index 160cdd8c0..0d8308ff4 100644 --- a/jumpserver/context_processors.py +++ b/jumpserver/context_processors.py @@ -1,4 +1,5 @@ from juser.models import User +from jasset.models import Asset def name_proc(request): @@ -6,8 +7,11 @@ def name_proc(request): role_id = request.session.get('role_id') user_total_num = User.objects.all().count() user_active_num = User.objects.filter(is_active=True).count() + host_total_num = Asset.objects.all().count() + host_active_num = Asset.objects.filter(is_active=True).count() request.session.set_expiry(3600) return {'session_user_id': user_id, 'session_role_id': role_id, - 'user_total_num': user_total_num, 'user_active_num': user_active_num} + 'user_total_num': user_total_num, 'user_active_num': user_active_num, + 'host_total_num': host_total_num, 'host_active_num': host_active_num} diff --git a/jumpserver/ssh.py b/jumpserver/ssh.py new file mode 100644 index 000000000..917d0a436 --- /dev/null +++ b/jumpserver/ssh.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python + +import socket +import sys +import os +import ast +import select +import time +from datetime import datetime +import paramiko +import struct +import fcntl +import signal +import textwrap +import django +import getpass +import fnmatch +import optparse +import readline +from multiprocessing import Pool +from ConfigParser import ConfigParser +from django.core.exceptions import ObjectDoesNotExist + +os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' +django.setup() +from juser.models import User +from jasset.models import Asset +from jlog.models import Log +from jumpserver.views import PyCrypt +from jumpserver.api import user_perm_asset_api, user_perm_group_api + +try: + import termios + import tty +except ImportError: + print '\033[1;31mOnly postfix supported.\033[0m' + time.sleep(3) + sys.exit() + +CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) +BASE_DIR = os.path.dirname(CURRENT_DIR) +CONF = ConfigParser() +CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) +LOG_DIR = os.path.join(BASE_DIR, 'logs') +SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') +SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') +KEY = CONF.get('web', 'key') +LOGIN_NAME = getpass.getuser() + + +def color_print(msg, color='blue'): + """Print colorful string.""" + color_msg = {'blue': '\033[1;36m%s\033[0m', + 'green': '\033[1;32m%s\033[0m', + 'red': '\033[1;31m%s\033[0m'} + + print color_msg.get(color, 'blue') % msg + + +def color_print_exit(msg, color='red'): + """Print colorful string and exit.""" + color_print(msg, color=color) + time.sleep(2) + sys.exit() + + +class ServerError(Exception): + pass + + +def get_win_size(): + """This function use to get the size of the windows!""" + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912L # Assume + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + +def set_win_size(sig, data): + """This function use to set the window size of the terminal!""" + try: + win_size = get_win_size() + channel.resize_pty(height=win_size[0], width=win_size[1]) + except: + pass + + +def get_object(model, **kwargs): + try: + the_object = model.objects.get(**kwargs) + except ObjectDoesNotExist: + raise ServerError('Object get %s failed.' % str(kwargs.values())) + return the_object + + +def log_record(username, host): + """Logging user command and output.""" + connect_log_dir = os.path.join(LOG_DIR, 'connect') + timestamp_start = int(time.time()) + today = time.strftime('%Y%m%d', time.localtime(timestamp_start)) + time_now = time.strftime('%H%M%S', time.localtime(timestamp_start)) + today_connect_log_dir = os.path.join(connect_log_dir, today) + log_filename = '%s_%s_%s.log' % (username, host, time_now) + log_file_path = os.path.join(today_connect_log_dir, log_filename) + pid = os.getpid() + ip_list = [] + remote_ip = os.popen("who |grep `ps aux |gawk '{if ($2==%s) print $1}'` |gawk '{print $5}'|tr -d '()'" % pid).readlines() + for ip in remote_ip: + ip_list.append(ip.strip('\n')) + ip_list = ','.join(list(set(ip_list))) + + if not os.path.isdir(today_connect_log_dir): + try: + os.makedirs(today_connect_log_dir) + os.chmod(today_connect_log_dir, 0777) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, connect_log_dir)) + + try: + log_file = open(log_file_path, 'a') + except IOError: + raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) + + log = Log(user=username, host=host, remote_ip=ip_list, log_path=log_file_path, start_time=datetime.now(), pid=pid) + log_file.write('Starttime is %s\n' % datetime.now()) + log.save() + return log_file, log + + +def posix_shell(chan, username, host): + """ + Use paramiko channel connect server interactive. + """ + log_file, log = log_record(username, host) + old_tty = termios.tcgetattr(sys.stdin) + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + chan.settimeout(0.0) + + while True: + try: + r, w, e = select.select([chan, sys.stdin], [], []) + except: + pass + + if chan in r: + try: + x = chan.recv(1024) + if len(x) == 0: + break + sys.stdout.write(x) + sys.stdout.flush() + log_file.write(x) + log_file.flush() + except socket.timeout: + pass + + if sys.stdin in r: + x = os.read(sys.stdin.fileno(), 1) + if len(x) == 0: + break + chan.send(x) + + finally: + timestamp_end = time.time() + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + log_file.write('Endtime is %s' % datetime.now()) + log_file.close() + log.is_finished = True + log.log_finished = False + log.end_time = datetime.now() + log.save() + + +def get_user_host(username): + """Get the hosts of under the user control.""" + hosts_attr = {} + asset_all = user_perm_asset_api(username) + for asset in asset_all: + hosts_attr[asset.ip] = [asset.id, asset.comment] + return hosts_attr + + +def get_user_hostgroup(username): + """Get the hostgroups of under the user control.""" + groups_attr = {} + group_all = user_perm_group_api(username) + for group in group_all: + groups_attr[group.name] = [group.id, group.comment] + return groups_attr + + +def get_connect_item(username, ip): + cryptor = PyCrypt(KEY) + + asset = get_object(Asset, ip=ip) + port = asset.port + + if not asset.is_active: + raise ServerError('Host %s is not active.' % ip) + + user = get_object(User, username=username) + + if not user.is_active: + raise ServerError('User %s is not active.' % username) + + login_type_dict = { + 'L': user.ldap_pwd, + } + + if asset.login_type in login_type_dict: + password = cryptor.decrypt(login_type_dict[asset.login_type]) + return username, password, ip, port + + elif asset.login_type == 'M': + username = asset.username + password = cryptor.decrypt(asset.password) + return username, password, ip, port + + else: + raise ServerError('Login type is not in ["L", "M"]') + + +def verify_connect(username, part_ip): + hosts_attr = get_user_host(username) + hosts = hosts_attr.keys() + ip_matched = [ip for ip in hosts if part_ip in ip] + + if len(ip_matched) > 1: + for ip in ip_matched: + print '%s -- %s' % (ip, hosts_attr[ip][1]) + elif len(ip_matched) < 1: + color_print('No Permission or No host.', 'red') + else: + username, password, host, port = get_connect_item(username, ip_matched[0]) + print username, password, host, port + connect(username, password, host, port, LOGIN_NAME) + + +def print_prompt(): + msg = """\033[1;32m### Welcome Use JumpServer To Login. ### \033[0m + 1) Type \033[32mIP ADDRESS\033[0m To Login. + 2) Type \033[32mP/p\033[0m To Print The Servers You Available. + 3) Type \033[32mG/g\033[0m To Print The Server Groups You Available. + 4) Type \033[32mE/e\033[0m To Execute Command On Several Servers. + 5) Type \033[32mQ/q\033[0m To Quit. + """ + print textwrap.dedent(msg) + + +def print_user_host(username): + hosts_attr = get_user_host(username) + hosts = hosts_attr.keys() + hosts.sort() + for ip in hosts: + print '%s -- %s' % (ip, hosts_attr[ip][1]) + + +def print_user_hostgroup(username): + group_attr = get_user_hostgroup(username) + groups = group_attr.keys() + for g in groups: + print '%s -- %s' % (g, group_attr[g][1]) + + +def connect(username, password, host, port, login_name): + """ + Connect server. + """ + ps1 = "PS1='[\u@%s \W]\$ '\n" % host + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % host + + # Make a ssh connection + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(host, port=port, username=username, password=password, compress=True) + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + raise ServerError('Authentication Error.') + except socket.error: + raise ServerError('Connect SSH Socket Port Error, Please Correct it.') + + # Make a channel and set windows size + global channel + win_size = get_win_size() + channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + #channel.resize_pty(height=win_size[0], width=win_size[1]) + try: + signal.signal(signal.SIGWINCH, set_win_size) + except: + pass + + # Set PS1 and msg it + channel.send(ps1) + channel.send(login_msg) + + # Make ssh interactive tunnel + posix_shell(channel, login_name, host) + + # Shutdown channel socket + channel.close() + ssh.close() + + +def remote_exec_cmd(ip, port, username, password, cmd): + try: + time.sleep(5) + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(ip, port, username, password, timeout=5) + stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) + out = stdout.readlines() + err = stderr.readlines() + color_print('%s:' %ip, 'blue') + for i in out: + color_print(" " * 4 + i.strip(), 'green') + for j in err: + color_print(" " * 4 + j.strip(), 'red') + ssh.close() + except Exception as e: + color_print(ip + ':', 'blue') + color_print(str(e), 'red') + + +def multi_remote_exec_cmd(hosts, username, cmd): + pool = Pool(processes=5) + for host in hosts: + username, password, ip, port = get_connect_item(username, host) + pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) + pool.close() + pool.join() + + +def exec_cmd_servers(username): + hosts = [] + color_print("Input the Host IP(s),Separated by Commas, q/Q to Quit.\n \ + You can choose in the following IP(s), Use Linux / Unix glob.", 'green') + print_user_host(LOGIN_NAME) + while True: + inputs = raw_input('\033[1;32mip(s)>: \033[0m') + if inputs in ['q', 'Q']: + break + get_hosts = get_user_host(username).keys() + for host in get_hosts: + if fnmatch.fnmatch(host, inputs): + hosts.append(host.strip()) + if len(hosts) == 0: + color_print("Check again, Not matched any ip!", 'red') + continue + else: + print "You matched ip: %s" % hosts + color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') + while True: + cmd = raw_input('\033[1;32mCmd(s): \033[0m') + if cmd in ['q', 'Q']: + break + exec_log_dir = os.path.join(LOG_DIR, 'exec_cmds') + if not os.path.isdir(exec_log_dir): + os.mkdir(exec_log_dir) + os.chmod(exec_log_dir, 0777) + filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) + f = open(filename, 'a') + f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % + (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) + multi_remote_exec_cmd(hosts, username, cmd) + + +def help(): + global p, options, arguments + usage = "usage: %prog '' [options] arg1 [options] arg2" + p = optparse.OptionParser(usage=usage) + p.add_option('-p', '--host', help = "Print The Servers You Available.") + p.add_option('-g', '--group', help = "Print The Server Groups You Available.") + options, arguments = p.parse_args() + + +def main(): + help() + if options.host: + pass + elif options.group: + pass + else: + try: + verify_connect(LOGIN_NAME, sys.argv[1]) + except ServerError, e: + color_print(e, 'red') + + +if __name__ == '__main__': + main() diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 96ee67b19..672130e89 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -169,22 +169,6 @@ def string_length(string, length): return '%s ...' % string[0:length] -@register.filter(name='filter_private') -def filter_private(group): - agroup = [] - pattern = re.compile(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') - p = BisGroup.objects.get(name='ALL') - for g in group: - if not pattern.match(g.name): - agroup.append(g) - try: - agroup.remove(p) - except ValueError: - pass - - return agroup - - @register.filter(name='to_name') def to_name(user_id): try: diff --git a/ssh.py b/ssh.py new file mode 100644 index 000000000..398698269 --- /dev/null +++ b/ssh.py @@ -0,0 +1,395 @@ +#!/usr/bin/env python + +import socket +import sys +import os +import ast +import select +import time +from datetime import datetime +import paramiko +import struct +import fcntl +import signal +import textwrap +import django +import getpass +import fnmatch +import optparse +import readline +from multiprocessing import Pool +from ConfigParser import ConfigParser +from django.core.exceptions import ObjectDoesNotExist + +os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' +django.setup() +from juser.models import User +from jasset.models import Asset +from jlog.models import Log +from jumpserver.views import PyCrypt +from jumpserver.api import user_perm_asset_api, user_perm_group_api + +try: + import termios + import tty +except ImportError: + print '\033[1;31mOnly postfix supported.\033[0m' + time.sleep(3) + sys.exit() + +BASE_DIR = os.path.abspath(os.path.dirname(__file__)) +CONF = ConfigParser() +CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) +LOG_DIR = os.path.join(BASE_DIR, 'logs') +SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') +SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') +KEY = CONF.get('web', 'key') +LOGIN_NAME = getpass.getuser() + + +def color_print(msg, color='blue'): + """Print colorful string.""" + color_msg = {'blue': '\033[1;36m%s\033[0m', + 'green': '\033[1;32m%s\033[0m', + 'red': '\033[1;31m%s\033[0m'} + + print color_msg.get(color, 'blue') % msg + + +def color_print_exit(msg, color='red'): + """Print colorful string and exit.""" + color_print(msg, color=color) + time.sleep(2) + sys.exit() + + +class ServerError(Exception): + pass + + +def get_win_size(): + """This function use to get the size of the windows!""" + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912L # Assume + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + +def set_win_size(sig, data): + """This function use to set the window size of the terminal!""" + try: + win_size = get_win_size() + channel.resize_pty(height=win_size[0], width=win_size[1]) + except: + pass + + +def get_object(model, **kwargs): + try: + the_object = model.objects.get(**kwargs) + except ObjectDoesNotExist: + raise ServerError('Object get %s failed.' % str(kwargs.values())) + return the_object + + +def log_record(username, host): + """Logging user command and output.""" + connect_log_dir = os.path.join(LOG_DIR, 'connect') + timestamp_start = int(time.time()) + today = time.strftime('%Y%m%d', time.localtime(timestamp_start)) + time_now = time.strftime('%H%M%S', time.localtime(timestamp_start)) + today_connect_log_dir = os.path.join(connect_log_dir, today) + log_filename = '%s_%s_%s.log' % (username, host, time_now) + log_file_path = os.path.join(today_connect_log_dir, log_filename) + pid = os.getpid() + ip_list = [] + remote_ip = os.popen("who |grep `ps aux |gawk '{if ($2==%s) print $1}'` |gawk '{print $5}'|tr -d '()'" % pid).readlines() + for ip in remote_ip: + ip_list.append(ip.strip('\n')) + ip_list = ','.join(list(set(ip_list))) + + if not os.path.isdir(today_connect_log_dir): + try: + os.makedirs(today_connect_log_dir) + os.chmod(today_connect_log_dir, 0777) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, connect_log_dir)) + + try: + log_file = open(log_file_path, 'a') + except IOError: + raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) + + log = Log(user=username, host=host, remote_ip=ip_list, log_path=log_file_path, start_time=datetime.now(), pid=pid) + log_file.write('Starttime is %s\n' % datetime.now()) + log.save() + return log_file, log + + +def posix_shell(chan, username, host): + """ + Use paramiko channel connect server interactive. + """ + log_file, log = log_record(username, host) + old_tty = termios.tcgetattr(sys.stdin) + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + chan.settimeout(0.0) + + while True: + try: + r, w, e = select.select([chan, sys.stdin], [], []) + except: + pass + + if chan in r: + try: + x = chan.recv(1024) + if len(x) == 0: + break + sys.stdout.write(x) + sys.stdout.flush() + log_file.write(x) + log_file.flush() + except socket.timeout: + pass + + if sys.stdin in r: + x = os.read(sys.stdin.fileno(), 1) + if len(x) == 0: + break + chan.send(x) + + finally: + timestamp_end = time.time() + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + log_file.write('Endtime is %s' % datetime.now()) + log_file.close() + log.is_finished = True + log.log_finished = False + log.end_time = datetime.now() + log.save() + + +def get_user_host(username): + """Get the hosts of under the user control.""" + hosts_attr = {} + asset_all = user_perm_asset_api(username) + for asset in asset_all: + hosts_attr[asset.ip] = [asset.id, asset.comment] + return hosts_attr + + +def get_user_hostgroup(username): + """Get the hostgroups of under the user control.""" + groups_attr = {} + group_all = user_perm_group_api(username) + for group in group_all: + groups_attr[group.name] = [group.id, group.comment] + return groups_attr + + +def get_connect_item(username, ip): + cryptor = PyCrypt(KEY) + + asset = get_object(Asset, ip=ip) + port = asset.port + + if not asset.is_active: + raise ServerError('Host %s is not active.' % ip) + + user = get_object(User, username=username) + + if not user.is_active: + raise ServerError('User %s is not active.' % username) + + login_type_dict = { + 'L': user.ldap_pwd, + } + + if asset.login_type in login_type_dict: + password = cryptor.decrypt(login_type_dict[asset.login_type]) + return username, password, ip, port + + elif asset.login_type == 'M': + username = asset.username + password = cryptor.decrypt(asset.password) + return username, password, ip, port + + else: + raise ServerError('Login type is not in ["L", "M"]') + + +def verify_connect(username, part_ip): + hosts_attr = get_user_host(username) + hosts = hosts_attr.keys() + ip_matched = [ip for ip in hosts if part_ip in ip] + + if len(ip_matched) > 1: + for ip in ip_matched: + print '%s -- %s' % (ip, hosts_attr[ip][1]) + elif len(ip_matched) < 1: + color_print('No Permission or No host.', 'red') + else: + username, password, host, port = get_connect_item(username, ip_matched[0]) + print username, password, host, port + connect(username, password, host, port, LOGIN_NAME) + + +def print_prompt(): + msg = """\033[1;32m### Welcome Use JumpServer To Login. ### \033[0m + 1) Type \033[32mIP ADDRESS\033[0m To Login. + 2) Type \033[32mP/p\033[0m To Print The Servers You Available. + 3) Type \033[32mG/g\033[0m To Print The Server Groups You Available. + 4) Type \033[32mE/e\033[0m To Execute Command On Several Servers. + 5) Type \033[32mQ/q\033[0m To Quit. + """ + print textwrap.dedent(msg) + + +def print_user_host(username): + hosts_attr = get_user_host(username) + hosts = hosts_attr.keys() + hosts.sort() + for ip in hosts: + print '%s -- %s' % (ip, hosts_attr[ip][1]) + + +def print_user_hostgroup(username): + group_attr = get_user_hostgroup(username) + groups = group_attr.keys() + for g in groups: + print '%s -- %s' % (g, group_attr[g][1]) + + +def connect(username, password, host, port, login_name): + """ + Connect server. + """ + ps1 = "PS1='[\u@%s \W]\$ '\n" % host + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % host + + # Make a ssh connection + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(host, port=port, username=username, password=password, compress=True) + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + raise ServerError('Authentication Error.') + except socket.error: + raise ServerError('Connect SSH Socket Port Error, Please Correct it.') + + # Make a channel and set windows size + global channel + win_size = get_win_size() + channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + #channel.resize_pty(height=win_size[0], width=win_size[1]) + try: + signal.signal(signal.SIGWINCH, set_win_size) + except: + pass + + # Set PS1 and msg it + channel.send(ps1) + channel.send(login_msg) + + # Make ssh interactive tunnel + posix_shell(channel, login_name, host) + + # Shutdown channel socket + channel.close() + ssh.close() + + +def remote_exec_cmd(ip, port, username, password, cmd): + try: + time.sleep(5) + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(ip, port, username, password, timeout=5) + stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) + out = stdout.readlines() + err = stderr.readlines() + color_print('%s:' %ip, 'blue') + for i in out: + color_print(" " * 4 + i.strip(), 'green') + for j in err: + color_print(" " * 4 + j.strip(), 'red') + ssh.close() + except Exception as e: + color_print(ip + ':', 'blue') + color_print(str(e), 'red') + + +def multi_remote_exec_cmd(hosts, username, cmd): + pool = Pool(processes=5) + for host in hosts: + username, password, ip, port = get_connect_item(username, host) + pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) + pool.close() + pool.join() + + +def exec_cmd_servers(username): + hosts = [] + color_print("Input the Host IP(s),Separated by Commas, q/Q to Quit.\n \ + You can choose in the following IP(s), Use Linux / Unix glob.", 'green') + print_user_host(LOGIN_NAME) + while True: + inputs = raw_input('\033[1;32mip(s)>: \033[0m') + if inputs in ['q', 'Q']: + break + get_hosts = get_user_host(username).keys() + for host in get_hosts: + if fnmatch.fnmatch(host, inputs): + hosts.append(host.strip()) + if len(hosts) == 0: + color_print("Check again, Not matched any ip!", 'red') + continue + else: + print "You matched ip: %s" % hosts + color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') + while True: + cmd = raw_input('\033[1;32mCmd(s): \033[0m') + if cmd in ['q', 'Q']: + break + exec_log_dir = os.path.join(LOG_DIR, 'exec_cmds') + if not os.path.isdir(exec_log_dir): + os.mkdir(exec_log_dir) + os.chmod(exec_log_dir, 0777) + filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) + f = open(filename, 'a') + f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % + (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) + multi_remote_exec_cmd(hosts, username, cmd) + + +def help(): + global p, options, arguments + usage = "usage: %prog '' [options] arg1 [options] arg2" + p = optparse.OptionParser(usage=usage) + p.add_option('-p', '--host', help = "Print The Servers You Available.") + p.add_option('-g', '--group', help = "Print The Server Groups You Available.") + options, arguments = p.parse_args() + + +def main(): + help() + if options.host: + pass + elif options.group: + pass + else: + try: + verify_connect(LOGIN_NAME, sys.argv[1]) + except ServerError, e: + color_print(e, 'red') + + +if __name__ == '__main__': + main() diff --git a/static/css/plugins/fullcalendar/fullcalendar.css b/static/css/plugins/fullcalendar/fullcalendar.css new file mode 100755 index 000000000..b2feb028e --- /dev/null +++ b/static/css/plugins/fullcalendar/fullcalendar.css @@ -0,0 +1,977 @@ +/*! + * FullCalendar v2.2.0 Stylesheet + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */ + + +.fc { + direction: ltr; + text-align: left; +} + +.fc-rtl { + text-align: right; +} + +body .fc { /* extra precedence to overcome jqui */ + font-size: 1em; +} + + +/* Colors +--------------------------------------------------------------------------------------------------*/ + +.fc-unthemed th, +.fc-unthemed td, +.fc-unthemed hr, +.fc-unthemed thead, +.fc-unthemed tbody, +.fc-unthemed .fc-row, +.fc-unthemed .fc-popover { + border-color: #ddd; +} + +.fc-unthemed .fc-popover { + background-color: #fff; +} + +.fc-unthemed hr, +.fc-unthemed .fc-popover .fc-header { + background: #eee; +} + +.fc-unthemed .fc-popover .fc-header .fc-close { + color: #666; +} + +.fc-unthemed .fc-today { + background: #fcf8e3; +} + +.fc-highlight { /* when user is selecting cells */ + background: #bce8f1; + opacity: .3; + filter: alpha(opacity=30); /* for IE */ +} + +.fc-bgevent { /* default look for background events */ + background: rgb(143, 223, 130); + opacity: .3; + filter: alpha(opacity=30); /* for IE */ +} + +.fc-nonbusiness { /* default look for non-business-hours areas */ + /* will inherit .fc-bgevent's styles */ + background: #ccc; +} + + +/* Icons (inline elements with styled text that mock arrow icons) +--------------------------------------------------------------------------------------------------*/ + +.fc-icon { + display: inline-block; + font-size: 2em; + line-height: .5em; + height: .5em; /* will make the total height 1em */ + font-family: "Courier New", Courier, monospace; +} + +.fc-icon-left-single-arrow:after { + content: "\02039"; + font-weight: bold; +} + +.fc-icon-right-single-arrow:after { + content: "\0203A"; + font-weight: bold; +} + +.fc-icon-left-double-arrow:after { + content: "\000AB"; +} + +.fc-icon-right-double-arrow:after { + content: "\000BB"; +} + +.fc-icon-x:after { + content: "\000D7"; +} + + +/* Buttons (styled ").click(function(){m.hasClass(f+"-state-disabled")||(l(),(m.hasClass(f+"-state-active")||m.hasClass(f+"-state-disabled"))&&m.removeClass(f+"-state-hover"))}).mousedown(function(){m.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-down")}).mouseup(function(){m.removeClass(f+"-state-down")}).hover(function(){m.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-hover")},function(){m.removeClass(f+"-state-hover").removeClass(f+"-state-down")}),s=s.add(m)))}),o&&s.first().addClass(f+"-corner-left").end().last().addClass(f+"-corner-right").end(),s.length>1?(i=t("
"),o&&i.addClass("fc-button-group"),i.append(s),r.append(i)):r.append(s)}),r}function o(t){g.find("h2").text(t)}function l(t){g.find(".fc-"+t+"-button").addClass(f+"-state-active")}function a(t){g.find(".fc-"+t+"-button").removeClass(f+"-state-active")}function c(t){g.find(".fc-"+t+"-button").attr("disabled","disabled").addClass(f+"-state-disabled")}function d(t){g.find(".fc-"+t+"-button").removeAttr("disabled").removeClass(f+"-state-disabled")}function u(){return p}var h=this;h.render=i,h.destroy=r,h.updateTitle=o,h.activateButton=l,h.deactivateButton=a,h.disableButton=c,h.enableButton=d,h.getViewsWithButtons=u;var f,g=t(),p=[]}function c(n){function i(t,e){return!A||t.clone().stripZone()Y.clone().stripZone()}function r(t,e){A=t,Y=e,q=[];var n=++j,i=Z.length;X=i;for(var r=0;i>r;r++)s(Z[r],n)}function s(e,n){o(e,function(i){var r,s,o,l=t.isArray(e.events);if(n==j){if(i)for(r=0;i.length>r;r++)s=i[r],o=l?s:S(s,e),o&&q.push.apply(q,E(o));X--,X||B(q)}})}function o(e,i){var r,s,l=_e.sourceFetchers;for(r=0;l.length>r;r++){if(s=l[r].call(N,e,A.clone(),Y.clone(),n.timezone,i),s===!0)return;if("object"==typeof s)return o(s,i),void 0}var a=e.events;if(a)t.isFunction(a)?(y(),a.call(N,A.clone(),Y.clone(),n.timezone,function(t){i(t),w()})):t.isArray(a)?i(a):i();else{var c=e.url;if(c){var d,u=e.success,h=e.error,f=e.complete;d=t.isFunction(e.data)?e.data():e.data;var g=t.extend({},d||{}),p=P(e.startParam,n.startParam),m=P(e.endParam,n.endParam),v=P(e.timezoneParam,n.timezoneParam);p&&(g[p]=A.format()),m&&(g[m]=Y.format()),n.timezone&&"local"!=n.timezone&&(g[v]=n.timezone),y(),t.ajax(t.extend({},Ne,e,{data:g,success:function(e){e=e||[];var n=R(u,this,arguments);t.isArray(n)&&(e=n),i(e)},error:function(){R(h,this,arguments),i()},complete:function(){R(f,this,arguments),w()}}))}else i()}}function l(t){var e=a(t);e&&(Z.push(e),X++,s(e,j))}function a(e){var n,i,r=_e.sourceNormalizers;if(t.isFunction(e)||t.isArray(e)?n={events:e}:"string"==typeof e?n={url:e}:"object"==typeof e&&(n=t.extend({},e)),n){for(n.className?"string"==typeof n.className&&(n.className=n.className.split(/\s+/)):n.className=[],t.isArray(n.events)&&(n.origArray=n.events,n.events=t.map(n.events,function(t){return S(t,n)})),i=0;r.length>i;i++)r[i].call(N,n);return n}}function c(e){Z=t.grep(Z,function(t){return!u(t,e)}),q=t.grep(q,function(t){return!u(t.source,e)}),B(q)}function u(t,e){return t&&e&&h(t)==h(e)}function h(t){return("object"==typeof t?t.origArray||t.url||t.events:null)||t}function f(t){t.start=N.moment(t.start),t.end&&(t.end=N.moment(t.end)),D(t),g(t),B(q)}function g(t){var e,n,i,r;for(e=0;q.length>e;e++)if(n=q[e],n._id==t._id&&n!==t)for(i=0;U.length>i;i++)r=U[i],void 0!==t[r]&&(n[r]=t[r])}function p(t,e){var n,i,r,s=S(t);if(s){for(n=E(s),i=0;n.length>i;i++)r=n[i],r.source||(e&&(W.events.push(r),r.source=W),q.push(r));return B(q),n}return[]}function m(e){var n,i;for(null==e?e=function(){return!0}:t.isFunction(e)||(n=e+"",e=function(t){return t._id==n}),q=t.grep(q,e,!0),i=0;Z.length>i;i++)t.isArray(Z[i].events)&&(Z[i].events=t.grep(Z[i].events,e,!0));B(q)}function v(e){return t.isFunction(e)?t.grep(q,e):null!=e?(e+="",t.grep(q,function(t){return t._id==e})):q}function y(){$++||V("loading",null,!0,O())}function w(){--$||V("loading",null,!1,O())}function S(i,r){var s,o,l,a,c={};if(n.eventDataTransform&&(i=n.eventDataTransform(i)),r&&r.eventDataTransform&&(i=r.eventDataTransform(i)),t.extend(c,i),r&&(c.source=r),c._id=i._id||(void 0===i.id?"_fc"+Ae++:i.id+""),c.className=i.className?"string"==typeof i.className?i.className.split(/\s+/):i.className:[],s=i.start||i.date,o=i.end,k(s)&&(s=e.duration(s)),k(o)&&(o=e.duration(o)),i.dow||e.isDuration(s)||e.isDuration(o))c.start=s?e.duration(s):null,c.end=o?e.duration(o):null,c._recurring=!0;else{if(s&&(s=N.moment(s),!s.isValid()))return!1;if(o&&(o=N.moment(o),!o.isValid()))return!1;l=i.allDay,void 0===l&&(a=P(r?r.allDayDefault:void 0,n.allDayDefault),l=void 0!==a?a:!(s.hasTime()||o&&o.hasTime())),b(s,o,l,c)}return c}function b(t,e,i,r){i?(t.hasTime()&&t.stripTime(),e&&e.hasTime()&&e.stripTime()):(t.hasTime()||(t=N.rezoneDate(t)),e&&!e.hasTime()&&(e=N.rezoneDate(e))),r.allDay=i,r.start=t,r.end=e||null,n.forceEventDuration&&!r.end&&(r.end=I(r)),d(r)}function E(e){var n,i,r,s,o,l,a,c,d,u,h=[],f=A,g=Y;if(f&&g||(n=N.getView(),f=n.start,g=n.end),e)if(e._recurring){if(r=e.dow)for(i={},s=0;r.length>s;s++)i[r[s]]=!0;for(o=f.clone().stripTime();o.isBefore(g);)(!i||i[o.day()])&&(l=e.start,a=e.end,c=o.clone(),d=null,l&&(c=c.time(l)),a&&(d=o.clone().time(a)),u=t.extend({},e),b(c,d,!l&&!a,u),h.push(u)),o.add(1,"days")}else h.push(e);return h}function D(t,e,n){var i,r,s,o,l=t._allDay,a=t._start,c=t._end,d=!1;return e||n||(e=t.start,n=t.end),i=t.allDay!=l?t.allDay:!(e||n).hasTime(),i&&(e&&(e=e.clone().stripTime()),n&&(n=n.clone().stripTime())),e&&(r=i?C(e,a.clone().stripTime()):C(e,a)),i!=l?d=!0:n&&(s=C(n||N.getDefaultEventEnd(i,e||a),e||a).subtract(C(c||N.getDefaultEventEnd(l,a),a))),o=T(v(t._id),d,i,r,s),{dateDelta:r,durationDelta:s,undo:o}}function T(e,i,r,s,o){var l=N.getIsAmbigTimezone(),a=[];return t.each(e,function(t,e){var c=e._allDay,u=e._start,h=e._end,f=null!=r?r:c,g=u.clone(),p=!i&&h?h.clone():null;f?(g.stripTime(),p&&p.stripTime()):(g.hasTime()||(g=N.rezoneDate(g)),p&&!p.hasTime()&&(p=N.rezoneDate(p))),p||!n.forceEventDuration&&!+o||(p=N.getDefaultEventEnd(f,g)),g.add(s),p&&p.add(s).add(o),l&&(+s||+o)&&(g.stripZone(),p&&p.stripZone()),e.allDay=f,e.start=g,e.end=p,d(e),a.push(function(){e.allDay=c,e.start=u,e.end=h,d(e)})}),function(){for(var t=0;a.length>t;t++)a[t]()}}function H(){var e,i=n.businessHours,r={className:"fc-nonbusiness",start:"09:00",end:"17:00",dow:[1,2,3,4,5],rendering:"inverse-background"};return i&&(e="object"==typeof i?t.extend({},r,i):r),e?E(S(e)):[]}function x(t,e,i){var r=t.source||{},s=P(t.constraint,r.constraint,n.eventConstraint),o=P(t.overlap,r.overlap,n.eventOverlap);return L(e,i,s,o,t)}function M(t,e){return L(t,e,n.selectConstraint,n.selectOverlap)}function F(t,e,n){var i;return n&&(i=E(S(n))[0])?x(i,t,e):M(t,e)}function L(t,e,n,i,r){var s,o,l,a,c;if(t=t.clone().stripZone(),e=e.clone().stripZone(),null!=n){for(s=z(n),o=!1,l=0;s.length>l;l++)if(_(s[l],t,e)){o=!0;break}if(!o)return!1}for(l=0;q.length>l;l++)if(a=q[l],(!r||r._id!==a._id)&&G(a,t,e)){if(i===!1)return!1;if("function"==typeof i&&!i(a,r))return!1;if(r){if(c=P(a.overlap,(a.source||{}).overlap),c===!1)return!1;if("function"==typeof c&&!c(r,a))return!1}}return!0}function z(t){return"businessHours"===t?H():"object"==typeof t?E(S(t)):v(t)}function _(t,e,n){var i=t.start.clone().stripZone(),r=N.getEventEnd(t).stripZone();return e>=i&&r>=n}function G(t,e,n){var i=t.start.clone().stripZone(),r=N.getEventEnd(t).stripZone();return r>e&&n>i}var N=this;N.isFetchNeeded=i,N.fetchEvents=r,N.addEventSource=l,N.removeEventSource=c,N.updateEvent=f,N.renderEvent=p,N.removeEvents=m,N.clientEvents=v,N.mutateEvent=D;var A,Y,V=N.trigger,O=N.getView,B=N.reportEvents,I=N.getEventEnd,W={events:[]},Z=[W],j=0,X=0,$=0,q=[];t.each((n.events?[n.events]:[]).concat(n.eventSources||[]),function(t,e){var n=a(e);n&&Z.push(n)});var U=["title","url","allDay","className","editable","color","backgroundColor","borderColor","textColor"];N.getBusinessHoursEvents=H,N.isEventAllowedInRange=x,N.isSelectionAllowedInRange=M,N.isExternalDragAllowedInRange=F}function d(t){t._allDay=t.allDay,t._start=t.start.clone(),t._end=t.end?t.end.clone():null}function u(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function h(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function f(){t("body").addClass("fc-not-allowed")}function g(){t("body").removeClass("fc-not-allowed")}function p(e,n,i){var r=Math.floor(n/e.length),s=Math.floor(n-r*(e.length-1)),o=[],l=[],a=[],c=0;m(e),e.each(function(n,i){var d=n===e.length-1?s:r,u=t(i).outerHeight(!0);d>u?(o.push(i),l.push(u),a.push(t(i).height())):c+=u}),i&&(n-=c,r=Math.floor(n/o.length),s=Math.floor(n-r*(o.length-1))),t(o).each(function(e,n){var i=e===o.length-1?s:r,c=l[e],d=a[e],u=i-(c-d);i>c&&t(n).height(u)})}function m(t){t.height("")}function v(e){var n=0;return e.find("> *").each(function(e,i){var r=t(i).outerWidth();r>n&&(n=r)}),n++,e.width(n),n}function y(t,e){return t.height(e).addClass("fc-scroller"),t[0].scrollHeight-1>t[0].clientHeight?!0:(w(t),!1)}function w(t){t.height("").removeClass("fc-scroller")}function S(e){var n=e.css("position"),i=e.parents().filter(function(){var e=t(this);return/(auto|scroll)/.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==n&&i.length?i:t(e[0].ownerDocument||document)}function b(t){var e=t.offset().left,n=e+t.width(),i=t.children(),r=i.offset().left,s=r+i.outerWidth();return{left:r-e,right:n-s}}function E(t){return 1==t.which&&!t.ctrlKey}function D(t,e,n,i){var r,s,o,l;return e>n&&i>t?(t>=n?(r=t.clone(),o=!0):(r=n.clone(),o=!1),i>=e?(s=e.clone(),l=!0):(s=i.clone(),l=!1),{start:r,end:s,isStart:o,isEnd:l}):void 0}function T(t,e){if(t=t||{},void 0!==t[e])return t[e];for(var n,i=e.split(/(?=[A-Z])/),r=i.length-1;r>=0;r--)if(n=t[i[r].toLowerCase()],void 0!==n)return n;return t["default"]}function C(t,n){return e.duration({days:t.clone().stripTime().diff(n.clone().stripTime(),"days"),ms:t.time()-n.time()})}function H(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function x(t,e){return t-e}function k(t){return/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function M(t){var e=function(){};return e.prototype=t,new e}function R(e,n,i){if(t.isFunction(e)&&(e=[e]),e){var r,s;for(r=0;e.length>r;r++)s=e[r].apply(n,i)||s;return s}}function P(){for(var t=0;arguments.length>t;t++)if(void 0!==arguments[t])return arguments[t]}function F(t){return(t+"").replace(/&/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"
")}function L(t){return t.replace(/&.*?;/g,"")}function z(t){return t.charAt(0).toUpperCase()+t.slice(1)}function _(t,e){var n,i,r,s,o=function(){var l=+new Date-s;e>l&&l>0?n=setTimeout(o,e-l):(n=null,t.apply(r,i),n||(r=i=null))};return function(){r=this,i=arguments,s=+new Date,n||(n=setTimeout(o,e))}}function G(n,i,r){var s,o,l,a,c=n[0],d=1==n.length&&"string"==typeof c;return e.isMoment(c)?(a=e.apply(null,n),A(c,a)):H(c)||void 0===c?a=e.apply(null,n):(s=!1,o=!1,d?Ie.test(c)?(c+="-01",n=[c],s=!0,o=!0):(l=We.exec(c))&&(s=!l[5],o=!0):t.isArray(c)&&(o=!0),a=i?e.utc.apply(e,n):e.apply(null,n),s?(a._ambigTime=!0,a._ambigZone=!0):r&&(o?a._ambigZone=!0:d&&a.zone(c))),a._fullCalendar=!0,a}function N(t,e){var n,i=[],r=!1,s=!1;for(n=0;t.length>n;n++)i.push(_e.moment.parseZone(t[n])),r=r||i[n]._ambigTime,s=s||i[n]._ambigZone;for(n=0;i.length>n;n++)r&&!e?i[n].stripTime():s&&i[n].stripZone();return i}function A(t,e){t._ambigTime?e._ambigTime=!0:e._ambigTime&&(e._ambigTime=!1),t._ambigZone?e._ambigZone=!0:e._ambigZone&&(e._ambigZone=!1)}function Y(t,e){t.year(e[0]||0).month(e[1]||0).date(e[2]||0).hours(e[3]||0).minutes(e[4]||0).seconds(e[5]||0).milliseconds(e[6]||0)}function V(t,e){return je.format.call(t,e)}function O(t,e){return B(t,X(e))}function B(t,e){var n,i="";for(n=0;e.length>n;n++)i+=I(t,e[n]);return i}function I(t,e){var n,i;return"string"==typeof e?e:(n=e.token)?Xe[n]?Xe[n](t):V(t,n):e.maybe&&(i=B(t,e.maybe),i.match(/[1-9]/))?i:""}function W(t,e,n,i,r){var s;return t=_e.moment.parseZone(t),e=_e.moment.parseZone(e),s=(t.localeData||t.lang).call(t),n=s.longDateFormat(n)||n,i=i||" - ",Z(t,e,X(n),i,r)}function Z(t,e,n,i,r){var s,o,l,a,c="",d="",u="",h="",f="";for(o=0;n.length>o&&(s=j(t,e,n[o]),s!==!1);o++)c+=s;for(l=n.length-1;l>o&&(s=j(t,e,n[l]),s!==!1);l--)d=s+d;for(a=o;l>=a;a++)u+=I(t,n[a]),h+=I(e,n[a]);return(u||h)&&(f=r?h+i+u:u+i+h),c+f+d}function j(t,e,n){var i,r;return"string"==typeof n?n:(i=n.token)&&(r=$e[i.charAt(0)],r&&t.isSame(e,r))?V(t,i):!1}function X(t){return t in qe?qe[t]:qe[t]=$(t)}function $(t){for(var e,n=[],i=/\[([^\]]*)\]|\(([^\)]*)\)|(LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=i.exec(t);)e[1]?n.push(e[1]):e[2]?n.push({maybe:$(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push(e[5]);return n}function q(t){this.options=t||{}}function U(t){this.grid=t}function K(t){this.coordMaps=t}function Q(t,e){this.coordMap=t,this.options=e||{}}function J(t,e){return t||e?t&&e?t.grid===e.grid&&t.row===e.row&&t.col===e.col:!1:!0}function te(e,n){this.options=n=n||{},this.sourceEl=e,this.parentEl=n.parentEl?t(n.parentEl):e.parent()}function ee(t){this.view=t}function ne(t){ee.call(this,t),this.coordMap=new U(this),this.elsByFill={}}function ie(t){var e=se(t);return"background"===e||"inverse-background"===e}function re(t){return"inverse-background"===se(t)}function se(t){return P((t.source||{}).rendering,t.rendering)}function oe(t){var e,n,i={};for(e=0;t.length>e;e++)n=t[e],(i[n._id]||(i[n._id]=[])).push(n);return i}function le(t,e){return t.eventStartMS-e.eventStartMS}function ae(t,e){return t.eventStartMS-e.eventStartMS||e.eventDurationMS-t.eventDurationMS||e.event.allDay-t.event.allDay||(t.event.title||"").localeCompare(e.event.title)}function ce(t){ne.call(this,t)}function de(t,e){var n,i;for(n=0;e.length>n;n++)if(i=e[n],i.leftCol<=t.rightCol&&i.rightCol>=t.leftCol)return!0;return!1}function ue(t,e){return t.leftCol-e.leftCol}function he(t){ne.call(this,t)}function fe(t){var e,n,i;if(t.sort(ae),e=ge(t),pe(e),n=e[0]){for(i=0;n.length>i;i++)me(n[i]);for(i=0;n.length>i;i++)ve(n[i],0,0)}}function ge(t){var e,n,i,r=[];for(e=0;t.length>e;e++){for(n=t[e],i=0;r.length>i&&ye(n,r[i]).length;i++);n.level=i,(r[i]||(r[i]=[])).push(n)}return r}function pe(t){var e,n,i,r,s;for(e=0;t.length>e;e++)for(n=t[e],i=0;n.length>i;i++)for(r=n[i],r.forwardSegs=[],s=e+1;t.length>s;s++)ye(r,t[s],r.forwardSegs)}function me(t){var e,n,i=t.forwardSegs,r=0;if(void 0===t.forwardPressure){for(e=0;i.length>e;e++)n=i[e],me(n),r=Math.max(r,1+n.forwardPressure);t.forwardPressure=r}}function ve(t,e,n){var i,r=t.forwardSegs;if(void 0===t.forwardCoord)for(r.length?(r.sort(Se),ve(r[0],e+1,n),t.forwardCoord=r[0].backwardCoord):t.forwardCoord=1,t.backwardCoord=t.forwardCoord-(t.forwardCoord-n)/(e+1),i=0;r.length>i;i++)ve(r[i],0,t.forwardCoord)}function ye(t,e,n){n=n||[];for(var i=0;e.length>i;i++)we(t,e[i])&&n.push(e[i]);return n}function we(t,e){return t.bottom>e.top&&t.topd;d++){var u=d*i,h=u+i-1,f=Math.max(a,u),g=Math.min(c,h);if(g>=f){var m=S(f),v=S(g),b=[m.col,v.col].sort(),D=p(f)==o,T=p(g)+1==l;r.push({row:d,leftCol:b[0],rightCol:b[1],isStart:D,isEnd:T})}}return r}function E(t,e){var n,i,r=t.clone().stripTime();return e&&(n=e.clone().stripTime(),i=+e.time(),i&&i>=k&&n.add(1,"days")),(!e||r>=n)&&(n=r.clone().add(1,"days")),{start:r,end:n}}function D(t){var e=E(t.start,t.end);return e.end.diff(e.start,"days")>1}var C=this;C.calendar=n,C.opt=i,C.trigger=r,C.isEventDraggable=s,C.isEventResizable=l,C.eventDrop=a,C.eventResize=c;var H=n.reportEventChange,x=n.options,k=e.duration(x.nextDayThreshold);C.init(),C.getEventTimeText=function(t,e){var r,s;return"object"==typeof t&&"object"==typeof e?(r=t,s=e,e=arguments[2]):(r=t.start,s=t.end),e=e||i("timeFormat"),s&&i("displayEventEnd")?n.formatRange(r,s,e):n.formatDate(r,e)},C.isHiddenDay=d,C.skipHiddenDays=h,C.getCellsPerWeek=u,C.dateToCell=v,C.dateToDayOffset=y,C.dayOffsetToCellOffset=w,C.cellOffsetToCell=S,C.cellToDate=f,C.cellToCellOffset=g,C.cellOffsetToDayOffset=p,C.dayOffsetToDate=m,C.rangeToSegments=b,C.isMultiDayEvent=D;var M,R=i("hiddenDays")||[],F=[],L=[],z=[],_=i("isRTL");(function(){i("weekends")===!1&&R.push(0,6);for(var e=0,n=0;7>e;e++)L[e]=n,F[e]=-1!=t.inArray(e,R),F[e]||(z[n]=e,n++);if(M=n,!M)throw"invalid hiddenDays"})()}function Ee(n){var i,r,s,o,l=_e.dataAttrPrefix;return l&&(l+="-"),i=n.data(l+"event")||null,i&&(i="object"==typeof i?t.extend({},i):{},r=i.start,null==r&&(r=i.time),s=i.duration,o=i.stick,delete i.start,delete i.time,delete i.duration,delete i.stick),null==r&&(r=n.data(l+"start")),null==r&&(r=n.data(l+"time")),null==s&&(s=n.data(l+"duration")),null==o&&(o=n.data(l+"stick")),r=null!=r?e.duration(r):null,s=null!=s?e.duration(s):null,o=Boolean(o),{eventProps:i,startTime:r,duration:s,stick:o}}function De(t){be.call(this,t),this.dayGrid=new ce(this),this.coordMap=this.dayGrid.coordMap}function Te(t){De.call(this,t)}function Ce(t){De.call(this,t)}function He(t){De.call(this,t)}function xe(t,e){return e.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"a")}function ke(t,e){return e.longDateFormat("LT").replace(/\s*a$/i,"")}function Me(t){be.call(this,t),this.timeGrid=new he(this),this.opt("allDaySlot")?(this.dayGrid=new ce(this),this.coordMap=new K([this.dayGrid.coordMap,this.timeGrid.coordMap])):this.coordMap=this.timeGrid.coordMap}function Re(t){Me.call(this,t)}function Pe(t){Me.call(this,t)}var Fe={lang:"en",defaultTimedEventDuration:"02:00:00",defaultAllDayEventDuration:{days:1},forceEventDuration:!1,nextDayThreshold:"09:00:00",defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberTitle:"W",weekNumberCalculation:"local",lazyFetching:!0,startParam:"start",endParam:"end",timezoneParam:"timezone",timezone:!1,titleFormat:{month:"MMMM YYYY",week:"ll",day:"LL"},columnFormat:{month:"ddd",week:i,day:"dddd"},timeFormat:{"default":n},displayEventEnd:{month:!1,basicWeek:!1,"default":!0},isRTL:!1,defaultButtonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",today:"today",month:"month",week:"week",day:"day"},buttonIcons:{prev:"left-single-arrow",next:"right-single-arrow",prevYear:"left-double-arrow",nextYear:"right-double-arrow"},theme:!1,themeButtonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e",prevYear:"seek-prev",nextYear:"seek-next"},dragOpacity:.75,dragRevertDuration:500,dragScroll:!0,unselectAuto:!0,dropAccept:"*",eventLimit:!1,eventLimitText:"more",eventLimitClick:"popover",dayPopoverFormat:"LL",handleWindowResize:!0,windowResizeDelay:200},Le={en:{columnFormat:{week:"ddd M/D"},dayPopoverFormat:"dddd, MMMM D"}},ze={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"right-single-arrow",next:"left-single-arrow",prevYear:"right-double-arrow",nextYear:"left-double-arrow"},themeButtonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w",nextYear:"seek-prev",prevYear:"seek-next"}},_e=t.fullCalendar={version:"2.2.0"},Ge=_e.views={};t.fn.fullCalendar=function(e){var n=Array.prototype.slice.call(arguments,1),i=this;return this.each(function(r,s){var o,a=t(s),c=a.data("fullCalendar");"string"==typeof e?c&&t.isFunction(c[e])&&(o=c[e].apply(c,n),r||(i=o),"destroy"===e&&a.removeData("fullCalendar")):c||(c=new l(a,e),a.data("fullCalendar",c),c.render())}),i},_e.langs=Le,_e.datepickerLang=function(e,n,i){var r=Le[e];r||(r=Le[e]={}),s(r,{isRTL:i.isRTL,weekNumberTitle:i.weekHeader,titleFormat:{month:i.showMonthAfterYear?"YYYY["+i.yearSuffix+"] MMMM":"MMMM YYYY["+i.yearSuffix+"]"},defaultButtonText:{prev:L(i.prevText),next:L(i.nextText),today:L(i.currentText)}}),t.datepicker&&(t.datepicker.regional[n]=t.datepicker.regional[e]=i,t.datepicker.regional.en=t.datepicker.regional[""],t.datepicker.setDefaults(i))},_e.lang=function(t,e){var n;e&&(n=Le[t],n||(n=Le[t]={}),s(n,e||{})),Fe.lang=t},_e.sourceNormalizers=[],_e.sourceFetchers=[];var Ne={dataType:"json",cache:!1},Ae=1,Ye=["sun","mon","tue","wed","thu","fri","sat"];_e.applyAll=R;var Ve,Oe,Be,Ie=/^\s*\d{4}-\d\d$/,We=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/,Ze=e.fn,je=t.extend({},Ze);_e.moment=function(){return G(arguments)},_e.moment.utc=function(){var t=G(arguments,!0);return t.hasTime()&&t.utc(),t},_e.moment.parseZone=function(){return G(arguments,!0,!0)},Ze.clone=function(){var t=je.clone.apply(this,arguments);return A(this,t),this._fullCalendar&&(t._fullCalendar=!0),t},Ze.time=function(t){if(!this._fullCalendar)return je.time.apply(this,arguments);if(null==t)return e.duration({hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()});this._ambigTime=!1,e.isDuration(t)||e.isMoment(t)||(t=e.duration(t));var n=0;return e.isDuration(t)&&(n=24*Math.floor(t.asDays())),this.hours(n+t.hours()).minutes(t.minutes()).seconds(t.seconds()).milliseconds(t.milliseconds())},Ze.stripTime=function(){var t=this.toArray();return this.utc(),Oe(this,t.slice(0,3)),this._ambigTime=!0,this._ambigZone=!0,this},Ze.hasTime=function(){return!this._ambigTime},Ze.stripZone=function(){var t=this.toArray(),e=this._ambigTime;return this.utc(),Oe(this,t),e&&(this._ambigTime=!0),this._ambigZone=!0,this},Ze.hasZone=function(){return!this._ambigZone},Ze.zone=function(t){return null!=t&&(this._ambigTime=!1,this._ambigZone=!1),je.zone.apply(this,arguments)},Ze.local=function(){var t=this.toArray(),e=this._ambigZone;return je.local.apply(this,arguments),e&&Be(this,t),this},Ze.format=function(){return this._fullCalendar&&arguments[0]?O(this,arguments[0]):this._ambigTime?V(this,"YYYY-MM-DD"):this._ambigZone?V(this,"YYYY-MM-DD[T]HH:mm:ss"):je.format.apply(this,arguments)},Ze.toISOString=function(){return this._ambigTime?V(this,"YYYY-MM-DD"):this._ambigZone?V(this,"YYYY-MM-DD[T]HH:mm:ss"):je.toISOString.apply(this,arguments)},Ze.isWithin=function(t,e){var n=N([this,t,e]);return n[0]>=n[1]&&n[0]').addClass(n.className||"").css({top:0,left:0}).append(n.content).appendTo(n.parentEl),this.el.on("click",".fc-close",function(){e.hide()}),n.autoHide&&t(document).on("mousedown",this.documentMousedownProxy=t.proxy(this,"documentMousedown"))},documentMousedown:function(e){this.el&&!t(e.target).closest(this.el).length&&this.hide()},destroy:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),t(document).off("mousedown",this.documentMousedownProxy) +},position:function(){var e,n,i,r,s,o=this.options,l=this.el.offsetParent().offset(),a=this.el.outerWidth(),c=this.el.outerHeight(),d=t(window),u=S(this.el);r=o.top||0,s=void 0!==o.left?o.left:void 0!==o.right?o.right-a:0,u.is(window)||u.is(document)?(u=d,e=0,n=0):(i=u.offset(),e=i.top,n=i.left),e+=d.scrollTop(),n+=d.scrollLeft(),o.viewportConstrain!==!1&&(r=Math.min(r,e+u.outerHeight()-c-this.margin),r=Math.max(r,e+this.margin),s=Math.min(s,n+u.outerWidth()-a-this.margin),s=Math.max(s,n+this.margin)),this.el.css({top:r-l.top,left:s-l.left})},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))}},U.prototype={grid:null,rows:null,cols:null,containerEl:null,minX:null,maxX:null,minY:null,maxY:null,build:function(){this.grid.buildCoords(this.rows=[],this.cols=[]),this.computeBounds()},getCell:function(t,e){var n,i=null,r=this.rows,s=this.cols,o=-1,l=-1;if(this.inBounds(t,e)){for(n=0;r.length>n;n++)if(e>=r[n][0]&&r[n][1]>e){o=n;break}for(n=0;s.length>n;n++)if(t>=s[n][0]&&s[n][1]>t){l=n;break}o>=0&&l>=0&&(i={row:o,col:l},i.grid=this.grid,i.date=this.grid.getCellDate(i))}return i},computeBounds:function(){var t;this.containerEl&&(t=this.containerEl.offset(),this.minX=t.left,this.maxX=t.left+this.containerEl.outerWidth(),this.minY=t.top,this.maxY=t.top+this.containerEl.outerHeight())},inBounds:function(t,e){return this.containerEl?t>=this.minX&&this.maxX>t&&e>=this.minY&&this.maxY>e:!0}},K.prototype={coordMaps:null,build:function(){var t,e=this.coordMaps;for(t=0;e.length>t;t++)e[t].build()},getCell:function(t,e){var n,i=this.coordMaps,r=null;for(n=0;i.length>n&&!r;n++)r=i[n].getCell(t,e);return r}},Q.prototype={coordMap:null,options:null,isListening:!1,isDragging:!1,origCell:null,origDate:null,cell:null,date:null,mouseX0:null,mouseY0:null,mousemoveProxy:null,mouseupProxy:null,scrollEl:null,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollHandlerProxy:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,mousedown:function(t){E(t)&&(t.preventDefault(),this.startListening(t),this.options.distance||this.startDrag(t))},startListening:function(e){var n,i;this.isListening||(e&&this.options.scroll&&(n=S(t(e.target)),n.is(window)||n.is(document)||(this.scrollEl=n,this.scrollHandlerProxy=_(t.proxy(this,"scrollHandler"),100),this.scrollEl.on("scroll",this.scrollHandlerProxy))),this.computeCoords(),e&&(i=this.getCell(e),this.origCell=i,this.origDate=i?i.date:null,this.mouseX0=e.pageX,this.mouseY0=e.pageY),t(document).on("mousemove",this.mousemoveProxy=t.proxy(this,"mousemove")).on("mouseup",this.mouseupProxy=t.proxy(this,"mouseup")).on("selectstart",this.preventDefault),this.isListening=!0,this.trigger("listenStart",e))},computeCoords:function(){this.coordMap.build(),this.computeScrollBounds()},mousemove:function(t){var e,n;this.isDragging||(e=this.options.distance||1,n=Math.pow(t.pageX-this.mouseX0,2)+Math.pow(t.pageY-this.mouseY0,2),n>=e*e&&this.startDrag(t)),this.isDragging&&this.drag(t)},startDrag:function(t){var e;this.isListening||this.startListening(),this.isDragging||(this.isDragging=!0,this.trigger("dragStart",t),e=this.getCell(t),e&&this.cellOver(e,!0))},drag:function(t){var e;this.isDragging&&(e=this.getCell(t),J(e,this.cell)||(this.cell&&this.cellOut(),e&&this.cellOver(e)),this.dragScroll(t))},cellOver:function(t){this.cell=t,this.date=t.date,this.trigger("cellOver",t,t.date)},cellOut:function(){this.cell&&(this.trigger("cellOut",this.cell),this.cell=null,this.date=null)},mouseup:function(t){this.stopDrag(t),this.stopListening(t)},stopDrag:function(t){this.isDragging&&(this.stopScrolling(),this.trigger("dragStop",t),this.isDragging=!1)},stopListening:function(e){this.isListening&&(this.scrollEl&&(this.scrollEl.off("scroll",this.scrollHandlerProxy),this.scrollHandlerProxy=null),t(document).off("mousemove",this.mousemoveProxy).off("mouseup",this.mouseupProxy).off("selectstart",this.preventDefault),this.mousemoveProxy=null,this.mouseupProxy=null,this.isListening=!1,this.trigger("listenStop",e),this.origCell=this.cell=null,this.origDate=this.date=null)},getCell:function(t){return this.coordMap.getCell(t.pageX,t.pageY)},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))},preventDefault:function(t){t.preventDefault()},computeScrollBounds:function(){var t,e=this.scrollEl;e&&(t=e.offset(),this.scrollBounds={top:t.top,left:t.left,bottom:t.top+e.outerHeight(),right:t.left+e.outerWidth()})},dragScroll:function(t){var e,n,i,r,s=this.scrollSensitivity,o=this.scrollBounds,l=0,a=0;o&&(e=(s-(t.pageY-o.top))/s,n=(s-(o.bottom-t.pageY))/s,i=(s-(t.pageX-o.left))/s,r=(s-(o.right-t.pageX))/s,e>=0&&1>=e?l=-1*e*this.scrollSpeed:n>=0&&1>=n&&(l=n*this.scrollSpeed),i>=0&&1>=i?a=-1*i*this.scrollSpeed:r>=0&&1>=r&&(a=r*this.scrollSpeed)),this.setScrollVel(l,a)},setScrollVel:function(e,n){this.scrollTopVel=e,this.scrollLeftVel=n,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(t.proxy(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var t=this.scrollEl;0>this.scrollTopVel?0>=t.scrollTop()&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),0>this.scrollLeftVel?0>=t.scrollLeft()&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.stopScrolling()},stopScrolling:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.computeCoords())},scrollHandler:function(){this.scrollIntervalId||this.computeCoords()}},te.prototype={options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,mouseY0:null,mouseX0:null,topDelta:null,leftDelta:null,mousemoveProxy:null,isFollowing:!1,isHidden:!1,isAnimating:!1,start:function(e){this.isFollowing||(this.isFollowing=!0,this.mouseY0=e.pageY,this.mouseX0=e.pageX,this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),t(document).on("mousemove",this.mousemoveProxy=t.proxy(this,"mousemove")))},stop:function(e,n){function i(){this.isAnimating=!1,r.destroyEl(),this.top0=this.left0=null,n&&n()}var r=this,s=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,t(document).off("mousemove",this.mousemoveProxy),e&&s&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:s,complete:i})):i())},getEl:function(){var t=this.el;return t||(this.sourceEl.width(),t=this.el=this.sourceEl.clone().css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}).appendTo(this.parentEl)),t},destroyEl:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var t,e;this.getEl(),null===this.top0&&(this.sourceEl.width(),t=this.sourceEl.offset(),e=this.el.offsetParent().offset(),this.top0=t.top-e.top,this.left0=t.left-e.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},mousemove:function(t){this.topDelta=t.pageY-this.mouseY0,this.leftDelta=t.pageX-this.mouseX0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}},ee.prototype={view:null,cellHtml:"",rowHtml:function(t,e){var n,i,r=this.view,s=this.getHtmlRenderer("cell",t),o="";for(e=e||0,n=0;r.colCnt>n;n++)i=r.cellToDate(e,n),o+=s(e,n,i);return o=this.bookendCells(o,t,e),""+o+""},bookendCells:function(t,e,n){var i=this.view,r=this.getHtmlRenderer("intro",e)(n||0),s=this.getHtmlRenderer("outro",e)(n||0),o=i.opt("isRTL"),l=o?s:r,a=o?r:s;return"string"==typeof t?l+t+a:t.prepend(l).append(a)},getHtmlRenderer:function(t,e){var n,i,r,s,o=this.view;return n=t+"Html",e&&(i=e+z(t)+"Html"),i&&(s=o[i])?r=o:i&&(s=this[i])?r=this:(s=o[n])?r=o:(s=this[n])&&(r=this),"function"==typeof s?function(){return s.apply(r,arguments)||""}:function(){return s||""}}},ne.prototype=M(ee.prototype),t.extend(ne.prototype,{el:null,coordMap:null,cellDuration:null,elsByFill:null,render:function(){this.bindHandlers()},destroy:function(){},buildCoords:function(){},getCellDate:function(){},getCellDayEl:function(){},rangeToSegs:function(){},bindHandlers:function(){var e=this;this.el.on("mousedown",function(n){t(n.target).is(".fc-event-container *, .fc-more")||t(n.target).closest(".fc-popover").length||e.dayMousedown(n)}),this.bindSegHandlers()},dayMousedown:function(t){var e,n,i,r=this,s=this.view,o=s.calendar,l=s.opt("selectable"),a=null,c=new Q(this.coordMap,{scroll:s.opt("dragScroll"),dragStart:function(){s.unselect()},cellOver:function(t,s){c.origDate&&(i=r.getCellDayEl(t),a=[s,c.origDate].sort(x),e=a[0],n=a[1].clone().add(r.cellDuration),l&&(o.isSelectionAllowedInRange(e,n)?r.renderSelection(e,n):(a=null,f())))},cellOut:function(){a=null,r.destroySelection(),g()},listenStop:function(t){a&&(a[0].isSame(a[1])&&s.trigger("dayClick",i[0],e,t),l&&s.reportSelection(e,n,t)),g()}});c.mousedown(t)},renderDrag:function(){},destroyDrag:function(){},renderResize:function(){},destroyResize:function(){},renderRangeHelper:function(t,e,n){var i,r=this.view;!e&&r.opt("forceEventDuration")&&(e=r.calendar.getDefaultEventEnd(!t.hasTime(),t)),i=n?M(n.event):{},i.start=t,i.end=e,i.allDay=!(t.hasTime()||e&&e.hasTime()),i.className=(i.className||[]).concat("fc-helper"),n||(i.editable=!1),this.renderHelper(i,n)},renderHelper:function(){},destroyHelper:function(){},renderSelection:function(t,e){this.renderHighlight(t,e)},destroySelection:function(){this.destroyHighlight()},renderHighlight:function(t,e){this.renderFill("highlight",this.rangeToSegs(t,e))},destroyHighlight:function(){this.destroyFill("highlight")},highlightSegClasses:function(){return["fc-highlight"]},renderFill:function(){},destroyFill:function(t){var e=this.elsByFill[t];e&&(e.remove(),delete this.elsByFill[t])},renderFillSegEls:function(e,n){var i,r=this,s=this[e+"SegEl"],o="",l=[];if(n.length){for(i=0;n.length>i;i++)o+=this.fillSegHtml(e,n[i]);t(o).each(function(e,i){var o=n[e],a=t(i);s&&(a=s.call(r,o,a)),a&&(a=t(a),a.is(r.fillSegTag)&&(o.el=a,l.push(o)))})}return l},fillSegTag:"div",fillSegHtml:function(t,e){var n=this[t+"SegClasses"],i=this[t+"SegStyles"],r=n?n.call(this,e):[],s=i?i.call(this,e):"";return"<"+this.fillSegTag+(r.length?' class="'+r.join(" ")+'"':"")+(s?' style="'+s+'"':"")+" />"},headHtml:function(){return'
'+""+""+this.rowHtml("head")+""+"
"+"
"},headCellHtml:function(t,e,n){var i=this.view,r=i.calendar,s=i.opt("columnFormat");return''+F(r.formatDate(n,s))+""},bgCellHtml:function(t,e,n){var i=this.view,r=this.getDayClasses(n);return r.unshift("fc-day",i.widgetContentClass),''},getDayClasses:function(t){var e=this.view,n=e.calendar.getNow().stripTime(),i=["fc-"+Ye[t.day()]];return"month"===e.name&&t.month()!=e.intervalStart.month()&&i.push("fc-other-month"),t.isSame(n,"day")?i.push("fc-today",e.highlightStateClass):n>t?i.push("fc-past"):i.push("fc-future"),i}}),t.extend(ne.prototype,{mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,segs:null,renderEvents:function(t){var e,n,i=this.eventsToSegs(t),r=[],s=[];for(e=0;i.length>e;e++)n=i[e],ie(n.event)?r.push(n):s.push(n);r=this.renderBgSegs(r)||r,s=this.renderFgSegs(s)||s,this.segs=r.concat(s)},destroyEvents:function(){this.triggerSegMouseout(),this.destroyFgSegs(),this.destroyBgSegs(),this.segs=null},getSegs:function(){return this.segs||[]},renderFgSegs:function(){},destroyFgSegs:function(){},renderFgSegEls:function(e,n){var i,r=this.view,s="",o=[];if(e.length){for(i=0;e.length>i;i++)s+=this.fgSegHtml(e[i],n);t(s).each(function(n,i){var s=e[n],l=r.resolveEventEl(s.event,t(i));l&&(l.data("fc-seg",s),s.el=l,o.push(s))})}return o},fgSegHtml:function(){},renderBgSegs:function(t){return this.renderFill("bgEvent",t)},destroyBgSegs:function(){this.destroyFill("bgEvent")},bgEventSegEl:function(t,e){return this.view.resolveEventEl(t.event,e)},bgEventSegClasses:function(t){var e=t.event,n=e.source||{};return["fc-bgevent"].concat(e.className,n.className||[])},bgEventSegStyles:function(t){var e=this.view,n=t.event,i=n.source||{},r=n.color,s=i.color,o=e.opt("eventColor"),l=n.backgroundColor||r||i.backgroundColor||s||e.opt("eventBackgroundColor")||o;return l?"background-color:"+l:""},businessHoursSegClasses:function(){return["fc-nonbusiness","fc-bgevent"]},bindSegHandlers:function(){var e=this,n=this.view;t.each({mouseenter:function(t,n){e.triggerSegMouseover(t,n)},mouseleave:function(t,n){e.triggerSegMouseout(t,n)},click:function(t,e){return n.trigger("eventClick",this,t.event,e)},mousedown:function(i,r){t(r.target).is(".fc-resizer")&&n.isEventResizable(i.event)?e.segResizeMousedown(i,r):n.isEventDraggable(i.event)&&e.segDragMousedown(i,r)}},function(n,i){e.el.on(n,".fc-event-container > *",function(n){var r=t(this).data("fc-seg");return!r||e.isDraggingSeg||e.isResizingSeg?void 0:i.call(this,r,n)})})},triggerSegMouseover:function(t,e){this.mousedOverSeg||(this.mousedOverSeg=t,this.view.trigger("eventMouseover",t.el[0],t.event,e))},triggerSegMouseout:function(t,e){e=e||{},this.mousedOverSeg&&(t=t||this.mousedOverSeg,this.mousedOverSeg=null,this.view.trigger("eventMouseout",t.el[0],t.event,e))},segDragMousedown:function(t,e){var n,i,r=this,s=this.view,o=s.calendar,l=t.el,a=t.event,c=new te(t.el,{parentEl:s.el,opacity:s.opt("dragOpacity"),revertDuration:s.opt("dragRevertDuration"),zIndex:2}),d=new Q(s.coordMap,{distance:5,scroll:s.opt("dragScroll"),listenStart:function(t){c.hide(),c.start(t)},dragStart:function(e){r.triggerSegMouseout(t,e),r.isDraggingSeg=!0,s.hideEvent(a),s.trigger("eventDragStart",l[0],a,e,{})},cellOver:function(e,l){var u=t.cellDate||d.origDate,h=r.computeDraggedEventDates(t,u,l);n=h.start,i=h.end,o.isEventAllowedInRange(a,n,h.visibleEnd)?s.renderDrag(n,i,t)?c.hide():c.show():(n=null,c.show(),f())},cellOut:function(){n=null,s.destroyDrag(),c.show(),g()},dragStop:function(t){var e=n&&!n.isSame(a.start);c.stop(!e,function(){r.isDraggingSeg=!1,s.destroyDrag(),s.showEvent(a),s.trigger("eventDragStop",l[0],a,t,{}),e&&s.eventDrop(l[0],a,n,t)}),g()},listenStop:function(){c.stop()}});d.mousedown(e)},computeDraggedEventDates:function(t,e,n){var i,r,s,o,l,a=this.view,c=t.event,d=c.start,u=a.calendar.getEventEnd(c);return n.hasTime()===e.hasTime()?(i=C(n,e),r=d.clone().add(i),s=null===c.end?null:u.clone().add(i),o=c.allDay):(r=n,s=null,o=!n.hasTime()),l=s||a.calendar.getDefaultEventEnd(o,r),{start:r,end:s,visibleEnd:l}},segResizeMousedown:function(t,e){function n(){r.destroyResize(),s.showEvent(a)}var i,r=this,s=this.view,o=s.calendar,l=t.el,a=t.event,c=a.start,d=s.calendar.getEventEnd(a),u=null;i=new Q(this.coordMap,{distance:5,scroll:s.opt("dragScroll"),dragStart:function(e){r.triggerSegMouseout(t,e),r.isResizingSeg=!0,s.trigger("eventResizeStart",l[0],a,e,{})},cellOver:function(e,i){i.isBefore(c)&&(i=c),u=i.clone().add(r.cellDuration),o.isEventAllowedInRange(a,c,u)?u.isSame(d)?(u=null,n()):(r.renderResize(c,u,t),s.hideEvent(a)):(u=null,n(),f())},cellOut:function(){u=null,n(),g()},dragStop:function(t){r.isResizingSeg=!1,n(),g(),s.trigger("eventResizeStop",l[0],a,t,{}),u&&s.eventResize(l[0],a,u,t)}}),i.mousedown(e)},getSegClasses:function(t,e,n){var i=t.event,r=["fc-event",t.isStart?"fc-start":"fc-not-start",t.isEnd?"fc-end":"fc-not-end"].concat(i.className,i.source?i.source.className:[]);return e&&r.push("fc-draggable"),n&&r.push("fc-resizable"),r},getEventSkinCss:function(t){var e=this.view,n=t.source||{},i=t.color,r=n.color,s=e.opt("eventColor"),o=t.backgroundColor||i||n.backgroundColor||r||e.opt("eventBackgroundColor")||s,l=t.borderColor||i||n.borderColor||r||e.opt("eventBorderColor")||s,a=t.textColor||n.textColor||e.opt("eventTextColor"),c=[];return o&&c.push("background-color:"+o),l&&c.push("border-color:"+l),a&&c.push("color:"+a),c.join(";")},eventsToSegs:function(t,e){var n,i=this.eventsToRanges(t),r=[];for(n=0;i.length>n;n++)r.push.apply(r,this.eventRangeToSegs(i[n],e));return r},eventsToRanges:function(e){var n=this,i=oe(e),r=[];return t.each(i,function(t,e){e.length&&r.push.apply(r,re(e[0])?n.eventsToInverseRanges(e):n.eventsToNormalRanges(e))}),r},eventsToNormalRanges:function(t){var e,n,i,r,s=this.view.calendar,o=[];for(e=0;t.length>e;e++)n=t[e],i=n.start.clone().stripZone(),r=s.getEventEnd(n).stripZone(),o.push({event:n,start:i,end:r,eventStartMS:+i,eventDurationMS:r-i});return o},eventsToInverseRanges:function(t){var e,n,i=this.view,r=i.start.clone().stripZone(),s=i.end.clone().stripZone(),o=this.eventsToNormalRanges(t),l=[],a=t[0],c=r;for(o.sort(le),e=0;o.length>e;e++)n=o[e],n.start>c&&l.push({event:a,start:c,end:n.start}),c=n.end;return s>c&&l.push({event:a,start:c,end:s}),l},eventRangeToSegs:function(t,e){var n,i,r;for(n=e?e(t.start,t.end):this.rangeToSegs(t.start,t.end),i=0;n.length>i;i++)r=n[i],r.event=t.event,r.eventStartMS=t.eventStartMS,r.eventDurationMS=t.eventDurationMS;return n}}),ce.prototype=M(ne.prototype),t.extend(ce.prototype,{numbersVisible:!1,cellDuration:e.duration({days:1}),bottomCoordPadding:0,rowEls:null,dayEls:null,helperEls:null,render:function(e){var n,i=this.view,r="";for(n=0;i.rowCnt>n;n++)r+=this.dayRowHtml(n,e);this.el.html(r),this.rowEls=this.el.find(".fc-row"),this.dayEls=this.el.find(".fc-day"),this.dayEls.each(function(e,n){var r=i.cellToDate(Math.floor(e/i.colCnt),e%i.colCnt);i.trigger("dayRender",null,r,t(n))}),ne.prototype.render.call(this)},destroy:function(){this.destroySegPopover()},dayRowHtml:function(t,e){var n=this.view,i=["fc-row","fc-week",n.widgetContentClass];return e&&i.push("fc-rigid"),'
'+'
'+""+this.rowHtml("day",t)+"
"+"
"+'
'+""+(this.numbersVisible?""+this.rowHtml("number",t)+"":"")+"
"+"
"+"
"},dayCellHtml:function(t,e,n){return this.bgCellHtml(t,e,n)},buildCoords:function(e,n){var i,r,s,o=this.view.colCnt;this.dayEls.slice(0,o).each(function(e,o){i=t(o),r=i.offset().left,e&&(s[1]=r),s=[r],n[e]=s}),s[1]=r+i.outerWidth(),this.rowEls.each(function(n,o){i=t(o),r=i.offset().top,n&&(s[1]=r),s=[r],e[n]=s}),s[1]=r+i.outerHeight()+this.bottomCoordPadding},getCellDate:function(t){return this.view.cellToDate(t)},getCellDayEl:function(t){return this.dayEls.eq(t.row*this.view.colCnt+t.col)},rangeToSegs:function(t,e){return this.view.rangeToSegments(t,e)},renderDrag:function(t,e,n){var i;return this.renderHighlight(t,e||this.view.calendar.getDefaultEventEnd(!0,t)),n&&!n.el.closest(this.el).length?(this.renderRangeHelper(t,e,n),i=this.view.opt("dragOpacity"),void 0!==i&&this.helperEls.css("opacity",i),!0):void 0},destroyDrag:function(){this.destroyHighlight(),this.destroyHelper()},renderResize:function(t,e,n){this.renderHighlight(t,e),this.renderRangeHelper(t,e,n)},destroyResize:function(){this.destroyHighlight(),this.destroyHelper()},renderHelper:function(e,n){var i,r=[],s=this.eventsToSegs([e]);s=this.renderFgSegEls(s),i=this.renderSegRows(s),this.rowEls.each(function(e,s){var o,l=t(s),a=t('
');o=n&&n.row===e?n.el.position().top:l.find(".fc-content-skeleton tbody").position().top,a.css("top",o).find("table").append(i[e].tbodyEl),l.append(a),r.push(a[0])}),this.helperEls=t(r)},destroyHelper:function(){this.helperEls&&(this.helperEls.remove(),this.helperEls=null)},fillSegTag:"td",renderFill:function(e,n){var i,r,s,o=[];for(n=this.renderFillSegEls(e,n),i=0;n.length>i;i++)r=n[i],s=this.renderFillRow(e,r),this.rowEls.eq(r.row).append(s),o.push(s[0]);return this.elsByFill[e]=t(o),n},renderFillRow:function(e,n){var i,r,s=this.view.colCnt,o=n.leftCol,l=n.rightCol+1;return i=t('
'+"
"+"
"),r=i.find("tr"),o>0&&r.append(''),r.append(n.el.attr("colspan",l-o)),s>l&&r.append(''),this.bookendCells(r,e),i}}),t.extend(ce.prototype,{rowStructs:null,destroyEvents:function(){this.destroySegPopover(),ne.prototype.destroyEvents.apply(this,arguments)},getSegs:function(){return ne.prototype.getSegs.call(this).concat(this.popoverSegs||[])},renderBgSegs:function(e){var n=t.grep(e,function(t){return t.event.allDay});return ne.prototype.renderBgSegs.call(this,n)},renderFgSegs:function(e){var n;return e=this.renderFgSegEls(e),n=this.rowStructs=this.renderSegRows(e),this.rowEls.each(function(e,i){t(i).find(".fc-content-skeleton > table").append(n[e].tbodyEl)}),e},destroyFgSegs:function(){for(var t,e=this.rowStructs||[];t=e.pop();)t.tbodyEl.remove();this.rowStructs=null},renderSegRows:function(t){var e,n,i=[];for(e=this.groupSegRows(t),n=0;e.length>n;n++)i.push(this.renderSegRow(n,e[n]));return i},fgSegHtml:function(t,e){var n,i=this.view,r=i.opt("isRTL"),s=t.event,o=i.isEventDraggable(s),l=!e&&s.allDay&&t.isEnd&&i.isEventResizable(s),a=this.getSegClasses(t,o,l),c=this.getEventSkinCss(s),d="";return a.unshift("fc-day-grid-event"),!s.allDay&&t.isStart&&(d=''+F(i.getEventTimeText(s))+""),n=''+(F(s.title||"")||" ")+"",'
"+'
'+(r?n+" "+d:d+" "+n)+"
"+(l?'
':"")+""},renderSegRow:function(e,n){function i(e){for(;e>o;)d=(y[r-1]||[])[o],d?d.attr("rowspan",parseInt(d.attr("rowspan")||1,10)+1):(d=t(""),l.append(d)),v[r][o]=d,y[r][o]=d,o++}var r,s,o,l,a,c,d,u=this.view,h=u.colCnt,f=this.buildSegLevels(n),g=Math.max(1,f.length),p=t(""),m=[],v=[],y=[];for(r=0;g>r;r++){if(s=f[r],o=0,l=t(""),m.push([]),v.push([]),y.push([]),s)for(a=0;s.length>a;a++){for(c=s[a],i(c.leftCol),d=t('').append(c.el),c.leftCol!=c.rightCol?d.attr("colspan",c.rightCol-c.leftCol+1):y[r][o]=d;c.rightCol>=o;)v[r][o]=d,m[r][o]=c,o++;l.append(d)}i(h),this.bookendCells(l,"eventSkeleton"),p.append(l)}return{row:e,tbodyEl:p,cellMatrix:v,segMatrix:m,segLevels:f,segs:n}},buildSegLevels:function(t){var e,n,i,r=[];for(t.sort(ae),e=0;t.length>e;e++){for(n=t[e],i=0;r.length>i&&de(n,r[i]);i++);n.level=i,(r[i]||(r[i]=[])).push(n)}for(i=0;r.length>i;i++)r[i].sort(ue);return r},groupSegRows:function(t){var e,n=this.view,i=[];for(e=0;n.rowCnt>e;e++)i.push([]);for(e=0;t.length>e;e++)i[t[e].row].push(t[e]);return i}}),t.extend(ce.prototype,{segPopover:null,popoverSegs:null,destroySegPopover:function(){this.segPopover&&this.segPopover.hide()},limitRows:function(t){var e,n,i=this.rowStructs||[];for(e=0;i.length>e;e++)this.unlimitRow(e),n=t?"number"==typeof t?t:this.computeRowLevelLimit(e):!1,n!==!1&&this.limitRow(e,n)},computeRowLevelLimit:function(t){var e,n,i=this.rowEls.eq(t),r=i.height(),s=this.rowStructs[t].tbodyEl.children();for(e=0;s.length>e;e++)if(n=s.eq(e).removeClass("fc-limited"),n.position().top+n.outerHeight()>r)return e;return!1},limitRow:function(e,n){function i(i){for(;i>T;)r={row:e,col:T},d=S.getCellSegs(r,n),d.length&&(f=o[n-1][T],w=S.renderMoreLink(r,d),y=t("
").append(w),f.append(y),D.push(y[0])),T++}var r,s,o,l,a,c,d,u,h,f,g,p,m,v,y,w,S=this,b=this.view,E=this.rowStructs[e],D=[],T=0;if(n&&E.segLevels.length>n){for(s=E.segLevels[n-1],o=E.cellMatrix,l=E.tbodyEl.children().slice(n).addClass("fc-limited").get(),a=0;s.length>a;a++){for(c=s[a],i(c.leftCol),h=[],u=0;c.rightCol>=T;)r={row:e,col:T},d=this.getCellSegs(r,n),h.push(d),u+=d.length,T++;if(u){for(f=o[n-1][c.leftCol],g=f.attr("rowspan")||1,p=[],m=0;h.length>m;m++)v=t('').attr("rowspan",g),d=h[m],r={row:e,col:c.leftCol+m},w=this.renderMoreLink(r,[c].concat(d)),y=t("
").append(w),v.append(y),p.push(v[0]),D.push(v[0]);f.addClass("fc-limited").after(t(p)),l.push(f[0])}}i(b.colCnt),E.moreEls=t(D),E.limitedEls=t(l)}},unlimitRow:function(t){var e=this.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},renderMoreLink:function(e,n){var i=this,r=this.view;return t('').text(this.getMoreLinkText(n.length)).on("click",function(s){var o=r.opt("eventLimitClick"),l=r.cellToDate(e),a=t(this),c=i.getCellDayEl(e),d=i.getCellSegs(e),u=i.resliceDaySegs(d,l),h=i.resliceDaySegs(n,l);"function"==typeof o&&(o=r.trigger("eventLimitClick",null,{date:l,dayEl:c,moreEl:a,segs:u,hiddenSegs:h},s)),"popover"===o?i.showSegPopover(l,e,a,u):"string"==typeof o&&r.calendar.zoomTo(l,o)})},showSegPopover:function(t,e,n,i){var r,s,o=this,l=this.view,a=n.parent();r=1==l.rowCnt?this.view.el:this.rowEls.eq(e.row),s={className:"fc-more-popover",content:this.renderSegPopoverContent(t,i),parentEl:this.el,top:r.offset().top,autoHide:!0,viewportConstrain:l.opt("popoverViewportConstrain"),hide:function(){o.segPopover.destroy(),o.segPopover=null,o.popoverSegs=null}},l.opt("isRTL")?s.right=a.offset().left+a.outerWidth()+1:s.left=a.offset().left-1,this.segPopover=new q(s),this.segPopover.show()},renderSegPopoverContent:function(e,n){var i,r=this.view,s=r.opt("theme"),o=e.format(r.opt("dayPopoverFormat")),l=t('
'+''+''+F(o)+""+'
'+"
"+'
'+'
'+"
"),a=l.find(".fc-event-container");for(n=this.renderFgSegEls(n,!0),this.popoverSegs=n,i=0;n.length>i;i++)n[i].cellDate=e,a.append(n[i].el);return l},resliceDaySegs:function(e,n){var i=t.map(e,function(t){return t.event}),r=n.clone().stripTime(),s=r.clone().add(1,"days");return this.eventsToSegs(i,function(t,e){var n=D(t,e,r,s);return n?[n]:[]})},getMoreLinkText:function(t){var e=this.view,n=e.opt("eventLimitText");return"function"==typeof n?n(t):"+"+t+" "+n},getCellSegs:function(t,e){for(var n,i=this.rowStructs[t.row].segMatrix,r=e||0,s=[];i.length>r;)n=i[r][t.col],n&&s.push(n),r++;return s}}),he.prototype=M(ne.prototype),t.extend(he.prototype,{slotDuration:null,snapDuration:null,minTime:null,maxTime:null,dayEls:null,slatEls:null,slatTops:null,helperEl:null,businessHourSegs:null,render:function(){this.processOptions(),this.el.html(this.renderHtml()),this.dayEls=this.el.find(".fc-day"),this.slatEls=this.el.find(".fc-slats tr"),this.computeSlatTops(),this.renderBusinessHours(),ne.prototype.render.call(this)},renderBusinessHours:function(){var t=this.view.calendar.getBusinessHoursEvents();this.businessHourSegs=this.renderFill("businessHours",this.eventsToSegs(t),"bgevent")},renderHtml:function(){return'
'+this.rowHtml("slotBg")+"
"+"
"+'
'+""+this.slatRowHtml()+"
"+"
"},slotBgCellHtml:function(t,e,n){return this.bgCellHtml(t,e,n)},slatRowHtml:function(){for(var t,n,i,r=this.view,s=r.calendar,o=r.opt("isRTL"),l="",a=0===this.slotDuration.asMinutes()%15,c=e.duration(+this.minTime);this.maxTime>c;)t=r.start.clone().time(c),n=t.minutes(),i='"+(a&&n?"":""+F(s.formatDate(t,r.opt("axisFormat")))+"")+"",l+=""+(o?"":i)+''+(o?i:"")+"",c.add(this.slotDuration);return l},processOptions:function(){var t=this.view,n=t.opt("slotDuration"),i=t.opt("snapDuration");n=e.duration(n),i=i?e.duration(i):n,this.slotDuration=n,this.snapDuration=i,this.cellDuration=i,this.minTime=e.duration(t.opt("minTime")),this.maxTime=e.duration(t.opt("maxTime"))},rangeToSegs:function(t,e){var n,i,r,s,o,l=this.view,a=[];for(t=t.clone().stripZone(),e=e.clone().stripZone(),i=0;l.colCnt>i;i++)r=l.cellToDate(0,i),s=r.clone().time(this.minTime),o=r.clone().time(this.maxTime),n=D(t,e,s,o),n&&(n.col=i,a.push(n));return a},resize:function(){this.computeSlatTops(),this.updateSegVerticals()},buildCoords:function(n,i){var r,s,o=this.view.colCnt,l=this.el.offset().top,a=e.duration(+this.minTime),c=null;for(this.dayEls.slice(0,o).each(function(e,n){r=t(n),s=r.offset().left,c&&(c[1]=s),c=[s],i[e]=c}),c[1]=s+r.outerWidth(),c=null;this.maxTime>a;)s=l+this.computeTimeTop(a),c&&(c[1]=s),c=[s],n.push(c),a.add(this.snapDuration);c[1]=l+this.computeTimeTop(a)},getCellDate:function(t){var e=this.view,n=e.calendar;return n.rezoneDate(e.cellToDate(0,t.col).time(this.minTime+this.snapDuration*t.row))},getCellDayEl:function(t){return this.dayEls.eq(t.col)},computeDateTop:function(t,n){return this.computeTimeTop(e.duration(t.clone().stripZone()-n.clone().stripTime()))},computeTimeTop:function(t){var e,n,i,r,s=(t-this.minTime)/this.slotDuration;return s=Math.max(0,s),s=Math.min(this.slatEls.length,s),e=Math.floor(s),n=s-e,i=this.slatTops[e],n?(r=this.slatTops[e+1],i+(r-i)*n):i},computeSlatTops:function(){var e,n=[];this.slatEls.each(function(i,r){e=t(r).position().top,n.push(e)}),n.push(e+this.slatEls.last().outerHeight()),this.slatTops=n},renderDrag:function(t,e,n){var i;return n?(this.renderRangeHelper(t,e,n),i=this.view.opt("dragOpacity"),void 0!==i&&this.helperEl.css("opacity",i),!0):(this.renderHighlight(t,e||this.view.calendar.getDefaultEventEnd(!1,t)),void 0)},destroyDrag:function(){this.destroyHelper(),this.destroyHighlight()},renderResize:function(t,e,n){this.renderRangeHelper(t,e,n)},destroyResize:function(){this.destroyHelper()},renderHelper:function(e,n){var i,r,s,o,l=this.eventsToSegs([e]);for(l=this.renderFgSegEls(l),i=this.renderSegTable(l),r=0;l.length>r;r++)s=l[r],n&&n.col===s.col&&(o=n.el,s.el.css({left:o.css("left"),right:o.css("right"),"margin-left":o.css("margin-left"),"margin-right":o.css("margin-right")}));this.helperEl=t('
').append(i).appendTo(this.el)},destroyHelper:function(){this.helperEl&&(this.helperEl.remove(),this.helperEl=null)},renderSelection:function(t,e){this.view.opt("selectHelper")?this.renderRangeHelper(t,e):this.renderHighlight(t,e)},destroySelection:function(){this.destroyHelper(),this.destroyHighlight()},renderFill:function(e,n,i){var r,s,o,l,a,c,d,u,h,f,g=this.view;if(n.length){for(n=this.renderFillSegEls(e,n),r=this.groupSegCols(n),i=i||e.toLowerCase(),s=t('
'+"
"+"
"),o=s.find("tr"),l=0;r.length>l;l++)if(a=r[l],c=t("").appendTo(o),a.length)for(d=t('
').appendTo(c),u=g.cellToDate(0,l),h=0;a.length>h;h++)f=a[h],d.append(f.el.css({top:this.computeDateTop(f.start,u),bottom:-this.computeDateTop(f.end,u)}));this.bookendCells(o,e),this.el.append(s),this.elsByFill[e]=s}return n}}),t.extend(he.prototype,{eventSkeletonEl:null,renderFgSegs:function(e){return e=this.renderFgSegEls(e),this.el.append(this.eventSkeletonEl=t('
').append(this.renderSegTable(e))),e},destroyFgSegs:function(){this.eventSkeletonEl&&(this.eventSkeletonEl.remove(),this.eventSkeletonEl=null)},renderSegTable:function(e){var n,i,r,s,o,l,a=t("
"),c=a.find("tr");for(n=this.groupSegCols(e),this.computeSegVerticals(e),s=0;n.length>s;s++){for(o=n[s],fe(o),l=t('
'),i=0;o.length>i;i++)r=o[i],r.el.css(this.generateSegPositionCss(r)),30>r.bottom-r.top&&r.el.addClass("fc-short"),l.append(r.el);c.append(t("").append(l))}return this.bookendCells(c,"eventSkeleton"),a +},updateSegVerticals:function(){var t,e=(this.segs||[]).concat(this.businessHourSegs||[]);for(this.computeSegVerticals(e),t=0;e.length>t;t++)e[t].el.css(this.generateSegVerticalCss(e[t]))},computeSegVerticals:function(t){var e,n;for(e=0;t.length>e;e++)n=t[e],n.top=this.computeDateTop(n.start,n.start),n.bottom=this.computeDateTop(n.end,n.start)},fgSegHtml:function(t,e){var n,i,r,s=this.view,o=t.event,l=s.isEventDraggable(o),a=!e&&t.isEnd&&s.isEventResizable(o),c=this.getSegClasses(t,l,a),d=this.getEventSkinCss(o);return c.unshift("fc-time-grid-event"),s.isMultiDayEvent(o)?(t.isStart||t.isEnd)&&(n=s.getEventTimeText(t.start,t.end),i=s.getEventTimeText(t.start,t.end,"LT"),r=s.getEventTimeText(t.start,null)):(n=s.getEventTimeText(o),i=s.getEventTimeText(o,"LT"),r=s.getEventTimeText(o.start,null)),'"+'
'+(n?'
"+""+F(n)+""+"
":"")+(o.title?'
'+F(o.title)+"
":"")+"
"+'
'+(a?'
':"")+""},generateSegPositionCss:function(t){var e,n,i=this.view,r=i.opt("isRTL"),s=i.opt("slotEventOverlap"),o=t.backwardCoord,l=t.forwardCoord,a=this.generateSegVerticalCss(t);return s&&(l=Math.min(1,o+2*(l-o))),r?(e=1-l,n=o):(e=o,n=1-l),a.zIndex=t.level+1,a.left=100*e+"%",a.right=100*n+"%",s&&t.forwardPressure&&(a[r?"marginLeft":"marginRight"]=20),a},generateSegVerticalCss:function(t){return{top:t.top,bottom:-t.bottom}},groupSegCols:function(t){var e,n=this.view,i=[];for(e=0;n.colCnt>e;e++)i.push([]);for(e=0;t.length>e;e++)i[t[e].col].push(t[e]);return i}}),be.prototype={calendar:null,coordMap:null,el:null,start:null,end:null,intervalStart:null,intervalEnd:null,rowCnt:null,colCnt:null,isSelected:!1,scrollerEl:null,scrollTop:null,widgetHeaderClass:null,widgetContentClass:null,highlightStateClass:null,documentMousedownProxy:null,documentDragStartProxy:null,init:function(){var e=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=e+"-widget-header",this.widgetContentClass=e+"-widget-content",this.highlightStateClass=e+"-state-highlight",this.documentMousedownProxy=t.proxy(this,"documentMousedown"),this.documentDragStartProxy=t.proxy(this,"documentDragStart")},render:function(){this.updateSize(),this.trigger("viewRender",this,this,this.el),t(document).on("mousedown",this.documentMousedownProxy).on("dragstart",this.documentDragStartProxy)},destroy:function(){this.unselect(),this.trigger("viewDestroy",this,this,this.el),this.destroyEvents(),this.el.empty(),t(document).off("mousedown",this.documentMousedownProxy).off("dragstart",this.documentDragStartProxy)},incrementDate:function(){},updateSize:function(t){t&&this.recordScroll(),this.updateHeight(),this.updateWidth()},updateWidth:function(){},updateHeight:function(){var t=this.calendar;this.setHeight(t.getSuggestedViewHeight(),t.isHeightAuto())},setHeight:function(){},computeScrollerHeight:function(t){var e,n=this.el.add(this.scrollerEl);return n.css({position:"relative",left:-1}),e=this.el.outerHeight()-this.scrollerEl.height(),n.css({position:"",left:""}),t-e},recordScroll:function(){this.scrollerEl&&(this.scrollTop=this.scrollerEl.scrollTop())},restoreScroll:function(){null!==this.scrollTop&&this.scrollerEl.scrollTop(this.scrollTop)},renderEvents:function(){this.segEach(function(t){this.trigger("eventAfterRender",t.event,t.event,t.el)}),this.trigger("eventAfterAllRender")},destroyEvents:function(){this.segEach(function(t){this.trigger("eventDestroy",t.event,t.event,t.el)})},resolveEventEl:function(e,n){var i=this.trigger("eventRender",e,e,n);return i===!1?n=null:i&&i!==!0&&(n=t(i)),n},showEvent:function(t){this.segEach(function(t){t.el.css("visibility","")},t)},hideEvent:function(t){this.segEach(function(t){t.el.css("visibility","hidden")},t)},segEach:function(t,e){var n,i=this.getSegs();for(n=0;i.length>n;n++)e&&i[n].event._id!==e._id||t.call(this,i[n])},getSegs:function(){},renderDrag:function(){},destroyDrag:function(){},documentDragStart:function(e){var n,i,r,s,o,l=this,a=this.calendar,c=null,d=null,u=null;this.opt("droppable")&&(n=t(e.target),i=this.opt("dropAccept"),(t.isFunction(i)?i.call(n[0],n):n.is(i))&&(r=Ee(n),s=r.eventProps,o=new Q(this.coordMap,{cellOver:function(e,n){c=n,d=r.duration?c.clone().add(r.duration):null,u=d||a.getDefaultEventEnd(!c.hasTime(),c),s&&t.extend(s,{start:c,end:d}),a.isExternalDragAllowedInRange(c,u,s)?l.renderDrag(c,u):(c=null,f())},cellOut:function(){c=null,l.destroyDrag(),g()}}),t(document).one("dragstop",function(t,e){var i;l.destroyDrag(),g(),c&&(r.startTime&&!c.hasTime()&&c.time(r.startTime),l.trigger("drop",n[0],c,t,e),s&&(i=a.renderEvent(s,r.stick),l.trigger("eventReceive",null,i[0])))}),o.startDrag(e)))},select:function(t,e,n){this.unselect(n),this.renderSelection(t,e),this.reportSelection(t,e,n)},renderSelection:function(){},reportSelection:function(t,e,n){this.isSelected=!0,this.trigger("select",null,t,e,n)},unselect:function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection(),this.trigger("unselect",null,t))},destroySelection:function(){},documentMousedown:function(e){var n;this.isSelected&&this.opt("unselectAuto")&&E(e)&&(n=this.opt("unselectCancel"),n&&t(e.target).closest(n).length||this.unselect(e))}},_e.dataAttrPrefix="",De.prototype=M(be.prototype),t.extend(De.prototype,{dayGrid:null,dayNumbersVisible:!1,weekNumbersVisible:!1,weekNumberWidth:null,headRowEl:null,render:function(t,e,n){this.rowCnt=t,this.colCnt=e,this.dayNumbersVisible=n,this.weekNumbersVisible=this.opt("weekNumbers"),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.weekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderHtml()),this.headRowEl=this.el.find("thead .fc-row"),this.scrollerEl=this.el.find(".fc-day-grid-container"),this.dayGrid.coordMap.containerEl=this.scrollerEl,this.dayGrid.el=this.el.find(".fc-day-grid"),this.dayGrid.render(this.hasRigidRows()),be.prototype.render.call(this)},destroy:function(){this.dayGrid.destroy(),be.prototype.destroy.call(this)},renderHtml:function(){return'"+""+""+""+""+'"+""+""+"
'+this.dayGrid.headHtml()+"
'+'
'+'
'+"
"+"
"},headIntroHtml:function(){return this.weekNumbersVisible?'"+""+F(this.opt("weekNumberTitle"))+""+"":void 0},numberIntroHtml:function(t){return this.weekNumbersVisible?'"+""+this.calendar.calculateWeekNumber(this.cellToDate(t,0))+""+"":void 0},dayIntroHtml:function(){return this.weekNumbersVisible?'":void 0},introHtml:function(){return this.weekNumbersVisible?'":void 0},numberCellHtml:function(t,e,n){var i;return this.dayNumbersVisible?(i=this.dayGrid.getDayClasses(n),i.unshift("fc-day-number"),''+n.date()+""):""},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},updateWidth:function(){this.weekNumbersVisible&&(this.weekNumberWidth=v(this.el.find(".fc-week-number")))},setHeight:function(t,e){var n,i=this.opt("eventLimit");w(this.scrollerEl),h(this.headRowEl),this.dayGrid.destroySegPopover(),i&&"number"==typeof i&&this.dayGrid.limitRows(i),n=this.computeScrollerHeight(t),this.setGridHeight(n,e),i&&"number"!=typeof i&&this.dayGrid.limitRows(i),!e&&y(this.scrollerEl,n)&&(u(this.headRowEl,b(this.scrollerEl)),n=this.computeScrollerHeight(t),this.scrollerEl.height(n),this.restoreScroll())},setGridHeight:function(t,e){e?m(this.dayGrid.rowEls):p(this.dayGrid.rowEls,t,!0)},renderEvents:function(t){this.dayGrid.renderEvents(t),this.updateHeight(),be.prototype.renderEvents.call(this,t)},getSegs:function(){return this.dayGrid.getSegs()},destroyEvents:function(){be.prototype.destroyEvents.call(this),this.recordScroll(),this.dayGrid.destroyEvents()},renderDrag:function(t,e,n){return this.dayGrid.renderDrag(t,e,n)},destroyDrag:function(){this.dayGrid.destroyDrag()},renderSelection:function(t,e){this.dayGrid.renderSelection(t,e)},destroySelection:function(){this.dayGrid.destroySelection()}}),r({fixedWeekCount:!0}),Ge.month=Te,Te.prototype=M(De.prototype),t.extend(Te.prototype,{name:"month",incrementDate:function(t,e){return t.clone().stripTime().add(e,"months").startOf("month")},render:function(t){var e;this.intervalStart=t.clone().stripTime().startOf("month"),this.intervalEnd=this.intervalStart.clone().add(1,"months"),this.start=this.intervalStart.clone(),this.start=this.skipHiddenDays(this.start),this.start.startOf("week"),this.start=this.skipHiddenDays(this.start),this.end=this.intervalEnd.clone(),this.end=this.skipHiddenDays(this.end,-1,!0),this.end.add((7-this.end.weekday())%7,"days"),this.end=this.skipHiddenDays(this.end,-1,!0),e=Math.ceil(this.end.diff(this.start,"weeks",!0)),this.isFixedWeeks()&&(this.end.add(6-e,"weeks"),e=6),this.title=this.calendar.formatDate(this.intervalStart,this.opt("titleFormat")),De.prototype.render.call(this,e,this.getCellsPerWeek(),!0)},setGridHeight:function(t,e){e=e||"variable"===this.opt("weekMode"),e&&(t*=this.rowCnt/6),p(this.dayGrid.rowEls,t,!e)},isFixedWeeks:function(){var t=this.opt("weekMode");return t?"fixed"===t:this.opt("fixedWeekCount")}}),Ge.basicWeek=Ce,Ce.prototype=M(De.prototype),t.extend(Ce.prototype,{name:"basicWeek",incrementDate:function(t,e){return t.clone().stripTime().add(e,"weeks").startOf("week")},render:function(t){this.intervalStart=t.clone().stripTime().startOf("week"),this.intervalEnd=this.intervalStart.clone().add(1,"weeks"),this.start=this.skipHiddenDays(this.intervalStart),this.end=this.skipHiddenDays(this.intervalEnd,-1,!0),this.title=this.calendar.formatRange(this.start,this.end.clone().subtract(1),this.opt("titleFormat")," — "),De.prototype.render.call(this,1,this.getCellsPerWeek(),!1)}}),Ge.basicDay=He,He.prototype=M(De.prototype),t.extend(He.prototype,{name:"basicDay",incrementDate:function(t,e){var n=t.clone().stripTime().add(e,"days");return n=this.skipHiddenDays(n,0>e?-1:1)},render:function(t){this.start=this.intervalStart=t.clone().stripTime(),this.end=this.intervalEnd=this.start.clone().add(1,"days"),this.title=this.calendar.formatDate(this.start,this.opt("titleFormat")),De.prototype.render.call(this,1,1,!1)}}),r({allDaySlot:!0,allDayText:"all-day",scrollTime:"06:00:00",slotDuration:"00:30:00",axisFormat:xe,timeFormat:{agenda:ke},minTime:"00:00:00",maxTime:"24:00:00",slotEventOverlap:!0});var Ue=5;Me.prototype=M(be.prototype),t.extend(Me.prototype,{timeGrid:null,dayGrid:null,axisWidth:null,noScrollRowEls:null,bottomRuleEl:null,bottomRuleHeight:null,render:function(e){this.rowCnt=1,this.colCnt=e,this.el.addClass("fc-agenda-view").html(this.renderHtml()),this.scrollerEl=this.el.find(".fc-time-grid-container"),this.timeGrid.coordMap.containerEl=this.scrollerEl,this.timeGrid.el=this.el.find(".fc-time-grid"),this.timeGrid.render(),this.bottomRuleEl=t('
').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.el=this.el.find(".fc-day-grid"),this.dayGrid.render(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)"),be.prototype.render.call(this),this.resetScroll()},destroy:function(){this.timeGrid.destroy(),this.dayGrid&&this.dayGrid.destroy(),be.prototype.destroy.call(this)},renderHtml:function(){return'"+""+""+""+""+'"+""+""+"
'+this.timeGrid.headHtml()+"
'+(this.dayGrid?'

':"")+'
'+'
'+"
"+"
"},headIntroHtml:function(){var t,e,n,i;return this.opt("weekNumbers")?(t=this.cellToDate(0,0),e=this.calendar.calculateWeekNumber(t),n=this.opt("weekNumberTitle"),i=this.opt("isRTL")?e+n:n+e,'"+""+F(i)+""+""):'"},dayIntroHtml:function(){return'"+""+(this.opt("allDayHtml")||F(this.opt("allDayText")))+""+""},slotBgIntroHtml:function(){return'"},introHtml:function(){return'"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},updateSize:function(t){t&&this.timeGrid.resize(),be.prototype.updateSize.call(this,t)},updateWidth:function(){this.axisWidth=v(this.el.find(".fc-axis"))},setHeight:function(t,e){var n,i;null===this.bottomRuleHeight&&(this.bottomRuleHeight=this.bottomRuleEl.outerHeight()),this.bottomRuleEl.hide(),this.scrollerEl.css("overflow",""),w(this.scrollerEl),h(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.destroySegPopover(),n=this.opt("eventLimit"),n&&"number"!=typeof n&&(n=Ue),n&&this.dayGrid.limitRows(n)),e||(i=this.computeScrollerHeight(t),y(this.scrollerEl,i)?(u(this.noScrollRowEls,b(this.scrollerEl)),i=this.computeScrollerHeight(t),this.scrollerEl.height(i),this.restoreScroll()):(this.scrollerEl.height(i).css("overflow","hidden"),this.bottomRuleEl.show()))},resetScroll:function(){function t(){n.scrollerEl.scrollTop(r)}var n=this,i=e.duration(this.opt("scrollTime")),r=this.timeGrid.computeTimeTop(i);r=Math.ceil(r),r&&r++,t(),setTimeout(t,0)},renderEvents:function(t){var e,n,i=[],r=[],s=[];for(n=0;t.length>n;n++)t[n].allDay?i.push(t[n]):r.push(t[n]);e=this.timeGrid.renderEvents(r),this.dayGrid&&(s=this.dayGrid.renderEvents(i)),this.updateHeight(),be.prototype.renderEvents.call(this,t)},getSegs:function(){return this.timeGrid.getSegs().concat(this.dayGrid?this.dayGrid.getSegs():[])},destroyEvents:function(){be.prototype.destroyEvents.call(this),this.recordScroll(),this.timeGrid.destroyEvents(),this.dayGrid&&this.dayGrid.destroyEvents()},renderDrag:function(t,e,n){return t.hasTime()?this.timeGrid.renderDrag(t,e,n):this.dayGrid?this.dayGrid.renderDrag(t,e,n):void 0},destroyDrag:function(){this.timeGrid.destroyDrag(),this.dayGrid&&this.dayGrid.destroyDrag()},renderSelection:function(t,e){t.hasTime()||e.hasTime()?this.timeGrid.renderSelection(t,e):this.dayGrid&&this.dayGrid.renderSelection(t,e)},destroySelection:function(){this.timeGrid.destroySelection(),this.dayGrid&&this.dayGrid.destroySelection()}}),Ge.agendaWeek=Re,Re.prototype=M(Me.prototype),t.extend(Re.prototype,{name:"agendaWeek",incrementDate:function(t,e){return t.clone().stripTime().add(e,"weeks").startOf("week")},render:function(t){this.intervalStart=t.clone().stripTime().startOf("week"),this.intervalEnd=this.intervalStart.clone().add(1,"weeks"),this.start=this.skipHiddenDays(this.intervalStart),this.end=this.skipHiddenDays(this.intervalEnd,-1,!0),this.title=this.calendar.formatRange(this.start,this.end.clone().subtract(1),this.opt("titleFormat")," — "),Me.prototype.render.call(this,this.getCellsPerWeek())}}),Ge.agendaDay=Pe,Pe.prototype=M(Me.prototype),t.extend(Pe.prototype,{name:"agendaDay",incrementDate:function(t,e){var n=t.clone().stripTime().add(e,"days");return n=this.skipHiddenDays(n,0>e?-1:1)},render:function(t){this.start=this.intervalStart=t.clone().stripTime(),this.end=this.intervalEnd=this.start.clone().add(1,"days"),this.title=this.calendar.formatDate(this.start,this.opt("titleFormat")),Me.prototype.render.call(this,1)}})}); \ No newline at end of file diff --git a/static/js/plugins/fullcalendar/moment.min.js b/static/js/plugins/fullcalendar/moment.min.js new file mode 100755 index 000000000..8b54f130f --- /dev/null +++ b/static/js/plugins/fullcalendar/moment.min.js @@ -0,0 +1,7 @@ +//! moment.js +//! version : 2.8.3 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +(function(a){function b(a,b,c){switch(arguments.length){case 2:return null!=a?a:b;case 3:return null!=a?a:null!=b?b:c;default:throw new Error("Implement me")}}function c(a,b){return zb.call(a,b)}function d(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function e(a){tb.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}function f(a,b){var c=!0;return m(function(){return c&&(e(a),c=!1),b.apply(this,arguments)},b)}function g(a,b){qc[a]||(e(b),qc[a]=!0)}function h(a,b){return function(c){return p(a.call(this,c),b)}}function i(a,b){return function(c){return this.localeData().ordinal(a.call(this,c),b)}}function j(){}function k(a,b){b!==!1&&F(a),n(this,a),this._d=new Date(+a._d)}function l(a){var b=y(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._locale=tb.localeData(),this._bubble()}function m(a,b){for(var d in b)c(b,d)&&(a[d]=b[d]);return c(b,"toString")&&(a.toString=b.toString),c(b,"valueOf")&&(a.valueOf=b.valueOf),a}function n(a,b){var c,d,e;if("undefined"!=typeof b._isAMomentObject&&(a._isAMomentObject=b._isAMomentObject),"undefined"!=typeof b._i&&(a._i=b._i),"undefined"!=typeof b._f&&(a._f=b._f),"undefined"!=typeof b._l&&(a._l=b._l),"undefined"!=typeof b._strict&&(a._strict=b._strict),"undefined"!=typeof b._tzm&&(a._tzm=b._tzm),"undefined"!=typeof b._isUTC&&(a._isUTC=b._isUTC),"undefined"!=typeof b._offset&&(a._offset=b._offset),"undefined"!=typeof b._pf&&(a._pf=b._pf),"undefined"!=typeof b._locale&&(a._locale=b._locale),Ib.length>0)for(c in Ib)d=Ib[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function o(a){return 0>a?Math.ceil(a):Math.floor(a)}function p(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&A(a[d])!==A(b[d]))&&g++;return g+f}function x(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=jc[a]||kc[b]||b}return a}function y(a){var b,d,e={};for(d in a)c(a,d)&&(b=x(d),b&&(e[b]=a[d]));return e}function z(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}tb[b]=function(e,f){var g,h,i=tb._locale[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=tb().utc().set(d,a);return i.call(tb._locale,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function A(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function B(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function C(a,b,c){return hb(tb([a,11,31+b-c]),b,c).week}function D(a){return E(a)?366:365}function E(a){return a%4===0&&a%100!==0||a%400===0}function F(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[Bb]<0||a._a[Bb]>11?Bb:a._a[Cb]<1||a._a[Cb]>B(a._a[Ab],a._a[Bb])?Cb:a._a[Db]<0||a._a[Db]>23?Db:a._a[Eb]<0||a._a[Eb]>59?Eb:a._a[Fb]<0||a._a[Fb]>59?Fb:a._a[Gb]<0||a._a[Gb]>999?Gb:-1,a._pf._overflowDayOfYear&&(Ab>b||b>Cb)&&(b=Cb),a._pf.overflow=b)}function G(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function H(a){return a?a.toLowerCase().replace("_","-"):a}function I(a){for(var b,c,d,e,f=0;f0;){if(d=J(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&w(e,c,!0)>=b-1)break;b--}f++}return null}function J(a){var b=null;if(!Hb[a]&&Jb)try{b=tb.locale(),require("./locale/"+a),tb.locale(b)}catch(c){}return Hb[a]}function K(a,b){return b._isUTC?tb(a).zone(b._offset||0):tb(a).local()}function L(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function M(a){var b,c,d=a.match(Nb);for(b=0,c=d.length;c>b;b++)d[b]=pc[d[b]]?pc[d[b]]:L(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function N(a,b){return a.isValid()?(b=O(b,a.localeData()),lc[b]||(lc[b]=M(b)),lc[b](a)):a.localeData().invalidDate()}function O(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Ob.lastIndex=0;d>=0&&Ob.test(a);)a=a.replace(Ob,c),Ob.lastIndex=0,d-=1;return a}function P(a,b){var c,d=b._strict;switch(a){case"Q":return Zb;case"DDDD":return _b;case"YYYY":case"GGGG":case"gggg":return d?ac:Rb;case"Y":case"G":case"g":return cc;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?bc:Sb;case"S":if(d)return Zb;case"SS":if(d)return $b;case"SSS":if(d)return _b;case"DDD":return Qb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Ub;case"a":case"A":return b._locale._meridiemParse;case"X":return Xb;case"Z":case"ZZ":return Vb;case"T":return Wb;case"SSSS":return Tb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?$b:Pb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Pb;case"Do":return Yb;default:return c=new RegExp(Y(X(a.replace("\\","")),"i"))}}function Q(a){a=a||"";var b=a.match(Vb)||[],c=b[b.length-1]||[],d=(c+"").match(hc)||["-",0,0],e=+(60*d[1])+A(d[2]);return"+"===d[0]?-e:e}function R(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[Bb]=3*(A(b)-1));break;case"M":case"MM":null!=b&&(e[Bb]=A(b)-1);break;case"MMM":case"MMMM":d=c._locale.monthsParse(b),null!=d?e[Bb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[Cb]=A(b));break;case"Do":null!=b&&(e[Cb]=A(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=A(b));break;case"YY":e[Ab]=tb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[Ab]=A(b);break;case"a":case"A":c._isPm=c._locale.isPM(b);break;case"H":case"HH":case"h":case"hh":e[Db]=A(b);break;case"m":case"mm":e[Eb]=A(b);break;case"s":case"ss":e[Fb]=A(b);break;case"S":case"SS":case"SSS":case"SSSS":e[Gb]=A(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=Q(b);break;case"dd":case"ddd":case"dddd":d=c._locale.weekdaysParse(b),null!=d?(c._w=c._w||{},c._w.d=d):c._pf.invalidWeekday=b;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":a=a.substr(0,1);case"gggg":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=A(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=tb.parseTwoDigitYear(b)}}function S(a){var c,d,e,f,g,h,i;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[Ab],hb(tb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(g=a._locale._week.dow,h=a._locale._week.doy,d=b(c.gg,a._a[Ab],hb(tb(),g,h).year),e=b(c.w,1),null!=c.d?(f=c.d,g>f&&++e):f=null!=c.e?c.e+g:g),i=ib(d,e,f,h,g),a._a[Ab]=i.year,a._dayOfYear=i.dayOfYear}function T(a){var c,d,e,f,g=[];if(!a._d){for(e=V(a),a._w&&null==a._a[Cb]&&null==a._a[Bb]&&S(a),a._dayOfYear&&(f=b(a._a[Ab],e[Ab]),a._dayOfYear>D(f)&&(a._pf._overflowDayOfYear=!0),d=db(f,0,a._dayOfYear),a._a[Bb]=d.getUTCMonth(),a._a[Cb]=d.getUTCDate()),c=0;3>c&&null==a._a[c];++c)a._a[c]=g[c]=e[c];for(;7>c;c++)a._a[c]=g[c]=null==a._a[c]?2===c?1:0:a._a[c];a._d=(a._useUTC?db:cb).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()+a._tzm)}}function U(a){var b;a._d||(b=y(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],T(a))}function V(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function W(a){if(a._f===tb.ISO_8601)return void $(a);a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=""+a._i,h=g.length,i=0;for(d=O(a._f,a._locale).match(Nb)||[],b=0;b0&&a._pf.unusedInput.push(f),g=g.slice(g.indexOf(c)+c.length),i+=c.length),pc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),R(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=h-i,g.length>0&&a._pf.unusedInput.push(g),a._isPm&&a._a[Db]<12&&(a._a[Db]+=12),a._isPm===!1&&12===a._a[Db]&&(a._a[Db]=0),T(a),F(a)}function X(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function Y(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Z(a){var b,c,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,c=b));m(a,c||b)}function $(a){var b,c,d=a._i,e=dc.exec(d);if(e){for(a._pf.iso=!0,b=0,c=fc.length;c>b;b++)if(fc[b][1].exec(d)){a._f=fc[b][0]+(e[6]||" ");break}for(b=0,c=gc.length;c>b;b++)if(gc[b][1].exec(d)){a._f+=gc[b][0];break}d.match(Vb)&&(a._f+="Z"),W(a)}else a._isValid=!1}function _(a){$(a),a._isValid===!1&&(delete a._isValid,tb.createFromInputFallback(a))}function ab(a,b){var c,d=[];for(c=0;ca&&h.setFullYear(a),h}function db(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function eb(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function fb(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function gb(a,b,c){var d=tb.duration(a).abs(),e=yb(d.as("s")),f=yb(d.as("m")),g=yb(d.as("h")),h=yb(d.as("d")),i=yb(d.as("M")),j=yb(d.as("y")),k=e0,k[4]=c,fb.apply({},k)}function hb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=tb(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ib(a,b,c,d,e){var f,g,h=db(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:D(a-1)+g}}function jb(b){var c=b._i,d=b._f;return b._locale=b._locale||tb.localeData(b._l),null===c||d===a&&""===c?tb.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=b._locale.preparse(c)),tb.isMoment(c)?new k(c,!0):(d?u(d)?Z(b):W(b):bb(b),new k(b)))}function kb(a,b){var c,d;if(1===b.length&&u(b[0])&&(b=b[0]),!b.length)return tb();for(c=b[0],d=1;d=0?"+":"-";return b+p(Math.abs(a),6)},gg:function(){return p(this.weekYear()%100,2)},gggg:function(){return p(this.weekYear(),4)},ggggg:function(){return p(this.weekYear(),5)},GG:function(){return p(this.isoWeekYear()%100,2)},GGGG:function(){return p(this.isoWeekYear(),4)},GGGGG:function(){return p(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return A(this.milliseconds()/100)},SS:function(){return p(A(this.milliseconds()/10),2)},SSS:function(){return p(this.milliseconds(),3)},SSSS:function(){return p(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+p(A(a/60),2)+":"+p(A(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+p(A(a/60),2)+p(A(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},qc={},rc=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];nc.length;)vb=nc.pop(),pc[vb+"o"]=i(pc[vb],vb);for(;oc.length;)vb=oc.pop(),pc[vb+vb]=h(pc[vb],2);pc.DDDD=h(pc.DDD,3),m(j.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=tb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=tb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return hb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),tb=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=c,g._l=e,g._strict=f,g._isUTC=!1,g._pf=d(),jb(g)},tb.suppressDeprecationWarnings=!1,tb.createFromInputFallback=f("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),tb.min=function(){var a=[].slice.call(arguments,0);return kb("isBefore",a)},tb.max=function(){var a=[].slice.call(arguments,0);return kb("isAfter",a)},tb.utc=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=b,g._f=c,g._strict=f,g._pf=d(),jb(g).utc()},tb.unix=function(a){return tb(1e3*a)},tb.duration=function(a,b){var d,e,f,g,h=a,i=null;return tb.isDuration(a)?h={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(h={},b?h[b]=a:h.milliseconds=a):(i=Lb.exec(a))?(d="-"===i[1]?-1:1,h={y:0,d:A(i[Cb])*d,h:A(i[Db])*d,m:A(i[Eb])*d,s:A(i[Fb])*d,ms:A(i[Gb])*d}):(i=Mb.exec(a))?(d="-"===i[1]?-1:1,f=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*d},h={y:f(i[2]),M:f(i[3]),d:f(i[4]),h:f(i[5]),m:f(i[6]),s:f(i[7]),w:f(i[8])}):"object"==typeof h&&("from"in h||"to"in h)&&(g=r(tb(h.from),tb(h.to)),h={},h.ms=g.milliseconds,h.M=g.months),e=new l(h),tb.isDuration(a)&&c(a,"_locale")&&(e._locale=a._locale),e},tb.version=wb,tb.defaultFormat=ec,tb.ISO_8601=function(){},tb.momentProperties=Ib,tb.updateOffset=function(){},tb.relativeTimeThreshold=function(b,c){return mc[b]===a?!1:c===a?mc[b]:(mc[b]=c,!0)},tb.lang=f("moment.lang is deprecated. Use moment.locale instead.",function(a,b){return tb.locale(a,b)}),tb.locale=function(a,b){var c;return a&&(c="undefined"!=typeof b?tb.defineLocale(a,b):tb.localeData(a),c&&(tb.duration._locale=tb._locale=c)),tb._locale._abbr},tb.defineLocale=function(a,b){return null!==b?(b.abbr=a,Hb[a]||(Hb[a]=new j),Hb[a].set(b),tb.locale(a),Hb[a]):(delete Hb[a],null)},tb.langData=f("moment.langData is deprecated. Use moment.localeData instead.",function(a){return tb.localeData(a)}),tb.localeData=function(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return tb._locale;if(!u(a)){if(b=J(a))return b;a=[a]}return I(a)},tb.isMoment=function(a){return a instanceof k||null!=a&&c(a,"_isAMomentObject")},tb.isDuration=function(a){return a instanceof l};for(vb=rc.length-1;vb>=0;--vb)z(rc[vb]);tb.normalizeUnits=function(a){return x(a)},tb.invalid=function(a){var b=tb.utc(0/0);return null!=a?m(b._pf,a):b._pf.userInvalidated=!0,b},tb.parseZone=function(){return tb.apply(null,arguments).parseZone()},tb.parseTwoDigitYear=function(a){return A(a)+(A(a)>68?1900:2e3)},m(tb.fn=k.prototype,{clone:function(){return tb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=tb(this).utc();return 00:!1},parsingFlags:function(){return m({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(a){return this.zone(0,a)},local:function(a){return this._isUTC&&(this.zone(0,a),this._isUTC=!1,a&&this.add(this._dateTzOffset(),"m")),this},format:function(a){var b=N(this,a||tb.defaultFormat);return this.localeData().postformat(b)},add:s(1,"add"),subtract:s(-1,"subtract"),diff:function(a,b,c){var d,e,f,g=K(a,this),h=6e4*(this.zone()-g.zone());return b=x(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+g.daysInMonth()),e=12*(this.year()-g.year())+(this.month()-g.month()),f=this-tb(this).startOf("month")-(g-tb(g).startOf("month")),f-=6e4*(this.zone()-tb(this).startOf("month").zone()-(g.zone()-tb(g).startOf("month").zone())),e+=f/d,"year"===b&&(e/=12)):(d=this-g,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-h)/864e5:"week"===b?(d-h)/6048e5:d),c?e:o(e)},from:function(a,b){return tb.duration({to:this,from:a}).locale(this.locale()).humanize(!b)},fromNow:function(a){return this.from(tb(),a)},calendar:function(a){var b=a||tb(),c=K(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.localeData().calendar(e,this))},isLeapYear:function(){return E(this.year())},isDST:function(){return this.zone()+a):+this.clone().startOf(b)>+tb(a).startOf(b)},isBefore:function(a,b){return b=x("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=tb.isMoment(a)?a:tb(a),+a>+this):+this.clone().startOf(b)<+tb(a).startOf(b)},isSame:function(a,b){return b=x(b||"millisecond"),"millisecond"===b?(a=tb.isMoment(a)?a:tb(a),+this===+a):+this.clone().startOf(b)===+K(a,this).startOf(b)},min:f("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(a){return a=tb.apply(null,arguments),this>a?this:a}),max:f("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=tb.apply(null,arguments),a>this?this:a}),zone:function(a,b){var c,d=this._offset||0;return null==a?this._isUTC?d:this._dateTzOffset():("string"==typeof a&&(a=Q(a)),Math.abs(a)<16&&(a=60*a),!this._isUTC&&b&&(c=this._dateTzOffset()),this._offset=a,this._isUTC=!0,null!=c&&this.subtract(c,"m"),d!==a&&(!b||this._changeInProgress?t(this,tb.duration(d-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,tb.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?tb(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return B(this.year(),this.month())},dayOfYear:function(a){var b=yb((tb(this).startOf("day")-tb(this).startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=hb(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==a?b:this.add(a-b,"y")},isoWeekYear:function(a){var b=hb(this,1,4).year;return null==a?b:this.add(a-b,"y")},week:function(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")},isoWeek:function(a){var b=hb(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")},weekday:function(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return C(this.year(),1,4)},weeksInYear:function(){var a=this.localeData()._week;return C(this.year(),a.dow,a.doy)},get:function(a){return a=x(a),this[a]()},set:function(a,b){return a=x(a),"function"==typeof this[a]&&this[a](b),this},locale:function(b){var c;return b===a?this._locale._abbr:(c=tb.localeData(b),null!=c&&(this._locale=c),this)},lang:f("moment().lang() is deprecated. Use moment().localeData() instead.",function(b){return b===a?this.localeData():this.locale(b)}),localeData:function(){return this._locale},_dateTzOffset:function(){return 15*Math.round(this._d.getTimezoneOffset()/15)}}),tb.fn.millisecond=tb.fn.milliseconds=ob("Milliseconds",!1),tb.fn.second=tb.fn.seconds=ob("Seconds",!1),tb.fn.minute=tb.fn.minutes=ob("Minutes",!1),tb.fn.hour=tb.fn.hours=ob("Hours",!0),tb.fn.date=ob("Date",!0),tb.fn.dates=f("dates accessor is deprecated. Use date instead.",ob("Date",!0)),tb.fn.year=ob("FullYear",!0),tb.fn.years=f("years accessor is deprecated. Use year instead.",ob("FullYear",!0)),tb.fn.days=tb.fn.day,tb.fn.months=tb.fn.month,tb.fn.weeks=tb.fn.week,tb.fn.isoWeeks=tb.fn.isoWeek,tb.fn.quarters=tb.fn.quarter,tb.fn.toJSON=tb.fn.toISOString,m(tb.duration.fn=l.prototype,{_bubble:function(){var a,b,c,d=this._milliseconds,e=this._days,f=this._months,g=this._data,h=0;g.milliseconds=d%1e3,a=o(d/1e3),g.seconds=a%60,b=o(a/60),g.minutes=b%60,c=o(b/60),g.hours=c%24,e+=o(c/24),h=o(pb(e)),e-=o(qb(h)),f+=o(e/30),e%=30,h+=o(f/12),f%=12,g.days=e,g.months=f,g.years=h},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return o(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*A(this._months/12)},humanize:function(a){var b=gb(this,!a,this.localeData());return a&&(b=this.localeData().pastFuture(+this,b)),this.localeData().postformat(b)},add:function(a,b){var c=tb.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=tb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=x(a),this[a.toLowerCase()+"s"]()},as:function(a){var b,c;if(a=x(a),"month"===a||"year"===a)return b=this._days+this._milliseconds/864e5,c=this._months+12*pb(b),"month"===a?c:c/12;switch(b=this._days+qb(this._months/12),a){case"week":return b/7+this._milliseconds/6048e5;case"day":return b+this._milliseconds/864e5;case"hour":return 24*b+this._milliseconds/36e5;case"minute":return 24*b*60+this._milliseconds/6e4;case"second":return 24*b*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*b*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+a)}},lang:tb.fn.lang,locale:tb.fn.locale,toIsoString:f("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"},localeData:function(){return this._locale}}),tb.duration.fn.toString=tb.duration.fn.toISOString;for(vb in ic)c(ic,vb)&&rb(vb.toLowerCase());tb.duration.fn.asMilliseconds=function(){return this.as("ms")},tb.duration.fn.asSeconds=function(){return this.as("s")},tb.duration.fn.asMinutes=function(){return this.as("m")},tb.duration.fn.asHours=function(){return this.as("h")},tb.duration.fn.asDays=function(){return this.as("d")},tb.duration.fn.asWeeks=function(){return this.as("weeks")},tb.duration.fn.asMonths=function(){return this.as("M")},tb.duration.fn.asYears=function(){return this.as("y")},tb.locale("en",{ordinal:function(a){var b=a%10,c=1===A(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th"; +return a+c}}),Jb?module.exports=tb:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(xb.moment=ub),tb}),sb(!0)):sb()}).call(this); \ No newline at end of file diff --git a/templates/foot_script.html b/templates/foot_script.html index 10074eef4..6a57f92cd 100644 --- a/templates/foot_script.html +++ b/templates/foot_script.html @@ -3,6 +3,8 @@ + + diff --git a/templates/index.html b/templates/index.html index 14e55a745..f1d4cc6b8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -7,7 +7,7 @@
- Monthly + Users
用户总数
@@ -20,7 +20,7 @@
- Annual + Hosts
主机总数
@@ -34,7 +34,7 @@
- Today + Online
实时在线用户
@@ -48,13 +48,13 @@
- Low value + Connected
已连接服务器

38%
- Connect host + Connected host
diff --git a/templates/jasset/group_add.html b/templates/jasset/group_add.html index bc39764ea..837c95f78 100644 --- a/templates/jasset/group_add.html +++ b/templates/jasset/group_add.html @@ -1,4 +1,5 @@ {% extends 'base.html' %} +{% load mytags %} {% block content %} {% include 'nav_cat_bar.html' %}
@@ -20,17 +21,27 @@
-
-
- -
- + {% ifequal session_role_id 2 %} +
+
+ +
+ +
-
+ {% endifequal %} + + {% ifequal session_role_id 1 %} +
+
+ +
+
+ {% endifequal %}
@@ -43,9 +54,9 @@
-
- - +
+ +
diff --git a/templates/jasset/host_add.html b/templates/jasset/host_add.html index e9dceecb3..b5ccddfa5 100644 --- a/templates/jasset/host_add.html +++ b/templates/jasset/host_add.html @@ -1,4 +1,5 @@ {% extends 'base.html' %} +{% load mytags %} {% block content %} {% include 'nav_cat_bar.html' %}
@@ -84,17 +85,26 @@
-
-
- -
- + {% ifequal session_role_id 2 %} +
+
+ +
+ +
-
+ {% endifequal %} + + {% ifequal session_role_id 1 %} +
+
+
+
+ {% endifequal %}
@@ -108,16 +118,6 @@
- - - - - - - - - -
diff --git a/templates/jasset/host_add_multi.html b/templates/jasset/host_add_multi.html index 9e83d1e1f..4fd629071 100644 --- a/templates/jasset/host_add_multi.html +++ b/templates/jasset/host_add_multi.html @@ -44,7 +44,7 @@ {% endif %}

按照文本框内主机信息格式填写, 多台主机回车换行

-
+
diff --git a/templates/jasset/jlist_ip.html b/templates/jasset/jlist_ip.html index e7ea87552..850e9cd9f 100644 --- a/templates/jasset/jlist_ip.html +++ b/templates/jasset/jlist_ip.html @@ -34,7 +34,7 @@ {{ post.port }} {{ login_types|get_item:post.login_type }} {{ post.idc.name }} - {% for group in post.bis_group.all|filter_private %} {{ group }} {% endfor %} + {% for group in post.bis_group.all %} {{ group }} {% endfor %} {{ post.date_added|date:"Y-m-d H:i:s" }} {{ post.comment }} diff --git a/templates/jlog/log_offline.html b/templates/jlog/log_offline.html index 914eb04a5..970f8ac6d 100644 --- a/templates/jlog/log_offline.html +++ b/templates/jlog/log_offline.html @@ -77,7 +77,9 @@ 用户名 + 所属部门 登录主机 + 来源IP 命令统计 登录时间 结束时间 @@ -88,7 +90,9 @@ {% for post in contacts.object_list %} {{ post.user }} + {{ post.dept_name }} {{ post.host }} + {{ post.remote_ip }} 命令统计 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }} diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html index b7003a076..6945e60b3 100644 --- a/templates/jlog/log_online.html +++ b/templates/jlog/log_online.html @@ -77,7 +77,9 @@ 用户名 + 所属部门 登录主机 + 来源IP 实时监控 阻断 登录时间 @@ -88,7 +90,9 @@ {% for post in contacts.object_list %} {{ post.user }} + {{ post.dept_name }} {{ post.host }} + {{ post.remote_ip }} 监控 {{ post.start_time|date:"Y-m-d H:i:s" }} @@ -145,9 +149,9 @@ var regx = /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/g; // tag.append('

'+escapeString(obj.content.replace(regx,''))+'

'); if (option == 'new') { - tag.append('

' + escapeString(obj.content) + '

'); + tag.append('

' + escapeString(obj.content) + '

'); } else if (option == 'exist') { - tag.append('

' + exsit_message + '

'); + tag.append('

' + exsit_message + '

'); } tag.animate({ scrollTop: tag[0].scrollHeight}, 1); }); diff --git a/templates/jlog/log_search.html b/templates/jlog/log_search.html index e7d2a93b8..5151bc390 100644 --- a/templates/jlog/log_search.html +++ b/templates/jlog/log_search.html @@ -3,7 +3,9 @@ 用户名 + 所属部门 登录主机 + 来源IP 命令统计 登录时间 结束时间 @@ -14,7 +16,9 @@ {% for post in contacts.object_list %} {{ post.user }} + {{ post.dept_name }} {{ post.host }} + {{ post.remote_ip }} 命令统计 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }} diff --git a/templates/link_css.html b/templates/link_css.html index 6894a91fe..d7d88b7e8 100644 --- a/templates/link_css.html +++ b/templates/link_css.html @@ -6,3 +6,4 @@ + diff --git a/templates/nav.html b/templates/nav.html index d397ea1ed..09ce635e2 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -27,7 +27,7 @@ 资产管理
+{% endifequal %} + +{% ifequal session_role_id 0 %} + {% endifequal %} \ No newline at end of file