The previous commit inadvertently added parts of the browser treeview implementation.

This adds the rest (which still requires further work to make it do anything useful).
This commit is contained in:
Dave Page 2015-02-13 13:24:13 +00:00
parent b09c1b6c68
commit 2f154bbed4
36 changed files with 7715 additions and 0 deletions

View File

@ -0,0 +1,549 @@
@charset "utf-8";
/*
* aciTree jQuery Plugin
* http://acoderinsights.ro
*
*/
.aciTree {
outline: none;
}
/* the big loader */
.aciTree.aciTreeLoad {
background:url(../image/load-root.gif) center center no-repeat;
}
.aciTree .aciTreeUl {
list-style:none;
margin:0;
padding:0;
}
.aciTree .aciTreeLi {
display:block;
clear:both;
white-space:nowrap;
}
.aciTree .aciTreeLine:focus {
outline:none;
}
.aciTree .aciTreeLi.aciTreeHidden {
display:none;
}
/* the branch line */
.aciTree .aciTreeBranch {
padding-left:20px; /* branch indent */
background:url(../image/tree-branch.png) left 1px repeat-y;
}
.aciTree[dir=rtl] .aciTreeBranch {
padding-left:0;
padding-right:20px; /* branch indent */
background:url(../image/tree-branch.png) right 1px repeat-y;
}
/* if the parent is the last child on his level (this is level based; added for #8 levels, if you need more ... add them as needed) */
.aciTree .aciTreeLi.aciTreeLevel0.aciTreeLast .aciTreeBranch.aciTreeLevel0,
.aciTree .aciTreeLi.aciTreeLevel1.aciTreeLast .aciTreeBranch.aciTreeLevel1,
.aciTree .aciTreeLi.aciTreeLevel2.aciTreeLast .aciTreeBranch.aciTreeLevel2,
.aciTree .aciTreeLi.aciTreeLevel3.aciTreeLast .aciTreeBranch.aciTreeLevel3,
.aciTree .aciTreeLi.aciTreeLevel4.aciTreeLast .aciTreeBranch.aciTreeLevel4,
.aciTree .aciTreeLi.aciTreeLevel5.aciTreeLast .aciTreeBranch.aciTreeLevel5,
.aciTree .aciTreeLi.aciTreeLevel6.aciTreeLast .aciTreeBranch.aciTreeLevel6,
.aciTree .aciTreeLi.aciTreeLevel7.aciTreeLast .aciTreeBranch.aciTreeLevel7,
.aciTree .aciTreeLi.aciTreeLevel8.aciTreeLast .aciTreeBranch.aciTreeLevel8,
.aciTree.aciTreeNoBranches .aciTreeBranch {
background:none;
}
/* the branch line behind the button (for the siblings) */
.aciTree .aciTreeEntry {
overflow:hidden;
background:url(../image/tree-branch.png) left 1px repeat-y;
}
.aciTree[dir=rtl] .aciTreeEntry {
background:url(../image/tree-branch.png) right 1px repeat-y;
}
.aciTree .aciTreeLi.aciTreeLast>.aciTreeLine .aciTreeEntry,
.aciTree.aciTreeNoBranches .aciTreeEntry {
background:none;
}
.aciTree.aciTreeBig .aciTreeEntry {
padding:0 0 2px 0;
}
.aciTree .aciTreeButton, .aciTree .aciTreePush, .aciTree .aciTreeItem, .aciTree .aciTreeIcon, .aciTree .aciTreeText, .aciTree .aciTreeColumn {
display:inline-block;
height:20px;
line-height:20px;
font-family:Verdana, Geneva, sans-serif;
font-size:11px;
color:#000;
vertical-align:top;
}
.aciTree.aciTreeBig .aciTreeText, .aciTree.aciTreeBig .aciTreeColumn {
font-size:12px;
}
.aciTree .aciTreeDisabled>.aciTreeLine .aciTreeText, .aciTree .aciTreeDisabled>.aciTreeLine .aciTreeColumn {
color:#888;
}
.aciTree .aciTreeItem {
padding:0 2px 0 2px;
border:1px solid transparent;
height:auto;
white-space:normal;
cursor:pointer;
/* margin-right need to be set to icon width [.aciTree .aciTreeIcon = 20] +
item padding [.aciTree .aciTreeItem = 4] (+ the width of all columns, if any) */
margin-right:24px;
}
.aciTree[dir=rtl] .aciTreeItem{
margin-right:0;
/* margin-left need to be set to icon width [.aciTree .aciTreeIcon = 20] +
item padding [.aciTree .aciTreeItem = 4] (+ the width of all columns, if any) */
margin-left:24px;
}
.aciTree .aciTreeText {
display:inline;
height:auto;
}
.aciTree .aciTreeColumn {
float:right;
height:auto;
white-space:normal;
cursor:default;
}
.aciTree[dir=rtl] .aciTreeColumn {
float:left;
}
/* columns width/style (left to right, if any) */
.aciTree .aciTreeColumn0 {
width:80px;
}
.aciTree .aciTreeColumn1 {
width:60px;
}
/* item selection */
.aciTree .aciTreeLine.aciTreeHover .aciTreeItem {
background-color:#EFF5FD;
border:1px dashed #D9D9D9;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
}
.aciTree .aciTreeFocus>.aciTreeLine .aciTreeItem {
/* not selected but with focus */
border:1px solid #D9D9D9;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
}
.aciTree .aciTreeSelected>.aciTreeLine .aciTreeItem {
background-color:#E8E8E8;
border:1px dashed #D9D9D9;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
}
.aciTree.aciTreeFocus .aciTreeSelected>.aciTreeLine .aciTreeItem {
background-color:#d0e5fe;
}
.aciTree.aciTreeFocus .aciTreeFocus>.aciTreeLine .aciTreeItem {
/* not selected but with focus */
border:1px solid #84acdd;
}
.aciTree.aciTreeFocus .aciTreeSelected.aciTreeFocus>.aciTreeLine .aciTreeItem {
border:1px dashed #84acdd;
}
/* full row selection */
.aciTree.aciTreeFullRow>.aciTreeUl {
margin:2px;
}
.aciTree.aciTreeFullRow .aciTreeLine {
margin:-2px;
border:1px solid transparent;
}
.aciTree.aciTreeFullRow .aciTreeLine.aciTreeHover .aciTreeItem {
background:none;
border:1px solid transparent;
}
.aciTree.aciTreeFullRow .aciTreeFocus>.aciTreeLine {
/* not selected but with focus */
border:1px solid #D9D9D9;
}
.aciTree.aciTreeFullRow .aciTreeSelected>.aciTreeLine .aciTreeItem,
.aciTree.aciTreeFullRow .aciTreeFocus>.aciTreeLine .aciTreeItem {
background:none;
border:1px solid transparent;
}
.aciTree.aciTreeFullRow.aciTreeFocus .aciTreeSelected>.aciTreeLine .aciTreeItem {
background:none;
border:1px solid transparent;
}
.aciTree.aciTreeFullRow .aciTreeLine.aciTreeHover {
background: #e3edf9;
background: -moz-linear-gradient(top, #e3edf9 0%, #9cb7d8 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#e3edf9), color-stop(100%,#9cb7d8));
background: -webkit-linear-gradient(top, #e3edf9 0%,#9cb7d8 100%);
background: -o-linear-gradient(top, #e3edf9 0%,#9cb7d8 100%);
background: -ms-linear-gradient(top, #e3edf9 0%,#9cb7d8 100%);
background: linear-gradient(to bottom, #e3edf9 0%,#9cb7d8 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e3edf9', endColorstr='#9cb7d8',GradientType=0 );
}
.aciTree.aciTreeFullRow .aciTreeSelected>.aciTreeLine {
background: #f4f4f4;
background: -moz-linear-gradient(top, #f4f4f4 0%, #c6c6c6 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f4f4f4), color-stop(100%,#c6c6c6));
background: -webkit-linear-gradient(top, #f4f4f4 0%,#c6c6c6 100%);
background: -o-linear-gradient(top, #f4f4f4 0%,#c6c6c6 100%);
background: -ms-linear-gradient(top, #f4f4f4 0%,#c6c6c6 100%);
background: linear-gradient(to bottom, #f4f4f4 0%,#c6c6c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#c6c6c6',GradientType=0 );
}
.aciTree.aciTreeFullRow.aciTreeFocus .aciTreeFocus>.aciTreeLine {
/* not selected but with focus */
border:1px solid #84acdd;
}
.aciTree.aciTreeFullRow.aciTreeFocus .aciTreeSelected>.aciTreeLine {
border:1px solid transparent;
background: #d0e5fe;
background: -moz-linear-gradient(top, #d0e5fe 0%, #84acdd 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d0e5fe), color-stop(100%,#84acdd));
background: -webkit-linear-gradient(top, #d0e5fe 0%,#84acdd 100%);
background: -o-linear-gradient(top, #d0e5fe 0%,#84acdd 100%);
background: -ms-linear-gradient(top, #d0e5fe 0%,#84acdd 100%);
background: linear-gradient(to bottom, #d0e5fe 0%,#84acdd 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d0e5fe', endColorstr='#84acdd',GradientType=0 );
}
.aciTree.aciTreeFullRow.aciTreeFocus .aciTreeSelected>.aciTreeLine.aciTreeHover,
.aciTree.aciTreeFullRow .aciTreeSelected>.aciTreeLine.aciTreeHover {
background: #c3dbf7;
background: -moz-linear-gradient(top, #c3dbf7 0%, #84a9d6 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c3dbf7), color-stop(100%,#84a9d6));
background: -webkit-linear-gradient(top, #c3dbf7 0%,#84a9d6 100%);
background: -o-linear-gradient(top, #c3dbf7 0%,#84a9d6 100%);
background: -ms-linear-gradient(top, #c3dbf7 0%,#84a9d6 100%);
background: linear-gradient(to bottom, #c3dbf7 0%,#84a9d6 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#c3dbf7', endColorstr='#84a9d6',GradientType=0 );
}
/* checkbox/radio tree */
.aciTree .aciTreeCheckbox label, .aciTree .aciTreeRadio label {
cursor:pointer;
}
.aciTree .aciTreeCheck {
width:20px;
height:20px;
display:inline-block;
margin:0 2px 0 2px;
padding:0;
vertical-align:text-bottom;
background:url(../image/tree-check-small.png) 0 0 no-repeat;
}
.aciTree.aciTreeBig .aciTreeCheck {
background:url(../image/tree-check-big.png) 0 0 no-repeat;
}
.aciTree[dir=rtl] .aciTreeCheck {
background:url(../image/tree-check-small-rtl.png) 0 0 no-repeat;
}
.aciTree.aciTreeBig[dir=rtl] .aciTreeCheck {
background:url(../image/tree-check-big-rtl.png) 0 0 no-repeat;
}
.aciTree .aciTreeCheckbox>.aciTreeLine .aciTreeCheck { background-position:-5px -65px; }
.aciTree .aciTreeCheckbox.aciTreeChecked>.aciTreeLine .aciTreeCheck { background-position:-5px -5px; }
.aciTree .aciTreeCheckbox.aciTreeChecked.aciTreeTristate>.aciTreeLine .aciTreeCheck { background-position:-5px -35px; }
.aciTree .aciTreeCheckbox>.aciTreeLine.aciTreeHover .aciTreeCheck { background-position:-35px -65px; }
.aciTree .aciTreeCheckbox.aciTreeChecked>.aciTreeLine.aciTreeHover .aciTreeCheck { background-position:-35px -5px; }
.aciTree .aciTreeCheckbox.aciTreeChecked.aciTreeTristate>.aciTreeLine.aciTreeHover .aciTreeCheck { background-position:-35px -35px; }
.aciTree .aciTreeCheckbox.aciTreeDisabled>.aciTreeLine .aciTreeCheck { background-position:-65px -65px; }
.aciTree .aciTreeCheckbox.aciTreeDisabled.aciTreeChecked>.aciTreeLine .aciTreeCheck { background-position:-65px -5px; }
.aciTree .aciTreeCheckbox.aciTreeDisabled.aciTreeChecked.aciTreeTristate>.aciTreeLine .aciTreeCheck { background-position:-65px -35px; }
.aciTree .aciTreeRadio>.aciTreeLine .aciTreeCheck { background-position:-95px -65px; }
.aciTree .aciTreeRadio.aciTreeChecked>.aciTreeLine .aciTreeCheck { background-position:-95px -5px; }
.aciTree .aciTreeRadio.aciTreeChecked.aciTreeTristate>.aciTreeLine .aciTreeCheck { background-position:-95px -35px; }
.aciTree .aciTreeRadio>.aciTreeLine.aciTreeHover .aciTreeCheck { background-position:-125px -65px; }
.aciTree .aciTreeRadio.aciTreeChecked>.aciTreeLine.aciTreeHover .aciTreeCheck { background-position:-125px -5px; }
.aciTree .aciTreeRadio.aciTreeChecked.aciTreeTristate>.aciTreeLine.aciTreeHover .aciTreeCheck { background-position:-125px -35px; }
.aciTree .aciTreeRadio.aciTreeDisabled>.aciTreeLine .aciTreeCheck { background-position:-155px -65px; }
.aciTree .aciTreeRadio.aciTreeDisabled.aciTreeChecked>.aciTreeLine .aciTreeCheck { background-position:-155px -5px; }
.aciTree .aciTreeRadio.aciTreeDisabled.aciTreeChecked.aciTreeTristate>.aciTreeLine .aciTreeCheck { background-position:-155px -35px; }
/* inplace editable */
.aciTree .aciTreeEdited input[type=text] {
outline: none;
border:1px solid #000;
padding:0;
margin:0;
line-height:18px;
height:18px;
display:inline-block;
font-family:Verdana, Geneva, sans-serif;
font-size:11px;
color:#000;
vertical-align:top;
}
.aciTree.aciTreeBig .aciTreeEdited input[type=text] {
font-size:12px;
}
/* sortable items */
.aciTree .aciTreeChild {
height:0;
line-height:0;
}
.aciTree .aciTreePlaceholder {
height:0;
line-height:0;
overflow:visible;
position:relative;
}
.aciTree .aciTreePlaceholder div {
position:absolute;
left:0;
width:16px;
height:16px;
margin-left:20px;
background:#fff url(../image/drag-drop.png) -7px -37px no-repeat;
border:1px solid #999;
}
.aciTree[dir=rtl] .aciTreePlaceholder div {
background:#fff url(../image/drag-drop-rtl.png) -7px -37px no-repeat;
}
.aciTree .aciTreeChild .aciTreePlaceholder div {
top:-20px;
left:-20px;
}
.aciTree.aciTreeBig .aciTreeChild .aciTreePlaceholder div {
top:-22px;
}
.aciTree .aciTreePlaceholder.aciTreeBefore div {
top:2px;
background-position:-7px -7px !important;
}
.aciTree .aciTreePlaceholder.aciTreeAfter div {
top:-20px;
background-position:-7px -67px !important;
}
.aciTree.aciTreeBig .aciTreePlaceholder.aciTreeAfter div {
top:-22px;
}
.aciTree.aciTreeDragDrop .aciTreeItem, .aciTree.aciTreeDragDrop .aciTreeColumn,
.aciTree.aciTreeDragDrop .aciTreeCheckbox label, .aciTree.aciTreeDragDrop .aciTreeRadio label {
cursor:inherit !important;
}
/* this is level based; added for #8 levels, if you need more ... add them as needed */
.aciTree .aciTreeLi.aciTreeLevel0 .aciTreePlaceholder div { margin-left:40px; }
.aciTree .aciTreeLi.aciTreeLevel1 .aciTreePlaceholder div { margin-left:60px; }
.aciTree .aciTreeLi.aciTreeLevel2 .aciTreePlaceholder div { margin-left:80px; }
.aciTree .aciTreeLi.aciTreeLevel3 .aciTreePlaceholder div { margin-left:100px; }
.aciTree .aciTreeLi.aciTreeLevel4 .aciTreePlaceholder div { margin-left:120px; }
.aciTree .aciTreeLi.aciTreeLevel5 .aciTreePlaceholder div { margin-left:140px; }
.aciTree .aciTreeLi.aciTreeLevel6 .aciTreePlaceholder div { margin-left:160px; }
.aciTree .aciTreeLi.aciTreeLevel7 .aciTreePlaceholder div { margin-left:180px; }
.aciTree .aciTreeLi.aciTreeLevel8 .aciTreePlaceholder div { margin-left:200px; }
.aciTree[dir=rtl] .aciTreePlaceholder div {
left:auto;
right:0;
margin-left:0;
margin-right:20px;
}
.aciTree .aciTreeChild .aciTreePlaceholder div {
right:-20px;
}
/* this is level based; added for #8 levels, if you need more ... add them as needed */
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel0 .aciTreePlaceholder div { margin-right:40px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel1 .aciTreePlaceholder div { margin-right:60px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel2 .aciTreePlaceholder div { margin-right:80px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel3 .aciTreePlaceholder div { margin-right:100px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel4 .aciTreePlaceholder div { margin-right:120px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel5 .aciTreePlaceholder div { margin-right:140px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel6 .aciTreePlaceholder div { margin-right:160px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel7 .aciTreePlaceholder div { margin-right:180px; }
.aciTree[dir=rtl] .aciTreeLi.aciTreeLevel8 .aciTreePlaceholder div { margin-right:200px; }
.aciTreeHelper {
position:absolute;
max-width:300px;
color:#000;
background-color:#d0e5fe;
border:1px dashed #84acdd;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
padding:4px;
margin:20px 0 0 20px;
}
/* default - item in the middle (comment the hover part to keep the same button image) */
.aciTree .aciTreeButton, .aciTree .aciTreePush {
width:18px;
background:url(../image/tree-small.png) -7px -35px no-repeat;
}
.aciTree.aciTreeBig .aciTreeButton, .aciTree.aciTreeBig .aciTreePush {
background:url(../image/tree-big.png) -7px -35px no-repeat;
}
.aciTree[dir=rtl] .aciTreeButton, .aciTree[dir=rtl] .aciTreePush {
background:url(../image/tree-small-rtl.png) -7px -35px no-repeat;
}
.aciTree.aciTreeBig[dir=rtl] .aciTreeButton, .aciTree.aciTreeBig[dir=rtl] .aciTreePush {
background:url(../image/tree-big-rtl.png) -7px -35px no-repeat;
}
.aciTree.aciTreeArrow .aciTreeButton, .aciTree.aciTreeArrow .aciTreePush {
background:url(../image/tree-arrow-small.png) -7px -35px no-repeat;
}
.aciTree.aciTreeBig.aciTreeArrow .aciTreeButton, .aciTree.aciTreeBig.aciTreeArrow .aciTreePush {
background:url(../image/tree-arrow-big.png) -7px -35px no-repeat;
}
.aciTree.aciTreeArrow[dir=rtl] .aciTreeButton, .aciTree.aciTreeArrow[dir=rtl] .aciTreePush {
background:url(../image/tree-arrow-small-rtl.png) -7px -35px no-repeat;
}
.aciTree.aciTreeBig.aciTreeArrow[dir=rtl] .aciTreeButton, .aciTree.aciTreeBig.aciTreeArrow[dir=rtl] .aciTreePush {
background:url(../image/tree-arrow-big-rtl.png) -7px -35px no-repeat;
}
.aciTree.aciTreeNoBranches .aciTreeButton,
.aciTree .aciTreeLeaf>.aciTreeLine .aciTreePush { background:none !important; }
.aciTree .aciTreeInodeMaybe>.aciTreeLine .aciTreePush { background-position:-67px -5px !important; }
.aciTree .aciTreeInodeMaybe>.aciTreeLine .aciTreePush.aciTreeHover { background-position:-67px -35px !important; }
.aciTree .aciTreeInode>.aciTreeLine .aciTreePush { background-position:-97px -5px !important; }
.aciTree .aciTreeInode>.aciTreeLine .aciTreePush.aciTreeHover { background-position:-97px -35px !important; }
.aciTree .aciTreeOpen>.aciTreeLine .aciTreePush { background-position:-127px -5px !important; }
.aciTree .aciTreeOpen>.aciTreeLine .aciTreePush.aciTreeHover { background-position:-127px -35px !important; }
/* if it's the last item in list */
.aciTree .aciTreeLi.aciTreeLast>.aciTreeLine .aciTreeButton { background-position:-37px -5px !important; }
/* the item loader */
.aciTree .aciTreePush>span {
display:none;
position:absolute;
width:18px;
height:18px;
left:0;
top:2px;
background:url(../image/load-node.gif) 0 0 no-repeat;
}
.aciTree .aciTreeLoad>.aciTreeLine .aciTreePush {
position:relative;
/* uncomment next line to hide the button while loading */
/*background:none !important;*/
}
.aciTree .aciTreeLoad>.aciTreeLine .aciTreePush>span {
display:inline-block;
}
/* tree item icon */
.aciTree .aciTreeIcon {
width:20px;
background:url(../image/tree-small.png) 0 0 no-repeat;
}
.aciTree.aciTreeBig .aciTreeIcon {
background:url(../image/tree-big.png) 0 0 no-repeat;
}
.aciTree[dir=rtl] .aciTreeIcon {
width:16px;
margin-left:4px;
background:url(../image/tree-small-rtl.png) 0 0 no-repeat;
}
.aciTree.aciTreeBig[dir=rtl] .aciTreeIcon {
background:url(../image/tree-big-rtl.png) 0 0 no-repeat;
}
.aciTree.aciTreeArrow .aciTreeIcon { background:url(../image/tree-arrow-small.png) 0 0 no-repeat; }
.aciTree.aciTreeBig.aciTreeArrow .aciTreeIcon { background:url(../image/tree-arrow-big.png) 0 0 no-repeat; }
.aciTree.aciTreeArrow[dir=rtl] .aciTreeIcon { background:url(../image/tree-arrow-small-rtl.png) 0 0 no-repeat; }
.aciTree.aciTreeBig.aciTreeArrow[dir=rtl] .aciTreeIcon { background:url(../image/tree-arrow-big-rtl.png) 0 0 no-repeat; }
/* demo file/folder icon class */
.aciTree .aciTreeIcon.folder {
background-position:-157px -5px !important;
}
.aciTree .aciTreeLine.aciTreeHover .aciTreeIcon.folder {
/* comment next line to keep the same icon on hover */
background-position:-157px -35px !important;
}
.aciTree .aciTreeIcon.file {
background-position:-187px -5px !important;
}
.aciTree .aciTreeLine.aciTreeHover .aciTreeIcon.file {
/* comment next line to keep the same icon on hover */
background-position:-187px -35px !important;
}
/* demo row colors */
.aciTree.aciTreeColors .aciTreeOdd>.aciTreeLine {
/* odd rows */
background-color:#FFFFC4;
}
.aciTree.aciTreeColors .aciTreeEven>.aciTreeLine {
/* even rows */
background-color:#CAFFCA;
}
.aciTree.aciTreeColors .aciTreeFirst>.aciTreeLine {
/* first item on each level */
/*background-color:#B0DFFF;*/
}
.aciTree.aciTreeColors .aciTreeLast>.aciTreeLine {
/* last item on each level */
/*background-color:#FFCEFF;*/
}
/* uncomment below to keep the item in one line and scroll the tree horizontally when needed */
/*
.aciTree .aciTreeEntry {
overflow:visible;
}
.aciTree .aciTreeItem {
white-space:nowrap;
margin-right:12px;
}
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,15 @@
/*
* aciFragment jQuery Plugin v1.1.0
* http://acoderinsights.ro
*
* Copyright (c) 2013 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.7.1 http://jquery.com
* + aciPlugin >= v1.1.1 https://github.com/dragosu/jquery-aciPlugin
*
* Date: Apr Fri 26 18:00 2013 +0200
*/
(function(e,t,n){var r={anchor:"anchor",poolDelay:250,scroll:{duration:"medium",easing:"linear"}};var i={__extend:function(){e.extend(this._instance,{lastHash:null,lastParsed:null,parsed:{},anchor:true,timeOut:null,"native":"onhashchange"in t&&(t.document.documentMode===n||t.document.documentMode>7)})},init:function(){var n=this;if(this.wasInit()){return}if(this._instance.native){e(t).bind("hashchange"+this._instance.nameSpace,function(){n._trigger()})}else{this._change()}this._instance.anchor=true;this._trigger();this._super()},_trigger:function(){this._instance.jQuery.trigger("acifragment",[this,this._instance.anchor]);this._instance.anchor=false},_change:function(){var e=this;var n=t.location.hash;if(n!=this._instance.lastHash){this._trigger();this._instance.lastHash=n}this._instance.timeOut=t.setTimeout(function(){e._change()},this._instance.options.poolDelay)},scroll:function(){var n=this.get(this._instance.options.anchor);if(n&&n.length){var r=e("#"+n+":first");if(!r.length){r=e('[name="'+n+'"]:first')}if(r.length){var i=r.get(0).getBoundingClientRect();var s=e(t).scrollLeft()+i.left,o=e(t).scrollTop()+i.top;if(this._instance.options.scroll){e("html,body").stop(true).animate({scrollLeft:s,scrollTop:o},this._instance.options.scroll)}else{t.scrollTo(s,o)}}}},click:function(e,t){var n=e.attr("href");if(n){var r=this.parse(n);this.update(r);if(t&&this.hasAnchor(r)){this.scroll()}}},parse:function(e){var n=e.indexOf("#"),r={};if(n!=-1){e=e.substr(n+1);var i=e.split("&"),s;for(var o in i){s=i[o].split("=");if(s.length>1){r[t.decodeURIComponent(s[0])]=t.decodeURIComponent(s[1])}else{r[this._instance.options.anchor]=t.decodeURIComponent(s[0])}}}return r},hasAnchor:function(e){return e[this._instance.options.anchor]&&e[this._instance.options.anchor].length>0},setAnchor:function(e){this.set(this._instance.options.anchor,e)},getAnchor:function(e){return this.get(this._instance.options.anchor,e)},parseHash:function(){var e=t.location.hash;if(e==this._instance.lastParsed){return this._instance.parsed}var n=this.parse(e);this._instance.parsed=n;this._instance.lastParsed=e;return n},get:function(e,r){var i=this.parseHash();if(i[e]!==null&&i[e]!==n&&t.String(i[e]).length){return i[e]}else{return r}},replace:function(e,r){var i=[];for(var s in e){if(e[s]!==null&&e[s]!==n&&t.String(e[s]).length){i[i.length]=t.encodeURIComponent(s)+"="+t.encodeURIComponent(e[s])}}if(!r&&this.hasAnchor(e)){this._instance.anchor=true}var o=t.location.hash;if(i.length){t.location.hash="#"+i.join("&")}else if(t.history&&t.history.pushState){t.history.pushState("",t.document.title,t.location.pathname+t.location.search)}else{t.location.hash=""}if(t.location.hash==o){this._trigger()}},update:function(e){var t=this.parseHash();for(var n in e){t[n]=e[n]}if(this.hasAnchor(e)){this._instance.anchor=true}this.replace(t,true)},set:function(e,t){var n=this.parseHash();n[e]=t;if(e==this._instance.options.anchor){this._instance.anchor=true}this.replace(n,true)},destroy:function(){if(!this.wasInit()){return}if(this._instance.native){e(t).unbind(this._instance.nameSpace)}t.clearTimeout(this._instance.timeOut);this._super()}};aciPluginClass.plugins.aciFragment=aciPluginClass.aciPluginUi.extend(i,"aciFragment");aciPluginClass.publish("aciFragment",r)})(jQuery,this);

View File

@ -0,0 +1,12 @@
/*
* aciPlugin little jQuery plugin helper v1.5.1
* http://acoderinsights.ro
*
* Copyright (c) 2013 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.2.3 http://jquery.com
*/
(function(d,c,e){if(typeof aciPluginClass!=="undefined"){return}var a;this.aciPluginClass=function(){};aciPluginClass.extend=function(g,j){i.extend=arguments.callee;function i(){if(a){this._instance={};return this.__construct.apply(this,arguments)}}a=false;i.prototype=new this();a=true;var h=this.prototype;for(var f in g){i.prototype[f]=((typeof g[f]=="function")&&(f!="proxy"))?(function(k){return function(){var p=this._parent;this._parent=h;var n=this._super;this._super=h[k];var o=this._private;if(this._instance&&j){var m=this._instance._private;if(m[j]===e){m[j]={nameSpace:"."+j}}this._private=m[j]}var l=g[k].apply(this,arguments);this._parent=p;this._super=n;this._private=o;return l}})(f):g[f]}return i};var b=0;aciPluginClass.aciPluginUi=aciPluginClass.extend({__construct:function(k,l,i,h,j){var f="."+k;var g=l.data(f);if(g){this._instance=g._instance;return g.__request(i,h,j)}l.data(f,this);d.extend(true,this._instance,{_private:{},nameSpace:f,jQuery:l,options:d.extend(true,{},d.fn[k].defaults,(typeof i=="object")?i:{}),index:b++,wasInit:false});this.__extend();return this.__request(i,h,j)},__extend:function(){},__request:function(g,f,h){if((g===e)||(typeof g=="object")){if(this._instance.options.autoInit){this.init()}}else{if(typeof g=="string"){switch(g){case"init":this.init();break;case"api":return{object:this};case"options":if(f===e){return{object:this.options()}}else{if(typeof f=="string"){return{object:this.options(f)}}else{this.options(f)}}break;case"option":this.option(f,h);break;case"destroy":this.destroy();break}}}return this._instance.jQuery},proxy:function(j,h){var m=c.Array.prototype.slice;var f=m.call(arguments,2);var i=this,g=i._parent,l=i._super,k=i._private;return function(){i._parent=g;i._super=l;i._private=k;return j.apply(i,h?f.concat([this],m.call(arguments)):f.concat(m.call(arguments)))}},init:function(){if(!this._instance.wasInit){this._instance.wasInit=true;return true}return false},wasInit:function(){return this._instance.wasInit},__parent:function(h,f,l){var m=f.split(".");if(m.length>1){var j=h,k;for(var g in m){k=j;j=j[m[g]]}l.name=m[g];return k}l.name=f;return h},options:function(f){if(f){var i={name:null};var h;if(typeof f=="string"){h=this.__parent(this._instance.options,f,i);return h[i.name]}else{for(var g in f){h=this.__parent(this._instance.options,g,i);h[i.name]=f[g]}}}else{return this._instance.options}},option:function(g,i){var h={name:null};var f=this.__parent(this._instance.options,g,h);f[h.name]=i},destroy:function(){if(this._instance.wasInit){this._instance.wasInit=false;this._instance.jQuery.removeData(this._instance.nameSpace);return true}return false}});aciPluginClass.plugins={};aciPluginClass.publish=function(f,g){d.fn[f]=function(j,m,n){var h=null;for(var l=0,k=this.length;l<k;l++){h=new aciPluginClass.plugins[f](f,d(this[l]),j,m,n);if(!(h instanceof d)){return h.object}}return this};d.fn[f].defaults=d.extend(true,{autoInit:true},(typeof g=="object")?g:{})};aciPluginClass.defaults=function(f,g){d.extend(true,d.fn[f].defaults,(typeof g=="object")?g:{})}})(jQuery,this);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,483 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds checkbox support to aciTree,
* should be used with the selectable extension.
*
* The are a few extra properties for the item data:
*
* {
* ...
* checkbox: true, // TRUE (default) means the item will have a checkbox (can be omitted if the `radio` extension is not used)
* checked: false, // if should be checked or not
* ...
* }
*
*/
(function($, window, undefined) {
// extra default options
var options = {
checkbox: false, // if TRUE then each item will have a checkbox
checkboxChain: true,
// if TRUE the selection will propagate to the parents/children
// if -1 the selection will propagate only to parents
// if +1 the selection will propagate only to children
// if FALSE the selection will not propagate in any way
checkboxBreak: true, // if TRUE then a missing checkbox will break the chaining
checkboxClick: false // if TRUE then a click will trigger a state change only when made over the checkbox itself
};
// aciTree checkbox extension
var aciTree_checkbox = {
// init checkbox
_checkboxInit: function() {
this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
switch (eventName) {
case 'loaded':
// check/update on item load
api._checkboxLoad(item);
break;
}
}).bind('keydown' + this._private.nameSpace, this.proxy(function(e) {
switch (e.which) {
case 32: // space
// support `selectable` extension
if (this.extSelectable && this.extSelectable() && !e.ctrlKey) {
var item = this.focused();
if (this.hasCheckbox(item) && this.isEnabled(item)) {
if (this.isChecked(item)) {
this.uncheck(item);
} else {
this.check(item);
}
e.stopImmediatePropagation();
// prevent page scroll
e.preventDefault();
}
}
break;
}
})).on('click' + this._private.nameSpace, '.aciTreeItem', this.proxy(function(e) {
if (!this._instance.options.checkboxClick || $(e.target).is('.aciTreeCheck')) {
var item = this.itemFrom(e.target);
if (this.hasCheckbox(item) && this.isEnabled(item) && (!this.extSelectable || !this.extSelectable() || (!e.ctrlKey && !e.shiftKey))) {
// change state on click
if (this.isChecked(item)) {
this.uncheck(item);
} else {
this.check(item);
}
e.preventDefault();
}
}
}));
},
// override `_initHook`
_initHook: function() {
if (this.extCheckbox()) {
this._checkboxInit();
}
// call the parent
this._super();
},
// override `_itemHook`
_itemHook: function(parent, item, itemData, level) {
if (this.extCheckbox()) {
// support `radio` extension
var radio = this.extRadio && this.hasRadio(item);
if (!radio && (itemData.checkbox || ((itemData.checkbox === undefined) && (!this.extRadio || !this.extRadio())))) {
this._checkboxDOM.add(item, itemData);
}
}
// call the parent
this._super(parent, item, itemData, level);
},
// low level DOM functions
_checkboxDOM: {
// add item checkbox
add: function(item, itemData) {
domApi.addClass(item[0], itemData.checked ? ['aciTreeCheckbox', 'aciTreeChecked'] : 'aciTreeCheckbox');
var text = domApi.childrenByClass(item[0].firstChild, 'aciTreeText');
var parent = text.parentNode;
var label = window.document.createElement('LABEL');
var check = window.document.createElement('SPAN');
check.className = 'aciTreeCheck';
label.appendChild(check);
label.appendChild(text);
parent.appendChild(label);
item[0].firstChild.setAttribute('aria-checked', !!itemData.checked);
},
// remove item checkbox
remove: function(item) {
domApi.removeClass(item[0], ['aciTreeCheckbox', 'aciTreeChecked', 'aciTreeTristate']);
var text = domApi.childrenByClass(item[0].firstChild, 'aciTreeText');
var label = text.parentNode;
var parent = label.parentNode;
parent.replaceChild(text, label)
item[0].firstChild.removeAttribute('aria-checked');
},
// (un)check items
check: function(items, state) {
domApi.toggleListClass(items.toArray(), 'aciTreeChecked', state, function(node) {
node.firstChild.setAttribute('aria-checked', state);
});
},
// (un)set tristate items
tristate: function(items, state) {
domApi.toggleListClass(items.toArray(), 'aciTreeTristate', state);
}
},
// update items on load, starting from the loaded node
_checkboxLoad: function(item) {
if (this._instance.options.checkboxChain === false) {
// do not update on load
return;
}
var state = undefined;
if (this.hasCheckbox(item)) {
if (this.isChecked(item)) {
if (!this.checkboxes(this.children(item, false, true), true).length) {
// the item is checked but no children are, check them all
state = true;
}
} else {
// the item is not checked, uncheck all children
state = false;
}
}
this._checkboxUpdate(item, state);
},
// get children list
_checkboxChildren: function(item) {
if (this._instance.options.checkboxBreak) {
var list = [];
var process = this.proxy(function(item) {
var children = this.children(item, false, true);
children.each(this.proxy(function(element) {
var item = $(element);
// break on missing checkbox
if (this.hasCheckbox(item)) {
list.push(element);
process(item);
}
}, true));
});
process(item);
return $(list);
} else {
var children = this.children(item, true, true);
return this.checkboxes(children);
}
},
// update checkbox state
_checkboxUpdate: function(item, state) {
// update children
var checkDown = this.proxy(function(item, count, state) {
var children = this.children(item, false, true);
var total = 0;
var checked = 0;
children.each(this.proxy(function(element) {
var item = $(element);
var subCount = {
total: 0,
checked: 0
};
if (this.hasCheckbox(item)) {
if ((state !== undefined) && (this._instance.options.checkboxChain !== -1)) {
this._checkboxDOM.check(item, state);
}
total++;
if (this.isChecked(item)) {
checked++;
}
checkDown(item, subCount, state);
} else {
if (this._instance.options.checkboxBreak) {
var reCount = {
total: 0,
checked: 0
};
checkDown(item, reCount);
} else {
checkDown(item, subCount, state);
}
}
total += subCount.total;
checked += subCount.checked;
}, true));
if (item) {
this._checkboxDOM.tristate(item, (checked > 0) && (checked != total));
count.total += total;
count.checked += checked;
}
});
var count = {
total: 0,
checked: 0
};
checkDown(item, count, state);
// update parents
var checkUp = this.proxy(function(item, tristate, state) {
var parent = this.parent(item);
if (parent.length) {
if (!tristate) {
var children = this._checkboxChildren(parent);
var checked = this.checkboxes(children, true).length;
var tristate = (checked > 0) && (checked != children.length);
}
if (this.hasCheckbox(parent)) {
if ((state !== undefined) && (this._instance.options.checkboxChain !== 1)) {
this._checkboxDOM.check(parent, tristate ? true : state);
}
this._checkboxDOM.tristate(parent, tristate);
checkUp(parent, tristate, state);
} else {
if (this._instance.options.checkboxBreak) {
checkUp(parent);
} else {
checkUp(parent, tristate, state);
}
}
}
});
checkUp(item, undefined, state);
},
// test if item have a checkbox
hasCheckbox: function(item) {
return item && domApi.hasClass(item[0], 'aciTreeCheckbox');
},
// add checkbox
addCheckbox: function(item, options) {
options = this._options(options, 'checkboxadded', 'addcheckboxfail', 'wascheckbox', item);
if (this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeaddcheckbox', options)) {
this._fail(item, options);
return;
}
if (this.hasCheckbox(item)) {
this._notify(item, options);
} else {
var process = function() {
this._checkboxDOM.add(item, {
});
this._success(item, options);
};
// support `radio` extension
if (this.extRadio && this.hasRadio(item)) {
// remove radio first
this.removeRadio(item, this._inner(options, {
success: process,
fail: options.fail
}));
} else {
process.apply(this);
}
}
} else {
this._fail(item, options);
}
},
// remove checkbox
removeCheckbox: function(item, options) {
options = this._options(options, 'checkboxremoved', 'removecheckboxfail', 'notcheckbox', item);
if (this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeremovecheckbox', options)) {
this._fail(item, options);
return;
}
if (this.hasCheckbox(item)) {
this._checkboxDOM.remove(item);
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
this._fail(item, options);
}
},
// test if it's checked
isChecked: function(item) {
if (this.hasCheckbox(item)) {
return domApi.hasClass(item[0], 'aciTreeChecked');
}
// support `radio` extension
if (this._super) {
// call the parent
return this._super(item);
}
return false;
},
// check checkbox
check: function(item, options) {
if (this.extCheckbox && this.hasCheckbox(item)) {
options = this._options(options, 'checked', 'checkfail', 'waschecked', item);
// a way to cancel the operation
if (!this._trigger(item, 'beforecheck', options)) {
this._fail(item, options);
return;
}
if (this.isChecked(item)) {
this._notify(item, options);
} else {
this._checkboxDOM.check(item, true);
if (this._instance.options.checkboxChain !== false) {
// chain them
this._checkboxUpdate(item, true);
}
this._success(item, options);
}
} else {
// support `radio` extension
if (this._super) {
// call the parent
this._super(item, options);
} else {
this._trigger(item, 'checkfail', options);
this._fail(item, options);
}
}
},
// uncheck checkbox
uncheck: function(item, options) {
if (this.extCheckbox && this.hasCheckbox(item)) {
options = this._options(options, 'unchecked', 'uncheckfail', 'notchecked', item);
// a way to cancel the operation
if (!this._trigger(item, 'beforeuncheck', options)) {
this._fail(item, options);
return;
}
if (this.isChecked(item)) {
this._checkboxDOM.check(item, false);
if (this._instance.options.checkboxChain !== false) {
// chain them
this._checkboxUpdate(item, false);
}
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
// support `radio` extension
if (this._super) {
// call the parent
this._super(item, options);
} else {
this._trigger(item, 'uncheckfail', options);
this._fail(item, options);
}
}
},
// filter items with checkbox by state (if set)
checkboxes: function(items, state) {
if (state !== undefined) {
return $(domApi.withClass(items.toArray(), state ? ['aciTreeCheckbox', 'aciTreeChecked'] : 'aciTreeCheckbox', state ? null : 'aciTreeChecked'));
}
return $(domApi.withClass(items.toArray(), 'aciTreeCheckbox'));
},
// override `_serialize`
_serialize: function(item, callback) {
var data = this._super(item, callback);
if (data && this.extCheckbox()) {
if (data.hasOwnProperty('checkbox')) {
data.checkbox = this.hasCheckbox(item);
data.checked = this.isChecked(item);
} else if (this.hasCheckbox(item)) {
if (this.extRadio && this.extRadio()) {
data.checkbox = true;
}
data.checked = this.isChecked(item);
}
}
return data;
},
// override `serialize`
serialize: function(item, what, callback) {
if (what == 'checkbox') {
var serialized = '';
var children = this.children(item, true, true);
this.checkboxes(children, true).each(this.proxy(function(element) {
var item = $(element);
if (callback) {
serialized += callback.call(this, item, what, this.getId(item));
} else {
serialized += this._instance.options.serialize.call(this, item, what, this.getId(item));
}
}, true));
return serialized;
}
return this._super(item, what, callback);
},
// test if item is in tristate
isTristate: function(item) {
return item && domApi.hasClass(item[0], 'aciTreeTristate');
},
// filter tristate items
tristate: function(items) {
return $(domApi.withClass(items.toArray(), 'aciTreeTristate'));
},
// test if checkbox is enabled
extCheckbox: function() {
return this._instance.options.checkbox;
},
// override set `option`
option: function(option, value) {
if (this.wasInit() && !this.isLocked()) {
if ((option == 'checkbox') && (value != this.extCheckbox())) {
if (value) {
this._checkboxInit();
} else {
this._checkboxDone();
}
}
}
// call the parent
this._super(option, value);
},
// done checkbox
_checkboxDone: function(destroy) {
this._instance.jQuery.unbind(this._private.nameSpace);
this._instance.jQuery.off(this._private.nameSpace, '.aciTreeItem');
if (!destroy) {
// remove checkboxes
this.checkboxes(this.children(null, true, true)).each(this.proxy(function(element) {
this.removeCheckbox($(element));
}, true));
}
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._checkboxDone(true);
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the checkbox stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_checkbox, 'aciTreeCheckbox');
// add extra default options
aciPluginClass.defaults('aciTree', options);
// for internal access
var domApi = aciPluginClass.plugins.aciTree_dom;
})(jQuery, this);

View File

@ -0,0 +1,250 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds multiple column support to aciTree.
*
* The `columnData` option is used to tell what are the columns and show one or
* more values that will be read from the item data object.
*
* Column data is an array of column definitions, each column definition is
* an object:
*
* {
* width: 100,
* props: 'column_x',
* value: 'default'
* }
*
* where the `width` is the column width in [px], if undefined - then the value
* from the CSS will be used; the `props` is the property name that will be
* read from the item data, if undefined (or the `item-data[column.props]`
* is undefined) then a default value will be set for the column: the `value`.
*
*/
(function($, window, undefined) {
// extra default options
var options = {
columnData: [] // column definitions list
};
// aciTree columns extension
// adds item columns, set width with CSS or using the API
var aciTree_column = {
__extend: function() {
// add extra data
$.extend(this._private, {
propsIndex: { // column index cache
}
});
// call the parent
this._super();
},
// override `_initHook`
_initHook: function() {
if (this._instance.options.columnData.length) {
// check column width
var found = false, data;
for (var i in this._instance.options.columnData) {
data = this._instance.options.columnData[i];
if (data.width !== undefined) {
// update column width
this._updateCss('.aciTree.aciTree' + this._instance.index + ' .aciTreeColumn' + i, 'width:' + data.width + 'px;');
found = true;
}
this._private.propsIndex[data.props] = i;
}
if (found) {
// at least a column width set
this._updateWidth();
}
}
// call the parent
this._super();
},
// read property value from a CSS class name
_getCss: function(className, property, numeric) {
var id = '_getCss_' + window.String(className).replace(/[^a-z0-9_-]/ig, '_');
var test = $('body').find('#' + id);
if (!test.length) {
if (className instanceof Array) {
var style = '', end = '';
for (var i in className) {
style += '<div class="' + className[i] + '">';
end += '</div>';
}
style += end;
} else {
var style = '<div class="' + className + '"></div>';
}
$('body').append('<div id="' + id + '" style="position:relative;display:inline-block;width:0px;height:0px;line-height:0px;overflow:hidden">' + style + '</div>');
test = $('body').find('#' + id);
}
var value = test.find('*:last').css(property);
if (numeric) {
value = parseInt(value);
if (isNaN(value)) {
value = null;
}
}
return value;
},
// dynamically change a CSS class definition
_updateCss: function(className, definition) {
var id = '_updateCss_' + window.String(className).replace('>', '_gt_').replace(/[^a-z0-9_-]/ig, '_');
var style = '<style id="' + id + '" type="text/css">' + className + '{' + definition + '}</style>';
var test = $('body').find('#' + id);
if (test.length) {
test.replaceWith(style);
} else {
$('body').prepend(style);
}
},
// get column width
// `index` is the #0 based column index
getWidth: function(index) {
if ((index >= 0) && (index < this.columns())) {
return this._getCss(['aciTree aciTree' + this._instance.index, 'aciTreeColumn' + index], 'width', true);
}
return null;
},
// set column width
// `index` is the #0 based column index
setWidth: function(index, width) {
if ((index >= 0) && (index < this.columns())) {
this._updateCss('.aciTree.aciTree' + this._instance.index + ' .aciTreeColumn' + index, 'width:' + width + 'px;');
this._updateWidth();
}
},
// update item margins
_updateWidth: function() {
var width = 0;
for (var i in this._instance.options.columnData) {
if (this.isColumn(i)) {
width += this.getWidth(i);
}
}
var icon = this._getCss(['aciTree', 'aciTreeIcon'], 'width', true);
// add item padding
width += this._getCss(['aciTree', 'aciTreeItem'], 'padding-left', true) + this._getCss(['aciTree', 'aciTreeItem'], 'padding-right', true);
this._updateCss('.aciTree.aciTree' + this._instance.index + ' .aciTreeItem', 'margin-right:' + (icon + width) + 'px;');
this._updateCss('.aciTree[dir=rtl].aciTree' + this._instance.index + ' .aciTreeItem', 'margin-right:0;margin-left:' + (icon + width) + 'px;');
},
// test if column is visible
// `index` is the #0 based column index
isColumn: function(index) {
if ((index >= 0) && (index < this.columns())) {
return this._getCss(['aciTree aciTree' + this._instance.index, 'aciTreeColumn' + index], 'display') != 'none';
}
return false;
},
// get column index by `props`
// return -1 if the column does not exists
columnIndex: function(props) {
if (this._private.propsIndex[props] !== undefined) {
return this._private.propsIndex[props];
}
return -1;
},
// get the column count
columns: function() {
return this._instance.options.columnData.length;
},
// set column to be visible or hidden
// `index` is the #0 based column index
// if `show` is undefined then the column visibility will be toggled
toggleColumn: function(index, show) {
if ((index >= 0) && (index < this.columns())) {
if (show === undefined) {
var show = !this.isColumn(index);
}
this._updateCss('.aciTree.aciTree' + this._instance.index + ' .aciTreeColumn' + index, 'display:' + (show ? 'inherit' : 'none') + ';');
this._updateWidth();
}
},
// override `_itemHook`
_itemHook: function(parent, item, itemData, level) {
if (this.columns()) {
var position = domApi.childrenByClass(item[0].firstChild, 'aciTreeEntry'), data, column;
for (var i in this._instance.options.columnData) {
data = this._instance.options.columnData[i];
column = this._createColumn(itemData, data, i);
position.insertBefore(column, position.firstChild);
}
}
// call the parent
this._super(parent, item, itemData, level);
},
// create column markup
// `itemData` item data object
// `columnData` column data definition
// `index` is the #0 based column index
_createColumn: function(itemData, columnData, index) {
var value = columnData.props && (itemData[columnData.props] !== undefined) ? itemData[columnData.props] :
((columnData.value === undefined) ? '' : columnData.value);
var column = window.document.createElement('DIV');
column.className = 'aciTreeColumn aciTreeColumn' + index;
column.innerHTML = value.length ? value : '&nbsp;';
return column;
},
// set column content
// `options.index` the #0 based column index
// `options.value` is the new content
// `options.oldValue` will keep the old content
setColumn: function(item, options) {
options = this._options(options, 'columnset', 'columnfail', 'wascolumn', item);
if (this.isItem(item) && (options.index >= 0) && (options.index < this.columns())) {
// a way to cancel the operation
if (!this._trigger(item, 'beforecolumn', options)) {
this._fail(item, options);
return;
}
var data = this.itemData(item);
// keep the old one
options.oldValue = data[this._instance.options.columnData[options.index].props];
if (options.value == options.oldValue) {
this._notify(item, options);
} else {
// set the column
item.children('.aciTreeLine').find('.aciTreeColumn' + options.index).html(options.value);
// remember this one
data[this._instance.options.columnData[options.index].props] = options.value;
this._success(item, options);
}
} else {
this._fail(item, options);
}
},
// get column content
getColumn: function(item, index) {
if ((index >= 0) && (index < this.columns())) {
var data = this.itemData(item);
return data ? data[this._instance.options.columnData[index].props] : null;
}
return null;
}
};
// extend the base aciTree class and add the columns stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_column, 'aciTreeColumn');
// add extra default options
aciPluginClass.defaults('aciTree', options);
// for internal access
var domApi = aciPluginClass.plugins.aciTree_dom;
})(jQuery, this);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds debug capabilities, for now it's just a log of the aciTree events.
*/
(function($, window, undefined) {
// extra default options
var options = {
logTo: null // selector for the element where to log the errors to
};
// aciTree debug extension
var aciTree_debug = {
__extend: function() {
$.extend(this._private, {
logTo: null
});
// call the parent
this._super();
},
// init debug
_debugInit: function() {
if (this._instance.options.logTo) {
this._private.logTo = $(this._instance.options.logTo);
}
this._instance.jQuery.bind('acitree' + this._private.nameSpace, this.proxy(function(event, api, item, eventName, options) {
var message = 'aciTree event:' + eventName + ' for:' + (item ? api.getId(item) : 'ROOT') + ' uid:' + options.uid +
' success:' + (options.success ? 'Y' : 'N') + ' fail:' + (options.fail ? 'Y' : 'N') + ' expand:' + (options.expand ? 'Y' : 'N') +
' collapse:' + (options.collapse ? 'Y' : 'N') + ' unique:' + (options.unique ? 'Y' : 'N') + ' animated:' + (options.unanimated ? 'N' : 'Y');
if (this._private.logTo) {
this._private.logTo.prepend(message.replace(/([^\s]+:)/g, '<span style="color:#888">$1</span>') + '<br>');
} else if (console && console.log) {
console.log(message);
} else {
throw new Error(message);
}
}));
},
// override `_initHook`
_initHook: function() {
this._debugInit();
// call the parent
this._super();
},
// override set `option`
option: function(option, value) {
if (option == 'logTo') {
this._private.logTo = value ? $(value) : null;
}
// call the parent
this._super(option, value);
},
// done debug
_debugDone: function() {
this._instance.jQuery.unbind(this._private.nameSpace);
},
// override _destroyHook
_destroyHook: function(unloaded) {
if (unloaded) {
this._debugDone();
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the hash stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_debug, 'aciTreeDebug');
// add extra default options
aciPluginClass.defaults('aciTree', options);
})(jQuery, this);

View File

@ -0,0 +1,660 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* The aciTree low-level DOM functions.
*
* A collection of functions optimised for aciTree DOM structure.
*
* Need to be included before the aciTree core and after aciPlugin.
*/
aciPluginClass.plugins.aciTree_dom = {
// get the UL container from a LI
// `node` must be valid LI DOM node
// can return NULL
container: function(node) {
var container = node.lastChild;
if (container && (container.nodeName == 'UL')) {
return container;
}
return null;
},
// get the first children from a LI (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node
// can return NULL
firstChild: function(node, callback) {
var container = this.container(node);
if (container) {
var firstChild = container.firstChild;
if (callback) {
while (firstChild && !callback.call(this, firstChild)) {
firstChild = firstChild.nextSibling;
}
}
return firstChild;
}
return null;
},
// get the last children from a LI (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node
// can return NULL
lastChild: function(node, callback) {
var container = this.container(node);
if (container) {
var lastChild = container.lastChild;
if (callback) {
while (lastChild && !callback.call(this, lastChild)) {
lastChild = lastChild.previousSibling;
}
}
return lastChild;
}
return null;
},
// get the previous LI sibling (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node
// can return NULL
prev: function(node, callback) {
var previous = node.previousSibling;
if (callback) {
while (previous && !callback.call(this, previous)) {
previous = previous.previousSibling;
}
}
return previous;
},
// get the next LI sibling (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node
// can return NULL
next: function(node, callback) {
var next = node.nextSibling;
if (callback) {
while (next && !callback.call(this, next)) {
next = next.nextSibling;
}
}
return next;
},
// get the previous LI in tree order (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node or NULL to prevent drill down/skip the node
// can return NULL
prevAll: function(node, callback) {
var previous, lastChild, drillDown, match, prev, parent;
while (true) {
previous = this.prev(node);
if (previous) {
if (callback) {
match = callback.call(this, previous);
if (match === null) {
node = previous;
continue;
}
}
lastChild = this.lastChild(previous);
if (lastChild) {
if (callback && (callback.call(this, lastChild) === null)) {
node = lastChild;
continue;
}
prev = false;
while (drillDown = this.lastChild(lastChild)) {
lastChild = drillDown;
if (callback) {
match = callback.call(this, lastChild);
if (match === null) {
node = lastChild;
prev = true;
break;
}
}
}
if (prev) {
continue;
}
if (callback) {
match = callback.call(this, lastChild);
if (match) {
return lastChild;
} else if (match !== null) {
node = lastChild;
continue;
}
} else {
return lastChild;
}
} else {
if (!callback || match) {
return previous;
} else {
node = previous;
continue;
}
}
}
parent = this.parent(node);
if (parent) {
if (callback) {
match = callback.call(this, parent);
if (match) {
return parent;
} else {
node = parent;
}
} else {
return parent;
}
} else {
return null;
}
}
return null;
},
// get the next LI in tree order (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node or NULL to prevent drill down/skip the node
// can return NULL
nextAll: function(node, callback) {
var firstChild, match, next, parent, child;
while (true) {
firstChild = this.firstChild(node);
if (firstChild) {
if (callback) {
match = callback.call(this, firstChild);
if (match) {
return firstChild;
} else {
node = firstChild;
if (match !== null) {
continue;
}
}
} else {
return firstChild;
}
}
while (true) {
next = this.next(node);
if (next) {
if (callback) {
match = callback.call(this, next);
if (match) {
return next;
} else {
node = next;
if (match !== null) {
break;
} else {
continue;
}
}
} else {
return next;
}
} else {
parent = node;
child = null;
while (parent = this.parent(parent)) {
next = this.next(parent);
if (next) {
if (callback) {
match = callback.call(this, next);
if (match) {
return next;
} else {
node = next;
if (match !== null) {
child = true;
} else {
child = false;
}
break;
}
} else {
return next;
}
}
}
if (child !== null) {
if (child) {
break;
} else {
continue;
}
}
return null;
}
}
}
return null;
},
// get the first LI in tree order (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node or NULL to prevent drill down/skip the node
// can return NULL
first: function(node, callback) {
var container = this.container(node);
if (container) {
var firstChild = container.firstChild;
if (firstChild) {
if (callback && !callback.call(this, firstChild)) {
return this.nextAll(firstChild, callback);
}
return firstChild;
}
}
return null;
},
// get the last LI in tree order (with filtering)
// `node` must be valid LI DOM node
// `callback` can return FALSE to skip a node or NULL to prevent drill down/skip the node
// can return NULL
last: function(node, callback) {
var container = this.container(node);
if (container) {
var lastChild = container.lastChild;
if (lastChild) {
if (callback && (callback.call(this, lastChild) === null)) {
return this.prevAll(lastChild, callback);
} else {
var drillDown;
while (drillDown = this.lastChild(lastChild)) {
lastChild = drillDown;
}
if (callback && !callback.call(this, lastChild)) {
return this.prevAll(lastChild, callback);
}
return lastChild;
}
}
}
return null;
},
// get the children LI from the node
// `node` must be valid LI DOM node
// `drillDown` if TRUE all children are returned
// `callback` can return FALSE to skip a node or NULL to prevent drill down/skip the node
children: function(node, drillDown, callback) {
var children = [], levels = [], match, next, skip;
var firstChild = this.firstChild(node);
if (firstChild) {
while (true) {
skip = false;
do {
if (callback) {
match = callback.call(this, firstChild);
if (match) {
children.push(firstChild);
}
if (drillDown && (match !== null)) {
next = this.firstChild(firstChild);
if (next) {
levels.push(firstChild);
firstChild = next;
skip = true;
break;
}
}
} else {
children.push(firstChild);
if (drillDown) {
next = this.firstChild(firstChild);
if (next) {
levels.push(firstChild);
firstChild = next;
skip = true;
break;
}
}
}
} while (firstChild = firstChild.nextSibling);
if (!skip) {
while (firstChild = levels.pop()) {
firstChild = firstChild.nextSibling;
if (firstChild) {
break;
}
}
if (!firstChild) {
break;
}
}
}
}
return children;
},
// get a children from the node
// `node` must be valid DOM node
// `callback` can return FALSE to skip a node or NULL to stop the search
// can return NULL
childrenTill: function(node, callback) {
var levels = [], match, next, skip;
var firstChild = node.firstChild;
if (firstChild) {
while (true) {
skip = false;
do {
match = callback.call(this, firstChild);
if (match) {
return firstChild;
} else if (match === null) {
return null;
}
next = firstChild.firstChild;
if (next) {
levels.push(firstChild);
firstChild = next;
skip = true;
break;
}
} while (firstChild = firstChild.nextSibling);
if (!skip) {
while (firstChild = levels.pop()) {
firstChild = firstChild.nextSibling;
if (firstChild) {
break;
}
}
if (!firstChild) {
break;
}
}
}
}
return null;
},
// get a children from the node having a class
// `node` must be valid DOM node
// `className` String or Array to check for
// can return NULL
childrenByClass: function(node, className) {
if (node.getElementsByClassName) {
var list = node.getElementsByClassName(className instanceof Array ? className.join(' ') : className);
return list ? list[0] : null;
} else {
return this.childrenTill(node, function(node) {
return this.hasClass(node, className);
});
}
},
// get the parent LI from the children LI
// `node` must be valid LI DOM node
// can return NULL
parent: function(node) {
var parent = node.parentNode.parentNode;
if (parent && (parent.nodeName == 'LI')) {
return parent;
}
return null;
},
// get the parent LI from any children
// `node` must be valid children of a LI DOM node
// can return NULL
parentFrom: function(node) {
while (node.nodeName != 'LI') {
node = node.parentNode;
if (!node) {
return null;
}
}
return node;
},
// get a parent from the node
// `node` must be valid DOM node
// `callback` can return FALSE to skip a node or NULL to stop the search
// can return NULL
parentTill: function(node, callback) {
var match;
while (node = node.parentNode) {
match = callback.call(this, node);
if (match) {
return node;
} else if (match === null) {
return null;
}
}
return null;
},
// get a parent from the node having a class
// `node` must be valid DOM node
// `className` String or Array to check for
// can return NULL
parentByClass: function(node, className) {
return this.parentTill(node, function(node) {
return this.hasClass(node, className);
});
},
// test if node has class(es)
// `className` String or Array to check for
// `withOut` String or Array to exclude with
hasClass: function(node, className, withOut) {
var oldClass = ' ' + node.className + ' ';
if (withOut instanceof Array) {
for (var i = 0; i < withOut.length; i++) {
if (oldClass.indexOf(' ' + withOut[i] + ' ') != -1) {
return false;
}
}
} else {
if (withOut && oldClass.indexOf(' ' + withOut + ' ') != -1) {
return false;
}
}
if (className instanceof Array) {
for (var i = 0; i < className.length; i++) {
if (oldClass.indexOf(' ' + className[i] + ' ') == -1) {
return false;
}
}
} else {
if (className && oldClass.indexOf(' ' + className + ' ') == -1) {
return false;
}
}
return true;
},
// filter nodes with class(es)
// `nodes` Array of DOM nodes
// @see `hasClass`
withClass: function(nodes, className, withOut) {
var filter = [];
for (var i = 0; i < nodes.length; i++) {
if (this.hasClass(nodes[i], className, withOut)) {
filter.push(nodes[i]);
}
}
return filter;
},
// test if node has any class(es)
// `className` String or Array to check for (any class)
// `withOut` String or Array to exclude with
hasAnyClass: function(node, className, withOut) {
var oldClass = ' ' + node.className + ' ';
if (withOut instanceof Array) {
for (var i = 0; i < withOut.length; i++) {
if (oldClass.indexOf(' ' + withOut[i] + ' ') != -1) {
return false;
}
}
} else {
if (withOut && oldClass.indexOf(' ' + withOut + ' ') != -1) {
return false;
}
}
if (className instanceof Array) {
for (var i = 0; i < className.length; i++) {
if (oldClass.indexOf(' ' + className[i] + ' ') != -1) {
return true;
}
}
} else {
if (className && oldClass.indexOf(' ' + className + ' ') != -1) {
return true;
}
}
return false;
},
// filter nodes with any class(es)
// `nodes` Array of DOM nodes
// @see `hasAnyClass`
withAnyClass: function(nodes, className, withOut) {
var filter = [];
for (var i = 0; i < nodes.length; i++) {
if (this.hasAnyClass(nodes[i], className, withOut)) {
filter.push(nodes[i]);
}
}
return filter;
},
// add class(es) to node
// `node` must be valid DOM node
// `className` String or Array to add
// return TRUE if className changed
addClass: function(node, className) {
var oldClass = ' ' + node.className + ' ', append = '';
if (className instanceof Array) {
for (var i = 0; i < className.length; i++) {
if (oldClass.indexOf(' ' + className[i] + ' ') == -1) {
append += ' ' + className[i];
}
}
} else {
if (oldClass.indexOf(' ' + className + ' ') == -1) {
append += ' ' + className;
}
}
if (append) {
node.className = node.className + append;
return true;
}
return false;
},
// add class(es) to nodes
// `nodes` Array of DOM nodes
// @see `addClass`
addListClass: function(nodes, className, callback) {
for (var i = 0; i < nodes.length; i++) {
this.addClass(nodes[i], className);
if (callback) {
callback.call(this, nodes[i]);
}
}
},
// remove class(es) from node
// `node` must be valid DOM node
// `className` String or Array to remove
// return TRUE if className changed
removeClass: function(node, className) {
var oldClass = ' ' + node.className + ' ';
if (className instanceof Array) {
for (var i = 0; i < className.length; i++) {
oldClass = oldClass.replace(' ' + className[i] + ' ', ' ');
}
} else {
oldClass = oldClass.replace(' ' + className + ' ', ' ');
}
oldClass = oldClass.substr(1, oldClass.length - 2);
if (node.className != oldClass) {
node.className = oldClass;
return true;
}
return false;
},
// remove class(es) from nodes
// `nodes` Array of DOM nodes
// @see `removeClass`
removeListClass: function(nodes, className, callback) {
for (var i = 0; i < nodes.length; i++) {
this.removeClass(nodes[i], className);
if (callback) {
callback.call(this, nodes[i]);
}
}
},
// toggle node class(es)
// `node` must be valid DOM node
// `className` String or Array to toggle
// `add` TRUE to add them
// return TRUE if className changed
toggleClass: function(node, className, add) {
if (add) {
return this.addClass(node, className);
} else {
return this.removeClass(node, className);
}
},
// toggle nodes class(es)
// `nodes` Array of DOM nodes
// @see `toggleClass`
toggleListClass: function(nodes, className, add, callback) {
for (var i = 0; i < nodes.length; i++) {
this.toggleClass(nodes[i], className, add);
if (callback) {
callback.call(this, nodes[i]);
}
}
},
// add/remove and keep old class(es)
// `node` must be valid DOM node
// `addClass` String or Array to add
// `removeClass` String or Array to remove
// return TRUE if className changed
addRemoveClass: function(node, addClass, removeClass) {
var oldClass = ' ' + node.className + ' ';
if (removeClass) {
if (removeClass instanceof Array) {
for (var i = 0; i < removeClass.length; i++) {
oldClass = oldClass.replace(' ' + removeClass[i] + ' ', ' ');
}
} else {
oldClass = oldClass.replace(' ' + removeClass + ' ', ' ');
}
}
if (addClass) {
var append = '';
if (addClass instanceof Array) {
for (var i = 0; i < addClass.length; i++) {
if (oldClass.indexOf(' ' + addClass[i] + ' ') == -1) {
append += addClass[i] + ' ';
}
}
} else {
if (oldClass.indexOf(' ' + addClass + ' ') == -1) {
append += addClass + ' ';
}
}
oldClass += append;
}
oldClass = oldClass.substr(1, oldClass.length - 2);
if (node.className != oldClass) {
node.className = oldClass;
return true;
}
return false;
},
// add/remove and keep old class(es)
// `nodes` Array of DOM nodes
// @see `addRemoveClass`
addRemoveListClass: function(nodes, addClass, removeClass, callback) {
for (var i = 0; i < nodes.length; i++) {
this.addRemoveClass(nodes[i], addClass, removeClass);
if (callback) {
callback.call(this, nodes[i]);
}
}
}
};

View File

@ -0,0 +1,294 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds inplace edit support to aciTree,
* should be used with the selectable extension.
*/
(function($, window, undefined) {
// extra default options
var options = {
editable: false, // if TRUE then each item will be inplace editable
editDelay: 250 // how many [ms] to wait (with mouse down) before starting the edit (on mouse release)
};
// aciTree editable extension
// add inplace item editing by pressing F2 key or mouse click (to enter edit mode)
// press enter/escape to save/cancel the text edit
var aciTree_editable = {
__extend: function() {
// add extra data
$.extend(this._private, {
editTimestamp: null
});
// call the parent
this._super();
},
// init editable
_editableInit: function() {
this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
switch (eventName) {
case 'blurred':
// support `selectable` extension
var item = api.edited();
if (item.length) {
// cancel edit/save the changes
api.endEdit();
}
break;
case 'deselected':
// support `selectable` extension
if (api.isEdited(item)) {
// cancel edit/save the changes
api.endEdit();
}
break;
}
}).bind('click' + this._private.nameSpace, this.proxy(function() {
// click on the tree
var item = this.edited();
if (item.length) {
// cancel edit/save the changes
this.endEdit();
}
})).bind('keydown' + this._private.nameSpace, this.proxy(function(e) {
switch (e.which) {
case 113: // F2
// support `selectable` extension
if (this.extSelectable && this.extSelectable()) {
var item = this.focused();
if (item.length && !this.isEdited(item) && this.isEnabled(item)) {
// enable edit on F2 key
this.edit(item);
// prevent default F2 key function
e.preventDefault();
}
}
break;
}
})).on('mousedown' + this._private.nameSpace, '.aciTreeItem', this.proxy(function(e) {
if ($(e.target).is('.aciTreeItem,.aciTreeText')) {
this._private.editTimestamp = $.now();
}
})).on('mouseup' + this._private.nameSpace, '.aciTreeItem', this.proxy(function(e) {
if ($(e.target).is('.aciTreeItem,.aciTreeText')) {
var passed = $.now() - this._private.editTimestamp;
// start edit only after N [ms] but before N * 4 [ms] have passed
if ((passed > this._instance.options.editDelay) && (passed < this._instance.options.editDelay * 4)) {
var item = this.itemFrom(e.target);
if ((!this.extSelectable || !this.extSelectable() || (this.isFocused(item) && (this.selected().length == 1))) && this.isEnabled(item)) {
// edit on mouseup
this.edit(item);
}
}
}
})).on('keydown' + this._private.nameSpace, 'input[type=text]', this.proxy(function(e) {
// key handling
switch (e.which) {
case 13: // enter
this.itemFrom(e.target).focus();
this.endEdit();
e.stopPropagation();
break;
case 27: // escape
this.itemFrom(e.target).focus();
this.endEdit({
save: false
});
e.stopPropagation();
// prevent default action on ESC
e.preventDefault();
break;
case 38: // up
case 40: // down
case 37: // left
case 39: // right
case 33: // pgup
case 34: // pgdown
case 36: // home
case 35: // end
case 32: // space
case 107: // numpad [+]
case 109: // numpad [-]
case 106: // numpad [*]
e.stopPropagation();
break;
}
})).on('blur' + this._private.nameSpace, 'input[type=text]', this.proxy(function() {
if (!this.extSelectable || !this.extSelectable()) {
// cancel edit/save the changes
this.endEdit();
}
})).on('click' + this._private.nameSpace + ' dblclick' + this._private.nameSpace, 'input[type=text]', function(e) {
e.stopPropagation();
});
},
// override `_initHook`
_initHook: function() {
if (this.extEditable()) {
this._editableInit();
}
// call the parent
this._super();
},
// low level DOM functions
_editableDOM: {
// add edit field
add: function(item) {
var line = item.addClass('aciTreeEdited').children('.aciTreeLine');
line.find('.aciTreeText').html('<input id="aciTree-editable-tree-item" type="text" value="" style="-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;-o-user-select:text;user-select:text" />');
line.find('label').attr('for', 'aciTree-editable-tree-item');
this._editableDOM.get(item).val(this.getLabel(item));
},
// remove edit field
remove: function(item, label) {
var line = item.removeClass('aciTreeEdited').children('.aciTreeLine');
line.find('.aciTreeText').html(this.getLabel(item));
line.find('label').removeAttr('for');
},
// return edit field
get: function(item) {
return item ? item.children('.aciTreeLine').find('input[type=text]') : $([]);
}
},
// get edited item
edited: function() {
return this._instance.jQuery.find('.aciTreeEdited');
},
// test if item is edited
isEdited: function(item) {
return item && domApi.hasClass(item[0], 'aciTreeEdited');
},
// set focus to the input
_focusEdit: function(item) {
var field = this._editableDOM.get(item).focus().trigger('click')[0];
if (field) {
if (typeof field.selectionStart == 'number') {
field.selectionStart = field.selectionEnd = field.value.length;
} else if (field.createTextRange !== undefined) {
var range = field.createTextRange();
range.collapse(false);
range.select();
}
}
},
// override `setLabel`
setLabel: function(item, options) {
if (!this.extEditable() || !this.isEdited(item)) {
// call the parent
this._super(item, options);
}
},
// edit item inplace
edit: function(item, options) {
options = this._options(options, 'edit', 'editfail', 'wasedit', item);
if (this.extEditable() && this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeedit', options)) {
this._fail(item, options);
return;
}
var edited = this.edited();
if (edited.length) {
if (edited[0] == item[0]) {
this._notify(item, options);
return;
} else {
this._editableDOM.remove.call(this, edited);
this._trigger(edited, 'endedit', options);
}
}
this._editableDOM.add.call(this, item);
this._focusEdit(item);
this._success(item, options);
} else {
this._fail(item, options);
}
},
// end edit
// `options.save` when set to FALSE will not save the changes
endEdit: function(options) {
var item = this.edited();
options = this._options(options, 'edited', 'endeditfail', 'endedit', item);
if (this.extEditable() && this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeendedit', options)) {
this._fail(item, options);
return;
}
var text = this._editableDOM.get(item).val();
this._editableDOM.remove.call(this, item);
if ((options.save === undefined) || options.save) {
this.setLabel(item, {
label: text
});
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
this._fail(item, options);
}
},
// test if editable is enabled
extEditable: function() {
return this._instance.options.editable;
},
// override set `option`
option: function(option, value) {
if (this.wasInit() && !this.isLocked()) {
if ((option == 'editable') && (value != this.extEditable())) {
if (value) {
this._editableInit();
} else {
this._editableDone();
}
}
}
// call the parent
this._super(option, value);
},
// done editable
_editableDone: function() {
this._instance.jQuery.unbind(this._private.nameSpace);
this._instance.jQuery.off(this._private.nameSpace, '.aciTreeItem');
this._instance.jQuery.off(this._private.nameSpace, 'input[type=text]');
var edited = this.edited();
if (edited.length) {
this.endEdit();
}
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._editableDone();
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the editable stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_editable, 'aciTreeEditable');
// add extra default options
aciPluginClass.defaults('aciTree', options);
// for internal access
var domApi = aciPluginClass.plugins.aciTree_dom;
})(jQuery, this);

View File

@ -0,0 +1,170 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds hash/fragment support using aciFragment, it opens/select item(s) based on variables stored in the fragment part of the URL.
* The states are loaded from the URL fragment and set on treeview init. Multiple item IDs separated with ";" are supported for
* opening/selecting deep items (if loading nodes is required).
* Require aciFragment https://github.com/dragosu/jquery-aciFragment and the utils extension for finding items by ID.
*/
(function($, window, undefined) {
// extra default options
var options = {
selectHash: null, // hash key name to select a item (item path IDs as key value, multiple item IDs separated with a ";")
openHash: null // hash key name to open item(s) (item path IDs as key value, multiple item IDs separated with a ";")
};
// aciTree hash extension
// select/open items based on IDs stored in the fragment of the current URL
var aciTree_hash = {
__extend: function() {
$.extend(this._private, {
lastSelect: null,
lastOpen: null,
// store `aciFragment` api
hashApi: null
});
// call the parent
this._super();
},
// init hash
_hashInit: function() {
// init `aciFragment`
this._instance.jQuery.aciFragment();
this._private.hashApi = this._instance.jQuery.aciFragment('api');
this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
switch (eventName) {
case 'init':
api._hashRestore();
break;
}
}).bind('acifragment' + this._private.nameSpace, this.proxy(function(event, api, anchorChanged) {
event.stopPropagation();
this._hashRestore();
}));
},
// override `_initHook`
_initHook: function() {
if (this.extHast()) {
this._hashInit();
}
// call the parent
this._super();
},
// restore item states from hash
_hashRestore: function() {
var queue = this._instance.queue;
var process = function(opened) {
// open all hash items
for (var i in opened) {
(function(id) {
// add item to queue
queue.push(function(complete) {
this.search(null, {
success: function(item) {
this.open(item, {
uid: 'ui.hash',
success: complete,
fail: complete
});
},
fail: complete,
search: id
});
});
})(opened[i]);
}
};
if (this._instance.options.openHash) {
var hash = this._private.hashApi.get(this._instance.options.openHash, '');
if (hash.length && (hash != this._private.lastOpen)) {
this._private.lastOpen = hash;
var opened = hash.split(';');
process(opened);
}
}
// support `selectable` extension
if (this._instance.options.selectHash && this.extSelectable && this.extSelectable()) {
var hash = this._private.hashApi.get(this._instance.options.selectHash, '');
if (hash.length && (hash != this._private.lastSelect)) {
this._private.lastSelect = hash;
var opened = hash.split(';');
var selected = opened.pop();
process(opened);
if (selected) {
// select item
queue.push(function(complete) {
this.search(null, {
success: function(item) {
this.select(item, {
uid: 'ui.hash',
success: function(item) {
this.setVisible(item, {
center: true
});
complete();
},
fail: complete
});
},
fail: complete,
search: selected
});
});
}
}
}
},
// test if hash is enabled
extHast: function() {
return this._instance.options.selectHash || this._instance.options.openHash;
},
// override set option
option: function(option, value) {
var hash = this.extHast();
// call the parent
this._super(option, value);
if (this.extHast() != hash) {
if (hash) {
this._hashDone();
} else {
this._hashInit();
}
}
},
// done hash
_hashDone: function() {
this._instance.jQuery.unbind(this._private.nameSpace);
this._private.hashApi = null;
this._instance.jQuery.aciFragment('destroy');
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._hashDone();
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the hash stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_hash, 'aciTreeHash');
// add extra default options
aciPluginClass.defaults('aciTree', options);
})(jQuery, this);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,290 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds save/restore support for item states (open/selected) using local storage.
* The states are saved on item select/open and restored on treeview init.
* Require jStorage https://github.com/andris9/jStorage and the utils extension for finding items by ID.
*/
(function($, window, undefined) {
// extra default options
var options = {
persist: null // the storage key name to keep the states (should be unique/treeview)
};
// aciTree persist extension
// save/restore item state in/from local storage
var aciTree_persist = {
__extend: function() {
$.extend(this._private, {
// timeouts for the save operation
selectTimeout: null,
focusTimeout: null,
openTimeout: null
});
// call the parent
this._super();
},
// init persist
_initPersist: function() {
this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
if (options.uid == 'ui.persist') {
// skip processing itself
return;
}
switch (eventName) {
case 'init':
api._persistRestore();
break;
case 'selected':
case 'deselected':
// support `selectable` extension
api._persistLater('selected');
break;
case 'focus':
case 'blur':
// support `selectable` extension
api._persistLater('focused');
break;
case 'opened':
case 'closed':
api._persistLater('opened');
break;
}
});
},
// override `_initHook`
_initHook: function() {
if (this.extPersist()) {
this._initPersist();
}
// call the parent
this._super();
},
// persist states
_persistLater: function(type) {
switch (type) {
case 'selected':
window.clearTimeout(this._private.selectTimeout);
this._private.selectTimeout = window.setTimeout(this.proxy(function() {
this._persistSelected();
}), 250);
break;
case 'focused':
window.clearTimeout(this._private.focusTimeout);
this._private.focusTimeout = window.setTimeout(this.proxy(function() {
this._persistFocused();
}), 250);
break;
case 'opened':
window.clearTimeout(this._private.openTimeout);
this._private.openTimeout = window.setTimeout(this.proxy(function() {
this._persistOpened();
}), 250);
break;
}
},
// restore item states
_persistRestore: function() {
var queue = new this._queue(this, this._instance.options.queue);
var task = new this._task(queue, function(complete) {
// support `selectable` extension
if (this.extSelectable && this.extSelectable()) {
var selected = $.jStorage.get('aciTree_' + this._instance.options.persist + '_selected');
if (selected instanceof Array) {
// select all saved items
for (var i in selected) {
(function(path) {
queue.push(function(complete) {
this.searchPath(null, {
success: function(item) {
this.select(item, {
uid: 'ui.persist',
success: function() {
complete();
},
fail: complete,
focus: false
});
},
fail: complete,
path: path.split(';')
});
});
})(selected[i]);
if (!this._instance.options.multiSelectable) {
break;
}
}
}
var focused = $.jStorage.get('aciTree_' + this._instance.options.persist + '_focused');
if (focused instanceof Array) {
// focus all saved items
for (var i in focused) {
(function(path) {
queue.push(function(complete) {
this.searchPath(null, {
success: function(item) {
this.focus(item, {
uid: 'ui.persist',
success: function(item) {
this.setVisible(item, {
center: true
});
complete();
},
fail: complete
});
},
fail: complete,
path: path.split(';')
});
});
})(focused[i]);
}
}
}
complete();
});
var opened = $.jStorage.get('aciTree_' + this._instance.options.persist + '_opened');
if (opened instanceof Array) {
// open all saved items
for (var i in opened) {
(function(path) {
// add item to queue
task.push(function(complete) {
this.searchPath(null, {
success: function(item) {
this.open(item, {
uid: 'ui.persist',
success: complete,
fail: complete
});
},
fail: complete,
path: path.split(';'),
load: true
});
});
})(opened[i]);
}
}
},
// persist selected items
_persistSelected: function() {
// support `selectable` extension
if (this.extSelectable && this.extSelectable()) {
var selected = [];
this.selected().each(this.proxy(function(element) {
var item = $(element);
var path = this.pathId(item);
path.push(this.getId(item));
selected.push(path.join(';'));
}, true));
$.jStorage.set('aciTree_' + this._instance.options.persist + '_selected', selected);
}
},
// persist focused item
_persistFocused: function() {
// support `selectable` extension
if (this.extSelectable && this.extSelectable()) {
var focused = [];
this.focused().each(this.proxy(function(element) {
var item = $(element);
var path = this.pathId(item);
path.push(this.getId(item));
focused.push(path.join(';'));
}, true));
$.jStorage.set('aciTree_' + this._instance.options.persist + '_focused', focused);
}
},
// persist opened items
_persistOpened: function() {
var opened = [];
this.inodes(this.children(null, true), true).each(this.proxy(function(element) {
var item = $(element);
if (this.isOpenPath(item)) {
var path = this.pathId(item);
path.push(this.getId(item));
opened.push(path.join(';'));
}
}, true));
$.jStorage.set('aciTree_' + this._instance.options.persist + '_opened', opened);
},
// test if there is any saved data
isPersist: function() {
if (this.extPersist()) {
var selected = $.jStorage.get('aciTree_' + this._instance.options.persist + '_selected');
if (selected instanceof Array) {
return true;
}
var focused = $.jStorage.get('aciTree_' + this._instance.options.persist + '_focused');
if (focused instanceof Array) {
return true;
}
var opened = $.jStorage.get('aciTree_' + this._instance.options.persist + '_opened');
if (opened instanceof Array) {
return true;
}
}
return false;
},
// remove any saved states
unpersist: function() {
if (this.extPersist()) {
$.jStorage.deleteKey('aciTree_' + this._instance.options.persist + '_selected');
$.jStorage.deleteKey('aciTree_' + this._instance.options.persist + '_focused');
$.jStorage.deleteKey('aciTree_' + this._instance.options.persist + '_opened');
}
},
// test if persist is enabled
extPersist: function() {
return this._instance.options.persist;
},
// override set `option`
option: function(option, value) {
var persist = this.extPersist();
// call the parent
this._super(option, value);
if (this.extPersist() != persist) {
if (persist) {
this._donePersist();
} else {
this._initPersist();
}
}
},
// done persist
_donePersist: function() {
this._instance.jQuery.unbind(this._private.nameSpace);
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._donePersist();
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the persist stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_persist, 'aciTreePersist');
// add extra default options
aciPluginClass.defaults('aciTree', options);
})(jQuery, this);

View File

@ -0,0 +1,471 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds radio-button support to aciTree,
* should be used with the selectable extension.
*
* The are a few extra properties for the item data:
*
* {
* ...
* radio: true, // TRUE (default) means the item will have a radio button (can be omitted if the `checkbox` extension is not used)
* checked: false, // if should be checked or not
* ...
* }
*
*/
(function($, window, undefined) {
// extra default options
var options = {
radio: false, // if TRUE then each item will have a radio button
radioChain: true, // if TRUE the selection will propagate to the parents/children
radioBreak: true, // if TRUE then a missing radio button will break the chaining
radioClick: false // if TRUE then a click will trigger a state change only when made over the radio-button itself
};
// aciTree radio extension
var aciTree_radio = {
// init radio
_radioInit: function() {
this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
switch (eventName) {
case 'loaded':
if (item) {
// check/update on item load
api._radioLoad(item);
}
break;
}
}).bind('keydown' + this._private.nameSpace, this.proxy(function(e) {
switch (e.which) {
case 32: // space
// support `selectable` extension
if (this.extSelectable && this.extSelectable() && !e.ctrlKey) {
var item = this.focused();
if (this.hasRadio(item) && this.isEnabled(item)) {
if (!this.isChecked(item)) {
this.check(item);
}
e.stopImmediatePropagation();
// prevent page scroll
e.preventDefault();
}
}
break;
}
})).on('click' + this._private.nameSpace, '.aciTreeItem', this.proxy(function(e) {
if (!this._instance.options.radioClick || $(e.target).is('.aciTreeCheck')) {
var item = this.itemFrom(e.target);
if (this.hasRadio(item) && this.isEnabled(item) && (!this.extSelectable || !this.extSelectable() || (!e.ctrlKey && !e.shiftKey))) {
// change state on click
if (!this.isChecked(item)) {
this.check(item);
}
e.preventDefault();
}
}
}));
},
// override `_initHook`
_initHook: function() {
if (this.extRadio()) {
this._radioInit();
}
// call the parent
this._super();
},
// override `_itemHook`
_itemHook: function(parent, item, itemData, level) {
if (this.extRadio()) {
// support `checkbox` extension
var checkbox = this.extCheckbox && this.hasCheckbox(item);
if (!checkbox && (itemData.radio || ((itemData.radio === undefined) && (!this.extCheckbox || !this.extCheckbox())))) {
this._radioDOM.add(item, itemData);
}
}
// call the parent
this._super(parent, item, itemData, level);
},
// low level DOM functions
_radioDOM: {
// add item radio
add: function(item, itemData) {
domApi.addClass(item[0], itemData.checked ? ['aciTreeRadio', 'aciTreeChecked'] : 'aciTreeRadio');
var text = domApi.childrenByClass(item[0].firstChild, 'aciTreeText');
var parent = text.parentNode;
var label = window.document.createElement('LABEL');
var check = window.document.createElement('SPAN');
check.className = 'aciTreeCheck';
label.appendChild(check);
label.appendChild(text);
parent.appendChild(label);
item[0].firstChild.setAttribute('aria-checked', !!itemData.checked);
},
// remove item radio
remove: function(item) {
domApi.removeClass(item[0], ['aciTreeRadio', 'aciTreeChecked']);
var text = domApi.childrenByClass(item[0].firstChild, 'aciTreeText');
var label = text.parentNode;
var parent = label.parentNode;
parent.replaceChild(text, label)
item[0].firstChild.removeAttribute('aria-checked');
},
// (un)check items
check: function(items, state) {
domApi.toggleListClass(items.toArray(), 'aciTreeChecked', state, function(node) {
node.firstChild.setAttribute('aria-checked', state);
});
}
},
// update item on load
_radioLoad: function(item) {
if (!this._instance.options.radioChain) {
// do not update on load
return;
}
if (this.hasRadio(item)) {
if (this.isChecked(item)) {
if (!this.radios(this.children(item, false, true), true).length) {
// the item is checked but no children are, check the children
this._radioUpdate(item, true);
}
} else {
// the item is not checked, uncheck children
this._radioUpdate(item);
}
}
},
// get children list
_radioChildren: function(item) {
if (this._instance.options.radioBreak) {
var list = [];
var process = this.proxy(function(item) {
var children = this.children(item, false, true);
children.each(this.proxy(function(element) {
var item = $(element);
// break on missing radio
if (this.hasRadio(item)) {
list.push(element);
process(item);
}
}, true));
});
process(item);
return $(list);
} else {
var children = this.children(item, true, true);
return this.radios(children);
}
},
// get children across items
_radioLevel: function(items) {
var list = [];
items.each(this.proxy(function(element) {
var item = $(element);
var children = this.children(item, false, true);
children.each(this.proxy(function(element) {
var item = $(element);
if (!this._instance.options.radioBreak || this.hasRadio(item)) {
list.push(element);
}
}, true));
}, true));
return $(list);
},
// update radio state
_radioUpdate: function(item, state) {
// update siblings
var siblings = this.proxy(function(item) {
var siblings = this.siblings(item, true);
this._radioDOM.check(this.radios(siblings), false);
siblings.each(this.proxy(function(element) {
var item = $(element);
if (!this._instance.options.radioBreak || this.hasRadio(item)) {
this._radioDOM.check(this._radioChildren(item), false);
}
}, true));
});
if (state) {
siblings(item);
}
// update children
var checkDown = this.proxy(function(item) {
var children = this._radioLevel(item);
var radios = this.radios(children);
if (radios.length) {
var checked = this.radios(children, true);
if (checked.length) {
checked = checked.first();
this._radioDOM.check(checked, true);
siblings(checked);
checkDown(checked);
} else {
checked = radios.first();
this._radioDOM.check(checked, true);
siblings(checked);
checkDown(checked);
}
} else if (children.length) {
checkDown(children);
}
});
if (state) {
checkDown(item);
} else {
this._radioDOM.check(this._radioChildren(item), false);
}
// update parents
var checkUp = this.proxy(function(item) {
var parent = this.parent(item);
if (parent.length) {
if (this.hasRadio(parent)) {
if (state) {
siblings(parent);
}
this._radioDOM.check(parent, state);
checkUp(parent);
} else {
if (!this._instance.options.radioBreak) {
if (state) {
siblings(parent);
}
checkUp(parent);
}
}
}
});
if (state !== undefined) {
checkUp(item);
}
},
// test if item have a radio
hasRadio: function(item) {
return item && domApi.hasClass(item[0], 'aciTreeRadio');
},
// add radio button
addRadio: function(item, options) {
options = this._options(options, 'radioadded', 'addradiofail', 'wasradio', item);
if (this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeaddradio', options)) {
this._fail(item, options);
return;
}
if (this.hasRadio(item)) {
this._notify(item, options);
} else {
var process = function() {
this._radioDOM.add(item, {
});
this._success(item, options);
};
// support `checkbox` extension
if (this.extCheckbox && this.hasCheckbox(item)) {
// remove checkbox first
this.removeCheckbox(item, this._inner(options, {
success: process,
fail: options.fail
}));
} else {
process.apply(this);
}
}
} else {
this._fail(item, options);
}
},
// remove radio button
removeRadio: function(item, options) {
options = this._options(options, 'radioremoved', 'removeradiofail', 'notradio', item);
if (this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeremoveradio', options)) {
this._fail(item, options);
return;
}
if (this.hasRadio(item)) {
this._radioDOM.remove(item);
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
this._fail(item, options);
}
},
// test if it's checked
isChecked: function(item) {
if (this.hasRadio(item)) {
return domApi.hasClass(item[0], 'aciTreeChecked');
}
// support `checkbox` extension
if (this._super) {
// call the parent
return this._super(item);
}
return false;
},
// check radio button
check: function(item, options) {
if (this.extRadio && this.hasRadio(item)) {
options = this._options(options, 'checked', 'checkfail', 'waschecked', item);
// a way to cancel the operation
if (!this._trigger(item, 'beforecheck', options)) {
this._fail(item, options);
return;
}
if (this.isChecked(item)) {
this._notify(item, options);
} else {
this._radioDOM.check(item, true);
if (this._instance.options.radioChain) {
// chain them
this._radioUpdate(item, true);
}
this._success(item, options);
}
} else {
// support `checkbox` extension
if (this._super) {
// call the parent
this._super(item, options);
} else {
this._trigger(item, 'checkfail', options);
this._fail(item, options);
}
}
},
// uncheck radio button
uncheck: function(item, options) {
if (this.extRadio && this.hasRadio(item)) {
options = this._options(options, 'unchecked', 'uncheckfail', 'notchecked', item);
// a way to cancel the operation
if (!this._trigger(item, 'beforeuncheck', options)) {
this._fail(item, options);
return;
}
if (this.isChecked(item)) {
this._radioDOM.check(item, false);
if (this._instance.options.radioChain) {
// chain them
this._radioUpdate(item, false);
}
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
// support `checkbox` extension
if (this._super) {
// call the parent
this._super(item, options);
} else {
this._trigger(item, 'uncheckfail', options);
this._fail(item, options);
}
}
},
// filter items with radio by state (if set)
radios: function(items, state) {
if (state !== undefined) {
return $(domApi.withClass(items.toArray(), state ? ['aciTreeRadio', 'aciTreeChecked'] : 'aciTreeRadio', state ? null : 'aciTreeChecked'));
}
return $(domApi.withClass(items.toArray(), 'aciTreeRadio'));
},
// override `_serialize`
_serialize: function(item, callback) {
var data = this._super(item, callback);
if (data && this.extRadio()) {
if (data.hasOwnProperty('radio')) {
data.radio = this.hasRadio(item);
data.checked = this.isChecked(item);
} else if (this.hasRadio(item)) {
if (this.extCheckbox && this.extCheckbox()) {
data.radio = true;
}
data.checked = this.isChecked(item);
}
}
return data;
},
// override `serialize`
serialize: function(item, what, callback) {
if (what == 'radio') {
var serialized = '';
var children = this.children(item, true, true);
this.radios(children, true).each(this.proxy(function(element) {
var item = $(element);
if (callback) {
serialized += callback.call(this, item, what, this.getId(item));
} else {
serialized += this._instance.options.serialize.call(this, item, what, this.getId(item));
}
}, true));
return serialized;
}
return this._super(item, what, callback);
},
// test if radio is enabled
extRadio: function() {
return this._instance.options.radio;
},
// override set `option`
option: function(option, value) {
if (this.wasInit() && !this.isLocked()) {
if ((option == 'radio') && (value != this.extRadio())) {
if (value) {
this._radioInit();
} else {
this._radioDone();
}
}
}
// call the parent
this._super(option, value);
},
// done radio
_radioDone: function(destroy) {
this._instance.jQuery.unbind(this._private.nameSpace);
this._instance.jQuery.off(this._private.nameSpace, '.aciTreeItem');
if (!destroy) {
// remove radios
this.radios(this.children(null, true, true)).each(this.proxy(function(element) {
this.removeRadio($(element));
}, true));
}
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._radioDone(true);
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the radio stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_radio, 'aciTreeRadio');
// add extra default options
aciPluginClass.defaults('aciTree', options);
// for internal access
var domApi = aciPluginClass.plugins.aciTree_dom;
})(jQuery, this);

View File

@ -0,0 +1,739 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds item selection/keyboard navigation to aciTree and need to
* be always included if you care about accessibility.
*
* There is an extra property for the item data:
*
* {
* ...
* selected: false, // TRUE means the item will be selected
* ...
* }
*
*/
(function($, window, undefined) {
// extra default options
var options = {
selectable: true, // if TRUE then one item can be selected (and the tree navigation with the keyboard will be enabled)
multiSelectable: false, // if TRUE then multiple items can be selected at a time
// the 'tabIndex' attribute need to be >= 0 set on the tree container (by default will be set to 0)
fullRow: false, // if TRUE then the selection will be made on the entire row (the CSS should reflect this)
textSelection: false // if FALSE then the item text can't be selected
};
// aciTree selectable extension
// adds item selection & keyboard navigation (left/right, up/down, pageup/pagedown, home/end, space, enter, escape)
// dblclick also toggles the item
var aciTree_selectable = {
__extend: function() {
// add extra data
$.extend(this._instance, {
focus: false
});
$.extend(this._private, {
blurTimeout: null,
spinPoint: null // the selected item to operate against when using the shift key with selection
});
// call the parent
this._super();
},
// test if has focus
hasFocus: function() {
return this._instance.focus;
},
// init selectable
_selectableInit: function() {
if (this._instance.jQuery.attr('tabindex') === undefined) {
// ensure the tree can get focus
this._instance.jQuery.attr('tabindex', 0);
}
if (!this._instance.options.textSelection) {
// disable text selection
this._selectable(false);
}
this._instance.jQuery.bind('acitree' + this._private.nameSpace, function(event, api, item, eventName, options) {
switch (eventName) {
case 'closed':
var focused = api.focused();
if (api.isChildren(item, focused)) {
// move focus to parent on close
api._focusOne(item);
}
// deselect children on parent close
api.children(item, true).each(api.proxy(function(element) {
var item = $(element);
if (this.isSelected(item)) {
this.deselect(item);
}
}, true));
break;
}
}).bind('focusin' + this._private.nameSpace, this.proxy(function() {
// handle tree focus
window.clearTimeout(this._private.blurTimeout);
if (!this.hasFocus()) {
this._instance.focus = true;
domApi.addClass(this._instance.jQuery[0], 'aciTreeFocus');
this._trigger(null, 'focused');
}
})).bind('focusout' + this._private.nameSpace, this.proxy(function() {
// handle tree focus
window.clearTimeout(this._private.blurTimeout);
this._private.blurTimeout = window.setTimeout(this.proxy(function() {
if (this.hasFocus()) {
this._instance.focus = false;
domApi.removeClass(this._instance.jQuery[0], 'aciTreeFocus');
this._trigger(null, 'blurred');
}
}), 10);
})).bind('keydown' + this._private.nameSpace, this.proxy(function(e) {
if (!this.hasFocus()) {
// do not handle if we do not have focus
return;
}
var focused = this.focused();
if (focused.length && this.isBusy(focused)) {
// skip when busy
return false;
}
var item = $([]);
switch (e.which) {
case 65: // aA
if (this._instance.options.multiSelectable && e.ctrlKey) {
// select all visible items
var select = this.visible(this.enabled(this.children(null, true))).not(this.selected());
select.each(this.proxy(function(element) {
this.select($(element), {
focus: false
});
}, true));
if (!this.focused().length) {
// ensure one item has focus
this._focusOne(this.visible(select, true).first());
}
// prevent default action
e.preventDefault();
}
break;
case 38: // up
item = focused.length ? this._prev(focused) : this.first();
break;
case 40: // down
item = focused.length ? this._next(focused) : this.first();
break;
case 37: // left
if (focused.length) {
if (this.isOpen(focused)) {
item = focused;
// close the item
this.close(focused, {
collapse: this._instance.options.collapse,
expand: this._instance.options.expand,
unique: this._instance.options.unique
});
} else {
item = this.parent(focused);
}
} else {
item = this._first();
}
break;
case 39: // right
if (focused.length) {
if (this.isInode(focused) && this.isClosed(focused)) {
item = focused;
// open the item
this.open(focused, {
collapse: this._instance.options.collapse,
expand: this._instance.options.expand,
unique: this._instance.options.unique
});
} else {
item = this.first(focused);
}
} else {
item = this._first();
}
break;
case 33: // pgup
item = focused.length ? this._prevPage(focused) : this._first();
break;
case 34: // pgdown
item = focused.length ? this._nextPage(focused) : this._first();
break;
case 36: // home
item = this._first();
break;
case 35: // end
item = this._last();
break;
case 13: // enter
case 107: // numpad [+]
item = focused;
if (this.isInode(focused) && this.isClosed(focused)) {
// open the item
this.open(focused, {
collapse: this._instance.options.collapse,
expand: this._instance.options.expand,
unique: this._instance.options.unique
});
}
break;
case 27: // escape
case 109: // numpad [-]
item = focused;
if (this.isOpen(focused)) {
// close the item
this.close(focused, {
collapse: this._instance.options.collapse,
expand: this._instance.options.expand,
unique: this._instance.options.unique
});
}
if (e.which == 27) {
// prevent default action on ESC
e.preventDefault();
}
break;
case 32: // space
item = focused;
if (this.isInode(focused) && !e.ctrlKey) {
// toggle the item
this.toggle(focused, {
collapse: this._instance.options.collapse,
expand: this._instance.options.expand,
unique: this._instance.options.unique
});
}
// prevent page scroll
e.preventDefault();
break;
case 106: // numpad [*]
item = focused;
if (this.isInode(focused)) {
// open all children
this.open(focused, {
collapse: this._instance.options.collapse,
expand: true,
unique: this._instance.options.unique
});
}
break;
}
if (item.length) {
if (this._instance.options.multiSelectable && !e.ctrlKey && !e.shiftKey) {
// unselect others
this._unselect(this.selected().not(item));
}
if (!this.isVisible(item)) {
// bring it into view
this.setVisible(item);
}
if (e.ctrlKey) {
if ((e.which == 32) && this.isEnabled(item)) { // space
if (this.isSelected(item)) {
this.deselect(item);
} else {
this.select(item);
}
// remember for later
this._private.spinPoint = item;
} else {
this._focusOne(item);
}
} else if (e.shiftKey) {
this._shiftSelect(item);
} else {
if (!this.isSelected(item) && this.isEnabled(item)) {
this.select(item);
} else {
this._focusOne(item);
}
// remember for later
this._private.spinPoint = item;
}
return false;
}
}));
this._fullRow(this._instance.options.fullRow);
this._multiSelectable(this._instance.options.multiSelectable);
},
// change full row mode
_fullRow: function(state) {
this._instance.jQuery.off(this._private.nameSpace, '.aciTreeLine,.aciTreeItem').off(this._private.nameSpace, '.aciTreeItem');
this._instance.jQuery.on('mousedown' + this._private.nameSpace + ' click' + this._private.nameSpace, state ? '.aciTreeLine,.aciTreeItem' : '.aciTreeItem', this.proxy(function(e) {
var item = this.itemFrom(e.target);
if (!this.isVisible(item)) {
this.setVisible(item);
}
if (e.ctrlKey) {
if (e.type == 'click') {
if (this.isEnabled(item)) {
// (de)select item
if (this.isSelected(item)) {
this.deselect(item);
this._focusOne(item);
} else {
this.select(item);
}
} else {
this._focusOne(item);
}
}
} else if (this._instance.options.multiSelectable && e.shiftKey) {
this._shiftSelect(item);
} else {
if (this._instance.options.multiSelectable && (!this.isSelected(item) || (e.type == 'click'))) {
// deselect all other (keep the old focus)
this._unselect(this.selected().not(item));
}
this._selectOne(item);
}
if (!e.shiftKey) {
this._private.spinPoint = item;
}
})).on('dblclick' + this._private.nameSpace, state ? '.aciTreeLine,.aciTreeItem' : '.aciTreeItem', this.proxy(function(e) {
var item = this.itemFrom(e.target);
if (this.isInode(item)) {
// toggle the item
this.toggle(item, {
collapse: this._instance.options.collapse,
expand: this._instance.options.expand,
unique: this._instance.options.unique
});
return false;
}
}));
if (state) {
domApi.addClass(this._instance.jQuery[0], 'aciTreeFullRow');
} else {
domApi.removeClass(this._instance.jQuery[0], 'aciTreeFullRow');
}
},
// change selection mode
_multiSelectable: function(state) {
if (state) {
this._instance.jQuery.attr('aria-multiselectable', true);
} else {
var focused = this.focused();
this._unselect(this.selected().not(focused));
this._instance.jQuery.removeAttr('aria-multiselectable');
}
},
// process `shift` key selection
_shiftSelect: function(item) {
var spinPoint = this._private.spinPoint;
if (!spinPoint || !$.contains(this._instance.jQuery[0], spinPoint[0]) || !this.isOpenPath(spinPoint)) {
spinPoint = this.focused();
}
if (spinPoint.length) {
// select a range of items
var select = [item[0]], start = spinPoint[0], found = false, stop = item[0];
var visible = this.visible(this.children(null, true));
visible.each(this.proxy(function(element) {
// find what items to select
if (found) {
if (this.isEnabled($(element))) {
select.push(element);
}
if ((element == start) || (element == stop)) {
return false;
}
} else if ((element == start) || (element == stop)) {
if (this.isEnabled($(element))) {
select.push(element);
}
if ((element == start) && (element == stop)) {
return false;
}
found = true;
}
}, true));
this._unselect(this.selected().not(select));
// select the items
$(select).not(item).each(this.proxy(function(element) {
var item = $(element);
if (!this.isSelected(item)) {
// select item (keep the old focus)
this.select(item, {
focus: false
});
}
}, true));
}
this._selectOne(item);
},
// override `_initHook`
_initHook: function() {
if (this.extSelectable()) {
this._selectableInit();
}
// call the parent
this._super();
},
// override `_itemHook`
_itemHook: function(parent, item, itemData, level) {
if (this.extSelectable() && itemData.selected) {
this._selectableDOM.select(item, true);
}
// call the parent
this._super(parent, item, itemData, level);
},
// low level DOM functions
_selectableDOM: {
// (de)select one or more items
select: function(items, state) {
if (state) {
domApi.addListClass(items.toArray(), 'aciTreeSelected', function(node) {
node.firstChild.setAttribute('aria-selected', true);
});
} else {
domApi.removeListClass(items.toArray(), 'aciTreeSelected', function(node) {
node.firstChild.setAttribute('aria-selected', false);
});
}
},
// focus one item, unfocus one or more items
focus: function(items, state) {
if (state) {
domApi.addClass(items[0], 'aciTreeFocus');
items[0].firstChild.focus();
} else {
domApi.removeListClass(items.toArray(), 'aciTreeFocus');
}
}
},
// make element (un)selectable
_selectable: function(state) {
if (state) {
this._instance.jQuery.css({
'-webkit-user-select': 'text',
'-moz-user-select': 'text',
'-ms-user-select': 'text',
'-o-user-select': 'text',
'user-select': 'text'
}).attr({
'unselectable': null,
'onselectstart': null
}).unbind('selectstart' + this._private.nameSpace);
} else {
this._instance.jQuery.css({
'-webkit-user-select': 'none',
'-moz-user-select': '-moz-none',
'-ms-user-select': 'none',
'-o-user-select': 'none',
'user-select': 'none'
}).attr({
'unselectable': 'on',
'onselectstart': 'return false'
}).bind('selectstart' + this._private.nameSpace, function(e) {
if (!$(e.target).is('input,textarea')) {
return false;
}
});
}
},
// get first visible item
_first: function() {
return $(domApi.first(this._instance.jQuery[0], function(node) {
return this.hasClass(node, 'aciTreeVisible') ? true : null;
}));
},
// get last visible item
_last: function() {
return $(domApi.last(this._instance.jQuery[0], function(node) {
return this.hasClass(node, 'aciTreeVisible') ? true : null;
}));
},
// get previous visible starting with item
_prev: function(item) {
return $(domApi.prevAll(item[0], function(node) {
return this.hasClass(node, 'aciTreeVisible') ? true : null;
}));
},
// get next visible starting with item
_next: function(item) {
return $(domApi.nextAll(item[0], function(node) {
return this.hasClass(node, 'aciTreeVisible') ? true : null;
}));
},
// get previous page starting with item
_prevPage: function(item) {
var space = this._instance.jQuery.height();
var now = item[0].firstChild.offsetHeight;
var prev = item, last = $();
while (now < space) {
prev = this._prev(prev);
if (prev[0]) {
now += prev[0].firstChild.offsetHeight;
last = prev;
} else {
break;
}
}
return last;
},
// get next page starting with item
_nextPage: function(item) {
var space = this._instance.jQuery.height();
var now = item[0].firstChild.offsetHeight;
var next = item, last = $();
while (now < space) {
next = this._next(next);
if (next[0]) {
now += next[0].firstChild.offsetHeight;
last = next;
} else {
break;
}
}
return last;
},
// select one item
_selectOne: function(item) {
if (this.isSelected(item)) {
this._focusOne(item);
} else {
if (this.isEnabled(item)) {
// select the item
this.select(item);
} else {
this._focusOne(item);
}
}
},
// unselect the items
_unselect: function(items) {
items.each(this.proxy(function(element) {
this.deselect($(element));
}, true));
},
// focus one item
_focusOne: function(item) {
if (!this._instance.options.multiSelectable) {
this._unselect(this.selected().not(item));
}
if (!this.isFocused(item)) {
this.focus(item);
}
},
// select item
// `options.focus` when set to FALSE will not set the focus
// `options.oldSelected` will keep the old selected items
select: function(item, options) {
options = this._options(options, 'selected', 'selectfail', 'wasselected', item);
if (this.extSelectable() && this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeselect', options)) {
this._fail(item, options);
return;
}
// keep the old ones
options.oldSelected = this.selected();
if (!this._instance.options.multiSelectable) {
// deselect all other
var unselect = options.oldSelected.not(item);
this._selectableDOM.select(unselect, false);
unselect.each(this.proxy(function(element) {
this._trigger($(element), 'deselected', options);
}, true));
}
if (this.isSelected(item)) {
this._notify(item, options);
} else {
this._selectableDOM.select(item, true);
this._success(item, options);
}
// process focus
if ((options.focus === undefined) || options.focus) {
if (!this.isFocused(item) || options.focus) {
this.focus(item, this._inner(options));
}
}
} else {
this._fail(item, options);
}
},
// deselect item
deselect: function(item, options) {
options = this._options(options, 'deselected', 'deselectfail', 'notselected', item);
if (this.extSelectable() && this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforedeselect', options)) {
this._fail(item, options);
return;
}
if (this.isSelected(item)) {
this._selectableDOM.select(item, false);
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
this._fail(item, options);
}
},
// set `virtual` focus
// `options.oldFocused` will keep the old focused item
focus: function(item, options) {
options = this._options(options, 'focus', 'focusfail', 'wasfocused', item);
if (this.extSelectable() && this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforefocus', options)) {
this._fail(item, options);
return;
}
// keep the old ones
options.oldFocused = this.focused();
// blur all other
var unfocus = options.oldFocused.not(item);
this._selectableDOM.focus(unfocus, false);
// unfocus all others
unfocus.each(this.proxy(function(element) {
this._trigger($(element), 'blur', options);
}, true));
if (this.isFocused(item)) {
this._notify(item, options);
} else {
this._selectableDOM.focus(item, true);
this._success(item, options);
}
} else {
this._fail(item, options);
}
},
// remove `virtual` focus
blur: function(item, options) {
options = this._options(options, 'blur', 'blurfail', 'notfocused', item);
if (this.extSelectable() && this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforeblur', options)) {
this._fail(item, options);
return;
}
if (this.isFocused(item)) {
this._selectableDOM.focus(item, false);
this._success(item, options);
} else {
this._notify(item, options);
}
} else {
this._fail(item, options);
}
},
// get selected items
selected: function() {
return this._instance.jQuery.find('.aciTreeSelected');
},
// override `_serialize`
_serialize: function(item, callback) {
// call the parent
var data = this._super(item, callback);
if (data && this.extSelectable()) {
if (data.hasOwnProperty('selected')) {
data.selected = this.isSelected(item);
} else if (this.isSelected(item)) {
data.selected = true;
}
}
return data;
},
// test if item is selected
isSelected: function(item) {
return item && domApi.hasClass(item[0], 'aciTreeSelected');
},
// return the focused item
focused: function() {
return this._instance.jQuery.find('.aciTreeFocus');
},
// test if item is focused
isFocused: function(item) {
return item && domApi.hasClass(item[0], 'aciTreeFocus');
},
// test if selectable is enabled
extSelectable: function() {
return this._instance.options.selectable;
},
// override set `option`
option: function(option, value) {
if (this.wasInit() && !this.isLocked()) {
if ((option == 'selectable') && (value != this.extSelectable())) {
if (value) {
this._selectableInit();
} else {
this._selectableDone();
}
}
if ((option == 'multiSelectable') && (value != this._instance.options.multiSelectable)) {
this._multiSelectable(value);
}
if ((option == 'fullRow') && (value != this._instance.options.fullRow)) {
this._fullRow(value);
}
if ((option == 'textSelection') && (value != this._instance.options.textSelection)) {
this._selectable(value);
}
}
// call the parent
this._super(option, value);
},
// done selectable
_selectableDone: function(destroy) {
if (this._instance.jQuery.attr('tabindex') == 0) {
this._instance.jQuery.removeAttr('tabindex');
}
if (!this._instance.options.textSelection) {
this._selectable(true);
}
this._instance.jQuery.unbind(this._private.nameSpace);
this._instance.jQuery.off(this._private.nameSpace, '.aciTreeLine,.aciTreeItem').off(this._private.nameSpace, '.aciTreeItem');
domApi.removeClass(this._instance.jQuery[0], ['aciTreeFocus', 'aciTreeFullRow']);
this._instance.jQuery.removeAttr('aria-multiselectable');
this._instance.focus = false;
this._private.spinPoint = null;
if (!destroy) {
// remove selection
this._unselect(this.selected());
var focused = this.focused();
if (focused.length) {
this.blur(focused);
}
}
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._selectableDone(true);
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the selectable stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_selectable, 'aciTreeSelectable');
// add extra default options
aciPluginClass.defaults('aciTree', options);
// for internal access
var domApi = aciPluginClass.plugins.aciTree_dom;
})(jQuery, this);

View File

@ -0,0 +1,339 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* This extension adds the possibility to sort the tree items.
* Require aciSortable https://github.com/dragosu/jquery-aciSortable and the utils extension for reordering items.
*/
(function($, window, undefined) {
// extra default options
var options = {
sortable: false, // if TRUE then the tree items can be sorted
sortDelay: 750, // how many [ms] before opening a inode on hovering when in drag
// called by the `aciSortable` inside the `drag` callback
sortDrag: function(item, placeholder, isValid, helper) {
if (!isValid) {
var move = this.getLabel(item);
if (this._private.dragDrop && (this._private.dragDrop.length > 1)) {
move += ' and #' + (this._private.dragDrop.length - 1) + ' more';
}
helper.html(move);
}
},
// called by the `aciSortable` inside the `valid` callback
sortValid: function(item, hover, before, isContainer, placeholder, helper) {
var move = this.getLabel(item);
if (this._private.dragDrop.length > 1) {
move += ' and #' + (this._private.dragDrop.length - 1) + ' more';
}
if (isContainer) {
helper.html('move ' + move + ' to ' + this.getLabel(this.itemFrom(hover)));
placeholder.removeClass('aciTreeAfter aciTreeBefore');
} else if (before !== null) {
if (before) {
helper.html('move ' + move + ' before ' + this.getLabel(hover));
placeholder.removeClass('aciTreeAfter').addClass('aciTreeBefore');
} else {
helper.html('move ' + move + ' after ' + this.getLabel(hover));
placeholder.removeClass('aciTreeBefore').addClass('aciTreeAfter');
}
}
}
};
// aciTree sortable extension
var aciTree_sortable = {
__extend: function() {
// add extra data
$.extend(this._private, {
openTimeout: null,
dragDrop: null // the items used in drag & drop
});
// call the parent
this._super();
},
// init sortable
_sortableInit: function() {
this._instance.jQuery.aciSortable({
container: '.aciTreeUl',
item: '.aciTreeLi',
child: 50,
childHolder: '<ul class="aciTreeUl aciTreeChild"></ul>',
childHolderSelector: '.aciTreeChild',
placeholder: '<li class="aciTreeLi aciTreePlaceholder"><div></div></li>',
placeholderSelector: '.aciTreePlaceholder',
helper: '<div class="aciTreeHelper"></div>',
helperSelector: '.aciTreeHelper',
// just before drag start
before: this.proxy(function(item) {
// init before drag
if (!this._initDrag(item)) {
return false;
}
// a way to cancel the operation
if (!this._trigger(item, 'beforedrag')) {
this._trigger(item, 'dragfail');
return false;
}
return true;
}),
// just after drag start, before dragging
start: this.proxy(function(item, placeholder, helper) {
this._instance.jQuery.addClass('aciTreeDragDrop');
helper.stop(true).css('opacity', 1);
}),
// when in drag
drag: this.proxy(function(item, placeholder, isValid, helper) {
if (!isValid) {
window.clearTimeout(this._private.openTimeout);
}
if (this._instance.options.sortDrag) {
this._instance.options.sortDrag.apply(this, arguments);
}
}),
// to check the drop target (when the placeholder is repositioned)
valid: this.proxy(function(item, hover, before, isContainer, placeholder, helper) {
window.clearTimeout(this._private.openTimeout);
if (!this._checkDrop(item, hover, before, isContainer, placeholder, helper)) {
return false;
}
var options = this._options({
hover: hover,
before: before,
isContainer: isContainer,
placeholder: placeholder,
helper: helper
});
// a way to cancel the operation
if (!this._trigger(item, 'checkdrop', options)) {
return false;
}
if (this.isInode(hover) && !this.isOpen(hover)) {
this._private.openTimeout = window.setTimeout(this.proxy(function() {
this.open(hover);
}), this._instance.options.sortDelay);
}
if (this._instance.options.sortValid) {
this._instance.options.sortValid.apply(this, arguments);
}
return true;
}),
// when dragged as child
create: this.proxy(function(api, item, hover) {
if (this.isLeaf(hover)) {
hover.append(api._instance.options.childHolder);
return true;
}
return false;
}, true),
// on drag end
end: this.proxy(function(item, hover, placeholder, helper) {
window.clearTimeout(this._private.openTimeout);
var options = {
placeholder: placeholder,
helper: helper
};
options = this._options(options, 'sorted', 'dropfail', null, item);
if (placeholder.parent().length) {
var prev = this.prev(placeholder, true);
if (prev.length) {
// add after a item
placeholder.detach();
var items = $(this._private.dragDrop.get().reverse());
this._private.dragDrop = null;
items.each(this.proxy(function(element) {
this.moveAfter($(element), this._inner(options, {
success: options.success,
fail: options.fail,
after: prev
}));
}, true));
} else {
var next = this.next(placeholder, true);
if (next.length) {
// add before a item
placeholder.detach();
var items = $(this._private.dragDrop.get().reverse());
this._private.dragDrop = null;
items.each(this.proxy(function(element) {
this.moveBefore($(element), this._inner(options, {
success: options.success,
fail: options.fail,
before: next
}));
}, true));
} else {
// add as a child
var parent = this.parent(placeholder);
var container = placeholder.parent();
placeholder.detach();
container.remove();
if (this.isLeaf(parent)) {
// we can set asChild only for leaves
var items = this._private.dragDrop;
this.asChild(items.eq(0), this._inner(options, {
success: function() {
this._success(item, options);
this.open(parent);
items.filter(':gt(0)').each(this.proxy(function(element) {
this.moveAfter($(element), this._inner(options, {
success: options.success,
fail: options.fail,
after: this.last(parent)
}));
}, true));
},
fail: options.fail,
parent: parent
}));
} else {
this._fail(item, options);
}
}
}
} else {
this._fail(item, options);
}
this._private.dragDrop = null;
if (helper.parent().length) {
// the helper is inserted in the DOM
var top = $(window).scrollTop();
var left = $(window).scrollLeft();
var rect = item[0].getBoundingClientRect();
// animate helper to item position
helper.animate({
top: rect.top + top,
left: rect.left + left,
opacity: 0
},
{
complete: function() {
// detach the helper when completed
helper.detach();
}
});
}
this._instance.jQuery.removeClass('aciTreeDragDrop');
})
});
},
// override `_initHook`
_initHook: function() {
if (this.extSortable()) {
this._sortableInit();
}
// call the parent
this._super();
},
// reduce items by removing the childrens
_parents: function(items) {
var len = items.length, a, b, remove = [];
for (var i = 0; i < len - 1; i++) {
a = items.eq(i);
for (var j = i + 1; j < len; j++) {
b = items.eq(j);
if (this.isChildren(a, b)) {
remove.push(items[j]);
} else if (this.isChildren(b, a)) {
remove.push(items[i]);
}
}
}
return items.not(remove);
},
// called before drag start
_initDrag: function(item) {
// support `selectable` extension
if (this.extSelectable && this.extSelectable()) {
if (!this.hasFocus()) {
this._instance.jQuery.focus();
}
if (!this.isEnabled(item)) {
return false;
}
var drag = this.selected();
if (drag.length) {
if (!this.isSelected(item)) {
return false;
}
} else {
drag = item;
}
this._private.dragDrop = this._parents(drag);
} else {
this._instance.jQuery.focus();
this._private.dragDrop = item;
}
return true;
},
// check the drop target
_checkDrop: function(item, hover, before, isContainer, placeholder, helper) {
var items = this._private.dragDrop;
if (!items) {
return false;
}
var test = this.itemFrom(hover);
if (items.is(test) || items.has(test[0]).length) {
return false;
}
if (!isContainer) {
test = before ? this.prev(hover) : this.next(hover);
if (items.is(test)) {
return false;
}
}
return true;
},
// test if sortable is enabled
extSortable: function() {
return this._instance.options.sortable;
},
// override set `option`
option: function(option, value) {
if (this.wasInit() && !this.isLocked()) {
if ((option == 'sortable') && (value != this.extSortable())) {
if (value) {
this._sortableInit();
} else {
this._sortableDone();
}
}
}
// call the parent
this._super(option, value);
},
// done sortable
_sortableDone: function() {
this._instance.jQuery.unbind(this._private.nameSpace);
this._instance.jQuery.aciSortable('destroy');
},
// override `_destroyHook`
_destroyHook: function(unloaded) {
if (unloaded) {
this._sortableDone();
}
// call the parent
this._super(unloaded);
}
};
// extend the base aciTree class and add the sortable stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_sortable, 'aciTreeSortable');
// add extra default options
aciPluginClass.defaults('aciTree', options);
})(jQuery, this);

View File

@ -0,0 +1,746 @@
/*
* aciTree jQuery Plugin v4.5.0-rc.7
* http://acoderinsights.ro
*
* Copyright (c) 2014 Dragos Ursu
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Require jQuery Library >= v1.9.0 http://jquery.com
* + aciPlugin >= v1.5.1 https://github.com/dragosu/jquery-aciPlugin
*/
/*
* A few utility functions for aciTree.
*/
(function($, window, undefined) {
// extra default options
var options = {
// called when items need to be filtered, for each tree item
// return TRUE/FALSE to include/exclude items on filtering
filterHook: function(item, search, regexp) {
return search.length ? regexp.test(window.String(this.getLabel(item))) : true;
}
};
// aciTree utils extension
// adds item update option, branch processing, moving items & item swapping, item search by ID
var aciTree_utils = {
__extend: function() {
// add extra data
$.extend(this._instance, {
filter: new this._queue(this, this._instance.options.queue)
});
// stop queue until needed
this._instance.filter.destroy();
// call the parent
this._super();
},
// call the `callback` function (item) for each children of item
// when `load` is TRUE will also try to load nodes
branch: function(item, callback, load) {
var queue = this._instance.queue;
var process = this.proxy(function(item, callback, next) {
var child = next ? this.next(item) : this.first(item);
if (child.length) {
if (this.isInode(child)) {
if (this.wasLoad(child)) {
queue.push(function(complete) {
callback.call(this, child);
process(child, callback);
process(child, callback, true);
complete();
});
} else if (load) {
// load the item first
this.ajaxLoad(child, {
success: function() {
callback.call(this, child);
process(child, callback);
process(child, callback, true);
},
fail: function() {
process(child, callback, true);
}
});
} else {
queue.push(function(complete) {
callback.call(this, child);
process(child, callback, true);
complete();
});
}
} else {
queue.push(function(complete) {
callback.call(this, child);
process(child, callback, true);
complete();
});
}
}
});
process(item, callback);
},
// swap two items (they can't be parent & children)
// `options.item1` & `options.item2` are the swapped items
swap: function(options) {
options = this._options(options, null, 'swapfail', null, null);
var item1 = options.item1;
var item2 = options.item2;
if (this.isItem(item1) && this.isItem(item2) && !this.isChildren(item1, item2) && !this.isChildren(item2, item1) && (item1[0] != item2[0])) {
// a way to cancel the operation
if (!this._trigger(null, 'beforeswap', options)) {
this._fail(null, options);
return;
}
var prev = this.prev(item1);
if (prev.length) {
if (item2[0] == prev[0]) {
item2.before(item1);
} else {
item1.insertAfter(item2);
item2.insertAfter(prev);
}
} else {
var next = this.next(item1);
if (next.length) {
if (item2[0] == next[0]) {
item2.after(item1);
} else {
item1.insertAfter(item2);
item2.insertBefore(next);
}
} else {
var parent = item1.parent();
item1.insertAfter(item2);
parent.append(item2);
}
}
// update item states
this._updateLevel(item1);
var parent = this.parent(item1);
this._setFirstLast(parent.length ? parent : null, item1);
this._updateHidden(item1);
this._updateLevel(item2);
parent = this.parent(item2);
this._setFirstLast(parent.length ? parent : null, item2);
this._updateHidden(item2);
this._setOddEven(item1.add(item2));
this._trigger(null, 'swapped', options);
this._success(null, options);
} else {
this._fail(null, options);
}
},
// update item level
_updateItemLevel: function(item, fromLevel, toLevel) {
domApi.addRemoveClass(item[0], 'aciTreeLevel' + toLevel, 'aciTreeLevel' + fromLevel);
var line = item[0].firstChild;
line.setAttribute('aria-level', toLevel + 1);
var entry = domApi.childrenByClass(line, 'aciTreeEntry');
if (fromLevel < toLevel) {
line = entry.parentNode;
var branch;
for (var i = fromLevel; i < toLevel; i++) {
branch = window.document.createElement('DIV');
line.appendChild(branch);
branch.className = 'aciTreeBranch aciTreeLevel' + i;
line = branch;
}
line.appendChild(entry);
} else {
var branch = entry;
for (var i = toLevel; i <= fromLevel; i++) {
branch = branch.parentNode;
}
branch.removeChild(branch.firstChild);
branch.appendChild(entry);
}
},
// update child level
_updateChildLevel: function(item, fromLevel, toLevel) {
this.children(item, false, true).each(this.proxy(function(element) {
var item = $(element);
this._updateItemLevel(item, fromLevel, toLevel);
if (this.isInode(item)) {
this._updateChildLevel(item, fromLevel + 1, toLevel + 1);
}
}, true));
},
// update item level
_updateLevel: function(item) {
var level = this.level(item);
var found = window.parseInt(item.attr('class').match(/aciTreeLevel[0-9]+/)[0].match(/[0-9]+/));
if (level != found) {
this._updateItemLevel(item, found, level);
this._updateChildLevel(item, found + 1, level + 1);
}
},
// move item up
moveUp: function(item, options) {
options = this._options(options);
options.index = window.Math.max(this.getIndex(item) - 1, 0);
this.setIndex(item, options);
},
// move item down
moveDown: function(item, options) {
options = this._options(options);
options.index = window.Math.min(this.getIndex(item) + 1, this.siblings(item).length);
this.setIndex(item, options);
},
// move item in first position
moveFirst: function(item, options) {
options = this._options(options);
options.index = 0;
this.setIndex(item, options);
},
// move item in last position
moveLast: function(item, options) {
options = this._options(options);
options.index = this.siblings(item).length;
this.setIndex(item, options);
},
// move item before another (they can't be parent & children)
// `options.before` is the element before which the item will be moved
moveBefore: function(item, options) {
options = this._options(options, null, 'movefail', 'wasbefore', item);
var before = options.before;
if (this.isItem(item) && this.isItem(before) && !this.isChildren(item, before) && (item[0] != before[0])) {
// a way to cancel the operation
if (!this._trigger(item, 'beforemove', options)) {
this._fail(item, options);
return;
}
if (this.prev(before, true)[0] == item[0]) {
this._notify(item, options);
} else {
var parent = this.parent(item);
var prev = this.prev(item, true);
if (!prev.length) {
prev = parent.length ? parent : this.first();
}
item.insertBefore(before);
if (parent.length && !this.hasChildren(parent, true)) {
this.setLeaf(parent);
}
this._updateLevel(item);
// update item states
this._setFirstLast(parent.length ? parent : null);
parent = this.parent(item);
this._setFirstLast(parent.length ? parent : null, item.add(before));
this._updateHidden(item);
this._setOddEven(item.add(before).add(prev));
this._trigger(item, 'moved', options);
this._success(item, options);
}
} else {
this._fail(item, options);
}
},
// move item after another (they can't be parent & children)
// `options.after` is the element after which the item will be moved
moveAfter: function(item, options) {
options = this._options(options, null, 'movefail', 'wasafter', item);
var after = options.after;
if (this.isItem(item) && this.isItem(after) && !this.isChildren(item, after) && (item[0] != after[0])) {
// a way to cancel the operation
if (!this._trigger(item, 'beforemove', options)) {
this._fail(item, options);
return;
}
if (this.next(after, true)[0] == item[0]) {
this._notify(item, options);
} else {
var parent = this.parent(item);
var prev = this.prev(item, true);
if (!prev.length) {
prev = parent.length ? parent : this.first();
}
item.insertAfter(after);
if (parent.length && !this.hasChildren(parent, true)) {
this.setLeaf(parent);
}
this._updateLevel(item);
this._setFirstLast(parent.length ? parent : null);
parent = this.parent(item);
this._setFirstLast(parent.length ? parent : null, item.add(after));
this._updateHidden(item);
this._setOddEven(item.add(after).add(prev));
this._trigger(item, 'moved', options);
this._success(item, options);
}
} else {
this._fail(item, options);
}
},
// move item to be a child of another (they can't be parent & children and the targeted parent item must be empty)
// `options.parent` is the parent element on which the item will be added
asChild: function(item, options) {
options = this._options(options, null, 'childfail', null, item);
var parent = options.parent;
if (this.isItem(item) && this.isItem(parent) && !this.isChildren(item, parent) && !this.hasChildren(parent, true) && (item[0] != parent[0])) {
// a way to cancel the operation
if (!this._trigger(item, 'beforechild', options)) {
this._fail(item, options);
return;
}
var process = function() {
var oldParent = this.parent(item);
var prev = this.prev(item);
if (!prev.length) {
prev = oldParent.length ? oldParent : this.first();
}
var container = this._createContainer(parent);
container.append(item);
if (oldParent.length && !this.hasChildren(oldParent, true)) {
// no more children
this.setLeaf(oldParent);
}
// update item states
this._updateLevel(item);
this._setFirstLast(oldParent.length ? oldParent : null);
this._setFirstLast(parent.length ? parent : null, item);
this._updateHidden(item);
this._setOddEven(item.add(prev));
this._trigger(item, 'childset', options);
this._success(item, options);
};
if (this.isInode(parent)) {
process.apply(this);
} else {
// set as inode first
this.setInode(parent, this._inner(options, {
success: process,
fail: options.fail
}));
}
} else {
this._fail(item, options);
}
},
// search a `path` ID from a parent
_search: function(parent, pathId) {
var items = this.children(parent);
var item, id, length, found, exact = false;
for (var i = 0, size = items.length; i < size; i++) {
item = items.eq(i);
id = window.String(this.getId(item));
length = id.length;
if (length) {
if (id == pathId.substr(0, length)) {
found = item;
exact = pathId.length == length;
break;
}
}
}
if (found) {
if (!exact) {
// try to search children
var child = this._search(found, pathId);
if (child) {
return child;
}
}
return {
item: found,
exact: exact
};
} else {
return null;
}
},
// search items by ID
// `options.id` is the ID to search for
// if `path` is TRUE then the search will be more optimized
// and reduced to the first branch that matches the ID
// but the ID must be set like a path otherwise will not work
// if `load` is TRUE will also try to load nodes (works only when `path` is TRUE)
searchId: function(path, load, options) {
options = this._options(options);
var id = options.id;
if (path) {
if (load) {
var process = this.proxy(function(item) {
var found = this._search(item, id);
if (found) {
if (found.exact) {
this._success(found.item, options);
} else {
if (this.wasLoad(found.item)) {
this._fail(item, options);
} else {
// load the item
this.ajaxLoad(found.item, this._inner(options, {
success: function() {
process(found.item);
},
fail: options.fail
}));
}
}
} else {
this._fail(item, options);
}
});
process();
} else {
var found = this._search(null, id);
if (found && found.exact) {
this._success(found.item, options);
} else {
this._fail(null, options);
}
}
} else {
var found = $();
this._instance.jQuery.find('.aciTreeLi').each(this.proxy(function(element) {
if (id == this.getId($(element))) {
found = $(element);
return false;
}
}, true));
if (found.length) {
this._success(found, options);
} else {
this._fail(null, options);
}
}
},
// search nodes by ID or custom property starting from item
// `options.search` is the value to be searched
// `options.load` if TRUE will try to load nodes
// `options.callback` function (item, search) return TRUE for the custom match
// `options.results` will keep the search results
search: function(item, options) {
var results = [];
options = this._options(options);
var task = new this._task(new this._queue(this, this._instance.options.queue), function(complete) {
// run this at the end
if (results.length) {
options.results = $(results);
this._success($(results[0]), options);
} else {
this._fail(item, options);
}
complete();
});
var children = this.proxy(function(item) {
this.children(item, false, true).each(this.proxy(function(element) {
if (options.callback) {
// custom search
var match = options.callback.call(this, $(element), options.search);
if (match) {
results.push(element);
} else if (match === null) {
// skip childrens
return;
}
} else if (this.getId($(element)) == options.search) {
// default ID match
results.push(element);
}
if (this.isInode($(element))) {
// process children
task.push(function(complete) {
search($(element));
complete();
});
}
}, true));
});
var search = this.proxy(function(item) {
if (this.wasLoad(item)) {
// process children
task.push(function(complete) {
children(item);
complete();
});
} else if (options.load) {
task.push(function(complete) {
// load the item first
this.ajaxLoad(item, {
success: function() {
children(item);
complete();
},
fail: complete
});
});
}
});
// run the search
task.push(function(complete) {
search(item);
complete();
});
},
// search node by a list of IDs starting from item
// `options.path` is a list of IDs to be searched - the path to the node
// `options.load` if TRUE will try to load nodes
searchPath: function(item, options) {
options = this._options(options);
var path = options.path;
var search = this.proxy(function(item, id) {
this.search(item, {
success: function(item) {
if (path.length) {
search(item, path.shift());
} else {
this._success(item, options);
}
},
fail: function() {
this._fail(item, options);
},
search: id,
load: options.load,
callback: function(item, search) {
// prevent drill-down
return (this.getId(item) == search) ? true : null;
}
});
});
search(item, path.shift());
},
// get item path IDs starting from the top parent (ROOT)
// when `reverse` is TRUE returns the IDs in reverse order
pathId: function(item, reverse) {
var path = this.path(item, reverse), id = [];
path.each(this.proxy(function(element) {
id.push(this.getId($(element)));
}, true));
return id;
},
// escape string and return RegExp
_regexp: function(search) {
return new window.RegExp(window.String(search).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').replace(/\x08/g, '\\x08'), 'i');
},
// filter the tree items based on search criteria
// `options.search` is the keyword
// `options.first` will be the first matched item (if any)
filter: function(item, options) {
options = this._options(options, null, 'filterfail', null, item);
if (!item || this.isItem(item)) {
// a way to cancel the operation
if (!this._trigger(item, 'beforefilter', options)) {
this._fail(item, options);
return;
}
var search = window.String(options.search);
var regexp = this._regexp(search);
var first = null;
this._instance.filter.init();
var task = new this._task(this._instance.filter, function(complete) {
// run this at the end
this._instance.filter.destroy();
options.first = first;
this._setOddEven();
this._trigger(item, 'filtered', options);
this._success(item, options);
complete();
});
// process children
var process = this.proxy(function(parent) {
var children = this.children(parent, false, true);
var found = false;
children.each(this.proxy(function(element) {
var item = $(element);
if (this._instance.options.filterHook.call(this, item, search, regexp)) {
if (!first) {
first = item;
}
found = true;
domApi.removeClass(item[0], 'aciTreeHidden');
} else {
domApi.addRemoveClass(item[0], 'aciTreeHidden', 'aciTreeVisible');
}
if (this.isInode(item)) {
// continue with the children
task.push(function(complete) {
process(item);
complete();
});
}
}, true));
if (found) {
// update item states
if (parent && this.isHidden(parent)) {
this._showHidden(parent);
}
if (!parent || (this.isOpenPath(parent) && this.isOpen(parent))) {
children.not('.aciTreeHidden').addClass('aciTreeVisible');
}
this._setFirstLast(parent, this._getFirstLast(parent));
}
});
task.push(function(complete) {
process(item);
complete();
});
} else {
this._fail(item, options);
}
},
// call the `callback` function (item) for the first item
_firstAll: function(callback) {
callback.call(this, this.first());
},
// call the `callback` function (item) for the last item
// when `load` is TRUE will also try to load nodes
_lastAll: function(item, callback, load) {
if (item) {
if (this.isInode(item)) {
if (this.wasLoad(item)) {
this._lastAll(this.last(item), callback, load);
return;
} else if (load) {
this.ajaxLoad(item, {
success: function() {
this._lastAll(this.last(item), callback, load);
},
fail: function() {
callback.call(this, item);
}
});
return;
}
}
callback.call(this, item);
} else {
callback.call(this, this.last());
}
},
// call the `callback` function (item) for the next item from tree
// when `load` is TRUE will also try to load nodes
_nextAll: function(item, callback, load) {
if (item) {
if (this.isInode(item)) {
if (this.wasLoad(item)) {
callback.call(this, this.first(item));
return;
} else if (load) {
this.ajaxLoad(item, {
success: function() {
callback.call(this, this.first(item));
},
fail: function() {
this._nextAll(item, callback, load);
}
});
return;
}
}
var next = this.next(item);
if (next.length) {
callback.call(this, next);
} else {
// search next by parents
var search = this.proxy(function(item) {
var parent = this.parent(item);
if (parent.length) {
var next = this.next(parent);
if (next.length) {
return next;
} else {
return search(parent);
}
}
return null;
});
callback.call(this, search(item));
}
} else {
callback.call(this, this.first());
}
},
// call the `callback` function (item) for the previous item from tree
// when `load` is TRUE will also try to load nodes
_prevAll: function(item, callback, load) {
if (item) {
var prev = this.prev(item);
if (prev.length) {
if (this.isInode(prev)) {
this._lastAll(prev, callback, load);
} else {
callback.call(this, prev);
}
} else {
var parent = this.parent(item);
callback.call(this, parent.length ? parent : null);
}
} else {
callback.call(this, this.last());
}
},
// call the `callback` function (item) with the previous found item based on search criteria
// `search` is the keyword
prevMatch: function(item, search, callback) {
var regexp = this._regexp(search);
this._instance.filter.init();
var task = new this._task(this._instance.filter, function(complete) {
this._instance.filter.destroy();
complete();
});
var process = function(item) {
task.push(function(complete) {
this._prevAll(item, function(item) {
if (item) {
if (this._instance.options.filterHook.call(this, item, search, regexp)) {
callback.call(this, item);
} else {
process(item);
}
} else {
callback.call(this, null);
}
complete();
});
});
};
process(this.isItem(item) ? item : null);
},
// call the `callback` function (item) with the next found item based on search criteria
// `search` is the keyword
nextMatch: function(item, search, callback) {
var regexp = this._regexp(search);
this._instance.filter.init();
var task = new this._task(this._instance.filter, function(complete) {
this._instance.filter.destroy();
complete();
});
var process = function(item) {
task.push(function(complete) {
this._nextAll(item, function(item) {
if (item) {
if (this._instance.options.filterHook.call(this, item, search, regexp)) {
callback.call(this, item);
} else {
process(item);
}
} else {
callback.call(this, null);
}
complete();
});
});
};
process(this.isItem(item) ? item : null);
}
};
// extend the base aciTree class and add the utils stuff
aciPluginClass.plugins.aciTree = aciPluginClass.plugins.aciTree.extend(aciTree_utils, 'aciTreeUtils');
// add extra default options
aciPluginClass.defaults('aciTree', options);
// for internal access
var domApi = aciPluginClass.plugins.aciTree_dom;
})(jQuery, this);

View File

@ -24,3 +24,12 @@ iframe {
.CodeMirror, .CodeMirror-gutters {
height: 100% !important;
}
/* Padding for the treeview */
.browser-browser-pane {
padding-left: 0;
}
.icon-servers {
background: url('/static/servers.png') 0 0 no-repeat !important;
}