diff --git a/ajax.php b/ajax.php index 0786b41a43691fa1667212753c01d48d2fde05ad..57b6274ccee12f4292f97aa7df5b4f9c9cd938ce 100644 --- a/ajax.php +++ b/ajax.php @@ -34,6 +34,9 @@ $dispatcher = patterns('', url_post('^(?P<id>\d+)/attach$', 'uploadInlineImageClient'), url_get('^(?P<namespace>[\w.]+)$', 'getDraftClient'), url_post('^(?P<namespace>[\w.]+)$', 'createDraftClient') + )), + url('^/form/', patterns('ajax.forms.php:DynamicFormsAjaxAPI', + url_get('^help-topic/(?P<id>\d+)$', 'getClientFormsForHelpTopic') )) ); print $dispatcher->resolve($ost->get_path_info()); diff --git a/assets/default/css/theme.css b/assets/default/css/theme.css index 4a1af12ac59e2fab9b7179b1c4ba250ee50badd7..bacbd67a060e81c7888561e4fa396da1849c7e06 100644 --- a/assets/default/css/theme.css +++ b/assets/default/css/theme.css @@ -532,9 +532,11 @@ body { width: 140px; float: left; } +label.required { + font-weight: bold; +} #ticketForm div label.required, #clientLogin div label.required { - font-weight: bold; text-align: left; } #ticketForm div input, diff --git a/bootstrap.php b/bootstrap.php index b2540b15936cfc82778b024fb9a0b42a1d7ad31b..ac25ecf3142858bd636486b1685c6fdc7c43696f 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -65,6 +65,8 @@ class Bootstrap { define('FILE_CHUNK_TABLE',$prefix.'file_chunk'); define('ATTACHMENT_TABLE',$prefix.'attachment'); + define('USER_TABLE',$prefix.'user'); + define('USER_EMAIL_TABLE',$prefix.'user_email'); define('STAFF_TABLE',$prefix.'staff'); define('TEAM_TABLE',$prefix.'team'); @@ -87,6 +89,15 @@ class Bootstrap { define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority'); define('PRIORITY_TABLE',TICKET_PRIORITY_TABLE); + define('FORM_SEC_TABLE',$prefix.'form'); + define('FORM_FIELD_TABLE',$prefix.'form_field'); + + define('LIST_TABLE',$prefix.'list'); + define('LIST_ITEM_TABLE',$prefix.'list_items'); + + define('FORM_ENTRY_TABLE',$prefix.'form_entry'); + define('FORM_ANSWER_TABLE',$prefix.'form_entry_values'); + define('TOPIC_TABLE',$prefix.'help_topic'); define('SLA_TABLE', $prefix.'sla'); @@ -153,6 +164,27 @@ class Bootstrap { self::croak($ferror); } + function loadCode() { + #include required files + require(INCLUDE_DIR.'class.ostsession.php'); + require(INCLUDE_DIR.'class.usersession.php'); + require(INCLUDE_DIR.'class.pagenate.php'); //Pagenate helper! + require(INCLUDE_DIR.'class.log.php'); + require(INCLUDE_DIR.'class.crypto.php'); + require(INCLUDE_DIR.'class.timezone.php'); + require(INCLUDE_DIR.'class.http.php'); + require(INCLUDE_DIR.'class.signal.php'); + require(INCLUDE_DIR.'class.nav.php'); + require(INCLUDE_DIR.'class.page.php'); + require_once(INCLUDE_DIR.'class.format.php'); //format helpers + require_once(INCLUDE_DIR.'class.validator.php'); //Class to help with basic form input validation...please help improve it. + require(INCLUDE_DIR.'class.mailer.php'); + if (extension_loaded('mysqli')) + require_once INCLUDE_DIR.'mysqli.php'; + else + require(INCLUDE_DIR.'mysql.php'); + } + function croak($message) { $msg = $message."\n\n".THISPAGE; Mailer::sendmail(ADMIN_EMAIL, 'osTicket Fatal Error', $msg, @@ -174,7 +206,7 @@ define('I18N_DIR', INCLUDE_DIR.'i18n/'); /*############## Do NOT monkey with anything else beyond this point UNLESS you really know what you are doing ##############*/ #Current version && schema signature (Changes from version to version) -define('THIS_VERSION','1.8.0-devel'); //Shown on admin panel +define('THIS_VERSION','1.8-git'); //Shown on admin panel //Path separator if(!defined('PATH_SEPARATOR')){ if(strpos($_ENV['OS'],'Win')!==false || !strcasecmp(substr(PHP_OS, 0, 3),'WIN')) @@ -187,6 +219,7 @@ if(!defined('PATH_SEPARATOR')){ ini_set('include_path', './'.PATH_SEPARATOR.INCLUDE_DIR.PATH_SEPARATOR.PEAR_DIR); require(INCLUDE_DIR.'class.osticket.php'); +require(INCLUDE_DIR.'class.misc.php'); // Determine the path in the URI used as the base of the osTicket // installation @@ -195,26 +228,6 @@ if (!defined('ROOT_PATH') && ($rp = osTicket::get_root_path(dirname(__file__)))) Bootstrap::init(); -#include required files -require(INCLUDE_DIR.'class.ostsession.php'); -require(INCLUDE_DIR.'class.usersession.php'); -require(INCLUDE_DIR.'class.pagenate.php'); //Pagenate helper! -require(INCLUDE_DIR.'class.log.php'); -require(INCLUDE_DIR.'class.crypto.php'); -require(INCLUDE_DIR.'class.misc.php'); -require(INCLUDE_DIR.'class.timezone.php'); -require(INCLUDE_DIR.'class.http.php'); -require(INCLUDE_DIR.'class.signal.php'); -require(INCLUDE_DIR.'class.nav.php'); -require(INCLUDE_DIR.'class.page.php'); -require(INCLUDE_DIR.'class.format.php'); //format helpers -require(INCLUDE_DIR.'class.validator.php'); //Class to help with basic form input validation...please help improve it. -require(INCLUDE_DIR.'class.mailer.php'); -if (extension_loaded('mysqli')) - require_once INCLUDE_DIR.'mysqli.php'; -else - require(INCLUDE_DIR.'mysql.php'); - #CURRENT EXECUTING SCRIPT. define('THISPAGE', Misc::currentURL()); define('THISURI', $_SERVER['REQUEST_URI']); diff --git a/css/osticket.css b/css/osticket.css index 87e6c6aed4b382095e42199f41a00f35bea32621..8b986fef422772ae58c24cbaed324763e8171c03 100644 --- a/css/osticket.css +++ b/css/osticket.css @@ -43,3 +43,10 @@ display: block; opacity: 0.3; } + +div.section-break { + margin-top: 1em; + margin-bottom: 0.5em; + padding-top: 0.8em !important; + border-top: 1px solid #ccc; +} diff --git a/include/ajax.config.php b/include/ajax.config.php index 7f2be0617f3d37f7df7484c5b4e2e5d44f98b407..bf4174fe8e6ff53150f8fe77fff541010b4f0e37 100644 --- a/include/ajax.config.php +++ b/include/ajax.config.php @@ -35,7 +35,7 @@ class ConfigAjaxAPI extends AjaxController { global $cfg; $config=array( - 'allow_attachments' => (bool) $cfg->allowOnlineAttachments, + 'allow_attachments' => (bool) $cfg->allowOnlineAttachments(), 'file_types' => $cfg->getAllowedFileTypes(), 'max_file_size' => (int) $cfg->getMaxFileSize(), 'max_file_uploads'=> (int) $cfg->getClientMaxFileUploads(), diff --git a/include/ajax.content.php b/include/ajax.content.php index 197d75ffe50c622296ca0bb22335874773fb499b..2451880ac8da4494fa44e690d07daa2926a4d7a8 100644 --- a/include/ajax.content.php +++ b/include/ajax.content.php @@ -49,7 +49,8 @@ class ContentAjaxAPI extends AjaxController { <tr><td width="130">%{ticket.id}</td><td>Ticket ID (internal ID)</td></tr> <tr><td>%{ticket.number}</td><td>Ticket number (external ID)</td></tr> <tr><td>%{ticket.email}</td><td>Email address</td></tr> - <tr><td>%{ticket.name}</td><td>Full name</td></tr> + <tr><td>%{ticket.name}</td><td>Full name — + <em>see name expansion</em></td></tr> <tr><td>%{ticket.subject}</td><td>Subject</td></tr> <tr><td>%{ticket.phone}</td><td>Phone number | ext</td></tr> <tr><td>%{ticket.status}</td><td>Status</td></tr> @@ -80,6 +81,18 @@ class ContentAjaxAPI extends AjaxController { <tr><td>%{reset_link}</td> <td>Reset link used by the password reset feature</td></tr> </table> + <table width="100%" border="0" cellspacing=1 cellpadding=1> + <tr><td colspan="2"><b>Name Expansion</b></td></tr> + <tr><td>.first</td><td>First Name</td></tr> + <tr><td>.middle</td><td>Middle Name(s)</td></tr> + <tr><td>.last</td><td>Last Name</td></tr> + <tr><td>.full</td><td>First Last</td></tr> + <tr><td>.legal</td><td>First M. Last</td></tr> + <tr><td>.short</td><td>First L.</td></tr> + <tr><td>.formal</td><td>Mr. Last</td></tr> + <tr><td>.shortformal</td><td>F. Last</td></tr> + <tr><td>.lastfirst</td><td>Last, First</td></tr> + </table> </td> </tr> </table> diff --git a/include/ajax.forms.php b/include/ajax.forms.php new file mode 100644 index 0000000000000000000000000000000000000000..a04c327a0f9153135cc57dfdc7099d6b6b3da0fd --- /dev/null +++ b/include/ajax.forms.php @@ -0,0 +1,95 @@ +<?php + +require_once(INCLUDE_DIR . 'class.topic.php'); +require_once(INCLUDE_DIR . 'class.dynamic_forms.php'); + +class DynamicFormsAjaxAPI extends AjaxController { + function getForm($form_id) { + $form = DynamicForm::lookup($form_id); + if (!$form) return; + + foreach ($form->getFields() as $field) { + $field->render(); + } + } + + function getFormsForHelpTopic($topic_id, $client=false) { + $topic = Topic::lookup($topic_id); + if ($topic->ht['form_id'] + && ($form = DynamicForm::lookup($topic->ht['form_id']))) + $form->render(!$client); + } + + function getClientFormsForHelpTopic($topic_id) { + return $this->getFormsForHelpTopic($topic_id, true); + } + + function getFieldConfiguration($field_id) { + $field = DynamicFormField::lookup($field_id); + include(STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php'); + } + + function saveFieldConfiguration($field_id) { + $field = DynamicFormField::lookup($field_id); + if (!$field->setConfiguration()) + include(STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php'); + else + $field->save(); + } + + function getUserInfo($user_id) { + $user = User::lookup($user_id); + + $data = $user->ht; + $data['email'] = $user->default_email->address; + + $custom = array(); + foreach ($user->getDynamicData() as $cd) { + $cd->addMissingFields(); + foreach ($cd->getFields() as $f) { + if ($f->get('name') == 'name') + $f->value = $user->getFullName(); + elseif ($f->get('name') == 'email') + $f->value = $user->getEmail(); + } + $custom[] = $cd->getForm(); + } + + include(STAFFINC_DIR . 'templates/user-info.tmpl.php'); + } + + function saveUserInfo($user_id) { + $user = User::lookup($user_id); + + $custom_data = $user->getDynamicData(); + $custom = array(); + $valid = true; + foreach ($custom_data as $cd) { + $cd->addMissingFields(); + $cf = $custom[] = $cd->getForm(); + $valid &= $cd->isValid(); + } + + if (!$valid) { + include(STAFFINC_DIR . 'templates/user-info.tmpl.php'); + return; + } + + // Save custom data + foreach ($custom_data as $cd) { + foreach ($cd->getFields() as $f) { + if ($f->get('name') == 'name') { + $user->name = $f->getClean(); + $user->save(); + } + elseif ($f->get('name') == 'email') { + $user->default_email->address = $f->getClean(); + $user->default_email->save(); + } + } + $cd->save(); + } + } +} + +?> diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 34c46325ed84d304c7a8c031bb8a5b686ea337a1..0f373efd613e3ac9cd90c792de1aee0b1f16582e 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -17,6 +17,7 @@ if(!defined('INCLUDE_DIR')) die('403'); include_once(INCLUDE_DIR.'class.ticket.php'); +require_once(INCLUDE_DIR.'class.ajax.php'); class TicketsAjaxAPI extends AjaxController { @@ -30,8 +31,10 @@ class TicketsAjaxAPI extends AjaxController { $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; $tickets=array(); - $sql='SELECT DISTINCT ticketID, email' - .' FROM '.TICKET_TABLE + $sql='SELECT DISTINCT ticketID, email.address AS email' + .' FROM '.TICKET_TABLE.' ticket' + .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' + .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' .' WHERE ticketID LIKE \''.db_input($_REQUEST['q'], false).'%\''; $sql.=' AND ( staff_id='.db_input($thisstaff->getId()); @@ -43,12 +46,17 @@ class TicketsAjaxAPI extends AjaxController { $sql.=' OR dept_id IN ('.implode(',', db_input($depts)).')'; $sql.=' ) ' - .' ORDER BY created LIMIT '.$limit; + .' ORDER BY ticket.created LIMIT '.$limit; if(($res=db_query($sql)) && db_num_rows($res)) { - while(list($id, $email)=db_fetch_row($res)) - $tickets[] = array('id'=>$id, 'email'=>$email, 'value'=>$id, 'info'=>"$id - $email"); + while(list($id, $email)=db_fetch_row($res)) { + $info = "$id - $email"; + $tickets[] = array('id'=>$id, 'email'=>$email, 'value'=>$id, + 'info'=>$info, 'matches'=>$_REQUEST['q']); + } } + if (!$tickets) + return self::lookupByEmail(); return $this->json_encode($tickets); } @@ -60,9 +68,15 @@ class TicketsAjaxAPI extends AjaxController { $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; $tickets=array(); - $sql='SELECT email, count(ticket_id) as tickets ' - .' FROM '.TICKET_TABLE - .' WHERE email LIKE \'%'.db_input(strtolower($_REQUEST['q']), false).'%\' '; + $sql='SELECT email.address AS email, count(ticket.ticket_id) as tickets ' + .' FROM '.TICKET_TABLE.' ticket' + .' JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' + .' JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' + .' LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON (entry.object_id = user.id + AND entry.object_type=\'U\') + LEFT JOIN '.FORM_ANSWER_TABLE.' data ON (data.entry_id = entry.id)' + .' WHERE (email.address LIKE \'%'.db_input(strtolower($_REQUEST['q']), false).'%\' + OR data.value LIKE \'%'.db_input($_REQUEST['q'], false).'%\')'; $sql.=' AND ( staff_id='.db_input($thisstaff->getId()); @@ -73,22 +87,23 @@ class TicketsAjaxAPI extends AjaxController { $sql.=' OR dept_id IN ('.implode(',', db_input($depts)).')'; $sql.=' ) ' - .' GROUP BY email ' - .' ORDER BY created LIMIT '.$limit; + .' GROUP BY email.address ' + .' ORDER BY ticket.created LIMIT '.$limit; if(($res=db_query($sql)) && db_num_rows($res)) { while(list($email, $count)=db_fetch_row($res)) - $tickets[] = array('email'=>$email, 'value'=>$email, 'info'=>"$email ($count)"); + $tickets[] = array('email'=>$email, 'value'=>$email, + 'info'=>"$email ($count)", 'matches'=>$_REQUEST['q']); } return $this->json_encode($tickets); } - function search() { + function _search($req) { global $thisstaff, $cfg; $result=array(); - $select = 'SELECT count( DISTINCT ticket.ticket_id) as tickets '; + $select = 'SELECT DISTINCT ticket.ticket_id'; $from = ' FROM '.TICKET_TABLE.' ticket '; $where = ' WHERE 1 '; @@ -104,15 +119,15 @@ class TicketsAjaxAPI extends AjaxController { $where.=' ) '; //Department - if($_REQUEST['deptId']) - $where.=' AND ticket.dept_id='.db_input($_REQUEST['deptId']); + if($req['deptId']) + $where.=' AND ticket.dept_id='.db_input($req['deptId']); //Help topic - if($_REQUEST['topicId']) - $where.=' AND ticket.topic_id='.db_input($_REQUEST['topicId']); + if($req['topicId']) + $where.=' AND ticket.topic_id='.db_input($req['topicId']); //Status - switch(strtolower($_REQUEST['status'])) { + switch(strtolower($req['status'])) { case 'open': $where.=' AND ticket.status="open" '; break; @@ -128,9 +143,9 @@ class TicketsAjaxAPI extends AjaxController { } //Assignee - if(isset($_REQUEST['assignee']) && strcasecmp($_REQUEST['status'], 'closed')) { - $id=preg_replace("/[^0-9]/", "", $_REQUEST['assignee']); - $assignee = $_REQUEST['assignee']; + if(isset($req['assignee']) && strcasecmp($req['status'], 'closed')) { + $id=preg_replace("/[^0-9]/", "", $req['assignee']); + $assignee = $req['assignee']; $where.= ' AND ( ( ticket.status="open" '; if($assignee[0]=='t') $where.=' AND ticket.team_id='.db_input($id); @@ -141,19 +156,19 @@ class TicketsAjaxAPI extends AjaxController { $where.=')'; - if($_REQUEST['staffId'] && !$_REQUEST['status']) //Assigned TO + Closed By - $where.= ' OR (ticket.staff_id='.db_input($_REQUEST['staffId']). ' AND ticket.status="closed") '; - elseif(isset($_REQUEST['staffId'])) // closed by any + if($req['staffId'] && !$req['status']) //Assigned TO + Closed By + $where.= ' OR (ticket.staff_id='.db_input($req['staffId']). ' AND ticket.status="closed") '; + elseif(isset($req['staffId'])) // closed by any $where.= ' OR ticket.status="closed" '; $where.= ' ) '; - } elseif($_REQUEST['staffId']) { - $where.=' AND (ticket.staff_id='.db_input($_REQUEST['staffId']).' AND ticket.status="closed") '; + } elseif($req['staffId']) { + $where.=' AND (ticket.staff_id='.db_input($req['staffId']).' AND ticket.status="closed") '; } //dates - $startTime =($_REQUEST['startDate'] && (strlen($_REQUEST['startDate'])>=8))?strtotime($_REQUEST['startDate']):0; - $endTime =($_REQUEST['endDate'] && (strlen($_REQUEST['endDate'])>=8))?strtotime($_REQUEST['endDate']):0; + $startTime =($req['startDate'] && (strlen($req['startDate'])>=8))?strtotime($req['startDate']):0; + $endTime =($req['endDate'] && (strlen($req['endDate'])>=8))?strtotime($req['endDate']):0; if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0)) $startTime=$endTime=0; @@ -164,23 +179,65 @@ class TicketsAjaxAPI extends AjaxController { $where.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')'; //Query - if($_REQUEST['query']) { - $queryterm=db_real_escape($_REQUEST['query'], false); - - $from.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'; - $where.=" AND ( ticket.email LIKE '%$queryterm%'" - ." OR ticket.name LIKE '%$queryterm%'" - ." OR ticket.subject LIKE '%$queryterm%'" + if($req['query']) { + $queryterm=db_real_escape($req['query'], false); + + $from.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )' + .' LEFT JOIN '.FORM_ENTRY_TABLE.' tentry ON (tentry.object_id = ticket.ticket_id + AND tentry.object_type="T") + LEFT JOIN '.FORM_ANSWER_TABLE.' tans ON (tans.entry_id = tentry.id + AND tans.value_id IS NULL) + LEFT JOIN '.FORM_ENTRY_TABLE.' uentry ON (uentry.object_id = ticket.user_id + AND uentry.object_type="U") + LEFT JOIN '.FORM_ANSWER_TABLE.' uans ON (uans.entry_id = uentry.id + AND uans.value_id IS NULL) + LEFT JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id) + LEFT JOIN '.USER_EMAIL_TABLE.' uemail ON (user.id = uemail.user_id)'; + + $where.=" AND ( uemail.address LIKE '%$queryterm%'" + ." OR user.name LIKE '%$queryterm%'" + ." OR tans.value LIKE '%$queryterm%'" + ." OR uans.value LIKE '%$queryterm%'" ." OR thread.title LIKE '%$queryterm%'" ." OR thread.body LIKE '%$queryterm%'" .' )'; } + // Dynamic fields + $dynfields='(SELECT entry.object_id, value, value_id FROM '.FORM_ANSWER_TABLE.' ans '. + 'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '. + 'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '. + 'WHERE field.name = %1$s AND entry.object_type="T")'; + foreach (TicketForm::getInstance()->getFields() as $f) { + if ($f->get('name') && isset($req[$f->getFormName()]) + && ($val = $req[$f->getFormName()])) { + $name = 'dyn_'.$f->get('id'); + $from .= ' LEFT JOIN '.sprintf($dynfields, db_input($f->get('name'))) + ." $name ON ($name.object_id = ticket.ticket_id)"; + $where .= " AND ($name.value_id = ".db_input($val) + . " OR $name.value LIKE '%".db_real_escape($val)."%')"; + } + } + $sql="$select $from $where"; - if(($tickets=db_result(db_query($sql)))) { - $result['success'] =sprintf("Search criteria matched %s - <a href='tickets.php?%s'>view</a>", - ($tickets>1?"$tickets tickets":"$tickets ticket"), - str_replace(array('&', '&'), array('&', '&'), $_SERVER['QUERY_STRING'])); + $res = db_query($sql); + while (list($tickets[]) = db_fetch_row($res)); + $tickets = array_filter($tickets); + + return $tickets; + } + + function search() { + $tickets = self::_search($_REQUEST); + + if (count($tickets)) { + $uid = md5($_SERVER['QUERY_STRING']); + $_SESSION["adv_$uid"] = $tickets; + $result['success'] =sprintf( + "Search criteria matched %d %s - <a href='tickets.php?%s'>view</a>", + count($tickets), (count($tickets)>1?"tickets":"ticket"), + 'advsid='.$uid + ); } else { $result['fail']='No tickets found matching your search criteria.'; } diff --git a/include/ajax.users.php b/include/ajax.users.php index d18f3c3323b889b3e155575b608f2e75fea61dd8..a075ff42176d219726c7d3323a20093c673a6f86 100644 --- a/include/ajax.users.php +++ b/include/ajax.users.php @@ -31,9 +31,10 @@ class UsersAjaxAPI extends AjaxController { $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25; $users=array(); - $sql='SELECT DISTINCT email, name ' - .' FROM '.TICKET_TABLE - .' WHERE email LIKE \'%'.db_input(strtolower($_REQUEST['q']), false).'%\' ' + $sql='SELECT DISTINCT email.address, name ' + .' FROM '.USER_TABLE.' user ' + .' JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id ' + .' WHERE email.address LIKE \'%'.db_input(strtolower($_REQUEST['q']), false).'%\' ' .' ORDER BY created ' .' LIMIT '.$limit; diff --git a/include/api.tickets.php b/include/api.tickets.php index f49680ce7f60c62b9e61cfe5d6fdbd8e21f7ab44..81dac7a927134e11e090cc1e356b42e82064b915 100644 --- a/include/api.tickets.php +++ b/include/api.tickets.php @@ -8,15 +8,25 @@ class TicketApiController extends ApiController { # Supported arguments -- anything else is an error. These items will be # inspected _after_ the fixup() method of the ApiXxxDataParser classes # so that all supported input formats should be supported - function getRequestStructure($format) { + function getRequestStructure($format, $data=null) { $supported = array( "alert", "autorespond", "source", "topicId", - "name", "email", "subject", "phone", "phone_ext", "attachments" => array("*" => array("name", "type", "data", "encoding") ), "message", "ip", "priorityId" ); + # Fetch dynamic form field names for the given help topic and add + # the names to the supported request structure + if (isset($data['topicId'])) { + $topic=Topic::lookup($data['topicId']); + $form=DynamicForm::lookup($topic->ht['form_id']); + foreach ($form->getDynamicFields() as $field) + $supported[] = $field->get('name'); + } + $form = TicketForm::lookup()->instanciate(); + foreach ($form->getDynamicFields() as $field) + $supported[] = $field->get('name'); if(!strcasecmp($format, 'email')) { $supported = array_merge($supported, array('header', 'mid', @@ -90,6 +100,15 @@ class TicketApiController extends ApiController { # Create the ticket with the data (attempt to anyway) $errors = array(); + + if ($topic=Topic::lookup($data['topicId'])) { + if ($form=DynamicForm::lookup($topic->ht['form_id'])) { + $form = $form->instanciate(); + if (!$form->isValid()) + $errors += $form->errors(); + } + } + $ticket = Ticket::create($data, $errors, $data['source'], $autorespond, $alert); # Return errors (?) if (count($errors)) { @@ -105,6 +124,12 @@ class TicketApiController extends ApiController { return $this->exerr(500, "Unable to create new ticket: unknown error"); } + # Save dynamic form + if (isset($form)) { + $form->setTicketId($ticket->getId()); + $form->save(); + } + return $ticket; } diff --git a/include/class.api.php b/include/class.api.php index 019d3fba39e46286e98cbe6664e229645e88adfc..418b8da76824c3574b89a508bd298bcccfa1476f 100644 --- a/include/class.api.php +++ b/include/class.api.php @@ -235,7 +235,7 @@ class ApiController { * Structure to validate the request against -- must be overridden to be * useful */ - function getRequestStructure($format) { return array(); } + function getRequestStructure($format, $data=null) { return array(); } /** * Simple validation that makes sure the keys of a parsed request are * expected. It is assumed that the functions actually implementing the @@ -266,7 +266,7 @@ class ApiController { function validate(&$data, $format) { return $this->validateRequestStructure( $data, - $this->getRequestStructure($format) + $this->getRequestStructure($format, $data) ); } diff --git a/include/class.client.php b/include/class.client.php index 5ff5911c26297cea963bf37b50e66ee955ba9b1f..3ee32c8f73f331ee5979fafebcc508a728066040 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -24,6 +24,8 @@ class Client { var $username; var $email; + var $_answers; + var $ticket_id; var $ticketID; @@ -40,11 +42,14 @@ class Client { if(!$id && !($id=$this->getId())) return false; - $sql='SELECT ticket_id, ticketID, name, email, phone, phone_ext ' - .' FROM '.TICKET_TABLE + $sql='SELECT ticket.ticket_id, ticketID, email.address as email ' + .' FROM '.TICKET_TABLE.' ticket ' + .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' + .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' .' WHERE ticketID='.db_input($id); + if($email) - $sql.=' AND email='.db_input($email); + $sql.=' AND email.address = '.db_input($email); if(!($res=db_query($sql)) || !db_num_rows($res)) return NULL; @@ -53,15 +58,26 @@ class Client { $this->id = $this->ht['ticketID']; //placeholder $this->ticket_id = $this->ht['ticket_id']; $this->ticketID = $this->ht['ticketID']; - $this->fullname = ucfirst($this->ht['name']); + + $user = User::lookup(array('emails__address'=>$this->ht['email'])); + $this->fullname = $user->getFullName(); + $this->username = $this->ht['email']; $this->email = $this->ht['email']; $this->stats = array(); - + return($this->id); } + function loadDynamicData() { + $this->_answers = array(); + foreach (DynamicFormEntry::forClient($this->getId()) as $form) + foreach ($form->getAnswers() as $answer) + $this->_answers[$answer->getField()->get('name')] = + $answer->getValue(); + } + function reload() { return $this->load(); } @@ -87,13 +103,9 @@ class Client { } function getPhone() { - return $this->ht['phone']; + return $this->_answers['phone']; } - function getPhoneExt() { - return $this->ht['phone_ext']; - } - function getTicketID() { return $this->ticketID; } @@ -120,9 +132,11 @@ class Client { /* ------------- Static ---------------*/ function getLastTicketIdByEmail($email) { - $sql='SELECT ticketID FROM '.TICKET_TABLE - .' WHERE email='.db_input($email) - .' ORDER BY created ' + $sql='SELECT ticket.ticketID '.TICKET_TABLE.' ticket ' + .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' + .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' + .' WHERE email.address = '.db_input($email) + .' ORDER BY ticket.created ' .' LIMIT 1'; if(($res=db_query($sql)) && db_num_rows($res)) list($tid) = db_fetch_row($res); @@ -175,12 +189,12 @@ class Client { //See if we can fetch local ticket id associated with the ID given if(($ticket=Ticket::lookupByExtId($ticketID, $email)) && $ticket->getId()) { //At this point we know the ticket ID is valid. - //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? + //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? //Check the email given. # Require auth token for automatic logins (GET METHOD). if (!strcasecmp($ticket->getEmail(), $email) && (!$auto_login || $auth === $ticket->getAuthToken())) { - + //valid match...create session goodies for the client. $user = new ClientSession($email,$ticket->getExtId()); $_SESSION['_client'] = array(); //clear. @@ -193,7 +207,7 @@ class Client { //Log login info... $msg=sprintf('%s/%s logged in [%s]', $ticket->getEmail(), $ticket->getExtId(), $_SERVER['REMOTE_ADDR']); $ost->logDebug('User login', $msg); - + //Regenerate session ID. $sid=session_id(); //Current session id. session_regenerate_id(TRUE); //get new ID. @@ -202,7 +216,7 @@ class Client { return $user; - } + } } //If we get to this point we know the login failed. diff --git a/include/class.config.php b/include/class.config.php index 5d0c8160fda9113de06af83bcf58482d0ad996e8..acd72ca3066f160618fad11970a93d7aa17f1372 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -146,6 +146,7 @@ class OsticketConfig extends Config { 'allow_email_attachments' => true, 'allow_online_attachments' => true, 'allow_online_attachments_onlogin' => false, + 'name_format' => 'full', # First Last ); function OsticketConfig($section=null) { @@ -326,6 +327,10 @@ class OsticketConfig extends Config { return $this->get('autolock_minutes'); } + function getDefaultNameFormat() { + return $this->get('name_format'); + } + function getDefaultDeptId() { return $this->get('default_dept_id'); } @@ -519,11 +524,6 @@ class OsticketConfig extends Config { return ($this->get('enable_mail_polling')); } - function allowPriorityChange() { - return ($this->get('allow_priority_change')); - } - - function useEmailPriority() { return ($this->get('use_email_priority')); } @@ -768,7 +768,6 @@ class OsticketConfig extends Config { $f['helpdesk_url']=array('type'=>'string', 'required'=>1, 'error'=>'Helpdesk URl required'); $f['helpdesk_title']=array('type'=>'string', 'required'=>1, 'error'=>'Helpdesk title required'); $f['default_dept_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default Dept. required'); - $f['default_template_id']=array('type'=>'int', 'required'=>1, 'error'=>'You must select template.'); $f['staff_session_timeout']=array('type'=>'int', 'required'=>1, 'error'=>'Enter idle time in minutes'); $f['client_session_timeout']=array('type'=>'int', 'required'=>1, 'error'=>'Enter idle time in minutes'); //Date & Time Options @@ -789,10 +788,10 @@ class OsticketConfig extends Config { 'helpdesk_title'=>$vars['helpdesk_title'], 'helpdesk_url'=>$vars['helpdesk_url'], 'default_dept_id'=>$vars['default_dept_id'], - 'default_template_id'=>$vars['default_template_id'], 'max_page_size'=>$vars['max_page_size'], 'log_level'=>$vars['log_level'], 'log_graceperiod'=>$vars['log_graceperiod'], + 'name_format'=>$vars['name_format'], 'passwd_reset_period'=>$vars['passwd_reset_period'], 'staff_max_logins'=>$vars['staff_max_logins'], 'staff_login_timeout'=>$vars['staff_login_timeout'], @@ -861,7 +860,6 @@ class OsticketConfig extends Config { 'default_sla_id'=>$vars['default_sla_id'], 'max_open_tickets'=>$vars['max_open_tickets'], 'autolock_minutes'=>$vars['autolock_minutes'], - 'allow_priority_change'=>isset($vars['allow_priority_change'])?1:0, 'use_email_priority'=>isset($vars['use_email_priority'])?1:0, 'enable_captcha'=>isset($vars['enable_captcha'])?1:0, 'log_ticket_activity'=>isset($vars['log_ticket_activity'])?1:0, @@ -889,6 +887,7 @@ class OsticketConfig extends Config { function updateEmailsSettings($vars, &$errors) { $f=array(); + $f['default_template_id']=array('type'=>'int', 'required'=>1, 'error'=>'You must select template.'); $f['default_email_id']=array('type'=>'int', 'required'=>1, 'error'=>'Default email required'); $f['alert_email_id']=array('type'=>'int', 'required'=>1, 'error'=>'Selection required'); $f['admin_email']=array('type'=>'email', 'required'=>1, 'error'=>'System admin email required'); @@ -903,6 +902,7 @@ class OsticketConfig extends Config { return false; return $this->updateAll(array( + 'default_template_id'=>$vars['default_template_id'], 'default_email_id'=>$vars['default_email_id'], 'alert_email_id'=>$vars['alert_email_id'], 'default_smtp_id'=>$vars['default_smtp_id'], diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php new file mode 100644 index 0000000000000000000000000000000000000000..92863db2fd331d0b5cbb5b239e6b9a8555ea219d --- /dev/null +++ b/include/class.dynamic_forms.php @@ -0,0 +1,817 @@ +<?php +/********************************************************************* + class.dynamic_forms.php + + Forms models built on the VerySimpleModel paradigm. Allows for arbitrary + data to be associated with tickets. Eventually this model can be + extended to associate arbitrary data with registered clients and thread + entries. + + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +require_once(INCLUDE_DIR . 'class.orm.php'); +require_once(INCLUDE_DIR . 'class.forms.php'); + +/** + * Form template, used for designing the custom form and for entering custom + * data for a ticket + */ +class DynamicForm extends VerySimpleModel { + + static $meta = array( + 'table' => FORM_SEC_TABLE, + 'ordering' => array('title'), + 'pk' => array('id'), + ); + + // Registered form types + static $types = array( + 'T' => 'Ticket Information', + 'U' => 'User Information', + ); + + var $_form; + var $_fields; + var $_dfields; + + function getFields() { + if (!isset($this->_fields)) { + $this->_fields = array(); + foreach ($this->getDynamicFields() as $f) + // TODO: Index by field name or id + $this->_fields[$f->get('id')] = $f->getImpl($f); + } + return $this->_fields; + } + + function getDynamicFields() { + if (!isset($this->_dfields)) + $this->_dfields = DynamicFormField::objects() + ->filter(array('form_id'=>$this->id)) + ->all(); + return $this->_dfields; + } + + // Multiple inheritance -- delegate to Form + function __call($what, $args) { + $delegate = array($this->getForm(), $what); + if (!is_callable($delegate)) + throw new Exception($what.': Call to non-existing function'); + return call_user_func_array($delegate, $args); + } + + function hasField($name) { + foreach ($this->getDynamicFields() as $f) + if ($f->get('name') == $name) + return true; + } + + function getTitle() { return $this->get('title'); } + function getInstructions() { return $this->get('instructions'); } + + function getForm() { + if (!$this->_form) { + $fields = $this->getFields(); + $this->_form = new Form($fields, false, array( + 'title'=>$this->title, 'instructions'=>$this->instructions)); + } + return $this->_form; + } + + function isDeletable() { + return $this->get('deletable'); + } + + function instanciate($sort=1) { + return DynamicFormEntry::create(array( + 'form_id'=>$this->get('id'), 'sort'=>$sort)); + } + + function data($data) { + if ($data instanceof DynamicFormEntry) { + $this->_fields = $data->getFields(); + } + } + + function save($refetch=false) { + if (count($this->dirty)) + $this->set('updated', new SqlFunction('NOW')); + if (isset($this->dirty['notes'])) + $this->notes = Format::sanitize($this->notes); + return parent::save($refetch); + } + + function delete() { + if (!$this->isDeletable()) + return false; + else + return parent::delete(); + } + + static function create($ht=false) { + $inst = parent::create($ht); + $inst->set('created', new SqlFunction('NOW')); + if (isset($ht['fields'])) { + $inst->save(); + foreach ($ht['fields'] as $f) { + $f = DynamicFormField::create($f); + $f->form_id = $inst->id; + $f->save(); + } + } + return $inst; + } +} + +class UserForm extends DynamicForm { + static $instance; + + static function objects() { + $os = parent::objects(); + return $os->filter(array('type'=>'U')); + } + + function getFields() { + $fields = parent::getFields(); + foreach ($fields as $f) { + if ($f->get('name') == 'email') { + $f->getConfiguration(); + $f->_config['classes'] = 'auto email typeahead'; + $f->_config['autocomplete'] = false; + } + elseif ($f->get('name') == 'name') { + $f->getConfiguration(); + $f->_config['classes'] = 'auto name'; + } + } + return $fields; + } + + static function getInstance() { + if (!isset(static::$instance)) { + $o = static::objects(); + static::$instance = $o[0]->instanciate(); + } + return static::$instance; + } +} + +class TicketForm extends DynamicForm { + static $instance; + + static function objects() { + $os = parent::objects(); + return $os->filter(array('type'=>'T')); + } + + static function getInstance() { + if (!isset(static::$instance)) { + $o = static::objects(); + static::$instance = $o[0]->instanciate(); + } + return static::$instance; + } +} +// Add fields from the standard ticket form to the ticket filterable fields +Filter::addSupportedMatches('Custom Fields', function() { + $matches = array(); + foreach (TicketForm::getInstance()->getFields() as $f) { + if (!$f->hasData()) + continue; + $matches['field.'.$f->get('id')] = $f->getLabel(); + } + return $matches; +}); + +require_once(INCLUDE_DIR . "class.json.php"); + +class DynamicFormField extends VerySimpleModel { + + static $meta = array( + 'table' => FORM_FIELD_TABLE, + 'ordering' => array('sort'), + 'pk' => array('id'), + 'joins' => array( + 'form' => array( + 'null' => true, + 'constraint' => array('form_id' => 'DynamicForm.id'), + ), + ), + ); + + var $_field; + + // Multiple inheritance -- delegate to FormField + function __call($what, $args) { + return call_user_func_array( + array($this->getField(), $what), $args); + } + + function getField() { + if (!isset($this->_field)) + $this->_field = new FormField($this->ht); + return $this->_field; + } + + function getAnswer() { return $this->answer; } + + /** + * setConfiguration + * + * Used in the POST request of the configuration process. The + * ::getConfigurationForm() method should be used to retrieve a + * configuration form for this field. That form should be submitted via + * a POST request, and this method should be called in that request. The + * data from the POST request will be interpreted and will adjust the + * configuration of this field + * + * Parameters: + * errors - (OUT array) receives validation errors of the parsed + * configuration form + * + * Returns: + * (bool) true if the configuration was updated, false if there were + * errors. If false, the errors were written into the received errors + * array. + */ + function setConfiguration(&$errors=array()) { + $config = array(); + foreach ($this->getConfigurationForm() as $name=>$field) { + $config[$name] = $field->getClean(); + $errors = array_merge($errors, $field->errors()); + } + if (count($errors) === 0) + $this->set('configuration', JsonDataEncoder::encode($config)); + $this->set('hint', $_POST['hint']); + return count($errors) === 0; + } + + function isDeletable() { + return ($this->get('edit_mask') & 1) == 0; + } + function isNameForced() { + return $this->get('edit_mask') & 2; + } + function isPrivacyForced() { + return $this->get('edit_mask') & 4; + } + function isRequirementForced() { + return $this->get('edit_mask') & 8; + } + + function delete() { + // Don't really delete form fields as that will screw up the data + // model. Instead, just drop the association with the form which + // will give the appearance of deletion. Not deleting means that + // the field will continue to exist on form entries it may already + // have answers on, but since it isn't associated with the form, it + // won't be available for new form submittals. + $this->set('form_id', 0); + $this->save(); + } + + function save() { + if (count($this->dirty)) + $this->set('updated', new SqlFunction('NOW')); + return parent::save(); + } + + static function create($ht=false) { + $inst = parent::create($ht); + $inst->set('created', new SqlFunction('NOW')); + if (isset($ht['configuration'])) + $inst->configuration = JsonDataEncoder::encode($ht['configuration']); + return $inst; + } +} + +/** + * Represents an entry to a dynamic form. Used to render the completed form + * in reference to the attached ticket, etc. A form is used to represent the + * template of enterable data. This represents the data entered into an + * instance of that template. + * + * The data of the entry is called 'answers' in this model. This model + * represents an instance of a form entry. The data / answers to that entry + * are represented individually in the DynamicFormEntryAnswer model. + */ +class DynamicFormEntry extends VerySimpleModel { + + static $meta = array( + 'table' => FORM_ENTRY_TABLE, + 'ordering' => array('sort'), + 'pk' => array('id'), + 'joins' => array( + 'form' => array( + 'null' => true, + 'constraint' => array('form_id' => 'DynamicForm.id'), + ), + ), + ); + + var $_values; + var $_fields; + var $_form; + var $_errors = false; + var $_clean = false; + + function getAnswers() { + if (!isset($this->_values)) { + $this->_values = DynamicFormEntryAnswer::objects() + ->filter(array('entry_id'=>$this->get('id'))) + ->all(); + foreach ($this->_values as $v) + $v->entry = $this; + } + return $this->_values; + } + + function getAnswer($name) { + foreach ($this->getAnswers() as $ans) + if ($ans->getField()->get('name') == $name) + return $ans->getValue(); + return null; + } + function setAnswer($name, $value, $id=false) { + foreach ($this->getAnswers() as $ans) { + if ($ans->getField()->get('name') == $name) { + $ans->set('value', $value); + if ($id !== false) + $ans->set('value_id', $id); + break; + } + } + } + + function errors() { + return $this->_errors; + } + + function getTitle() { return $this->getForm()->getTitle(); } + function getInstructions() { return $this->getForm()->getInstructions(); } + + function getForm() { + if (!isset($this->_form)) { + $this->_form = DynamicForm::lookup($this->get('form_id')); + if ($this->id) + $this->_form->data($this); + } + return $this->_form; + } + + function getFields() { + if (!isset($this->_fields)) { + $this->_fields = array(); + foreach ($this->getAnswers() as $a) + $this->_fields[] = $a->getField(); + } + return $this->_fields; + } + + function isValid() { + if (!is_array($this->_errors)) { + $this->_errors = array(); + $this->getClean(); + foreach ($this->getFields() as $field) + if ($field->errors()) + $this->_errors[$field->get('id')] = $field->errors(); + } + return !$this->_errors; + } + + function getClean() { + if (!$this->_clean) { + $this->_clean = array(); + foreach ($this->getFields() as $field) + $this->_clean[$field->get('id')] + = $this->_clean[$field->get('name')] = $field->getClean(); + } + return $this->_clean; + } + + function forTicket($ticket_id, $force=false) { + static $entries = array(); + if (!isset($entries[$ticket_id]) || $force) + $entries[$ticket_id] = DynamicFormEntry::objects() + ->filter(array('object_id'=>$ticket_id, 'object_type'=>'T')); + return $entries[$ticket_id]; + } + function setTicketId($ticket_id) { + $this->object_type = 'T'; + $this->object_id = $ticket_id; + } + + function forClient($user_id) { + return DynamicFormEntry::objects() + ->filter(array('object_id'=>$user_id, 'object_type'=>'U')); + } + function setClientId($user_id) { + $this->object_type = 'U'; + $this->object_id = $user_id; + } + + function render($staff=true) { + return $this->getForm()->render($staff); + } + + /** + * addMissingFields + * + * Adds fields that have been added to the linked form (field set) since + * this entry was originally created. If fields are added to the form, + * the method will automatically add the fields and null answers to the + * entry. + */ + function addMissingFields() { + foreach ($this->getForm()->getDynamicFields() as $field) { + $found = false; + foreach ($this->getAnswers() as $answer) { + if ($answer->get('field_id') == $field->get('id')) { + $found = true; break; + } + } + if (!$found && ($field = $field->getImpl($field)) + && !$field->isPresentationOnly()) { + $a = DynamicFormEntryAnswer::create( + array('field_id'=>$field->get('id'), 'entry_id'=>$this->id)); + $a->field = $field; + $a->entry = $this; + // Add to list of answers + $this->_values[] = $a; + $this->_fields[] = $field; + // Omit fields without data + // For user entries, the name and email fields should not be + // saved with the rest of the data + if (!($this->object_type == 'U' + && in_array($field->get('name'), array('name','email'))) + && $field->hasData()) + $a->save(); + $this->_form = null; + } + // Sort the form the way it is declared to be sorted + if ($this->_fields) + usort($this->_fields, function($a, $b) { + return $a->get('sort') - $b->get('sort'); + }); + } + } + + function save() { + if (count($this->dirty)) + $this->set('updated', new SqlFunction('NOW')); + parent::save(); + foreach ($this->getAnswers() as $a) { + $field = $a->getField(); + if ($this->object_type == 'U' + && in_array($field->get('name'), array('name','email'))) + continue; + $val = $field->to_database($field->getClean()); + if (is_array($val)) { + $a->set('value', $val[0]); + $a->set('value_id', $val[1]); + } + else + $a->set('value', $val); + $a->set('entry_id', $this->get('id')); + // Don't save answers for presentation-only fields + if ($field->hasData() && !$field->isPresentationOnly()) + $a->save(); + } + $this->_values = array(); + } + + function delete() { + foreach ($this->getAnswers() as $a) + $a->delete(); + return parent::delete(); + } + + static function create($ht=false) { + $inst = parent::create($ht); + $inst->set('created', new SqlFunction('NOW')); + foreach ($inst->getForm()->getFields() as $f) { + if (!$f->hasData()) continue; + $a = DynamicFormEntryAnswer::create( + array('field_id'=>$f->get('id'))); + $a->field = $f; + $inst->_values[] = $a; + } + return $inst; + } +} + +/** + * Represents a single answer to a single field on a dynamic form. The + * data / answer to the field is linked back to the form and field which was + * originally used for the submission. + */ +class DynamicFormEntryAnswer extends VerySimpleModel { + + static $meta = array( + 'table' => FORM_ANSWER_TABLE, + 'ordering' => array('field__sort'), + 'pk' => array('entry_id', 'field_id'), + 'joins' => array( + 'field' => array( + 'constraint' => array('field_id' => 'DynamicFormField.id'), + ), + 'entry' => array( + 'constraint' => array('entry_id' => 'DynamicFormEntry.id'), + ), + ), + ); + + var $field; + var $form; + var $entry; + var $_value; + + function getEntry() { + return $this->entry; + } + + function getForm() { + if (!$this->form) + $this->form = $this->getEntry()->getForm(); + return $this->form; + } + + function getField() { + if (!isset($this->field)) { + $f = DynamicFormField::lookup($this->get('field_id')); + $this->field = $f->getImpl($f); + $this->field->answer = $this; + } + return $this->field; + } + + function getValue() { + if (!$this->_value) + $this->_value = $this->getField()->to_php( + $this->get('value'), $this->get('value_id')); + return $this->_value; + } + + function getIdValue() { + return $this->get('value_id'); + } + + function toString() { + return $this->getField()->toString($this->getValue()); + } + + function __toString() { + return $this->toString(); + } +} + +/** + * Dynamic lists are used to represent list of arbitrary data that can be + * used as dropdown or typeahead selections in dynamic forms. This model + * defines a list. The individual items are stored in the DynamicListItem + * model. + */ +class DynamicList extends VerySimpleModel { + + static $meta = array( + 'table' => LIST_TABLE, + 'ordering' => array('name'), + 'pk' => array('id'), + ); + + var $_items; + + function getSortModes() { + return array( + 'Alpha' => 'Alphabetical', + '-Alpha' => 'Alphabetical (Reversed)', + 'SortCol' => 'Manually Sorted' + ); + } + + function getListOrderBy() { + switch ($this->sort_mode) { + case 'Alpha': return 'value'; + case '-Alpha': return '-value'; + case 'SortCol': return 'sort'; + } + } + + function getPluralName() { + if ($name = $this->get('name_plural')) + return $name; + else + return $this->get('name') . 's'; + } + + function getItems($limit=false, $offset=false) { + if (!$this->_items) { + $this->_items = DynamicListItem::objects()->filter( + array('list_id'=>$this->get('id'))) + ->order_by($this->getListOrderBy()); + if ($limit) + $this->_items->limit($limit); + if ($offset) + $this->_items->offset($offset); + } + return $this->_items; + } + + function getItemCount() { + return DynamicListItem::objects()->filter(array('list_id'=>$this->id)) + ->count(); + } + + function save($refetch=false) { + if (count($this->dirty)) + $this->set('updated', new SqlFunction('NOW')); + if (isset($this->dirty['notes'])) + $this->notes = Format::sanitize($this->notes); + return parent::save($refetch); + } + + function delete() { + $fields = DynamicFormField::objects()->filter(array( + 'type'=>'list-'.$this->id))->count(); + if ($fields == 0) + return parent::delete(); + else + // Refuse to delete lists that are in use by fields + return false; + } + + static function create($ht=false) { + $inst = parent::create($ht); + $inst->set('created', new SqlFunction('NOW')); + return $inst; + } + + static function getSelections() { + $selections = array(); + foreach (DynamicList::objects() as $list) { + $selections['list-'.$list->id] = + array($list->getPluralName(), + SelectionField, $list->get('id')); + } + return $selections; + } +} +FormField::addFieldTypes('Custom Lists', array(DynamicList, 'getSelections')); + +/** + * Represents a single item in a dynamic list + * + * Fields: + * value - (char * 255) Actual list item content + * extra - (char * 255) Other values that represent the same item in the + * list, such as an abbreviation. In practice, should be a + * space-separated list of tokens which should hit this list item in a + * search + * sort - (int) If sorting by this field, represents the numeric sort order + * that this item should come in the dropdown list + */ +class DynamicListItem extends VerySimpleModel { + + static $meta = array( + 'table' => LIST_ITEM_TABLE, + 'pk' => array('id'), + 'joins' => array( + 'list' => array( + 'null' => true, + 'constraint' => array('list_id' => 'DynamicList.id'), + ), + ), + ); + + function toString() { + return $this->get('value'); + } + + function delete() { + # Don't really delete, just unset the list_id to un-associate it with + # the list + $this->set('list_id', null); + return $this->save(); + } +} + +class SelectionField extends FormField { + function getListId() { + list(,$list_id) = explode('-', $this->get('type')); + return $list_id; + } + + function getList() { + if (!$this->_list) + $this->_list = DynamicList::lookup($this->getListId()); + return $this->_list; + } + + function getWidget() { + return new SelectionWidget($this); + } + + function parse($value) { + return $this->to_php($value); + } + + function to_php($value, $id=false) { + $item = DynamicListItem::lookup($id ? $id : $value); + # Attempt item lookup by name too + if (!$item) { + $item = DynamicListItem::lookup(array( + 'value'=>$value, + 'list_id'=>$this->getListId())); + } + return ($item) ? $item : $id; + } + + function to_database($item) { + if ($item instanceof DynamicListItem) + return array($item->value, $item->id); + return null; + } + + function toString($item) { + return ($item instanceof DynamicListItem) ? $item->toString() : $item; + } + + function validateEntry($item) { + parent::validateEntry($item); + if ($item && !$item instanceof DynamicListItem) + $this->_errors[] = 'Select a value from the list'; + } + + function getConfigurationOptions() { + return array( + 'typeahead' => new ChoiceField(array( + 'id'=>1, 'label'=>'Widget', 'required'=>false, + 'default'=>false, + 'choices'=>array(false=>'Drop Down', true=>'Typeahead'), + 'hint'=>'Typeahead will work better for large lists')), + ); + } + + function getChoices() { + if (!$this->_choices) { + $this->_choices = array(); + foreach ($this->getList()->getItems() as $i) + $this->_choices[$i->get('id')] = $i->get('value'); + } + return $this->_choices; + } +} + +class SelectionWidget extends ChoicesWidget { + function render() { + $config = $this->field->getConfiguration(); + $value = false; + if ($this->value instanceof DynamicListItem) { + // Loaded from database + $value = $this->value->get('id'); + $name = $this->value->get('value'); + } elseif ($this->value) { + // Loaded from POST + $value = $this->value; + $name = DynamicListItem::lookup($value); + $name = ($name) ? $name->get('value') : $value; + } + + if (!$config['typeahead']) { + $this->value = $value; + return parent::render(); + } + + $source = array(); + foreach ($this->field->getList()->getItems() as $i) + $source[] = array( + 'value' => $i->get('value'), + 'info' => $i->get('value')." -- ".$i->get('extra'), + ); + ?> + <span style="display:inline-block"> + <input type="text" size="30" name="<?php echo $this->name; ?>" + value="<?php echo $name; ?>" autocomplete="off" /> + <script type="text/javascript"> + $(function() { + $('input[name=<?php echo $this->name; ?>]').typeahead({ + source: <?php echo JsonDataEncoder::encode($source); ?>, + property: 'info', + onselect: function(item) { + $('input[name="<?php echo $this->name; ?>"]').val(item['value']) + } + }); + }); + </script> + </span> + <?php + } +} +?> diff --git a/include/class.filter.php b/include/class.filter.php index 1f94120793d338ddb57be5979eb31a500d9c1ada..81c6a39fe17a86e8e5981385a0dd417d6e47955f 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -18,6 +18,17 @@ class Filter { var $id; var $ht; + static $match_types = array( + 'Basic Fields' => array( + 'name' => 'Name', + 'email' => 'Email', + 'subject' => 'Subject', + 'body' => 'Body/Text', + 'reply-to' => 'Reply-To Email', + 'reply-to-name' => 'Reply-To Name', + ), + ); + function Filter($id) { $this->id=0; $this->load($id); @@ -165,7 +176,7 @@ class Filter { function addRule($what, $how, $val,$extra=array()) { - $rule= array_merge($extra,array('w'=>$what, 'h'=>$how, 'v'=>$val)); + $rule= array_merge($extra,array('what'=>$what, 'how'=>$how, 'val'=>$val)); $rule['filter_id']=$this->getId(); return FilterRule::create($rule,$errors); # nolint @@ -299,13 +310,28 @@ class Filter { $ticket['cannedResponseId'] = $this->getCannedResponse(); } /* static */ function getSupportedMatches() { - return array( - 'name'=> 'Name', - 'email'=> 'Email', - 'subject'=> 'Subject', - 'body'=> 'Body/Text' - ); + foreach (static::$match_types as $k=>&$v) { + if (is_callable($v)) + $v = $v(); + } + unset($v); + return static::$match_types; + } + + static function addSupportedMatches($group, $callable) { + static::$match_types[$group] = $callable; } + + static function getSupportedMatchFields() { + $keys = array(); + foreach (static::getSupportedMatches() as $group=>$matches) { + foreach ($matches as $key=>$label) { + $keys[] = $key; + } + } + return $keys; + } + /* static */ function getSupportedMatchTypes() { return array( 'equal'=> 'Equal', @@ -370,7 +396,7 @@ class Filter { function save_rules($id,$vars,&$errors) { - $matches = array_keys(self::getSupportedMatches()); + $matches = array_keys(self::getSupportedMatchFields()); $types = array_keys(self::getSupportedMatchTypes()); $rules=array(); @@ -382,7 +408,9 @@ class Filter { $errors["rule_$i"]='Invalid match type selection'; elseif(!$vars["rule_v$i"]) $errors["rule_$i"]='Value required'; - elseif($vars["rule_w$i"]=='email' && $vars["rule_h$i"]=='equal' && !Validator::is_email($vars["rule_v$i"])) + elseif($vars["rule_w$i"]=='email' + && $vars["rule_h$i"]=='equal' + && !Validator::is_email($vars["rule_v$i"])) $errors["rule_$i"]='Valid email required for the match type'; else //for everything-else...we assume it's valid. $rules[]=array('what'=>$vars["rule_w$i"], @@ -640,22 +668,18 @@ class TicketFilter { * deal with the data in the incoming ticket (based on $vars) will be considered. * @see ::quickList() for more information. */ - function TicketFilter($origin, $vars=null) { + function TicketFilter($origin, $vars=array()) { //Normalize the target based on ticket's origin. $this->target = self::origin2target($origin); //Extract the vars we care about (fields we filter by!). - $this->vars = array_filter(array_map('trim', - array( - 'email' => $vars['email'], - 'subject' => $vars['subject'], - 'name' => $vars['name'], - 'body' => $vars['message'], - 'emailId' => $vars['emailId'], - 'reply-to' => @$vars['reply-to'], - 'reply-to-name' => @$vars['reply-to-name'], - ))); + $this->vars = array('body'=>$vars['message']); + $interest = Filter::getSupportedMatchFields(); + foreach ($vars as $k=>$v) { + if (in_array($k, $interest)) + $this->vars[$k] = trim($v); + } //Init filters. $this->build(); diff --git a/include/class.forms.php b/include/class.forms.php new file mode 100644 index 0000000000000000000000000000000000000000..adb525e8078c931340123f6b9e5d85e8c36b10cc --- /dev/null +++ b/include/class.forms.php @@ -0,0 +1,991 @@ +<?php +/********************************************************************* + class.forms.php + + osTicket forms framework + + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +/** + * Form template, used for designing the custom form and for entering custom + * data for a ticket + */ +class Form { + var $fields = array(); + var $title = 'Unnamed'; + var $instructions = ''; + + var $_errors; + var $_source = false; + + function Form() { + call_user_func_array(array($this, '__construct'), func_get_args()); + } + function __construct($fields=array(), $source=null, $options=array()) { + $this->fields = $fields; + foreach ($fields as $f) + $f->setForm($this); + if (isset($options['title'])) + $this->title = $options['title']; + if (isset($options['instructions'])) + $this->instructions = $options['instructions']; + // Use POST data if source was not specified + $this->_source = ($source) ? $source : $_POST; + } + function data($source) { + foreach ($this->fields as $name=>$f) + if (isset($source[$name])) + $f->value = $source[$name]; + } + + function getFields() { + return $this->fields; + } + function getTitle() { return $this->title; } + function getInstructions() { return $this->instructions; } + function getSource() { return $this->_source; } + + function isValid() { + if (!is_array($this->_errors)) { + $this->_errors = array(); + $this->getClean(); + foreach ($this->getFields() as $field) + if ($field->errors()) + $this->_errors[$field->get('id')] = $field->errors(); + } + return !$this->_errors; + } + + function getClean() { + if (!$this->_clean) { + $this->_clean = array(); + foreach ($this->getFields() as $key=>$field) { + $this->_clean[$key] = $this->_clean[$field->get('name')] + = $field->getClean(); + } + } + return $this->_clean; + } + + function errors() { + return $this->_errors; + } + + function render($staff=true, $title=false, $instructions=false) { + if ($title) + $this->title = $title; + if ($instructions) + $this->instructions = $instructions; + $form = $this; + if ($staff) + include(STAFFINC_DIR . 'templates/dynamic-form.tmpl.php'); + else + include(CLIENTINC_DIR . 'templates/dynamic-form.tmpl.php'); + } +} + +require_once(INCLUDE_DIR . "class.json.php"); + +class FormField { + static $widget = false; + + var $ht = array( + 'label' => 'Unlabeled', + 'required' => false, + 'default' => false, + 'configuration' => array(), + ); + + var $_form; + var $_cform; + var $_clean; + var $_errors = array(); + var $_widget; + var $parent; + var $presentation_only = false; + + static $types = array( + 'Basic Fields' => array( + 'text' => array('Short Answer', TextboxField), + 'memo' => array('Long Answer', TextareaField), + 'thread' => array('Thread Entry', ThreadEntryField, false), + 'datetime' => array('Date and Time', DatetimeField), + 'phone' => array('Phone Number', PhoneField), + 'bool' => array('Checkbox', BooleanField), + 'choices' => array('Choices', ChoiceField), + 'break' => array('Section Break', SectionBreakField), + ), + ); + static $more_types = array(); + + function __construct($options=array()) { + static $uid = 100; + $this->ht = array_merge($this->ht, $options); + if (!isset($this->ht['id'])) + $this->ht['id'] = $uid++; + } + + static function addFieldTypes($group, $callable) { + static::$more_types[$group] = $callable; + } + + static function allTypes() { + if (static::$more_types) { + foreach (static::$more_types as $group=>$c) + static::$types[$group] = call_user_func($c); + static::$more_types = array(); + } + return static::$types; + } + + static function getFieldType($type) { + foreach (static::allTypes() as $group=>$types) + if (isset($types[$type])) + return $types[$type]; + } + + function get($what) { + return $this->ht[$what]; + } + + /** + * getClean + * + * Validates and cleans inputs from POST request. This is performed on a + * field instance, after a DynamicFormSet / DynamicFormSection is + * submitted via POST, in order to kick off parsing and validation of + * user-entered data. + */ + function getClean() { + if (!isset($this->_clean)) { + $value = $this->getWidget()->value; + $this->_clean = $this->parse($value); + $this->validateEntry($this->_clean); + } + return $this->_clean; + } + + function errors() { + return $this->_errors; + } + + function isValidEntry() { + $this->validateEntry(); + return count($this->_errors) == 0; + } + + /** + * validateEntry + * + * Validates user entry on an instance of the field on a dynamic form. + * This is called when an instance of this field (like a TextboxField) + * receives data from the user and that value should be validated. + * + * Parameters: + * $value - (string) input from the user + */ + function validateEntry($value) { + if (!$value && count($this->_errors)) + return; + + # Validates a user-input into an instance of this field on a dynamic + # form + if ($this->get('required') && !$value && $this->hasData()) + $this->_errors[] = sprintf('%s is a required field', $this->getLabel()); + } + + /** + * parse + * + * Used to transform user-submitted data to a PHP value. This value is + * not yet considered valid. The ::validateEntry() method will be called + * on the value to determine if the entry is valid. Therefore, if the + * data is clearly invalid, return something like NULL that can easily + * be deemed invalid in ::validateEntry(), however, can still produce a + * useful error message indicating what is wrong with the input. + */ + function parse($value) { + return trim($value); + } + + /** + * to_php + * + * Transforms the data from the value stored in the database to a PHP + * value. The ::to_database() method is used to produce the database + * valse, so this method is the compliment to ::to_database(). + * + * Parameters: + * $value - (string or null) database representation of the field's + * content + */ + function to_php($value) { + return $value; + } + + /** + * to_database + * + * Determines the value to be stored in the database. The database + * backend for all fields is a text field, so this method should return + * a text value or NULL to represent the value of the field. The + * ::to_php() method will convert this value back to PHP. + * + * Paremeters: + * $value - PHP value of the field's content + */ + function to_database($value) { + return $value; + } + + /** + * toString + * + * Converts the PHP value created in ::parse() or ::to_php() to a + * pretty-printed value to show to the user. This is especially useful + * for something like dates which are stored considerably different in + * the database from their respective human-friendly versions. + * Furthermore, this method allows for internationalization and + * localization. + * + * Parametes: + * $value - PHP value of the field's content + */ + function toString($value) { + return $value; + } + + function getLabel() { return $this->get('label'); } + + /** + * getImpl + * + * Magic method that will return an implementation instance of this + * field based on the simple text value of the 'type' value of this + * field instance. The list of registered fields is determined by the + * global get_dynamic_field_types() function. The data from this model + * will be used to initialize the returned instance. + * + * For instance, if the value of this field is 'text', a TextField + * instance will be returned. + */ + function getImpl($parent=null) { + // Allow registration with ::addFieldTypes and delayed calling + $type = static::getFieldType($this->get('type')); + $clazz = $type[1]; + $inst = new $clazz($this->ht); + $inst->parent = $parent; + $inst->setForm($this->_form); + return $inst; + } + + function __call($what, $args) { + // XXX: Throw exception if $this->parent is not set + if (!$this->parent) + throw new Exception($what.': Call to undefined function'); + // BEWARE: DynamicFormField has a __call() which will create a new + // FormField instance and invoke __call() on it or bounce + // immediately back + return call_user_func_array( + array($this->parent, $what), $args); + } + + function getAnswer() { return $this->answer; } + + function getFormName() { + if (is_numeric($this->get('id'))) + return '-field-id-'.$this->get('id'); + else + return $this->get('id'); + } + + function setForm($form) { + $this->_form = $form; + } + function getForm() { + return $this->_form; + } + /** + * Returns the data source for this field. If created from a form, the + * data source from the form is returned. Otherwise, if the request is a + * POST, then _POST is returned. + */ + function getSource() { + if ($this->_form) + return $this->_form->getSource(); + elseif ($_SERVER['REQUEST_METHOD'] == 'POST') + return $_POST; + else + return array(); + } + + function render($mode=null) { + $this->getWidget()->render($mode); + } + + function renderExtras($mode=null) { + return; + } + + function getConfigurationOptions() { + return array(); + } + + /** + * getConfiguration + * + * Loads configuration information from database into hashtable format. + * Also, the defaults from ::getConfigurationOptions() are integrated + * into the database-backed options, so that if options have not yet + * been set or a new option has been added and not saved for this field, + * the default value will be reflected in the returned configuration. + */ + function getConfiguration() { + if (!$this->_config) { + $this->_config = $this->get('configuration'); + if (is_string($this->_config)) + $this->_config = JsonDataParser::parse($this->_config); + elseif (!$this->_config) + $this->_config = array(); + foreach ($this->getConfigurationOptions() as $name=>$field) + if (!isset($this->_config[$name])) + $this->_config[$name] = $field->get('default'); + } + return $this->_config; + } + + /** + * If the [Config] button should be shown to allow for the configuration + * of this field + */ + function isConfigurable() { + return true; + } + + /** + * Field type is changeable in the admin interface + */ + function isChangeable() { + return true; + } + + /** + * Field does not contain data that should be saved to the database. Ie. + * non data fields like section headers + */ + function hasData() { + return true; + } + + /** + * Returns true if the field/widget should be rendered as an entire + * block in the target form. + */ + function isBlockLevel() { + return false; + } + + /** + * Fields should not be saved with the dynamic data. It is assumed that + * some static processing will store the data elsewhere. + */ + function isPresentationOnly() { + return $this->presentation_only; + } + + function getConfigurationForm() { + if (!$this->_cform) { + $type = static::getFieldType($this->get('type')); + $clazz = $type[1]; + $T = new $clazz(); + $this->_cform = $T->getConfigurationOptions(); + } + return $this->_cform; + } + + function getWidget() { + if (!static::$widget) + throw new Exception('Widget not defined for this field'); + if (!isset($this->_widget)) { + $this->_widget = new static::$widget($this); + $this->_widget->parseValue(); + } + return $this->_widget; + } +} + +class TextboxField extends FormField { + static $widget = 'TextboxWidget'; + + function getConfigurationOptions() { + return array( + 'size' => new TextboxField(array( + 'id'=>1, 'label'=>'Size', 'required'=>false, 'default'=>16, + 'validator' => 'number')), + 'length' => new TextboxField(array( + 'id'=>2, 'label'=>'Max Length', 'required'=>false, 'default'=>30, + 'validator' => 'number')), + 'validator' => new ChoiceField(array( + 'id'=>3, 'label'=>'Validator', 'required'=>false, 'default'=>'', + 'choices' => array('phone'=>'Phone Number','email'=>'Email Address', + 'ip'=>'IP Address', 'number'=>'Number', ''=>'None'))), + 'validator-error' => new TextboxField(array( + 'id'=>4, 'label'=>'Validation Error', 'default'=>'', + 'configuration'=>array('size'=>40), + 'hint'=>'Message shown to user if the input does not match the validator')), + ); + } + + function validateEntry($value) { + parent::validateEntry($value); + $validators = array( + '' => null, + 'email' => array(array('Validator', 'is_email'), + 'Enter a valid email address'), + 'phone' => array(array('Validator', 'is_phone'), + 'Enter a valid phone number'), + 'ip' => array(array('Validator', 'is_ip'), + 'Enter a valid IP address'), + 'number' => array('is_numeric', 'Enter a number') + ); + // Support configuration forms, as well as GUI-based form fields + $valid = $this->get('validator'); + if (!$valid) { + $config = $this->getConfiguration(); + $valid = $config['validator']; + } + if (!$value || !isset($validators[$valid])) + return; + $func = $validators[$valid]; + if (is_array($func) && is_callable($func[0])) + if (!call_user_func($func[0], $value)) + $this->_errors[] = $func[1]; + } +} + +class TextareaField extends FormField { + static $widget = 'TextareaWidget'; + + function getConfigurationOptions() { + return array( + 'cols' => new TextboxField(array( + 'id'=>1, 'label'=>'Width (chars)', 'required'=>true, 'default'=>40)), + 'rows' => new TextboxField(array( + 'id'=>2, 'label'=>'Height (rows)', 'required'=>false, 'default'=>4)), + 'length' => new TextboxField(array( + 'id'=>3, 'label'=>'Max Length', 'required'=>false, 'default'=>30)), + 'html' => new BooleanField(array( + 'id'=>4, 'label'=>'HTML', 'required'=>false, 'default'=>true, + 'configuration'=>array('desc'=>'Allow HTML input in this box'))), + ); + } +} + +class PhoneField extends FormField { + static $widget = 'PhoneNumberWidget'; + + function validateEntry($value) { + parent::validateEntry($value); + # Run validator against $this->value for email type + list($phone, $ext) = explode("X", $value, 2); + if ($phone && !Validator::is_phone($phone)) + $this->_errors[] = "Enter a valid phone number"; + if ($ext) { + if (!is_numeric($ext)) + $this->_errors[] = "Enter a valide phone extension"; + elseif (!$phone) + $this->_errors[] = "Enter a phone number for the extension"; + } + } + + function toString($value) { + list($phone, $ext) = explode("X", $value, 2); + $phone=Format::phone($phone); + if ($ext) + $phone.=" x$ext"; + return $phone; + } +} + +class BooleanField extends FormField { + static $widget = 'CheckboxWidget'; + + function getConfigurationOptions() { + return array( + 'desc' => new TextareaField(array( + 'id'=>1, 'label'=>'Description', 'required'=>false, 'default'=>'', + 'hint'=>'Text shown inline with the widget', + 'configuration'=>array('rows'=>2))) + ); + } + + function to_database($value) { + return ($value) ? '1' : '0'; + } + + function parse($value) { + return $this->to_php($value); + } + function to_php($value) { + return $value ? true : false; + } + + function toString($value) { + return ($value) ? 'Yes' : 'No'; + } +} + +class ChoiceField extends FormField { + static $widget = 'ChoicesWidget'; + + function getConfigurationOptions() { + return array( + 'choices' => new TextareaField(array( + 'id'=>1, 'label'=>'Choices', 'required'=>false, 'default'=>'')), + ); + } + + function toString($value) { + $choices = $this->getChoices(); + if (isset($choices[$value])) + return $choices[$value]; + else + return $choices[$this->get('default')]; + } + + function getChoices() { + if ($this->_choices === null) { + // Allow choices to be set in this->ht (for configurationOptions) + $this->_choices = $this->get('choices'); + if (!$this->_choices) { + $this->_choices = array(); + $config = $this->getConfiguration(); + $choices = explode("\n", $config['choices']); + foreach ($choices as $choice) { + // Allow choices to be key: value + list($key, $val) = explode(':', $choice); + if ($val == null) + $val = $key; + $this->_choices[trim($key)] = trim($val); + } + } + } + return $this->_choices; + } +} + +class DatetimeField extends FormField { + static $widget = 'DatetimePickerWidget'; + + function to_database($value) { + // Store time in gmt time, unix epoch format + return (string) $value; + } + + function to_php($value) { + if (!$value) + return $value; + else + return (int) $value; + } + + function parse($value) { + if (!$value) return null; + $config = $this->getConfiguration(); + return ($config['gmt']) ? Misc::db2gmtime($value) : strtotime($value); + } + + function toString($value) { + global $cfg; + $config = $this->getConfiguration(); + $format = ($config['time']) + ? $cfg->getDateTimeFormat() : $cfg->getDateFormat(); + if ($config['gmt']) + // Return time local to user's timezone + return Format::userdate($format, $value); + else + return Format::date($format, $value); + } + + function getConfigurationOptions() { + return array( + 'time' => new BooleanField(array( + 'id'=>1, 'label'=>'Time', 'required'=>false, 'default'=>false, + 'configuration'=>array( + 'desc'=>'Show time selection with date picker'))), + 'gmt' => new BooleanField(array( + 'id'=>2, 'label'=>'Timezone Aware', 'required'=>false, + 'configuration'=>array( + 'desc'=>"Show date/time relative to user's timezone"))), + 'min' => new DatetimeField(array( + 'id'=>3, 'label'=>'Earliest', 'required'=>false, + 'hint'=>'Earliest date selectable')), + 'max' => new DatetimeField(array( + 'id'=>4, 'label'=>'Latest', 'required'=>false, + 'default'=>null)), + 'future' => new BooleanField(array( + 'id'=>5, 'label'=>'Allow Future Dates', 'required'=>false, + 'default'=>true, 'configuration'=>array( + 'desc'=>'Allow entries into the future'))), + ); + } + + function validateEntry($value) { + $config = $this->getConfiguration(); + parent::validateEntry($value); + if (!$value) return; + if ($config['min'] and $value < $config['min']) + $this->_errors[] = 'Selected date is earlier than permitted'; + elseif ($config['max'] and $value > $config['max']) + $this->_errors[] = 'Selected date is later than permitted'; + // strtotime returns -1 on error for PHP < 5.1.0 and false thereafter + elseif ($value === -1 or $value === false) + $this->_errors[] = 'Enter a valid date'; + } +} + +/** + * This is kind-of a special field that doesn't have any data. It's used as + * a field to provide a horizontal section break in the display of a form + */ +class SectionBreakField extends FormField { + static $widget = 'SectionBreakWidget'; + + function hasData() { + return false; + } + + function isBlockLevel() { + return true; + } +} + +class ThreadEntryField extends FormField { + static $widget = 'ThreadEntryWidget'; + + function isChangeable() { + return false; + } + function isBlockLevel() { + return true; + } + function isPresentationOnly() { + return true; + } + function renderExtras($mode=null) { + if ($mode == 'client') + $this->getWidget()->showAttachments(); + } +} + +class PriorityField extends ChoiceField { + function getWidget() { + $widget = parent::getWidget(); + if ($widget->value instanceof Priority) + $widget->value = $widget->value->getId(); + return $widget; + } + + function getChoices() { + $this->ht['default'] = 0; + + $sql = 'SELECT priority_id, priority_desc FROM '.PRIORITY_TABLE + .' ORDER BY priority_urgency DESC'; + $choices = array(0 => '— Default —'); + if (!($res = db_query($sql))) + return $choices; + + while ($row = db_fetch_row($res)) + $choices[$row[0]] = $row[1]; + return $choices; + } + + function parse($id) { + return $this->to_php(null, $id); + } + + function to_php($value, $id) { + return Priority::lookup($id); + } + + function to_database($prio) { + return ($prio instanceof Priority) + ? array($prio->getDesc(), $prio->getId()) + : $prio; + } + + function toString($value) { + return ($value instanceof Priority) ? $value->getDesc() : $value; + } + + function getConfigurationOptions() { + return array(); + } +} +FormField::addFieldTypes('Built-in Lists', function() { + return array( + 'priority' => array('Priority Level', PriorityField), + ); +}); + +class Widget { + + function __construct($field) { + $this->field = $field; + $this->name = $field->getFormName(); + } + + function parseValue() { + $this->value = $this->getValue(); + if (!isset($this->value) && is_object($this->field->getAnswer())) + $this->value = $this->field->getAnswer()->getValue(); + if (!isset($this->value) && $this->field->value) + $this->value = $this->field->value; + } + + function getValue() { + $data = $this->field->getSource(); + if (!isset($data[$this->name])) + return null; + return $data[$this->name]; + } +} + +class TextboxWidget extends Widget { + function render() { + $config = $this->field->getConfiguration(); + if (isset($config['size'])) + $size = "size=\"{$config['size']}\""; + if (isset($config['length'])) + $maxlength = "maxlength=\"{$config['length']}\""; + if (isset($config['classes'])) + $classes = 'class="'.$config['classes'].'"'; + if (isset($config['autocomplete'])) + $autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"'; + ?> + <span style="display:inline-block"> + <input type="text" id="<?php echo $this->name; ?>" + <?php echo $size . " " . $maxlength; ?> + <?php echo $classes.' '.$autocomplete; ?> + name="<?php echo $this->name; ?>" + value="<?php echo Format::htmlchars($this->value); ?>"/> + </span> + <?php + } +} + +class TextareaWidget extends Widget { + function render() { + $config = $this->field->getConfiguration(); + $class = ""; + if (isset($config['rows'])) + $rows = "rows=\"{$config['rows']}\""; + if (isset($config['cols'])) + $cols = "cols=\"{$config['cols']}\""; + if (isset($config['length'])) + $maxlength = "maxlength=\"{$config['length']}\""; + if (isset($config['html']) && $config['html']) + $class = 'class="richtext no-bar small"'; + ?> + <span style="display:inline-block;width:100%"> + <textarea <?php echo $rows." ".$cols." ".$length." ".$class; ?> + name="<?php echo $this->name; ?>"><?php + echo Format::htmlchars($this->value); + ?></textarea> + </span> + <?php + } +} + +class PhoneNumberWidget extends Widget { + function render() { + list($phone, $ext) = explode("X", $this->value); + ?> + <input type="text" name="<?php echo $this->name; ?>" value="<?php + echo $phone; ?>"/> Ext: <input type="text" name="<?php + echo $this->name; ?>-ext" value="<?php echo $ext; ?>" size="5"/> + <?php + } + + function getValue() { + $data = $this->field->getSource(); + $base = parent::getValue(); + if ($base === null) + return $base; + $ext = $data["{$this->name}-ext"]; + if ($ext) $ext = 'X'.$ext; + return $base . $ext; + } +} + +class ChoicesWidget extends Widget { + function render() { + $config = $this->field->getConfiguration(); + // Determine the value for the default (the one listed if nothing is + // selected) + $def_key = $this->field->get('default'); + $choices = $this->field->getChoices(); + $have_def = isset($choices[$def_key]); + if (!$have_def) + $def_val = 'Select '.$this->field->get('label'); + else + $def_val = $choices[$def_key]; + ?> <span style="display:inline-block"> + <select name="<?php echo $this->name; ?>"> + <?php if (!$have_def) { ?> + <option value="<?php echo $def_key; ?>">— <?php + echo $def_val; ?> —</option> + <?php } + foreach ($choices as $key=>$name) { + if (!$have_def && $key == $def_key) + continue; ?> + <option value="<?php echo $key; ?>" <?php + if ($this->value == $key) echo 'selected="selected"'; + ?>><?php echo $name; ?></option> + <?php } ?> + </select> + </span> + <?php + } +} + +class CheckboxWidget extends Widget { + function __construct($field) { + parent::__construct($field); + $this->name = '_field-checkboxes'; + } + + function render() { + $config = $this->field->getConfiguration(); + ?> + <input type="checkbox" name="<?php echo $this->name; ?>[]" <?php + if ($this->value) echo 'checked="checked"'; ?> value="<?php + echo $this->field->get('id'); ?>"/> + <?php + if ($config['desc']) { ?> + <em style="display:inline-block"><?php + echo Format::htmlchars($config['desc']); ?></em> + <?php } + } + + function getValue() { + $data = $this->field->getSource(); + if (count($data)) + return @in_array($this->field->get('id'), $data[$this->name]); + return parent::getValue(); + } +} + +class DatetimePickerWidget extends Widget { + function render() { + $config = $this->field->getConfiguration(); + if ($this->value) { + $this->value = (is_int($this->value) ? $this->value : + strtotime($this->value)); + if ($config['gmt']) + $this->value += 3600 * + $_SESSION['TZ_OFFSET']+($_SESSION['TZ_DST']?date('I',$time):0); + + list($hr, $min) = explode(':', date('H:i', $this->value)); + $this->value = date('m/d/Y', $this->value); + } + ?> + <input type="text" name="<?php echo $this->name; ?>" + value="<?php echo Format::htmlchars($this->value); ?>" size="12" + autocomplete="off" /> + <script type="text/javascript"> + $(function() { + $('input[name="<?php echo $this->name; ?>"]').datepicker({ + <?php + if ($config['min']) + echo "minDate: new Date({$config['min']}000),"; + if ($config['max']) + echo "maxDate: new Date({$config['max']}000),"; + elseif (!$config['future']) + echo "maxDate: new Date().getTime(),"; + ?> + numberOfMonths: 2, + showButtonPanel: true, + buttonImage: './images/cal.png', + showOn:'both' + }); + }); + </script> + <?php + if ($config['time']) + // TODO: Add time picker -- requires time picker or selection with + // Misc::timeDropdown + echo ' ' . Misc::timeDropdown($hr, $min, $this->name . ':time'); + } + + /** + * Function: getValue + * Combines the datepicker date value and the time dropdown selected + * time value into a single date and time string value. + */ + function getValue() { + $data = $this->field->getSource(); + $datetime = parent::getValue(); + if ($datetime && isset($data[$this->name . ':time'])) + $datetime .= ' ' . $data[$this->name . ':time']; + return $datetime; + } +} + +class SectionBreakWidget extends Widget { + function render() { + ?><div class="form-header section-break"><h3><?php + echo Format::htmlchars($this->field->get('label')); + ?></h3><em><?php echo Format::htmlchars($this->field->get('hint')); + ?></em></div> + <?php + } +} + +class ThreadEntryWidget extends Widget { + function render($client=null) { + global $cfg; + + ?><div style="margin-bottom:0.5em;margin-top:0.5em"><strong><?php + echo Format::htmlchars($this->field->get('label')); + ?></strong>:</div> + <textarea name="<?php echo $this->field->get('name'); ?>" + placeholder="<?php echo Format::htmlchars($this->field->get('hint')); ?>" + <?php if (!$client) { ?> + data-draft-namespace="ticket.staff" + <?php } else { ?> + data-draft-namespace="ticket.client" + data-draft-object-id="<?php echo substr(session_id(), -12); ?>" + <?php } ?> + class="richtext draft draft-delete ifhtml" + cols="21" rows="8" style="width:80%;"><?php echo + $this->value; ?></textarea> + <?php + } + + function showAttachments() { + global $cfg, $thisclient; + + if(($cfg->allowOnlineAttachments() + && !$cfg->allowAttachmentsOnlogin()) + || ($cfg->allowAttachmentsOnlogin() + && ($thisclient && $thisclient->isValid()))) { ?> + <hr/> + <div><strong>Attachments:</strong></div> + <div class="uploads"></div><br> + <input type="file" class="multifile" name="attachments[]" id="attachments" size="30" value="" /> + <font class="error"> <?php echo $errors['attachments']; ?></font> + <?php + } + } +} + +?> diff --git a/include/class.i18n.php b/include/class.i18n.php index 0f8bc844301d1dcc42db87f1e92b98d51e89a622..7891d7ef937ecacf2bc148ae4a4e441801a8e003 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -41,17 +41,20 @@ class Internationalization { * system language and reload the data. */ function loadDefaultData() { + # notrans -- do not translate the contents of this array $models = array( - 'email_template_group.yaml' => 'EmailTemplateGroup', # notrans - 'department.yaml' => 'Dept', # notrans - 'sla.yaml' => 'SLA', # notrans - // Note that department and sla are required for help_topic - 'help_topic.yaml' => 'Topic', # notrans - 'filter.yaml' => 'Filter', # notrans - 'team.yaml' => 'Team', # notrans + 'email_template_group.yaml' => 'EmailTemplateGroup', + 'department.yaml' => 'Dept', + 'sla.yaml' => 'SLA', + 'form.yaml' => 'DynamicForm', + // Note that department, sla, and forms are required for + // help_topic + 'help_topic.yaml' => 'Topic', + 'filter.yaml' => 'Filter', + 'team.yaml' => 'Team', // Note that group requires department - 'group.yaml' => 'Group', # notrans - 'file.yaml' => 'AttachmentFile', # notrans + 'group.yaml' => 'Group', + 'file.yaml' => 'AttachmentFile', ); $errors = array(); diff --git a/include/class.json.php b/include/class.json.php index b5a589cfac122e008acc207f3bd2942931f31fe7..f3dfd282cdf565522c45b4f7f173da45e7e40bfa 100644 --- a/include/class.json.php +++ b/include/class.json.php @@ -22,10 +22,12 @@ include_once "JSON.php"; class JsonDataParser { function parse($stream) { - $contents = ''; - while (!feof($stream)) { - $contents .= fread($stream, 8192); - } + if (is_resource($stream)) { + $contents = ''; + while (!feof($stream)) + $contents .= fread($stream, 8192); + } else + $contents = $stream; return self::decode($contents); } diff --git a/include/class.nav.php b/include/class.nav.php index a92a5629ec149579cd3d7883be2aa8ff2ef661fc..89f47ed75e1cd46ef75a95016e5ab019cea024a5 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -207,7 +207,9 @@ class AdminNav extends StaffNav{ 'title'=>'Ticket Filters','iconclass'=>'ticketFilters'); $subnav[]=array('desc'=>'SLA Plans','href'=>'slas.php','iconclass'=>'sla'); $subnav[]=array('desc'=>'API Keys','href'=>'apikeys.php','iconclass'=>'api'); - $subnav[]=array('desc'=>'Site Pages', 'href'=>'pages.php','title'=>'Pages','iconclass'=>'pages'); + $subnav[]=array('desc'=>'Pages', 'href'=>'pages.php','title'=>'Pages','iconclass'=>'pages'); + $subnav[]=array('desc'=>'Forms','href'=>'forms.php','iconclass'=>'forms'); + $subnav[]=array('desc'=>'Lists','href'=>'lists.php','iconclass'=>'lists'); break; case 'emails': $subnav[]=array('desc'=>'Emails','href'=>'emails.php', 'title'=>'Email Addresses', 'iconclass'=>'emailSettings'); diff --git a/include/class.orm.php b/include/class.orm.php new file mode 100644 index 0000000000000000000000000000000000000000..8aa8e5d39fe0e57ee715d334411ddfb49790d47b --- /dev/null +++ b/include/class.orm.php @@ -0,0 +1,979 @@ +<?php +/********************************************************************* + class.orm.php + + Simple ORM (Object Relational Mapper) for PHP5 based on Django's ORM, + except that complex filter operations are not supported. The ORM simply + supports ANDed filter operations without any GROUP BY support. + + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ + +class VerySimpleModel { + static $meta = array( + 'table' => false, + 'ordering' => false, + 'pk' => false + ); + + var $ht; + var $dirty; + var $__new__ = false; + + function __construct($row) { + $this->ht = $row; + $this->__setupForeignLists(); + $this->dirty = array(); + } + + function get($field) { + return $this->ht[$field]; + } + function __get($field) { + if (array_key_exists($field, $this->ht)) + return $this->ht[$field]; + elseif (isset(static::$meta['joins'][$field])) { + // TODO: Support instrumented lists and such + $j = static::$meta['joins'][$field]; + $class = $j['fkey'][0]; + $v = $this->ht[$field] = $class::lookup($this->ht[$j['local']]); + return $v; + } + } + + function set($field, $value) { + // Update of foreign-key by assignment to model instance + if (isset(static::$meta['joins'][$field])) { + $j = static::$meta['joins'][$field]; + // XXX: Ensure $value instanceof $j['fkey'][0] + if ($value->__new__) + $value->save(); + // Capture the object under the object's field name + $this->ht[$field] = $value; + // Capture the foreign key id value + $field = $j['local']; + $value = $value->{$j['fkey'][1]}; + // Fall through to the standard logic below + } + // XXX: Fully support or die if updating pk + // XXX: The contents of $this->dirty should be the value after the + // previous fetch or save. For instance, if the value is changed more + // than once, the original value should be preserved in the dirty list + // on the second edit. + $old = isset($this->ht[$field]) ? $this->ht[$field] : null; + if ($old != $value) { + $this->dirty[$field] = $old; + $this->ht[$field] = $value; + } + } + function __set($field, $value) { + return $this->set($field, $value); + } + + function setAll($props) { + foreach ($props as $field=>$value) + $this->set($field, $value); + } + + function __setupForeignLists() { + // Construct related lists + if (isset(static::$meta['joins'])) { + foreach (static::$meta['joins'] as $name => $j) { + if (isset($j['list']) && $j['list']) { + $fkey = $j['fkey']; + $this->{$name} = new InstrumentedList( + // Send Model, Foriegn-Field, Local-Id + array($fkey[0], $fkey[1], $this->{$j['local']}) + ); + } + } + } + } + + static function _inspect() { + if (!static::$meta['table']) + throw new OrmConfigurationError( + 'Model does not define meta.table', get_called_class()); + + // Break down foreign-key metadata + foreach (static::$meta['joins'] as $field => &$j) { + if (isset($j['reverse'])) { + list($model, $key) = explode('.', $j['reverse']); + $info = $model::$meta['joins'][$key]; + $constraint = array(); + foreach ($info['constraint'] as $foreign => $local) { + list(,$field) = explode('.', $local); + $constraint[$field] = "$model.$foreign"; + } + $j['constraint'] = $constraint; + $j['list'] = true; + } + // XXX: Make this better (ie. composite keys) + $keys = array_keys($j['constraint']); + $foreign = $j['constraint'][$keys[0]]; + $j['fkey'] = explode('.', $foreign); + $j['local'] = $keys[0]; + } + } + + static function objects() { + return new QuerySet(get_called_class()); + } + + static function lookup($criteria) { + if (!is_array($criteria)) + // Model::lookup(1), where >1< is the pk value + $criteria = array(static::$meta['pk'][0] => $criteria); + $list = static::objects()->filter($criteria)->limit(1); + // TODO: Throw error if more than one result from database + return $list[0]; + } + + function delete($pk=false) { + $table = static::$meta['table']; + $sql = 'DELETE FROM '.$table; + + if (!$pk) $pk = static::$meta['pk']; + if (!is_array($pk)) $pk=array($pk); + + foreach ($pk as $p) + $filter[] = $p.' = '.db_input($this->get($p)); + $sql .= ' WHERE '.implode(' AND ', $filter).' LIMIT 1'; + return db_affected_rows(db_query($sql)) == 1; + } + + function save($refetch=false) { + $pk = static::$meta['pk']; + if (!is_array($pk)) $pk=array($pk); + if ($this->__new__) + $sql = 'INSERT INTO '.static::$meta['table']; + else + $sql = 'UPDATE '.static::$meta['table']; + $filter = $fields = array(); + if (count($this->dirty) === 0) + return; + foreach ($this->dirty as $field=>$old) + if ($this->__new__ or !in_array($field, $pk)) + if (@get_class($this->get($field)) == 'SqlFunction') + $fields[] = $field.' = '.$this->get($field)->toSql(); + else + $fields[] = $field.' = '.db_input($this->get($field)); + foreach ($pk as $p) + $filter[] = $p.' = '.db_input($this->get($p)); + $sql .= ' SET '.implode(', ', $fields); + if (!$this->__new__) { + $sql .= ' WHERE '.implode(' AND ', $filter); + $sql .= ' LIMIT 1'; + } + if (db_affected_rows(db_query($sql)) != 1) { + throw new Exception(db_error()); + return false; + } + if ($this->__new__) { + if (count($pk) == 1) + $this->ht[$pk[0]] = db_insert_id(); + $this->__new__ = false; + // Setup lists again + $this->__setupForeignLists(); + } + # Refetch row from database + # XXX: Too much voodoo + if ($refetch) { + # XXX: Support composite PK + $criteria = array($pk[0] => $this->get($pk[0])); + $self = static::lookup($criteria); + $this->ht = $self->ht; + } + $this->dirty = array(); + return $this->get($pk[0]); + } + + static function create($ht=false) { + if (!$ht) $ht=array(); + $class = get_called_class(); + $i = new $class(array()); + $i->__new__ = true; + foreach ($ht as $field=>$value) + if (!is_array($value)) + $i->set($field, $value); + return $i; + } + + /** + * isValid + * + * Validates the contents of $this->ht before the model should be + * committed to the database. This is the validation for the field + * template -- edited in the admin panel for a form section. + */ + function isValid() { + return true; + } +} + +class SqlFunction { + function SqlFunction($name) { + $this->func = $name; + $this->args = array_slice(func_get_args(), 1); + } + + function toSql() { + $args = (count($this->args)) ? implode(',', db_input($this->args)) : ""; + return sprintf('%s(%s)', $this->func, $args); + } +} + +class QuerySet implements IteratorAggregate, ArrayAccess { + var $model; + + var $constraints = array(); + var $exclusions = array(); + var $ordering = array(); + var $limit = false; + var $offset = 0; + var $related = array(); + var $values = array(); + + var $compiler = 'MySqlCompiler'; + var $iterator = 'ModelInstanceIterator'; + + var $params; + var $query; + + function __construct($model) { + $this->model = $model; + } + + function filter() { + // Multiple arrays passes means OR + $this->constraints[] = func_get_args(); + return $this; + } + + function exclude() { + $this->exclusions[] = func_get_args(); + return $this; + } + + function order_by() { + $this->ordering = array_merge($this->ordering, func_get_args()); + return $this; + } + + function limit($count) { + $this->limit = $count; + return $this; + } + + function offset($at) { + $this->offset = $at; + return $this; + } + + function select_related() { + $this->related = array_merge($this->related, func_get_args()); + return $this; + } + + function values() { + $this->values = func_get_args(); + $this->iterator = 'HashArrayIterator'; + return $this; + } + + function values_flat() { + $this->values = func_get_args(); + $this->iterator = 'FlatArrayIterator'; + return $this; + } + + function all() { + return $this->getIterator()->asArray(); + } + + function count() { + $compiler = new $this->compiler(); + return $compiler->compileCount($this); + } + + function exists() { + return $this->count() > 0; + } + + // IteratorAggregate interface + function getIterator() { + if (!isset($this->_iterator)) + $this->_iterator = new $this->iterator($this); + return $this->_iterator; + } + + // ArrayAccess interface + function offsetExists($offset) { + return $this->getIterator()->offsetExists($offset); + } + function offsetGet($offset) { + return $this->getIterator()->offsetGet($offset); + } + function offsetUnset($a) { + throw new Exception('QuerySet is read-only'); + } + function offsetSet($a, $b) { + throw new Exception('QuerySet is read-only'); + } + + function __toString() { + return (string)$this->getQuery(); + } + + function getQuery($options=array()) { + if (isset($this->query)) + return $this->query; + + // Load defaults from model + $model = $this->model; + if (!$this->ordering && isset($model::$meta['ordering'])) + $this->ordering = $model::$meta['ordering']; + + $compiler = new $this->compiler($options); + $this->query = $compiler->compileSelect($this); + + return $this->query; + } +} + +class ModelInstanceIterator implements Iterator, ArrayAccess { + var $model; + var $resource; + var $cache = array(); + var $position = 0; + var $queryset; + + function __construct($queryset=false) { + if ($queryset) { + $this->model = $queryset->model; + $this->resource = $queryset->getQuery(); + } + } + + function buildModel($row) { + // TODO: Traverse to foreign keys + return new $this->model($row); + } + + function fillTo($index) { + while ($this->resource && $index >= count($this->cache)) { + if ($row = $this->resource->getArray()) { + $this->cache[] = $this->buildModel($row); + } else { + $this->resource->close(); + $this->resource = null; + break; + } + } + } + + function asArray() { + $this->fillTo(PHP_INT_MAX); + return $this->cache; + } + + // Iterator interface + function rewind() { + $this->position = 0; + } + function current() { + $this->fillTo($this->position); + return $this->cache[$this->position]; + } + function key() { + return $this->position; + } + function next() { + $this->position++; + } + function valid() { + $this->fillTo($this->position); + return count($this->cache) > $this->position; + } + + // ArrayAccess interface + function offsetExists($offset) { + $this->fillTo($offset); + return $this->position >= $offset; + } + function offsetGet($offset) { + $this->fillTo($offset); + return $this->cache[$offset]; + } + function offsetUnset($a) { + throw new Exception(sprintf('%s is read-only', get_class($this))); + } + function offsetSet($a, $b) { + throw new Exception(sprintf('%s is read-only', get_class($this))); + } +} + +class FlatArrayIterator extends ModelInstanceIterator { + function __construct($queryset) { + $this->resource = $queryset->getQuery(); + } + function fillTo($index) { + while ($this->resource && $index >= count($this->cache)) { + if ($row = $this->resource->getRow()) { + $this->cache += $row; + } else { + $this->resource->close(); + $this->resource = null; + break; + } + } + } +} + +class InstrumentedList extends ModelInstanceIterator { + var $key; + var $id; + + function __construct($fkey, $queryset=false) { + list($model, $this->key, $this->id) = $fkey; + if (!$queryset) + $queryset = $model::objects()->filter(array($this->key=>$this->id)); + parent::__construct($queryset); + if (!$this->id) + $this->resource = null; + } + + function add($object) { + $object->{$this->key} = $this->id; + $object->save(); + $this->list[] = $object; + } + function remove($object) { + $object->delete(); + } + + function offsetUnset($a) { + $this->fillTo($a); + $this->cache[$a]->delete(); + } + function offsetSet($a, $b) { + $this->fillTo($a); + $this->cache[$a]->delete(); + $this->add($b); + } +} + +class SqlCompiler { + var $options = array(); + var $params = array(); + var $joins = array(); + var $aliases = array(); + var $alias_num = 1; + + static $operators = array( + 'exact' => '%$1s = %$2s' + ); + + function __construct($options=false) { + if ($options) + $this->options = array_merge($this->options, $options); + } + + /** + * Handles breaking down a field or model search descriptor into the + * model search path, field, and operator parts. When used in a queryset + * filter, an expression such as + * + * user__email__hostname__contains => 'foobar' + * + * would be broken down to search from the root model (passed in, + * perhaps a ticket) to the user and email models by inspecting the + * model metadata 'joins' property. The 'constraint' value found there + * will be used to build the JOIN sql clauses. + * + * The 'hostname' will be the field on 'email' model that should be + * compared in the WHERE clause. The comparison should be made using a + * 'contains' function, which in MySQL, might be implemented using + * something like "<lhs> LIKE '%foobar%'" + * + * This function will rely heavily on the pushJoin() function which will + * handle keeping track of joins made previously in the query and + * therefore prevent multiple joins to the same table for the same + * reason. (Self joins are still supported). + * + * Comparison functions supported by this function are defined for each + * respective SqlCompiler subclass; however at least these functions + * should be defined: + * + * function a__function => b + * ----------+------------------------------------------------ + * exact | a is exactly equal to b + * gt | a is greater than b + * lte | b is greater than a + * lt | a is less than b + * gte | b is less than a + * ----------+------------------------------------------------ + * contains | (string) b is contained within a + * statswith | (string) first len(b) chars of a are exactly b + * endswith | (string) last len(b) chars of a are exactly b + * like | (string) a matches pattern b + * ----------+------------------------------------------------ + * in | a is in the list or the nested queryset b + * ----------+------------------------------------------------ + * isnull | a is null (if b) else a is not null + * + * If no comparison function is declared in the field descriptor, + * 'exact' is assumed. + */ + function getField($field, $model, $options=array()) { + $joins = array(); + + // Break apart the field descriptor by __ (double-underbars). The + // first part is assumed to be the root field in the given model. + // The parts after each of the __ pieces are links to other tables. + // The last item (after the last __) is allowed to be an operator + // specifiction. + $parts = explode('__', $field); + $operator = static::$operators['exact']; + if (!isset($options['table'])) { + $field = array_pop($parts); + if (array_key_exists($field, static::$operators)) { + $operator = static::$operators[$field]; + $field = array_pop($parts); + } + } + + $path = array(); + $crumb = ''; + $alias = $this->quote($model::$meta['table']); + + // Traverse through the parts and establish joins between the tables + // if the field is joined to a foreign model + if (count($parts) && isset($model::$meta['joins'][$parts[0]])) { + // Call pushJoin for each segment in the join path. A new + // JOIN fragment will need to be emitted and/or cached + foreach ($parts as $p) { + $path[] = $p; + $tip = implode('__', $path); + $info = $model::$meta['joins'][$p]; + $alias = $this->pushJoin($crumb, $tip, $model, $info); + // Roll to foreign model + foreach ($info['constraint'] as $local => $foreign) { + list($model, $f) = explode('.', $foreign); + if (class_exists($model)) + break; + } + $crumb = $tip; + } + } + if (isset($options['table']) && $options['table']) + $field = $alias; + elseif ($alias) + $field = $alias.'.'.$this->quote($field); + else + $field = $this->quote($field); + return array($field, $operator); + } + + /** + * Uses the compiler-specific `compileJoin` function to compile the join + * statement fragment, and caches the result in the local $joins list. A + * new alias is acquired using the `nextAlias` function which will be + * associated with the join. If the same path is requested again, the + * algorithm is short-circuited and the originally-assigned table alias + * is returned immediately. + */ + function pushJoin($tip, $path, $model, $info) { + // TODO: Build the join statement fragment and return the table + // alias. The table alias will be useful where the join is used in + // the WHERE and ORDER BY clauses + + // If the join already exists for the statement-being-compiled, just + // return the alias being used. + if (isset($this->joins[$path])) + return $this->joins[$path]['alias']; + + // TODO: Support only using aliases if necessary. Use actual table + // names for everything except oddities like self-joins + + $alias = $this->nextAlias(); + // Keep an association between the table alias and the model. This + // will make model construction much easier when we have the data + // and the table alias from the database. + $this->aliases[$alias] = $model; + + // TODO: Stash joins and join constraints into local ->joins array. + // This will be useful metadata in the executor to construct the + // final models for fetching + // TODO: Always use a table alias. This will further help with + // coordination between the data returned from the database (where + // table alias is available) and the corresponding data. + $this->joins[$path] = array( + 'alias' => $alias, + 'sql'=> $this->compileJoin($tip, $model, $alias, $info), + ); + return $alias; + } + + function compileWhere($where, $model) { + $constrints = array(); + foreach ($where as $constraint) { + $filter = array(); + foreach ($constraint as $field=>$value) { + list($field, $op) = $this->getField($field, $model); + // Allow operators to be callable rather than sprintf + // strings + if ($value === null) + $filter[] = sprintf('%s IS NULL', $field); + elseif (is_callable($op)) + $filter[] = call_user_func($op, $field, $value); + else + $filter[] = sprintf($op, $field, $this->input($value)); + } + // Multiple constraints here are ANDed together + $constraints[] = implode(' AND ', $filter); + } + // Multiple constrains here are ORed together + $filter = implode(' OR ', $constraints); + if (count($constraints) > 1) + $filter = '(' . $filter . ')'; + return $filter; + } + + function getParams() { + return $this->params; + } + + function getJoins() { + $sql = ''; + foreach ($this->joins as $j) + $sql .= $j['sql']; + return $sql; + } + + function nextAlias() { + // Use alias A1-A9,B1-B9,... + $alias = chr(65 + (int)($this->alias_num / 9)) . $this->alias_num % 9; + $this->alias_num++; + return $alias; + } +} + +class DbEngine { + + function __construct($info) { + } + + function connect() { + } + + // Gets a compiler compatible with this database engine that can compile + // and execute a queryset or DML request. + function getCompiler() { + } +} + +class MySqlCompiler extends SqlCompiler { + + static $operators = array( + 'exact' => '%1$s = %2$s', + 'contains' => array('self', '__contains'), + 'gt' => '%1$s > %2$s', + 'lt' => '%1$s < %2$s', + 'isnull' => '%1$s IS NULL', + 'like' => '%1$s LIKE %2$s', + 'in' => array('self', '__in'), + ); + + function __contains($a, $b) { + # {%a} like %{$b}% + return sprintf('%s LIKE %s', $a, $this->input("%$b%")); + } + + function __in($a, $b) { + if (is_array($b)) { + $vals = array_map(array($this, 'input'), $b); + $b = implode(', ', $vals); + } + else { + $b = $this->input($b); + } + return sprintf('%s IN (%s)', $a, $b); + } + + function compileJoin($tip, $model, $alias, $info) { + $constraints = array(); + $join = ' JOIN '; + if (isset($info['null']) && $info['null']) + $join = ' LEFT'.$join; + if (isset($this->joins[$tip])) + $table = $this->joins[$tip]['alias']; + else + $table = $this->quote($model::$meta['table']); + foreach ($info['constraint'] as $local => $foreign) { + list($rmodel, $right) = explode('.', $foreign); + // TODO: Support a constant constraint + $constraints[] = sprintf("%s.%s = %s.%s", + $table, $this->quote($local), $alias, + $this->quote($right) + ); + } + return $join.$this->quote($rmodel::$meta['table']) + .' '.$alias.' ON ('.implode(' AND ', $constraints).')'; + } + + function input($what) { + if ($what instanceof QuerySet) { + $q = $what->getQuery(array('nosort'=>true)); + $this->params += $q->params; + return (string)$q; + } + else { + $this->params[] = $what; + return '?'; + } + } + + function quote($what) { + return "`$what`"; + } + + function compileCount($queryset) { + $model = $queryset->model; + $table = $model::$meta['table']; + $where_pos = array(); + $where_neg = array(); + $joins = array(); + foreach ($queryset->constraints as $where) { + $where_pos[] = $this->compileWhere($where, $model); + } + foreach ($queryset->exclusions as $where) { + $where_neg[] = $this->compileWhere($where, $model); + } + + $where = ''; + if ($where_pos || $where_neg) { + $where = ' WHERE '.implode(' AND ', $where_pos) + .implode(' AND NOT ', $where_neg); + } + $joins = $this->getJoins(); + $sql = 'SELECT COUNT(*) AS count FROM '.$this->quote($table).$joins.$where; + $exec = new MysqlExecutor($sql, $this->params); + $row = $exec->getArray(); + return $row['count']; + } + + function compileSelect($queryset) { + $model = $queryset->model; + $where_pos = array(); + $where_neg = array(); + $joins = array(); + foreach ($queryset->constraints as $where) { + $where_pos[] = $this->compileWhere($where, $model); + } + foreach ($queryset->exclusions as $where) { + $where_neg[] = $this->compileWhere($where, $model); + } + + $where = ''; + if ($where_pos || $where_neg) { + $where = ' WHERE '.implode(' AND ', $where_pos) + .implode(' AND NOT ', $where_neg); + } + + $sort = ''; + if ($queryset->ordering && !isset($this->options['nosort'])) { + $orders = array(); + foreach ($queryset->ordering as $sort) { + $dir = 'ASC'; + if (substr($sort, 0, 1) == '-') { + $dir = 'DESC'; + $sort = substr($sort, 1); + } + list($field) = $this->getField($sort, $model); + $orders[] = $field.' '.$dir; + } + $sort = ' ORDER BY '.implode(', ', $orders); + } + + // Include related tables + $fields = array(); + $table = $model::$meta['table']; + if ($queryset->related) { + $fields = array($this->quote($table).'.*'); + foreach ($queryset->related as $rel) { + // XXX: This is ugly + list($t) = $this->getField($rel, $model, + array('table'=>true)); + $fields[] = $t.'.*'; + } + // Support only retrieving a list of values rather than a model + } elseif ($queryset->values) { + foreach ($queryset->values as $v) { + list($fields[]) = $this->getField($v, $model); + } + } else { + $fields[] = $this->quote($table).'.*'; + } + + $joins = $this->getJoins(); + $sql = 'SELECT '.implode(', ', $fields).' FROM ' + .$this->quote($table).$joins.$where.$sort; + if ($queryset->limit) + $sql .= ' LIMIT '.$queryset->limit; + if ($queryset->offset) + $sql .= ' OFFSET '.$queryset->offset; + + return new MysqlExecutor($sql, $this->params); + } + + function compileUpdate() { + } + + function compileInsert() { + } + + function compileDelete() { + } + + // Returns meta data about the table used to build queries + function inspectTable($table) { + } +} + +class MysqlExecutor { + + var $stmt; + var $fields = array(); + + var $sql; + var $params; + + function __construct($sql, $params) { + $this->sql = $sql; + $this->params = $params; + } + + function _prepare() { + if (!($this->stmt = db_prepare($this->sql))) + throw new Exception('Unable to prepare query: '.db_error() + .' '.$this->sql); + if (count($this->params)) + $this->_bind($this->params); + $this->stmt->execute(); + $this->_setup_output(); + $this->stmt->store_result(); + } + + function _bind($params) { + if (count($params) != $this->stmt->param_count) + throw new Exception('Parameter count does not match query'); + + $types = ''; + $ps = array(); + foreach ($params as &$p) { + if (is_int($p)) + $types .= 'i'; + elseif (is_string($p)) + $types .= 's'; + // TODO: Emit error if param is null + $ps[] = &$p; + } + unset($p); + array_unshift($ps, $types); + call_user_func_array(array($this->stmt,'bind_param'), $ps); + } + + function _setup_output() { + $meta = $this->stmt->result_metadata(); + while ($f = $meta->fetch_field()) + $this->fields[] = $f; + } + + // Iterator interface + function rewind() { + if (!isset($this->stmt)) + $this->_prepare(); + $this->stmt->data_seek(0); + } + + function next() { + $status = $this->stmt->fetch(); + if ($status === false) + throw new Exception($this->stmt->error_list . db_error()); + elseif ($status === null) { + $this->close(); + return false; + } + return true; + } + + function getArray() { + $output = array(); + $variables = array(); + + if (!isset($this->stmt)) + $this->_prepare(); + + foreach ($this->fields as $f) + $variables[] = &$output[$f->name]; // pass by reference + + call_user_func_array(array($this->stmt, 'bind_result'), $variables); + if (!$this->next()) + return false; + return $output; + } + + function getRow() { + $output = array(); + $variables = array(); + + if (!isset($this->stmt)) + $this->_prepare(); + + foreach ($this->fields as $f) + $variables[] = &$output[]; // pass by reference + + call_user_func_array(array($this->stmt, 'bind_result'), $variables); + if (!$this->next()) + return false; + return $output; + } + + function getStruct() { + $output = array(); + $variables = array(); + + if (!isset($this->stmt)) + $this->_prepare(); + + foreach ($this->fields as $f) + $variables[] = &$output[$f->table][$f->name]; // pass by reference + + // TODO: Figure out what the table alias for the root model will be + call_user_func_array(array($this->stmt, 'bind_result'), $variables); + if (!$this->next()) + return false; + return $output; + } + + function close() { + if (!$this->stmt) + return; + + $this->stmt->close(); + $this->stmt = null; + } + + function __toString() { + return $this->sql; + } +} +?> diff --git a/include/class.pdf.php b/include/class.pdf.php index ea658378685c04f8345a5e3e73d7042499b1eeb1..b20eaf7ac53df9290a0532fafcb7ac62d466e491 100644 --- a/include/class.pdf.php +++ b/include/class.pdf.php @@ -109,7 +109,7 @@ class Ticket2PDF extends FPDF function WriteText($w, $text, $border) { $this->SetFont('Times','',11); - $this->MultiCell($w, 5, $text, $border, 'L'); + $this->MultiCell($w, 7, $text, $border, 'L'); } @@ -187,7 +187,7 @@ class Ticket2PDF extends FPDF if($ticket->getIP()) $source.=' ('.$ticket->getIP().')'; $this->Cell($c, 7, $source, 1, 0, 'L', true); - $this->Ln(15); + $this->Ln(12); $this->SetFont('Arial', 'B', 11); if($ticket->isOpen()) { @@ -233,7 +233,30 @@ class Ticket2PDF extends FPDF $this->Cell($l, 7, 'Last Message', 1, 0, 'L', true); $this->SetFont(''); $this->Cell($c, 7, Format::db_datetime($ticket->getLastMsgDate()), 1, 1, 'L', true); - $this->Ln(5); + + $this->SetFillColor(255, 255, 255); + foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) { + $idx = 0; + foreach ($form->getAnswers() as $a) { + if (in_array($a->getField()->get('name'), + array('email','name','subject','phone'))) + continue; + $this->SetFont('Arial', 'B', 11); + if ($idx++ === 0) { + $this->Ln(5); + $this->SetFillColor(244, 250, 255); + $this->Cell(($l+$c)*2, 7, $a->getForm()->get('title'), + 1, 0, 'L', true); + $this->Ln(7); + $this->SetFillColor(255, 255, 255); + } + $this->Cell($l*2, 7, $a->getField()->get('label'), 1, 0, 'L', true); + $this->SetFont(''); + $this->WriteText($c*2, $a->toString(), 1); + } + } + $this->SetFillColor(244, 250, 255); + $this->Ln(10); $this->SetFont('Arial', 'B', 11); $this->cMargin = 0; diff --git a/include/class.staff.php b/include/class.staff.php index a1fe8e7c821576632df1e2e6062a26e19e123409..93708bc0cccfbd71120f321f22e722f7af98834e 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -19,6 +19,7 @@ include_once(INCLUDE_DIR.'class.error.php'); include_once(INCLUDE_DIR.'class.team.php'); include_once(INCLUDE_DIR.'class.group.php'); include_once(INCLUDE_DIR.'class.passwd.php'); +include_once(INCLUDE_DIR.'class.user.php'); class Staff { @@ -165,7 +166,7 @@ class Staff { } function getName() { - return ucfirst($this->ht['firstname'].' '.$this->ht['lastname']); + return new PersonsName($this->ht['firstname'].' '.$this->ht['lastname']); } function getFirstName() { diff --git a/include/class.ticket.php b/include/class.ticket.php index 2ab7ba1e683c0bfe84c8185b6626ad205a9fda99..596254830eb8b4536bcee86df05d25d975643897 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -30,6 +30,8 @@ include_once(INCLUDE_DIR.'class.variable.php'); include_once(INCLUDE_DIR.'class.priority.php'); include_once(INCLUDE_DIR.'class.sla.php'); include_once(INCLUDE_DIR.'class.canned.php'); +require_once(INCLUDE_DIR.'class.dynamic_forms.php'); +require_once(INCLUDE_DIR.'class.user.php'); class Ticket { @@ -60,14 +62,12 @@ class Ticket { if(!$id && !($id=$this->getId())) return false; - $sql='SELECT ticket.*, lock_id, dept_name, priority_desc ' + $sql='SELECT ticket.*, lock_id, dept_name ' .' ,IF(sla.id IS NULL, NULL, DATE_ADD(ticket.created, INTERVAL sla.grace_period HOUR)) as sla_duedate ' .' ,count(attach.attach_id) as attachments ' .' FROM '.TICKET_TABLE.' ticket ' .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) ' .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.isactive=1) ' - .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (' - .'ticket.priority_id=pri.priority_id) ' .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (' .'ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (' @@ -84,6 +84,9 @@ class Ticket { $this->id = $this->ht['ticket_id']; $this->number = $this->ht['ticketID']; + $this->_answers = array(); + + $this->loadDynamicData(); //Reset the sub classes (initiated ondemand)...good for reloads. $this->staff = null; @@ -102,6 +105,15 @@ class Ticket { return true; } + function loadDynamicData() { + if (!$this->_answers) { + foreach (DynamicFormEntry::forTicket($this->getId(), true) as $form) + foreach ($form->getAnswers() as $answer) + $this->_answers[$answer->getField()->get('name')] + = $answer; + } + } + function reload() { return $this->load(); } @@ -170,8 +182,31 @@ class Ticket { return $this->number; } - function getEmail() { - return $this->ht['email']; + function getOwnerId() { + return $this->ht['user_id']; + } + + function getOwner() { + if (!isset($this->user)) + $this->user = User::lookup($this->getOwnerId()); + return $this->user; + } + + function getEmail(){ + if ($o = $this->getOwner()) + return $o->getEmail(); + return null; + } + + function getReplyToEmail() { + if ($this->ht['user_email_id']) { + if (!isset($this->reply_email)) + $this->reply_email = UserEmail::lookup($this->ht['user_email_id']); + return $this->reply_email->address; + } + else { + return $this->getEmail(); + } } function getAuthToken() { @@ -179,12 +214,14 @@ class Ticket { return md5($this->getId() . $this->getEmail() . SECRET_SALT); } - function getName() { - return $this->ht['name']; + function getName(){ + if ($o = $this->getOwner()) + return $o->getName(); + return null; } function getSubject() { - return $this->ht['subject']; + return $this->_answers['subject']; } /* Help topic title - NOT object -> $topic */ @@ -251,27 +288,31 @@ class Ticket { } function getPriorityId() { - return $this->ht['priority_id']; + global $cfg; + + if ($a = $this->_answers['priority']->getValue()) + return $a->getId(); + return $cfg->getDefaultPriorityId(); } - function getPriority() { //TODO: Make it an obj. - return $this->ht['priority_desc']; + function getPriority() { + if ($a = $this->_answers['priority']->getValue()) + return $a->getDesc(); + return '...ummm...'; } function getPhone() { - return $this->ht['phone']; + list($phone, $ext) = explode(" ", $this->_answers['phone'], 2); + return $phone; } function getPhoneExt() { - return $this->ht['phone_ext']; + list($phone, $ext) = explode(" ", $this->_answers['phone'], 2); + return $ext; } function getPhoneNumber() { - $phone=Format::phone($this->getPhone()); - if(($ext=$this->getPhoneExt())) - $phone.=" $ext"; - - return $phone; + return $this->_answers['phone']; } function getSource() { @@ -288,11 +329,8 @@ class Ticket { function getUpdateInfo() { - $info=array('name' => $this->getName(), - 'email' => $this->getEmail(), - 'phone' => $this->getPhone(), + $info=array('phone' => $this->getPhone(), 'phone_ext' => $this->getPhoneExt(), - 'subject' => $this->getSubject(), 'source' => $this->getSource(), 'topicId' => $this->getTopicId(), 'priorityId' => $this->getPriorityId(), @@ -533,20 +571,6 @@ class Ticket { return $this->lastMsgId=$msgid; } - function setPriority($priorityId) { - - //XXX: what happens to SLA priority??? - - if(!$priorityId || $priorityId==$this->getPriorityId()) - return ($priorityId); - - $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() ' - .', priority_id='.db_input($priorityId) - .' WHERE ticket_id='.db_input($this->getId()); - - return (($res=db_query($sql)) && db_affected_rows($res)); - } - //DeptId can NOT be 0. No orphans please! function setDeptId($deptId) { @@ -1069,6 +1093,12 @@ class Ticket { return $closedate; break; + default: + if (isset($this->_answers[$tag])) + // The answer object is retrieved here which will + // automatically invoke the toString() method when the + // answer is coerced into text + return $this->_answers[$tag]; } return false; @@ -1614,6 +1644,9 @@ class Ticket { //delete just orphaned ticket thread & associated attachments. $this->getThread()->delete(); + foreach (DynamicFormEntry::forTicket($this->getId()) as $form) + $form->delete(); + return true; } @@ -1625,13 +1658,8 @@ class Ticket { return false; $fields=array(); - $fields['name'] = array('type'=>'string', 'required'=>1, 'error'=>'Name required'); - $fields['email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); - $fields['subject'] = array('type'=>'string', 'required'=>1, 'error'=>'Subject required'); $fields['topicId'] = array('type'=>'int', 'required'=>1, 'error'=>'Help topic required'); - $fields['priorityId'] = array('type'=>'int', 'required'=>1, 'error'=>'Priority required'); $fields['slaId'] = array('type'=>'int', 'required'=>0, 'error'=>'Select SLA'); - $fields['phone'] = array('type'=>'phone', 'required'=>0, 'error'=>'Valid phone # required'); $fields['duedate'] = array('type'=>'date', 'required'=>0, 'error'=>'Invalid date - must be MM/DD/YY'); $fields['note'] = array('type'=>'text', 'required'=>1, 'error'=>'Reason for the update required'); @@ -1650,23 +1678,9 @@ class Ticket { $errors['duedate']='Due date must be in the future'; } - //Make sure phone extension is valid - if($vars['phone_ext'] ) { - if(!is_numeric($vars['phone_ext']) && !$errors['phone']) - $errors['phone']='Invalid phone ext.'; - elseif(!$vars['phone']) //make sure they just didn't enter ext without phone # - $errors['phone']='Phone number required'; - } - if($errors) return false; $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() ' - .' ,email='.db_input($vars['email']) - .' ,name='.db_input(Format::striptags($vars['name'])) - .' ,subject='.db_input(Format::striptags($vars['subject'])) - .' ,phone="'.db_input($vars['phone'],false).'"' - .' ,phone_ext='.db_input($vars['phone_ext']?$vars['phone_ext']:NULL) - .' ,priority_id='.db_input($vars['priorityId']) .' ,topic_id='.db_input($vars['topicId']) .' ,sla_id='.db_input($vars['slaId']) .' ,duedate='.($vars['duedate']?db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time']))):'NULL'); @@ -1708,11 +1722,13 @@ class Ticket { if(!$extId || !is_numeric($extId)) return 0; - $sql ='SELECT ticket_id FROM '.TICKET_TABLE.' ticket ' - .' WHERE ticketID='.db_input($extId); + $sql ='SELECT ticket.ticket_id FROM '.TICKET_TABLE.' ticket ' + .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' + .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' + .' WHERE ticket.ticketID='.db_input($extId); if($email) - $sql.=' AND email='.db_input($email); + $sql .= ' AND email.address = '.db_input($email); if(($res=db_query($sql)) && db_num_rows($res)) list($id)=db_fetch_row($res); @@ -1763,15 +1779,6 @@ class Ticket { return $id; } - function getOpenTicketsByEmail($email) { - - $sql='SELECT count(*) as open FROM '.TICKET_TABLE.' WHERE status='.db_input('open').' AND email='.db_input($email); - if(($res=db_query($sql)) && db_num_rows($res)) - list($num)=db_fetch_row($res); - - return $num; - } - /* Quick staff's tickets stats */ function getStaffStats($staff) { global $cfg; @@ -1828,6 +1835,8 @@ class Ticket { if(!$email || !Validator::is_email($email)) return null; + if (!$user = User::lookup(array('emails__address'=>$email))) + return null; $sql='SELECT count(open.ticket_id) as open, count(closed.ticket_id) as closed ' .' FROM '.TICKET_TABLE.' ticket ' @@ -1835,7 +1844,7 @@ class Ticket { ON (open.ticket_id=ticket.ticket_id AND open.status=\'open\') ' .' LEFT JOIN '.TICKET_TABLE.' closed ON (closed.ticket_id=ticket.ticket_id AND closed.status=\'closed\')' - .' WHERE ticket.email='.db_input($email); + .' WHERE ticket.user_id = '.db_input($user->getId()); return db_fetch_array(db_query($sql)); } @@ -1848,10 +1857,12 @@ class Ticket { function create($vars, &$errors, $origin, $autorespond=true, $alertstaff=true) { global $ost, $cfg, $thisclient, $_FILES; - // Drop extra whitespace - foreach (array('email', 'phone', 'subject', 'name') as $f) - if (isset($vars[$f])) - $vars[$f] = trim($vars[$f]); + // Identify the user creating the ticket and unpack user information + // fields into local scope for filtering and banning purposes + $user_form = UserForm::getInstance(); + $user_info = $user_form->getClean(); + if ($user_form->isValid()) + $vars += $user_info; //Check for 403 if ($vars['email'] && Validator::is_email($vars['email'])) { @@ -1879,6 +1890,23 @@ class Ticket { } } + // Create and verify the dynamic form entry for the new ticket + $form = TicketForm::getInstance(); + // If submitting via email, ensure we have a subject and such + foreach ($form->getFields() as $field) { + $fname = $field->get('name'); + if ($fname && isset($vars[$fname]) && !$field->value) + $field->value = $vars[$fname]; + } + + // Don't enforce form validation for email + if (!$form->isValid() && strtolower($origin) != 'email') + $errors += $form->errors(); + + // Unpack dynamic variables into $vars for filter application + foreach ($form->getFields() as $f) + $vars['field.'.$f->get('id')] = $f->toString($f->getClean()); + //Init ticket filters... $ticket_filter = new TicketFilter($origin, $vars); // Make sure email contents should not be rejected @@ -1895,9 +1923,6 @@ class Ticket { $id=0; $fields=array(); - $fields['name'] = array('type'=>'string', 'required'=>1, 'error'=>'Name required'); - $fields['email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); - $fields['subject'] = array('type'=>'string', 'required'=>1, 'error'=>'Subject required'); $fields['message'] = array('type'=>'text', 'required'=>1, 'error'=>'Message required'); switch (strtolower($origin)) { case 'web': @@ -1917,20 +1942,10 @@ class Ticket { # TODO: Return error message $errors['err']=$errors['origin'] = 'Invalid origin given'; } - $fields['priorityId'] = array('type'=>'int', 'required'=>0, 'error'=>'Invalid Priority'); - $fields['phone'] = array('type'=>'phone', 'required'=>0, 'error'=>'Valid phone # required'); if(!Validator::process($fields, $vars, $errors) && !$errors['err']) $errors['err'] ='Missing or invalid data - check the errors and try again'; - //Make sure phone extension is valid - if($vars['phone_ext'] ) { - if(!is_numeric($vars['phone_ext']) && !$errors['phone']) - $errors['phone']='Invalid phone ext.'; - elseif(!$vars['phone']) //make sure they just didn't enter ext without phone # XXX: reconsider allowing! - $errors['phone']='Phone number required'; - } - //Make sure the due date is valid if($vars['duedate']) { if(!$vars['time'] || strpos($vars['time'],':')===false) @@ -1941,9 +1956,21 @@ class Ticket { $errors['duedate']='Due date must be in the future'; } + // Data is slightly different between HTTP posts and emails + if ((isset($vars['emailId']) && $vars['emailId']) + || !isset($user_info['email']) || !$user_info['email']) { + $user_info = $vars; + } + elseif (!$user_form->isValid()) { + $errors['user'] = 'Incomplete client information'; + } + //Any error above is fatal. if($errors) return 0; + $user = User::fromForm($user_info); + $user_email = UserEmail::ensure($user_info['email']); + # Perform ticket filter actions on the new ticket arguments if ($ticket_filter) $ticket_filter->apply($vars); @@ -1953,13 +1980,13 @@ class Ticket { // OK...just do it. $deptId=$vars['deptId']; //pre-selected Dept if any. - $priorityId=$vars['priorityId']; $source=ucfirst($vars['source']); $topic=NULL; // Intenal mapping magic...see if we need to override anything if(isset($vars['topicId']) && ($topic=Topic::lookup($vars['topicId']))) { //Ticket created via web by user/or staff $deptId=$deptId?$deptId:$topic->getDeptId(); - $priorityId=$priorityId?$priorityId:$topic->getPriorityId(); + if (!$form->getAnswer('priority')) + $form->setAnswer('priority', null, $topic->getPriorityId()); if($autorespond) $autorespond=$topic->autoRespond(); $source=$vars['source']?$vars['source']:'Web'; @@ -1977,13 +2004,15 @@ class Ticket { }elseif($vars['emailId'] && !$vars['deptId'] && ($email=Email::lookup($vars['emailId']))) { //Emailed Tickets $deptId=$email->getDeptId(); - $priorityId=$priorityId?$priorityId:$email->getPriorityId(); + if (!$form->getAnswer('priority')) + $form->setAnswer('priority', null, $email->getPriorityId()); if($autorespond) $autorespond=$email->autoRespond(); $email=null; $source='Email'; } //Last minute checks - $priorityId=$priorityId?$priorityId:$cfg->getDefaultPriorityId(); + if (!$form->getAnswer('priority')) + $form->setAnswer('priority', null, $cfg->getDefaultPriorityId()); $deptId=$deptId?$deptId:$cfg->getDefaultDeptId(); $topicId=$vars['topicId']?$vars['topicId']:0; $ipaddress=$vars['ip']?$vars['ip']:$_SERVER['REMOTE_ADDR']; @@ -1992,15 +2021,11 @@ class Ticket { $extId=Ticket::genExtRandID(); $sql='INSERT INTO '.TICKET_TABLE.' SET created=NOW() ' .' ,lastmessage= NOW()' + .' ,user_id='.db_input($user->id) + .' ,user_email_id='.db_input($user_email->id) .' ,ticketID='.db_input($extId) .' ,dept_id='.db_input($deptId) .' ,topic_id='.db_input($topicId) - .' ,priority_id='.db_input($priorityId) - .' ,email='.db_input($vars['email']) - .' ,name='.db_input(Format::striptags($vars['name'])) - .' ,subject='.db_input(Format::striptags($vars['subject'])) - .' ,phone="'.db_input($vars['phone'],false).'"' - .' ,phone_ext='.db_input($vars['phone_ext']?$vars['phone_ext']:'') .' ,ip_address='.db_input($ipaddress) .' ,source='.db_input($source); @@ -2021,6 +2046,10 @@ class Ticket { //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } + // Save the (common) dynamic form + $form->setTicketId($id); + $form->save(); + $dept = $ticket->getDept(); //post the message. @@ -2092,11 +2121,6 @@ class Ticket { if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other'))) $errors['source']='Invalid source - '.Format::htmlchars($vars['source']); - if(!$vars['issue']) - $errors['issue']='Summary of the issue required'; - else - $vars['message']=$vars['issue']; - if(!($ticket=Ticket::create($vars, $errors, 'staff', false, (!$vars['assignId'])))) return false; diff --git a/include/class.topic.php b/include/class.topic.php index 801e6e684a0b9cc08f480d7ee80ecf7abaf0b66d..688cb7aaa2715d8e68b6ebfc03f2dc5148175ea6 100644 --- a/include/class.topic.php +++ b/include/class.topic.php @@ -223,10 +223,11 @@ class Topic { .',dept_id='.db_input($vars['dept_id']) .',priority_id='.db_input($vars['priority_id']) .',sla_id='.db_input($vars['sla_id']) + .',form_id='.db_input($vars['form_id']) .',page_id='.db_input($vars['page_id']) .',isactive='.db_input($vars['isactive']) .',ispublic='.db_input($vars['ispublic']) - .',noautoresp='.db_input(isset($vars['noautoresp'])?1:0) + .',noautoresp='.db_input(isset($vars['noautoresp']) && $vars['noautoresp']?1:0) .',notes='.db_input(Format::sanitize($vars['notes'])); //Auto assign ID is overloaded... diff --git a/include/class.user.php b/include/class.user.php new file mode 100644 index 0000000000000000000000000000000000000000..b38ec252886395ecdba9580990fe648fb03e4149 --- /dev/null +++ b/include/class.user.php @@ -0,0 +1,305 @@ +<?php +/********************************************************************* + class.user.php + + External end-user identification for osTicket + + Peter Rotich <peter@osticket.com> + Jared Hancock <jared@osticket.com> + Copyright (c) 2006-2013 osTicket + http://www.osticket.com + + Released under the GNU General Public License WITHOUT ANY WARRANTY. + See LICENSE.TXT for details. + + vim: expandtab sw=4 ts=4 sts=4: +**********************************************************************/ +require_once(INCLUDE_DIR . 'class.orm.php'); + +class UserEmailModel extends VerySimpleModel { + static $meta = array( + 'table' => USER_EMAIL_TABLE, + 'pk' => array('id'), + 'joins' => array( + 'user' => array( + 'constraint' => array('user_id' => 'UserModel.id') + ) + ) + ); +} + +class UserModel extends VerySimpleModel { + static $meta = array( + 'table' => USER_TABLE, + 'pk' => array('id'), + 'joins' => array( + 'emails' => array( + 'reverse' => 'UserEmailModel.user', + ), + 'default_email' => array( + 'null' => true, + 'constraint' => array('default_email_id' => 'UserEmailModel.id') + ), + ) + ); + + var $emails; + + static function objects() { + $qs = parent::objects(); + #$qs->select_related('default_email'); + return $qs; + } + + function getId() { + return $this->id; + } + + function getDefaultEmailAddress() { + return $this->getDefaultEmail()->address; + } + + function getDefaultEmail() { + return $this->default_email; + } +} + +class User extends UserModel { + + function __construct($ht) { + parent::__construct($ht); + // TODO: Make this automatic with select_related() + if (isset($ht['default_email_id'])) + $this->default_email = UserEmail::lookup($ht['default_email_id']); + } + + static function fromForm($data=false) { + // Try and lookup by email address + $user = User::lookup(array('emails__address'=>$data['email'])); + if (!$user) { + $user = User::create(array('name'=>$data['name'], + 'default_email'=> + UserEmail::create(array('address'=>$data['email'])) + )); + $user->save(); + $user->emails->add($user->default_email); + + // Attach initial custom fields + $uf = UserForm::getInstance(); + foreach ($uf->getFields() as $f) + if (isset($data[$f->get('name')])) + $uf->setAnswer($f->get('name'), $data[$f->get('name')]); + $uf->save(); + } + + return $user; + } + + function getEmail() { + return $this->default_email->address; + } + + function getFullName() { + return $this->name; + } + + function getName() { + return new PersonsName($this->name); + } + + function getDynamicData() { + $data = DynamicFormEntry::forClient($this->id); + if (!$data[0]) { + $data = array(); + $g = UserForm::getInstance(); + $g->setClientId($this->id); + $data[] = $g; + } + return $data; + } + + function save($refetch=false) { + // Drop commas and reorganize the name without them + $parts = array_map('trim', explode(',', $this->name)); + switch (count($parts)) { + case 2: + // Assume last, first --or-- last suff., first + $this->name = $parts[1].' '.$parts[0]; + // XXX: Consider last, first suff. + break; + case 3: + // Assume last, first, suffix, write 'first last suffix' + $this->name = $parts[1].' '.$parts[0].' '.$parts[2]; + break; + } + return parent::save($refetch); + } +} +User::_inspect(); + +class PersonsName { + var $parts; + var $name; + + static $formats = array( + 'first' => array("First", 'getFirst'), + 'last' => array("Last", 'getLast'), + 'full' => array("First Last", 'getFull'), + 'legal' => array("First M. Last", 'getLegal'), + 'lastfirst' => array("Last, First", 'getLastFirst'), + 'formal' => array("Mr. Last", 'getFormal'), + 'short' => array("First L.", 'getShort'), + 'shortformal' => array("F. Last", 'getShortFormal'), + 'complete' => array("Mr. First M. Last Sr.", 'getComplete'), + ); + + function __construct($name) { + $this->parts = static::splitName($name); + $this->name = $name; + } + + function getFirst() { + return $this->parts['first']; + } + + function getLast() { + return $this->parts['last']; + } + + function getMiddle() { + return $this->parts['middle']; + } + + function getMiddleInitial() { + return mb_substr($this->parts['middle'],0,1).'.'; + } + + function getFormal() { + return trim($this->parts['salutation'].' '.$this->parts['last']); + } + + function getFull() { + return trim($this->parts['first'].' '.$this->parts['last']); + } + + function getLegal() { + $parts = array( + $this->parts['first'], + mb_substr($this->parts['middle'],0,1), + $this->parts['last'], + ); + if ($parts[1]) $parts[1] .= '.'; + return implode(' ', array_filter($parts)); + } + + function getComplete() { + $parts = array( + $this->parts['salutation'], + $this->parts['first'], + mb_substr($this->parts['middle'],0,1), + $this->parts['last'], + $this->parts['suffix'] + ); + if ($parts[2]) $parts[2] .= '.'; + return implode(' ', array_filter($parts)); + } + + function getLastFirst() { + $name = $this->parts['last'].', '.$this->parts['first']; + if ($this->parts['suffix']) + $name .= ', '.$this->parts['suffix']; + return $name; + } + + function getShort() { + return $this->parts['first'].' '.mb_substr($this->parts['last'],0,1).'.'; + } + + function getShortFormal() { + return mb_substr($this->parts['first'],0,1).'. '.$this->parts['last']; + } + + function getOriginal() { + return $this->name; + } + + function asVar() { + return $this->__toString(); + } + + function __toString() { + global $cfg; + $format = $cfg->getDefaultNameFormat(); + list(,$func) = static::$formats[$format]; + if (!$func) $func = 'getFull'; + return call_user_func(array($this, $func)); + } + + static function allFormats() { + return static::$formats; + } + + /** + * Thanks, http://stackoverflow.com/a/14420217 + */ + static function splitName($name) { + $results = array(); + + $r = explode(' ', $name); + $size = count($r); + + //check first for period, assume salutation if so + if (mb_strpos($r[0], '.') === false) + { + $results['salutation'] = ''; + $results['first'] = $r[0]; + } + else + { + $results['salutation'] = $r[0]; + $results['first'] = $r[1]; + } + + //check last for period, assume suffix if so + if (mb_strpos($r[$size - 1], '.') === false) + { + $results['suffix'] = ''; + } + else + { + $results['suffix'] = $r[$size - 1]; + } + + //combine remains into last + $start = ($results['salutation']) ? 2 : 1; + $end = ($results['suffix']) ? $size - 2 : $size - 1; + + $middle = array(); + for ($i = $start; $i <= $end; $i++) + { + $middle[] = $r[$i]; + } + if (count($middle) > 1) { + $results['last'] = array_pop($middle); + $results['middle'] = implode(' ', $middle); + } + else { + $results['last'] = $middle[0]; + $results['middle'] = ''; + } + + return $results; + } + +} + +class UserEmail extends UserEmailModel { + static function ensure($address) { + $email = static::lookup(array('address'=>$address)); + if (!$email) { + $email = static::create(array('address'=>$address)); + $email->save(); + } + return $email; + } +} diff --git a/include/client/header.inc.php b/include/client/header.inc.php index 2849ccbe3221aafd203cc8dcaf2bf6c28d39fd51..e03f36cbb1aa29b762fc8f4e5eaadba7a51aa489 100644 --- a/include/client/header.inc.php +++ b/include/client/header.inc.php @@ -13,7 +13,10 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/osticket.css" media="screen"> <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/theme.css" media="screen"> <link rel="stylesheet" href="<?php echo ASSETS_PATH; ?>css/print.css" media="print"> - <link type="text/css" href="<?php echo ROOT_PATH; ?>css/ui-lightness/jquery-ui-1.10.3.custom.min.css" rel="stylesheet" /> + <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>scp/css/typeahead.css" + media="screen" /> + <link type="text/css" href="<?php echo ROOT_PATH; ?>css/ui-lightness/jquery-ui-1.10.3.custom.min.css" + rel="stylesheet" media="screen" /> <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/thread.css" media="screen"> <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/redactor.css" media="screen"> <link type="text/css" rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/font-awesome.min.css"> @@ -21,6 +24,7 @@ header("Content-Type: text/html; charset=UTF-8\r\n"); <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/jquery-ui-1.10.3.custom.min.js"></script> <script src="<?php echo ROOT_PATH; ?>js/jquery.multifile.js"></script> <script src="<?php echo ROOT_PATH; ?>js/osticket.js"></script> + <script src="<?php echo ROOT_PATH; ?>scp/js/bootstrap-typeahead.js"></script> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor.min.js"></script> <script type="text/javascript" src="<?php echo ROOT_PATH; ?>js/redactor-osticket.js"></script> <?php diff --git a/include/client/open.inc.php b/include/client/open.inc.php index a0a97b7a3aa711a7446236f249cff0cb084fec8b..b9ba42e2421ed3b35dff180016427ce484c3648e 100644 --- a/include/client/open.inc.php +++ b/include/client/open.inc.php @@ -4,8 +4,7 @@ $info=array(); if($thisclient && $thisclient->isValid()) { $info=array('name'=>$thisclient->getName(), 'email'=>$thisclient->getEmail(), - 'phone'=>$thisclient->getPhone(), - 'phone_ext'=>$thisclient->getPhoneExt()); + 'phone'=>$thisclient->getPhone()); } $info=($_POST && $errors)?Format::htmlchars($_POST):$info; @@ -16,47 +15,14 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info; <?php csrf_token(); ?> <input type="hidden" name="a" value="open"> <table width="800" cellpadding="1" cellspacing="0" border="0"> - <tr> - <th class="required" width="160">Full Name:</th> - <td> - <?php - if($thisclient && $thisclient->isValid()) { - echo $thisclient->getName(); - } else { ?> - <input id="name" type="text" name="name" size="30" value="<?php echo $info['name']; ?>"> - <font class="error">* <?php echo $errors['name']; ?></font> - <?php - } ?> - </td> - </tr> - <tr> - <th class="required" width="160">Email Address:</th> - <td> - <?php - if($thisclient && $thisclient->isValid()) { - echo $thisclient->getEmail(); - } else { ?> - <input id="email" type="text" name="email" size="30" value="<?php echo $info['email']; ?>"> - <font class="error">* <?php echo $errors['email']; ?></font> - <?php - } ?> - </td> - </tr> - <tr> - <th>Telephone:</th> - <td> - - <input id="phone" type="text" name="phone" size="17" value="<?php echo $info['phone']; ?>"> - <label for="ext" class="inline">Ext.:</label> - <input id="ext" type="text" name="phone_ext" size="3" value="<?php echo $info['phone_ext']; ?>"> - <font class="error"> <?php echo $errors['phone']; ?> <?php echo $errors['phone_ext']; ?></font> - </td> - </tr> - <tr><td colspan=2> </td></tr> + <tbody> <tr> <td class="required">Help Topic:</td> <td> - <select id="topicId" name="topicId"> + <select id="topicId" name="topicId" onchange="javascript: + $('#dynamic-form').load( + 'ajax.php/form/help-topic/' + this.value); + "> <option value="" selected="selected">— Select a Help Topic —</option> <?php if($topics=Topic::getPublicHelpTopics()) { @@ -72,60 +38,23 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info; <font class="error">* <?php echo $errors['topicId']; ?></font> </td> </tr> - <tr> - <td class="required">Subject:</td> - <td> - <input id="subject" type="text" name="subject" size="40" value="<?php echo $info['subject']; ?>"> - <font class="error">* <?php echo $errors['subject']; ?></font> - </td> - </tr> - <tr> - <td class="required">Message:</td> - <td> - <div style="margin-bottom:0.5em;"><em>Please provide as much detail as possible so we can best assist you.</em> <font class="error">* <?php echo $errors['message']; ?></font> - </div> - <textarea id="message" cols="60" rows="8" name="message" - class="richtext ifhtml draft" - data-draft-namespace="ticket.client" - data-draft-object-id="<?php echo substr(session_id(), -12); ?>" - ><?php echo $info['message']; ?></textarea> - </td> - </tr> - - <?php if(($cfg->allowOnlineAttachments() && !$cfg->allowAttachmentsOnlogin()) - || ($cfg->allowAttachmentsOnlogin() && ($thisclient && $thisclient->isValid()))) { ?> - <tr> - <td>Attachments:</td> - <td> - <div class="uploads"></div><br> - <input type="file" class="multifile" name="attachments[]" id="attachments" size="30" value="" /> - <font class="error"> <?php echo $errors['attachments']; ?></font> - </td> - </tr> - <tr><td colspan=2> </td></tr> - <?php } ?> - <?php - if($cfg->allowPriorityChange() && ($priorities=Priority::getPriorities())) { ?> - <tr> - <td>Ticket Priority:</td> - <td> - <select id="priority" name="priorityId"> - <?php - if(!$info['priorityId']) - $info['priorityId'] = $cfg->getDefaultPriorityId(); //System default. - foreach($priorities as $id =>$name) { - echo sprintf('<option value="%d" %s>%s</option>', - $id, ($info['priorityId']==$id)?'selected="selected"':'', $name); - - } - ?> - </select> - <font class="error"> <?php echo $errors['priorityId']; ?></font> - </td> - </tr> - <?php - } - ?> +<?php + if (!$thisclient) { + UserForm::getInstance()->render(false, 'Your Information'); + } + else { ?> + <tr><td colspan="2"><hr /></td></tr> + <tr><td>Email:</td><td><?php echo $thisclient->getEmail(); ?></td></tr> + <tr><td>Client:</td><td><?php echo $thisclient->getName(); ?></td></tr> + <?php } + TicketForm::getInstance()->render(false); ?> + </tbody> + <tbody id="dynamic-form"> + <?php if ($form) { + include(CLIENTINC_DIR . 'templates/dynamic-form.tmpl.php'); + } ?> + </tbody> + <tbody> <?php if($cfg && $cfg->isCaptchaEnabled() && (!$thisclient || !$thisclient->isValid())) { if($_POST && $errors && !$errors['captcha']) @@ -144,6 +73,7 @@ $info=($_POST && $errors)?Format::htmlchars($_POST):$info; <?php } ?> <tr><td colspan=2> </td></tr> + </tbody> </table> <p style="padding-left:150px;"> <input type="submit" value="Create Ticket"> diff --git a/include/client/templates/dynamic-form.tmpl.php b/include/client/templates/dynamic-form.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..5eac1d38a00826a471d88dba0fde260b53c7798a --- /dev/null +++ b/include/client/templates/dynamic-form.tmpl.php @@ -0,0 +1,51 @@ +<?php + // Form headline and deck with a horizontal divider above and an extra + // space below. + // XXX: Would be nice to handle the decoration with a CSS class + ?> + <tr><td colspan="2"><hr /> + <div class="form-header" style="margin-bottom:0.5em"> + <h3><?php echo Format::htmlchars($form->getTitle()); ?></h3> + <em><?php echo Format::htmlchars($form->getInstructions()); ?></em> + </div> + </td></tr> + <?php + // Form fields, each with corresponding errors follows. Fields marked + // 'private' are not included in the output for clients + global $thisclient; + foreach ($form->getFields() as $field) { + if ($field->get('private')) + continue; + ?> + <tr> + <?php if ($field->isBlockLevel()) { ?> + <td colspan="2"> + <?php + } + else { ?> + <td><label for="<?php echo $field->getFormname(); ?>" class="<?php + if ($field->get('required')) echo 'required'; ?>"> + <?php echo Format::htmlchars($field->get('label')); ?>:</label></td><td> + <?php + } + $field->render('client'); ?> + <?php if ($field->get('required')) { ?> + <font class="error">*</font> + <?php + } + if ($field->get('hint') && !$field->isBlockLevel()) { ?> + <br /><em style="color:gray;display:inline-block"><?php + echo Format::htmlchars($field->get('hint')); ?></em> + <?php + } + foreach ($field->errors() as $e) { ?> + <br /> + <font class="error"><?php echo $e; ?></font> + <?php } + $field->renderExtras('client'); + ?> + </td> + </tr> + <?php + } +?> diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php index 8eda7eca323e9cf5a9cc8b142575acd9a72d59de..7c2c6e56436e5fcf454caf8c6dca5d117f4f6e72 100644 --- a/include/client/tickets.inc.php +++ b/include/client/tickets.inc.php @@ -18,8 +18,8 @@ if(isset($_REQUEST['status'])) { //Query string status has nothing to do with th $status='open'; //Defaulting to open } -$sortOptions=array('id'=>'ticketID', 'name'=>'ticket.name', 'subject'=>'ticket.subject', - 'email'=>'ticket.email', 'status'=>'ticket.status', 'dept'=>'dept_name','date'=>'ticket.created'); +$sortOptions=array('id'=>'ticketID', 'name'=>'user.name', 'subject'=>'subject.value', + 'email'=>'email.address', 'status'=>'ticket.status', 'dept'=>'dept_name','date'=>'ticket.created'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); //Sorting options... $order_by=$order=null; @@ -38,13 +38,24 @@ if($order_by && strpos($order_by,',')) $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$qselect='SELECT ticket.ticket_id,ticket.ticketID,ticket.dept_id,isanswered, dept.ispublic, ticket.subject, ticket.name, ticket.email '. - ',dept_name,ticket. status, ticket.source, ticket.created '; +$qselect='SELECT ticket.ticket_id,ticket.ticketID,ticket.dept_id,isanswered, ' + .'dept.ispublic, subject.value as subject, ' + .'user.name, email.address as email, ' + .'dept_name,ticket. status, ticket.source, ticket.created '; + +$dynfields='(SELECT entry.object_id, value FROM '.FORM_ANSWER_TABLE.' ans '. + 'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '. + 'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '. + 'WHERE field.name = "%1$s" AND entry.object_type="T")'; +$subject_sql = sprintf($dynfields, 'subject'); $qfrom='FROM '.TICKET_TABLE.' ticket ' - .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) '; + .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) ' + .' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id' + .' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id' + .' LEFT JOIN '.$subject_sql.' subject ON ticket.ticket_id = subject.object_id '; -$qwhere =' WHERE ticket.email='.db_input($thisclient->getEmail()); +$qwhere =' WHERE email.address='.db_input($thisclient->getEmail()); if($status){ $qwhere.=' AND ticket.status='.db_input($status); @@ -58,7 +69,7 @@ if($search) { } else {//Deep search! $queryterm=db_real_escape($_REQUEST['q'],false); //escape the term ONLY...no quotes. $qwhere.=' AND ( ' - ." ticket.subject LIKE '%$queryterm%'" + ." subject.value LIKE '%$queryterm%'" ." OR thread.body LIKE '%$queryterm%'" .' ) '; $deep_search=true; diff --git a/include/client/view.inc.php b/include/client/view.inc.php index f85cff8daeeb59620f5d188978b7aa2ba1a0ba51..0ec5b9941224397b5f5e99239454bc05b35be33e 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -52,6 +52,30 @@ if(!$dept || !$dept->isPublic()) </table> </td> </tr> + <tr> +<?php +foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) { + $answers = $form->getAnswers(); + if ($idx > 0 and $idx % 2 == 0) { ?> + </tr><tr> + <?php } ?> + <td width="50%"> + <table class="infoTable" cellspacing="1" cellpadding="3" width="100%" border="0"> + <?php foreach ($answers as $answer) { + if (in_array($answer->getField()->get('name'), array('name', 'email', 'subject'))) + continue; + elseif ($answer->getField()->get('private')) + continue; + ?> + <tr> + <th width="100"><?php echo $answer->getField()->get('label'); + ?>:</th> + <td><?php echo $answer->toString(); ?></td> + </tr> + <?php } ?> + </table></td> +<?php } ?> +</tr> </table> <br> <h2>Subject:<?php echo Format::htmlchars($ticket->getSubject()); ?></h2> diff --git a/include/i18n/en_US/form.yaml b/include/i18n/en_US/form.yaml new file mode 100644 index 0000000000000000000000000000000000000000..69b3ae30b0d0af5afd2df0dd0f25a2a0c1ba4482 --- /dev/null +++ b/include/i18n/en_US/form.yaml @@ -0,0 +1,105 @@ +# +# Default (dynamic) form configuration. This data is used as the initial, +# minimal data for dynamic forms that ships with the system. +# +# Fields: +# id: Used only if associated with a help topic +# title: Bold section title of the form +# instructions: Title deck, detailed instructions on entering form data +# notes: Notes for the form, shown under the fields +# deletable: True if the form can be removed from the system +# fields: List of fields for the form +# type: Field type (short name) (eg. 'text', 'memo', 'phone', ...) +# label: Field label shown to the user +# name: Name used with the data internally. This is especially +# useful for page and email templates, where %{ ticket.<name> } +# will be used to retrieve the data from the field. +# hint: Help text shown with the field +# edit_mask: Mask out edits to the field (1=>delete, 2=>change name, +# 4=>privacy setting, 8=>requirement setting) +# private: True if the field should be hidden from the client +# required: True if entry for the field is required +# configuration: Field-specific configuration +# size: (text) width of the field +# length: (text) maximum size of the data in the field +# cols: (memo) width of the textarea +# rows: (memo) height of the textarea +# +--- +- id: 1 + type: U # notrans + title: Contact Information + deletable: false + fields: + - type: text # notrans + name: email # notrans + label: Email Address + required: true + sort: 1 + edit_mask: 15 + configuration: + size: 40 + length: 64 + validator: email # notrans + + - type: text # notrans + name: name # notrans + label: Full Name + required: true + sort: 2 + edit_mask: 15 + configuration: + size: 40 + length: 64 + + - type: phone # notrans + name: phone # notrans + label: Phone Number + required: false + sort: 3 + + - type: memo # notrans + name: notes + label: Internal Notes + required: false + private: true + sort: 4 + configuration: + rows: 4 + cols: 40 + +- id: 2 + type: T # notrans + title: Ticket Details + instructions: Please Describe Your Issue + notes: | + This form will be attached to every ticket, regardless of its source. + You can add any fields to this form and they will be available to all + tickets, and will be searchable with advanced search and filterable. + deletable: false + fields: + - type: text # notrans + name: subject # notrans + label: Issue Summary + required: true + edit_mask: 15 + sort: 1 + configuration: + size: 40 + length: 50 + + - type: thread # notrans + name: message # notrans + label: Issue Details + hint: Details on the reason(s) for opening the ticket. + required: true + edit_mask: 15 + sort: 2 + + - type: priority # notrans + name: priority # notrans + label: Priority Level + required: false + private: true + edit_mask: 3 + sort: 3 diff --git a/include/i18n/en_US/help_topic.yaml b/include/i18n/en_US/help_topic.yaml index 11d7e819ccdaf3b5469e622bc4a974536130192d..fe5294319fa36118dd756a53b7bb5d6bc2ded8a8 100644 --- a/include/i18n/en_US/help_topic.yaml +++ b/include/i18n/en_US/help_topic.yaml @@ -23,6 +23,7 @@ dept_id: 1 sla_id: 1 priority_id: 2 + form_id: 0 topic: Support notes: | Tickets that primarily concern the support deparment @@ -32,6 +33,7 @@ dept_id: 1 sla_id: 0 priority_id: 2 + form_id: 0 topic: Billing notes: | Tickets that primarily concern the sales and billing deparments diff --git a/include/i18n/en_US/templates/premade.yaml b/include/i18n/en_US/templates/premade.yaml index 8483de8b9ce7a05dd2a510b64cf1833d509af2ba..fa73ee8f9f87c79e212ddf42e8fbda7f899f6643 100644 --- a/include/i18n/en_US/templates/premade.yaml +++ b/include/i18n/en_US/templates/premade.yaml @@ -17,7 +17,7 @@ - title: Sample (with variables) response: | - %{ticket.name}, + Hi %{ticket.name.first}, Your ticket #%{ticket.number} created on %{ticket.create_date} is in %{ticket.dept.name} department. diff --git a/include/mysqli.php b/include/mysqli.php index ad545a5dbfd8a9fae8206c0e6e14189809e1105b..13ce86ec5ffa293cf91a23de4bfe2dc8e5fe4044 100644 --- a/include/mysqli.php +++ b/include/mysqli.php @@ -225,6 +225,11 @@ function db_field_type($res, $col=0) { return $res->fetch_field_direct($col); # nolint } +function db_prepare($stmt) { + global $__db; + return $__db->prepare($stmt); +} + function db_connect_error() { global $__db; return $__db->connect_error; diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..b5f18b1411284bf260901077a9e2a987c7cde636 --- /dev/null +++ b/include/staff/dynamic-form.inc.php @@ -0,0 +1,208 @@ +<?php + +$info=array(); +if($form && $_REQUEST['a']!='add') { + $title = 'Update custom form section'; + $action = 'update'; + $submit_text='Save Changes'; + $info = $form->ht; + $newcount=2; +} else { + $title = 'Add new custom form section'; + $action = 'add'; + $submit_text='Add Form'; + $newcount=4; +} +$info=Format::htmlchars(($errors && $_POST)?$_POST:$info); + +?> +<form action="?id=<?php echo urlencode($_REQUEST['id']); ?>" method="post" id="save"> + <?php csrf_token(); ?> + <input type="hidden" name="do" value="<?php echo $action; ?>"> + <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> + <h2>Custom Form</h2> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th colspan="2"> + <h4><?php echo $title; ?></h4> + <em>Custom forms are used to allow custom data to be + associated with tickets</em> + </th> + </tr> + </thead> + <tbody> + <tr> + <td width="180" class="required">Title:</td> + <td><input type="text" name="title" size="40" value="<?php + echo $info['title']; ?>"/></td> + </tr> + <tr> + <td width="180">Instructions:</td> + <td><textarea name="instructions" rows="3" cols="40"><?php + echo $info['instructions']; ?></textarea> + </td> + </tr> + </tbody> + </table> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <?php if ($form && $form->get('type') == 'T') { ?> + <thead> + <tr> + <th colspan="7"> + <em><strong>User Information Fields</strong> more information here</em> + </th> + </tr> + <tr> + <th></th> + <th>Label</th> + <th>Type</th> + <th>Internal</th> + <th>Required</th> + <th>Name</th> + <th>Delete</th> + </tr> + </thead> + <tbody> + <?php + $uform = UserForm::objects()->all(); + $ftypes = FormField::allTypes(); + foreach ($uform[0]->getFields() as $f) { + if ($f->get('private')) continue; + ?> + <tr> + <td></td> + <td><?php echo $f->get('label'); ?></td> + <td><?php $t=FormField::getFieldType($f->get('type')); echo $t[0]; ?></td> + <td><input type="checkbox" disabled="disabled"/></td> + <td><input type="checkbox" disabled="disabled" + <?php echo $f->get('required') ? 'checked="checked"' : ''; ?>/></td> + <td><?php echo $f->get('name'); ?></td> + <td><input type="checkbox" disabled="disabled"/></td></tr> + + <?php } ?> + </tbody> + <?php } # form->type == 'T' ?> + <thead> + <tr> + <th colspan="7"> + <em><strong>Form Fields</strong> fields available for ticket information</em> + </th> + </tr> + <tr> + <th>Sort</th> + <th>Label</th> + <th>Type</th> + <th>Internal</th> + <th>Required</th> + <th>Name</th> + <th>Delete</th> + </tr> + </thead> + <tbody class="sortable-rows" data-sort="sort-"> + <?php if ($form) foreach ($form->getFields() as $f) { + $id = $f->get('id'); + $deletable = !$f->isDeletable() ? 'disabled="disabled"' : ''; + $force_name = $f->isNameForced() ? 'disabled="disabled"' : ''; + $force_privacy = $f->isPrivacyForced() ? 'disabled="disabled"' : ''; + $force_required = $f->isRequirementForced() ? 'disabled="disabled"' : ''; + $errors = $f->errors(); ?> + <tr> + <td><i class="icon-sort"></i></td> + <td><input type="text" size="32" name="label-<?php echo $id; ?>" + value="<?php echo $f->get('label'); ?>"/></td> + <td><select name="type-<?php echo $id; ?>" <?php + if (!$f->isChangeable()) echo 'disabled="disabled"'; ?>> + <?php foreach (FormField::allTypes() as $group=>$types) { + ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php + foreach ($types as $type=>$nfo) { + if ($f->get('type') != $type + && isset($nfo[2]) && !$nfo[2]) continue; ?> + <option value="<?php echo $type; ?>" <?php + if ($f->get('type') == $type) echo 'selected="selected"'; ?>> + <?php echo $nfo[0]; ?></option> + <?php } ?> + </optgroup> + <?php } ?> + </select> + <?php if ($f->isConfigurable()) { ?> + <a class="action-button" style="float:none;overflow:inherit" + href="ajax.php/form/field-config/<?php + echo $f->get('id'); ?>" + onclick="javascript: + $('#overlay').show(); + $('#field-config .body').load(this.href); + $('#field-config').show(); + return false; + "><i class="icon-edit"></i> Config</a> + <?php } ?></td> + <td><input type="checkbox" name="private-<?php echo $id; ?>" + <?php if ($f->get('private')) echo 'checked="checked"'; ?> + <?php echo $force_privacy ?>/></td> + <td><input type="checkbox" name="required-<?php echo $id; ?>" + <?php if ($f->get('required')) echo 'checked="checked"'; ?> + <?php echo $force_required ?>/> + </td> + <td> + <input type="text" size="20" name="name-<?php echo $id; ?>" + value="<?php echo $f->get('name'); ?>" <?php echo $force_name ?>/> + <font class="error"><?php + if ($errors['name']) echo '<br/>'; echo $errors['name']; + ?></font> + </td> + <td><input type="checkbox" name="delete-<?php echo $id; ?>" + <?php echo $deletable; ?>/> + <input type="hidden" name="sort-<?php echo $id; ?>" + value="<?php echo $f->get('sort'); ?>"/> + </td> + </tr> + <?php + } + for ($i=0; $i<$newcount; $i++) { ?> + <td><em>+</em> + <input type="hidden" name="sort-new-<?php echo $i; ?>"/></td> + <td><input type="text" size="32" name="label-new-<?php echo $i; ?>"/></td> + <td><select name="type-new-<?php echo $i; ?>"> + <?php foreach (FormField::allTypes() as $group=>$types) { + ?><optgroup label="<?php echo Format::htmlchars($group); ?>"><?php + foreach ($types as $type=>$nfo) { + if (isset($nfo[2]) && !$nfo[2]) continue; ?> + <option value="<?php echo $type; ?>"> + <?php echo $nfo[0]; ?></option> + <?php } ?> + </optgroup> + <?php } ?> + </select></td> + <td><input type="checkbox" name="private-new-<?php echo $i; ?>" + <?php if ($form && $form->get('type') == 'U') + echo 'checked="checked"'; ?>/></td> + <td><input type="checkbox" name="required-new-<?php echo $i; ?>"/></td> + <td><input type="text" size="20" name="name-new-<?php echo $i; ?>"/></td> + <td></td> + </tr> + <?php } ?> + </tbody> + <tbody> + <tr> + <th colspan="7"> + <em><strong>Internal Notes:</strong> be liberal, they're internal</em> + </th> + </tr> + <tr> + <td colspan="7"><textarea class="richtext no-bar" name="notes" + rows="6" cols="80"><?php + echo $info['notes']; ?></textarea> + </td> + </tr> + </tbody> + </table> +<p class="centered"> + <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> + <input type="reset" name="reset" value="Reset"> + <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'> +</p> +</form> + +<div style="display:none;" class="dialog" id="field-config"> + <div class="body"></div> +</div> diff --git a/include/staff/dynamic-forms.inc.php b/include/staff/dynamic-forms.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..2c4fa1d2c413191f5e648f16bc614292470814c7 --- /dev/null +++ b/include/staff/dynamic-forms.inc.php @@ -0,0 +1,128 @@ +<div style="width:700;padding-top:5px; float:left;"> + <h2>Custom Forms</h2> +</div> +<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"> + <b><a href="forms.php?a=add" class="Icon form-add">Add New Custom Form</a></b></div> +<div class="clear"></div> + +<?php +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = DynamicForm::objects()->filter(array('type__in'=>array('T','U','G')))->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$pageNav->setURL('forms.php'); +$showing=$pageNav->showing().' forms'; +?> + +<form action="forms.php" method="POST" name="forms"> +<?php csrf_token(); ?> +<input type="hidden" name="do" value="mass_process" > +<input type="hidden" id="action" name="a" value="" > +<table class="list" border="0" cellspacing="1" cellpadding="0" width="940"> + <thead> + <tr> + <th width="7"> </th> + <th>Client Information Form <em>Added to all clients</em></th> + <th>Last Updated</th> + </tr> + </thead> + <tbody> + <?php foreach (UserForm::objects()->order_by('title') + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()) as $form) { ?> + <tr> + <td/> + <td><a href="?id=<?php echo $form->get('id'); ?>"><?php echo $form->get('title'); ?></a></td> + <td><?php echo $form->get('updated'); ?></td> + </tr> + <?php } ?> + </tbody> + <thead> + <tr> + <th width="7"> </th> + <th>Common Ticket Form <em>Added to all tickets</em></th> + <th>Last Updated</th> + </tr> + </thead> + <tbody> + <?php foreach (TicketForm::objects()->order_by('title') + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()) as $form) { ?> + <tr> + <td/> + <td><a href="?id=<?php echo $form->get('id'); ?>"><?php echo $form->get('title'); ?></a></td> + <td><?php echo $form->get('updated'); ?></td> + </tr> + <?php } ?> + </tbody> + <tbody> + <caption><?php echo $showing; ?></caption> + <thead> + <tr> + <th width="7"> </th> + <th>Title</th> + <th>Last Updated</th> + </tr> + </thead> + <tbody> + <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) + ->order_by('title') + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()) as $form) { + $sel=false; + if($ids && in_array($form->get('id'),$ids)) + $sel=true; ?> + <tr> + <td><?php if ($form->isDeletable()) { ?> + <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $form->get('id'); ?>" + <?php echo $sel?'checked="checked"':''; ?>> + <?php } ?></td> + <td><a href="?id=<?php echo $form->get('id'); ?>"><?php echo $form->get('title'); ?></a></td> + <td><?php echo $form->get('updated'); ?></td> + </tr> + <?php } + ?> + </tbody> + <tfoot> + <tr> + <td colspan="3"> + <?php if($count){ ?> + Select: + <a id="selectAll" href="#ckb">All</a> + <a id="selectNone" href="#ckb">None</a> + <a id="selectToggle" href="#ckb">Toggle</a> + <?php }else{ + echo 'No extra forms defined yet — add one!'; + } ?> + </td> + </tr> + </tfoot> +</table> +<?php +if ($count) //Show options.. + echo '<div> Page:'.$pageNav->getPageLinks().' </div>'; +?> +<p class="centered" id="actions"> + <input class="button" type="submit" name="delete" value="Delete"> +</p> +</form> + +<div style="display:none;" class="dialog" id="confirm-action"> + <h3>Please Confirm</h3> + <a class="close" href="">×</a> + <hr/> + <p class="confirm-action" style="display:none;" id="delete-confirm"> + <font color="red"><strong>Are you sure you want to DELETE selected forms?</strong></font> + <br><br>Deleted forms CANNOT be recovered. + </p> + <div>Please confirm to continue.</div> + <hr style="margin-top:1em"/> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="button" value="No, Cancel" class="close"> + </span> + <span class="buttons" style="float:right"> + <input type="button" value="Yes, Do it!" class="confirm"> + </span> + </p> + <div class="clear"></div> +</div> diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..fffe5d9b2ad549a8ebe34deb9c57167bac7dd04a --- /dev/null +++ b/include/staff/dynamic-list.inc.php @@ -0,0 +1,129 @@ +<?php + +$info=array(); +if($list && $_REQUEST['a']!='add') { + $title = 'Update custom list'; + $action = 'update'; + $submit_text='Save Changes'; + $info = $list->ht; + $newcount=2; +} else { + $title = 'Add new custom list'; + $action = 'add'; + $submit_text='Add List'; + $newcount=4; +} +$info=Format::htmlchars(($errors && $_POST)?$_POST:$info); + +?> +<form action="?" method="post" id="save"> + <?php csrf_token(); ?> + <input type="hidden" name="do" value="<?php echo $action; ?>"> + <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> + <h2>Custom List</h2> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th colspan="2"> + <h4><?php echo $title; ?></h4> + <em>Custom lists are used to provide drop-down lists for custom forms</em> + </th> + </tr> + </thead> + <tbody> + <tr> + <td width="180" class="required">Name:</td> + <td><input size="50" type="text" name="name" value="<?php echo $info['name']; ?>"/></td> + </tr> + <tr> + <td width="180">Plural Name:</td> + <td><input size="50" type="text" name="name_plural" value="<?php echo $info['name_plural']; ?>"/></td> + </tr> + <tr> + <td width="180">Sort Order:</td> + <td><select name="sort_mode"> + <?php foreach (DynamicList::getSortModes() as $key=>$desc) { ?> + <option value="<?php echo $key; ?>" <?php + if ($key == $info['sort_mode']) echo 'selected="selected"'; + ?>><?php echo $desc; ?></option> + <?php } ?> + </select></td> + </tr> + </tbody> + </table> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <?php if ($list) { + $page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; + $count = $list->getItemCount(); + $pageNav = new Pagenate($count, $page, PAGE_LIMIT); + $pageNav->setURL('dynamic-list.php', 'id='.urlencode($_REQUEST['id'])); + $showing=$pageNav->showing().' list items'; + ?> + <?php } + else $showing = 'Add a few initial items to the list'; + ?> + <tr> + <th colspan="4"> + <em><?php echo $showing; ?></em> + </th> + </tr> + <tr> + <th></th> + <th>Value</th> + <th>Extra <em style="display:inline">— abbreviations and such</em></th> + <th>Delete</th> + </tr> + </thead> + <tbody <?php if ($info['sort_mode'] == 'SortCol') { ?> + class="sortable-rows" data-sort="sort-"<?php } ?>> + <?php if ($list) + $icon = ($info['sort_mode'] == 'SortCol') + ? '<i class="icon-sort"></i> ' : ''; + if ($list) { + foreach ($list->getItems() as $i) { + $id = $i->get('id'); ?> + <tr> + <td><?php echo $icon; ?> + <input type="hidden" name="sort-<?php echo $id; ?>" + value="<?php echo $i->get('sort'); ?>"/></td> + <td><input type="text" size="40" name="value-<?php echo $id; ?>" + value="<?php echo $i->get('value'); ?>"/></td> + <td><input type="text" size="30" name="extra-<?php echo $id; ?>" + value="<?php echo $i->get('extra'); ?>"/></td> + <td> + <input type="checkbox" name="delete-<?php echo $id; ?>"/></td> + </tr> + <?php } + } + for ($i=0; $i<$newcount; $i++) { ?> + <tr> + <td><?php echo $icon; ?> <em>+</em> + <input type="hidden" name="sort-new-<?php echo $i; ?>"/></td> + <td><input type="text" size="40" name="value-new-<?php echo $i; ?>"/></td> + <td><input type="text" size="30" name="extra-new-<?php echo $i; ?>"/></td> + <td></td> + </tr> + <?php } ?> + </tbody> + <tbody> + <tr> + <th colspan="7"> + <em><strong>Internal Notes:</strong> be liberal, they're internal</em> + </th> + </tr> + <tr> + <td colspan="7"><textarea name="notes" class="richtext no-bar" + rows="6" cols="80"><?php + echo $info['notes']; ?></textarea> + </td> + </tr> + </tbody> + </table> + </table> +<p class="centered"> + <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> + <input type="reset" name="reset" value="Reset"> + <input type="button" name="cancel" value="Cancel" onclick='window.location.href="?"'> +</p> +</form> diff --git a/include/staff/dynamic-lists.inc.php b/include/staff/dynamic-lists.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..846cab58b8bc4d17dbfcbd275a193d7175cd2da6 --- /dev/null +++ b/include/staff/dynamic-lists.inc.php @@ -0,0 +1,92 @@ +<div style="width:700;padding-top:5px; float:left;"> + <h2>Custom Lists</h2> +</div> +<div style="float:right;text-align:right;padding-top:5px;padding-right:5px;"> + <b><a href="lists.php?a=add" class="Icon list-add">Add New Custom List</a></b></div> +<div class="clear"></div> + +<?php +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = DynamicList::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$pageNav->setURL('lists.php'); +$showing=$pageNav->showing().' custom lists'; +?> + +<form action="lists.php" method="POST" name="lists"> +<?php csrf_token(); ?> +<input type="hidden" name="do" value="mass_process" > +<input type="hidden" id="action" name="a" value="" > +<table class="list" border="0" cellspacing="1" cellpadding="0" width="940"> + <caption><?php echo $showing; ?></caption> + <thead> + <tr> + <th width="7"> </th> + <th>List Name</th> + <th>Created</th> + <th>Last Updated</th> + </tr> + </thead> + <tbody> + <?php foreach (DynamicList::objects()->order_by('name') + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()) as $list) { + $sel = false; + if ($ids && in_array($form->get('id'),$ids)) + $sel = true; ?> + <tr> + <td> + <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $list->get('id'); ?>" + <?php echo $sel?'checked="checked"':''; ?>></td> + <td><a href="?id=<?php echo $list->get('id'); ?>"><?php echo $list->get('name'); ?></a></td> + <td><?php echo $list->get('created'); ?></td> + <td><?php echo $list->get('updated'); ?></td> + </tr> + <?php } + ?> + </tbody> + <tfoot> + <tr> + <td colspan="4"> + <?php if($count){ ?> + Select: + <a id="selectAll" href="#ckb">All</a> + <a id="selectNone" href="#ckb">None</a> + <a id="selectToggle" href="#ckb">Toggle</a> + <?php } else { + echo 'No custom lists defined yet — add one!'; + } ?> + </td> + </tr> + </tfoot> +</table> +<?php +if ($count) //Show options.. + echo '<div> Page:'.$pageNav->getPageLinks().' </div>'; +?> + +<p class="centered" id="actions"> + <input class="button" type="submit" name="delete" value="Delete"> +</p> +</form> + +<div style="display:none;" class="dialog" id="confirm-action"> + <h3>Please Confirm</h3> + <a class="close" href="">×</a> + <hr/> + <p class="confirm-action" style="display:none;" id="delete-confirm"> + <font color="red"><strong>Are you sure you want to DELETE selected lists?</strong></font> + <br><br>Deleted forms CANNOT be recovered. + </p> + <div>Please confirm to continue.</div> + <hr style="margin-top:1em"/> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="button" value="No, Cancel" class="close"> + </span> + <span class="buttons" style="float:right"> + <input type="button" value="Yes, Do it!" class="confirm"> + </span> + </p> + <div class="clear"></div> +</div> diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php index b029fbb682a8ebfdebf386027d561c772aba2382..37ac6c69925bf4dc15711f0a76767803aeacc66a 100644 --- a/include/staff/filter.inc.php +++ b/include/staff/filter.inc.php @@ -125,11 +125,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <select name="rule_w<?php echo $i; ?>"> <option value="">— Select One ‐</option> <?php - foreach($matches as $k=>$v){ - $sel=($info["rule_w$i"]==$k)?'selected="selected"':''; - echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v); - } - ?> + foreach ($matches as $group=>$ms) { ?> + <optgroup label="<?php echo $group; ?>"><?php + foreach ($ms as $k=>$v) { + $sel=($info["rule_w$i"]==$k)?'selected="selected"':''; + echo sprintf('<option value="%s" %s>%s</option>',$k,$sel,$v); + } ?> + </optgroup> + <?php } ?> </select> <select name="rule_h<?php echo $i; ?>"> <option value="0">— Select One ‐</option> diff --git a/include/staff/header.inc.php b/include/staff/header.inc.php index e5b04bff53e4fec7dd0880423aee27b43704e41f..d4e2ce612239c405dc5335650a2c0cf869ff6996 100644 --- a/include/staff/header.inc.php +++ b/include/staff/header.inc.php @@ -23,7 +23,8 @@ <link rel="stylesheet" href="./css/scp.css" media="screen"> <link rel="stylesheet" href="<?php echo ROOT_PATH; ?>css/redactor.css" media="screen"> <link rel="stylesheet" href="./css/typeahead.css" media="screen"> - <link type="text/css" href="<?php echo ROOT_PATH; ?>css/ui-lightness/jquery-ui-1.10.3.custom.min.css" rel="stylesheet" /> + <link type="text/css" href="<?php echo ROOT_PATH; ?>css/ui-lightness/jquery-ui-1.10.3.custom.min.css" + rel="stylesheet" media="screen" /> <link type="text/css" rel="stylesheet" href="../css/font-awesome.min.css"> <link type="text/css" rel="stylesheet" href="./css/dropdown.css"> <script type="text/javascript" src="./js/jquery.dropdown.js"></script> diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index fdf82a404c187cf9d8f0ba6421b635f70a7e3f4a..39de0b76e4bcf48af7b9c82de365834fbcd38c00 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -89,6 +89,22 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> <tr><th colspan="2"><em>New ticket options</em></th></tr> + <tr> + <td><strong>Custom Form</strong>:</td> + <td><select name="form_id"> + <option value="0">— No Extra Fields —</option> + <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $group) { ?> + <option value="<?php echo $group->get('id'); ?>" + <?php if ($group->get('id') == $info['form_id']) + echo 'selected="selected"'; ?>> + <?php echo $group->get('title'); ?> + </option> + <?php } ?> + </select> + <em>Extra information for tickets associated with this help topic</em> + <span class="error"> <?php echo $errors['form_id']; ?></span> + </td> + </tr> <tr> <td width="180" class="required"> Priority: @@ -217,7 +233,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); this topic (Overrides Dept. settings). </td> </tr> - <tr> <th colspan="2"> <em><strong>Admin Notes</strong>: Internal notes about the help topic. </em> diff --git a/include/staff/settings-emails.inc.php b/include/staff/settings-emails.inc.php index e433cd7a2d9eb0d10243d886ce06e941d8dc7773..f8b96f04b345ef4ecf8d6f80847da53f48a5f2fb 100644 --- a/include/staff/settings-emails.inc.php +++ b/include/staff/settings-emails.inc.php @@ -15,6 +15,23 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin() || !$config) </tr> </thead> <tbody> + <tr> + <td width="180" class="required">Default Email Templates:</td> + <td> + <select name="default_template_id"> + <option value="">— Select Default Template —</option> + <?php + $sql='SELECT tpl_id,name FROM '.EMAIL_TEMPLATE_GRP_TABLE.' WHERE isactive=1 ORDER BY name'; + if(($res=db_query($sql)) && db_num_rows($res)){ + while (list($id, $name) = db_fetch_row($res)){ + $selected = ($config['default_template_id']==$id)?'selected="selected"':''; ?> + <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?></option> + <?php + } + } ?> + </select> <font class="error">* <?php echo $errors['default_template_id']; ?></font> + </td> + </tr> <tr> <td width="180" class="required">Default System Email:</td> <td> diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php index 8dd170e4161a97601b589017b4430ba8a15303ae..838b4c0fcb323cb26042d6f9ed8b99a195f08199 100644 --- a/include/staff/settings-system.inc.php +++ b/include/staff/settings-system.inc.php @@ -54,23 +54,6 @@ $gmtime = Misc::gmtime(); </select> <font class="error">* <?php echo $errors['default_dept_id']; ?></font> </td> </tr> - <tr> - <td width="220" class="required">Default Email Templates:</td> - <td> - <select name="default_template_id"> - <option value="">— Select Default Template —</option> - <?php - $sql='SELECT tpl_id,name FROM '.EMAIL_TEMPLATE_GRP_TABLE.' WHERE isactive=1 ORDER BY name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while (list($id, $name) = db_fetch_row($res)){ - $selected = ($config['default_template_id']==$id)?'selected="selected"':''; ?> - <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?></option> - <?php - } - } ?> - </select> <font class="error">* <?php echo $errors['default_template_id']; ?></font> - </td> - </tr> <tr><td>Default Page Size:</td> <td> @@ -112,6 +95,19 @@ $gmtime = Misc::gmtime(); </select> </td> </tr> + <tr> + <td width="180">Default Name Formatting:</td> + <td> + <select name="name_format"> +<?php foreach (PersonsName::allFormats() as $n=>$f) { + list($desc, $func) = $f; + $selected = ($config['name_format'] == $n) ? 'selected="selected"' : ''; ?> + <option value="<?php echo $n; ?>" <?php echo $selected; + ?>><?php echo $desc; ?></option> +<?php } ?> + </select> + </td> + </tr> <tr> <th colspan="2"> <em><b>Authentication Settings</b></em> diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index d360b4d29bf1c9364ade32ce3f5955f043e15f26..6585e8aaa798be387eb119ebcd3f843f3244b726 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -77,13 +77,6 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) </td> </tr> <tr> - <td width="180">Web Tickets Priority:</td> - <td> - <input type="checkbox" name="allow_priority_change" value="1" <?php echo $config['allow_priority_change'] ?'checked="checked"':''; ?>> - <em>(Allow user to override/set priority)</em> - </td> - </tr> - <tr> <td width="180">Emailed Tickets Priority:</td> <td> <input type="checkbox" name="use_email_priority" value="1" <?php echo $config['use_email_priority'] ?'checked="checked"':''; ?> > diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..aa6b01696c45645cd5c29fedfecaa60c4138014c --- /dev/null +++ b/include/staff/templates/dynamic-field-config.tmpl.php @@ -0,0 +1,80 @@ + <h3>Field Configuration — <?php echo $field->get('label') ?></h3> + <a class="close" href="">×</a> + <hr/> + <form method="post" action="ajax.php/form/field-config/<?php + echo $field->get('id'); ?>" onsubmit="javascript: + var form = $(this); + $.post(this.action, form.serialize(), function(data, status, xhr) { + if (!data.length) { + form.closest('.dialog').hide(); + $('#overlay').hide(); + } else { + form.closest('.dialog').empty().append(data); + } + }); + return false; + "> + <table width="100%"> + <?php + echo csrf_token(); + $config = $field->getConfiguration(); + foreach ($field->getConfigurationForm() as $name=>$f) { + if (isset($config[$name])) + $f->value = $config[$name]; + else if ($f->get('default')) + $f->value = $f->get('default'); + ?> + <tr><td> + <label for="<?php echo $f->getWidget()->name; ?>" + style="vertical-align:top;padding-top:0.2em"> + <?php echo Format::htmlchars($f->get('label')); ?>:</label> + </td><td> + <span style="display:inline-block"> + <?php + $f->render(); + if ($f->get('required')) { ?> + <font class="error">*</font> + <?php + } + if ($f->get('hint')) { ?> + <br /><em style="color:gray;display:inline-block"><?php + echo Format::htmlchars($f->get('hint')); ?></em> + <?php + } + ?> + </span> + <?php + foreach ($f->errors() as $e) { ?> + <br /> + <font class="error"><?php echo $e; ?></font> + <?php } ?> + </td></tr> + <?php + } + ?> + <tr><td colspan="2"><hr/></td></tr> + <tr><td> + <label for="hint" + style="vertical-align:top;padding-top:0.2em">Help Text:</label> + </td><td> + <span style="display:inline-block"> + <textarea name="hint" rows="2" cols="40"><?php + echo Format::htmlchars($field->get('hint')); ?></textarea> + <br /> + <em style="color:gray;display:inline-block"> + Help text shown with the field</em> + </span> + </td></tr> + </table> + <hr style="margin-top:3em"/> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="reset" value="Reset"> + <input type="button" value="Cancel" class="close"> + </span> + <span class="buttons" style="float:right"> + <input type="submit" value="Save"> + </span> + </p> + </form> + <div class="clear"></div> diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..c60cb8e0af7abe788132c06e49b3b5439d2783ae --- /dev/null +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -0,0 +1,37 @@ + <?php if ($form->getTitle()) { ?> + <tr><th colspan="2"> + <em><strong><?php echo Format::htmlchars($form->getTitle()); ?></strong>: + <?php echo Format::htmlchars($form->getInstructions()); ?></em> + </th></tr> + <?php + } + foreach ($form->getFields() as $field) { + ?> + <tr><?php if ($field->isBlockLevel()) { ?> + <td colspan="2"> + <?php + } + else { ?> + <td class="multi-line <?php if ($field->get('required')) echo 'required'; ?>"> + <?php echo Format::htmlchars($field->get('label')); ?>:</td> + <td><?php + } + $field->render(); ?> + <?php if ($field->get('required')) { ?> + <font class="error">*</font> + <?php + } + if ($field->get('hint') && !$field->isBlockLevel()) { ?> + <br /><em style="color:gray;display:inline-block"><?php + echo Format::htmlchars($field->get('hint')); ?></em> + <?php + } + foreach ($field->errors() as $e) { ?> + <br /> + <font class="error"><?php echo $e; ?></font> + <?php } ?> + </td> + </tr> + <?php + } +?> diff --git a/include/staff/templates/user-info.tmpl.php b/include/staff/templates/user-info.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..b9802b8f0ad16064fc5172f81c9aab43aee0ff6b --- /dev/null +++ b/include/staff/templates/user-info.tmpl.php @@ -0,0 +1,36 @@ +<h3>User Information — <?php echo $user->getFullName() ?></h3> +<a class="close" href="">×</a> +<hr/> +<form method="post" action="ajax.php/form/user-info/<?php + echo $user->get('id'); ?>" onsubmit="javascript: + var form = $(this); + $.post(this.action, form.serialize(), function(data, status, xhr) { + if (!data.length) { + form.closest('.dialog').hide(); + $('#overlay').hide(); + location.reload(); + } else { + form.closest('.dialog').empty().append(data); + } + }); + return false; + "> + <table width="100%"> + <?php + echo csrf_token(); + foreach ($custom as $form) + $form->render(); + ?> + </table> + <hr style="margin-top:3em"/> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="reset" value="Reset"> + <input type="button" value="Cancel" class="close"> + </span> + <span class="buttons" style="float:right"> + <input type="submit" value="Save"> + </span> + </p> +</form> +<div class="clear"></div> diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php index 4a641d782b41005e0e17b4197b1bff668523d8d7..e2d7864a42c710fd50c049d15e655cacfd00c9cf 100644 --- a/include/staff/ticket-edit.inc.php +++ b/include/staff/ticket-edit.inc.php @@ -8,46 +8,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo()); <input type="hidden" name="do" value="update"> <input type="hidden" name="a" value="edit"> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> - <h2>Update Ticket# <?php echo $ticket->getExtId(); ?></h2> + <h2>Update Ticket #<?php echo $ticket->getExtId(); ?></h2> <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> - <thead> - <tr> - <th colspan="2"> - <h4>Ticket Update</h4> - <em><strong>User Information</strong>: Make sure the email address is valid.</em> - </th> - </tr> - </thead> <tbody> - <tr> - <td width="160" class="required"> - Full Name: - </td> - <td> - <input type="text" size="50" name="name" value="<?php echo $info['name']; ?>"> - <span class="error">* <?php echo $errors['name']; ?></span> - </td> - </tr> - <tr> - <td width="160" class="required"> - Email Address: - </td> - <td> - <input type="text" size="50" name="email" value="<?php echo $info['email']; ?>"> - <span class="error">* <?php echo $errors['email']; ?></span> - </td> - </tr> - <tr> - <td width="160"> - Phone Number: - </td> - <td> - <input type="text" size="20" name="phone" value="<?php echo $info['phone']; ?>"> - <span class="error"> <?php echo $errors['phone']; ?></span> - Ext <input type="text" size="6" name="phone_ext" value="<?php echo $info['phone_ext']; ?>"> - <span class="error"> <?php echo $errors['phone_ext']; ?></span> - </td> - </tr> <tr> <th colspan="2"> <em><strong>Ticket Information</strong>: Due date overrides SLA's grace period.</em> @@ -88,34 +51,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo()); <font class="error"><b>*</b> <?php echo $errors['topicId']; ?></font> </td> </tr> - <tr> - <td width="160" class="required"> - Priority Level: - </td> - <td> - <select name="priorityId"> - <option value="" selected >— Select Priority —</option> - <?php - if($priorities=Priority::getPriorities()) { - foreach($priorities as $id =>$name) { - echo sprintf('<option value="%d" %s>%s</option>', - $id, ($info['priorityId']==$id)?'selected="selected"':'',$name); - } - } - ?> - </select> - <font class="error">* <?php echo $errors['priorityId']; ?></font> - </td> - </tr> - <tr> - <td width="160" class="required"> - Subject: - </td> - <td> - <input type="text" name="subject" size="60" value="<?php echo $info['subject']; ?>"> - <font class="error">* <?php $errors['subject']; ?></font> - </td> - </tr> <tr> <td width="160"> SLA Plan: @@ -153,6 +88,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo()); <em>Time is based on your time zone (GMT <?php echo $thisstaff->getTZoffset(); ?>)</em> </td> </tr> + </tbody> + <tbody id="dynamic-form"> + <?php if ($forms) + foreach ($forms as $form) { + $form->render(true); + } ?> + </tbody> + <tbody> <tr> <th colspan="2"> <em><strong>Internal Note</strong>: Reason for editing the ticket (required) <font class="error"> <?php echo $errors['note'];?></font></em> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index cf9c49dfbec5be1211cb9b74696ba9595e223b79..826d19e7962553244971c135af0c4ce5280c59bd 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -13,48 +13,21 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <tr> <th colspan="2"> <h4>New Ticket</h4> - <em><strong>User Information</strong></em> </th> </tr> </thead> <tbody> + <?php + $uf = UserForm::objects(); + $uf[0]->render(); + if($cfg->notifyONNewStaffTicket()) { ?> <tr> - <td width="160" class="required"> - Email Address: - </td> - <td> - - <input type="text" size="50" name="email" id="email" class="typeahead" value="<?php echo $info['email']; ?>" - autocomplete="off" autocorrect="off" autocapitalize="off"> - <span class="error">* <?php echo $errors['email']; ?></span> - <?php - if($cfg->notifyONNewStaffTicket()) { ?> - - <input type="checkbox" name="alertuser" <?php echo (!$errors || $info['alertuser'])? 'checked="checked"': ''; ?>>Send alert to user. - <?php - } ?> - </td> - </tr> - <tr> - <td width="160" class="required"> - Full Name: - </td> - <td> - <input type="text" size="50" name="name" id="name" value="<?php echo $info['name']; ?>"> - <span class="error">* <?php echo $errors['name']; ?></span> - </td> - </tr> - <tr> - <td width="160"> - Phone Number: - </td> + <td width="160">Alert:</td> <td> - <input type="text" size="20" name="phone" id="phone" value="<?php echo $info['phone']; ?>"> - <span class="error"> <?php echo $errors['phone']; ?></span> - Ext <input type="text" size="6" name="phone_ext" id="phone_ext" value="<?php echo $info['phone_ext']; ?>"> - <span class="error"> <?php echo $errors['phone_ext']; ?></span> + <input type="checkbox" name="alertuser" <?php echo (!$errors || $info['alertuser'])? 'checked="checked"': ''; ?>>Send alert to user. </td> </tr> + <?php } ?> <tr> <th colspan="2"> <em><strong>Ticket Information & Options</strong>:</em> @@ -99,7 +72,10 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Help Topic: </td> <td> - <select name="topicId"> + <select name="topicId" onchange="javascript: + $('#dynamic-form').load( + 'ajax.php/form/help-topic/' + this.value); + "> <option value="" selected >— Select Help Topic —</option> <?php if($topics=Topic::getHelpTopics()) { @@ -113,25 +89,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <font class="error"><b>*</b> <?php echo $errors['topicId']; ?></font> </td> </tr> - <tr> - <td width="160"> - Priority: - </td> - <td> - <select name="priorityId"> - <option value="0" selected >— System Default —</option> - <?php - if($priorities=Priority::getPriorities()) { - foreach($priorities as $id =>$name) { - echo sprintf('<option value="%d" %s>%s</option>', - $id, ($info['priorityId']==$id)?'selected="selected"':'',$name); - } - } - ?> - </select> - <font class="error"> <?php echo $errors['priorityId']; ?></font> - </td> - </tr> <tr> <td width="160"> SLA Plan: @@ -203,29 +160,16 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> </tr> <?php - } ?> - <tr> - <th colspan="2"> - <em><strong>Issue</strong>: The user will be able to see the issue summary below and any associated responses.</em> - </th> - </tr> - <tr> - <td colspan=2> - <div> - <em><strong>Subject</strong>: Issue summary </em> <font class="error">* <?php echo $errors['subject']; ?></font><br> - <input type="text" name="subject" size="60" value="<?php echo $info['subject']; ?>"> - </div> - <div style="margin-top:0.5em; margin-bottom:0.5em"> - <em><strong>Issue</strong></em> - <font class="error">* <?php echo $errors['issue']; ?></font> - </div> - <textarea class="richtext ifhtml draft draft-delete" - placeholder="Details on the reason(s) for opening the ticket." - data-draft-namespace="ticket.staff" name="issue" - cols="21" rows="8" style="width:80%;" - ><?php echo $info['issue']; ?></textarea> - </td> - </tr> + } + TicketForm::getInstance()->render(true); + ?> + </tbody> + <tbody id="dynamic-form"> + <?php + if ($form) $form->render(true); + ?> + </tbody> + <tbody> <?php //is the user allowed to post replies?? if($thisstaff->canPostReply()) { diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index c8dcde436704a486d0c42ee112adb1b88b1e6483..a3ef79d5d4d29f0752ef720c41cd769ab318d6df 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -140,32 +140,35 @@ if($ticket->isOverdue()) </tr> </table> </td> - <td width="50%"> + <td width="50%" style="vertical-align:top"> <table border="0" cellspacing="" cellpadding="4" width="100%"> <tr> - <th width="100">Name:</th> - <td><?php echo Format::htmlchars($ticket->getName()); ?></td> - </tr> - <tr> - <th>Email:</th> - <td> - <?php - echo $ticket->getEmail(); + <th width="100">Client:</th> + <td><a href="ajax.php/form/user-info/<?php + echo $ticket->getOwnerId(); ?>" + onclick="javascript: + $('#overlay').show(); + $('#user-info .body').load(this.href); + $('#user-info').show(); + return false; + "><i class="icon-user"></i> <?php + echo Format::htmlchars($ticket->getName()); ?></a> + <?php if(($client=$ticket->getClient())) { - echo sprintf(' <a href="tickets.php?a=search&query=%s" title="Related Tickets" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>', - urlencode($ticket->getEmail()), $client->getNumTickets()); + echo sprintf(' <a href="tickets.php?a=search&ownerId=%d" title="Related Tickets" data-dropdown="#action-dropdown-stats">(<b>%d</b>)</a>', + urlencode($ticket->getOwnerId()), $client->getNumTickets()); ?> <div id="action-dropdown-stats" class="action-dropdown anchor-right"> <ul> <?php if(($open=$client->getNumOpenTickets())) - echo sprintf('<li><a href="tickets.php?a=search&status=open&query=%s"><i class="icon-folder-open-alt"></i> %d Open Tickets</a></li>', - $ticket->getEmail(), $open); + echo sprintf('<li><a href="tickets.php?a=search&status=open&ownerId=%s"><i class="icon-folder-open-alt"></i> %d Open Tickets</a></li>', + $ticket->getOwnerId(), $open); if(($closed=$client->getNumClosedTickets())) - echo sprintf('<li><a href="tickets.php?a=search&status=closed&query=%s"><i class="icon-folder-close-alt"></i> %d Closed Tickets</a></li>', - $ticket->getEmail(), $closed); + echo sprintf('<li><a href="tickets.php?a=search&status=closed&ownerId=%d"><i class="icon-folder-close-alt"></i> %d Closed Tickets</a></li>', + $ticket->getOwnerId(), $closed); ?> - <li><a href="tickets.php?a=search&query=<?php echo $ticket->getEmail(); ?>"><i class="icon-double-angle-right"></i> All Tickets</a></li> + <li><a href="tickets.php?a=search&ownerId=<?php echo $ticket->getOwnerId(); ?>"><i class="icon-double-angle-right"></i> All Tickets</a></li> </u> </div> <?php @@ -174,8 +177,10 @@ if($ticket->isOverdue()) </td> </tr> <tr> - <th>Phone:</th> - <td><?php echo $ticket->getPhoneNumber(); ?></td> + <th>Email:</th> + <td> + <?php echo $ticket->getEmail(); ?> + </td> </tr> <tr> <th>Source:</th> @@ -184,8 +189,6 @@ if($ticket->isOverdue()) if($ticket->getIP()) echo ' <span class="faded">('.$ticket->getIP().')</span>'; - - ?> </td> </tr> @@ -265,6 +268,43 @@ if($ticket->isOverdue()) </td> </tr> </table> +<br> +<table class="ticket_info" cellspacing="0" cellpadding="0" width="940" border="0"> +<?php +$idx = 0; +foreach (DynamicFormEntry::forTicket($ticket->getId()) as $form) { + // Skip core fields shown earlier in the ticket view + // TODO: Rewrite getAnswers() so that one could write + // ->getAnswers()->filter(not(array('field__name__in'=> + // array('email', ...)))); + $answers = array_filter($form->getAnswers(), function ($a) { + return !in_array($a->getField()->get('name'), + array('email','subject','name','priority')); + }); + if (count($answers) == 0) + continue; + ?> + </tr><tr> + <td colspan="2"> + <table cellspacing="0" cellpadding="4" width="100%" border="0"> + <?php foreach($answers as $a) { + if (!$a->toString()) continue; ?> + <tr> + <th width="100"><?php + echo $a->getField()->get('label'); + ?>:</th> + <td><?php + echo $a->toString(); + ?></td> + </tr> + <?php } ?> + </table> + </td> + <?php + $idx++; + } ?> + </tr> +</table> <div class="clear"></div> <h2 style="padding:10px 0 5px 0; font-size:11pt;"><?php echo Format::htmlchars($ticket->getSubject()); ?></h2> <?php @@ -411,9 +451,9 @@ if(!$cfg->showNotesInline()) { ?> </td> <td width="765"> <?php - $to = $ticket->getEmail(); + $to = $ticket->getReplyToEmail(); if(($name=$ticket->getName()) && !strpos($name,'@')) - $to =sprintf('%s <em><%s></em>', $name, $ticket->getEmail()); + $to =sprintf('%s <em><%s></em>', $name, $to); echo $to; ?> @@ -785,6 +825,9 @@ if(!$cfg->showNotesInline()) { ?> <?php } ?> </div> +<div style="display:none;" class="dialog" id="user-info"> + <div class="body"></div> +</div> <div style="display:none;" class="dialog" id="print-options"> <h3>Ticket Print Options</h3> <a class="close" href="">×</a> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index ac38d5ff8c87eddf718c1a547315f292fed5ccb7..eb2b6f1315e8dd67edd5aaaa6e9e94b32f59f7bb 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -12,7 +12,7 @@ $searchTerm=''; //make sure the search query is 3 chars min...defaults to no query with warning message if($search) { $searchTerm=$_REQUEST['query']; - if( ($_REQUEST['query'] && strlen($_REQUEST['query'])<3) + if( ($_REQUEST['query'] && strlen($_REQUEST['query'])<3) || (!$_REQUEST['query'] && isset($_REQUEST['basic_search'])) ){ //Why do I care about this crap... $search=false; //Instead of an error page...default back to regular query..with no search. $errors['err']='Search term must be more than 3 chars'; @@ -21,7 +21,7 @@ if($search) { } $showoverdue=$showanswered=false; $staffId=0; //Nothing for now...TODO: Allow admin and manager to limit tickets to single staff level. -$showassigned= true; //show Assigned To column - defaults to true +$showassigned= true; //show Assigned To column - defaults to true //Get status we are actually going to use on the query...making sure it is clean! $status=null; @@ -49,17 +49,17 @@ switch(strtolower($_REQUEST['status'])){ //Status is overloaded $results_type='Answered Tickets'; break; default: - if(!$search) + if(!$search && !isset($_REQUEST['advsid'])) $_REQUEST['status']=$status='open'; } $qwhere =''; -/* +/* STRICT DEPARTMENTS BASED PERMISSION! User can also see tickets assigned to them regardless of the ticket's dept. */ -$depts=$thisstaff->getDepts(); +$depts=$thisstaff->getDepts(); $qwhere =' WHERE ( ' .' ticket.staff_id='.db_input($thisstaff->getId()); @@ -73,7 +73,7 @@ $qwhere .= ' )'; //STATUS if($status) { - $qwhere.=' AND status='.db_input(strtolower($status)); + $qwhere.=' AND status='.db_input(strtolower($status)); } //Queues: Overloaded sub-statuses - you've got to just have faith! @@ -87,10 +87,10 @@ if($staffId && ($staffId==$thisstaff->getId())) { //My tickets $qwhere.=' AND isanswered=1 '; }elseif(!strcasecmp($status, 'open') && !$search) { //Open queue (on search OPEN means all open tickets - regardless of state). //Showing answered tickets on open queue?? - if(!$cfg->showAnsweredTickets()) + if(!$cfg->showAnsweredTickets()) $qwhere.=' AND isanswered=0 '; - /* Showing assigned tickets on open queue? + /* Showing assigned tickets on open queue? Don't confuse it with show assigned To column -> F'it it's confusing - just trust me! */ if(!($cfg->showAssignedTickets() || $thisstaff->showAssignedTickets())) { @@ -99,7 +99,7 @@ if($staffId && ($staffId==$thisstaff->getId())) { //My tickets } } -//Search?? Somebody...get me some coffee +//Search?? Somebody...get me some coffee $deep_search=false; if($search): $qstr.='&a='.urlencode($_REQUEST['a']); @@ -109,92 +109,36 @@ if($search): if($searchTerm){ $qstr.='&query='.urlencode($searchTerm); $queryterm=db_real_escape($searchTerm,false); //escape the term ONLY...no quotes. - if(is_numeric($searchTerm)){ + if (is_numeric($searchTerm)) { $qwhere.=" AND ticket.ticketID LIKE '$queryterm%'"; - }elseif(strpos($searchTerm,'@') && Validator::is_email($searchTerm)){ //pulling all tricks! + } elseif (strpos($searchTerm,'@') && Validator::is_email($searchTerm)) { + //pulling all tricks! # XXX: What about searching for email addresses in the body of # the thread message - $qwhere.=" AND ticket.email='$queryterm'"; - }else{//Deep search! - //This sucks..mass scan! search anything that moves! - - $deep_search=true; - $qwhere.=" AND ( ticket.email LIKE '%$queryterm%'". - " OR ticket.name LIKE '%$queryterm%'". - " OR ticket.subject LIKE '%$queryterm%'". - " OR thread.body LIKE '%$queryterm%'". - " OR thread.title LIKE '%$queryterm%'". - ' ) '; - } - } - //department - if($_REQUEST['deptId'] && in_array($_REQUEST['deptId'],$thisstaff->getDepts())) { - //This is dept based search..perm taken care above..put the sucker in. - $qwhere.=' AND ticket.dept_id='.db_input($_REQUEST['deptId']); - $qstr.='&deptId='.urlencode($_REQUEST['deptId']); - } - - //Help topic - if($_REQUEST['topicId']) { - $qwhere.=' AND ticket.topic_id='.db_input($_REQUEST['topicId']); - $qstr.='&topicId='.urlencode($_REQUEST['topicId']); - } - - //Assignee - if(isset($_REQUEST['assignee']) && strcasecmp($_REQUEST['status'], 'closed')) { - $id=preg_replace("/[^0-9]/", "", $_REQUEST['assignee']); - $assignee = $_REQUEST['assignee']; - $qstr.='&assignee='.urlencode($_REQUEST['assignee']); - $qwhere.= ' AND ( - ( ticket.status="open" '; - - if($assignee[0]=='t') - $qwhere.=' AND ticket.team_id='.db_input($id); - elseif($assignee[0]=='s') - $qwhere.=' AND ticket.staff_id='.db_input($id); - elseif(is_numeric($id)) - $qwhere.=' AND ticket.staff_id='.db_input($id); - - $qwhere.=' ) '; - - if($_REQUEST['staffId'] && !$_REQUEST['status']) { //Assigned TO + Closed By - $qwhere.= ' OR (ticket.staff_id='.db_input($_REQUEST['staffId']). ' AND ticket.status="closed") '; - $qstr.='&staffId='.urlencode($_REQUEST['staffId']); - }elseif(isset($_REQUEST['staffId'])) { - $qwhere.= ' OR ticket.status="closed" '; - $qstr.='&staffId='.urlencode($_REQUEST['staffId']); - } - - $qwhere.= ' ) '; - } elseif($_REQUEST['staffId']) { - $qwhere.=' AND (ticket.staff_id='.db_input($_REQUEST['staffId']).' AND ticket.status="closed") '; - $qstr.='&staffId='.urlencode($_REQUEST['staffId']); - } - - //dates - $startTime =($_REQUEST['startDate'] && (strlen($_REQUEST['startDate'])>=8))?strtotime($_REQUEST['startDate']):0; - $endTime =($_REQUEST['endDate'] && (strlen($_REQUEST['endDate'])>=8))?strtotime($_REQUEST['endDate']):0; - if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0)){ - $errors['err']='Entered date span is invalid. Selection ignored.'; - $startTime=$endTime=0; - }else{ - //Have fun with dates. - if($startTime){ - $qwhere.=' AND ticket.created>=FROM_UNIXTIME('.$startTime.')'; - $qstr.='&startDate='.urlencode($_REQUEST['startDate']); - - } - if($endTime){ - $qwhere.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')'; - $qstr.='&endDate='.urlencode($_REQUEST['endDate']); + $qwhere.=" AND email.address='$queryterm'"; + } else {//Deep search! + //This sucks..mass scan! search anything that moves! + require_once(INCLUDE_DIR.'ajax.tickets.php'); + + $tickets = TicketsAjaxApi::_search(array('query'=>$queryterm)); + if (count($tickets)) + $qwhere .= ' AND ticket.ticket_id IN ('. + implode(',',db_input($tickets)).')'; } } endif; -$sortOptions=array('date'=>'ticket.created','ID'=>'ticketID','pri'=>'priority_urgency','name'=>'ticket.name', - 'subj'=>'ticket.subject','status'=>'ticket.status','assignee'=>'assigned','staff'=>'staff', - 'dept'=>'dept_name'); +if ($_REQUEST['advsid'] && isset($_SESSION['adv_'.$_REQUEST['advsid']])) { + $qstr.='advsid='.$_REQUEST['advsid']; + $qwhere .= ' AND ticket.ticket_id IN ('. implode(',', + db_input($_SESSION['adv_'.$_REQUEST['advsid']])).')'; +} + +$sortOptions=array('date'=>'ticket.created','ID'=>'ticketID', + 'pri'=>'priority_id','name'=>'user.name','subj'=>'subject', + 'status'=>'ticket.status','assignee'=>'assigned','staff'=>'staff', + 'dept'=>'dept_name'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); @@ -222,21 +166,21 @@ if($_REQUEST['sort'] && $queue) { //Set default sort by columns. if(!$order_by ) { - if($showanswered) + if($showanswered) $order_by='ticket.lastresponse, ticket.created'; //No priority sorting for answered tickets. elseif(!strcasecmp($status,'closed')) $order_by='ticket.closed, ticket.created'; //No priority sorting for closed tickets. elseif($showoverdue) //priority> duedate > age in ASC order. - $order_by='priority_urgency ASC, ISNULL(duedate) ASC, duedate ASC, effective_date ASC, ticket.created'; - else //XXX: Add due date here?? No - - $order_by='priority_urgency ASC, effective_date DESC, ticket.created'; + $order_by='priority_id, ISNULL(duedate) ASC, duedate ASC, effective_date ASC, ticket.created'; + else //XXX: Add due date here?? No - + $order_by='priority_id, effective_date DESC, ticket.created'; } $order=$order?$order:'DESC'; if($order_by && strpos($order_by,',') && $order) $order_by=preg_replace('/(?<!ASC|DESC),/', " $order,", $order_by); -$sort=$_REQUEST['sort']?strtolower($_REQUEST['sort']):'urgency'; //Urgency is not on display table. +$sort=$_REQUEST['sort']?strtolower($_REQUEST['sort']):'priority_id'; //Urgency is not on display table. $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; @@ -244,11 +188,21 @@ if($_GET['limit']) $qstr.='&limit='.urlencode($_GET['limit']); $qselect ='SELECT DISTINCT ticket.ticket_id,lock_id,ticketID,ticket.dept_id,ticket.staff_id,ticket.team_id ' - .' ,ticket.subject,ticket.name,ticket.email,dept_name ' - .' ,ticket.status,ticket.source,isoverdue,isanswered,ticket.created,pri.* '; + .',MAX(IF(field.name = \'subject\', ans.value, NULL)) as `subject`' + .',MAX(IF(field.name = \'priority\', ans.value, NULL)) as `priority_desc`' + .',MAX(IF(field.name = \'priority\', ans.value_id, NULL)) as `priority_id`' + .' ,user.name' + .' ,email.address as email, dept_name ' + .' ,ticket.status,ticket.source,isoverdue,isanswered,ticket.created '; $qfrom=' FROM '.TICKET_TABLE.' ticket '. - ' LEFT JOIN '.DEPT_TABLE.' dept ON ticket.dept_id=dept.dept_id '; + ' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'. + ' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'. + ' LEFT JOIN '.DEPT_TABLE.' dept ON ticket.dept_id=dept.dept_id '. + ' LEFT JOIN ost_form_entry entry ON entry.object_type=\'T\' + and entry.object_id=ticket.ticket_id'. + ' LEFT JOIN ost_form_entry_values ans ON ans.entry_id = entry.id'. + ' LEFT JOIN ost_form_field field ON field.id=ans.field_id'; $sjoin=''; if($search && $deep_search) { @@ -273,8 +227,7 @@ $qselect.=' ,count(attach.attach_id) as attachments ' .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned ' .' ,IF(ptopic.topic_pid IS NULL, topic.topic, CONCAT_WS(" / ", ptopic.topic, topic.topic)) as helptopic '; -$qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.priority_id) ' - .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW() +$qfrom.=' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON (ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW() AND tlock.staff_id!='.db_input($thisstaff->getId()).') ' .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON (ticket.ticket_id=attach.ticket_id) ' .' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON ( ticket.ticket_id=thread.ticket_id) ' @@ -284,6 +237,11 @@ $qfrom.=' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (ticket.priority_id=pri.pri .' LEFT JOIN '.TOPIC_TABLE.' topic ON (ticket.topic_id=topic.topic_id) ' .' LEFT JOIN '.TOPIC_TABLE.' ptopic ON (ptopic.topic_id=topic.topic_pid) '; +// Fetch priority information +$res = db_query('select * from '.PRIORITY_TABLE); +$prios = array(); +while ($row = db_fetch_array($res)) + $prios[$row['priority_id']] = $row; $query="$qselect $qfrom $qwhere $qgroup ORDER BY $order_by $order LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); //echo $query; @@ -333,13 +291,13 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. <th width="8px"> </th> <?php } ?> <th width="70"> - <a <?php echo $id_sort; ?> href="tickets.php?sort=ID&order=<?php echo $negorder; ?><?php echo $qstr; ?>" + <a <?php echo $id_sort; ?> href="tickets.php?sort=ID&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Ticket ID <?php echo $negorder; ?>">Ticket</a></th> <th width="70"> - <a <?php echo $date_sort; ?> href="tickets.php?sort=date&order=<?php echo $negorder; ?><?php echo $qstr; ?>" + <a <?php echo $date_sort; ?> href="tickets.php?sort=date&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Date <?php echo $negorder; ?>">Date</a></th> <th width="280"> - <a <?php echo $subj_sort; ?> href="tickets.php?sort=subj&order=<?php echo $negorder; ?><?php echo $qstr; ?>" + <a <?php echo $subj_sort; ?> href="tickets.php?sort=subj&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Subject <?php echo $negorder; ?>">Subject</a></th> <th width="170"> <a <?php echo $name_sort; ?> href="tickets.php?sort=name&order=<?php echo $negorder; ?><?php echo $qstr; ?>" @@ -352,27 +310,27 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. <?php } else { ?> <th width="60" <?php echo $pri_sort;?>> - <a <?php echo $pri_sort; ?> href="tickets.php?sort=pri&order=<?php echo $negorder; ?><?php echo $qstr; ?>" + <a <?php echo $pri_sort; ?> href="tickets.php?sort=pri&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Priority <?php echo $negorder; ?>">Priority</a></th> <?php } - if($showassigned ) { + if($showassigned ) { //Closed by if(!strcasecmp($status,'closed')) { ?> <th width="150"> - <a <?php echo $staff_sort; ?> href="tickets.php?sort=staff&order=<?php echo $negorder; ?><?php echo $qstr; ?>" + <a <?php echo $staff_sort; ?> href="tickets.php?sort=staff&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Closing Staff Name <?php echo $negorder; ?>">Closed By</a></th> <?php } else { //assigned to ?> <th width="150"> - <a <?php echo $assignee_sort; ?> href="tickets.php?sort=assignee&order=<?php echo $negorder; ?><?php echo $qstr; ?>" + <a <?php echo $assignee_sort; ?> href="tickets.php?sort=assignee&order=<?php echo $negorder; ?><?php echo $qstr; ?>" title="Sort By Assignee <?php echo $negorder;?>">Assigned To</a></th> <?php } } else { ?> <th width="150"> - <a <?php echo $dept_sort; ?> href="tickets.php?sort=dept&order=<?php echo $negorder;?><?php echo $qstr; ?>" + <a <?php echo $dept_sort; ?> href="tickets.php?sort=dept&order=<?php echo $negorder;?><?php echo $qstr; ?>" title="Sort By Department <?php echo $negorder; ?>">Department</a></th> <?php } ?> @@ -411,8 +369,8 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. } ?> <tr id="<?php echo $row['ticket_id']; ?>"> - <?php if($thisstaff->canManageTickets()) { - + <?php if($thisstaff->canManageTickets()) { + $sel=false; if($ids && in_array($row['ticket_id'], $ids)) $sel=true; @@ -422,34 +380,34 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. </td> <?php } ?> <td align="center" title="<?php echo $row['email']; ?>" nowrap> - <a class="Icon <?php echo strtolower($row['source']); ?>Ticket ticketPreview" title="Preview Ticket" + <a class="Icon <?php echo strtolower($row['source']); ?>Ticket ticketPreview" title="Preview Ticket" href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $tid; ?></a></td> <td align="center" nowrap><?php echo Format::db_date($row['created']); ?></td> - <td><a <?php if($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?> + <td><a <?php if($flag) { ?> class="Icon <?php echo $flag; ?>Ticket" title="<?php echo ucfirst($flag); ?> Ticket" <?php } ?> href="tickets.php?id=<?php echo $row['ticket_id']; ?>"><?php echo $subject; ?></a> <?php echo ($threadcount>1)?" <small>($threadcount)</small> ":''?> <?php echo $row['attachments']?"<span class='Icon file'> </span>":''; ?> </td> <td nowrap> <?php echo Format::truncate($row['name'],22,strpos($row['name'],'@')); ?> </td> - <?php + <?php if($search && !$status){ $displaystatus=ucfirst($row['status']); if(!strcasecmp($row['status'],'open')) $displaystatus="<b>$displaystatus</b>"; echo "<td>$displaystatus</td>"; } else { ?> - <td class="nohover" align="center" style="background-color:<?php echo $row['priority_color']; ?>;"> - <?php echo $row['priority_desc']; ?></td> + <td class="nohover" align="center" style="background-color:<?php echo $prios[$row['priority_id']]['priority_color']; ?>;"> + <?php echo $prios[$row['priority_id']]['priority_desc']; ?></td> <?php - } + } ?> <td nowrap> <?php echo $lc; ?></td> </tr> <?php } //end of while. else: //not tickets found!! set fetch error. - $ferror='There are no tickets here. (Leave a little early today).'; + $ferror='There are no tickets here. (Leave a little early today).'; endif; ?> </tbody> <tfoot> @@ -477,7 +435,7 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. ?> <?php if($thisstaff->canManageTickets()) { ?> - <p class="centered" id="actions"> + <p class="centered" id="actions"> <?php $status=$_REQUEST['status']?$_REQUEST['status']:$status; switch (strtolower($status)) { @@ -572,7 +530,7 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. <?php if(($mydepts = $thisstaff->getDepts()) && ($depts=Dept::getDepartments())) { foreach($depts as $id =>$name) { - if(!in_array($id, $mydepts)) continue; + if(!in_array($id, $mydepts)) continue; echo sprintf('<option value="%d">%s</option>', $id, $name); } } @@ -594,7 +552,7 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. } echo '</OPTGROUP>'; } - + if(($teams=Team::getTeams())) { echo '<OPTGROUP label="Teams ('.count($teams).')">'; foreach($teams as $id => $name) { @@ -635,6 +593,17 @@ $negorder=$order=='DESC'?'ASC':'DESC'; //Negate the sorting.. <span>TO</span> <input class="dp" type="input" size="20" name="endDate"> </fieldset> + <fieldset> + <?php + foreach (TicketForm::getInstance()->getFields() as $f) { + if (in_array($f->get('type'), array('text', 'memo', 'phone', 'thread'))) + continue; + elseif (!$f->hasData()) + continue; + ?><label><?php echo $f->getLabel(); ?>:</label> + <div style="display:inline-block;width: 12.5em;"><?php $f->render(); ?></div> + <?php } ?> + </fieldset> <p> <span class="buttons"> <input type="submit" value="Search"> diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig index 8ced61f0cdd122a0aaeead6269f21f5ee31c8d88..ce6d3fd6537de8fbe1b5c9acda7833e5972d37b6 100644 --- a/include/upgrader/streams/core.sig +++ b/include/upgrader/streams/core.sig @@ -1 +1 @@ -dad45ca24a3800102f8b92c7279347db +17f0cc96b366a747113622fa4780f9bc diff --git a/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql b/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql index 6f2727600eed75fcf85c3e7c0bc844470c842738..94b8f8aa7fcd59f202bcc281324c58894f160a72 100644 --- a/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql +++ b/include/upgrader/streams/core/d51f303a-dad45ca2.patch.sql @@ -45,92 +45,129 @@ CREATE TABLE `%TABLE_PREFIX%draft` ( -- Migrate email templates to HTML UPDATE `%TABLE_PREFIX%email_template` - SET `body` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `body`)))); + SET `body` = REPLACE( REPLACE( REPLACE( REPLACE( + `body`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); -- Migrate notes to HTML UPDATE `%TABLE_PREFIX%api_key` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%email` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%email_template_group` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%faq` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%faq_category` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%filter` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%groups` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%help_topic` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%page` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%sla` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%staff` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); UPDATE `%TABLE_PREFIX%team` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); -- Migrate canned responses to HTML UPDATE `%TABLE_PREFIX%canned_response` - SET `notes` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `notes`)))), - `response` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', - REPLACE('&', '&', `response`)))); + SET `notes` = REPLACE( REPLACE( REPLACE( REPLACE( + `notes`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'), + `response` = REPLACE( REPLACE( REPLACE( REPLACE( + `response`, + '<', '<'), + '>', '>'), + '\n', '<br/>'), + '&', '&'); -- Migrate ticket-thread to HTML -- XXX: Migrate & -> & ? -- the problem is that there's a fix in 1.7.1 -- that properly encodes these characters, so encoding & would mean possible -- double encoding. UPDATE `%TABLE_PREFIX%ticket_thread` - SET `body` = REPLACE('\n', '<br/>', - REPLACE('<', '<', - REPLACE('>', '>', `body`))); + SET `body` = REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( + `body`, + '\r', ''), + '\n ', '\n'), + '\n\n\n', '\n\n'), + '\n\n\n', '\n\n'), + '\n\n\n', '\n\n'), + '<', '<'), + '>', '>'), + '\n', '<br/>'); -- Finished with patch UPDATE `%TABLE_PREFIX%config` diff --git a/include/upgrader/streams/core/dad45ca2-17f0cc96.cleanup.sql b/include/upgrader/streams/core/dad45ca2-17f0cc96.cleanup.sql new file mode 100644 index 0000000000000000000000000000000000000000..4fe8c0d0a9253566015880bba8b3c615ae143c23 --- /dev/null +++ b/include/upgrader/streams/core/dad45ca2-17f0cc96.cleanup.sql @@ -0,0 +1,104 @@ +-- Port data from the ticket table +-- 1. Create form entries for each ticket +INSERT INTO `%TABLE_PREFIX%form_entry` ( + `form_id`, `object_id`, `object_type`, `sort`, `created`, `updated`) + SELECT (SELECT id FROM ost_form WHERE `type`='T'), + `ticket_id`, 'T', 10, `created`, `updated` + FROM `%TABLE_PREFIX%ticket`; + +-- 2. Copy subject lines from the ticket table into form entry +INSERT INTO `%TABLE_PREFIX%form_entry_values` ( + `field_id`, `entry_id`, `value`) + SELECT A3.`id`, A2.`id`, A1.`subject` + FROM `%TABLE_PREFIX%ticket` A1 + INNER JOIN `%TABLE_PREFIX%form` A4 ON (`type`='T') + INNER JOIN `%TABLE_PREFIX%form_entry` A2 ON (A2.`object_id` + = A1.`ticket_id` AND A2.`object_type` = 'T') + INNER JOIN `%TABLE_PREFIX%form_field` A3 ON (A2.`form_id` + = A4.`id`) + WHERE A3.`name` = 'subject'; + +-- 2b. Copy priority from ticket to custom form entry +INSERT INTO `%TABLE_PREFIX%form_entry_values` ( + `field_id`, `entry_id`, `value`, `value_id`) + SELECT A3.`id`, A2.`id`, A5.`priority_desc`, A1.`priority_id` + FROM `%TABLE_PREFIX%ticket` A1 + INNER JOIN `%TABLE_PREFIX%form` A4 ON (`type`='T') + INNER JOIN `%TABLE_PREFIX%form_entry` A2 ON (A2.`object_id` + = A1.`ticket_id` AND A2.`object_type` = 'T') + INNER JOIN `%TABLE_PREFIX%form_field` A3 ON (A2.`form_id` + = A4.`id`) + INNER JOIN `%TABLE_PREFIX%ticket_priority` A5 ON (A5.`priority_id` + = A1.`priority_id`) + WHERE A3.`name` = 'priority'; + +-- 3. Create <user> accounts for everybody +-- - Start with creating email addresses for the accounts +INSERT INTO `%TABLE_PREFIX%user_email` (`address`) + SELECT DISTINCT `email` FROM `%TABLE_PREFIX%ticket`; + +-- - Then create the accounts and link the `default_email`s +INSERT INTO `%TABLE_PREFIX%user` (`name`, `default_email_id`, `created`, `updated`) + SELECT MAX(`name`), A2.`id`, A1.`created`, A1.`updated` + FROM `%TABLE_PREFIX%ticket` A1 + INNER JOIN `%TABLE_PREFIX%user_email` A2 ON (A1.`email` = A2.`address`) + GROUP BY A2.`id`; + +-- - Now link the user and user_email tables +ALTER TABLE `%TABLE_PREFIX%user` ADD KEY `def_eml_id` (`default_email_id`, `id`); +UPDATE `%TABLE_PREFIX%user_email` A1 + SET user_id = ( + SELECT A2.`id` FROM `%TABLE_PREFIX%user` A2 + WHERE `default_email_id` = A1.`id`); +ALTER TABLE `%TABLE_PREFIX%user` DROP INDEX `def_eml_id`; + +-- - Update the ticket table +ALTER TABLE `%TABLE_PREFIX%ticket` ADD KEY `email_lookup` (`email`); +UPDATE `%TABLE_PREFIX%ticket` A1 + JOIN `%TABLE_PREFIX%user_email` A2 ON A2.`address` = A1.`email` + SET A1.`user_id` = A2.`user_id`, + A1.`user_email_id` = A2.`id`; +ALTER TABLE `%TABLE_PREFIX%ticket` DROP INDEX `email_lookup`; + +-- TODO: Move this to a client info dynamic entry +-- 4. Create form entries for each ticket +INSERT INTO `%TABLE_PREFIX%form_entry` ( + `form_id`, `object_id`, `object_type`, `sort`, `created`, `updated`) + SELECT DISTINCT A2.`id`, `user_id`, 'U', 10, MIN(A1.`created`), + MAX(A1.`updated`) + FROM `%TABLE_PREFIX%ticket` A1 + JOIN `%TABLE_PREFIX%form` A2 ON (A2.`type` = 'U') + GROUP BY `user_id`, A2.`id`; + +-- 5. Copy Phone from the ticket table to section #1 +INSERT INTO `%TABLE_PREFIX%form_entry_values` ( + `field_id`, `entry_id`, `value`) + SELECT A3.`id`, A2.`id`, MAX(CONCAT( + REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( + A1.`phone`, + ' ', ''), + ')', ''), + '(', ''), + '+', ''), + '-', ''), + '.', ''), 'X', A1.`phone_ext`)) + FROM `%TABLE_PREFIX%ticket` A1 + INNER JOIN `%TABLE_PREFIX%form` A4 ON (`type`='U') + INNER JOIN `%TABLE_PREFIX%form_entry` A2 ON (A2.`object_id` + = A1.`ticket_id` AND A2.`object_type` = 'U') + INNER JOIN `%TABLE_PREFIX%form_field` A3 ON (A2.`form_id` + = A4.`id`) + WHERE A3.`name` = 'phone' AND LENGTH(A1.`phone`) + GROUP BY A3.`id`, A2.`id`; + +-- 6. Remove columns from ticket table +ALTER TABLE `%TABLE_PREFIX%ticket` + DROP COLUMN `name`, + DROP COLUMN `email`, + DROP COLUMN `phone`, + DROP COLUMN `phone_ext`, + DROP COLUMN `subject`, + DROP COLUMN `priority_id`; + +-- 5. Cleanup ticket table with dropped varchar columns +OPTIMIZE TABLE `%TABLE_PREFIX%ticket`; diff --git a/include/upgrader/streams/core/dad45ca2-17f0cc96.patch.sql b/include/upgrader/streams/core/dad45ca2-17f0cc96.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..d6c7bd6304bb56cdaba8a4c38700ca5fcf9b7185 --- /dev/null +++ b/include/upgrader/streams/core/dad45ca2-17f0cc96.patch.sql @@ -0,0 +1,124 @@ +/** + * @version v1.8.0-dpr1 Dynamic Forms + * @signature 17f0cc96b366a747113622fa4780f9bc + * + * Adds the database structure for the dynamic forms feature and migrates + * the database from the legacy <=1.7 format to the new format with the + * dynamic forms feature. Basically, a default form is installed with the + * fields found in the legacy version of osTicket, the data is migrated from + * the fields in the ticket table to the new forms tables, and then the + * fields are dropped from the ticket table. + */ + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form`; +CREATE TABLE `%TABLE_PREFIX%form` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` char(1) NOT NULL DEFAULT 'G', + `deletable` tinyint(1) NOT NULL DEFAULT 1, + `title` varchar(255) NOT NULL, + `instructions` varchar(512), + `notes` text, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form_field`; +CREATE TABLE `%TABLE_PREFIX%form_field` ( + `id` int(11) unsigned NOT NULL auto_increment, + `form_id` int(11) unsigned NOT NULL, + `type` varchar(255) NOT NULL DEFAULT 'text', + `label` varchar(255) NOT NULL, + `required` tinyint(1) NOT NULL DEFAULT 0, + `private` tinyint(1) NOT NULL DEFAULT 0, + `edit_mask` tinyint(1) NOT NULL DEFAULT 0, + `name` varchar(64) NOT NULL, + `configuration` text, + `sort` int(11) unsigned NOT NULL, + `hint` varchar(512), + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form_entry`; +CREATE TABLE `%TABLE_PREFIX%form_entry` ( + `id` int(11) unsigned NOT NULL auto_increment, + `form_id` int(11) unsigned NOT NULL, + `object_id` int(11) unsigned, + `object_type` char(1) NOT NULL DEFAULT 'T', + `sort` int(11) unsigned NOT NULL DEFAULT 1, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `entry_lookup` (`object_id`, `object_type`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form_entry_values`; +CREATE TABLE `%TABLE_PREFIX%form_entry_values` ( + -- references form_entry.id + `entry_id` int(11) unsigned NOT NULL, + `field_id` int(11) unsigned NOT NULL, + `value` text, + `value_id` int(11), + PRIMARY KEY (`entry_id`, `field_id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%list`; +CREATE TABLE `%TABLE_PREFIX%list` ( + `id` int(11) unsigned NOT NULL auto_increment, + `name` varchar(255) NOT NULL, + `name_plural` varchar(255), + `sort_mode` enum('Alpha', '-Alpha', 'SortCol') NOT NULL DEFAULT 'Alpha', + `notes` text, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%list_items`; +CREATE TABLE `%TABLE_PREFIX%list_items` ( + `id` int(11) unsigned NOT NULL auto_increment, + `list_id` int(11), + `value` varchar(255) NOT NULL, + -- extra value such as abbreviation + `extra` varchar(255), + `sort` int(11) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `list_item_lookup` (`list_id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%user`; +CREATE TABLE `%TABLE_PREFIX%user` ( + `id` int(10) unsigned NOT NULL auto_increment, + `default_email_id` int(10) NOT NULL, + `name` varchar(128) NOT NULL, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%user_email`; +CREATE TABLE `%TABLE_PREFIX%user_email` ( + `id` int(10) unsigned NOT NULL auto_increment, + `user_id` int(10) unsigned NOT NULL, + `address` varchar(128) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `address` (`address`), + KEY `user_email_lookup` (`user_id`) +) DEFAULT CHARSET=utf8; + +ALTER TABLE `%TABLE_PREFIX%filter_rule` + CHANGE `what` `what` varchar(32) NOT NULL; + +ALTER TABLE `%TABLE_PREFIX%help_topic` + ADD `formset_id` int(11) unsigned NOT NULL default '0' AFTER `sla_id`; + +ALTER TABLE `%TABLE_PREFIX%ticket` + ADD `user_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ticket_id`, + ADD `user_email_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `user_id`; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = '17f0cc96b366a747113622fa4780f9bc' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/dad45ca2-17f0cc96.task.php b/include/upgrader/streams/core/dad45ca2-17f0cc96.task.php new file mode 100644 index 0000000000000000000000000000000000000000..98d128fbdaed3a1a4a57fe3af06cd0fbb99c591e --- /dev/null +++ b/include/upgrader/streams/core/dad45ca2-17f0cc96.task.php @@ -0,0 +1,21 @@ +<?php + +/* + * Loads the initial data for dynamic forms into the system. This is + * preferred over providing the data inside the SQL scripts + */ + +class DynamicFormLoader extends MigrationTask { + var $description = "Loading initial data for dynamic forms"; + + function run($max_time) { + $i18n = new Internationalization('en_US'); + $forms = $i18n->getTemplate('form.yaml')->getData(); + foreach ($forms as $f) + DynamicForm::create($f); + } +} + +return 'DynamicFormLoader'; + +?> diff --git a/js/jquery-ui-1.10.3.custom.min.js b/js/jquery-ui-1.10.3.custom.min.js index 8d62da712ce1181ff7030358326918325a3dc5db..bdd65eebc8cf831258c641abba61ff9bb1a7cb26 100755 --- a/js/jquery-ui-1.10.3.custom.min.js +++ b/js/jquery-ui-1.10.3.custom.min.js @@ -1,7 +1,7 @@ -/*! jQuery UI - v1.10.3 - 2013-08-24 +/*! jQuery UI - v1.10.3 - 2013-10-08 * http://jqueryui.com -* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.datepicker.js -* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */ +* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.menu.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js, jquery.ui.effect.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js +* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ -(function(e,t){function i(t,i){var a,n,r,o=t.nodeName.toLowerCase();return"area"===o?(a=t.parentNode,n=a.name,t.href&&n&&"map"===a.nodeName.toLowerCase()?(r=e("img[usemap=#"+n+"]")[0],!!r&&s(r)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:"a"===o?t.href||i:i)&&s(t)}function s(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var a=0,n=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.3",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var s,a,n=e(this[0]);n.length&&n[0]!==document;){if(s=n.css("position"),("absolute"===s||"relative"===s||"fixed"===s)&&(a=parseInt(n.css("zIndex"),10),!isNaN(a)&&0!==a))return a;n=n.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++a)})},removeUniqueId:function(){return this.each(function(){n.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var s=e.attr(t,"tabindex"),a=isNaN(s);return(a||s>=0)&&i(t,!a)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(i,s){function a(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===s?["Left","Right"]:["Top","Bottom"],r=s.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+s]=function(i){return i===t?o["inner"+s].call(this):this.each(function(){e(this).css(r,a(this,i)+"px")})},e.fn["outer"+s]=function(t,i){return"number"!=typeof t?o["outer"+s].call(this,t):this.each(function(){e(this).css(r,a(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,s){var a,n=e.ui[t].prototype;for(a in s)n.plugins[a]=n.plugins[a]||[],n.plugins[a].push([i,s[a]])},call:function(e,t,i){var s,a=e.plugins[t];if(a&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(s=0;a.length>s;s++)e.options[a[s][0]]&&a[s][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",a=!1;return t[s]>0?!0:(t[s]=1,a=t[s]>0,t[s]=0,a)}})})(jQuery);(function(e,t){var i=0,s=Array.prototype.slice,n=e.cleanData;e.cleanData=function(t){for(var i,s=0;null!=(i=t[s]);s++)try{e(i).triggerHandler("remove")}catch(a){}n(t)},e.widget=function(i,s,n){var a,r,o,h,l={},u=i.split(".")[0];i=i.split(".")[1],a=u+"-"+i,n||(n=s,s=e.Widget),e.expr[":"][a.toLowerCase()]=function(t){return!!e.data(t,a)},e[u]=e[u]||{},r=e[u][i],o=e[u][i]=function(e,i){return this._createWidget?(arguments.length&&this._createWidget(e,i),t):new o(e,i)},e.extend(o,r,{version:n.version,_proto:e.extend({},n),_childConstructors:[]}),h=new s,h.options=e.widget.extend({},h.options),e.each(n,function(i,n){return e.isFunction(n)?(l[i]=function(){var e=function(){return s.prototype[i].apply(this,arguments)},t=function(e){return s.prototype[i].apply(this,e)};return function(){var i,s=this._super,a=this._superApply;return this._super=e,this._superApply=t,i=n.apply(this,arguments),this._super=s,this._superApply=a,i}}(),t):(l[i]=n,t)}),o.prototype=e.widget.extend(h,{widgetEventPrefix:r?h.widgetEventPrefix:i},l,{constructor:o,namespace:u,widgetName:i,widgetFullName:a}),r?(e.each(r._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete r._childConstructors):s._childConstructors.push(o),e.widget.bridge(i,o)},e.widget.extend=function(i){for(var n,a,r=s.call(arguments,1),o=0,h=r.length;h>o;o++)for(n in r[o])a=r[o][n],r[o].hasOwnProperty(n)&&a!==t&&(i[n]=e.isPlainObject(a)?e.isPlainObject(i[n])?e.widget.extend({},i[n],a):e.widget.extend({},a):a);return i},e.widget.bridge=function(i,n){var a=n.prototype.widgetFullName||i;e.fn[i]=function(r){var o="string"==typeof r,h=s.call(arguments,1),l=this;return r=!o&&h.length?e.widget.extend.apply(null,[r].concat(h)):r,o?this.each(function(){var s,n=e.data(this,a);return n?e.isFunction(n[r])&&"_"!==r.charAt(0)?(s=n[r].apply(n,h),s!==n&&s!==t?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):t):e.error("no such method '"+r+"' for "+i+" widget instance"):e.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,a);t?t.option(r||{})._init():e.data(this,a,new n(r,this))}),l}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(i,s){var n,a,r,o=i;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof i)if(o={},n=i.split("."),i=n.shift(),n.length){for(a=o[i]=e.widget.extend({},this.options[i]),r=0;n.length-1>r;r++)a[n[r]]=a[n[r]]||{},a=a[n[r]];if(i=n.pop(),s===t)return a[i]===t?null:a[i];a[i]=s}else{if(s===t)return this.options[i]===t?null:this.options[i];o[i]=s}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!t).attr("aria-disabled",t),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var a,r=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=a=e(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,a=this.widget()),e.each(n,function(n,o){function h(){return i||r.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?r[o]:o).apply(r,arguments):t}"string"!=typeof o&&(h.guid=o.guid=o.guid||h.guid||e.guid++);var l=n.match(/^(\w+)\s*(.*)$/),u=l[1]+r.eventNamespace,c=l[2];c?a.delegate(c,u,h):s.bind(u,h)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,r=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(r)&&r.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var r,o=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),r=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),r&&e.effects&&e.effects.effect[o]?s[t](n):o!==t&&s[o]?s[o](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}})})(jQuery);(function(e){var t=!1;e(document).mouseup(function(){t=!1}),e.widget("ui.mouse",{version:"1.10.3",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!t){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?e(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===e.data(i.target,this.widgetName+".preventClickEvent")&&e.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return s._mouseMove(e)},this._mouseUpDelegate=function(e){return s._mouseUp(e)},e(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),t=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return e(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(e){e.widget("ui.draggable",e.ui.mouse,{version:"1.10.3",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(t){var i=this.options;return this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(e(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){e("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var i=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",t,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var i=this,s=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(s=e.ui.ddmanager.drop(this,t)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||e.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",t)!==!1&&i._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1):!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(t){var i=this.options,s=e.isFunction(i.helper)?e(i.helper.apply(this.element[0],[t])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.element.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,e(document).width()-this.helperProportions.width-this.margins.left,(e(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=e(n.containment),s=i[0],s&&(t="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(t?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(t,i){i||(i=this.position);var s="absolute"===t?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(t){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=t.pageX,l=t.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left<i[0]&&(h=i[0]+this.offset.click.left),t.pageY-this.offset.click.top<i[1]&&(l=i[1]+this.offset.click.top),t.pageX-this.offset.click.left>i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(t,i,s){return s=s||this._uiHash(),e.ui.plugin.call(this,t,[i,s]),"drag"===t&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,i){var s=e(this).data("ui-draggable"),n=s.options,a=e.extend({},i,{item:s.element});s.sortables=[],e(n.connectToSortable).each(function(){var i=e.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",t,a))})},stop:function(t,i){var s=e(this).data("ui-draggable"),n=e.extend({},i,{item:s.element});e.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,n))})},drag:function(t,i){var s=e(this).data("ui-draggable"),n=this;e.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,e.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&e.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",t),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",t),s.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(){var t=e("body"),i=e(this).data("ui-draggable").options;t.css("cursor")&&(i._cursor=t.css("cursor")),t.css("cursor",i.cursor)},stop:function(){var t=e(this).data("ui-draggable").options;t._cursor&&e("body").css("cursor",t._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,i){var s=e(i.helper),n=e(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(t,i){var s=e(this).data("ui-draggable").options;s._opacity&&e(i.helper).css("opacity",s._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(){var t=e(this).data("ui-draggable");t.scrollParent[0]!==document&&"HTML"!==t.scrollParent[0].tagName&&(t.overflowOffset=t.scrollParent.offset())},drag:function(t){var i=e(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-t.pageY<s.scrollSensitivity?i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop+s.scrollSpeed:t.pageY-i.overflowOffset.top<s.scrollSensitivity&&(i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop-s.scrollSpeed)),s.axis&&"y"===s.axis||(i.overflowOffset.left+i.scrollParent[0].offsetWidth-t.pageX<s.scrollSensitivity?i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft+s.scrollSpeed:t.pageX-i.overflowOffset.left<s.scrollSensitivity&&(i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft-s.scrollSpeed))):(s.axis&&"x"===s.axis||(t.pageY-e(document).scrollTop()<s.scrollSensitivity?n=e(document).scrollTop(e(document).scrollTop()-s.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<s.scrollSensitivity&&(n=e(document).scrollTop(e(document).scrollTop()+s.scrollSpeed))),s.axis&&"y"===s.axis||(t.pageX-e(document).scrollLeft()<s.scrollSensitivity?n=e(document).scrollLeft(e(document).scrollLeft()-s.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<s.scrollSensitivity&&(n=e(document).scrollLeft(e(document).scrollLeft()+s.scrollSpeed)))),n!==!1&&e.ui.ddmanager&&!s.dropBehaviour&&e.ui.ddmanager.prepareOffsets(i,t)}}),e.ui.plugin.add("draggable","snap",{start:function(){var t=e(this).data("ui-draggable"),i=t.options;t.snapElements=[],e(i.snap.constructor!==String?i.snap.items||":data(ui-draggable)":i.snap).each(function(){var i=e(this),s=i.offset();this!==t.element[0]&&t.snapElements.push({item:this,width:i.outerWidth(),height:i.outerHeight(),top:s.top,left:s.left})})},drag:function(t,i){var s,n,a,o,r,h,l,u,c,d,p=e(this).data("ui-draggable"),f=p.options,m=f.snapTolerance,g=i.offset.left,v=g+p.helperProportions.width,b=i.offset.top,y=b+p.helperProportions.height;for(c=p.snapElements.length-1;c>=0;c--)r=p.snapElements[c].left,h=r+p.snapElements[c].width,l=p.snapElements[c].top,u=l+p.snapElements[c].height,r-m>v||g>h+m||l-m>y||b>u+m||!e.contains(p.snapElements[c].item.ownerDocument,p.snapElements[c].item)?(p.snapElements[c].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,t,e.extend(p._uiHash(),{snapItem:p.snapElements[c].item})),p.snapElements[c].snapping=!1):("inner"!==f.snapMode&&(s=m>=Math.abs(l-y),n=m>=Math.abs(u-b),a=m>=Math.abs(r-v),o=m>=Math.abs(h-g),s&&(i.position.top=p._convertPositionTo("relative",{top:l-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:u,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:h}).left-p.margins.left)),d=s||n||a||o,"outer"!==f.snapMode&&(s=m>=Math.abs(l-b),n=m>=Math.abs(u-y),a=m>=Math.abs(r-g),o=m>=Math.abs(h-v),s&&(i.position.top=p._convertPositionTo("relative",{top:l,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:u-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:h-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[c].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,t,e.extend(p._uiHash(),{snapItem:p.snapElements[c].item})),p.snapElements[c].snapping=s||n||a||o||d)}}),e.ui.plugin.add("draggable","stack",{start:function(){var t,i=this.data("ui-draggable").options,s=e.makeArray(e(i.stack)).sort(function(t,i){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(i).css("zIndex"),10)||0)});s.length&&(t=parseInt(e(s[0]).css("zIndex"),10)||0,e(s).each(function(i){e(this).css("zIndex",t+i)}),this.css("zIndex",t+s.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,i){var s=e(i.helper),n=e(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(t,i){var s=e(this).data("ui-draggable").options;s._zIndex&&e(i.helper).css("zIndex",s._zIndex)}})})(jQuery);(function(t,e){function i(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},t.extend(this._defaults,this.regional[""]),this.dpDiv=s(t("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function s(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.delegate(i,"mouseout",function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",function(){t.datepicker._isDisabledDatepicker(a.inline?e.parent()[0]:a.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))})}function n(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}t.extend(t.ui,{datepicker:{version:"1.10.3"}});var a,r="datepicker";t.extend(i.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(t){return n(this._defaults,t||{}),this},_attachDatepicker:function(e,i){var s,n,a;s=e.nodeName.toLowerCase(),n="div"===s||"span"===s,e.id||(this.uuid+=1,e.id="dp"+this.uuid),a=this._newInst(t(e),n),a.settings=t.extend({},i||{}),"input"===s?this._connectDatepicker(e,a):n&&this._inlineDatepicker(e,a)},_newInst:function(e,i){var n=e[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:n,input:e,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?s(t("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(e,i){var s=t(e);i.append=t([]),i.trigger=t([]),s.hasClass(this.markerClassName)||(this._attachments(s,i),s.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),t.data(e,r,i),i.settings.disabled&&this._disableDatepicker(e))},_attachments:function(e,i){var s,n,a,r=this._get(i,"appendText"),o=this._get(i,"isRTL");i.append&&i.append.remove(),r&&(i.append=t("<span class='"+this._appendClass+"'>"+r+"</span>"),e[o?"before":"after"](i.append)),e.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),s=this._get(i,"showOn"),("focus"===s||"both"===s)&&e.focus(this._showDatepicker),("button"===s||"both"===s)&&(n=this._get(i,"buttonText"),a=this._get(i,"buttonImage"),i.trigger=t(this._get(i,"buttonImageOnly")?t("<img/>").addClass(this._triggerClass).attr({src:a,alt:n,title:n}):t("<button type='button'></button>").addClass(this._triggerClass).html(a?t("<img/>").attr({src:a,alt:n,title:n}):n)),e[o?"before":"after"](i.trigger),i.trigger.click(function(){return t.datepicker._datepickerShowing&&t.datepicker._lastInput===e[0]?t.datepicker._hideDatepicker():t.datepicker._datepickerShowing&&t.datepicker._lastInput!==e[0]?(t.datepicker._hideDatepicker(),t.datepicker._showDatepicker(e[0])):t.datepicker._showDatepicker(e[0]),!1}))},_autoSize:function(t){if(this._get(t,"autoSize")&&!t.inline){var e,i,s,n,a=new Date(2009,11,20),r=this._get(t,"dateFormat");r.match(/[DM]/)&&(e=function(t){for(i=0,s=0,n=0;t.length>n;n++)t[n].length>i&&(i=t[n].length,s=n);return s},a.setMonth(e(this._get(t,r.match(/MM/)?"monthNames":"monthNamesShort"))),a.setDate(e(this._get(t,r.match(/DD/)?"dayNames":"dayNamesShort"))+20-a.getDay())),t.input.attr("size",this._formatDate(t,a).length)}},_inlineDatepicker:function(e,i){var s=t(e);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),t.data(e,r,i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(e),i.dpDiv.css("display","block"))},_dialogDatepicker:function(e,i,s,a,o){var h,l,c,u,d,p=this._dialogInst;return p||(this.uuid+=1,h="dp"+this.uuid,this._dialogInput=t("<input type='text' id='"+h+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),t("body").append(this._dialogInput),p=this._dialogInst=this._newInst(this._dialogInput,!1),p.settings={},t.data(this._dialogInput[0],r,p)),n(p.settings,a||{}),i=i&&i.constructor===Date?this._formatDate(p,i):i,this._dialogInput.val(i),this._pos=o?o.length?o:[o.pageX,o.pageY]:null,this._pos||(l=document.documentElement.clientWidth,c=document.documentElement.clientHeight,u=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[l/2-100+u,c/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),p.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),t.blockUI&&t.blockUI(this.dpDiv),t.data(this._dialogInput[0],r,p),this},_destroyDatepicker:function(e){var i,s=t(e),n=t.data(e,r);s.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),t.removeData(e,r),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty())},_enableDatepicker:function(e){var i,s,n=t(e),a=t.data(e,r);n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!1,a.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}))},_disableDatepicker:function(e){var i,s,n=t(e),a=t.data(e,r);n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!0,a.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}),this._disabledInputs[this._disabledInputs.length]=e)},_isDisabledDatepicker:function(t){if(!t)return!1;for(var e=0;this._disabledInputs.length>e;e++)if(this._disabledInputs[e]===t)return!0;return!1},_getInst:function(e){try{return t.data(e,r)}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(i,s,a){var r,o,h,l,c=this._getInst(i);return 2===arguments.length&&"string"==typeof s?"defaults"===s?t.extend({},t.datepicker._defaults):c?"all"===s?t.extend({},c.settings):this._get(c,s):null:(r=s||{},"string"==typeof s&&(r={},r[s]=a),c&&(this._curInst===c&&this._hideDatepicker(),o=this._getDateDatepicker(i,!0),h=this._getMinMaxDate(c,"min"),l=this._getMinMaxDate(c,"max"),n(c.settings,r),null!==h&&r.dateFormat!==e&&r.minDate===e&&(c.settings.minDate=this._formatDate(c,h)),null!==l&&r.dateFormat!==e&&r.maxDate===e&&(c.settings.maxDate=this._formatDate(c,l)),"disabled"in r&&(r.disabled?this._disableDatepicker(i):this._enableDatepicker(i)),this._attachments(t(i),c),this._autoSize(c),this._setDate(c,o),this._updateAlternate(c),this._updateDatepicker(c)),e)},_changeDatepicker:function(t,e,i){this._optionDatepicker(t,e,i)},_refreshDatepicker:function(t){var e=this._getInst(t);e&&this._updateDatepicker(e)},_setDateDatepicker:function(t,e){var i=this._getInst(t);i&&(this._setDate(i,e),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(t,e){var i=this._getInst(t);return i&&!i.inline&&this._setDateFromField(i,e),i?this._getDate(i):null},_doKeyDown:function(e){var i,s,n,a=t.datepicker._getInst(e.target),r=!0,o=a.dpDiv.is(".ui-datepicker-rtl");if(a._keyEvent=!0,t.datepicker._datepickerShowing)switch(e.keyCode){case 9:t.datepicker._hideDatepicker(),r=!1;break;case 13:return n=t("td."+t.datepicker._dayOverClass+":not(."+t.datepicker._currentClass+")",a.dpDiv),n[0]&&t.datepicker._selectDay(e.target,a.selectedMonth,a.selectedYear,n[0]),i=t.datepicker._get(a,"onSelect"),i?(s=t.datepicker._formatDate(a),i.apply(a.input?a.input[0]:null,[s,a])):t.datepicker._hideDatepicker(),!1;case 27:t.datepicker._hideDatepicker();break;case 33:t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(a,"stepBigMonths"):-t.datepicker._get(a,"stepMonths"),"M");break;case 34:t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(a,"stepBigMonths"):+t.datepicker._get(a,"stepMonths"),"M");break;case 35:(e.ctrlKey||e.metaKey)&&t.datepicker._clearDate(e.target),r=e.ctrlKey||e.metaKey;break;case 36:(e.ctrlKey||e.metaKey)&&t.datepicker._gotoToday(e.target),r=e.ctrlKey||e.metaKey;break;case 37:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,o?1:-1,"D"),r=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(a,"stepBigMonths"):-t.datepicker._get(a,"stepMonths"),"M");break;case 38:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,-7,"D"),r=e.ctrlKey||e.metaKey;break;case 39:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,o?-1:1,"D"),r=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(a,"stepBigMonths"):+t.datepicker._get(a,"stepMonths"),"M");break;case 40:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,7,"D"),r=e.ctrlKey||e.metaKey;break;default:r=!1}else 36===e.keyCode&&e.ctrlKey?t.datepicker._showDatepicker(this):r=!1;r&&(e.preventDefault(),e.stopPropagation())},_doKeyPress:function(i){var s,n,a=t.datepicker._getInst(i.target);return t.datepicker._get(a,"constrainInput")?(s=t.datepicker._possibleChars(t.datepicker._get(a,"dateFormat")),n=String.fromCharCode(null==i.charCode?i.keyCode:i.charCode),i.ctrlKey||i.metaKey||" ">n||!s||s.indexOf(n)>-1):e},_doKeyUp:function(e){var i,s=t.datepicker._getInst(e.target);if(s.input.val()!==s.lastVal)try{i=t.datepicker.parseDate(t.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,t.datepicker._getFormatConfig(s)),i&&(t.datepicker._setDateFromField(s),t.datepicker._updateAlternate(s),t.datepicker._updateDatepicker(s))}catch(n){}return!0},_showDatepicker:function(e){if(e=e.target||e,"input"!==e.nodeName.toLowerCase()&&(e=t("input",e.parentNode)[0]),!t.datepicker._isDisabledDatepicker(e)&&t.datepicker._lastInput!==e){var i,s,a,r,o,h,l;i=t.datepicker._getInst(e),t.datepicker._curInst&&t.datepicker._curInst!==i&&(t.datepicker._curInst.dpDiv.stop(!0,!0),i&&t.datepicker._datepickerShowing&&t.datepicker._hideDatepicker(t.datepicker._curInst.input[0])),s=t.datepicker._get(i,"beforeShow"),a=s?s.apply(e,[e,i]):{},a!==!1&&(n(i.settings,a),i.lastVal=null,t.datepicker._lastInput=e,t.datepicker._setDateFromField(i),t.datepicker._inDialog&&(e.value=""),t.datepicker._pos||(t.datepicker._pos=t.datepicker._findPos(e),t.datepicker._pos[1]+=e.offsetHeight),r=!1,t(e).parents().each(function(){return r|="fixed"===t(this).css("position"),!r}),o={left:t.datepicker._pos[0],top:t.datepicker._pos[1]},t.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),t.datepicker._updateDatepicker(i),o=t.datepicker._checkOffset(i,o,r),i.dpDiv.css({position:t.datepicker._inDialog&&t.blockUI?"static":r?"fixed":"absolute",display:"none",left:o.left+"px",top:o.top+"px"}),i.inline||(h=t.datepicker._get(i,"showAnim"),l=t.datepicker._get(i,"duration"),i.dpDiv.zIndex(t(e).zIndex()+1),t.datepicker._datepickerShowing=!0,t.effects&&t.effects.effect[h]?i.dpDiv.show(h,t.datepicker._get(i,"showOptions"),l):i.dpDiv[h||"show"](h?l:null),t.datepicker._shouldFocusInput(i)&&i.input.focus(),t.datepicker._curInst=i))}},_updateDatepicker:function(e){this.maxRows=4,a=e,e.dpDiv.empty().append(this._generateHTML(e)),this._attachHandlers(e),e.dpDiv.find("."+this._dayOverClass+" a").mouseover();var i,s=this._getNumberOfMonths(e),n=s[1],r=17;e.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),n>1&&e.dpDiv.addClass("ui-datepicker-multi-"+n).css("width",r*n+"em"),e.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),e.dpDiv[(this._get(e,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),e===t.datepicker._curInst&&t.datepicker._datepickerShowing&&t.datepicker._shouldFocusInput(e)&&e.input.focus(),e.yearshtml&&(i=e.yearshtml,setTimeout(function(){i===e.yearshtml&&e.yearshtml&&e.dpDiv.find("select.ui-datepicker-year:first").replaceWith(e.yearshtml),i=e.yearshtml=null},0))},_shouldFocusInput:function(t){return t.input&&t.input.is(":visible")&&!t.input.is(":disabled")&&!t.input.is(":focus")},_checkOffset:function(e,i,s){var n=e.dpDiv.outerWidth(),a=e.dpDiv.outerHeight(),r=e.input?e.input.outerWidth():0,o=e.input?e.input.outerHeight():0,h=document.documentElement.clientWidth+(s?0:t(document).scrollLeft()),l=document.documentElement.clientHeight+(s?0:t(document).scrollTop());return i.left-=this._get(e,"isRTL")?n-r:0,i.left-=s&&i.left===e.input.offset().left?t(document).scrollLeft():0,i.top-=s&&i.top===e.input.offset().top+o?t(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>h&&h>n?Math.abs(i.left+n-h):0),i.top-=Math.min(i.top,i.top+a>l&&l>a?Math.abs(a+o):0),i},_findPos:function(e){for(var i,s=this._getInst(e),n=this._get(s,"isRTL");e&&("hidden"===e.type||1!==e.nodeType||t.expr.filters.hidden(e));)e=e[n?"previousSibling":"nextSibling"];return i=t(e).offset(),[i.left,i.top]},_hideDatepicker:function(e){var i,s,n,a,o=this._curInst;!o||e&&o!==t.data(e,r)||this._datepickerShowing&&(i=this._get(o,"showAnim"),s=this._get(o,"duration"),n=function(){t.datepicker._tidyDialog(o)},t.effects&&(t.effects.effect[i]||t.effects[i])?o.dpDiv.hide(i,t.datepicker._get(o,"showOptions"),s,n):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,a=this._get(o,"onClose"),a&&a.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),t.blockUI&&(t.unblockUI(),t("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(t){t.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(e){if(t.datepicker._curInst){var i=t(e.target),s=t.datepicker._getInst(i[0]);(i[0].id!==t.datepicker._mainDivId&&0===i.parents("#"+t.datepicker._mainDivId).length&&!i.hasClass(t.datepicker.markerClassName)&&!i.closest("."+t.datepicker._triggerClass).length&&t.datepicker._datepickerShowing&&(!t.datepicker._inDialog||!t.blockUI)||i.hasClass(t.datepicker.markerClassName)&&t.datepicker._curInst!==s)&&t.datepicker._hideDatepicker()}},_adjustDate:function(e,i,s){var n=t(e),a=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(a,i+("M"===s?this._get(a,"showCurrentAtPos"):0),s),this._updateDatepicker(a))},_gotoToday:function(e){var i,s=t(e),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(e,i,s){var n=t(e),a=this._getInst(n[0]);a["selected"+("M"===s?"Month":"Year")]=a["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(a),this._adjustDate(n)},_selectDay:function(e,i,s,n){var a,r=t(e);t(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(r[0])||(a=this._getInst(r[0]),a.selectedDay=a.currentDay=t("a",n).html(),a.selectedMonth=a.currentMonth=i,a.selectedYear=a.currentYear=s,this._selectDate(e,this._formatDate(a,a.currentDay,a.currentMonth,a.currentYear)))},_clearDate:function(e){var i=t(e);this._selectDate(i,"")},_selectDate:function(e,i){var s,n=t(e),a=this._getInst(n[0]);i=null!=i?i:this._formatDate(a),a.input&&a.input.val(i),this._updateAlternate(a),s=this._get(a,"onSelect"),s?s.apply(a.input?a.input[0]:null,[i,a]):a.input&&a.input.trigger("change"),a.inline?this._updateDatepicker(a):(this._hideDatepicker(),this._lastInput=a.input[0],"object"!=typeof a.input[0]&&a.input.focus(),this._lastInput=null)},_updateAlternate:function(e){var i,s,n,a=this._get(e,"altField");a&&(i=this._get(e,"altFormat")||this._get(e,"dateFormat"),s=this._getDate(e),n=this.formatDate(i,s,this._getFormatConfig(e)),t(a).each(function(){t(this).val(n)}))},noWeekends:function(t){var e=t.getDay();return[e>0&&6>e,""]},iso8601Week:function(t){var e,i=new Date(t.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),e=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((e-i)/864e5)/7)+1},parseDate:function(i,s,n){if(null==i||null==s)throw"Invalid arguments";if(s="object"==typeof s?""+s:s+"",""===s)return null;var a,r,o,h,l=0,c=(n?n.shortYearCutoff:null)||this._defaults.shortYearCutoff,u="string"!=typeof c?c:(new Date).getFullYear()%100+parseInt(c,10),d=(n?n.dayNamesShort:null)||this._defaults.dayNamesShort,p=(n?n.dayNames:null)||this._defaults.dayNames,f=(n?n.monthNamesShort:null)||this._defaults.monthNamesShort,m=(n?n.monthNames:null)||this._defaults.monthNames,g=-1,v=-1,_=-1,b=-1,y=!1,x=function(t){var e=i.length>a+1&&i.charAt(a+1)===t;return e&&a++,e},k=function(t){var e=x(t),i="@"===t?14:"!"===t?20:"y"===t&&e?4:"o"===t?3:2,n=RegExp("^\\d{1,"+i+"}"),a=s.substring(l).match(n);if(!a)throw"Missing number at position "+l;return l+=a[0].length,parseInt(a[0],10)},w=function(i,n,a){var r=-1,o=t.map(x(i)?a:n,function(t,e){return[[e,t]]}).sort(function(t,e){return-(t[1].length-e[1].length)});if(t.each(o,function(t,i){var n=i[1];return s.substr(l,n.length).toLowerCase()===n.toLowerCase()?(r=i[0],l+=n.length,!1):e}),-1!==r)return r+1;throw"Unknown name at position "+l},D=function(){if(s.charAt(l)!==i.charAt(a))throw"Unexpected literal at position "+l;l++};for(a=0;i.length>a;a++)if(y)"'"!==i.charAt(a)||x("'")?D():y=!1;else switch(i.charAt(a)){case"d":_=k("d");break;case"D":w("D",d,p);break;case"o":b=k("o");break;case"m":v=k("m");break;case"M":v=w("M",f,m);break;case"y":g=k("y");break;case"@":h=new Date(k("@")),g=h.getFullYear(),v=h.getMonth()+1,_=h.getDate();break;case"!":h=new Date((k("!")-this._ticksTo1970)/1e4),g=h.getFullYear(),v=h.getMonth()+1,_=h.getDate();break;case"'":x("'")?D():y=!0;break;default:D()}if(s.length>l&&(o=s.substr(l),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===g?g=(new Date).getFullYear():100>g&&(g+=(new Date).getFullYear()-(new Date).getFullYear()%100+(u>=g?0:-100)),b>-1)for(v=1,_=b;;){if(r=this._getDaysInMonth(g,v-1),r>=_)break;v++,_-=r}if(h=this._daylightSavingAdjust(new Date(g,v-1,_)),h.getFullYear()!==g||h.getMonth()+1!==v||h.getDate()!==_)throw"Invalid date";return h},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(t,e,i){if(!e)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,a=(i?i.dayNames:null)||this._defaults.dayNames,r=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,o=(i?i.monthNames:null)||this._defaults.monthNames,h=function(e){var i=t.length>s+1&&t.charAt(s+1)===e;return i&&s++,i},l=function(t,e,i){var s=""+e;if(h(t))for(;i>s.length;)s="0"+s;return s},c=function(t,e,i,s){return h(t)?s[e]:i[e]},u="",d=!1;if(e)for(s=0;t.length>s;s++)if(d)"'"!==t.charAt(s)||h("'")?u+=t.charAt(s):d=!1;else switch(t.charAt(s)){case"d":u+=l("d",e.getDate(),2);break;case"D":u+=c("D",e.getDay(),n,a);break;case"o":u+=l("o",Math.round((new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()-new Date(e.getFullYear(),0,0).getTime())/864e5),3);break;case"m":u+=l("m",e.getMonth()+1,2);break;case"M":u+=c("M",e.getMonth(),r,o);break;case"y":u+=h("y")?e.getFullYear():(10>e.getYear()%100?"0":"")+e.getYear()%100;break;case"@":u+=e.getTime();break;case"!":u+=1e4*e.getTime()+this._ticksTo1970;break;case"'":h("'")?u+="'":d=!0;break;default:u+=t.charAt(s)}return u},_possibleChars:function(t){var e,i="",s=!1,n=function(i){var s=t.length>e+1&&t.charAt(e+1)===i;return s&&e++,s};for(e=0;t.length>e;e++)if(s)"'"!==t.charAt(e)||n("'")?i+=t.charAt(e):s=!1;else switch(t.charAt(e)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=t.charAt(e)}return i},_get:function(t,i){return t.settings[i]!==e?t.settings[i]:this._defaults[i]},_setDateFromField:function(t,e){if(t.input.val()!==t.lastVal){var i=this._get(t,"dateFormat"),s=t.lastVal=t.input?t.input.val():null,n=this._getDefaultDate(t),a=n,r=this._getFormatConfig(t);try{a=this.parseDate(i,s,r)||n}catch(o){s=e?"":s}t.selectedDay=a.getDate(),t.drawMonth=t.selectedMonth=a.getMonth(),t.drawYear=t.selectedYear=a.getFullYear(),t.currentDay=s?a.getDate():0,t.currentMonth=s?a.getMonth():0,t.currentYear=s?a.getFullYear():0,this._adjustInstDate(t)}},_getDefaultDate:function(t){return this._restrictMinMax(t,this._determineDate(t,this._get(t,"defaultDate"),new Date))},_determineDate:function(e,i,s){var n=function(t){var e=new Date;return e.setDate(e.getDate()+t),e},a=function(i){try{return t.datepicker.parseDate(t.datepicker._get(e,"dateFormat"),i,t.datepicker._getFormatConfig(e))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?t.datepicker._getDate(e):null)||new Date,a=n.getFullYear(),r=n.getMonth(),o=n.getDate(),h=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,l=h.exec(i);l;){switch(l[2]||"d"){case"d":case"D":o+=parseInt(l[1],10);break;case"w":case"W":o+=7*parseInt(l[1],10);break;case"m":case"M":r+=parseInt(l[1],10),o=Math.min(o,t.datepicker._getDaysInMonth(a,r));break;case"y":case"Y":a+=parseInt(l[1],10),o=Math.min(o,t.datepicker._getDaysInMonth(a,r))}l=h.exec(i)}return new Date(a,r,o)},r=null==i||""===i?s:"string"==typeof i?a(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return r=r&&"Invalid Date"==""+r?s:r,r&&(r.setHours(0),r.setMinutes(0),r.setSeconds(0),r.setMilliseconds(0)),this._daylightSavingAdjust(r)},_daylightSavingAdjust:function(t){return t?(t.setHours(t.getHours()>12?t.getHours()+2:0),t):null},_setDate:function(t,e,i){var s=!e,n=t.selectedMonth,a=t.selectedYear,r=this._restrictMinMax(t,this._determineDate(t,e,new Date));t.selectedDay=t.currentDay=r.getDate(),t.drawMonth=t.selectedMonth=t.currentMonth=r.getMonth(),t.drawYear=t.selectedYear=t.currentYear=r.getFullYear(),n===t.selectedMonth&&a===t.selectedYear||i||this._notifyChange(t),this._adjustInstDate(t),t.input&&t.input.val(s?"":this._formatDate(t))},_getDate:function(t){var e=!t.currentYear||t.input&&""===t.input.val()?null:this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return e},_attachHandlers:function(e){var i=this._get(e,"stepMonths"),s="#"+e.id.replace(/\\\\/g,"\\");e.dpDiv.find("[data-handler]").map(function(){var e={prev:function(){t.datepicker._adjustDate(s,-i,"M")},next:function(){t.datepicker._adjustDate(s,+i,"M")},hide:function(){t.datepicker._hideDatepicker()},today:function(){t.datepicker._gotoToday(s)},selectDay:function(){return t.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return t.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return t.datepicker._selectMonthYear(s,this,"Y"),!1}};t(this).bind(this.getAttribute("data-event"),e[this.getAttribute("data-handler")])})},_generateHTML:function(t){var e,i,s,n,a,r,o,h,l,c,u,d,p,f,m,g,v,_,b,y,x,k,w,D,T,C,M,S,N,I,P,A,z,H,E,F,O,W,j,R=new Date,L=this._daylightSavingAdjust(new Date(R.getFullYear(),R.getMonth(),R.getDate())),Y=this._get(t,"isRTL"),B=this._get(t,"showButtonPanel"),J=this._get(t,"hideIfNoPrevNext"),K=this._get(t,"navigationAsDateFormat"),Q=this._getNumberOfMonths(t),V=this._get(t,"showCurrentAtPos"),U=this._get(t,"stepMonths"),q=1!==Q[0]||1!==Q[1],X=this._daylightSavingAdjust(t.currentDay?new Date(t.currentYear,t.currentMonth,t.currentDay):new Date(9999,9,9)),G=this._getMinMaxDate(t,"min"),$=this._getMinMaxDate(t,"max"),Z=t.drawMonth-V,te=t.drawYear;if(0>Z&&(Z+=12,te--),$)for(e=this._daylightSavingAdjust(new Date($.getFullYear(),$.getMonth()-Q[0]*Q[1]+1,$.getDate())),e=G&&G>e?G:e;this._daylightSavingAdjust(new Date(te,Z,1))>e;)Z--,0>Z&&(Z=11,te--);for(t.drawMonth=Z,t.drawYear=te,i=this._get(t,"prevText"),i=K?this.formatDate(i,this._daylightSavingAdjust(new Date(te,Z-U,1)),this._getFormatConfig(t)):i,s=this._canAdjustMonth(t,-1,te,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>":J?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"e":"w")+"'>"+i+"</span></a>",n=this._get(t,"nextText"),n=K?this.formatDate(n,this._daylightSavingAdjust(new Date(te,Z+U,1)),this._getFormatConfig(t)):n,a=this._canAdjustMonth(t,1,te,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>":J?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(Y?"w":"e")+"'>"+n+"</span></a>",r=this._get(t,"currentText"),o=this._get(t,"gotoCurrent")&&t.currentDay?X:L,r=K?this.formatDate(r,o,this._getFormatConfig(t)):r,h=t.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(t,"closeText")+"</button>",l=B?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(Y?h:"")+(this._isInRange(t,o)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+r+"</button>":"")+(Y?"":h)+"</div>":"",c=parseInt(this._get(t,"firstDay"),10),c=isNaN(c)?0:c,u=this._get(t,"showWeek"),d=this._get(t,"dayNames"),p=this._get(t,"dayNamesMin"),f=this._get(t,"monthNames"),m=this._get(t,"monthNamesShort"),g=this._get(t,"beforeShowDay"),v=this._get(t,"showOtherMonths"),_=this._get(t,"selectOtherMonths"),b=this._getDefaultDate(t),y="",k=0;Q[0]>k;k++){for(w="",this.maxRows=4,D=0;Q[1]>D;D++){if(T=this._daylightSavingAdjust(new Date(te,Z,t.selectedDay)),C=" ui-corner-all",M="",q){if(M+="<div class='ui-datepicker-group",Q[1]>1)switch(D){case 0:M+=" ui-datepicker-group-first",C=" ui-corner-"+(Y?"right":"left");break;case Q[1]-1:M+=" ui-datepicker-group-last",C=" ui-corner-"+(Y?"left":"right");break;default:M+=" ui-datepicker-group-middle",C=""}M+="'>"}for(M+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+C+"'>"+(/all|left/.test(C)&&0===k?Y?a:s:"")+(/all|right/.test(C)&&0===k?Y?s:a:"")+this._generateMonthYearHeader(t,Z,te,G,$,k>0||D>0,f,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",S=u?"<th class='ui-datepicker-week-col'>"+this._get(t,"weekHeader")+"</th>":"",x=0;7>x;x++)N=(x+c)%7,S+="<th"+((x+c+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+d[N]+"'>"+p[N]+"</span></th>";for(M+=S+"</tr></thead><tbody>",I=this._getDaysInMonth(te,Z),te===t.selectedYear&&Z===t.selectedMonth&&(t.selectedDay=Math.min(t.selectedDay,I)),P=(this._getFirstDayOfMonth(te,Z)-c+7)%7,A=Math.ceil((P+I)/7),z=q?this.maxRows>A?this.maxRows:A:A,this.maxRows=z,H=this._daylightSavingAdjust(new Date(te,Z,1-P)),E=0;z>E;E++){for(M+="<tr>",F=u?"<td class='ui-datepicker-week-col'>"+this._get(t,"calculateWeek")(H)+"</td>":"",x=0;7>x;x++)O=g?g.apply(t.input?t.input[0]:null,[H]):[!0,""],W=H.getMonth()!==Z,j=W&&!_||!O[0]||G&&G>H||$&&H>$,F+="<td class='"+((x+c+6)%7>=5?" ui-datepicker-week-end":"")+(W?" ui-datepicker-other-month":"")+(H.getTime()===T.getTime()&&Z===t.selectedMonth&&t._keyEvent||b.getTime()===H.getTime()&&b.getTime()===T.getTime()?" "+this._dayOverClass:"")+(j?" "+this._unselectableClass+" ui-state-disabled":"")+(W&&!v?"":" "+O[1]+(H.getTime()===X.getTime()?" "+this._currentClass:"")+(H.getTime()===L.getTime()?" ui-datepicker-today":""))+"'"+(W&&!v||!O[2]?"":" title='"+O[2].replace(/'/g,"'")+"'")+(j?"":" data-handler='selectDay' data-event='click' data-month='"+H.getMonth()+"' data-year='"+H.getFullYear()+"'")+">"+(W&&!v?" ":j?"<span class='ui-state-default'>"+H.getDate()+"</span>":"<a class='ui-state-default"+(H.getTime()===L.getTime()?" ui-state-highlight":"")+(H.getTime()===X.getTime()?" ui-state-active":"")+(W?" ui-priority-secondary":"")+"' href='#'>"+H.getDate()+"</a>")+"</td>",H.setDate(H.getDate()+1),H=this._daylightSavingAdjust(H);M+=F+"</tr>"}Z++,Z>11&&(Z=0,te++),M+="</tbody></table>"+(q?"</div>"+(Q[0]>0&&D===Q[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),w+=M}y+=w}return y+=l,t._keyEvent=!1,y},_generateMonthYearHeader:function(t,e,i,s,n,a,r,o){var h,l,c,u,d,p,f,m,g=this._get(t,"changeMonth"),v=this._get(t,"changeYear"),_=this._get(t,"showMonthAfterYear"),b="<div class='ui-datepicker-title'>",y="";if(a||!g)y+="<span class='ui-datepicker-month'>"+r[e]+"</span>";else{for(h=s&&s.getFullYear()===i,l=n&&n.getFullYear()===i,y+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",c=0;12>c;c++)(!h||c>=s.getMonth())&&(!l||n.getMonth()>=c)&&(y+="<option value='"+c+"'"+(c===e?" selected='selected'":"")+">"+o[c]+"</option>");y+="</select>"}if(_||(b+=y+(!a&&g&&v?"":" ")),!t.yearshtml)if(t.yearshtml="",a||!v)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(u=this._get(t,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(t){var e=t.match(/c[+\-].*/)?i+parseInt(t.substring(1),10):t.match(/[+\-].*/)?d+parseInt(t,10):parseInt(t,10); -return isNaN(e)?d:e},f=p(u[0]),m=Math.max(f,p(u[1]||"")),f=s?Math.max(f,s.getFullYear()):f,m=n?Math.min(m,n.getFullYear()):m,t.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=f;f++)t.yearshtml+="<option value='"+f+"'"+(f===i?" selected='selected'":"")+">"+f+"</option>";t.yearshtml+="</select>",b+=t.yearshtml,t.yearshtml=null}return b+=this._get(t,"yearSuffix"),_&&(b+=(!a&&g&&v?"":" ")+y),b+="</div>"},_adjustInstDate:function(t,e,i){var s=t.drawYear+("Y"===i?e:0),n=t.drawMonth+("M"===i?e:0),a=Math.min(t.selectedDay,this._getDaysInMonth(s,n))+("D"===i?e:0),r=this._restrictMinMax(t,this._daylightSavingAdjust(new Date(s,n,a)));t.selectedDay=r.getDate(),t.drawMonth=t.selectedMonth=r.getMonth(),t.drawYear=t.selectedYear=r.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(t)},_restrictMinMax:function(t,e){var i=this._getMinMaxDate(t,"min"),s=this._getMinMaxDate(t,"max"),n=i&&i>e?i:e;return s&&n>s?s:n},_notifyChange:function(t){var e=this._get(t,"onChangeMonthYear");e&&e.apply(t.input?t.input[0]:null,[t.selectedYear,t.selectedMonth+1,t])},_getNumberOfMonths:function(t){var e=this._get(t,"numberOfMonths");return null==e?[1,1]:"number"==typeof e?[1,e]:e},_getMinMaxDate:function(t,e){return this._determineDate(t,this._get(t,e+"Date"),null)},_getDaysInMonth:function(t,e){return 32-this._daylightSavingAdjust(new Date(t,e,32)).getDate()},_getFirstDayOfMonth:function(t,e){return new Date(t,e,1).getDay()},_canAdjustMonth:function(t,e,i,s){var n=this._getNumberOfMonths(t),a=this._daylightSavingAdjust(new Date(i,s+(0>e?e:n[0]*n[1]),1));return 0>e&&a.setDate(this._getDaysInMonth(a.getFullYear(),a.getMonth())),this._isInRange(t,a)},_isInRange:function(t,e){var i,s,n=this._getMinMaxDate(t,"min"),a=this._getMinMaxDate(t,"max"),r=null,o=null,h=this._get(t,"yearRange");return h&&(i=h.split(":"),s=(new Date).getFullYear(),r=parseInt(i[0],10),o=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(r+=s),i[1].match(/[+\-].*/)&&(o+=s)),(!n||e.getTime()>=n.getTime())&&(!a||e.getTime()<=a.getTime())&&(!r||e.getFullYear()>=r)&&(!o||o>=e.getFullYear())},_getFormatConfig:function(t){var e=this._get(t,"shortYearCutoff");return e="string"!=typeof e?e:(new Date).getFullYear()%100+parseInt(e,10),{shortYearCutoff:e,dayNamesShort:this._get(t,"dayNamesShort"),dayNames:this._get(t,"dayNames"),monthNamesShort:this._get(t,"monthNamesShort"),monthNames:this._get(t,"monthNames")}},_formatDate:function(t,e,i,s){e||(t.currentDay=t.selectedDay,t.currentMonth=t.selectedMonth,t.currentYear=t.selectedYear);var n=e?"object"==typeof e?e:this._daylightSavingAdjust(new Date(s,i,e)):this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return this.formatDate(this._get(t,"dateFormat"),n,this._getFormatConfig(t))}}),t.fn.datepicker=function(e){if(!this.length)return this;t.datepicker.initialized||(t(document).mousedown(t.datepicker._checkExternalClick),t.datepicker.initialized=!0),0===t("#"+t.datepicker._mainDivId).length&&t("body").append(t.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof e||"isDisabled"!==e&&"getDate"!==e&&"widget"!==e?"option"===e&&2===arguments.length&&"string"==typeof arguments[1]?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof e?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this].concat(i)):t.datepicker._attachDatepicker(this,e)}):t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i))},t.datepicker=new i,t.datepicker.initialized=!1,t.datepicker.uuid=(new Date).getTime(),t.datepicker.version="1.10.3"})(jQuery); \ No newline at end of file +(function(e,t){function i(t,i){var s,a,o,r=t.nodeName.toLowerCase();return"area"===r?(s=t.parentNode,a=s.name,t.href&&a&&"map"===s.nodeName.toLowerCase()?(o=e("img[usemap=#"+a+"]")[0],!!o&&n(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||i:i)&&n(t)}function n(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}var s=0,a=/^ui-id-\d+$/;e.ui=e.ui||{},e.extend(e.ui,{version:"1.10.3",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({focus:function(t){return function(i,n){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),n&&n.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),scrollParent:function(){var t;return t=e.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(e.css(this,"position"))&&/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(e.css(this,"overflow")+e.css(this,"overflow-y")+e.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!t.length?e(document):t},zIndex:function(i){if(i!==t)return this.css("zIndex",i);if(this.length)for(var n,s,a=e(this[0]);a.length&&a[0]!==document;){if(n=a.css("position"),("absolute"===n||"relative"===n||"fixed"===n)&&(s=parseInt(a.css("zIndex"),10),!isNaN(s)&&0!==s))return s;a=a.parent()}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++s)})},removeUniqueId:function(){return this.each(function(){a.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,n){return!!e.data(t,n[3])},focusable:function(t){return i(t,!isNaN(e.attr(t,"tabindex")))},tabbable:function(t){var n=e.attr(t,"tabindex"),s=isNaN(n);return(s||n>=0)&&i(t,!s)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(i,n){function s(t,i,n,s){return e.each(a,function(){i-=parseFloat(e.css(t,"padding"+this))||0,n&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var a="Width"===n?["Left","Right"]:["Top","Bottom"],o=n.toLowerCase(),r={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+n]=function(i){return i===t?r["inner"+n].call(this):this.each(function(){e(this).css(o,s(this,i)+"px")})},e.fn["outer"+n]=function(t,i){return"number"!=typeof t?r["outer"+n].call(this,t):this.each(function(){e(this).css(o,s(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,n){var s,a=e.ui[t].prototype;for(s in n)a.plugins[s]=a.plugins[s]||[],a.plugins[s].push([i,n[s]])},call:function(e,t,i){var n,s=e.plugins[t];if(s&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(n=0;s.length>n;n++)e.options[s[n][0]]&&s[n][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var n=i&&"left"===i?"scrollLeft":"scrollTop",s=!1;return t[n]>0?!0:(t[n]=1,s=t[n]>0,t[n]=0,s)}})})(jQuery);(function(t,e){var i=0,s=Array.prototype.slice,n=t.cleanData;t.cleanData=function(e){for(var i,s=0;null!=(i=e[s]);s++)try{t(i).triggerHandler("remove")}catch(o){}n(e)},t.widget=function(i,s,n){var o,a,r,h,l={},c=i.split(".")[0];i=i.split(".")[1],o=c+"-"+i,n||(n=s,s=t.Widget),t.expr[":"][o.toLowerCase()]=function(e){return!!t.data(e,o)},t[c]=t[c]||{},a=t[c][i],r=t[c][i]=function(t,i){return this._createWidget?(arguments.length&&this._createWidget(t,i),e):new r(t,i)},t.extend(r,a,{version:n.version,_proto:t.extend({},n),_childConstructors:[]}),h=new s,h.options=t.widget.extend({},h.options),t.each(n,function(i,n){return t.isFunction(n)?(l[i]=function(){var t=function(){return s.prototype[i].apply(this,arguments)},e=function(t){return s.prototype[i].apply(this,t)};return function(){var i,s=this._super,o=this._superApply;return this._super=t,this._superApply=e,i=n.apply(this,arguments),this._super=s,this._superApply=o,i}}(),e):(l[i]=n,e)}),r.prototype=t.widget.extend(h,{widgetEventPrefix:a?h.widgetEventPrefix:i},l,{constructor:r,namespace:c,widgetName:i,widgetFullName:o}),a?(t.each(a._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,r,i._proto)}),delete a._childConstructors):s._childConstructors.push(r),t.widget.bridge(i,r)},t.widget.extend=function(i){for(var n,o,a=s.call(arguments,1),r=0,h=a.length;h>r;r++)for(n in a[r])o=a[r][n],a[r].hasOwnProperty(n)&&o!==e&&(i[n]=t.isPlainObject(o)?t.isPlainObject(i[n])?t.widget.extend({},i[n],o):t.widget.extend({},o):o);return i},t.widget.bridge=function(i,n){var o=n.prototype.widgetFullName||i;t.fn[i]=function(a){var r="string"==typeof a,h=s.call(arguments,1),l=this;return a=!r&&h.length?t.widget.extend.apply(null,[a].concat(h)):a,r?this.each(function(){var s,n=t.data(this,o);return n?t.isFunction(n[a])&&"_"!==a.charAt(0)?(s=n[a].apply(n,h),s!==n&&s!==e?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):e):t.error("no such method '"+a+"' for "+i+" widget instance"):t.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var e=t.data(this,o);e?e.option(a||{})._init():t.data(this,o,new n(a,this))}),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this.bindings=t(),this.hoverable=t(),this.focusable=t(),s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(i,s){var n,o,a,r=i;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof i)if(r={},n=i.split("."),i=n.shift(),n.length){for(o=r[i]=t.widget.extend({},this.options[i]),a=0;n.length-1>a;a++)o[n[a]]=o[n[a]]||{},o=o[n[a]];if(i=n.pop(),s===e)return o[i]===e?null:o[i];o[i]=s}else{if(s===e)return this.options[i]===e?null:this.options[i];r[i]=s}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!e).attr("aria-disabled",e),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var o,a=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=o=t(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,o=this.widget()),t.each(n,function(n,r){function h(){return i||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof r?a[r]:r).apply(a,arguments):e}"string"!=typeof r&&(h.guid=r.guid=r.guid||h.guid||t.guid++);var l=n.match(/^(\w+)\s*(.*)$/),c=l[1]+a.eventNamespace,u=l[2];u?o.delegate(u,c,h):s.bind(c,h)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(e).undelegate(e)},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}})})(jQuery);(function(t){var e=!1;t(document).mouseup(function(){e=!1}),t.widget("ui.mouse",{version:"1.10.3",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).bind("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!e){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?t(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===t.data(i.target,this.widgetName+".preventClickEvent")&&t.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return s._mouseMove(t)},this._mouseUpDelegate=function(t){return s._mouseUp(t)},t(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),e=!0,!0)):!0}},_mouseMove:function(e){return t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button?this._mouseUp(e):this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(t,e){function i(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function s(e,i){return parseInt(t.css(e,i),10)||0}function n(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var a,o=Math.max,r=Math.abs,l=Math.round,h=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(a!==e)return a;var i,s,n=t("<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),o=n.children()[0];return t("body").append(n),i=o.offsetWidth,n.css("overflow","scroll"),s=o.offsetWidth,i===s&&(s=n[0].clientWidth),n.remove(),a=i-s},getScrollInfo:function(e){var i=e.isWindow?"":e.element.css("overflow-x"),s=e.isWindow?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width<e.element[0].scrollWidth,a="scroll"===s||"auto"===s&&e.height<e.element[0].scrollHeight;return{width:a?t.position.scrollbarWidth():0,height:n?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]);return{element:i,isWindow:s,offset:i.offset()||{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:s?i.width():i.outerWidth(),height:s?i.height():i.outerHeight()}}},t.fn.position=function(e){if(!e||!e.of)return f.apply(this,arguments);e=t.extend({},e);var a,p,g,m,v,_,b=t(e.of),y=t.position.getWithinInfo(e.within),k=t.position.getScrollInfo(y),w=(e.collision||"flip").split(" "),D={};return _=n(b),b[0].preventDefault&&(e.at="left top"),p=_.width,g=_.height,m=_.offset,v=t.extend({},m),t.each(["my","at"],function(){var t,i,s=(e[this]||"").split(" ");1===s.length&&(s=h.test(s[0])?s.concat(["center"]):c.test(s[0])?["center"].concat(s):["center","center"]),s[0]=h.test(s[0])?s[0]:"center",s[1]=c.test(s[1])?s[1]:"center",t=u.exec(s[0]),i=u.exec(s[1]),D[this]=[t?t[0]:0,i?i[0]:0],e[this]=[d.exec(s[0])[0],d.exec(s[1])[0]]}),1===w.length&&(w[1]=w[0]),"right"===e.at[0]?v.left+=p:"center"===e.at[0]&&(v.left+=p/2),"bottom"===e.at[1]?v.top+=g:"center"===e.at[1]&&(v.top+=g/2),a=i(D.at,p,g),v.left+=a[0],v.top+=a[1],this.each(function(){var n,h,c=t(this),u=c.outerWidth(),d=c.outerHeight(),f=s(this,"marginLeft"),_=s(this,"marginTop"),x=u+f+s(this,"marginRight")+k.width,C=d+_+s(this,"marginBottom")+k.height,M=t.extend({},v),T=i(D.my,c.outerWidth(),c.outerHeight());"right"===e.my[0]?M.left-=u:"center"===e.my[0]&&(M.left-=u/2),"bottom"===e.my[1]?M.top-=d:"center"===e.my[1]&&(M.top-=d/2),M.left+=T[0],M.top+=T[1],t.support.offsetFractions||(M.left=l(M.left),M.top=l(M.top)),n={marginLeft:f,marginTop:_},t.each(["left","top"],function(i,s){t.ui.position[w[i]]&&t.ui.position[w[i]][s](M,{targetWidth:p,targetHeight:g,elemWidth:u,elemHeight:d,collisionPosition:n,collisionWidth:x,collisionHeight:C,offset:[a[0]+T[0],a[1]+T[1]],my:e.my,at:e.at,within:y,elem:c})}),e.using&&(h=function(t){var i=m.left-M.left,s=i+p-u,n=m.top-M.top,a=n+g-d,l={target:{element:b,left:m.left,top:m.top,width:p,height:g},element:{element:c,left:M.left,top:M.top,width:u,height:d},horizontal:0>s?"left":i>0?"right":"center",vertical:0>a?"top":n>0?"bottom":"middle"};u>p&&p>r(i+s)&&(l.horizontal="center"),d>g&&g>r(n+a)&&(l.vertical="middle"),l.important=o(r(i),r(s))>o(r(n),r(a))?"horizontal":"vertical",e.using.call(this,t,l)}),c.offset(t.extend(M,{using:h}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,a=n.offset.left+n.scrollLeft,o=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-o-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-o-a,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,a=n.offset.top+n.scrollTop,o=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-o-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-o-a,t.top+p+f+g>c&&(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,t.top+p+f+g>u&&(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,o=document.getElementsByTagName("body")[0],r=document.createElement("div");e=document.createElement(o?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(a in s)e.style[a]=s[a];e.appendChild(r),i=o||document.documentElement,i.insertBefore(e,i.firstChild),r.style.cssText="position: absolute; left: 10.7432222px;",n=t(r).offset().left,t.support.offsetFractions=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()})(jQuery);(function(t){t.widget("ui.draggable",t.ui.mouse,{version:"1.10.3",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(t(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){t("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(t(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_mouseDrag:function(e,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||t.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1):!1},_mouseUp:function(e){return t("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.element.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,t(document).width()-this.helperProportions.width-this.margins.left,(t(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(e){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=e.pageX,h=e.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.left<i[0]&&(l=i[0]+this.offset.click.left),e.pageY-this.offset.click.top<i[1]&&(h=i[1]+this.offset.click.top),e.pageX-this.offset.click.left>i[2]&&(l=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,l=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s]),"drag"===e&&(this.positionAbs=this._convertPositionTo("absolute")),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i){var s=t(this).data("ui-draggable"),n=s.options,a=t.extend({},i,{item:s.element});s.sortables=[],t(n.connectToSortable).each(function(){var i=t.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",e,a))})},stop:function(e,i){var s=t(this).data("ui-draggable"),n=t.extend({},i,{item:s.element});t.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(e),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",e,n))})},drag:function(e,i){var s=t(this).data("ui-draggable"),n=this;t.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,t.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&t.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=t(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},e.target=this.instance.currentItem[0],this.instance._mouseCapture(e,!0),this.instance._mouseStart(e,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",e),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(e)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",e,this.instance._uiHash(this.instance)),this.instance._mouseStop(e,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",e),s.dropped=!1)})}}),t.ui.plugin.add("draggable","cursor",{start:function(){var e=t("body"),i=t(this).data("ui-draggable").options;e.css("cursor")&&(i._cursor=e.css("cursor")),e.css("cursor",i.cursor)},stop:function(){var e=t(this).data("ui-draggable").options;e._cursor&&t("body").css("cursor",e._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._opacity&&t(i.helper).css("opacity",s._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(){var e=t(this).data("ui-draggable");e.scrollParent[0]!==document&&"HTML"!==e.scrollParent[0].tagName&&(e.overflowOffset=e.scrollParent.offset())},drag:function(e){var i=t(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-e.pageY<s.scrollSensitivity?i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop+s.scrollSpeed:e.pageY-i.overflowOffset.top<s.scrollSensitivity&&(i.scrollParent[0].scrollTop=n=i.scrollParent[0].scrollTop-s.scrollSpeed)),s.axis&&"y"===s.axis||(i.overflowOffset.left+i.scrollParent[0].offsetWidth-e.pageX<s.scrollSensitivity?i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft+s.scrollSpeed:e.pageX-i.overflowOffset.left<s.scrollSensitivity&&(i.scrollParent[0].scrollLeft=n=i.scrollParent[0].scrollLeft-s.scrollSpeed))):(s.axis&&"x"===s.axis||(e.pageY-t(document).scrollTop()<s.scrollSensitivity?n=t(document).scrollTop(t(document).scrollTop()-s.scrollSpeed):t(window).height()-(e.pageY-t(document).scrollTop())<s.scrollSensitivity&&(n=t(document).scrollTop(t(document).scrollTop()+s.scrollSpeed))),s.axis&&"y"===s.axis||(e.pageX-t(document).scrollLeft()<s.scrollSensitivity?n=t(document).scrollLeft(t(document).scrollLeft()-s.scrollSpeed):t(window).width()-(e.pageX-t(document).scrollLeft())<s.scrollSensitivity&&(n=t(document).scrollLeft(t(document).scrollLeft()+s.scrollSpeed)))),n!==!1&&t.ui.ddmanager&&!s.dropBehaviour&&t.ui.ddmanager.prepareOffsets(i,e)}}),t.ui.plugin.add("draggable","snap",{start:function(){var e=t(this).data("ui-draggable"),i=e.options;e.snapElements=[],t(i.snap.constructor!==String?i.snap.items||":data(ui-draggable)":i.snap).each(function(){var i=t(this),s=i.offset();this!==e.element[0]&&e.snapElements.push({item:this,width:i.outerWidth(),height:i.outerHeight(),top:s.top,left:s.left})})},drag:function(e,i){var s,n,a,o,r,l,h,c,u,d,p=t(this).data("ui-draggable"),g=p.options,f=g.snapTolerance,m=i.offset.left,_=m+p.helperProportions.width,v=i.offset.top,b=v+p.helperProportions.height;for(u=p.snapElements.length-1;u>=0;u--)r=p.snapElements[u].left,l=r+p.snapElements[u].width,h=p.snapElements[u].top,c=h+p.snapElements[u].height,r-f>_||m>l+f||h-f>b||v>c+f||!t.contains(p.snapElements[u].item.ownerDocument,p.snapElements[u].item)?(p.snapElements[u].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=!1):("inner"!==g.snapMode&&(s=f>=Math.abs(h-b),n=f>=Math.abs(c-v),a=f>=Math.abs(r-_),o=f>=Math.abs(l-m),s&&(i.position.top=p._convertPositionTo("relative",{top:h-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l}).left-p.margins.left)),d=s||n||a||o,"outer"!==g.snapMode&&(s=f>=Math.abs(h-v),n=f>=Math.abs(c-b),a=f>=Math.abs(r-m),o=f>=Math.abs(l-_),s&&(i.position.top=p._convertPositionTo("relative",{top:h,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[u].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=s||n||a||o||d)}}),t.ui.plugin.add("draggable","stack",{start:function(){var e,i=this.data("ui-draggable").options,s=t.makeArray(t(i.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});s.length&&(e=parseInt(t(s[0]).css("zIndex"),10)||0,t(s).each(function(i){t(this).css("zIndex",e+i)}),this.css("zIndex",e+s.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._zIndex&&t(i.helper).css("zIndex",s._zIndex)}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}t.widget("ui.droppable",{version:"1.10.3",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e=this.options,i=e.accept;this.isover=!1,this.isout=!0,this.accept=t.isFunction(i)?i:function(t){return t.is(i)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},t.ui.ddmanager.droppables[e.scope]=t.ui.ddmanager.droppables[e.scope]||[],t.ui.ddmanager.droppables[e.scope].push(this),e.addClasses&&this.element.addClass("ui-droppable")},_destroy:function(){for(var e=0,i=t.ui.ddmanager.droppables[this.options.scope];i.length>e;e++)i[e]===this&&i.splice(e,1);this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(e,i){"accept"===e&&(this.accept=t.isFunction(i)?i:function(t){return t.is(i)}),t.Widget.prototype._setOption.apply(this,arguments)},_activate:function(e){var i=t.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",e,this.ui(i))},_deactivate:function(e){var i=t.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",e,this.ui(i))},_over:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",e,this.ui(i)))},_out:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",e,this.ui(i)))},_drop:function(e,i){var s=i||t.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var e=t.data(this,"ui-droppable");return e.options.greedy&&!e.options.disabled&&e.options.scope===s.options.scope&&e.accept.call(e.element[0],s.currentItem||s.element)&&t.ui.intersect(s,t.extend(e,{offset:e.element.offset()}),e.options.tolerance)?(n=!0,!1):undefined}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",e,this.ui(s)),this.element):!1):!1},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}}}),t.ui.intersect=function(t,i,s){if(!i.offset)return!1;var n,a,o=(t.positionAbs||t.position.absolute).left,r=o+t.helperProportions.width,l=(t.positionAbs||t.position.absolute).top,h=l+t.helperProportions.height,c=i.offset.left,u=c+i.proportions.width,d=i.offset.top,p=d+i.proportions.height;switch(s){case"fit":return o>=c&&u>=r&&l>=d&&p>=h;case"intersect":return o+t.helperProportions.width/2>c&&u>r-t.helperProportions.width/2&&l+t.helperProportions.height/2>d&&p>h-t.helperProportions.height/2;case"pointer":return n=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,a=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,e(a,d,i.proportions.height)&&e(n,c,i.proportions.width);case"touch":return(l>=d&&p>=l||h>=d&&p>=h||d>l&&h>p)&&(o>=c&&u>=o||r>=c&&u>=r||c>o&&r>u);default:return!1}},t.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,i){var s,n,a=t.ui.ddmanager.droppables[e.options.scope]||[],o=i?i.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();t:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||e&&!a[s].accept.call(a[s].element[0],e.currentItem||e.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions.height=0;continue t}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions={width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight})}},drop:function(e,i){var s=!1;return t.each((t.ui.ddmanager.droppables[e.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&t.ui.intersect(e,this,this.options.tolerance)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(e,i){e.element.parentsUntil("body").bind("scroll.droppable",function(){e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)})},drag:function(e,i){e.options.refreshPositions&&t.ui.ddmanager.prepareOffsets(e,i),t.each(t.ui.ddmanager.droppables[e.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=t.ui.intersect(e,this,this.options.tolerance),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return t.data(this,"ui-droppable").options.scope===n}),a.length&&(s=t.data(a[0],"ui-droppable"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(e,i){e.element.parentsUntil("body").unbind("scroll.droppable"),e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)}}})(jQuery);(function(t){function e(t){return parseInt(t,10)||0}function i(t){return!isNaN(parseInt(t,10))}t.widget("ui.resizable",t.ui.mouse,{version:"1.10.3",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var e,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(t("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),e=this.handles.split(","),this.handles={},i=0;e.length>i;i++)s=t.trim(e[i]),a="ui-resizable-"+s,n=t("<div class='ui-resizable-handle "+a+"'></div>"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(e){var i,s,n,a;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=t(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=t(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,a),this._proportionallyResize()),t(this.handles[i]).length},this._renderAxis(this.element),this._handles=t(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),t(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(t(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(t(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=e(this.helper.css("left")),n=e(this.helper.css("top")),o.containment&&(s+=t(o.containment).scrollLeft()||0,n+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(e){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,c=this.size.height,u=e.pageX-a.left||0,d=e.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[e,u,d]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==c&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(n)||this._trigger("resize",e,this.ui()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&t.ui.hasScroll(i[0],"left")?0:c.sizeDiff.height,a=s?0:c.sizeDiff.width,o={width:c.helper.width()-a,height:c.helper.height()-n},r=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null,h=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(o,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(t){var e,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),i(t.left)&&(this.position.left=t.left),i(t.top)&&(this.position.top=t.top),i(t.height)&&(this.size.height=t.height),i(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,s=this.size,n=this.axis;return i(t.height)?t.width=t.height*this.aspectRatio:i(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===n&&(t.left=e.left+(s.width-t.width),t.top=null),"nw"===n&&(t.top=e.top+(s.height-t.height),t.left=e.left+(s.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,s=this.axis,n=i(t.width)&&e.maxWidth&&e.maxWidth<t.width,a=i(t.height)&&e.maxHeight&&e.maxHeight<t.height,o=i(t.width)&&e.minWidth&&e.minWidth>t.width,r=i(t.height)&&e.minHeight&&e.minHeight>t.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,c=/sw|nw|w/.test(s),u=/nw|ne|n/.test(s);return o&&(t.width=e.minWidth),r&&(t.height=e.minHeight),n&&(t.width=e.maxWidth),a&&(t.height=e.maxHeight),o&&c&&(t.left=h-e.minWidth),n&&c&&(t.left=h-e.maxWidth),r&&u&&(t.top=l-e.minHeight),a&&u&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var t,e,i,s,n,a=this.helper||this.element;for(t=0;this._proportionallyResizeElements.length>t;t++){if(n=this._proportionallyResizeElements[t],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],e=0;i.length>e;e++)this.borderDif[e]=(parseInt(i[e],10)||0)+(parseInt(s[e],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&t.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,c=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=t(this).data("ui-resizable"),c=l.options,u=l.element,d=c.containment,p=d instanceof t?d.get(0):/parent/.test(d)?u.parent().get(0):d;p&&(l.containerElement=t(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(i=t(p),s=[],t(["Top","Right","Left","Bottom"]).each(function(t,n){s[t]=e(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=t.ui.hasScroll(p,"left")?p.scrollWidth:o,h=t.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(e){var i,s,n,a,o=t(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,c=o._aspectRatio||e.shiftKey,u={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-u.left),c&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),c&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-u.left:o.offset.left-u.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-u.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=o.parentData.left),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,c&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,c&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.containerOffset,n=e.containerPosition,a=e.containerElement,o=t(e.helper),r=o.offset(),h=o.outerWidth()-e.sizeDiff.width,l=o.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=function(e){t(e).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseInt(e.width(),10),height:parseInt(e.height(),10),left:parseInt(e.css("left"),10),top:parseInt(e.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):t.each(i.alsoResize,function(t){s(t)})},resize:function(e,i){var s=t(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(e,s){t(e).each(function(){var e=t(this),n=t(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(n[e]||0)+(r[e]||0);i&&i>=0&&(a[e]=i||null)}),e.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):t.each(n.alsoResize,function(t,e){h(t,e)})},stop:function(){t(this).removeData("resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).data("ui-resizable");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).data("ui-resizable");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size,n=e.originalSize,a=e.originalPosition,o=e.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,c=Math.round((s.width-n.width)/h)*h,u=Math.round((s.height-n.height)/l)*l,d=n.width+c,p=n.height+u,f=i.maxWidth&&d>i.maxWidth,g=i.maxHeight&&p>i.maxHeight,m=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,m&&(d+=h),v&&(p+=l),f&&(d-=h),g&&(p-=l),/^(se|s|e)$/.test(o)?(e.size.width=d,e.size.height=p):/^(ne)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.top=a.top-u):/^(sw)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.left=a.left-c):(e.size.width=d,e.size.height=p,e.position.top=a.top-u,e.position.left=a.left-c)}})})(jQuery);(function(t){t.widget("ui.selectable",t.ui.mouse,{version:"1.10.3",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var e,i=this;this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){e=t(i.options.filter,i.element[0]),e.addClass("ui-selectee"),e.each(function(){var e=t(this),i=e.offset();t.data(this,"selectable-item",{element:this,$element:e,left:i.left,top:i.top,right:i.left+e.outerWidth(),bottom:i.top+e.outerHeight(),startselected:!1,selected:e.hasClass("ui-selected"),selecting:e.hasClass("ui-selecting"),unselecting:e.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=e.addClass("ui-selectee"),this._mouseInit(),this.helper=t("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(e){var i=this,s=this.options;this.opos=[e.pageX,e.pageY],this.options.disabled||(this.selectees=t(s.filter,this.element[0]),this._trigger("start",e),t(s.appendTo).append(this.helper),this.helper.css({left:e.pageX,top:e.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=t.data(this,"selectable-item");s.startselected=!0,e.metaKey||e.ctrlKey||(s.$element.removeClass("ui-selected"),s.selected=!1,s.$element.addClass("ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",e,{unselecting:s.element}))}),t(e.target).parents().addBack().each(function(){var s,n=t.data(this,"selectable-item");return n?(s=!e.metaKey&&!e.ctrlKey||!n.$element.hasClass("ui-selected"),n.$element.removeClass(s?"ui-unselecting":"ui-selected").addClass(s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",e,{selecting:n.element}):i._trigger("unselecting",e,{unselecting:n.element}),!1):undefined}))},_mouseDrag:function(e){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,a=this.opos[0],o=this.opos[1],r=e.pageX,l=e.pageY;return a>r&&(i=r,r=a,a=i),o>l&&(i=l,l=o,o=i),this.helper.css({left:a,top:o,width:r-a,height:l-o}),this.selectees.each(function(){var i=t.data(this,"selectable-item"),h=!1;i&&i.element!==s.element[0]&&("touch"===n.tolerance?h=!(i.left>r||a>i.right||i.top>l||o>i.bottom):"fit"===n.tolerance&&(h=i.left>a&&r>i.right&&i.top>o&&l>i.bottom),h?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,s._trigger("selecting",e,{selecting:i.element}))):(i.selecting&&((e.metaKey||e.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",e,{unselecting:i.element}))),i.selected&&(e.metaKey||e.ctrlKey||i.startselected||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",e,{unselecting:i.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,t(".ui-unselecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");s.$element.removeClass("ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",e,{unselected:s.element})}),t(".ui-selecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");s.$element.removeClass("ui-selecting").addClass("ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",e,{selected:s.element})}),this._trigger("stop",e),this.helper.remove(),!1}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}function i(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))}t.widget("ui.sortable",t.ui.mouse,{version:"1.10.3",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_create:function(){var t=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===t.axis||i(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_setOption:function(e,i){"disabled"===e?(this.options[e]=i,this.widget().toggleClass("ui-sortable-disabled",!!i)):t.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):undefined}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("<style>*{ cursor: "+a.cursor+" !important; }</style>").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<a.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+a.scrollSpeed:e.pageY-this.overflowOffset.top<a.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-a.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<a.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+a.scrollSpeed:e.pageX-this.overflowOffset.left<a.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-a.scrollSpeed)):(e.pageY-t(document).scrollTop()<a.scrollSensitivity?r=t(document).scrollTop(t(document).scrollTop()-a.scrollSpeed):t(window).height()-(e.pageY-t(document).scrollTop())<a.scrollSensitivity&&(r=t(document).scrollTop(t(document).scrollTop()+a.scrollSpeed)),e.pageX-t(document).scrollLeft()<a.scrollSensitivity?r=t(document).scrollLeft(t(document).scrollLeft()-a.scrollSpeed):t(window).width()-(e.pageX-t(document).scrollLeft())<a.scrollSensitivity&&(r=t(document).scrollLeft(t(document).scrollLeft()+a.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var i="x"===this.options.axis||e(this.positionAbs.top+this.offset.click.top,t.top,t.height),s="y"===this.options.axis||e(this.positionAbs.left+this.offset.click.left,t.left,t.width),n=i&&s,o=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return n?this.floating?a&&"right"===a||"down"===o?2:1:o&&("down"===o?2:1):!1},_intersectsWithSides:function(t){var i=e(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),s=e(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),n=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return this.floating&&o?"right"===o&&s||"left"===o&&!s:n&&("down"===n&&i||"up"===n&&!i)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){var i,s,n,o,a=[],r=[],h=this._connectWith();if(h&&e)for(i=h.length-1;i>=0;i--)for(n=t(h[i]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&r.push([t.isFunction(o.options.items)?o.options.items.call(o.element):t(o.options.items,o.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),o]);for(r.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),i=r.length-1;i>=0;i--)r[i][0].each(function(){a.push(this)});return t(a)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]).addClass(i||e.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?e.currentItem.children().each(function(){t("<td> </td>",e.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_contactContainers:function(s){var n,o,a,r,h,l,c,u,d,p,f=null,g=null;for(n=this.containers.length-1;n>=0;n--)if(!t.contains(this.currentItem[0],this.containers[n].element[0]))if(this._intersectsWith(this.containers[n].containerCache)){if(f&&t.contains(this.containers[n].element[0],f.element[0]))continue;f=this.containers[n],g=n}else this.containers[n].containerCache.over&&(this.containers[n]._trigger("out",s,this._uiHash(this)),this.containers[n].containerCache.over=0);if(f)if(1===this.containers.length)this.containers[g].containerCache.over||(this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1);else{for(a=1e4,r=null,p=f.floating||i(this.currentItem),h=p?"left":"top",l=p?"width":"height",c=this.positionAbs[h]+this.offset.click[h],o=this.items.length-1;o>=0;o--)t.contains(this.containers[g].element[0],this.items[o].item[0])&&this.items[o].item[0]!==this.currentItem[0]&&(!p||e(this.positionAbs.top+this.offset.click.top,this.items[o].top,this.items[o].height))&&(u=this.items[o].item.offset()[h],d=!1,Math.abs(u-c)>Math.abs(u+this.items[o][l]-c)&&(d=!0,u+=this.items[o][l]),a>Math.abs(u-c)&&(a=Math.abs(u-c),r=this.items[o],this.direction=d?"up":"down"));if(!r&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[g])return;r?this._rearrange(s,r,null,!0):this._rearrange(s,null,this.containers[g].element,!0),this._trigger("change",s,this._uiHash()),this.containers[g]._trigger("change",s,this._uiHash(this)),this.currentContainer=this.containers[g],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,t("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(t("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(o=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(a=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){this.reverting=!1;var i,s=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)("auto"===this._storedCSS[i]||"static"===this._storedCSS[i])&&(this._storedCSS[i]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&s.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||s.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(s.push(function(t){this._trigger("remove",t,this._uiHash())}),s.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),s.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;i>=0;i--)e||s.push(function(t){return function(e){t._trigger("deactivate",e,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over&&(s.push(function(t){return function(e){t._trigger("out",e,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!e){for(this._trigger("beforeStop",t,this._uiHash()),i=0;s.length>i;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}if(e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!e){for(i=0;s.length>i;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})})(jQuery);(function(e){var t=0,i={},a={};i.height=i.paddingTop=i.paddingBottom=i.borderTopWidth=i.borderBottomWidth="hide",a.height=a.paddingTop=a.paddingBottom=a.borderTopWidth=a.borderBottomWidth="show",e.widget("ui.accordion",{version:"1.10.3",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e(),content:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),this._destroyIcons(),e=this.headers.next().css("display","").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").each(function(){/^ui-accordion/.test(this.id)&&this.removeAttribute("id")}),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),undefined):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t),undefined)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,a=this.headers.length,s=this.headers.index(t.target),n=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:n=this.headers[(s+1)%a];break;case i.LEFT:case i.UP:n=this.headers[(s-1+a)%a];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:n=this.headers[0];break;case i.END:n=this.headers[a-1]}n&&(e(t.target).attr("tabIndex",-1),e(n).attr("tabIndex",0),n.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all"),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()},_refresh:function(){var i,a=this.options,s=a.heightStyle,n=this.element.parent(),r=this.accordionId="ui-accordion-"+(this.element.attr("id")||++t);this.active=this._findActive(a.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(t){var i=e(this),a=i.attr("id"),s=i.next(),n=s.attr("id");a||(a=r+"-header-"+t,i.attr("id",a)),n||(n=r+"-panel-"+t,s.attr("id",n)),i.attr("aria-controls",n),s.attr("aria-labelledby",a)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false",tabIndex:-1}).next().attr({"aria-expanded":"false","aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true",tabIndex:0}).next().attr({"aria-expanded":"true","aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(a.event),"fill"===s?(i=n.height(),this.element.siblings(":visible").each(function(){var t=e(this),a=t.css("position");"absolute"!==a&&"fixed"!==a&&(i-=t.outerHeight(!0))}),this.headers.each(function(){i-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(i=0,this.headers.next().each(function(){i=Math.max(i,e(this).css("height","").height())}).height(i))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,a=this.active,s=e(t.currentTarget),n=s[0]===a[0],r=n&&i.collapsible,o=r?e():s.next(),h=a.next(),d={oldHeader:a,oldPanel:h,newHeader:r?e():s,newPanel:o};t.preventDefault(),n&&!i.collapsible||this._trigger("beforeActivate",t,d)===!1||(i.active=r?!1:this.headers.index(s),this.active=n?e():s,this._toggle(d),a.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&a.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),n||(s.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),s.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,a=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=a,this.options.animate?this._animate(i,a,t):(a.hide(),i.show(),this._toggleComplete(t)),a.attr({"aria-expanded":"false","aria-hidden":"true"}),a.prev().attr("aria-selected","false"),i.length&&a.length?a.prev().attr("tabIndex",-1):i.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),i.attr({"aria-expanded":"true","aria-hidden":"false"}).prev().attr({"aria-selected":"true",tabIndex:0})},_animate:function(e,t,s){var n,r,o,h=this,d=0,c=e.length&&(!t.length||e.index()<t.index()),l=this.options.animate||{},u=c&&l.down||l,v=function(){h._toggleComplete(s)};return"number"==typeof u&&(o=u),"string"==typeof u&&(r=u),r=r||u.easing||l.easing,o=o||u.duration||l.duration,t.length?e.length?(n=e.show().outerHeight(),t.animate(i,{duration:o,easing:r,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(a,{duration:o,easing:r,complete:v,step:function(e,i){i.now=Math.round(e),"height"!==i.prop?d+=i.now:"content"!==h.options.heightStyle&&(i.now=Math.round(n-t.outerHeight()-d),d=0)}}),undefined):t.animate(i,o,r,v):e.animate(a,o,r,v)},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}})})(jQuery);(function(e){var t=0;e.widget("ui.autocomplete",{version:"1.10.3",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,undefined;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:case a.NUMPAD_ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),undefined;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),undefined):(this._searchTimeout(e),undefined)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,undefined):(clearTimeout(this.searching),this.close(e),this._change(e),undefined)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().data("ui-menu"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(s){s.target===t.element[0]||s.target===i||e.contains(i,s.target)||t.close()})})},menufocus:function(t,i){if(this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type)))return this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),undefined;var s=i.item.data("ui-autocomplete-item");!1!==this._trigger("focus",t,{item:s})?t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(s.value):this.liveRegion.text(s.value)},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("<span>",{role:"status","aria-live":"polite"}).addClass("ui-helper-hidden-accessible").insertBefore(this.element),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,s=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,s){s(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,n){s.xhr&&s.xhr.abort(),s.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){n(e)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){this.term!==this._value()&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length<this.options.minLength?this.close(t):this._trigger("search",t)!==!1?this._search(e):undefined},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var e=this,i=++t;return function(s){i===t&&e.__response(s),e.pending--,e.pending||e.element.removeClass("ui-autocomplete-loading")}},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return"string"==typeof t?{label:t,value:t}:e.extend({label:t.label||t.value,value:t.value||t.label},t)})},_suggest:function(t){var i=this.menu.element.empty();this._renderMenu(i,t),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,i){var s=this;e.each(i,function(e,i){s._renderItemData(t,i)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,i){return e("<li>").append(e("<a>").text(i.label)).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this._value(this.term),this.menu.blur(),undefined):(this.menu[e](t),undefined):(this.search(null,t),undefined)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var s=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return s.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var t;this._superApply(arguments),this.options.disabled||this.cancelSearch||(t=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.text(t))}})})(jQuery);(function(e){var t,i,n,s,a="ui-button ui-widget ui-state-default ui-corner-all",o="ui-state-hover ui-state-active ",r="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",h=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},u=function(t){var i=t.name,n=t.form,s=e([]);return i&&(i=i.replace(/'/g,"\\'"),s=n?e(n).find("[name='"+i+"']"):e("[name='"+i+"']",t.ownerDocument).filter(function(){return!this.form})),s};e.widget("ui.button",{version:"1.10.3",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,h),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var o=this,r=this.options,l="checkbox"===this.type||"radio"===this.type,c=l?"":"ui-state-active",d="ui-state-focus";null===r.label&&(r.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(a).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){r.disabled||this===t&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){r.disabled||e(this).removeClass(c)}).bind("click"+this.eventNamespace,function(e){r.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this.element.bind("focus"+this.eventNamespace,function(){o.buttonElement.addClass(d)}).bind("blur"+this.eventNamespace,function(){o.buttonElement.removeClass(d)}),l&&(this.element.bind("change"+this.eventNamespace,function(){s||o.refresh()}),this.buttonElement.bind("mousedown"+this.eventNamespace,function(e){r.disabled||(s=!1,i=e.pageX,n=e.pageY)}).bind("mouseup"+this.eventNamespace,function(e){r.disabled||(i!==e.pageX||n!==e.pageY)&&(s=!0)})),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return r.disabled||s?!1:undefined}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(r.disabled||s)return!1;e(this).addClass("ui-state-active"),o.buttonElement.attr("aria-pressed","true");var t=o.element[0];u(t).not(t).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return r.disabled?!1:(e(this).addClass("ui-state-active"),t=this,o.document.one("mouseup",function(){t=null}),undefined)}).bind("mouseup"+this.eventNamespace,function(){return r.disabled?!1:(e(this).removeClass("ui-state-active"),undefined)}).bind("keydown"+this.eventNamespace,function(t){return r.disabled?!1:((t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active"),undefined)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",r.disabled),this._resetButton()},_determineButtonType:function(){var e,t,i;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),i=this.element.is(":checked"),i&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",i)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(a+" "+o+" "+r).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){return this._super(e,t),"disabled"===e?(t?this.element.prop("disabled",!0):this.element.prop("disabled",!1),undefined):(this._resetButton(),undefined)},refresh:function(){var t=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),"radio"===this.type?u(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),undefined;var t=this.buttonElement.removeClass(r),i=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),n=this.options.icons,s=n.primary&&n.secondary,a=[];n.primary||n.secondary?(this.options.text&&a.push("ui-button-text-icon"+(s?"s":n.primary?"-primary":"-secondary")),n.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+n.primary+"'></span>"),n.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+n.secondary+"'></span>"),this.options.text||(a.push(s?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(i)))):a.push("ui-button-text-only"),t.addClass(a.join(" "))}}),e.widget("ui.buttonset",{version:"1.10.3",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction");this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}})})(jQuery);(function(e,t){function i(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.dpDiv=a(e("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",function(){e.datepicker._isDisabledDatepicker(n.inline?t.parent()[0]:n.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))})}function s(t,i){e.extend(t,i);for(var a in i)null==i[a]&&(t[a]=i[a]);return t}e.extend(e.ui,{datepicker:{version:"1.10.3"}});var n,r="datepicker";e.extend(i.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return s(this._defaults,e||{}),this},_attachDatepicker:function(t,i){var a,s,n;a=t.nodeName.toLowerCase(),s="div"===a||"span"===a,t.id||(this.uuid+=1,t.id="dp"+this.uuid),n=this._newInst(e(t),s),n.settings=e.extend({},i||{}),"input"===a?this._connectDatepicker(t,n):s&&this._inlineDatepicker(t,n)},_newInst:function(t,i){var s=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:s,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?a(e("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,i){var a=e(t);i.append=e([]),i.trigger=e([]),a.hasClass(this.markerClassName)||(this._attachments(a,i),a.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),e.data(t,r,i),i.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,i){var a,s,n,r=this._get(i,"appendText"),o=this._get(i,"isRTL");i.append&&i.append.remove(),r&&(i.append=e("<span class='"+this._appendClass+"'>"+r+"</span>"),t[o?"before":"after"](i.append)),t.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),a=this._get(i,"showOn"),("focus"===a||"both"===a)&&t.focus(this._showDatepicker),("button"===a||"both"===a)&&(s=this._get(i,"buttonText"),n=this._get(i,"buttonImage"),i.trigger=e(this._get(i,"buttonImageOnly")?e("<img/>").addClass(this._triggerClass).attr({src:n,alt:s,title:s}):e("<button type='button'></button>").addClass(this._triggerClass).html(n?e("<img/>").attr({src:n,alt:s,title:s}):s)),t[o?"before":"after"](i.trigger),i.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,i,a,s,n=new Date(2009,11,20),r=this._get(e,"dateFormat");r.match(/[DM]/)&&(t=function(e){for(i=0,a=0,s=0;e.length>s;s++)e[s].length>i&&(i=e[s].length,a=s);return a},n.setMonth(t(this._get(e,r.match(/MM/)?"monthNames":"monthNamesShort"))),n.setDate(t(this._get(e,r.match(/DD/)?"dayNames":"dayNamesShort"))+20-n.getDay())),e.input.attr("size",this._formatDate(e,n).length)}},_inlineDatepicker:function(t,i){var a=e(t);a.hasClass(this.markerClassName)||(a.addClass(this.markerClassName).append(i.dpDiv),e.data(t,r,i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(t),i.dpDiv.css("display","block"))},_dialogDatepicker:function(t,i,a,n,o){var u,c,l,h,d,p=this._dialogInst;return p||(this.uuid+=1,u="dp"+this.uuid,this._dialogInput=e("<input type='text' id='"+u+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),p=this._dialogInst=this._newInst(this._dialogInput,!1),p.settings={},e.data(this._dialogInput[0],r,p)),s(p.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(p,i):i,this._dialogInput.val(i),this._pos=o?o.length?o:[o.pageX,o.pageY]:null,this._pos||(c=document.documentElement.clientWidth,l=document.documentElement.clientHeight,h=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[c/2-100+h,l/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),p.settings.onSelect=a,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],r,p),this},_destroyDatepicker:function(t){var i,a=e(t),s=e.data(t,r);a.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),e.removeData(t,r),"input"===i?(s.append.remove(),s.trigger.remove(),a.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&a.removeClass(this.markerClassName).empty())},_enableDatepicker:function(t){var i,a,s=e(t),n=e.data(t,r);s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!1,n.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(a=s.children("."+this._inlineClass),a.children().removeClass("ui-state-disabled"),a.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var i,a,s=e(t),n=e.data(t,r);s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!0,n.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(a=s.children("."+this._inlineClass),a.children().addClass("ui-state-disabled"),a.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,r)}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(i,a,n){var r,o,u,c,l=this._getInst(i);return 2===arguments.length&&"string"==typeof a?"defaults"===a?e.extend({},e.datepicker._defaults):l?"all"===a?e.extend({},l.settings):this._get(l,a):null:(r=a||{},"string"==typeof a&&(r={},r[a]=n),l&&(this._curInst===l&&this._hideDatepicker(),o=this._getDateDatepicker(i,!0),u=this._getMinMaxDate(l,"min"),c=this._getMinMaxDate(l,"max"),s(l.settings,r),null!==u&&r.dateFormat!==t&&r.minDate===t&&(l.settings.minDate=this._formatDate(l,u)),null!==c&&r.dateFormat!==t&&r.maxDate===t&&(l.settings.maxDate=this._formatDate(l,c)),"disabled"in r&&(r.disabled?this._disableDatepicker(i):this._enableDatepicker(i)),this._attachments(e(i),l),this._autoSize(l),this._setDate(l,o),this._updateAlternate(l),this._updateDatepicker(l)),t)},_changeDatepicker:function(e,t,i){this._optionDatepicker(e,t,i)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var i=this._getInst(e);i&&(this._setDate(i,t),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(e,t){var i=this._getInst(e);return i&&!i.inline&&this._setDateFromField(i,t),i?this._getDate(i):null},_doKeyDown:function(t){var i,a,s,n=e.datepicker._getInst(t.target),r=!0,o=n.dpDiv.is(".ui-datepicker-rtl");if(n._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),r=!1;break;case 13:return s=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",n.dpDiv),s[0]&&e.datepicker._selectDay(t.target,n.selectedMonth,n.selectedYear,s[0]),i=e.datepicker._get(n,"onSelect"),i?(a=e.datepicker._formatDate(n),i.apply(n.input?n.input[0]:null,[a,n])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(n,"stepBigMonths"):-e.datepicker._get(n,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(n,"stepBigMonths"):+e.datepicker._get(n,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),r=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),r=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?1:-1,"D"),r=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(n,"stepBigMonths"):-e.datepicker._get(n,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),r=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?-1:1,"D"),r=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(n,"stepBigMonths"):+e.datepicker._get(n,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),r=t.ctrlKey||t.metaKey;break;default:r=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):r=!1;r&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(i){var a,s,n=e.datepicker._getInst(i.target);return e.datepicker._get(n,"constrainInput")?(a=e.datepicker._possibleChars(e.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==i.charCode?i.keyCode:i.charCode),i.ctrlKey||i.metaKey||" ">s||!a||a.indexOf(s)>-1):t},_doKeyUp:function(t){var i,a=e.datepicker._getInst(t.target);if(a.input.val()!==a.lastVal)try{i=e.datepicker.parseDate(e.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,e.datepicker._getFormatConfig(a)),i&&(e.datepicker._setDateFromField(a),e.datepicker._updateAlternate(a),e.datepicker._updateDatepicker(a))}catch(s){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var i,a,n,r,o,u,c;i=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==i&&(e.datepicker._curInst.dpDiv.stop(!0,!0),i&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),a=e.datepicker._get(i,"beforeShow"),n=a?a.apply(t,[t,i]):{},n!==!1&&(s(i.settings,n),i.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(i),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),r=!1,e(t).parents().each(function(){return r|="fixed"===e(this).css("position"),!r}),o={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(i),o=e.datepicker._checkOffset(i,o,r),i.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":r?"fixed":"absolute",display:"none",left:o.left+"px",top:o.top+"px"}),i.inline||(u=e.datepicker._get(i,"showAnim"),c=e.datepicker._get(i,"duration"),i.dpDiv.zIndex(e(t).zIndex()+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[u]?i.dpDiv.show(u,e.datepicker._get(i,"showOptions"),c):i.dpDiv[u||"show"](u?c:null),e.datepicker._shouldFocusInput(i)&&i.input.focus(),e.datepicker._curInst=i))}},_updateDatepicker:function(t){this.maxRows=4,n=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t),t.dpDiv.find("."+this._dayOverClass+" a").mouseover();var i,a=this._getNumberOfMonths(t),s=a[1],r=17;t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),s>1&&t.dpDiv.addClass("ui-datepicker-multi-"+s).css("width",r*s+"em"),t.dpDiv[(1!==a[0]||1!==a[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(i=t.yearshtml,setTimeout(function(){i===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),i=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,i,a){var s=t.dpDiv.outerWidth(),n=t.dpDiv.outerHeight(),r=t.input?t.input.outerWidth():0,o=t.input?t.input.outerHeight():0,u=document.documentElement.clientWidth+(a?0:e(document).scrollLeft()),c=document.documentElement.clientHeight+(a?0:e(document).scrollTop());return i.left-=this._get(t,"isRTL")?s-r:0,i.left-=a&&i.left===t.input.offset().left?e(document).scrollLeft():0,i.top-=a&&i.top===t.input.offset().top+o?e(document).scrollTop():0,i.left-=Math.min(i.left,i.left+s>u&&u>s?Math.abs(i.left+s-u):0),i.top-=Math.min(i.top,i.top+n>c&&c>n?Math.abs(n+o):0),i},_findPos:function(t){for(var i,a=this._getInst(t),s=this._get(a,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[s?"previousSibling":"nextSibling"];return i=e(t).offset(),[i.left,i.top]},_hideDatepicker:function(t){var i,a,s,n,o=this._curInst;!o||t&&o!==e.data(t,r)||this._datepickerShowing&&(i=this._get(o,"showAnim"),a=this._get(o,"duration"),s=function(){e.datepicker._tidyDialog(o)},e.effects&&(e.effects.effect[i]||e.effects[i])?o.dpDiv.hide(i,e.datepicker._get(o,"showOptions"),a,s):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?a:null,s),i||s(),this._datepickerShowing=!1,n=this._get(o,"onClose"),n&&n.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var i=e(t.target),a=e.datepicker._getInst(i[0]);(i[0].id!==e.datepicker._mainDivId&&0===i.parents("#"+e.datepicker._mainDivId).length&&!i.hasClass(e.datepicker.markerClassName)&&!i.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||i.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==a)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,i,a){var s=e(t),n=this._getInst(s[0]);this._isDisabledDatepicker(s[0])||(this._adjustInstDate(n,i+("M"===a?this._get(n,"showCurrentAtPos"):0),a),this._updateDatepicker(n))},_gotoToday:function(t){var i,a=e(t),s=this._getInst(a[0]);this._get(s,"gotoCurrent")&&s.currentDay?(s.selectedDay=s.currentDay,s.drawMonth=s.selectedMonth=s.currentMonth,s.drawYear=s.selectedYear=s.currentYear):(i=new Date,s.selectedDay=i.getDate(),s.drawMonth=s.selectedMonth=i.getMonth(),s.drawYear=s.selectedYear=i.getFullYear()),this._notifyChange(s),this._adjustDate(a)},_selectMonthYear:function(t,i,a){var s=e(t),n=this._getInst(s[0]);n["selected"+("M"===a?"Month":"Year")]=n["draw"+("M"===a?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(n),this._adjustDate(s)},_selectDay:function(t,i,a,s){var n,r=e(t);e(s).hasClass(this._unselectableClass)||this._isDisabledDatepicker(r[0])||(n=this._getInst(r[0]),n.selectedDay=n.currentDay=e("a",s).html(),n.selectedMonth=n.currentMonth=i,n.selectedYear=n.currentYear=a,this._selectDate(t,this._formatDate(n,n.currentDay,n.currentMonth,n.currentYear)))},_clearDate:function(t){var i=e(t);this._selectDate(i,"")},_selectDate:function(t,i){var a,s=e(t),n=this._getInst(s[0]);i=null!=i?i:this._formatDate(n),n.input&&n.input.val(i),this._updateAlternate(n),a=this._get(n,"onSelect"),a?a.apply(n.input?n.input[0]:null,[i,n]):n.input&&n.input.trigger("change"),n.inline?this._updateDatepicker(n):(this._hideDatepicker(),this._lastInput=n.input[0],"object"!=typeof n.input[0]&&n.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var i,a,s,n=this._get(t,"altField");n&&(i=this._get(t,"altFormat")||this._get(t,"dateFormat"),a=this._getDate(t),s=this.formatDate(i,a,this._getFormatConfig(t)),e(n).each(function(){e(this).val(s)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,i=new Date(e.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),t=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((t-i)/864e5)/7)+1},parseDate:function(i,a,s){if(null==i||null==a)throw"Invalid arguments";if(a="object"==typeof a?""+a:a+"",""===a)return null;var n,r,o,u,c=0,l=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,h="string"!=typeof l?l:(new Date).getFullYear()%100+parseInt(l,10),d=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,p=(s?s.dayNames:null)||this._defaults.dayNames,g=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,m=(s?s.monthNames:null)||this._defaults.monthNames,f=-1,_=-1,v=-1,k=-1,b=!1,y=function(e){var t=i.length>n+1&&i.charAt(n+1)===e;return t&&n++,t},D=function(e){var t=y(e),i="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,s=RegExp("^\\d{1,"+i+"}"),n=a.substring(c).match(s);if(!n)throw"Missing number at position "+c;return c+=n[0].length,parseInt(n[0],10)},w=function(i,s,n){var r=-1,o=e.map(y(i)?n:s,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(o,function(e,i){var s=i[1];return a.substr(c,s.length).toLowerCase()===s.toLowerCase()?(r=i[0],c+=s.length,!1):t}),-1!==r)return r+1;throw"Unknown name at position "+c},M=function(){if(a.charAt(c)!==i.charAt(n))throw"Unexpected literal at position "+c;c++};for(n=0;i.length>n;n++)if(b)"'"!==i.charAt(n)||y("'")?M():b=!1;else switch(i.charAt(n)){case"d":v=D("d");break;case"D":w("D",d,p);break;case"o":k=D("o");break;case"m":_=D("m");break;case"M":_=w("M",g,m);break;case"y":f=D("y");break;case"@":u=new Date(D("@")),f=u.getFullYear(),_=u.getMonth()+1,v=u.getDate();break;case"!":u=new Date((D("!")-this._ticksTo1970)/1e4),f=u.getFullYear(),_=u.getMonth()+1,v=u.getDate();break;case"'":y("'")?M():b=!0;break;default:M()}if(a.length>c&&(o=a.substr(c),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===f?f=(new Date).getFullYear():100>f&&(f+=(new Date).getFullYear()-(new Date).getFullYear()%100+(h>=f?0:-100)),k>-1)for(_=1,v=k;;){if(r=this._getDaysInMonth(f,_-1),r>=v)break;_++,v-=r}if(u=this._daylightSavingAdjust(new Date(f,_-1,v)),u.getFullYear()!==f||u.getMonth()+1!==_||u.getDate()!==v)throw"Invalid date";return u},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,i){if(!t)return"";var a,s=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,n=(i?i.dayNames:null)||this._defaults.dayNames,r=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,o=(i?i.monthNames:null)||this._defaults.monthNames,u=function(t){var i=e.length>a+1&&e.charAt(a+1)===t;return i&&a++,i},c=function(e,t,i){var a=""+t;if(u(e))for(;i>a.length;)a="0"+a;return a},l=function(e,t,i,a){return u(e)?a[t]:i[t]},h="",d=!1;if(t)for(a=0;e.length>a;a++)if(d)"'"!==e.charAt(a)||u("'")?h+=e.charAt(a):d=!1;else switch(e.charAt(a)){case"d":h+=c("d",t.getDate(),2);break;case"D":h+=l("D",t.getDay(),s,n);break;case"o":h+=c("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":h+=c("m",t.getMonth()+1,2);break;case"M":h+=l("M",t.getMonth(),r,o);break;case"y":h+=u("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":h+=t.getTime();break;case"!":h+=1e4*t.getTime()+this._ticksTo1970;break;case"'":u("'")?h+="'":d=!0;break;default:h+=e.charAt(a)}return h},_possibleChars:function(e){var t,i="",a=!1,s=function(i){var a=e.length>t+1&&e.charAt(t+1)===i;return a&&t++,a};for(t=0;e.length>t;t++)if(a)"'"!==e.charAt(t)||s("'")?i+=e.charAt(t):a=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":s("'")?i+="'":a=!0;break;default:i+=e.charAt(t)}return i},_get:function(e,i){return e.settings[i]!==t?e.settings[i]:this._defaults[i]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var i=this._get(e,"dateFormat"),a=e.lastVal=e.input?e.input.val():null,s=this._getDefaultDate(e),n=s,r=this._getFormatConfig(e);try{n=this.parseDate(i,a,r)||s}catch(o){a=t?"":a}e.selectedDay=n.getDate(),e.drawMonth=e.selectedMonth=n.getMonth(),e.drawYear=e.selectedYear=n.getFullYear(),e.currentDay=a?n.getDate():0,e.currentMonth=a?n.getMonth():0,e.currentYear=a?n.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,i,a){var s=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},n=function(i){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),i,e.datepicker._getFormatConfig(t))}catch(a){}for(var s=(i.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,n=s.getFullYear(),r=s.getMonth(),o=s.getDate(),u=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,c=u.exec(i);c;){switch(c[2]||"d"){case"d":case"D":o+=parseInt(c[1],10);break;case"w":case"W":o+=7*parseInt(c[1],10);break;case"m":case"M":r+=parseInt(c[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(n,r));break;case"y":case"Y":n+=parseInt(c[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(n,r))}c=u.exec(i)}return new Date(n,r,o)},r=null==i||""===i?a:"string"==typeof i?n(i):"number"==typeof i?isNaN(i)?a:s(i):new Date(i.getTime());return r=r&&"Invalid Date"==""+r?a:r,r&&(r.setHours(0),r.setMinutes(0),r.setSeconds(0),r.setMilliseconds(0)),this._daylightSavingAdjust(r)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,i){var a=!t,s=e.selectedMonth,n=e.selectedYear,r=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=r.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=r.getMonth(),e.drawYear=e.selectedYear=e.currentYear=r.getFullYear(),s===e.selectedMonth&&n===e.selectedYear||i||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(a?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var i=this._get(t,"stepMonths"),a="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(a,-i,"M")},next:function(){e.datepicker._adjustDate(a,+i,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(a)},selectDay:function(){return e.datepicker._selectDay(a,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(a,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(a,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,i,a,s,n,r,o,u,c,l,h,d,p,g,m,f,_,v,k,b,y,D,w,M,C,x,I,N,T,A,E,S,Y,F,P,O,j,K,R,H=new Date,W=this._daylightSavingAdjust(new Date(H.getFullYear(),H.getMonth(),H.getDate())),L=this._get(e,"isRTL"),U=this._get(e,"showButtonPanel"),B=this._get(e,"hideIfNoPrevNext"),z=this._get(e,"navigationAsDateFormat"),q=this._getNumberOfMonths(e),G=this._get(e,"showCurrentAtPos"),J=this._get(e,"stepMonths"),Q=1!==q[0]||1!==q[1],V=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),$=this._getMinMaxDate(e,"min"),X=this._getMinMaxDate(e,"max"),Z=e.drawMonth-G,et=e.drawYear;if(0>Z&&(Z+=12,et--),X)for(t=this._daylightSavingAdjust(new Date(X.getFullYear(),X.getMonth()-q[0]*q[1]+1,X.getDate())),t=$&&$>t?$:t;this._daylightSavingAdjust(new Date(et,Z,1))>t;)Z--,0>Z&&(Z=11,et--);for(e.drawMonth=Z,e.drawYear=et,i=this._get(e,"prevText"),i=z?this.formatDate(i,this._daylightSavingAdjust(new Date(et,Z-J,1)),this._getFormatConfig(e)):i,a=this._canAdjustMonth(e,-1,et,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"e":"w")+"'>"+i+"</span></a>":B?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"e":"w")+"'>"+i+"</span></a>",s=this._get(e,"nextText"),s=z?this.formatDate(s,this._daylightSavingAdjust(new Date(et,Z+J,1)),this._getFormatConfig(e)):s,n=this._canAdjustMonth(e,1,et,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+s+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"w":"e")+"'>"+s+"</span></a>":B?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+s+"'><span class='ui-icon ui-icon-circle-triangle-"+(L?"w":"e")+"'>"+s+"</span></a>",r=this._get(e,"currentText"),o=this._get(e,"gotoCurrent")&&e.currentDay?V:W,r=z?this.formatDate(r,o,this._getFormatConfig(e)):r,u=e.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(e,"closeText")+"</button>",c=U?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(L?u:"")+(this._isInRange(e,o)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+r+"</button>":"")+(L?"":u)+"</div>":"",l=parseInt(this._get(e,"firstDay"),10),l=isNaN(l)?0:l,h=this._get(e,"showWeek"),d=this._get(e,"dayNames"),p=this._get(e,"dayNamesMin"),g=this._get(e,"monthNames"),m=this._get(e,"monthNamesShort"),f=this._get(e,"beforeShowDay"),_=this._get(e,"showOtherMonths"),v=this._get(e,"selectOtherMonths"),k=this._getDefaultDate(e),b="",D=0;q[0]>D;D++){for(w="",this.maxRows=4,M=0;q[1]>M;M++){if(C=this._daylightSavingAdjust(new Date(et,Z,e.selectedDay)),x=" ui-corner-all",I="",Q){if(I+="<div class='ui-datepicker-group",q[1]>1)switch(M){case 0:I+=" ui-datepicker-group-first",x=" ui-corner-"+(L?"right":"left");break;case q[1]-1:I+=" ui-datepicker-group-last",x=" ui-corner-"+(L?"left":"right");break;default:I+=" ui-datepicker-group-middle",x=""}I+="'>"}for(I+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+x+"'>"+(/all|left/.test(x)&&0===D?L?n:a:"")+(/all|right/.test(x)&&0===D?L?a:n:"")+this._generateMonthYearHeader(e,Z,et,$,X,D>0||M>0,g,m)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",N=h?"<th class='ui-datepicker-week-col'>"+this._get(e,"weekHeader")+"</th>":"",y=0;7>y;y++)T=(y+l)%7,N+="<th"+((y+l+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+d[T]+"'>"+p[T]+"</span></th>";for(I+=N+"</tr></thead><tbody>",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),E=(this._getFirstDayOfMonth(et,Z)-l+7)%7,S=Math.ceil((E+A)/7),Y=Q?this.maxRows>S?this.maxRows:S:S,this.maxRows=Y,F=this._daylightSavingAdjust(new Date(et,Z,1-E)),P=0;Y>P;P++){for(I+="<tr>",O=h?"<td class='ui-datepicker-week-col'>"+this._get(e,"calculateWeek")(F)+"</td>":"",y=0;7>y;y++)j=f?f.apply(e.input?e.input[0]:null,[F]):[!0,""],K=F.getMonth()!==Z,R=K&&!v||!j[0]||$&&$>F||X&&F>X,O+="<td class='"+((y+l+6)%7>=5?" ui-datepicker-week-end":"")+(K?" ui-datepicker-other-month":"")+(F.getTime()===C.getTime()&&Z===e.selectedMonth&&e._keyEvent||k.getTime()===F.getTime()&&k.getTime()===C.getTime()?" "+this._dayOverClass:"")+(R?" "+this._unselectableClass+" ui-state-disabled":"")+(K&&!_?"":" "+j[1]+(F.getTime()===V.getTime()?" "+this._currentClass:"")+(F.getTime()===W.getTime()?" ui-datepicker-today":""))+"'"+(K&&!_||!j[2]?"":" title='"+j[2].replace(/'/g,"'")+"'")+(R?"":" data-handler='selectDay' data-event='click' data-month='"+F.getMonth()+"' data-year='"+F.getFullYear()+"'")+">"+(K&&!_?" ":R?"<span class='ui-state-default'>"+F.getDate()+"</span>":"<a class='ui-state-default"+(F.getTime()===W.getTime()?" ui-state-highlight":"")+(F.getTime()===V.getTime()?" ui-state-active":"")+(K?" ui-priority-secondary":"")+"' href='#'>"+F.getDate()+"</a>")+"</td>",F.setDate(F.getDate()+1),F=this._daylightSavingAdjust(F);I+=O+"</tr>"}Z++,Z>11&&(Z=0,et++),I+="</tbody></table>"+(Q?"</div>"+(q[0]>0&&M===q[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),w+=I}b+=w}return b+=c,e._keyEvent=!1,b},_generateMonthYearHeader:function(e,t,i,a,s,n,r,o){var u,c,l,h,d,p,g,m,f=this._get(e,"changeMonth"),_=this._get(e,"changeYear"),v=this._get(e,"showMonthAfterYear"),k="<div class='ui-datepicker-title'>",b="";if(n||!f)b+="<span class='ui-datepicker-month'>"+r[t]+"</span>";else{for(u=a&&a.getFullYear()===i,c=s&&s.getFullYear()===i,b+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",l=0;12>l;l++)(!u||l>=a.getMonth())&&(!c||s.getMonth()>=l)&&(b+="<option value='"+l+"'"+(l===t?" selected='selected'":"")+">"+o[l]+"</option>");b+="</select>"}if(v||(k+=b+(!n&&f&&_?"":" ")),!e.yearshtml)if(e.yearshtml="",n||!_)k+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(h=this._get(e,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?d+parseInt(e,10):parseInt(e,10); +return isNaN(t)?d:t},g=p(h[0]),m=Math.max(g,p(h[1]||"")),g=a?Math.max(g,a.getFullYear()):g,m=s?Math.min(m,s.getFullYear()):m,e.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";m>=g;g++)e.yearshtml+="<option value='"+g+"'"+(g===i?" selected='selected'":"")+">"+g+"</option>";e.yearshtml+="</select>",k+=e.yearshtml,e.yearshtml=null}return k+=this._get(e,"yearSuffix"),v&&(k+=(!n&&f&&_?"":" ")+b),k+="</div>"},_adjustInstDate:function(e,t,i){var a=e.drawYear+("Y"===i?t:0),s=e.drawMonth+("M"===i?t:0),n=Math.min(e.selectedDay,this._getDaysInMonth(a,s))+("D"===i?t:0),r=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(a,s,n)));e.selectedDay=r.getDate(),e.drawMonth=e.selectedMonth=r.getMonth(),e.drawYear=e.selectedYear=r.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),s=i&&i>t?i:t;return a&&s>a?a:s},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,a){var s=this._getNumberOfMonths(e),n=this._daylightSavingAdjust(new Date(i,a+(0>t?t:s[0]*s[1]),1));return 0>t&&n.setDate(this._getDaysInMonth(n.getFullYear(),n.getMonth())),this._isInRange(e,n)},_isInRange:function(e,t){var i,a,s=this._getMinMaxDate(e,"min"),n=this._getMinMaxDate(e,"max"),r=null,o=null,u=this._get(e,"yearRange");return u&&(i=u.split(":"),a=(new Date).getFullYear(),r=parseInt(i[0],10),o=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(r+=a),i[1].match(/[+\-].*/)&&(o+=a)),(!s||t.getTime()>=s.getTime())&&(!n||t.getTime()<=n.getTime())&&(!r||t.getFullYear()>=r)&&(!o||o>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,a){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var s=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(a,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),s,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new i,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.10.3"})(jQuery);(function(e){var t={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},i={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0};e.widget("ui.dialog",{version:"1.10.3",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var i=e(this).css(t).offset().top;0>i&&e(this).css("top",t.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var i=this;this._isOpen&&this._trigger("beforeClose",t)!==!1&&(this._isOpen=!1,this._destroyOverlay(),this.opener.filter(":focusable").focus().length||e(this.document[0].activeElement).blur(),this._hide(this.uiDialog,this.options.hide,function(){i._trigger("close",t)}))},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(e,t){var i=!!this.uiDialog.nextAll(":visible").insertBefore(this.uiDialog).length;return i&&!t&&this._trigger("focus",e),i},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),undefined):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._trigger("open"),undefined)},_focusTabbable:function(){var e=this.element.find("[autofocus]");e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()},_keepFocus:function(t){function i(){var t=this.document[0].activeElement,i=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);i||this._focusTabbable()}t.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=e("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),undefined;if(t.keyCode===e.ui.keyCode.TAB){var i=this.uiDialog.find(":tabbable"),a=i.filter(":first"),s=i.filter(":last");t.target!==s[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==a[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(s.focus(1),t.preventDefault()):(a.focus(1),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("<button></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html(" "),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(i)||e.isArray(i)&&!i.length?(this.uiDialog.removeClass("ui-dialog-buttons"),undefined):(e.each(i,function(i,a){var s,n;a=e.isFunction(a)?{click:a,text:i}:a,a=e.extend({type:"button"},a),s=a.click,a.click=function(){s.apply(t.element[0],arguments)},n={icons:a.icons,text:a.showText},delete a.icons,delete a.showText,e("<button></button>",a).button(n).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),undefined)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var i=this,a=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(a,s){e(this).addClass("ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",a,t(s))},drag:function(e,a){i._trigger("drag",e,t(a))},stop:function(s,n){a.position=[n.position.left-i.document.scrollLeft(),n.position.top-i.document.scrollTop()],e(this).removeClass("ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",s,t(n))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var i=this,a=this.options,s=a.resizable,n=this.uiDialog.css("position"),r="string"==typeof s?s:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:a.maxWidth,maxHeight:a.maxHeight,minWidth:a.minWidth,minHeight:this._minHeight(),handles:r,start:function(a,s){e(this).addClass("ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",a,t(s))},resize:function(e,a){i._trigger("resize",e,t(a))},stop:function(s,n){a.height=e(this).height(),a.width=e(this).width(),e(this).removeClass("ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",s,t(n))}}).css("position",n)},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(a){var s=this,n=!1,r={};e.each(a,function(e,a){s._setOption(e,a),e in t&&(n=!0),e in i&&(r[e]=a)}),n&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",r)},_setOption:function(e,t){var i,a,s=this.uiDialog;"dialogClass"===e&&s.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(i=s.is(":data(ui-draggable)"),i&&!t&&s.draggable("destroy"),!i&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(a=s.is(":data(ui-resizable)"),a&&!t&&s.resizable("destroy"),a&&"string"==typeof t&&s.resizable("option","handles",t),a||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,i,a=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),a.minWidth>a.width&&(a.width=a.minWidth),e=this.uiDialog.css({height:"auto",width:a.width}).outerHeight(),t=Math.max(0,a.minHeight-e),i="number"==typeof a.maxHeight?Math.max(0,a.maxHeight-e):"none","auto"===a.height?this.element.css({minHeight:t,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,a.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=this,i=this.widgetFullName;e.ui.dialog.overlayInstances||this._delay(function(){e.ui.dialog.overlayInstances&&this.document.bind("focusin.dialog",function(a){t._allowInteraction(a)||(a.preventDefault(),e(".ui-dialog:visible:last .ui-dialog-content").data(i)._focusTabbable())})}),this.overlay=e("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),e.ui.dialog.overlayInstances++}},_destroyOverlay:function(){this.options.modal&&this.overlay&&(e.ui.dialog.overlayInstances--,e.ui.dialog.overlayInstances||this.document.unbind("focusin.dialog"),this.overlay.remove(),this.overlay=null)}}),e.ui.dialog.overlayInstances=0,e.uiBackCompat!==!1&&e.widget("ui.dialog",e.ui.dialog,{_position:function(){var t,i=this.options.position,a=[],s=[0,0];i?(("string"==typeof i||"object"==typeof i&&"0"in i)&&(a=i.split?i.split(" "):[i[0],i[1]],1===a.length&&(a[1]=a[0]),e.each(["left","top"],function(e,t){+a[e]===a[e]&&(s[e]=a[e],a[e]=t)}),i={my:a[0]+(0>s[0]?s[0]:"+"+s[0])+" "+a[1]+(0>s[1]?s[1]:"+"+s[1]),at:a.join(" ")}),i=e.extend({},e.ui.dialog.prototype.options.position,i)):i=e.ui.dialog.prototype.options.position,t=this.uiDialog.is(":visible"),t||this.uiDialog.show(),this.uiDialog.position(i),t||this.uiDialog.hide()}})})(jQuery);(function(t){t.widget("ui.menu",{version:"1.10.3",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content ui-corner-all").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}).bind("click"+this.eventNamespace,t.proxy(function(t){this.options.disabled&&t.preventDefault()},this)),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item > a":function(t){t.preventDefault()},"click .ui-state-disabled > a":function(t){t.preventDefault()},"click .ui-menu-item:has(a)":function(e){var i=t(e.target).closest(".ui-menu-item");!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.mouseHandled=!0,this.select(e),i.has(".ui-menu").length?this.expand(e):this.element.is(":focus")||(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){var i=t(e.currentTarget);i.siblings().children(".ui-state-active").removeClass("ui-state-active"),this.focus(e,i)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.children(".ui-menu-item").eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){t.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){t(e.target).closest(".ui-menu").length||this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").children("a").removeUniqueId().removeClass("ui-corner-all ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var e=t(this);e.data("ui-menu-submenu-carat")&&e.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(e){function i(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var s,n,a,o,r,l=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:l=!1,n=this.previousFilter||"",a=String.fromCharCode(e.keyCode),o=!1,clearTimeout(this.filterTimer),a===n?o=!0:a=n+a,r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())}),s=o&&-1!==s.index(this.active.next())?this.active.nextAll(".ui-menu-item"):s,s.length||(a=String.fromCharCode(e.keyCode),r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())})),s.length?(this.focus(e,s),s.length>1?(this.previousFilter=a,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}l&&e.preventDefault()},_activate:function(t){this.active.is(".ui-state-disabled")||(this.active.children("a[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i=this.options.icons.submenu,s=this.element.find(this.options.menus);s.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-corner-all").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),s=e.prev("a"),n=t("<span>").addClass("ui-menu-icon ui-icon "+i).data("ui-menu-submenu-carat",!0);s.attr("aria-haspopup","true").prepend(n),e.attr("aria-labelledby",s.attr("id"))}),e=s.add(this.element),e.children(":not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","presentation").children("a").uniqueId().addClass("ui-corner-all").attr({tabIndex:-1,role:this._itemRole()}),e.children(":not(.ui-menu-item)").each(function(){var e=t(this);/[^\-\u2014\u2013\s]/.test(e.text())||e.addClass("ui-widget-content ui-menu-divider")}),e.children(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){"icons"===t&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(e.submenu),this._super(t,e)},focus:function(t,e){var i,s;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children("a").addClass("ui-state-focus"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=e.height(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this.active.children("a").removeClass("ui-state-focus"),this.active=null,this._trigger("blur",t,{item:this.active}))},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find("a.ui-state-active").removeClass("ui-state-active")},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").children(".ui-menu-item").first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.children(".ui-menu-item")[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item")[this.active?"last":"first"]())),undefined):(this.next(e),undefined)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item").first())),undefined):(this.next(e),undefined)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(e){this.active=this.active||t(e.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(e,!0),this._trigger("select",e,i)}})})(jQuery);(function(t,e){t.widget("ui.progressbar",{version:"1.10.3",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=t("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(t){return t===e?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),e)},_constrainedValue:function(t){return t===e&&(t=this.options.value),this.indeterminate=t===!1,"number"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),this._super(t,e)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=t("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),e===this.options.max&&this._trigger("complete")}})})(jQuery);(function(t){var e=5;t.widget("ui.slider",t.ui.mouse,{version:"1.10.3",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)o.push(a);this.handles=n.add(t(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data("ui-slider-handle-index",e)})},_createRange:function(){var e=this.options,i="";e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=t("<div></div>").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===e.range||"max"===e.range?" ui-slider-range-"+e.range:""))):this.range=t([])},_setupEvents:function(){var t=this.handles.add(this.range).filter("a");this._off(t),this._on(t,this._handleEvents),this._hoverable(t),this._focusable(t)},_destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,a,o,r,l,h,u=this,c=this.options;return c.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-u.values(e));(n>i||n===i&&(e===u._lastChangedValue||u.values(e)===c.min))&&(n=i,a=t(this),o=e)}),r=this._start(e,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),l=a.offset(),h=!t(e.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=h?{left:0,top:0}:{left:e.pageX-l.left-a.width()/2,top:e.pageY-l.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(e,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,i,s,n,a;return"horizontal"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(t,e){var i={handle:this.handles[e],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("start",t,i)},_slide:function(t,e,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(e?0:1),2===this.options.values.length&&this.options.range===!0&&(0===e&&i>s||1===e&&s>i)&&(i=s),i!==this.values(e)&&(n=this.values(),n[e]=i,a=this._trigger("slide",t,{handle:this.handles[e],value:i,values:n}),s=this.values(e?0:1),a!==!1&&this.values(e,i,!0))):i!==this.value()&&(a=this._trigger("slide",t,{handle:this.handles[e],value:i}),a!==!1&&this.value(i))},_stop:function(t,e){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("stop",t,i)},_change:function(t,e){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._lastChangedValue=e,this._trigger("change",t,i)}},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),undefined):this._value()},values:function(e,i){var s,n,a;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),undefined;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(e):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch("range"===e&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),t.Widget.prototype._setOption.apply(this,arguments),e){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var e,i,s,n,a,o=this.options.range,r=this.options,l=this,h=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((l.values(s)-l._valueMin())/(l._valueMax()-l._valueMin())),u["horizontal"===l.orientation?"left":"bottom"]=i+"%",t(this).stop(1,1)[h?"animate":"css"](u,r.animate),l.options.range===!0&&("horizontal"===l.orientation?(0===s&&l.range.stop(1,1)[h?"animate":"css"]({left:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:r.animate})):(0===s&&l.range.stop(1,1)[h?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[h?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[h?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[h?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(i){var s,n,a,o,r=t(i.target).data("ui-slider-handle-index");switch(i.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(i.preventDefault(),!this._keySliding&&(this._keySliding=!0,t(i.target).addClass("ui-state-active"),s=this._start(i,r),s===!1))return}switch(o=this.options.step,n=a=this.options.values&&this.options.values.length?this.values(r):this.value(),i.keyCode){case t.ui.keyCode.HOME:a=this._valueMin();break;case t.ui.keyCode.END:a=this._valueMax();break;case t.ui.keyCode.PAGE_UP:a=this._trimAlignValue(n+(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.PAGE_DOWN:a=this._trimAlignValue(n-(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(n===this._valueMax())return;a=this._trimAlignValue(n+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(n===this._valueMin())return;a=this._trimAlignValue(n-o)}this._slide(i,r,a)},click:function(t){t.preventDefault()},keyup:function(e){var i=t(e.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),t(e.target).removeClass("ui-state-active"))}}})})(jQuery);(function(t){function e(t){return function(){var e=this.element.val();t.apply(this,arguments),this._refresh(),e!==this.element.val()&&this._trigger("change")}}t.widget("ui.spinner",{version:"1.10.3",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e={},i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);void 0!==n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var t=this.element[0]===this.document[0].activeElement;t||(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var t=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=t.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*t.height())&&t.height()>0&&t.height(t.height()),this.options.disabled&&this.disable()},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>▲</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>▼</span>"+"</a>"},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){if("culture"===t||"numberFormat"===t){var i=this._parse(this.element.val());return this.options[t]=e,this.element.val(this._format(i)),void 0}("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(e.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(e.down)),this._super(t,e),"disabled"===t&&(e?(this.element.prop("disabled",!0),this.buttons.button("disable")):(this.element.prop("disabled",!1),this.buttons.button("enable")))},_setOptions:e(function(t){this._super(t),this._value(this.element.val())}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:e(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:e(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:e(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:e(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(e(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}})})(jQuery);(function(t,e){function i(){return++n}function s(t){return t.hash.length>1&&decodeURIComponent(t.href.replace(a,""))===decodeURIComponent(location.href.replace(a,""))}var n=0,a=/#.*$/;t.widget("ui.tabs",{version:"1.10.3",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_create:function(){var e=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var i=this.options.active,s=this.options.collapsible,n=location.hash.substring(1);return null===i&&(n&&this.tabs.each(function(s,a){return t(a).attr("aria-controls")===n?(i=s,!1):e}),null===i&&(i=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===i||-1===i)&&(i=this.tabs.length?0:!1)),i!==!1&&(i=this.tabs.index(this.tabs.eq(i)),-1===i&&(i=s?!1:0)),!s&&i===!1&&this.anchors.length&&(i=0),i},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(i){var s=t(this.document[0].activeElement).closest("li"),n=this.tabs.index(s),a=!0;if(!this._handlePageNav(i)){switch(i.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:n++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:a=!1,n--;break;case t.ui.keyCode.END:n=this.anchors.length-1;break;case t.ui.keyCode.HOME:n=0;break;case t.ui.keyCode.SPACE:return i.preventDefault(),clearTimeout(this.activating),this._activate(n),e;case t.ui.keyCode.ENTER:return i.preventDefault(),clearTimeout(this.activating),this._activate(n===this.options.active?!1:n),e;default:return}i.preventDefault(),clearTimeout(this.activating),n=this._focusNextTab(n,a),i.ctrlKey||(s.attr("aria-selected","false"),this.tabs.eq(n).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",n)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.focus())},_handlePageNav:function(i){return i.altKey&&i.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):i.altKey&&i.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):e},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).focus(),t},_setOption:function(t,i){return"active"===t?(this._activate(i),e):"disabled"===t?(this._setupDisabled(i),e):(this._super(t,i),"collapsible"===t&&(this.element.toggleClass("ui-tabs-collapsible",i),i||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(i),"heightStyle"===t&&this._setupHeightStyle(i),e)},_tabId:function(t){return t.attr("aria-controls")||"ui-tabs-"+i()},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-expanded":"false","aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-expanded":"true","aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=t(),this.anchors.each(function(i,n){var a,o,r,h=t(n).uniqueId().attr("id"),l=t(n).closest("li"),c=l.attr("aria-controls");s(n)?(a=n.hash,o=e.element.find(e._sanitizeSelector(a))):(r=e._tabId(l),a="#"+r,o=e.element.find(a),o.length||(o=e._createPanel(r),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),c&&l.data("ui-tabs-aria-controls",c),l.attr({"aria-controls":a.substring(1),"aria-labelledby":h}),o.attr("aria-labelledby",h)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.element.find("ol,ul").eq(0)},_createPanel:function(e){return t("<div>").attr("id",e).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(e){t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1);for(var i,s=0;i=this.tabs[s];s++)e===!0||-1!==t.inArray(s,e)?t(i).addClass("ui-state-disabled").attr("aria-disabled","true"):t(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=e},_setupEvents:function(e){var i={click:function(t){t.preventDefault()}};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?t():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():a,newPanel:h};e.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",e,c)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?t():a,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),e),this._toggle(e,c))},_toggle:function(e,i){function s(){a.running=!1,a._trigger("activate",e,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr({"aria-expanded":"false","aria-hidden":"true"}),i.oldTab.attr("aria-selected","false"),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr({"aria-expanded":"true","aria-hidden":"false"}),i.newTab.attr({"aria-selected":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(t){return"string"==typeof t&&(t=this.anchors.index(this.anchors.filter("[href$='"+t+"']"))),t},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(i){var s=this.options.disabled;s!==!1&&(i===e?s=!1:(i=this._getIndex(i),s=t.isArray(s)?t.map(s,function(t){return t!==i?t:null}):t.map(this.tabs,function(t,e){return e!==i?e:null})),this._setupDisabled(s))},disable:function(i){var s=this.options.disabled;if(s!==!0){if(i===e)s=!0;else{if(i=this._getIndex(i),-1!==t.inArray(i,s))return;s=t.isArray(s)?t.merge([i],s).sort():[i]}this._setupDisabled(s)}},load:function(e,i){e=this._getIndex(e);var n=this,a=this.tabs.eq(e),o=a.find(".ui-tabs-anchor"),r=this._getPanelForTab(a),h={tab:a,panel:r};s(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,h)),this.xhr&&"canceled"!==this.xhr.statusText&&(a.addClass("ui-tabs-loading"),r.attr("aria-busy","true"),this.xhr.success(function(t){setTimeout(function(){r.html(t),n._trigger("load",i,h)},1)}).complete(function(t,e){setTimeout(function(){"abort"===e&&n.panels.stop(!1,!0),a.removeClass("ui-tabs-loading"),r.removeAttr("aria-busy"),t===n.xhr&&delete n.xhr},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href"),beforeSend:function(e,a){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:a},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}})})(jQuery);(function(t){function e(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))}function i(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")}var s=0;t.widget("ui.tooltip",{version:"1.10.3",options:{content:function(){var e=t(this).attr("title")||"";return t("<a>").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable()},_setOption:function(e,i){var s=this;return"disabled"===e?(this[i?"_disable":"_enable"](),this.options[e]=i,void 0):(this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e)}),void 0)},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0)}),this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.is("[title]")&&e.data("ui-tooltip-title",e.attr("title")).attr("title","")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))})},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s?this._open(e,t,s):(i=s.call(t[0],function(i){t.data("ui-tooltip-open")&&n._delay(function(){e&&(e.type=o),this._open(e,t,i)})}),i&&this._open(e,t,i),void 0)},_open:function(i,s,n){function o(t){l.of=t,a.is(":hidden")||a.position(l)}var a,r,h,l=t.extend({},this.options.position);if(n){if(a=this._find(s),a.length)return a.find(".ui-tooltip-content").html(n),void 0;s.is("[title]")&&(i&&"mouseover"===i.type?s.attr("title",""):s.removeAttr("title")),a=this._tooltip(s),e(s,a.attr("id")),a.find(".ui-tooltip-content").html(n),this.options.track&&i&&/^mouse/.test(i.type)?(this._on(this.document,{mousemove:o}),o(i)):a.position(t.extend({of:s},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.show&&this.options.show.delay&&(h=this.delayedShow=setInterval(function(){a.is(":visible")&&(o(l.of),clearInterval(h))},t.fx.interval)),this._trigger("open",i,{tooltip:a}),r={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var i=t.Event(e);i.currentTarget=s[0],this.close(i,!0)}},remove:function(){this._removeTooltip(a)}},i&&"mouseover"!==i.type||(r.mouseleave="close"),i&&"focusin"!==i.type||(r.focusout="close"),this._on(!0,s,r)}},close:function(e){var s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);this.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&n.attr("title",n.data("ui-tooltip-title")),i(n),o.stop(!0),this._hide(o,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),this.closing=!0,this._trigger("close",e,{tooltip:o}),this.closing=!1)},_tooltip:function(e){var i="ui-tooltip-"+s++,n=t("<div>").attr({id:i,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return t("<div>").addClass("ui-tooltip-content").appendTo(n),n.appendTo(this.document[0].body),this.tooltips[i]=e,n},_find:function(e){var i=e.data("ui-tooltip-id");return i?t("#"+i):t()},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s[0],e.close(n,!0),t("#"+i).remove(),s.data("ui-tooltip-title")&&(s.attr("title",s.data("ui-tooltip-title")),s.removeData("ui-tooltip-title"))})}})})(jQuery);(function(t,e){var i="ui-effects-";t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=h(),n=s._rgba=[];return i=i.toLowerCase(),f(l,function(t,a){var o,r=a.re.exec(i),l=r&&a.parse(r),h=a.space||"rgba";return l?(o=s[h](l),s[c[h].cache]=o[c[h].cache],n=s._rgba=o._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,a.transparent),s):a[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,l=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],h=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=h.support={},p=t("<p>")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),h.fn=t.extend(h.prototype,{parse:function(n,o,r,l){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(o),o=e);var u=this,d=t.type(n),p=this._rgba=[];return o!==e&&(n=[n,o,r,l],d="array"),"string"===d?this.parse(s(n)||a._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof h?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var a=s.cache;f(s.props,function(t,e){if(!u[a]&&s.to){if("alpha"===t||null==n[t])return;u[a]=s.to(u._rgba)}u[a][e.idx]=i(n[t],e,!0)}),u[a]&&0>t.inArray(null,u[a].slice(0,3))&&(u[a][3]=1,s.from&&(u._rgba=s.from(u[a])))}),this):e},is:function(t){var i=h(t),s=!0,n=this;return f(c,function(t,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=h(t),n=s._space(),a=c[n],o=0===this.alpha()?h("transparent"):this,r=o[a.cache]||a.to(o._rgba),l=r.slice();return s=s[a.cache],f(a.props,function(t,n){var a=n.idx,o=r[a],h=s[a],c=u[n.type]||{};null!==h&&(null===o?l[a]=h:(c.mod&&(h-o>c.mod/2?o+=c.mod:o-h>c.mod/2&&(o-=c.mod)),l[a]=i((h-o)*e+o,n)))}),this[n](l)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=h(e)._rgba;return h(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),h.fn.parse.prototype=h.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,a=t[2]/255,o=t[3],r=Math.max(s,n,a),l=Math.min(s,n,a),h=r-l,c=r+l,u=.5*c;return e=l===r?0:s===r?60*(n-a)/h+360:n===r?60*(a-s)/h+120:60*(s-n)/h+240,i=0===h?0:.5>=u?h/c:h/(2-c),[Math.round(e)%360,i,u,null==o?1:o]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],a=t[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,e+1/3)),Math.round(255*n(r,o,e)),Math.round(255*n(r,o,e-1/3)),a]},f(c,function(s,n){var a=n.props,o=n.cache,l=n.to,c=n.from;h.fn[s]=function(s){if(l&&!this[o]&&(this[o]=l(this._rgba)),s===e)return this[o].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[o].slice();return f(a,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=h(c(d)),n[o]=d,n):h(d)},f(a,function(e,i){h.fn[e]||(h.fn[e]=function(n){var a,o=t.type(n),l="alpha"===e?this._hsla?"hsla":"rgba":s,h=this[l](),c=h[i.idx];return"undefined"===o?c:("function"===o&&(n=n.call(this,c),o=t.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=c+parseFloat(a[2])*("+"===a[1]?1:-1))),h[i.idx]=n,this[l](h)))})})}),h.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var a,o,r="";if("transparent"!==n&&("string"!==t.type(n)||(a=s(n)))){if(n=h(a||n),!d.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&o&&o.style;)try{r=t.css(o,"backgroundColor"),o=o.parentNode}catch(l){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(l){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=h(e.elem,i),e.end=h(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},h.hook(o),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},a=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function i(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function s(e,i){var s,n,o={};for(s in i)n=i[s],e[s]!==n&&(a[s]||(t.fx.step[s]||!isNaN(parseFloat(n)))&&(o[s]=n));return o}var n=["add","remove","toggle"],a={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(jQuery.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(e,a,o,r){var l=t.speed(a,o,r);return this.queue(function(){var a,o=t(this),r=o.attr("class")||"",h=l.children?o.find("*").addBack():o;h=h.map(function(){var e=t(this);return{el:e,start:i(this)}}),a=function(){t.each(n,function(t,i){e[i]&&o[i+"Class"](e[i])})},a(),h=h.map(function(){return this.end=i(this.el[0]),this.diff=s(this.start,this.end),this}),o.attr("class",r),h=h.map(function(){var e=this,i=t.Deferred(),s=t.extend({},l,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,h.get()).done(function(){a(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),l.complete.call(o[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,a){return s?t.effects.animateClass.call(this,{add:i},s,n,a):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,a){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,a):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(i){return function(s,n,a,o,r){return"boolean"==typeof n||n===e?a?t.effects.animateClass.call(this,n?{add:s}:{remove:s},a,o,r):i.apply(this,arguments):t.effects.animateClass.call(this,{toggle:s},n,a,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,a){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,a)}})}(),function(){function s(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function n(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}t.extend(t.effects,{version:"1.10.3",save:function(t,e){for(var s=0;e.length>s;s++)null!==e[s]&&t.data(i+e[s],t[0].style[e[s]])},restore:function(t,s){var n,a;for(a=0;s.length>a;a++)null!==s[a]&&(n=t.data(i+s[a]),n===e&&(n=""),t.css(s[a],n))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return e.wrap(s),(e[0]===a||t.contains(e[0],a))&&t(a).focus(),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).focus()),e},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var a=e.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),t.fn.extend({effect:function(){function e(e){function s(){t.isFunction(a)&&a.call(n[0]),t.isFunction(e)&&e()}var n=t(this),a=i.complete,r=i.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),s()):o.call(n[0],i,s)}var i=s.apply(this,arguments),n=i.mode,a=i.queue,o=t.effects.effect[i.effect];return t.fx.off||!o?n?this[n](i.duration,i.complete):this.each(function(){i.complete&&i.complete.call(this)}):a===!1?this.each(e):this.queue(a||"fx",e)},show:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="show",this.effect.call(this,i)}}(t.fn.show),hide:function(t){return function(e){if(n(e))return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="hide",this.effect.call(this,i)}}(t.fn.hide),toggle:function(t){return function(e){if(n(e)||"boolean"==typeof e)return t.apply(this,arguments);var i=s.apply(this,arguments);return i.mode="toggle",this.effect.call(this,i)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s}})}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}()})(jQuery);(function(t){var e=/up|down|vertical/,i=/up|left|vertical|horizontal/;t.effects.effect.blind=function(s,n){var a,o,r,l=t(this),h=["position","top","bottom","left","right","height","width"],c=t.effects.setMode(l,s.mode||"hide"),u=s.direction||"up",d=e.test(u),p=d?"height":"width",f=d?"top":"left",g=i.test(u),m={},v="show"===c;l.parent().is(".ui-effects-wrapper")?t.effects.save(l.parent(),h):t.effects.save(l,h),l.show(),a=t.effects.createWrapper(l).css({overflow:"hidden"}),o=a[p](),r=parseFloat(a.css(f))||0,m[p]=v?o:0,g||(l.css(d?"bottom":"right",0).css(d?"top":"left","auto").css({position:"absolute"}),m[f]=v?r:o+r),v&&(a.css(p,0),g||a.css(f,r+o)),a.animate(m,{duration:s.duration,easing:s.easing,queue:!1,complete:function(){"hide"===c&&l.hide(),t.effects.restore(l,h),t.effects.removeWrapper(l),n()}})}})(jQuery);(function(t){t.effects.effect.bounce=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","height","width"],l=t.effects.setMode(o,e.mode||"effect"),h="hide"===l,c="show"===l,u=e.direction||"up",d=e.distance,p=e.times||5,f=2*p+(c||h?1:0),g=e.duration/f,m=e.easing,v="up"===u||"down"===u?"top":"left",_="up"===u||"left"===u,b=o.queue(),y=b.length;for((c||h)&&r.push("opacity"),t.effects.save(o,r),o.show(),t.effects.createWrapper(o),d||(d=o["top"===v?"outerHeight":"outerWidth"]()/3),c&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,_?2*-d:2*d).animate(a,g,m)),h&&(d/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(_?"-=":"+=")+d,o.animate(n,g,m).animate(a,g,m),d=h?2*d:d/2;h&&(n={opacity:0},n[v]=(_?"-=":"+=")+d,o.animate(n,g,m)),o.queue(function(){h&&o.hide(),t.effects.restore(o,r),t.effects.removeWrapper(o),i()}),y>1&&b.splice.apply(b,[1,0].concat(b.splice(y,f+1))),o.dequeue()}})(jQuery);(function(t){t.effects.effect.clip=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","height","width"],l=t.effects.setMode(o,e.mode||"hide"),h="show"===l,c=e.direction||"vertical",u="vertical"===c,d=u?"height":"width",p=u?"top":"left",f={};t.effects.save(o,r),o.show(),s=t.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[d](),h&&(n.css(d,0),n.css(p,a/2)),f[d]=h?a:0,f[p]=h?0:a/2,n.animate(f,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){h||o.hide(),t.effects.restore(o,r),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.drop=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","opacity","height","width"],o=t.effects.setMode(n,e.mode||"hide"),r="show"===o,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l?"pos":"neg",u={opacity:r?1:0};t.effects.save(n,a),n.show(),t.effects.createWrapper(n),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(h,"pos"===c?-s:s),u[h]=(r?"pos"===c?"+=":"-=":"pos"===c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery);(function(t){t.effects.effect.explode=function(e,i){function s(){b.push(this),b.length===u*d&&n()}function n(){p.css({visibility:"visible"}),t(b).remove(),g||p.hide(),i()}var a,o,r,l,h,c,u=e.pieces?Math.round(Math.sqrt(e.pieces)):3,d=u,p=t(this),f=t.effects.setMode(p,e.mode||"hide"),g="show"===f,m=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/d),_=Math.ceil(p.outerHeight()/u),b=[];for(a=0;u>a;a++)for(l=m.top+a*_,c=a-(u-1)/2,o=0;d>o;o++)r=m.left+o*v,h=o-(d-1)/2,p.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*_}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:_,left:r+(g?h*v:0),top:l+(g?c*_:0),opacity:g?0:1}).animate({left:r+(g?0:h*v),top:l+(g?0:c*_),opacity:g?1:0},e.duration||500,e.easing,s)}})(jQuery);(function(t){t.effects.effect.fade=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:e.duration,easing:e.easing,complete:i})}})(jQuery);(function(t){t.effects.effect.fold=function(e,i){var s,n,a=t(this),o=["position","top","bottom","left","right","height","width"],r=t.effects.setMode(a,e.mode||"hide"),l="show"===r,h="hide"===r,c=e.size||15,u=/([0-9]+)%/.exec(c),d=!!e.horizFirst,p=l!==d,f=p?["width","height"]:["height","width"],g=e.duration/2,m={},v={};t.effects.save(a,o),a.show(),s=t.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],u&&(c=parseInt(u[1],10)/100*n[h?0:1]),l&&s.css(d?{height:0,width:c}:{height:c,width:0}),m[f[0]]=l?n[0]:c,v[f[1]]=l?n[1]:0,s.animate(m,g,e.easing).animate(v,g,e.easing,function(){h&&a.hide(),t.effects.restore(a,o),t.effects.removeWrapper(a),i()})}})(jQuery);(function(t){t.effects.effect.highlight=function(e,i){var s=t(this),n=["backgroundImage","backgroundColor","opacity"],a=t.effects.setMode(s,e.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),t.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:e.color||"#ffff99"}).animate(o,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===a&&s.hide(),t.effects.restore(s,n),i()}})}})(jQuery);(function(t){t.effects.effect.pulsate=function(e,i){var s,n=t(this),a=t.effects.setMode(n,e.mode||"show"),o="show"===a,r="hide"===a,l=o||"hide"===a,h=2*(e.times||5)+(l?1:0),c=e.duration/h,u=0,d=n.queue(),p=d.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),u=1),s=1;h>s;s++)n.animate({opacity:u},c,e.easing),u=1-u;n.animate({opacity:u},c,e.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&d.splice.apply(d,[1,0].concat(d.splice(p,h+1))),n.dequeue()}})(jQuery);(function(t){t.effects.effect.puff=function(e,i){var s=t(this),n=t.effects.setMode(s,e.mode||"hide"),a="hide"===n,o=parseInt(e.percent,10)||150,r=o/100,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};t.extend(e,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?l:{height:l.height*r,width:l.width*r,outerHeight:l.outerHeight*r,outerWidth:l.outerWidth*r}}),s.effect(e)},t.effects.effect.scale=function(e,i){var s=t(this),n=t.extend(!0,{},e),a=t.effects.setMode(s,e.mode||"effect"),o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"hide"===a?0:100),r=e.direction||"both",l=e.origin,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},c={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=l||["middle","center"],n.restore=!0),n.from=e.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:h),n.to={height:h.height*c.y,width:h.width*c.x,outerHeight:h.outerHeight*c.y,outerWidth:h.outerWidth*c.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},t.effects.effect.size=function(e,i){var s,n,a,o=t(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],l=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],c=["fontSize"],u=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],d=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=t.effects.setMode(o,e.mode||"effect"),f=e.restore||"effect"!==p,g=e.scale||"both",m=e.origin||["middle","center"],v=o.css("position"),_=f?r:l,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===e.mode&&"show"===p?(o.from=e.to||b,o.to=e.from||s):(o.from=e.from||("show"===p?b:s),o.to=e.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===g||"both"===g)&&(a.from.y!==a.to.y&&(_=_.concat(u),o.from=t.effects.setTransition(o,u,a.from.y,o.from),o.to=t.effects.setTransition(o,u,a.to.y,o.to)),a.from.x!==a.to.x&&(_=_.concat(d),o.from=t.effects.setTransition(o,d,a.from.x,o.from),o.to=t.effects.setTransition(o,d,a.to.x,o.to))),("content"===g||"both"===g)&&a.from.y!==a.to.y&&(_=_.concat(c).concat(h),o.from=t.effects.setTransition(o,c,a.from.y,o.from),o.to=t.effects.setTransition(o,c,a.to.y,o.to)),t.effects.save(o,_),o.show(),t.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),m&&(n=t.effects.getBaseline(m,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===g||"both"===g)&&(u=u.concat(["marginTop","marginBottom"]).concat(c),d=d.concat(["marginLeft","marginRight"]),h=r.concat(u).concat(d),o.find("*[width]").each(function(){var i=t(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};f&&t.effects.save(i,h),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=t.effects.setTransition(i,u,a.from.y,i.from),i.to=t.effects.setTransition(i,u,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=t.effects.setTransition(i,d,a.from.x,i.from),i.to=t.effects.setTransition(i,d,a.to.x,i.to)),i.css(i.from),i.animate(i.to,e.duration,e.easing,function(){f&&t.effects.restore(i,h)})})),o.animate(o.to,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),t.effects.restore(o,_),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):t.each(["top","left"],function(t,e){o.css(e,function(e,i){var s=parseInt(i,10),n=t?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),t.effects.removeWrapper(o),i()}})}})(jQuery);(function(t){t.effects.effect.shake=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","height","width"],o=t.effects.setMode(n,e.mode||"effect"),r=e.direction||"left",l=e.distance||20,h=e.times||3,c=2*h+1,u=Math.round(e.duration/c),d="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},g={},m={},v=n.queue(),_=v.length;for(t.effects.save(n,a),n.show(),t.effects.createWrapper(n),f[d]=(p?"-=":"+=")+l,g[d]=(p?"+=":"-=")+2*l,m[d]=(p?"-=":"+=")+2*l,n.animate(f,u,e.easing),s=1;h>s;s++)n.animate(g,u,e.easing).animate(m,u,e.easing);n.animate(g,u,e.easing).animate(f,u/2,e.easing).queue(function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}),_>1&&v.splice.apply(v,[1,0].concat(v.splice(_,c+1))),n.dequeue()}})(jQuery);(function(t){t.effects.effect.slide=function(e,i){var s,n=t(this),a=["position","top","bottom","left","right","width","height"],o=t.effects.setMode(n,e.mode||"show"),r="show"===o,l=e.direction||"left",h="up"===l||"down"===l?"top":"left",c="up"===l||"left"===l,u={};t.effects.save(n,a),n.show(),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0),t.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(h,c?isNaN(s)?"-"+s:-s:s),u[h]=(r?c?"+=":"-=":c?"-=":"+=")+s,n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){"hide"===o&&n.hide(),t.effects.restore(n,a),t.effects.removeWrapper(n),i()}})}})(jQuery);(function(t){t.effects.effect.transfer=function(e,i){var s=t(this),n=t(e.to),a="fixed"===n.css("position"),o=t("body"),r=a?o.scrollTop():0,l=a?o.scrollLeft():0,h=n.offset(),c={top:h.top-r,left:h.left-l,height:n.innerHeight(),width:n.innerWidth()},d=s.offset(),u=t("<div class='ui-effects-transfer'></div>").appendTo(document.body).addClass(e.className).css({top:d.top-r,left:d.left-l,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(c,e.duration,e.easing,function(){u.remove(),i()})}})(jQuery); \ No newline at end of file diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index 8cb4de81737beb78afbc895e8d980b7b7fd39064..0fd53146f7e986b754f48e565476dffcf0dbbe18 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -130,12 +130,13 @@ $(function() { 'air': el.hasClass('no-bar'), 'airButtons': ['formatting', '|', 'bold', 'italic', 'deleted', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', 'image'], 'autoresize': !el.hasClass('no-bar'), - 'minHeight': 150, + 'minHeight': el.hasClass('small') ? 75 : 150, 'focus': false, 'plugins': ['fontcolor','fontfamily'], 'imageGetJson': 'ajax.php/draft/images/browse', 'syncBeforeCallback': captureImageSizes }; + if (el.data('redactor')) return; if (el.hasClass('draft')) { var reset = $('input[type=reset]', el.closest('form')), draft_saved = $('<span>') @@ -174,16 +175,20 @@ $(function() { options['draft_object_id'] = el.data('draftObjectId'); } el.redactor(options); - } - $('.richtext').each(function(i,el) { - if ($(el).hasClass('ifhtml')) - // Check if html_thread is enabled first - getConfig().then(function(c) { - if (c.html_thread) - redact(el); - }); - else - // Make a rich text editor immediately - redact(el); - }); + }, + findRichtextBoxes = function() { + $('.richtext').each(function(i,el) { + if ($(el).hasClass('ifhtml')) + // Check if html_thread is enabled first + getConfig().then(function(c) { + if (c.html_thread) + redact(el); + }); + else + // Make a rich text editor immediately + redact(el); + }); + }; + findRichtextBoxes(); + $('#user-info').ajaxComplete(findRichtextBoxes); }); diff --git a/main.inc.php b/main.inc.php index 5dd4109d3f6f4b940e95243cfba7901958f877fd..56da2cb710261bb7fa21c04c700aa9823981f669 100644 --- a/main.inc.php +++ b/main.inc.php @@ -1,42 +1,43 @@ <?php /********************************************************************* - main.inc.php +main.inc.php - Master include file which must be included at the start of every file. - The brain of the whole sytem. Don't monkey with it. +Master include file which must be included at the start of every file. +The brain of the whole sytem. Don't monkey with it. - Peter Rotich <peter@osticket.com> - Copyright (c) 2006-2013 osTicket - http://www.osticket.com +Peter Rotich <peter@osticket.com> +Copyright (c) 2006-2013 osTicket +http://www.osticket.com - Released under the GNU General Public License WITHOUT ANY WARRANTY. - See LICENSE.TXT for details. +Released under the GNU General Public License WITHOUT ANY WARRANTY. +See LICENSE.TXT for details. - vim: expandtab sw=4 ts=4 sts=4: +vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ - #Disable direct access. - if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']),basename(__FILE__))) die('kwaheri rafiki!'); +#Disable direct access. +if(!strcasecmp(basename($_SERVER['SCRIPT_NAME']),basename(__FILE__))) die('kwaheri rafiki!'); - require('bootstrap.php'); - Bootstrap::loadConfig(); - Bootstrap::defineTables(TABLE_PREFIX); - Bootstrap::connect(); +require('bootstrap.php'); +Bootstrap::loadConfig(); +Bootstrap::defineTables(TABLE_PREFIX); +Bootstrap::loadCode(); +Bootstrap::connect(); - if(!($ost=osTicket::start()) || !($cfg = $ost->getConfig())) - Bootstrap::croak('Unable to load config info from DB. Get tech support.'); +if(!($ost=osTicket::start()) || !($cfg = $ost->getConfig())) +Bootstrap::croak('Unable to load config info from DB. Get tech support.'); - //Init - $session = $ost->getSession(); +//Init +$session = $ost->getSession(); - //System defaults we might want to make global// - #pagenation default - user can override it! - define('DEFAULT_PAGE_LIMIT', $cfg->getPageSize()?$cfg->getPageSize():25); +//System defaults we might want to make global// +#pagenation default - user can override it! +define('DEFAULT_PAGE_LIMIT', $cfg->getPageSize()?$cfg->getPageSize():25); - #Cleanup magic quotes crap. - if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { - $_POST=Format::strip_slashes($_POST); - $_GET=Format::strip_slashes($_GET); - $_REQUEST=Format::strip_slashes($_REQUEST); - } +#Cleanup magic quotes crap. +if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { +$_POST=Format::strip_slashes($_POST); +$_GET=Format::strip_slashes($_GET); +$_REQUEST=Format::strip_slashes($_REQUEST); +} ?> diff --git a/open.php b/open.php index db51e85d395fab9225dbb4ca0efb88df7f7139cb..468f700b562289c56cda08d69c465acec420a63c 100644 --- a/open.php +++ b/open.php @@ -30,13 +30,38 @@ if($_POST): $errors['captcha']='Invalid - try again!'; } - if(!$errors && $cfg->allowOnlineAttachments() && $_FILES['attachments']) + $interest = array('subject'); + if ($topic = Topic::lookup($vars['topicId'])) { + if ($form = DynamicForm::lookup($topic->ht['form_id'])) { + $form = $form->instanciate(); + if (!$form->isValid()) + $errors += $form->errors(); + } + } + // Don't process contact information for logged-in clients + if (!$thisclient) { + $contact_form = UserForm::getInstance(); + if (!$contact_form->isValid()) + $errors += $contact_form->errors(); + } + + if (!$errors && $cfg->allowOnlineAttachments() && $_FILES['attachments']) $vars['files'] = AttachmentFile::format($_FILES['attachments'], true); //Ticket::create...checks for errors.. if(($ticket=Ticket::create($vars, $errors, SOURCE))){ $msg='Support ticket request created'; Draft::deleteForNamespace('ticket.client.'.substr(session_id(), -12)); + # TODO: Save dynamic form(s) + if (isset($form)) { + $form->setTicketId($ticket->getId()); + $form->save(); + $ticket->loadDynamicData(); + } + if (isset($contact_form)) { + $contact_form->setClientId($ticket->getOwnerId()); + $contact_form->save(); + } //Logged in...simply view the newly created ticket. if($thisclient && $thisclient->isValid()) { if(!$cfg->showRelatedTickets()) diff --git a/scp/ajax.php b/scp/ajax.php index 3eeca0f0062e868f67aa660739943b356edd43fe..46fc6385cba5f40b401e5744730bd2763f09dfde 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -45,6 +45,13 @@ $dispatcher = patterns('', url('^/config/', patterns('ajax.config.php:ConfigAjaxAPI', url_get('^scp', 'scp') )), + url('^/form/', patterns('ajax.forms.php:DynamicFormsAjaxAPI', + url_get('^help-topic/(?P<id>\d+)$', 'getFormsForHelpTopic'), + url_get('^field-config/(?P<id>\d+)$', 'getFieldConfiguration'), + url_post('^field-config/(?P<id>\d+)$', 'saveFieldConfiguration'), + url_get('^user-info/(?P<id>\d+)$', 'getUserInfo'), + url_post('^user-info/(?P<id>\d+)$', 'saveUserInfo') + )), url('^/report/overview/', patterns('ajax.reports.php:OverviewReportAjaxAPI', # Send url_get('^graph$', 'getPlotData'), diff --git a/scp/css/scp.css b/scp/css/scp.css index 648d05c1348e68f8ea600ef8338caaf7d5dea101..557f03442b14d53f3ecb72d18207125acc3a337b 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -295,6 +295,9 @@ a.newDepartment { background:url(../images/icons/new_department.gif) } a.pages { background:url(../images/icons/pages.gif) } a.newPage { background:url(../images/icons/new_page.gif) } +a.forms { background:url(../images/icons/icon-form.png); background-size: 16px 16px; } +a.lists { background:url(../images/icons/icon-list.png); background-size: 16px 16px; } + /* Generic CSS based Icons. use=> <tag class="Icon iconname">text</tag> */ .Icon { @@ -302,6 +305,8 @@ a.newPage { background:url(../images/icons/new_page.gif) } padding-left:20px; background-position: left center; background-repeat: no-repeat; + min-height: 16px; + display: inline-block; } @@ -352,6 +357,8 @@ a.Icon:hover { .Icon.alertLog { background:url(../images/icons/log_alert.gif) 0 2px no-repeat; } .Icon.errorLog { background:url(../images/icons/log_error.gif) 0 2px no-repeat; } +.Icon.form-add { background:url(../images/icons/icon-form-add.png) 0 0 no-repeat; background-size: 16px 16px; } +.Icon.list-add { background:url(../images/icons/icon-list-add.png) 0 0 no-repeat; background-size: 16px 16px; } #content { @@ -536,6 +543,8 @@ a.print { .form_table td.multi-line { vertical-align:top; + padding-top: 0.4em; + padding-bottom: 0.4em; } .form_table input[type=text], .form_table input[type=password], .form_table textarea { @@ -565,23 +574,31 @@ a.print { border:1px solid #f00; } -.form_table th { +.form_table th, div.section-break { text-align:left; border:1px solid #ccc; background:#eee; padding:0; + padding:5px; +} + +div.section-break h3 { + margin: 0; + padding: 0; } .form_table th h4 { - margin:0; - padding:5px; + margin: -0.3em -0.3em; color:#fff; background:#929292; + padding: 0.3em; } +.form_table th em:not(:first-child) { + margin-top: 0.6em; +} .form_table th em { display:block; - padding:5px; color:#000; } @@ -1195,13 +1212,13 @@ time { background:#f8f8f8; border:2px solid #2a67ac; display:none; - z-index:1200; + z-index:5; box-shadow: 0 5px 60px #001; border-radius: 5px; } .redactor_air { - z-index: 1201 !important; + z-index: 6 !important; } .dialog#advanced-search { @@ -1328,6 +1345,14 @@ time { background-position:bottom left; } +/* Dynamic forms in dialogs */ +.dialog th { + background-color: #eee; + padding: 0.3em; + text-align: left; + padding-left: 1em; +} + #result-count div { padding:5px 10px; text-align:left; @@ -1395,7 +1420,7 @@ ul.progress li.no small {color:red;} width: 100%; height: 100%; background: #000; - z-index: 1000; + z-index: 1; -webkit-transform: translate3d(0,0,0); } @@ -1455,3 +1480,7 @@ ul.progress li.no small {color:red;} display: block; opacity: 0.3; } + +input[type=text]:disabled, input[type=checkbox]:disabled { + background-color: #eee; +} diff --git a/scp/forms.php b/scp/forms.php new file mode 100644 index 0000000000000000000000000000000000000000..5c0a2ad0481f53c013cc4cd509d778a91a0dd2ba --- /dev/null +++ b/scp/forms.php @@ -0,0 +1,109 @@ +<?php +require('admin.inc.php'); +require_once(INCLUDE_DIR."/class.dynamic_forms.php"); + +$form=null; +if($_REQUEST['id'] && !($form=DynamicForm::lookup($_REQUEST['id']))) + $errors['err']='Unknown or invalid dynamic form ID.'; + +if($_POST) { + $fields = array('title', 'notes', 'instructions'); + $required = array('subject'); + $max_sort = 0; + switch(strtolower($_POST['do'])) { + case 'update': + foreach ($fields as $f) + if (isset($_POST[$f])) + $form->set($f, $_POST[$f]); + if ($form->isValid()) + $form->save(true); + foreach ($form->getDynamicFields() as $field) { + $id = $field->get('id'); + if ($_POST["delete-$id"] == 'on' && $field->isDeletable()) { + $field->delete(); + // Don't bother updating the field + continue; + } + if (isset($_POST["type-$id"]) && $field->isChangeable()) + $field->set('type', $_POST["type-$id"]); + if (isset($_POST["name-$id"]) && !$field->isNameForced()) + $field->set('name', $_POST["name-$id"]); + # TODO: make sure all help topics still have all required fields + if (!$field->isRequirementForced()) + $field->set('required', $_POST["required-$id"] == 'on' ? 1 : 0); + if (!$field->isPrivacyForced()) + $field->set('private', $_POST["private-$id"] == 'on' ? 1 : 0); + foreach (array('sort','label') as $f) { + if (isset($_POST["$f-$id"])) { + $field->set($f, $_POST["$f-$id"]); + } + } + if ($field->isValid()) + $field->save(); + // Keep track of the last sort number + $max_sort = max($max_sort, $field->get('sort')); + } + break; + case 'add': + $form = DynamicForm::create(array( + 'title'=>$_POST['title'], + 'instructions'=>$_POST['instructions'], + 'notes'=>$_POST['notes'])); + if ($form->isValid()) + $form->save(true); + break; + + case 'mass_process': + if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { + $errors['err'] = 'You must select at least one API key'; + } else { + $count = count($_POST['ids']); + switch(strtolower($_POST['a'])) { + case 'delete': + $i=0; + foreach($_POST['ids'] as $k=>$v) { + if(($t=DynamicForm::lookup($v)) && $t->delete()) + $i++; + } + if ($i && $i==$count) + $msg = 'Selected custom forms deleted successfully'; + elseif ($i > 0) + $warn = "$i of $count selected forms deleted"; + elseif (!$errors['err']) + $errors['err'] = 'Unable to delete selected custom forms'; + break; + } + } + break; + } + + if ($form) { + for ($i=0; isset($_POST["sort-new-$i"]); $i++) { + if (!$_POST["label-new-$i"]) + continue; + $field = DynamicFormField::create(array( + 'form_id'=>$form->get('id'), + 'sort'=>$_POST["sort-new-$i"] ? $_POST["sort-new-$i"] : $max_sort++, + 'label'=>$_POST["label-new-$i"], + 'type'=>$_POST["type-new-$i"], + 'name'=>$_POST["name-new-$i"], + 'private'=>$_POST["private-new-$i"] == 'on' ? 1 : 0, + 'required'=>$_POST["required-new-$i"] == 'on' ? 1 : 0 + )); + if ($field->isValid()) + $field->save(); + } + // XXX: Move to an instrumented list that can handle this better + $form->_dfields = $form->_fields = null; + } +} + +$page='dynamic-forms.inc.php'; +if($form || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) + $page='dynamic-form.inc.php'; + +$nav->setTabActive('manage'); +require(STAFFINC_DIR.'header.inc.php'); +require(STAFFINC_DIR.$page); +include(STAFFINC_DIR.'footer.inc.php'); +?> diff --git a/scp/helptopics.php b/scp/helptopics.php index 7cb150741e307951f5d80d1a4ba5d5b30ffeee54..9f34fab9e848a01f36928a266cc6b69d2cb09d09 100644 --- a/scp/helptopics.php +++ b/scp/helptopics.php @@ -15,6 +15,7 @@ **********************************************************************/ require('admin.inc.php'); include_once(INCLUDE_DIR.'class.topic.php'); +require_once(INCLUDE_DIR.'class.dynamic_forms.php'); $topic=null; if($_REQUEST['id'] && !($topic=Topic::lookup($_REQUEST['id']))) @@ -95,6 +96,9 @@ if($_POST){ $errors['err']='Unknown command/action'; break; } + if ($id or $topic) { + if (!$id) $id=$topic->getId(); + } } $page='helptopics.inc.php'; diff --git a/scp/images/icons/icon-form-add.png b/scp/images/icons/icon-form-add.png new file mode 100644 index 0000000000000000000000000000000000000000..55a300f10b9f8ccccd1bc7f8005f03aa9cc03634 Binary files /dev/null and b/scp/images/icons/icon-form-add.png differ diff --git a/scp/images/icons/icon-form.png b/scp/images/icons/icon-form.png new file mode 100644 index 0000000000000000000000000000000000000000..810092eeab835eb478442b5971cc92f4a4b0e7e0 Binary files /dev/null and b/scp/images/icons/icon-form.png differ diff --git a/scp/images/icons/icon-list-add.png b/scp/images/icons/icon-list-add.png new file mode 100644 index 0000000000000000000000000000000000000000..425e949d33bd31f44e49feffa45fee319e46af32 Binary files /dev/null and b/scp/images/icons/icon-list-add.png differ diff --git a/scp/images/icons/icon-list.png b/scp/images/icons/icon-list.png new file mode 100644 index 0000000000000000000000000000000000000000..1b6ac133d500da5c83c7f97cb07d26ce96d9d736 Binary files /dev/null and b/scp/images/icons/icon-list.png differ diff --git a/scp/js/scp.js b/scp/js/scp.js index d7db69d13008ea9d98e15b757f0a1b217f735493..961e9e9326516fff7de9bdf98a88ff7b53a4d77b 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -295,13 +295,14 @@ $(document).ready(function(){ }); }, onselect: function (obj) { + $('#basic-ticket-search').val(obj.value); $('#basic-ticket-search').closest('form').submit(); }, - property: "value" + property: "matches" }); /* Typeahead user lookup */ - $('#email.typeahead').typeahead({ + $('.email.typeahead').typeahead({ source: function (typeahead, query) { if(query.length > 2) { $.ajax({ @@ -314,9 +315,9 @@ $(document).ready(function(){ } }, onselect: function (obj) { - var fObj=$('#email.typeahead').closest('form'); + var fObj=$('.email.typeahead').closest('form'); if(obj.name) - $('#name', fObj).val(obj.name); + $('.auto.name', fObj).val(obj.name); }, property: "email" }); @@ -419,6 +420,24 @@ $(document).ready(function(){ $('.buttons', elem).show(); }); }); + + // Return a helper with preserved width of cells + var fixHelper = function(e, ui) { + ui.children().each(function() { + $(this).width($(this).width()); + }); + return ui; + }; + // Sortable tables for dynamic forms objects + $('.sortable-rows').sortable({ + 'helper': fixHelper, + 'stop': function(e, ui) { + var attr = ui.item.parent('tbody').data('sort'); + $('input[name^='+attr+']', ui.item.parent('tbody')).each(function(i, el) { + $(el).val(i+1); + }); + } + }); }); // NOTE: getConfig should be global diff --git a/scp/lists.php b/scp/lists.php new file mode 100644 index 0000000000000000000000000000000000000000..81cf2663d2e4d3cfc3f26bc48dbee0da150cb679 --- /dev/null +++ b/scp/lists.php @@ -0,0 +1,92 @@ +<?php +require('admin.inc.php'); +require_once(INCLUDE_DIR."/class.dynamic_forms.php"); + +$list=null; +if($_REQUEST['id'] && !($list=DynamicList::lookup($_REQUEST['id']))) + $errors['err']='Unknown or invalid dynamic list ID.'; + +if($_POST) { + $fields = array('name', 'name_plural', 'sort_mode', 'notes'); + switch(strtolower($_POST['do'])) { + case 'update': + foreach ($fields as $f) + if (isset($_POST[$f])) + $list->set($f, $_POST[$f]); + if ($list->isValid()) + $list->save(true); + foreach ($list->getItems() as $item) { + $id = $item->get('id'); + if ($_POST["delete-$id"] == 'on') { + $item->delete(); + continue; + } + foreach (array('sort','value','extra') as $i) + if (isset($_POST["$i-$id"])) + $item->set($i, $_POST["$i-$id"]); + if ($item->isValid()) + $item->save(); + } + break; + case 'add': + $list = DynamicList::create(array( + 'name'=>$_POST['name'], + 'name_plural'=>$_POST['name_plural'], + 'sort_mode'=>$_POST['sort_mode'], + 'notes'=>$_POST['notes'])); + if ($list->isValid()) + $list->save(true); + break; + + case 'mass_process': + if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { + $errors['err'] = 'You must select at least one API key'; + } else { + $count = count($_POST['ids']); + switch(strtolower($_POST['a'])) { + case 'delete': + $i=0; + foreach($_POST['ids'] as $k=>$v) { + if(($t=DynamicList::lookup($v)) && $t->delete()) + $i++; + } + if ($i && $i==$count) + $msg = 'Selected custom lists deleted successfully'; + elseif ($i > 0) + $warn = "$i of $count selected lists deleted"; + elseif (!$errors['err']) + $errors['err'] = 'Unable to delete selected custom lists' + .' — they may be in use on a custom form'; + break; + } + } + break; + } + + if ($list) { + for ($i=0; isset($_POST["sort-new-$i"]); $i++) { + if (!$_POST["value-new-$i"]) + continue; + $item = DynamicListItem::create(array( + 'list_id'=>$list->get('id'), + 'sort'=>$_POST["sort-new-$i"], + 'value'=>$_POST["value-new-$i"], + 'extra'=>$_POST["extra-new-$i"] + )); + if ($item->isValid()) + $item->save(); + } + # Invalidate items cache + $list->_items = false; + } +} + +$page='dynamic-lists.inc.php'; +if($list || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'],'add'))) + $page='dynamic-list.inc.php'; + +$nav->setTabActive('manage'); +require(STAFFINC_DIR.'header.inc.php'); +require(STAFFINC_DIR.$page); +include(STAFFINC_DIR.'footer.inc.php'); +?> diff --git a/scp/tickets.php b/scp/tickets.php index 45d0cd44a9634e4662ab249dba0ae2a73e7b29cb..0fde54a63b35bfb20c1116aacf90836a17ab894e 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -20,6 +20,7 @@ require_once(INCLUDE_DIR.'class.dept.php'); require_once(INCLUDE_DIR.'class.filter.php'); require_once(INCLUDE_DIR.'class.canned.php'); require_once(INCLUDE_DIR.'class.json.php'); +require_once(INCLUDE_DIR.'class.dynamic_forms.php'); $page=''; @@ -193,12 +194,17 @@ if($_POST && !$errors): break; case 'edit': case 'update': + $forms=DynamicFormEntry::forTicket($ticket->getId()); + foreach ($forms as $form) + if (!$form->isValid()) + $errors = array_merge($errors, $form->errors()); if(!$ticket || !$thisstaff->canEditTickets()) $errors['err']='Perm. Denied. You are not allowed to edit tickets'; elseif($ticket->update($_POST,$errors)) { $msg='Ticket updated successfully'; $_REQUEST['a'] = null; //Clear edit action - going back to view. //Check to make sure the staff STILL has access post-update (e.g dept change). + foreach ($forms as $f) $f->save(); if(!$ticket->checkStaffAccess($thisstaff)) $ticket=null; } elseif(!$errors['err']) { @@ -451,6 +457,22 @@ if($_POST && !$errors): break; case 'open': $ticket=null; + $interest=array('name','email','subject'); + if ($topic=Topic::lookup($_POST['topicId'])) { + if ($form = DynamicForm::lookup($topic->ht['form_id'])) { + $form = $form->instanciate(); + # Collect name, email, and subject address for banning and such + foreach ($form->getAnswers() as $answer) { + $fname = $answer->getField()->get('name'); + if (in_array($fname, $interest)) + # XXX: Assigning to _POST not considered great PHP + # coding style + $_POST[$fname] = $answer->getField()->getClean(); + } + if (!$form->isValid()) + $errors = array_merge($errors, $form->errors()); + } + } if(!$thisstaff || !$thisstaff->canCreateTickets()) { $errors['err']='You do not have permission to create tickets. Contact admin for such access'; } else { @@ -461,7 +483,12 @@ if($_POST && !$errors): if(($ticket=Ticket::open($vars, $errors))) { $msg='Ticket created successfully'; $_REQUEST['a']=null; - if(!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed()) + # Save extra dynamic form(s) + if (isset($form)) { + $form->setTicketId($ticket->getId()); + $form->save(); + } + if (!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed()) $ticket=null; Draft::deleteForNamespace('ticket.staff%', $thisstaff->getId()); } elseif(!$errors['err']) { @@ -555,9 +582,12 @@ if($ticket) { $ost->setPageTitle('Ticket #'.$ticket->getNumber()); $nav->setActiveSubMenu(-1); $inc = 'ticket-view.inc.php'; - if($_REQUEST['a']=='edit' && $thisstaff->canEditTickets()) + if($_REQUEST['a']=='edit' && $thisstaff->canEditTickets()) { $inc = 'ticket-edit.inc.php'; - elseif($_REQUEST['a'] == 'print' && !$ticket->pdfExport($_REQUEST['psize'], $_REQUEST['notes'])) + if (!$forms) $forms=DynamicFormEntry::forTicket($ticket->getId()); + // Auto add new fields to the entries + foreach ($forms as $f) $f->addMissingFields(); + } elseif($_REQUEST['a'] == 'print' && !$ticket->pdfExport($_REQUEST['psize'], $_REQUEST['notes'])) $errors['err'] = 'Internal error: Unable to export the ticket to PDF for print.'; } else { $inc = 'tickets.inc.php'; diff --git a/setup/doc/forms.md b/setup/doc/forms.md new file mode 100644 index 0000000000000000000000000000000000000000..c3545096c5e6daaee3ff394eb4b490f9a5a84daa --- /dev/null +++ b/setup/doc/forms.md @@ -0,0 +1,51 @@ +osTicket Forms API +================== + +osTicket now includes a (relatively) complete forms API. Forms can be +created as objects and used to render their corresponding widgets to the +screen without actually writing and HTML. + +Defining a form +--------------- +Instanciate a form from a list of fields. PHP unfortunately does not allow +one to create a class and instanciate a list of instances into a static +property. Because of this limitation, the field list is specified at the +form instance construction time by passing the list of fields into the +constructor. + +The simplest way to create forms is to instanciate the Form instance +directly: + + $form = new Form(array( + 'email' => new TextboxField(array('label'=>'Email Address')), + ); + +The form can then be rendered to HTML and sent to the user. Later the form +can be recreated after a POST and the data from the request will +automatically be placed into the form. Check if the form is valid: + + if ($form->isValid()) + $object->update($form->getClean()); + +The `getClean()` method will return a hash array, where the keys are the +keys in the field array passed to the form constructor, and the values are +the cleaned values from the form fields based on the data from the request. + +To create a class that defines the fields statically, one might write a +trampoline constructor: + + class UserForm extends Form { + function __construct() { + $args = func_get_args(); + $fields = array( + 'email' => new TextboxField(array( + 'label'=>'Email Address') + ), + ); + array_unshift($args, $fields); + call_user_func_array(array('parent','__construct'), $args); + } + } + +Here, the fields are defined statically in the constructor. Do not bother +trying to specify the fields in a static property. You'll end up crying. diff --git a/setup/doc/orm.md b/setup/doc/orm.md new file mode 100644 index 0000000000000000000000000000000000000000..3a77fc7de41e58534f155cd7b66d5ca1870e7997 --- /dev/null +++ b/setup/doc/orm.md @@ -0,0 +1,11 @@ +osTicket ORM +============ + +Creating a new model +-------------------- +Eventually, we will have scripts to create the orm model from a mockup file +with field definitions, complete with validation routines. Currently, the +model validation must be written manually, and the fields are assumed based +on the fields available in the database on fetch, and the name of the field +updated on update. + diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index 78fd35849c546468251c9151152c4efa73816435..ad2ee14a3c0459dd3b1fee34e52a42d03c85cebe 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -15,7 +15,6 @@ **********************************************************************/ require_once INCLUDE_DIR.'class.migrater.php'; require_once INCLUDE_DIR.'class.setup.php'; -require_once INCLUDE_DIR.'class.i18n.php'; class Installer extends SetupWizard { @@ -111,6 +110,7 @@ class Installer extends SetupWizard { define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install. define('PREFIX',$vars['prefix']); //Table prefix Bootstrap::defineTables(PREFIX); + Bootstrap::loadCode(); $debug = true; // Change it to false to squelch SQL errors. @@ -141,26 +141,28 @@ class Installer extends SetupWizard { } } - // TODO: Use language selected from install worksheet - $i18n = new Internationalization('en_US'); - $i18n->loadDefaultData(); + if(!$this->errors) { + // TODO: Use language selected from install worksheet + require_once INCLUDE_DIR.'class.i18n.php'; - $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1'; - $sla_id_1 = db_result(db_query($sql, false), 0); + $i18n = new Internationalization('en_US'); + $i18n->loadDefaultData(); - $sql='SELECT `dept_id` FROM '.PREFIX.'department ORDER BY `dept_id` LIMIT 1'; - $dept_id_1 = db_result(db_query($sql, false), 0); + $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1'; + $sla_id_1 = db_result(db_query($sql, false), 0); - $sql='SELECT `tpl_id` FROM '.PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1'; - $template_id_1 = db_result(db_query($sql, false), 0); + $sql='SELECT `dept_id` FROM '.PREFIX.'department ORDER BY `dept_id` LIMIT 1'; + $dept_id_1 = db_result(db_query($sql, false), 0); - $sql='SELECT `group_id` FROM '.PREFIX.'groups ORDER BY `group_id` LIMIT 1'; - $group_id_1 = db_result(db_query($sql, false), 0); + $sql='SELECT `tpl_id` FROM '.PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1'; + $template_id_1 = db_result(db_query($sql, false), 0); - $sql='SELECT `id` FROM '.PREFIX.'timezone WHERE offset=-5.0 LIMIT 1'; - $eastern_timezone = db_result(db_query($sql, false), 0); + $sql='SELECT `group_id` FROM '.PREFIX.'groups ORDER BY `group_id` LIMIT 1'; + $group_id_1 = db_result(db_query($sql, false), 0); + + $sql='SELECT `id` FROM '.PREFIX.'timezone WHERE offset=-5.0 LIMIT 1'; + $eastern_timezone = db_result(db_query($sql, false), 0); - if(!$this->errors) { //Create admin user. $sql='INSERT INTO '.PREFIX.'staff SET created=NOW() ' .", isactive=1, isadmin=1, group_id=$group_id_1, dept_id=$dept_id_1" @@ -232,25 +234,22 @@ class Installer extends SetupWizard { .", autoresp_email_id=$support_email_id"; db_query($sql, false); + global $cfg; + $cfg = new OsticketConfig(); + //Create a ticket to make the system warm and happy. - $sql='INSERT INTO '.PREFIX.'ticket SET created=NOW(), status="open", source="Web" ' - ." ,priority_id=0, dept_id=$dept_id_1, topic_id=0 " - .' ,ticketID='.db_input(Misc::randNumber(6)) - .' ,email="support@osticket.com" ' - .' ,name="osTicket Support" ' - .' ,subject="osTicket Installed!"'; - if(db_query($sql, false) && ($tid=db_insert_id())) { - if(!($msg=file_get_contents(INC_DIR.'msg/installed.txt'))) - $msg='Congratulations and Thank you for choosing osTicket!'; - - $sql='INSERT INTO '.PREFIX.'ticket_thread SET created=NOW()' - .', source="Web" ' - .', thread_type="M" ' - .', ticket_id='.db_input($tid) - .', title='.db_input('osTicket Installed') - .', body='.db_input($msg); - db_query($sql, false); - } + if(!($msg=file_get_contents(INC_DIR.'msg/installed.txt'))) + $msg='Congratulations and Thank you for choosing osTicket!'; + $errors = array(); + $tid = Ticket::create(array( + 'email' => 'support@osticket.com', + 'name' => 'osTicket Support', + 'subject' => 'osTicket Installed!', + 'message' => $msg, + 'source' => 'email', + 'deptId' => $dept_id_1), + $errors, + 'api', false, false); //TODO: create another personalized ticket and assign to admin?? //Log a message. diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index 4891bcb0f9023b7cf97a1facd84b7bd19e54cc7c..e5859e815129110747cd04653679859e6ce9c2f8 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -170,6 +170,85 @@ INSERT INTO `%TABLE_PREFIX%config` (`namespace`, `key`, `value`) VALUES ('core', 'helpdesk_url', ''), ('core', 'schema_signature', ''); +DROP TABLE IF EXISTS `%TABLE_PREFIX%form`; +CREATE TABLE `%TABLE_PREFIX%form` ( + `id` int(11) unsigned NOT NULL auto_increment, + `type` char(1) NOT NULL DEFAULT 'G', + `deletable` tinyint(1) NOT NULL DEFAULT 1, + `title` varchar(255) NOT NULL, + `instructions` varchar(512), + `notes` text, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form_field`; +CREATE TABLE `%TABLE_PREFIX%form_field` ( + `id` int(11) unsigned NOT NULL auto_increment, + `form_id` int(11) unsigned NOT NULL, + `type` varchar(255) NOT NULL DEFAULT 'text', + `label` varchar(255) NOT NULL, + `required` tinyint(1) NOT NULL DEFAULT 0, + `private` tinyint(1) NOT NULL DEFAULT 0, + `edit_mask` tinyint(1) NOT NULL DEFAULT 0, + `name` varchar(64) NOT NULL, + `configuration` text, + `sort` int(11) unsigned NOT NULL, + `hint` varchar(512), + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form_entry`; +CREATE TABLE `%TABLE_PREFIX%form_entry` ( + `id` int(11) unsigned NOT NULL auto_increment, + `form_id` int(11) unsigned NOT NULL, + `object_id` int(11) unsigned, + `object_type` char(1) NOT NULL DEFAULT 'T', + `sort` int(11) unsigned NOT NULL DEFAULT 1, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `entry_lookup` (`object_id`, `object_type`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%form_entry_values`; +CREATE TABLE `%TABLE_PREFIX%form_entry_values` ( + -- references form_entry.id + `entry_id` int(11) unsigned NOT NULL, + `field_id` int(11) unsigned NOT NULL, + `value` text, + `value_id` int(11), + PRIMARY KEY (`entry_id`, `field_id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%list`; +CREATE TABLE `%TABLE_PREFIX%list` ( + `id` int(11) unsigned NOT NULL auto_increment, + `name` varchar(255) NOT NULL, + `name_plural` varchar(255), + `sort_mode` enum('Alpha', '-Alpha', 'SortCol') NOT NULL DEFAULT 'Alpha', + `notes` text, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%list_items`; +CREATE TABLE `%TABLE_PREFIX%list_items` ( + `id` int(11) unsigned NOT NULL auto_increment, + `list_id` int(11), + `value` varchar(255) NOT NULL, + -- extra value such as abbreviation + `extra` varchar(255), + `sort` int(11) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `list_item_lookup` (`list_id`) +) DEFAULT CHARSET=utf8; + + DROP TABLE IF EXISTS `%TABLE_PREFIX%department`; CREATE TABLE `%TABLE_PREFIX%department` ( `dept_id` int(11) unsigned NOT NULL auto_increment, @@ -258,6 +337,7 @@ CREATE TABLE `%TABLE_PREFIX%filter` ( `staff_id` int(10) unsigned NOT NULL default '0', `team_id` int(10) unsigned NOT NULL default '0', `sla_id` int(10) unsigned NOT NULL default '0', + `form_id` int(11) unsigned NOT NULL default '0', `target` ENUM( 'Any', 'Web', 'Email', 'API' ) NOT NULL DEFAULT 'Any', `name` varchar(32) NOT NULL default '', `notes` text, @@ -272,7 +352,7 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%filter_rule`; CREATE TABLE `%TABLE_PREFIX%filter_rule` ( `id` int(11) unsigned NOT NULL auto_increment, `filter_id` int(10) unsigned NOT NULL default '0', - `what` enum('name','email','subject','body','header') NOT NULL, + `what` varchar(32) NOT NULL, `how` enum('equal','not_equal','contains','dn_contain','starts','ends') NOT NULL, `val` varchar(255) NOT NULL, `isactive` tinyint(1) unsigned NOT NULL DEFAULT '1', @@ -376,6 +456,7 @@ CREATE TABLE `%TABLE_PREFIX%help_topic` ( `team_id` int(10) unsigned NOT NULL default '0', `sla_id` int(10) unsigned NOT NULL default '0', `page_id` int(10) unsigned NOT NULL default '0', + `form_id` int(10) unsigned NOT NULL default '0', `topic` varchar(32) NOT NULL default '', `notes` text, `created` datetime NOT NULL, @@ -502,17 +583,13 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket`; CREATE TABLE `%TABLE_PREFIX%ticket` ( `ticket_id` int(11) unsigned NOT NULL auto_increment, `ticketID` int(11) unsigned NOT NULL default '0', + `user_id` int(11) unsigned NOT NULL default '0', + `user_email_id` int(11) unsigned NOT NULL default '0', `dept_id` int(10) unsigned NOT NULL default '0', `sla_id` int(10) unsigned NOT NULL default '0', - `priority_id` int(10) unsigned NOT NULL default '0', `topic_id` int(10) unsigned NOT NULL default '0', `staff_id` int(10) unsigned NOT NULL default '0', `team_id` int(10) unsigned NOT NULL default '0', - `email` varchar(255) NOT NULL default '', - `name` varchar(255) NOT NULL default '', - `subject` varchar(255) NOT NULL default '[no subject]', - `phone` varchar(16) default NULL, - `phone_ext` varchar(8) default NULL, `ip_address` varchar(64) NOT NULL default '', `status` enum('open','closed') NOT NULL default 'open', `source` enum('Web','Email','Phone','API','Other') NOT NULL default 'Other', @@ -526,12 +603,10 @@ CREATE TABLE `%TABLE_PREFIX%ticket` ( `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`ticket_id`), - UNIQUE KEY `email_extid` (`ticketID`,`email`), KEY `dept_id` (`dept_id`), KEY `staff_id` (`staff_id`), KEY `team_id` (`staff_id`), KEY `status` (`status`), - KEY `priority_id` (`priority_id`), KEY `created` (`created`), KEY `closed` (`closed`), KEY `duedate` (`duedate`), @@ -677,3 +752,23 @@ CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%page` ( PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%user`; +CREATE TABLE `%TABLE_PREFIX%user` ( + `id` int(10) unsigned NOT NULL auto_increment, + `default_email_id` int(10) NOT NULL, + `name` varchar(128) NOT NULL, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%user_email`; +CREATE TABLE `%TABLE_PREFIX%user_email` ( + `id` int(10) unsigned NOT NULL auto_increment, + `user_id` int(10) unsigned NOT NULL, + `address` varchar(128) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `address` (`address`), + KEY `user_email_lookup` (`user_id`) +) DEFAULT CHARSET=utf8; diff --git a/setup/setup.inc.php b/setup/setup.inc.php index c68bd7a62a2c9658411f36cd0fe4bd7cd0dee41f..6eb4a495fb3c2a98350b033a0a80a88f3d25fa48 100644 --- a/setup/setup.inc.php +++ b/setup/setup.inc.php @@ -14,9 +14,6 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ -#This version - changed on every release -define('THIS_VERSION', '1.8-git'); - #inits - error reporting. $error_reporting = E_ALL & ~E_NOTICE; if (defined('E_STRICT')) # 5.4.0