diff --git a/api/v1/entries/project.php b/api/v1/entries/project.php index 2251b7fdbe..38096ddc09 100644 --- a/api/v1/entries/project.php +++ b/api/v1/entries/project.php @@ -62,6 +62,11 @@ class projectEntry extends entry $project->teams = $teams; break; + case "products": + $project->products = array(); + $productList = $this->loadModel('product')->getOrderedProducts('all', 40, $projectID); + foreach($productList as $product) $project->products[] = $product; + break; case "stat": $project->stat = $data->data->statData; break; diff --git a/api/v1/entries/taskbatchcreate.php b/api/v1/entries/taskbatchcreate.php index 6a65f4c898..92fa3d2c47 100644 --- a/api/v1/entries/taskbatchcreate.php +++ b/api/v1/entries/taskbatchcreate.php @@ -43,21 +43,22 @@ class taskBatchCreateEntry extends Entry $desc = array(); $pri = array(); $stories = array(); - foreach($this->request('tasks') as $task) + foreach($this->request('tasks') as $key => $task) { + $number = $key + 1; if(!isset($task->name) or !isset($task->type)) return $this->send400('Task must have name and type.'); - $modules[] = isset($task->module) ? $task->module : $moduleID; - $parents[] = isset($task->parent) ? $task->parent : $taskID; - $names[] = $task->name; - $colors[] = isset($task->color) ? $task->color : ''; - $types[] = $task->type; - $estimates[] = isset($task->estimate) ? $task->estimate : 0; - $estStarted[] = isset($task->estStarted) ? $task->estStarted : 0; - $deadlines[] = isset($task->deadline) ? $task->deadline : null; - $desc[] = isset($task->desc) ? $task->desc : ''; - $pri[] = isset($task->pri) ? $task->pri : 0; - $stories[] = isset($task->story) ? $task->story : $storyID; + $modules[$number] = isset($task->module) ? $task->module : $moduleID; + $parents[$number] = isset($task->parent) ? $task->parent : $taskID; + $names[$number] = $task->name; + $colors[$number] = isset($task->color) ? $task->color : ''; + $types[$number] = $task->type; + $estimates[$number] = isset($task->estimate) ? $task->estimate : 0; + $estStarted[$number] = isset($task->estStarted) ? $task->estStarted : 0; + $deadlines[$number] = isset($task->deadline) ? $task->deadline : null; + $desc[$number] = isset($task->desc) ? $task->desc : ''; + $pri[$number] = isset($task->pri) ? $task->pri : 0; + $stories[$number] = isset($task->story) ? $task->story : $storyID; } $this->setPost('module', $modules); $this->setPost('parent', $parents); diff --git a/module/block/control.php b/module/block/control.php index 12609da8ca..bee0955441 100644 --- a/module/block/control.php +++ b/module/block/control.php @@ -1354,7 +1354,7 @@ class block extends control $this->app->loadClass('pager', $static = true); $pager = pager::init(0, $count, 1); $this->app->loadLang('execution'); - $this->view->executionStats = $this->loadModel('project')->getStats($this->session->project, $type, 0, 0, 30, 'id_desc', $pager); + $this->view->executionStats = !defined('TUTORIAL') ? $this->loadModel('project')->getStats($this->session->project, $type, 0, 0, 30, 'id_desc', $pager) : array($this->loadModel('tutorial')->getExecution()); } /** @@ -1739,7 +1739,7 @@ class block extends control ->orderBy($orderBy) ->beginIF($limitCount)->limit($limitCount)->fi() ->fetchAll(); - + if($objectType == 'todo') { $this->app->loadClass('date'); diff --git a/module/common/model.php b/module/common/model.php index fb2200d17a..6b81d3bd1e 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -2257,7 +2257,7 @@ EOD; $module = $this->app->getModuleName(); $method = $this->app->getMethodName(); - if($module == 'index' || $module == 'tutorial' || $module == 'install' || $module == 'upgrade' || ($module == 'user' && ($method == 'login' || $method == 'deny' || $method == 'logout')) || ($module == 'my' && $method == 'changepassword') || ($module == 'file' && $method == 'read') || ($module == 'file' && $method == 'download') || ($module == 'file' && $method == 'uploadimages')) return; + if($module == 'index' || $module == 'tutorial' || $module == 'install' || $module == 'upgrade' || ($module == 'user' && ($method == 'login' || $method == 'deny' || $method == 'logout')) || ($module == 'my' && ($method == 'changepassword' || $method == 'preference')) || ($module == 'file' && $method == 'read') || ($module == 'file' && $method == 'download') || ($module == 'file' && $method == 'uploadimages')) return; $url = helper::safe64Encode($_SERVER['REQUEST_URI']); $redirectUrl = helper::createLink('index', 'index', "open=$url"); diff --git a/module/execution/control.php b/module/execution/control.php index 35fa6b8c2a..ac274568cf 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -2561,13 +2561,6 @@ class execution extends control if(!empty($_POST)) { - /* Get executionType and determine whether a product is linked with the stage. */ - $executionType = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch('type'); - if($executionType == 'stage') - { - if(!isset($_POST['products'])) return print(js::alert($this->lang->execution->noLinkProduct) . js::locate($this->createLink('execution', 'manageProducts', "executionID=$executionID&from=$from"))); - } - $oldProducts = $this->product->getProducts($executionID); if($from == 'buildCreate' && $this->session->buildCreate) $browseExecutionLink = $this->session->buildCreate; diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js index 054986c08c..60da0e0797 100644 --- a/module/execution/js/kanban.js +++ b/module/execution/js/kanban.js @@ -1338,6 +1338,7 @@ $(function() { selector: '.region, .kanban-board, .kanban-lane', trigger: '.region.sort > .region-header, .kanban-board.sort > .kanban-header > .kanban-group-header, .kanban-lane.sort > .kanban-lane-name', + dropOnMouseleave: true, container: function($ele) { return $ele.parent(); diff --git a/module/execution/model.php b/module/execution/model.php index 77f3417d65..81f3e2fc5a 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -305,14 +305,6 @@ class executionModel extends model $type = 'sprint'; if($project) $type = zget($this->config->execution->modelList, $project->model, 'sprint'); - /* If the execution model is a stage, determine whether the product is linked. */ - $products = array_filter($this->post->products); - if(empty($products)) - { - dao::$errors['message'][] = $this->lang->execution->noLinkProduct; - return false; - } - $this->config->execution->create->requiredFields .= ',project'; } @@ -468,13 +460,7 @@ class executionModel extends model dao::$errors['days'] = sprintf($this->lang->project->workdaysExceed, $workdays); return false; } - $products = array_filter($this->post->products); - $noLinkTip = $oldExecution->type != 'kanban' ? $this->lang->execution->noLinkProduct : $this->lang->execution->kanbanNoLinkProduct; - if(empty($products)) - { - dao::$errors['message'][] = $noLinkTip; - return false; - } + /* Get the data from the post. */ $execution = fixer::input('post') ->setDefault('lastEditedBy', $this->app->user->account) diff --git a/module/execution/view/create.html.php b/module/execution/view/create.html.php index fef68cc881..cce35666a4 100644 --- a/module/execution/view/create.html.php +++ b/module/execution/view/create.html.php @@ -144,8 +144,8 @@
-
- +
+
diff --git a/module/execution/view/edit.html.php b/module/execution/view/edit.html.php index aba139060f..56ead0a3f4 100644 --- a/module/execution/view/edit.html.php +++ b/module/execution/view/edit.html.php @@ -141,7 +141,7 @@ type != 'normal' and isset($branchGroups[$product->id]);?> id] as $branchID => $branch):?>
-
"> +
id, "class='form-control chosen' $class onchange='loadBranches(this)' data-last='" . $product->id . "' data-type='". $product->type ."'");?> id], $branchID, "class='form-control chosen' $class onchange=\"loadPlans('#products{$i}', this.value)\" data-last='" . $branchID . "'");?> @@ -159,7 +159,7 @@
- +
diff --git a/module/kanban/js/view.js b/module/kanban/js/view.js index d7def024ed..89800ac5d2 100644 --- a/module/kanban/js/view.js +++ b/module/kanban/js/view.js @@ -1388,6 +1388,7 @@ function initSortable() { selector: '.region, .kanban-board, .kanban-lane, .kanban-col', trigger: '.region.sort > .region-header, .kanban-board.sort > .kanban-header > .kanban-group-header, .kanban-lane.sort > .kanban-lane-name, .kanban-header-col.sort', + dropOnMouseleave: true, container: function($ele) { return $ele.parent(); diff --git a/module/program/model.php b/module/program/model.php index 49c9e07f15..9def3df994 100644 --- a/module/program/model.php +++ b/module/program/model.php @@ -1259,7 +1259,7 @@ class programModel extends model */ public function getProjectStats($programID = 0, $browseType = 'undone', $queryID = 0, $orderBy = 'id_desc', $pager = null, $programTitle = 0, $involved = 0, $queryAll = false) { - if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProjectStats(); + if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProjectStats($browseType); /* Init vars. */ $projects = $this->getProjectList($programID, $browseType, $queryID, $orderBy, $pager, $programTitle, $involved, $queryAll); diff --git a/module/task/control.php b/module/task/control.php index 2c360a27e5..88d0f6b940 100644 --- a/module/task/control.php +++ b/module/task/control.php @@ -562,7 +562,7 @@ class task extends control if($this->post->names) { $allChanges = $this->task->batchUpdate(); - if(dao::isError()) return print(js::error(dao::getError())); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if(!empty($allChanges)) { diff --git a/module/task/lang/en.php b/module/task/lang/en.php index c0cf60ed5b..b6eacee3be 100644 --- a/module/task/lang/en.php +++ b/module/task/lang/en.php @@ -246,6 +246,8 @@ $lang->task->error->finishedDateEmpty = '"Finished Date" should not be empty.'; $lang->task->error->finishedDateSmall = '"Finished Date" should be > "Real Started"'; $lang->task->error->alreadyConsumed = 'The currently selected parent task has been consumed.'; $lang->task->error->date = 'The date should be >= today.'; +$lang->task->error->leftEmptyAB = 'When the task status is %s, "Hours Left" cannot be 0'; +$lang->task->error->leftEmpty = 'Task#%sWhen the task status is %s, "Left" cannot be 0'; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index 8f7f05cecd..f1a9a910b1 100644 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -246,6 +246,8 @@ $lang->task->error->finishedDateEmpty = '实际完成不能为空'; $lang->task->error->finishedDateSmall = '实际完成不能小于实际开始'; $lang->task->error->alreadyConsumed = '当前选中的父任务已有消耗。'; $lang->task->error->date = '日期不能大于今天'; +$lang->task->error->leftEmptyAB = '任务状态为%s时,预计剩余不能为0'; +$lang->task->error->leftEmpty = 'Task#%s任务状态为%s时,剩余不能为0'; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/model.php b/module/task/model.php index f524c69262..f959536a27 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -35,6 +35,30 @@ class taskModel extends model if($this->post->selectTestStory) { + /* Check required fields when create test task. */ + foreach($this->post->testStory as $i => $storyID) + { + if(empty($storyID)) continue; + + $task = new stdclass(); + $task->pri = $this->post->testPri[$i]; + $task->estStarted = $this->post->testEstStarted[$i]; + $task->deadline = $this->post->testDeadline[$i]; + $task->assignedTo = $this->post->testAssignedTo[$i]; + $task->estimate = $this->post->testEstimate[$i]; + $task->left = $this->post->testEstimate[$i]; + + $this->dao->insert(TABLE_TASK)->data($task)->batchCheck($requiredFields, 'notempty'); + if(dao::isError()) + { + foreach(dao::getError() as $field => $error) + { + dao::$errors[] = $error; + return false; + } + } + } + $requiredFields = str_replace(",estimate,", ',', "$requiredFields"); $requiredFields = str_replace(",story,", ',', "$requiredFields"); $requiredFields = str_replace(",estStarted,", ',', "$requiredFields"); @@ -998,6 +1022,12 @@ class taskModel extends model $requiredFields = str_replace(',estimate,', ',', $requiredFields); } + if(strpos(',doing,pause,', $task->status) && empty($teams) && empty($task->left)) + { + dao::$errors[] = sprintf($this->lang->task->error->leftEmptyAB, $this->lang->task->statusList[$task->status]); + return false; + } + $requiredFields = trim($requiredFields, ','); $this->dao->update(TABLE_TASK)->data($task) @@ -1008,7 +1038,6 @@ class taskModel extends model ->checkIF($task->estimate != false, 'estimate', 'float') ->checkIF($task->left != false, 'left', 'float') ->checkIF($task->consumed != false, 'consumed', 'float') - ->checkIF($task->status != 'wait' and empty($teams) and $task->left == 0 and $task->status != 'cancel' and $task->status != 'closed', 'status', 'equal', 'done') ->batchCheckIF($task->status == 'wait' or $task->status == 'doing', 'finishedBy, finishedDate,canceledBy, canceledDate, closedBy, closedDate, closedReason', 'empty') @@ -1113,7 +1142,6 @@ class taskModel extends model { if(isset($data->modules[$taskID]) and ($data->modules[$taskID] == 'ditto')) $data->modules[$taskID] = isset($prev['module']) ? $prev['module'] : 0; if($data->types[$taskID] == 'ditto') $data->types[$taskID] = isset($prev['type']) ? $prev['type'] : ''; - if($data->statuses[$taskID] == 'ditto') $data->statuses[$taskID] = isset($prev['status']) ? $prev['status'] : ''; if($data->assignedTos[$taskID] == 'ditto') $data->assignedTos[$taskID] = isset($prev['assignedTo']) ? $prev['assignedTo'] : ''; if($data->pris[$taskID] == 'ditto') $data->pris[$taskID] = isset($prev['pri']) ? $prev['pri'] : 0; if($data->finishedBys[$taskID] == 'ditto') $data->finishedBys[$taskID] = isset($prev['finishedBy']) ? $prev['finishedBy'] : ''; @@ -1124,7 +1152,6 @@ class taskModel extends model $prev['module'] = $data->modules[$taskID]; $prev['type'] = $data->types[$taskID]; - $prev['status'] = $data->statuses[$taskID]; $prev['assignedTo'] = $data->assignedTos[$taskID]; $prev['pri'] = $data->pris[$taskID]; $prev['finishedBy'] = $data->finishedBys[$taskID]; @@ -1145,7 +1172,7 @@ class taskModel extends model $task->name = $data->names[$taskID]; $task->module = isset($data->modules[$taskID]) ? $data->modules[$taskID] : 0; $task->type = $data->types[$taskID]; - $task->status = $data->statuses[$taskID]; + $task->status = isset($data->statuses[$taskID]) ? $data->statuses[$taskID] : $oldTask->status; $task->assignedTo = $task->status == 'closed' ? 'closed' : $data->assignedTos[$taskID]; $task->pri = $data->pris[$taskID]; $task->estimate = isset($data->estimates[$taskID]) ? $data->estimates[$taskID] : $oldTask->estimate; @@ -1180,11 +1207,12 @@ class taskModel extends model if($message) return print(js::alert($message)); } - if($data->consumeds[$taskID]) + if(isset($data->consumeds[$taskID])) { if($data->consumeds[$taskID] < 0) { - echo js::alert(sprintf($this->lang->task->error->consumed, $taskID)); + dao::$errors[] = sprintf($this->lang->task->error->consumed, $taskID); + return false; } else { @@ -1256,9 +1284,19 @@ class taskModel extends model /* Check field not empty. */ foreach($tasks as $taskID => $task) { - if($task->status == 'done' and $task->consumed == false) return print(js::error('task#' . $taskID . sprintf($this->lang->error->notempty, $this->lang->task->consumedThisTime))); if($task->status == 'cancel') continue; - if(!empty($task->deadline) and $task->estStarted > $task->deadline) return print(js::error('task#' . $taskID . $this->lang->task->error->deadlineSmall)); + if($task->status == 'done' and $task->consumed == false) + { + dao::$errors[] = 'Task#' . $taskID . sprintf($this->lang->error->notempty, $this->lang->task->consumedThisTime); + return false; + } + + if(!empty($task->deadline) and $task->estStarted > $task->deadline) + { + dao::$errors[] = 'Task#' . $taskID . $this->lang->task->error->deadlineSmall; + return false; + } + foreach(explode(',', $this->config->task->edit->requiredFields) as $field) { $field = trim($field); @@ -1275,6 +1313,12 @@ class taskModel extends model foreach($tasks as $taskID => $task) { + if(strpos(',doing,pause,', $task->status) && empty($teams) && $task->parent >= 0 && empty($task->left)) + { + dao::$errors[] = sprintf($this->lang->task->error->leftEmpty, $taskID, $this->lang->task->statusList[$task->status]); + return false; + } + $oldTask = $oldTasks[$taskID]; $this->dao->update(TABLE_TASK)->data($task) ->autoCheck() @@ -1282,7 +1326,6 @@ class taskModel extends model ->checkIF($task->estimate != false, 'estimate', 'float') ->checkIF($task->consumed != false, 'consumed', 'float') ->checkIF($task->left != false, 'left', 'float') - ->checkIF($task->parent > 0 and $task->left == 0 and $task->status != 'cancel' and $task->status != 'closed' and $task->status != 'wait' and $task->consumed != 0, 'status', 'equal', 'done') ->batchCheckIF($task->status == 'wait' or $task->status == 'doing', 'finishedBy, finishedDate,canceledBy, canceledDate, closedBy, closedDate, closedReason', 'empty') @@ -1294,6 +1337,11 @@ class taskModel extends model ->batchCheckIF($task->closedReason == 'cancel', 'finishedBy, finishedDate', 'empty') ->where('id')->eq((int)$taskID) ->exec(); + if(dao::isError()) + { + dao::$errors[] = 'Task#' . $taskID . dao::getError(true); + return false; + } if($task->status == 'done' and $task->closedReason) $this->dao->update(TABLE_TASK)->set('status')->eq('closed')->where('id')->eq($taskID)->exec(); @@ -1324,10 +1372,6 @@ class taskModel extends model if($task->status != $oldTask->status) $this->loadModel('kanban')->updateLane($oldTask->execution, 'task', $oldTask->id); $allChanges[$taskID] = common::createChanges($oldTask, $task); } - else - { - return print(js::error('task#' . $taskID . dao::getError(true))); - } } if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchEdit'); return $allChanges; diff --git a/module/task/view/batchedit.html.php b/module/task/view/batchedit.html.php index 2c1de060e9..ecfdc202fa 100755 --- a/module/task/view/batchedit.html.php +++ b/module/task/view/batchedit.html.php @@ -127,7 +127,7 @@ js::set('dittoNotice', $dittoNotice); ' style='overflow:visible'>module, "class='form-control chosen'")?> ' style='overflow:visible'>assignedTo, "class='form-control chosen' {$disableAssignedTo}");?> type, "class='form-control'");?> - >status, "class='form-control'");?> + >status, "class='form-control' {$disableHour}");?> >estStarted) ? '' : $tasks[$taskID]->estStarted, "class='form-control text-center form-date'");?> >deadline) ? '' : $tasks[$taskID]->deadline, "class='form-control text-center form-date'");?> >pri, "class='form-control'");?> diff --git a/module/tutorial/lang/en.php b/module/tutorial/lang/en.php index 7f7124b580..18742840c3 100644 --- a/module/tutorial/lang/en.php +++ b/module/tutorial/lang/en.php @@ -59,12 +59,12 @@ $lang->tutorial->tasks['createProject']['desc'] = "

Create a project:

').css(t.extend({zIndex:1060,position:"absolute",top:e,left:i,pointerEvents:"none"},h.options.rangeStyle)).appendTo(t("body")))),n.css(a),clearTimeout(l),l=setTimeout(b,10),u=!1))},x=function(e){t(document).off(p),clearTimeout(v),m&&(m=!1,n&&n.remove(),u||a&&(clearTimeout(l),b(),a=null),h.callEvent("finish",{selections:h.selections,selected:h.getSelectedArray()}),e.preventDefault())},C=function(o){if(m)return x(o);var a=t.zui.getMouseButtonCode(c.mouseButton);if(!(a>-1&&o.button!==a||t(o.target).closest("input,select,textarea,label").length||h.altKey||3===o.which||h.callEvent("start",o)===!1)){var s=h.$children=h.$.find(c.selector);s.addClass("selectable-item");var r=h.multiKey?"multi":c.clickBehavior;if("single"===r&&h.unselect(),c.listenClick&&("multi"===r?h.toggle(o.target):"single"===r?h.select(o.target):"toggle"===r&&h.toggle(o.target,null,function(t){h.unselect()})),h.callEvent("startDrag",o)===!1)return void h.callEvent("finish",{selections:h.selections,selected:h.getSelectedArray()});e=o.pageX,i=o.pageY,n=null,u=!0,m=!0,t(document).on("mousemove"+p,w).on("mouseup"+p,x),v=setTimeout(function(){t(document).on(y,x)},10),o.preventDefault()}},_=c.container&&"default"!==c.container?t(c.container):this.$;c.trigger?_.on(y,c.trigger,C):_.on(y,C),t(document).on("keydown",function(t){var e=t.keyCode;17===e||91==e?h.multiKey=e:18===e&&(h.altKey=!0)}).on("keyup",function(t){h.multiKey=!1,h.altKey=!1})},i.prototype.callEvent=function(e,i){var n=t.Event(e+"."+this.name);this.$.trigger(n,i);var o=n.result,a=this.options[e];return"function"==typeof a&&(o=a.apply(this,Array.isArray(i)?i:[i])),o},t.fn.selectable=function(n){return this.each(function(){var o=t(this),a=o.data(e),s="object"==typeof n&&n;a||o.data(e,a=new i(this,s)),"string"==typeof n&&a[n]()})},t.fn.selectable.Constructor=i,t(function(){t('[data-ride="selectable"]').selectable()})}(jQuery),+function(t,e,i){"use strict";if(!t.fn.droppable)return void console.error("Sortable requires droppable.js");var n="zui.sortable",o={selector:"li,div",dragCssClass:"invisible",sortingClass:"sortable-sorting"},a="order",s=function(e,i){var n=this;n.$=t(e),n.options=t.extend({},o,n.$.data(),i),n.init()};s.DEFAULTS=o,s.NAME=n,s.prototype.init=function(){var e,i=this,n=i.$,o=i.options,s=o.selector,r=o.containerSelector,l=o.sortingClass,c=o.dragCssClass,h=o.targetSelector,d=o.reverse,u=o.moveDirection,p=function(e){e=e||i.getItems(1);var n=e.length;n&&e.each(function(e){var i=d?n-e:e;t(this).attr("data-"+a,i).data(a,i)})};h||p(),n.droppable({handle:o.trigger,target:h?h:r?s+","+r:s,selector:s,container:o.container||n,always:o.always,flex:!0,lazy:o.lazy,canMoveHere:o.canMoveHere,dropToClass:o.dropToClass,before:o.before,nested:!!r,mouseButton:o.mouseButton,noShadow:o.noShadow,dropOnMouseleave:o.dropOnMouseleave,stopPropagation:o.stopPropagation,start:function(t){if(c&&t.element.addClass(c),e=!1,i.$element=t.element,!u&&t.targets.length>1){var n=t.targets.eq(0).offset(),o=t.targets.eq(1).offset();u=Math.abs(n.left-o.left)>Math.abs(n.top-o.top)?"h":"v"}p(),i.trigger("start",t)},drag:function(t){if(n.addClass(l),t.isIn){var o=t.element,c=t.target,h=r&&c.is(r);if(h)return void(c.children(s).filter(".dragging").length||(c.append(o),p(b),i.trigger(a,{list:b,element:o})));var f=o.data(a),g=c.data(a);if(f===g)return;var m="h"===u?"left":"top",v=t.mouseOffset[m]-t.lastMouseOffset[m];if(0===v)return;var y=f>g?d:!d;if(v<0&&y||v>0&&!y)return;c[y?"after":"before"](o),e=!0;var b=i.getItems(1);p(b),i.trigger(a,{list:b,element:o})}},finish:function(t){c&&t.element&&t.element.removeClass(c),n.removeClass(l),i.trigger("finish",{list:i.getItems(),element:t.element,changed:e}),i.$element=null}})},s.prototype.destroy=function(){this.$.droppable("destroy"),this.$.data(n,null)},s.prototype.reset=function(){this.destroy(),this.init()},s.prototype.getItems=function(e){var i,n=this,o=n.options.targetSelector;return i=o?"function"==typeof o?o(n.$element,n.$):n.$.find(o):n.$.find(n.options.selector),i=i.not(".drag-shadow"),e?i:i.map(function(){var e=t(this);return{item:e,order:e.data("order")}})},s.prototype.trigger=function(e,i){return t.zui.callEvent(this.options[e],i,this)},t.fn.sortable=function(e){return this.each(function(){var i=t(this),o=i.data(n),a="object"==typeof e&&e;o?"object"==typeof e&&o.reset():i.data(n,o=new s(this,a)),"string"==typeof e&&o[e]()})},t.fn.sortable.Constructor=s}(jQuery,window,document),function(t,e){"use strict";function i(e,i){if("string"==typeof e&&(e="seperator"===e||"divider"===e||"-"===e||"|"===e?{type:"seperator"}:{label:e,id:i}),"seperator"===e.type||"divider"===e.type)return t('
  • ');var n=t("
    ").attr(t.extend({href:e.url||"###","class":e.className,style:e.style},e.attrs)).data("item",e);e.html?e.html===!0?n.html(e.label||e.text):n=t(e.html):n.text(e.label||e.text),e.icon&&n.prepend(''),e.onClick&&n.on("click",e.onClick);var o=t("
  • ").toggleClass("disabled",e.disabled===!0).append(n);return e.items&&o.data("item",e).addClass("dropdown-submenu"),o}function n(e,n,o){var a=o.itemCreator||i,s=typeof e;return"string"===s?e=e.split(","):"function"===s&&(e=e(o)),!!e&&(t.each(e,function(t,e){n.append(a(e,t,o))}),!0)}var o="zui.contextmenu",a={animation:"fade",menuTemplate:'',toggleTrigger:!1,duration:200},s=!1,r={},l="zui-contextmenu-"+t.zui.uuid(),c=0,h=0,d=function(){return t(document).off("mousemove."+o).on("mousemove."+o,function(t){c=t.clientX,h=t.clientY}),r},u=function(e){var i=t("#"+l);return i.length&&i.hasClass("contextmenu-show")&&(!e||(i.data("options")||{}).id===e)},p=null,f=function(e,i){"function"==typeof e&&(i=e,e=null),p&&(clearTimeout(p),p=null);var n=t("#"+l);if(n.length){var o=n.removeClass("contextmenu-show").data("options");if(!e||o.id===e){var a=function(){n.find(".contextmenu-menu").removeClass("open"),o.onHidden&&o.onHidden(),i&&i()};o.onHide&&o.onHide();var s=o.animation;n.find(".contextmenu-menu").removeClass("in"),s?p=setTimeout(a,o.duration):a()}}return r},g=function(i,d,u){t.isPlainObject(i)&&(u=d,d=i,i=d.items),s=!0,d=t.extend({},a,d);var g=t("#"+l);g.length||(g=t('
    ').appendTo("body"));var m=g.find(".contextmenu-menu").empty();m.off("click."+o).on("click."+o,"a,.contextmenu-item",function(e){var i=t(this),n=d.onClickItem&&d.onClickItem(i.data("item"),i,e,d);n!==!1&&f()}).off("mouseenter."+o).on("mouseenter."+o,".dropdown-submenu",function(e){var i=t(this),o=i.data("item"),a=i.children(".dropdown-menu");if(o&&(o.items&&(a.length||(a=t(d.menuTemplate).appendTo(i)),n(o.items,a,d)),i.removeData("item")),a.length){a.removeClass("pull-left").css("top",0);var s=(i[0].getBoundingClientRect(),a[0].getBoundingClientRect()),r=window.innerWidth,l=window.innerHeight;if(s.bottom>l){var c=Math.max(-s.top,l-s.bottom);a.css("top",c)}s.right>r&&a.addClass("pull-left")}}),m.attr("class","contextmenu-menu"+(d.className?" "+d.className:"")),g.attr("class","contextmenu contextmenu-show");var v=d.menuCreator;if(v)m.append(v(i,d));else{m.append(d.menuTemplate);var y=m.children().first(),b=n(i,y,d);if(b===!1)return b}var w=d.animation,x=d.duration;w===!0&&(d.animation=w="fade"),p&&(clearTimeout(p),p=null);var C=function(){m.addClass("in"),d.onShown&&d.onShown(),u&&u()};d.onShow&&d.onShow(),g.data("options",{animation:w,onHide:d.onHide,onHidden:d.onHidden,id:d.id,duration:x});var _=d.x,k=d.y;_===e&&(_=(d.event||d).clientX),_===e&&(_=c),k===e&&(k=(d.event||d).clientY),k===e&&(k=h);var T=window.innerHeight,S=window.innerWidth,y=m.children().first(),D=y.outerWidth(),M=y.outerHeight();if(d.position){var L=d.position({x:_,y:k,width:D,height:M,winHeight:T,winWidth:S},d,m);L&&(_=L.x,k=L.y)}return _=Math.max(0,Math.min(_,S-D)),k=Math.max(0,Math.min(k,T-M)),g.css({left:_,top:k}).show(),m.addClass("open"),w?(m.addClass(w),p=setTimeout(function(){C(),s=!1},10)):(C(),s=!1),r};t.extend(r,{NAME:o,DEFAULTS:a,show:g,hide:f,listenMouse:d,isShow:u}),t.zui({ContextMenu:r});var m=function(e,i){var n=this;n.name=o,n.$=t(e),n.id=t.zui.uuid(),i=n.options=t.extend({trigger:"contextmenu"},r.DEFAULTS,this.$.data(),i);var a=function(t){if("mousedown"!==t.type||2===t.button){if(i.toggleTrigger&&n.isShow())n.hide();else{var e={x:t.clientX,y:t.clientY,event:t};if(n.show(e)===!1)return}return t.preventDefault(),t.returnValue=!1,!1}},s=i.trigger,l=s+"."+o;i.selector?n.$.on(l,i.selector,a):n.$.on(l,a),i.show&&n.show("object"==typeof i.show?i.show:null)};m.prototype.destory=function(){that.$.off("."+o)},m.prototype.hide=function(t){return r.hide(this.id,t)},m.prototype.show=function(e,i){return e=t.extend({id:this.id,$toggle:this.$},this.options,e),r.show(e,i)},m.prototype.isShow=function(){return u(this.id)},t.fn.contextmenu=function(e){return this.each(function(){var i=t(this),n=i.data(o),a="object"==typeof e&&e;n||i.data(o,n=new m(this,a)),"string"==typeof e&&n[e]()})},t.fn.contextmenu.Constructor=m,t.fn.contextDropdown=function(e){t(this).contextmenu(t.extend({trigger:"click",animation:"fade",toggleTrigger:!0,menuCreator:function(e,i){var n=i.$toggle,o=n.attr("data-target");o||(o=n.attr("href"),o=o&&/#/.test(o)&&o.replace(/.*(?=#[^\s]*$)/,""));var a=o?t(o):n.next(".dropdown-menu"),s=i.transferEvent;if(s!==!1){var r="data-contextmenu-index";a.find("a,.contextmenu-item").each(function(e){t(this).attr(r,e)});var l=a.clone();return l.on("string"==typeof s?s:"click","a,.contextmenu-item",function(e){var i=a.find("["+r+'="'+t(this).attr(r)+'"]'),n=i[0];if(n)return n[e.type]?n[e.type]():i.trigger(e.type),e.preventDefault(),e.stopPropagation(),!1}),l}return a.clone()},position:function(t,e,i){var n=e.placement,o=e.$toggle;if(!n){var a=i.find(".dropdown-menu"),s=a.hasClass("pull-right"),r=o.parent().hasClass("dropup");n=s?r?"top-right":"bottom-right":r?"top-left":"bottom-left",s&&a.removeClass("pull-right")}var l=o[0].getBoundingClientRect();switch(n){case"top-left":return{x:l.left,y:Math.floor(l.top-t.height)};case"top-right":return{x:Math.floor(l.right-t.width),y:Math.floor(l.top-t.height)};case"bottom-left":return{x:l.left,y:l.bottom};case"bottom-right":return{x:Math.floor(l.right-t.width),y:l.bottom}}return t}},e))},t(document).on("click",function(e){var i=t(e.target),n=i.closest('[data-toggle="context-dropdown"]');if(n.length){var a=n.data(o);a||n.contextDropdown({show:!0})}else s||i.closest(".contextmenu").length||f()})}(jQuery,void 0),/*! * jQuery Form Plugin * version: 4.2.2 * Requires jQuery v1.7.2 or later