Changeset 70 for pro-bachkim-filespace
- Timestamp:
- Sep 9, 2014 4:14:10 PM (11 years ago)
- Location:
- pro-bachkim-filespace/sourcecode
- Files:
-
- 13 added
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
pro-bachkim-filespace/sourcecode/api.violet.vn/www/apps/platform/modules/space/actions/actions.class.php
r69 r70 77 77 } 78 78 rmdir($path); 79 } 80 81 private function CopySpaceDir ($dirObj, $destination, $userId) { 82 $dirId = $dirObj->id; 83 $dirName = $dirObj->name; 84 $aryChidlDirs = $dirObj->childs; 85 $aryChidlFiles = $dirObj->files; 86 87 $c = new Criteria(); 88 $c->add(TblspacecategoryPeer::CAT_ID, $dirId); 89 $c->add(TblspacecategoryPeer::CAT_USER, $userId); 90 $thisCat = TblspacecategoryPeer::doSelectOne($c); 91 var_dump($thisCat); 79 92 } 80 93 … … 218 231 } 219 232 233 public function executeCopy () { 234 $userId = $this->getRequestParameter('userid'); 235 $destination = $this->getRequestParameter('destination'); 236 $data = $this->getRequestParameter('data'); 237 238 $treeObj = json_decode($data); 239 240 $this->CopySpaceDir($treeObj, $destination, $userId); 241 242 } 243 220 244 public function executeUpload() { 221 245 //if (!$this->checkAuthentication()) return sfView::NONE; -
pro-bachkim-filespace/sourcecode/application/modules/ajax/controllers/privatecontent.php
r66 r70 85 85 $parentDir = $this->input->post('fparentid',TRUE); 86 86 $name = $this->input->post('fname',TRUE); 87 87 88 $xmlData = $this->vservices->actionExecute('mkdir',array('name' => $name, 'parent_id' => $parentDir == 0 ? -1:$parentDir)); 88 89 … … 90 91 $aryError = array('err' => $this->xml->status->_param['err'], 'errCode' => (int)$this->xml->status->_param['errCode']); 91 92 $aryData = array('id' => $this->xml->status->_param['id'], 'name' => $name, 'parentID' => $parentDir, 'ERROR' => $aryError); 93 94 /* $aryError = array('err' => '', 'errCode' => 0); 95 $aryData = array('id' => rand(100, 1000), 'name' => $name, 'parentID' => $parentDir, 'ERROR' => $aryError); */ 92 96 echo json_encode($aryData); 93 97 } … … 97 101 $xmlData = $this->vservices->actionExecute('deletemulti',array('delobj' => $delobj)); 98 102 $this->xml->parse($xmlData); 103 $aryFolders = array(); 104 105 if (isset($this->xml->tree->folder)) { 106 if (is_array($this->xml->tree->folder)) { 107 foreach ($this->xml->tree->folder as $key => $folder) { 108 $aryFolders['DIRECTORIES'][] = $folder->_param['id']; 109 } 110 } 111 else { 112 $aryFolders['DIRECTORIES'][] = $this->xml->tree->folder->_param['id']; 113 } 114 } 115 116 if (isset($this->xml->tree->file)) { 117 if (is_array($this->xml->tree->file)) { 118 foreach ($this->xml->tree->file as $key => $file) { 119 $aryFolders['FILES'][] = $file->_param['id']; 120 } 121 } 122 else { 123 $aryFolders['FILES'][] = $this->xml->tree->folder->_param['id']; 124 } 125 } 126 99 127 $aryError = array('err' => $this->xml->tree->_param['err'], 'errCode' => (int)$this->xml->tree->_param['errCode']); 100 $aryData = array(' ERROR' => $aryError);128 $aryData = array('DIRECTORIES' => isset($aryFolders['DIRECTORIES']) ? $aryFolders['DIRECTORIES'] : array(), 'FILES' => isset($aryFolders['FILES'])? $aryFolders['FILES'] : array() ,'ERROR' => $aryError); 101 129 echo json_encode($aryData); 102 130 } … … 104 132 public function rename() { 105 133 $id = $this->input->post('id',TRUE); 106 $newName = $this->input->post('newname',TRUE); 107 $objectType = $this->input->post('objtype',TRUE); 108 134 $data = $this->input->post('data',TRUE); 135 109 136 $aryData = array(); 110 137 $aryData['RESULT'] = $this->objDirectory->rename($id, $newName, $objectType);; 111 138 $aryData['UPDATED'] = array('id' => $id, 'name' => $newName, 'type' => $objectType); 112 139 echo json_encode($aryData); 140 } 141 142 public function copy () { 143 $destination = $this->input->post('destination',TRUE); 144 $data = $this->input->post('data',TRUE); 145 146 $aryData = array(); 147 $aryData['RESULT'] = json_decode($data); 148 $aryError = array('err' => '', 'errCode' => 0); 149 $aryData['ERROR'] = $aryError; 150 echo json_encode($aryData); 151 113 152 } 114 153 -
pro-bachkim-filespace/sourcecode/application/modules/filemanager/views/main.php
r42 r70 21 21 <div class="row"> 22 22 <div id="tools-bar" class="col-sm-12 col-xs-12 col-md-12 col-lg-12"> 23 <div class="btn-group ">23 <div class="btn-group basic"> 24 24 <button id="btnNewFolder" class="btn btn-success" title="Tạo thư mục má»i"><i class="icon-folder-close"></i></button> 25 25 <button id="btnDel" class="btn btn-success" title="Xóa"><i class="icon-eraser"></i></button> … … 28 28 <button id="btnPaste" class="btn btn-success" title="Dán"><i class="icon-paste"></i></button> 29 29 </div> 30 <div class="btn-group ">31 <button class="btn btn-danger" title="Chia sẻ"><i class="icon-share-alt"></i></button>32 <button class="btn btn-danger" title="Xem trưá»c"><i class="icon-eye-open"></i></button>30 <div class="btn-group social"> 31 <button id="btnShare" class="btn btn-danger" title="Chia sẻ"><i class="icon-share-alt"></i></button> 32 <button id="btnPreview" class="btn btn-danger" title="Xem trưá»c"><i class="icon-eye-open"></i></button> 33 33 </div> 34 <div class="btn-group ">35 <button class="btn btn-warning" title="Tải xuá»ng"><i class="icon-cloud-download"></i></button>34 <div class="btn-group creation"> 35 <button id="btnDownload" class="btn btn-warning" title="Tải xuá»ng"><i class="icon-cloud-download"></i></button> 36 36 <button id="btnUpload" class="btn btn-info" title="Tải lên"><i class="icon-cloud-upload"></i></button> 37 </div> 38 <div class="btn-group control"> 39 <button id="btnRefresh" class="btn btn-info" title="Tải lại"><i class="icon-refresh"></i></button> 37 40 </div> 38 41 </div> -
pro-bachkim-filespace/sourcecode/application/views/layout/contextmenu.php
r66 r70 1 < ul id="treeMenu" class="contextMenu">1 <!-- <ul id="treeMenu" class="contextMenu"> 2 2 <li class="newfolder"><a href="#newfolder">Tạo thư mục má»i</a></li> 3 3 <li class="share separator"><a href="#share">Chia sẻ</a></li> … … 36 36 <li class="delete"><a href="#delete">Xóa</a></li> 37 37 <li class="rename separator"><a href="#rename">Äá»i tên</a></li> 38 </ul> 38 </ul> --> -
pro-bachkim-filespace/sourcecode/application/views/layout/footer.php
r66 r70 26 26 27 27 <script src="assets/js/jquery/jquery-1.11.1.min.js"></script> 28 <!-- <script src="assets/js/jquery/jquery.hotkeys-0.7.9.min.js"></script> --> 28 29 <script src="assets/js/jquery/jquery-ui-1.10.4.min.js"></script> 30 <script src="assets/js/jquery/jquery.ui.position.js"></script> 31 <script src="assets/js/jquery/jquery.alsodrag.js"></script> 29 32 <script src="assets/js/jquery/jquery.contextMenu.js"></script> 30 33 <script src="assets/js/bootstrap/bootstrap.min.js"></script> … … 35 38 <script src="assets/js/manager.js"></script> 36 39 <script type="text/javascript"> 37 var data = {"DIRECTORIES":[{"id":"1","name":"Dir1","parentID":0},{"id":"2","name":" E1","parentID":0},{"id":"4","name":"Th\u01b0 m\u1ee5c c\u1ee7a D\u0169ng","parentID":0},{"id":"3","name":"E1.1","parentID":"2"},{"id":"5","name":"D\u0169ng 1","parentID":"4"},{"id":"8","name":"D\u0169ng 2","parentID":"4"},{"id":"6","name":"Dir 3","parentID":"5"},{"id":"7","name":"Dir3.1","parentID":"6"}],"FILES":[{"id":"1","name":"File 1","parentID":"1","minetype":"text"},{"id":"2","name":"File in root 1","parentID":0,"minetype":"text"}],"ERROR":{"err":"","errCode":""}}40 var data = {"DIRECTORIES":[{"id":"1","name":"Dir1","parentID":0},{"id":"2","name":"Dir2","parentID":0},{"id":"3","name":"Dir3","parentID":0},{"id":"4","name":"Dir1.1","parentID":"1"},{"id":"5","name":"Dir1.2","parentID":"1"},{"id":"15","name":"Dir1.3","parentID":"3"},{"id":"16","name":"Dir1.4","parentID":"15"},{"id":"17","name":"Dir1.5","parentID":"1"},{"id":"18","name":"Dir1.6","parentID":"1"},{"id":"19","name":"Dir1.7","parentID":"1"},{"id":"20","name":"Dir2.1","parentID":"2"}],"FILES":[{"id":"2","name":"File in root 1","parentID":0,"minetype":"text"}],"ERROR":{"err":"","errCode":0}} 38 41 if(jQuery){ 39 42 var manager = $().violetFileManager({ … … 45 48 statusbar: 'status-bar', 46 49 datasource: 'ajax'/* 'json' , 47 data: data */50 data: data */ 48 51 }); 49 52 } -
pro-bachkim-filespace/sourcecode/assets/css/jquery/jquery.contextMenu.css
r15 r70 13 13 */ 14 14 15 .context-menu-list {15 UL.context-menu-list { 16 16 margin:0; 17 17 padding:0; 18 18 19 min-width: 120px; 19 20 max-width: 250px; … … 35 36 } 36 37 37 .context-menu-item {38 LI.context-menu-item { 38 39 padding: 2px 2px 2px 24px; 39 40 background-color: #EEE; … … 45 46 } 46 47 47 .context-menu-separator {48 LI.context-menu-item.context-menu-separator { 48 49 padding-bottom:0; 49 50 border-bottom: 1px solid #DDD; … … 88 89 .context-menu-item.icon:before {} 89 90 */ 90 .context-menu-item.ico n{ min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; }91 .context-menu-item.ico n-edit { background-image: url(images/page_white_edit.png); }92 .context-menu-item.ico n-cut { background-image: url(images/cut.png); }93 .context-menu-item.ico n-copy { background-image: url(images/page_white_copy.png); }94 .context-menu-item.ico n-paste { background-image: url(images/page_white_paste.png); }95 .context-menu-item.ico n-delete { background-image: url(images/page_white_delete.png); }96 .context-menu-item.ico n-add { background-image: url(images/page_white_add.png); }97 .context-menu-item.ico n-quit { background-image: url(images/door.png); }91 .context-menu-item.ico { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; } 92 .context-menu-item.ico-edit { background-image: url(../../images/contextmenu/page_white_edit.png); } 93 .context-menu-item.ico-cut { background-image: url(../../images/contextmenu/cut.png); } 94 .context-menu-item.ico-copy { background-image: url(../../images/contextmenu/page_white_copy.png); } 95 .context-menu-item.ico-paste { background-image: url(../../images/contextmenu/page_white_paste.png); } 96 .context-menu-item.ico-delete { background-image: url(../../images/contextmenu/page_white_delete.png); } 97 .context-menu-item.ico-add { background-image: url(../../images/contextmenu/page_white_add.png); } 98 .context-menu-item.ico-quit { background-image: url(../../images/contextmenu/door.png); } 98 99 99 100 /* vertically align inside labels */ -
pro-bachkim-filespace/sourcecode/assets/css/space/grid.css
r66 r70 33 33 } 34 34 35 #file-container UL.vsgrid LI.vscell DIV[class^="icon-"].selected{35 #file-container UL.vsgrid LI.vscell.selected DIV[class^="icon-"] { 36 36 background-color: #5eaee5; 37 37 } 38 39 /* #file-container UL.vsgrid LI.vscell DIV[class^="icon-"].selected{ 40 background-color: #5eaee5; 41 } */ 38 42 39 43 #file-container UL.vsgrid LI.vscell DIV.icon-directory { … … 61 65 } 62 66 63 #file-container UL.vsgrid LI.vscell DIV.file-name.selected{67 #file-container UL.vsgrid LI.vscell.selected DIV.file-name { 64 68 background-color: #5eaee5; 65 69 } 70 71 #file-container UL.vsgrid LI.vscell.selected DIV.file-name INPUT.rename { 72 width:100%; 73 height: 18px; 74 border: 1px #5eaee5 solid; 75 padding: 0px; 76 text-overflow: initial; 77 font-family: Verdana, sans-serif; 78 font-size: 12px; 79 } -
pro-bachkim-filespace/sourcecode/assets/css/space/style.css
r66 r70 2 2 @import "../bootstrap/bootstrap.min.css"; 3 3 @import "../jquery/jquery-ui-1.10.4.min.css"; 4 @import "../jquery/jquery.context menu_new.css";4 @import "../jquery/jquery.contextMenu.css"; 5 5 @import "../font-awesome.min.css"; 6 6 @import "../ace.min.css"; 7 7 @import "vsgrid.css"; 8 8 @import "grid.css"; 9 @import "contextmenu.css";10 9 @import "../uploadfile.min.css"; 11 10 -
pro-bachkim-filespace/sourcecode/assets/js/grid.js
r66 r70 8 8 9 9 var oContainer = this; 10 var documentBoundEvent = false; 11 var posTopArray = []; 12 var posLeftArray = []; 13 var self = this; 10 14 11 15 var createNode = function (node) { … … 21 25 var inputID = $('<input />',{'class':'id', type:'hidden',value:node.item.id}); 22 26 var inputName = $('<input />',{'class':'name',type:'hidden',value:node.item.name}); 23 var inputType = $('<input />',{'class':'type',type:'hidden',value:type}); 27 var inputParent = $('<input />',{'class':'parentID',type:'hidden',value:node.item.parentID}); 28 var inputType = $('<input />',{'class':'type',type:'hidden',value:type}); 24 29 25 30 var a = $('<a></a>',{id: node.item.id,href: '#',rel: node.item.name,text: node.item.name}); 26 var divLink = $('<div></div>',{'class':'file-name'}).append(a,inputID,inputName,input Type);27 var li = $('<li></li>',{'class':'vscell' }).append(div, divLink);31 var divLink = $('<div></div>',{'class':'file-name'}).append(a,inputID,inputName,inputParent,inputType); 32 var li = $('<li></li>',{'class':'vscell',id:type+node.item.id}).append(div, divLink); 28 33 29 34 bindNodeEvents(li); … … 33 38 34 39 var bindNodeEvents = function (node) { 35 $(node).bind("click", function (e){ 36 if(e.button == 0)nodeClick(this,e);return false;}); 40 var disabledItemsList = null; 41 var menuName = ''; 42 43 var type = $(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="type"]').val(); 44 45 $(node) 46 .bind("click", function (e){nodeClick(this,e);return false;}) 47 .bind("dblclick", function (e){nodeDblClick(this,e);return false;}) 37 48 } 38 49 … … 45 56 } 46 57 else if (event.shiftKey) { 47 var firstItem = $(oContainer).find(' > UL > LI > DIV[class^="icon-"][class$="selected"]:first-child').parent();48 var aryNode = $(oContainer).find('> UL > LI > DIV[class^="icon-"]').parent();58 var firstItem = $(oContainer).find('LI.selected'); 59 var aryNode = $(oContainer).find('> UL > LI'); 49 60 var start = aryNode.index(firstItem); 50 61 var finish = aryNode.index(node); … … 58 69 59 70 var nodeDblClick = function (node) { 60 71 var nodeObj = {id:$(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="id"]').val(), 72 name: $(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="name"]').val(), 73 parentID: $(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="parentID"]').val(), 74 minetype: $(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="type"]').val()}; 75 76 o.manager.gridNodeDblClick(nodeObj); 61 77 } 62 78 63 79 var hightlightNode = function (node) { 64 $(node). find('> DIV').addClass('selected');80 $(node).addClass('selected'); 65 81 } 66 82 67 83 var isHightLight = function (node) { 68 return $(node). find('> DIV').hasClass('selected');84 return $(node).hasClass('selected'); 69 85 } 70 86 71 87 var clearAllHightLightNode = function () { 72 $(oContainer).find('> UL > LI > DIV').removeClass('selected');88 $(oContainer).find('> UL > LI').removeClass('selected'); 73 89 } 74 90 75 91 var clearHightLightNode = function (node) { 76 $(node).find('> DIV').removeClass('selected'); 92 $(node).removeClass('selected'); 93 } 94 95 var hightLightAllNode = function () { 96 clearAllHightLightNode(); 97 $(oContainer).find('> UL > LI').addClass('selected'); 98 } 99 100 var getAllHightLightNode = function () { 101 return $(oContainer).find('> UL > LI.selected'); 77 102 } 78 103 79 104 var initGrid = function () { 105 $(oContainer).attr('unselectable','on') 106 .css({'-moz-user-select':'-moz-none', 107 '-moz-user-select':'none', 108 '-o-user-select':'none', 109 '-khtml-user-select':'none', 110 '-webkit-user-select':'none', 111 '-ms-user-select':'none', 112 'user-select':'none' 113 }).bind('selectstart', function(){ return false; 114 }).bind('click', function(e){clearAllHightLightNode(); return false; }); 115 80 116 var ul = $('<ul></ul>',{'class':'vsgrid'}); 117 81 118 var parentDirID = o.manager.getTreeCurrentNode(); 82 119 if (o.data.DIRECTORIES.length > 0) { … … 100 137 $(oContainer).find ('UL').remove(); 101 138 $(oContainer).append(ul); 102 103 $(oContainer).attr('unselectable','on') 104 .css({'-moz-user-select':'-moz-none', 105 '-moz-user-select':'none', 106 '-o-user-select':'none', 107 '-khtml-user-select':'none', 108 '-webkit-user-select':'none', 109 '-ms-user-select':'none', 110 'user-select':'none' 111 }).bind('selectstart', function(){ return false; 112 }).bind('click', function(){ clearAllHightLightNode(); return false; }); 113 } 139 } 140 141 var destroyNode = function (node) { 142 var nodeID = $(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="id"]').val(); 143 var nodeType = $(node).find('> DIV[class^="file-name"] > INPUT[type="hidden"][class^="type"]').val(); 144 $(node).remove(); 145 } 146 147 /****************** 148 * RENAME - START * 149 ******************/ 150 var renameInit = function (node) { 151 var editor = $('<input />',{'class':'rename', type: 'text', value:$(node).text()}); 152 $(editor).bind('focusout', function(e){renameCancel(node)}); 153 $(editor).bind('keydown', function(e){ 154 var keycode = (e.keyCode ? e.keyCode : e.which); 155 if (keycode == '27') { 156 renameCancel(node); 157 }else if(keycode == '13') renameComplete(node); 158 e.stopPropagation(); 159 }); 160 161 $(node).text(''); 162 $(node).append(editor); 163 $(editor).focus(); 164 $(editor).select(); 165 } 166 167 var renameCancel = function (node) { 168 var nodeID = $(node).parent().find('> INPUT[type="hidden"][class^="id"]').val(); 169 var nodeType = $(node).parent().find('> INPUT[type="hidden"][class^="type"]').val(); 170 var nodeName = $(node).parent().find('> INPUT[type="hidden"][class^="name"]').val(); 171 var nodeParent = $(node).parent().find('> INPUT[type="hidden"][class^="parentID"]').val(); 172 var itemIndex = o.manager.searchItemByID(nodeID, nodeType == 'directory' ? nodeType:'file'); 173 174 $(node).find('>INPUT').remove(); 175 $(node).text(nodeName); 176 $(node).select(); 177 } 178 179 var renameComplete = function (node) { 180 var editor = $(node).find('>INPUT'); 181 var dirID = $(node).attr('id'); 182 var newName = $(editor).val(); 183 $(node).attr('rel', newName); 184 $(node).text(newName); 185 $(node).find('>INPUT').remove(); 186 187 /*treeNodes[dirID].name = newName; 188 updateData(dirID, 'rename');*/ 189 } 190 /****************** 191 * RENAME - END * 192 ******************/ 114 193 115 194 this.reloadGrid = function (nodeID) { … … 122 201 123 202 this.rename = function (item) { 124 console.log('rename'); 125 console.log(item); 126 } 127 128 this.deletion = function (id) { 129 203 /*console.log('rename'); 204 console.log(item);*/ 205 } 206 207 this.createNode = function (node) { 208 var node = createNode ({item: node}); 209 $(oContainer).find ('UL').append(node); 210 } 211 212 this.deletion = function (id, type) { 213 destroyNode($(oContainer).find('LI#'+type+id)); 214 } 215 216 this.rename = function (key) { 217 var hightlightedNodes = self.getHightLightItem(); 218 if ($(hightlightedNodes).length == 0 || $(hightlightedNodes).length > 1) return false; 219 220 var nodeID = $(hightlightedNodes).get(0).id; 221 var nodeType = $(hightlightedNodes).get(0).type; 222 223 var selectedNode = $(oContainer).find('LI#' + nodeType + nodeID + '.vscell > DIV.file-name > A#' + nodeID); 224 if (key == 113) { 225 renameInit(selectedNode); 226 } 227 else if (key == 27) { 228 renameCancel(selectedNode); 229 } 230 231 return true; 232 } 233 234 this.getHightLightItem = function () { 235 var nodeSelected = $(oContainer).find('LI.vscell.selected'); 236 var items = []; 237 238 if ($(nodeSelected).length > 0) { 239 $(nodeSelected).each (function(i,o) { 240 var item = { 241 id: $(this).find('> DIV.file-name > INPUT[type="hidden"][class^="id"]').val(), 242 name: $(this).find('> DIV.file-name > INPUT[type="hidden"][class^="name"]').val(), 243 parentID: $(this).find('> DIV.file-name > INPUT[type="hidden"][class^="parentID"]').val(), 244 type: $(this).find('> DIV.file-name > INPUT[type="hidden"][class^="type"]').val() 245 }; 246 items.push(item); 247 }); 248 } 249 250 return items; 251 } 252 253 this.selectAllNode = function () { 254 hightLightAllNode(); 130 255 } 131 256 -
pro-bachkim-filespace/sourcecode/assets/js/jquery/jquery.contextMenu.js
r66 r70 1 // jQuery Context Menu Plugin 2 // 3 // Version 1.01 4 // 5 // Cory S.N. LaViska 6 // A Beautiful Site (http://abeautifulsite.net/) 7 // 8 // More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/ 9 // 10 // Terms of Use 11 // 12 // This plugin is dual-licensed under the GNU General Public License 13 // and the MIT License and is copyright A Beautiful Site, LLC. 14 // 15 if(jQuery)( function() { 16 $.extend($.fn, { 17 18 contextMenu: function(o, callback) { 19 // Defaults 20 if( o.menu == undefined ) return false; 21 if( o.inSpeed == undefined ) o.inSpeed = 150; 22 if( o.outSpeed == undefined ) o.outSpeed = 75; 23 // 0 needs to be -1 for expected results (no fade) 24 if( o.inSpeed == 0 ) o.inSpeed = -1; 25 if( o.outSpeed == 0 ) o.outSpeed = -1; 26 27 if( o.disabledItems == undefined ) o.disabledItems = null; 28 if( o.enabledItems == undefined ) o.enabledItems = null; 29 30 // Loop each context menu 31 $(this).each( function() { 32 var el = $(this); 33 var offset = $(el).offset(); 34 // Add contextMenu class 35 $('#' + o.menu).addClass('contextMenu'); 36 // Simulate a true right click 37 $(this).mousedown( function(e) { 38 var evt = e; 39 evt.stopPropagation(); 40 $(this).mouseup( function(e) { 41 e.stopPropagation(); 42 var srcElement = $(this); 43 $(this).unbind('mouseup'); 44 if( evt.button == 2 ) { 45 // Hide context menus that may be showing 46 $(".contextMenu").hide(); 47 // Get this context menu 48 var menu = $('#' + o.menu); 49 50 if( $(el).hasClass('disabled') ) return false; 51 52 // Detect mouse position 53 var d = {}, x, y; 54 if( self.innerHeight ) { 55 d.pageYOffset = self.pageYOffset; 56 d.pageXOffset = self.pageXOffset; 57 d.innerHeight = self.innerHeight; 58 d.innerWidth = self.innerWidth; 59 } else if( document.documentElement && 60 document.documentElement.clientHeight ) { 61 d.pageYOffset = document.documentElement.scrollTop; 62 d.pageXOffset = document.documentElement.scrollLeft; 63 d.innerHeight = document.documentElement.clientHeight; 64 d.innerWidth = document.documentElement.clientWidth; 65 } else if( document.body ) { 66 d.pageYOffset = document.body.scrollTop; 67 d.pageXOffset = document.body.scrollLeft; 68 d.innerHeight = document.body.clientHeight; 69 d.innerWidth = document.body.clientWidth; 70 } 71 (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft; 72 (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop; 73 74 // Show the menu 75 $(document).unbind('click'); 76 $(menu).css({ top: y, left: x }).fadeIn(o.inSpeed); 77 // Hover events 78 $(menu).find('A').mouseover( function() { 79 $(menu).find('LI.hover').removeClass('hover'); 80 $(this).parent().addClass('hover'); 81 }).mouseout( function() { 82 $(menu).find('LI.hover').removeClass('hover'); 83 }); 84 85 if (o.disabledItems != null) { 86 for (var i = 0; i < o.disabledItems.length; i ++) { 87 $(menu).find('LI.' + o.disabledItems[i]).addClass('disabled'); 88 } 89 } 90 else if (o.enabledItems != null) { 91 for (var i = 0; i < o.enabledItems.length; i ++) { 92 $(menu).find('LI.' + o.enabledItems[i]).removeClass('disabled'); 93 } 94 } 95 else { 96 $(menu).find('LI').removeClass('disabled'); 97 } 98 99 // Keyboard 100 $(document).keypress( function(e) { 101 switch( e.keyCode ) { 102 case 38: // up 103 if( $(menu).find('LI.hover').size() == 0 ) { 104 $(menu).find('LI:last').addClass('hover'); 105 } else { 106 $(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover'); 107 if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover'); 108 } 109 break; 110 case 40: // down 111 if( $(menu).find('LI.hover').size() == 0 ) { 112 $(menu).find('LI:first').addClass('hover'); 113 } else { 114 $(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover'); 115 if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover'); 116 } 117 break; 118 case 13: // enter 119 $(menu).find('LI.hover A').trigger('click'); 120 break; 121 case 27: // esc 122 $(document).trigger('click'); 123 break 124 } 125 }); 126 127 // When items are selected 128 $('#' + o.menu).find('A').unbind('click'); 129 $('#' + o.menu).find('LI:not(.disabled) A').click( function() { 130 $(document).unbind('click').unbind('keypress'); 131 $(".contextMenu").hide(); 132 // Callback 133 if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} ); 134 return false; 135 }); 136 137 // Hide bindings 138 setTimeout( function() { // Delay for Mozilla 139 $(document).click( function() { 140 $(document).unbind('click').unbind('keypress'); 141 $(menu).fadeOut(o.outSpeed); 142 return false; 143 }); 144 }, 0); 145 } 146 }); 147 }); 148 149 // Disable text selection 150 /*if( $.browser.mozilla ) { 151 $('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); }); 152 } else if( $.browser.msie ) { 153 $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); }); 154 } else { 155 $('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); }); 156 }*/ 157 // Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome) 158 159 $('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); }); 160 $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); }); 161 $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); }); 162 163 $(el).add($('UL.contextMenu')).bind('contextmenu', function() { return false; }); 164 165 }); 166 return $(this); 167 }, 168 169 // Disable context menu items on the fly 170 disableContextMenuItems: function(o) { 171 if( o == undefined ) { 172 // Disable all 173 $(this).find('LI').addClass('disabled'); 174 return( $(this) ); 175 } 176 $(this).each( function() { 177 if( o != undefined ) { 178 var d = o.split(','); 179 for( var i = 0; i < d.length; i++ ) { 180 $(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled'); 181 182 } 183 } 184 }); 185 return( $(this) ); 186 }, 187 188 // Enable context menu items on the fly 189 enableContextMenuItems: function(o) { 190 if( o == undefined ) { 191 // Enable all 192 $(this).find('LI.disabled').removeClass('disabled'); 193 return( $(this) ); 194 } 195 $(this).each( function() { 196 if( o != undefined ) { 197 var d = o.split(','); 198 for( var i = 0; i < d.length; i++ ) { 199 $(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled'); 200 201 } 202 } 203 }); 204 return( $(this) ); 205 }, 206 207 // Disable context menu(s) 208 disableContextMenu: function() { 209 $(this).each( function() { 210 $(this).addClass('disabled'); 211 }); 212 return( $(this) ); 213 }, 214 215 // Enable context menu(s) 216 enableContextMenu: function() { 217 $(this).each( function() { 218 $(this).removeClass('disabled'); 219 }); 220 return( $(this) ); 221 }, 222 223 // Destroy context menu(s) 224 destroyContextMenu: function() { 225 // Destroy specified context menus 226 $(this).each( function() { 227 // Disable action 228 $(this).unbind('mousedown').unbind('mouseup'); 229 }); 230 return( $(this) ); 231 } 232 233 }); 1 /*! 2 * jQuery contextMenu - Plugin for simple contextMenu handling 3 * 4 * Version: git-master 5 * 6 * Authors: Rodney Rehm, Addy Osmani (patches for FF) 7 * Web: http://medialize.github.com/jQuery-contextMenu/ 8 * 9 * Licensed under 10 * MIT License http://www.opensource.org/licenses/mit-license 11 * GPL v3 http://opensource.org/licenses/GPL-3.0 12 * 13 */ 14 15 (function($, undefined){ 16 17 // TODO: - 18 // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio 19 // create <menu> structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative 20 21 // determine html5 compatibility 22 $.support.htmlMenuitem = ('HTMLMenuItemElement' in window); 23 $.support.htmlCommand = ('HTMLCommandElement' in window); 24 $.support.eventSelectstart = ("onselectstart" in document.documentElement); 25 /* // should the need arise, test for css user-select 26 $.support.cssUserSelect = (function(){ 27 var t = false, 28 e = document.createElement('div'); 29 30 $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) { 31 var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect', 32 prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select'; 33 34 e.style.cssText = prop + ': text;'; 35 if (e.style[propCC] == 'text') { 36 t = true; 37 return false; 38 } 39 40 return true; 41 }); 42 43 return t; 44 })(); 45 */ 46 47 if (!$.ui || !$.ui.widget) { 48 // duck punch $.cleanData like jQueryUI does to get that remove event 49 // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js#L16-24 50 var _cleanData = $.cleanData; 51 $.cleanData = function( elems ) { 52 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { 53 try { 54 $( elem ).triggerHandler( "remove" ); 55 // http://bugs.jquery.com/ticket/8235 56 } catch( e ) {} 57 } 58 _cleanData( elems ); 59 }; 60 } 61 62 var // currently active contextMenu trigger 63 $currentTrigger = null, 64 // is contextMenu initialized with at least one menu? 65 initialized = false, 66 // window handle 67 $win = $(window), 68 // number of registered menus 69 counter = 0, 70 // mapping selector to namespace 71 namespaces = {}, 72 // mapping namespace to options 73 menus = {}, 74 // custom command type handlers 75 types = {}, 76 // default values 77 defaults = { 78 // selector of contextMenu trigger 79 selector: null, 80 // where to append the menu to 81 appendTo: null, 82 // method to trigger context menu ["right", "left", "hover"] 83 trigger: "right", 84 // hide menu when mouse leaves trigger / menu elements 85 autoHide: false, 86 // ms to wait before showing a hover-triggered context menu 87 delay: 200, 88 // flag denoting if a second trigger should simply move (true) or rebuild (false) an open menu 89 // as long as the trigger happened on one of the trigger-element's child nodes 90 reposition: true, 91 // determine position to show menu at 92 determinePosition: function($menu) { 93 // position to the lower middle of the trigger element 94 if ($.ui && $.ui.position) { 95 // .position() is provided as a jQuery UI utility 96 // (...and it won't work on hidden elements) 97 $menu.css('display', 'block').position({ 98 my: "center top", 99 at: "center bottom", 100 of: this, 101 offset: "0 5", 102 collision: "fit" 103 }).css('display', 'none'); 104 } else { 105 // determine contextMenu position 106 var offset = this.offset(); 107 offset.top += this.outerHeight(); 108 offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2; 109 $menu.css(offset); 110 } 111 }, 112 // position menu 113 position: function(opt, x, y) { 114 var $this = this, 115 offset; 116 // determine contextMenu position 117 if (!x && !y) { 118 opt.determinePosition.call(this, opt.$menu); 119 return; 120 } else if (x === "maintain" && y === "maintain") { 121 // x and y must not be changed (after re-show on command click) 122 offset = opt.$menu.position(); 123 } else { 124 // x and y are given (by mouse event) 125 offset = {top: y, left: x}; 126 } 127 128 // correct offset if viewport demands it 129 var bottom = $win.scrollTop() + $win.height(), 130 right = $win.scrollLeft() + $win.width(), 131 height = opt.$menu.height(), 132 width = opt.$menu.width(); 133 134 if (offset.top + height > bottom) { 135 offset.top -= height; 136 } 137 138 if (offset.left + width > right) { 139 offset.left -= width; 140 } 141 142 opt.$menu.css(offset); 143 }, 144 // position the sub-menu 145 positionSubmenu: function($menu) { 146 if ($.ui && $.ui.position) { 147 // .position() is provided as a jQuery UI utility 148 // (...and it won't work on hidden elements) 149 $menu.css('display', 'block').position({ 150 my: "left top", 151 at: "right top", 152 of: this, 153 collision: "flipfit fit" 154 }).css('display', ''); 155 } else { 156 // determine contextMenu position 157 var offset = { 158 top: 0, 159 left: this.outerWidth() 160 }; 161 $menu.css(offset); 162 } 163 }, 164 // offset to add to zIndex 165 zIndex: 1, 166 // show hide animation settings 167 animation: { 168 duration: 50, 169 show: 'slideDown', 170 hide: 'slideUp' 171 }, 172 // events 173 events: { 174 show: $.noop, 175 hide: $.noop 176 }, 177 // default callback 178 callback: null, 179 // list of contextMenu items 180 items: {} 181 }, 182 // mouse position for hover activation 183 hoveract = { 184 timer: null, 185 pageX: null, 186 pageY: null 187 }, 188 // determine zIndex 189 zindex = function($t) { 190 var zin = 0, 191 $tt = $t; 192 193 while (true) { 194 zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0); 195 $tt = $tt.parent(); 196 if (!$tt || !$tt.length || "html body".indexOf($tt.prop('nodeName').toLowerCase()) > -1 ) { 197 break; 198 } 199 } 200 201 return zin; 202 }, 203 // event handlers 204 handle = { 205 // abort anything 206 abortevent: function(e){ 207 e.preventDefault(); 208 e.stopImmediatePropagation(); 209 }, 210 211 // contextmenu show dispatcher 212 contextmenu: function(e) { 213 var $this = $(this); 214 215 // disable actual context-menu 216 e.preventDefault(); 217 e.stopImmediatePropagation(); 218 219 // abort native-triggered events unless we're triggering on right click 220 if (e.data.trigger != 'right' && e.originalEvent) { 221 return; 222 } 223 224 // abort event if menu is visible for this trigger 225 if ($this.hasClass('context-menu-active')) { 226 return; 227 } 228 229 if (!$this.hasClass('context-menu-disabled')) { 230 // theoretically need to fire a show event at <menu> 231 // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus 232 // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this }); 233 // e.data.$menu.trigger(evt); 234 235 $currentTrigger = $this; 236 if (e.data.build) { 237 var built = e.data.build($currentTrigger, e); 238 // abort if build() returned false 239 if (built === false) { 240 return; 241 } 242 243 // dynamically build menu on invocation 244 e.data = $.extend(true, {}, defaults, e.data, built || {}); 245 246 // abort if there are no items to display 247 if (!e.data.items || $.isEmptyObject(e.data.items)) { 248 // Note: jQuery captures and ignores errors from event handlers 249 if (window.console) { 250 (console.error || console.log)("No items specified to show in contextMenu"); 251 } 252 253 throw new Error('No Items specified'); 254 } 255 256 // backreference for custom command type creation 257 e.data.$trigger = $currentTrigger; 258 259 op.create(e.data); 260 } 261 // show menu 262 op.show.call($this, e.data, e.pageX, e.pageY); 263 } 264 }, 265 // contextMenu left-click trigger 266 click: function(e) { 267 e.preventDefault(); 268 e.stopImmediatePropagation(); 269 $(this).trigger($.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY })); 270 }, 271 // contextMenu right-click trigger 272 mousedown: function(e) { 273 // register mouse down 274 var $this = $(this); 275 276 // hide any previous menus 277 if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) { 278 $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide'); 279 } 280 281 // activate on right click 282 if (e.button == 2) { 283 $currentTrigger = $this.data('contextMenuActive', true); 284 } 285 }, 286 // contextMenu right-click trigger 287 mouseup: function(e) { 288 // show menu 289 var $this = $(this); 290 if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) { 291 e.preventDefault(); 292 e.stopImmediatePropagation(); 293 $currentTrigger = $this; 294 $this.trigger($.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY })); 295 } 296 297 $this.removeData('contextMenuActive'); 298 }, 299 // contextMenu hover trigger 300 mouseenter: function(e) { 301 var $this = $(this), 302 $related = $(e.relatedTarget), 303 $document = $(document); 304 305 // abort if we're coming from a menu 306 if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { 307 return; 308 } 309 310 // abort if a menu is shown 311 if ($currentTrigger && $currentTrigger.length) { 312 return; 313 } 314 315 hoveract.pageX = e.pageX; 316 hoveract.pageY = e.pageY; 317 hoveract.data = e.data; 318 $document.on('mousemove.contextMenuShow', handle.mousemove); 319 hoveract.timer = setTimeout(function() { 320 hoveract.timer = null; 321 $document.off('mousemove.contextMenuShow'); 322 $currentTrigger = $this; 323 $this.trigger($.Event("contextmenu", { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY })); 324 }, e.data.delay ); 325 }, 326 // contextMenu hover trigger 327 mousemove: function(e) { 328 hoveract.pageX = e.pageX; 329 hoveract.pageY = e.pageY; 330 }, 331 // contextMenu hover trigger 332 mouseleave: function(e) { 333 // abort if we're leaving for a menu 334 var $related = $(e.relatedTarget); 335 if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { 336 return; 337 } 338 339 try { 340 clearTimeout(hoveract.timer); 341 } catch(e) {} 342 343 hoveract.timer = null; 344 }, 345 346 // click on layer to hide contextMenu 347 layerClick: function(e) { 348 var $this = $(this), 349 root = $this.data('contextMenuRoot'), 350 mouseup = false, 351 button = e.button, 352 x = e.pageX, 353 y = e.pageY, 354 target, 355 offset, 356 selectors; 357 358 e.preventDefault(); 359 e.stopImmediatePropagation(); 360 361 setTimeout(function() { 362 var $window, hideshow, possibleTarget; 363 var triggerAction = ((root.trigger == 'left' && button === 0) || (root.trigger == 'right' && button === 2)); 364 365 // find the element that would've been clicked, wasn't the layer in the way 366 if (document.elementFromPoint) { 367 root.$layer.hide(); 368 target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); 369 root.$layer.show(); 370 } 371 372 if (root.reposition && triggerAction) { 373 if (document.elementFromPoint) { 374 if (root.$trigger.is(target) || root.$trigger.has(target).length) { 375 root.position.call(root.$trigger, root, x, y); 376 return; 377 } 378 } else { 379 offset = root.$trigger.offset(); 380 $window = $(window); 381 // while this looks kinda awful, it's the best way to avoid 382 // unnecessarily calculating any positions 383 offset.top += $window.scrollTop(); 384 if (offset.top <= e.pageY) { 385 offset.left += $window.scrollLeft(); 386 if (offset.left <= e.pageX) { 387 offset.bottom = offset.top + root.$trigger.outerHeight(); 388 if (offset.bottom >= e.pageY) { 389 offset.right = offset.left + root.$trigger.outerWidth(); 390 if (offset.right >= e.pageX) { 391 // reposition 392 root.position.call(root.$trigger, root, x, y); 393 return; 394 } 395 } 396 } 397 } 398 } 399 } 400 401 if (target && triggerAction) { 402 root.$trigger.one('contextmenu:hidden', function() { 403 $(target).contextMenu({x: x, y: y}); 404 }); 405 } 406 407 root.$menu.trigger('contextmenu:hide'); 408 }, 50); 409 }, 410 // key handled :hover 411 keyStop: function(e, opt) { 412 if (!opt.isInput) { 413 e.preventDefault(); 414 } 415 416 e.stopPropagation(); 417 }, 418 key: function(e) { 419 var opt = $currentTrigger.data('contextMenu') || {}; 420 421 switch (e.keyCode) { 422 case 9: 423 case 38: // up 424 handle.keyStop(e, opt); 425 // if keyCode is [38 (up)] or [9 (tab) with shift] 426 if (opt.isInput) { 427 if (e.keyCode == 9 && e.shiftKey) { 428 e.preventDefault(); 429 opt.$selected && opt.$selected.find('input, textarea, select').blur(); 430 opt.$menu.trigger('prevcommand'); 431 return; 432 } else if (e.keyCode == 38 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') { 433 // checkboxes don't capture this key 434 e.preventDefault(); 435 return; 436 } 437 } else if (e.keyCode != 9 || e.shiftKey) { 438 opt.$menu.trigger('prevcommand'); 439 return; 440 } 441 // omitting break; 442 443 // case 9: // tab - reached through omitted break; 444 case 40: // down 445 handle.keyStop(e, opt); 446 if (opt.isInput) { 447 if (e.keyCode == 9) { 448 e.preventDefault(); 449 opt.$selected && opt.$selected.find('input, textarea, select').blur(); 450 opt.$menu.trigger('nextcommand'); 451 return; 452 } else if (e.keyCode == 40 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') { 453 // checkboxes don't capture this key 454 e.preventDefault(); 455 return; 456 } 457 } else { 458 opt.$menu.trigger('nextcommand'); 459 return; 460 } 461 break; 462 463 case 37: // left 464 handle.keyStop(e, opt); 465 if (opt.isInput || !opt.$selected || !opt.$selected.length) { 466 break; 467 } 468 469 if (!opt.$selected.parent().hasClass('context-menu-root')) { 470 var $parent = opt.$selected.parent().parent(); 471 opt.$selected.trigger('contextmenu:blur'); 472 opt.$selected = $parent; 473 return; 474 } 475 break; 476 477 case 39: // right 478 handle.keyStop(e, opt); 479 if (opt.isInput || !opt.$selected || !opt.$selected.length) { 480 break; 481 } 482 483 var itemdata = opt.$selected.data('contextMenu') || {}; 484 if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) { 485 opt.$selected = null; 486 itemdata.$selected = null; 487 itemdata.$menu.trigger('nextcommand'); 488 return; 489 } 490 break; 491 492 case 35: // end 493 case 36: // home 494 if (opt.$selected && opt.$selected.find('input, textarea, select').length) { 495 return; 496 } else { 497 (opt.$selected && opt.$selected.parent() || opt.$menu) 498 .children(':not(.disabled, .not-selectable)')[e.keyCode == 36 ? 'first' : 'last']() 499 .trigger('contextmenu:focus'); 500 e.preventDefault(); 501 return; 502 } 503 break; 504 505 case 13: // enter 506 handle.keyStop(e, opt); 507 if (opt.isInput) { 508 if (opt.$selected && !opt.$selected.is('textarea, select')) { 509 e.preventDefault(); 510 return; 511 } 512 break; 513 } 514 opt.$selected && opt.$selected.trigger('mouseup'); 515 return; 516 517 case 32: // space 518 case 33: // page up 519 case 34: // page down 520 // prevent browser from scrolling down while menu is visible 521 handle.keyStop(e, opt); 522 return; 523 524 case 27: // esc 525 handle.keyStop(e, opt); 526 opt.$menu.trigger('contextmenu:hide'); 527 return; 528 529 default: // 0-9, a-z 530 var k = (String.fromCharCode(e.keyCode)).toUpperCase(); 531 if (opt.accesskeys[k]) { 532 // according to the specs accesskeys must be invoked immediately 533 opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu 534 ? 'contextmenu:focus' 535 : 'mouseup' 536 ); 537 return; 538 } 539 break; 540 } 541 // pass event to selected item, 542 // stop propagation to avoid endless recursion 543 e.stopPropagation(); 544 opt.$selected && opt.$selected.trigger(e); 545 }, 546 547 // select previous possible command in menu 548 prevItem: function(e) { 549 e.stopPropagation(); 550 var opt = $(this).data('contextMenu') || {}; 551 552 // obtain currently selected menu 553 if (opt.$selected) { 554 var $s = opt.$selected; 555 opt = opt.$selected.parent().data('contextMenu') || {}; 556 opt.$selected = $s; 557 } 558 559 var $children = opt.$menu.children(), 560 $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(), 561 $round = $prev; 562 563 // skip disabled 564 while ($prev.hasClass('disabled') || $prev.hasClass('not-selectable')) { 565 if ($prev.prev().length) { 566 $prev = $prev.prev(); 567 } else { 568 $prev = $children.last(); 569 } 570 if ($prev.is($round)) { 571 // break endless loop 572 return; 573 } 574 } 575 576 // leave current 577 if (opt.$selected) { 578 handle.itemMouseleave.call(opt.$selected.get(0), e); 579 } 580 581 // activate next 582 handle.itemMouseenter.call($prev.get(0), e); 583 584 // focus input 585 var $input = $prev.find('input, textarea, select'); 586 if ($input.length) { 587 $input.focus(); 588 } 589 }, 590 // select next possible command in menu 591 nextItem: function(e) { 592 e.stopPropagation(); 593 var opt = $(this).data('contextMenu') || {}; 594 595 // obtain currently selected menu 596 if (opt.$selected) { 597 var $s = opt.$selected; 598 opt = opt.$selected.parent().data('contextMenu') || {}; 599 opt.$selected = $s; 600 } 601 602 var $children = opt.$menu.children(), 603 $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(), 604 $round = $next; 605 606 // skip disabled 607 while ($next.hasClass('disabled') || $next.hasClass('not-selectable')) { 608 if ($next.next().length) { 609 $next = $next.next(); 610 } else { 611 $next = $children.first(); 612 } 613 if ($next.is($round)) { 614 // break endless loop 615 return; 616 } 617 } 618 619 // leave current 620 if (opt.$selected) { 621 handle.itemMouseleave.call(opt.$selected.get(0), e); 622 } 623 624 // activate next 625 handle.itemMouseenter.call($next.get(0), e); 626 627 // focus input 628 var $input = $next.find('input, textarea, select'); 629 if ($input.length) { 630 $input.focus(); 631 } 632 }, 633 634 // flag that we're inside an input so the key handler can act accordingly 635 focusInput: function(e) { 636 var $this = $(this).closest('.context-menu-item'), 637 data = $this.data(), 638 opt = data.contextMenu, 639 root = data.contextMenuRoot; 640 641 root.$selected = opt.$selected = $this; 642 root.isInput = opt.isInput = true; 643 }, 644 // flag that we're inside an input so the key handler can act accordingly 645 blurInput: function(e) { 646 var $this = $(this).closest('.context-menu-item'), 647 data = $this.data(), 648 opt = data.contextMenu, 649 root = data.contextMenuRoot; 650 651 root.isInput = opt.isInput = false; 652 }, 653 654 // :hover on menu 655 menuMouseenter: function(e) { 656 var root = $(this).data().contextMenuRoot; 657 root.hovering = true; 658 }, 659 // :hover on menu 660 menuMouseleave: function(e) { 661 var root = $(this).data().contextMenuRoot; 662 if (root.$layer && root.$layer.is(e.relatedTarget)) { 663 root.hovering = false; 664 } 665 }, 666 667 // :hover done manually so key handling is possible 668 itemMouseenter: function(e) { 669 var $this = $(this), 670 data = $this.data(), 671 opt = data.contextMenu, 672 root = data.contextMenuRoot; 673 674 root.hovering = true; 675 676 // abort if we're re-entering 677 if (e && root.$layer && root.$layer.is(e.relatedTarget)) { 678 e.preventDefault(); 679 e.stopImmediatePropagation(); 680 } 681 682 // make sure only one item is selected 683 (opt.$menu ? opt : root).$menu 684 .children('.hover').trigger('contextmenu:blur'); 685 686 if ($this.hasClass('disabled') || $this.hasClass('not-selectable')) { 687 opt.$selected = null; 688 return; 689 } 690 691 $this.trigger('contextmenu:focus'); 692 }, 693 // :hover done manually so key handling is possible 694 itemMouseleave: function(e) { 695 var $this = $(this), 696 data = $this.data(), 697 opt = data.contextMenu, 698 root = data.contextMenuRoot; 699 700 if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) { 701 root.$selected && root.$selected.trigger('contextmenu:blur'); 702 e.preventDefault(); 703 e.stopImmediatePropagation(); 704 root.$selected = opt.$selected = opt.$node; 705 return; 706 } 707 708 $this.trigger('contextmenu:blur'); 709 }, 710 // contextMenu item click 711 itemClick: function(e) { 712 var $this = $(this), 713 data = $this.data(), 714 opt = data.contextMenu, 715 root = data.contextMenuRoot, 716 key = data.contextMenuKey, 717 callback; 718 719 // abort if the key is unknown or disabled or is a menu 720 if (!opt.items[key] || $this.is('.disabled, .context-menu-submenu, .context-menu-separator, .not-selectable')) { 721 return; 722 } 723 724 e.preventDefault(); 725 e.stopImmediatePropagation(); 726 727 if ($.isFunction(root.callbacks[key]) && Object.prototype.hasOwnProperty.call(root.callbacks, key)) { 728 // item-specific callback 729 callback = root.callbacks[key]; 730 } else if ($.isFunction(root.callback)) { 731 // default callback 732 callback = root.callback; 733 } else { 734 // no callback, no action 735 return; 736 } 737 738 // hide menu if callback doesn't stop that 739 if (callback.call(root.$trigger, key, root) !== false) { 740 root.$menu.trigger('contextmenu:hide'); 741 } else if (root.$menu.parent().length) { 742 op.update.call(root.$trigger, root); 743 } 744 }, 745 // ignore click events on input elements 746 inputClick: function(e) { 747 e.stopImmediatePropagation(); 748 }, 749 750 // hide <menu> 751 hideMenu: function(e, data) { 752 var root = $(this).data('contextMenuRoot'); 753 op.hide.call(root.$trigger, root, data && data.force); 754 }, 755 // focus <command> 756 focusItem: function(e) { 757 e.stopPropagation(); 758 var $this = $(this), 759 data = $this.data(), 760 opt = data.contextMenu, 761 root = data.contextMenuRoot; 762 763 $this.addClass('hover') 764 .siblings('.hover').trigger('contextmenu:blur'); 765 766 // remember selected 767 opt.$selected = root.$selected = $this; 768 769 // position sub-menu - do after show so dumb $.ui.position can keep up 770 if (opt.$node) { 771 root.positionSubmenu.call(opt.$node, opt.$menu); 772 } 773 }, 774 // blur <command> 775 blurItem: function(e) { 776 e.stopPropagation(); 777 var $this = $(this), 778 data = $this.data(), 779 opt = data.contextMenu, 780 root = data.contextMenuRoot; 781 782 $this.removeClass('hover'); 783 opt.$selected = null; 784 } 785 }, 786 // operations 787 op = { 788 show: function(opt, x, y) { 789 var $trigger = $(this), 790 offset, 791 css = {}; 792 793 // hide any open menus 794 $('#context-menu-layer').trigger('mousedown'); 795 796 // backreference for callbacks 797 opt.$trigger = $trigger; 798 799 // show event 800 if (opt.events.show.call($trigger, opt) === false) { 801 $currentTrigger = null; 802 return; 803 } 804 805 // create or update context menu 806 op.update.call($trigger, opt); 807 808 // position menu 809 opt.position.call($trigger, opt, x, y); 810 811 // make sure we're in front 812 if (opt.zIndex) { 813 css.zIndex = zindex($trigger) + opt.zIndex; 814 } 815 816 // add layer 817 op.layer.call(opt.$menu, opt, css.zIndex); 818 819 // adjust sub-menu zIndexes 820 opt.$menu.find('ul').css('zIndex', css.zIndex + 1); 821 822 // position and show context menu 823 opt.$menu.css( css )[opt.animation.show](opt.animation.duration, function() { 824 $trigger.trigger('contextmenu:visible'); 825 }); 826 // make options available and set state 827 $trigger 828 .data('contextMenu', opt) 829 .addClass("context-menu-active"); 830 831 // register key handler 832 $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key); 833 // register autoHide handler 834 if (opt.autoHide) { 835 // mouse position handler 836 $(document).on('mousemove.contextMenuAutoHide', function(e) { 837 // need to capture the offset on mousemove, 838 // since the page might've been scrolled since activation 839 var pos = $trigger.offset(); 840 pos.right = pos.left + $trigger.outerWidth(); 841 pos.bottom = pos.top + $trigger.outerHeight(); 842 843 if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) { 844 // if mouse in menu... 845 opt.$menu.trigger('contextmenu:hide'); 846 } 847 }); 848 } 849 }, 850 hide: function(opt, force) { 851 var $trigger = $(this); 852 if (!opt) { 853 opt = $trigger.data('contextMenu') || {}; 854 } 855 856 // hide event 857 if (!force && opt.events && opt.events.hide.call($trigger, opt) === false) { 858 return; 859 } 860 861 // remove options and revert state 862 $trigger 863 .removeData('contextMenu') 864 .removeClass("context-menu-active"); 865 866 if (opt.$layer) { 867 // keep layer for a bit so the contextmenu event can be aborted properly by opera 868 setTimeout((function($layer) { 869 return function(){ 870 $layer.remove(); 871 }; 872 })(opt.$layer), 10); 873 874 try { 875 delete opt.$layer; 876 } catch(e) { 877 opt.$layer = null; 878 } 879 } 880 881 // remove handle 882 $currentTrigger = null; 883 // remove selected 884 opt.$menu.find('.hover').trigger('contextmenu:blur'); 885 opt.$selected = null; 886 // unregister key and mouse handlers 887 //$(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705 888 $(document).off('.contextMenuAutoHide').off('keydown.contextMenu'); 889 // hide menu 890 opt.$menu && opt.$menu[opt.animation.hide](opt.animation.duration, function (){ 891 // tear down dynamically built menu after animation is completed. 892 if (opt.build) { 893 opt.$menu.remove(); 894 $.each(opt, function(key, value) { 895 switch (key) { 896 case 'ns': 897 case 'selector': 898 case 'build': 899 case 'trigger': 900 return true; 901 902 default: 903 opt[key] = undefined; 904 try { 905 delete opt[key]; 906 } catch (e) {} 907 return true; 908 } 909 }); 910 } 911 912 setTimeout(function() { 913 $trigger.trigger('contextmenu:hidden'); 914 }, 10); 915 }); 916 }, 917 create: function(opt, root) { 918 if (root === undefined) { 919 root = opt; 920 } 921 // create contextMenu 922 opt.$menu = $('<ul class="context-menu-list"></ul>').addClass(opt.className || "").data({ 923 'contextMenu': opt, 924 'contextMenuRoot': root 925 }); 926 927 $.each(['callbacks', 'commands', 'inputs'], function(i,k){ 928 opt[k] = {}; 929 if (!root[k]) { 930 root[k] = {}; 931 } 932 }); 933 934 root.accesskeys || (root.accesskeys = {}); 935 936 // create contextMenu items 937 $.each(opt.items, function(key, item){ 938 var $t = $('<li class="context-menu-item"></li>').addClass(item.className || ""), 939 $label = null, 940 $input = null; 941 942 // iOS needs to see a click-event bound to an element to actually 943 // have the TouchEvents infrastructure trigger the click event 944 $t.on('click', $.noop); 945 946 item.$node = $t.data({ 947 'contextMenu': opt, 948 'contextMenuRoot': root, 949 'contextMenuKey': key 950 }); 951 952 // register accesskey 953 // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that 954 if (item.accesskey) { 955 var aks = splitAccesskey(item.accesskey); 956 for (var i=0, ak; ak = aks[i]; i++) { 957 if (!root.accesskeys[ak]) { 958 root.accesskeys[ak] = item; 959 item._name = item.name.replace(new RegExp('(' + ak + ')', 'i'), '<span class="context-menu-accesskey">$1</span>'); 960 break; 961 } 962 } 963 } 964 965 if (typeof item == "string") { 966 $t.addClass('context-menu-separator not-selectable'); 967 } else if (item.type && types[item.type]) { 968 // run custom type handler 969 types[item.type].call($t, item, opt, root); 970 // register commands 971 $.each([opt, root], function(i,k){ 972 k.commands[key] = item; 973 if ($.isFunction(item.callback)) { 974 k.callbacks[key] = item.callback; 975 } 976 }); 977 } else { 978 // add label for input 979 if (item.type == 'html') { 980 $t.addClass('context-menu-html not-selectable'); 981 } else if (item.type) { 982 $label = $('<label></label>').appendTo($t); 983 $('<span></span>').html(item._name || item.name).appendTo($label); 984 $t.addClass('context-menu-input'); 985 opt.hasTypes = true; 986 $.each([opt, root], function(i,k){ 987 k.commands[key] = item; 988 k.inputs[key] = item; 989 }); 990 } else if (item.items) { 991 item.type = 'sub'; 992 } 993 994 switch (item.type) { 995 case 'text': 996 $input = $('<input type="text" value="1" name="" value="">') 997 .attr('name', 'context-menu-input-' + key) 998 .val(item.value || "") 999 .appendTo($label); 1000 break; 1001 1002 case 'textarea': 1003 $input = $('<textarea name=""></textarea>') 1004 .attr('name', 'context-menu-input-' + key) 1005 .val(item.value || "") 1006 .appendTo($label); 1007 1008 if (item.height) { 1009 $input.height(item.height); 1010 } 1011 break; 1012 1013 case 'checkbox': 1014 $input = $('<input type="checkbox" value="1" name="" value="">') 1015 .attr('name', 'context-menu-input-' + key) 1016 .val(item.value || "") 1017 .prop("checked", !!item.selected) 1018 .prependTo($label); 1019 break; 1020 1021 case 'radio': 1022 $input = $('<input type="radio" value="1" name="" value="">') 1023 .attr('name', 'context-menu-input-' + item.radio) 1024 .val(item.value || "") 1025 .prop("checked", !!item.selected) 1026 .prependTo($label); 1027 break; 1028 1029 case 'select': 1030 $input = $('<select name="">') 1031 .attr('name', 'context-menu-input-' + key) 1032 .appendTo($label); 1033 if (item.options) { 1034 $.each(item.options, function(value, text) { 1035 $('<option></option>').val(value).text(text).appendTo($input); 1036 }); 1037 $input.val(item.selected); 1038 } 1039 break; 1040 1041 case 'sub': 1042 // FIXME: shouldn't this .html() be a .text()? 1043 $('<span></span>').html(item._name || item.name).appendTo($t); 1044 item.appendTo = item.$node; 1045 op.create(item, root); 1046 $t.data('contextMenu', item).addClass('context-menu-submenu'); 1047 item.callback = null; 1048 break; 1049 1050 case 'html': 1051 $(item.html).appendTo($t); 1052 break; 1053 1054 default: 1055 $.each([opt, root], function(i,k){ 1056 k.commands[key] = item; 1057 if ($.isFunction(item.callback)) { 1058 k.callbacks[key] = item.callback; 1059 } 1060 }); 1061 // FIXME: shouldn't this .html() be a .text()? 1062 $('<span></span>').html(item._name || item.name || "").appendTo($t); 1063 break; 1064 } 1065 1066 // disable key listener in <input> 1067 if (item.type && item.type != 'sub' && item.type != 'html') { 1068 $input 1069 .on('focus', handle.focusInput) 1070 .on('blur', handle.blurInput); 1071 1072 if (item.events) { 1073 $input.on(item.events, opt); 1074 } 1075 } 1076 1077 // add icons 1078 if (item.icon) { 1079 $t.addClass("ico ico-" + item.icon); 1080 } 1081 } 1082 1083 // cache contained elements 1084 item.$input = $input; 1085 item.$label = $label; 1086 1087 // attach item to menu 1088 $t.appendTo(opt.$menu); 1089 1090 // Disable text selection 1091 if (!opt.hasTypes && $.support.eventSelectstart) { 1092 // browsers support user-select: none, 1093 // IE has a special event for text-selection 1094 // browsers supporting neither will not be preventing text-selection 1095 $t.on('selectstart.disableTextSelect', handle.abortevent); 1096 } 1097 }); 1098 // attach contextMenu to <body> (to bypass any possible overflow:hidden issues on parents of the trigger element) 1099 if (!opt.$node) { 1100 opt.$menu.css('display', 'none').addClass('context-menu-root'); 1101 } 1102 opt.$menu.appendTo(opt.appendTo || document.body); 1103 }, 1104 resize: function($menu, nested) { 1105 // determine widths of submenus, as CSS won't grow them automatically 1106 // position:absolute within position:absolute; min-width:100; max-width:200; results in width: 100; 1107 // kinda sucks hard... 1108 1109 // determine width of absolutely positioned element 1110 $menu.css({position: 'absolute', display: 'block'}); 1111 // don't apply yet, because that would break nested elements' widths 1112 // add a pixel to circumvent word-break issue in IE9 - #80 1113 $menu.data('width', Math.ceil($menu.width()) + 1); 1114 // reset styles so they allow nested elements to grow/shrink naturally 1115 $menu.css({ 1116 position: 'static', 1117 minWidth: '0px', 1118 maxWidth: '100000px' 1119 }); 1120 // identify width of nested menus 1121 $menu.find('> li > ul').each(function() { 1122 op.resize($(this), true); 1123 }); 1124 // reset and apply changes in the end because nested 1125 // elements' widths wouldn't be calculatable otherwise 1126 if (!nested) { 1127 $menu.find('ul').andSelf().css({ 1128 position: '', 1129 display: '', 1130 minWidth: '', 1131 maxWidth: '' 1132 }).width(function() { 1133 return $(this).data('width'); 1134 }); 1135 } 1136 }, 1137 update: function(opt, root) { 1138 var $trigger = this; 1139 if (root === undefined) { 1140 root = opt; 1141 op.resize(opt.$menu); 1142 } 1143 // re-check disabled for each item 1144 opt.$menu.children().each(function(){ 1145 var $item = $(this), 1146 key = $item.data('contextMenuKey'), 1147 item = opt.items[key], 1148 disabled = ($.isFunction(item.disabled) && item.disabled.call($trigger, key, root)) || item.disabled === true; 1149 1150 // dis- / enable item 1151 $item[disabled ? 'addClass' : 'removeClass']('disabled'); 1152 1153 if (item.type) { 1154 // dis- / enable input elements 1155 $item.find('input, select, textarea').prop('disabled', disabled); 1156 1157 // update input states 1158 switch (item.type) { 1159 case 'text': 1160 case 'textarea': 1161 item.$input.val(item.value || ""); 1162 break; 1163 1164 case 'checkbox': 1165 case 'radio': 1166 item.$input.val(item.value || "").prop('checked', !!item.selected); 1167 break; 1168 1169 case 'select': 1170 item.$input.val(item.selected || ""); 1171 break; 1172 } 1173 } 1174 1175 if (item.$menu) { 1176 // update sub-menu 1177 op.update.call($trigger, item, root); 1178 } 1179 }); 1180 }, 1181 layer: function(opt, zIndex) { 1182 // add transparent layer for click area 1183 // filter and background for Internet Explorer, Issue #23 1184 var $layer = opt.$layer = $('<div id="context-menu-layer" style="position:fixed; z-index:' + zIndex + '; top:0; left:0; opacity: 0; filter: alpha(opacity=0); background-color: #000;"></div>') 1185 .css({height: $win.height(), width: $win.width(), display: 'block'}) 1186 .data('contextMenuRoot', opt) 1187 .insertBefore(this) 1188 .on('contextmenu', handle.abortevent) 1189 .on('mousedown', handle.layerClick); 1190 1191 // IE6 doesn't know position:fixed; 1192 if (!$.support.fixedPosition) { 1193 $layer.css({ 1194 'position' : 'absolute', 1195 'height' : $(document).height() 1196 }); 1197 } 1198 1199 return $layer; 1200 } 1201 }; 1202 1203 // split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key 1204 function splitAccesskey(val) { 1205 var t = val.split(/\s+/), 1206 keys = []; 1207 1208 for (var i=0, k; k = t[i]; i++) { 1209 k = k[0].toUpperCase(); // first character only 1210 // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it. 1211 // a map to look up already used access keys would be nice 1212 keys.push(k); 1213 } 1214 1215 return keys; 1216 } 1217 1218 // handle contextMenu triggers 1219 $.fn.contextMenu = function(operation) { 1220 if (operation === undefined) { 1221 this.first().trigger('contextmenu'); 1222 } else if (operation.x && operation.y) { 1223 this.first().trigger($.Event("contextmenu", {pageX: operation.x, pageY: operation.y})); 1224 } else if (operation === "hide") { 1225 var $menu = this.data('contextMenu').$menu; 1226 $menu && $menu.trigger('contextmenu:hide'); 1227 } else if (operation === "destroy") { 1228 $.contextMenu("destroy", {context: this}); 1229 } else if ($.isPlainObject(operation)) { 1230 operation.context = this; 1231 $.contextMenu("create", operation); 1232 } else if (operation) { 1233 this.removeClass('context-menu-disabled'); 1234 } else if (!operation) { 1235 this.addClass('context-menu-disabled'); 1236 } 1237 1238 return this; 1239 }; 1240 1241 // manage contextMenu instances 1242 $.contextMenu = function(operation, options) { 1243 if (typeof operation != 'string') { 1244 options = operation; 1245 operation = 'create'; 1246 } 1247 1248 if (typeof options == 'string') { 1249 options = {selector: options}; 1250 } else if (options === undefined) { 1251 options = {}; 1252 } 1253 1254 // merge with default options 1255 var o = $.extend(true, {}, defaults, options || {}); 1256 var $document = $(document); 1257 var $context = $document; 1258 var _hasContext = false; 1259 1260 if (!o.context || !o.context.length) { 1261 o.context = document; 1262 } else { 1263 // you never know what they throw at you... 1264 $context = $(o.context).first(); 1265 o.context = $context.get(0); 1266 _hasContext = o.context !== document; 1267 } 1268 1269 switch (operation) { 1270 case 'create': 1271 // no selector no joy 1272 if (!o.selector) { 1273 throw new Error('No selector specified'); 1274 } 1275 // make sure internal classes are not bound to 1276 if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) { 1277 throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className'); 1278 } 1279 if (!o.build && (!o.items || $.isEmptyObject(o.items))) { 1280 throw new Error('No Items specified'); 1281 } 1282 counter ++; 1283 o.ns = '.contextMenu' + counter; 1284 if (!_hasContext) { 1285 namespaces[o.selector] = o.ns; 1286 } 1287 menus[o.ns] = o; 1288 1289 // default to right click 1290 if (!o.trigger) { 1291 o.trigger = 'right'; 1292 } 1293 1294 if (!initialized) { 1295 // make sure item click is registered first 1296 $document 1297 .on({ 1298 'contextmenu:hide.contextMenu': handle.hideMenu, 1299 'prevcommand.contextMenu': handle.prevItem, 1300 'nextcommand.contextMenu': handle.nextItem, 1301 'contextmenu.contextMenu': handle.abortevent, 1302 'mouseenter.contextMenu': handle.menuMouseenter, 1303 'mouseleave.contextMenu': handle.menuMouseleave 1304 }, '.context-menu-list') 1305 .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick) 1306 .on({ 1307 'mouseup.contextMenu': handle.itemClick, 1308 'contextmenu:focus.contextMenu': handle.focusItem, 1309 'contextmenu:blur.contextMenu': handle.blurItem, 1310 'contextmenu.contextMenu': handle.abortevent, 1311 'mouseenter.contextMenu': handle.itemMouseenter, 1312 'mouseleave.contextMenu': handle.itemMouseleave 1313 }, '.context-menu-item'); 1314 1315 initialized = true; 1316 } 1317 1318 // engage native contextmenu event 1319 $context 1320 .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu); 1321 1322 if (_hasContext) { 1323 // add remove hook, just in case 1324 $context.on('remove' + o.ns, function() { 1325 $(this).contextMenu("destroy"); 1326 }); 1327 } 1328 1329 switch (o.trigger) { 1330 case 'hover': 1331 $context 1332 .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter) 1333 .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave); 1334 break; 1335 1336 case 'left': 1337 $context.on('click' + o.ns, o.selector, o, handle.click); 1338 break; 1339 /* 1340 default: 1341 // http://www.quirksmode.org/dom/events/contextmenu.html 1342 $document 1343 .on('mousedown' + o.ns, o.selector, o, handle.mousedown) 1344 .on('mouseup' + o.ns, o.selector, o, handle.mouseup); 1345 break; 1346 */ 1347 } 1348 1349 // create menu 1350 if (!o.build) { 1351 op.create(o); 1352 } 1353 break; 1354 1355 case 'destroy': 1356 var $visibleMenu; 1357 if (_hasContext) { 1358 // get proper options 1359 var context = o.context; 1360 $.each(menus, function(ns, o) { 1361 if (o.context !== context) { 1362 return true; 1363 } 1364 1365 $visibleMenu = $('.context-menu-list').filter(':visible'); 1366 if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is($(o.context).find(o.selector))) { 1367 $visibleMenu.trigger('contextmenu:hide', {force: true}); 1368 } 1369 1370 try { 1371 if (menus[o.ns].$menu) { 1372 menus[o.ns].$menu.remove(); 1373 } 1374 1375 delete menus[o.ns]; 1376 } catch(e) { 1377 menus[o.ns] = null; 1378 } 1379 1380 $(o.context).off(o.ns); 1381 1382 return true; 1383 }); 1384 } else if (!o.selector) { 1385 $document.off('.contextMenu .contextMenuAutoHide'); 1386 $.each(menus, function(ns, o) { 1387 $(o.context).off(o.ns); 1388 }); 1389 1390 namespaces = {}; 1391 menus = {}; 1392 counter = 0; 1393 initialized = false; 1394 1395 $('#context-menu-layer, .context-menu-list').remove(); 1396 } else if (namespaces[o.selector]) { 1397 $visibleMenu = $('.context-menu-list').filter(':visible'); 1398 if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) { 1399 $visibleMenu.trigger('contextmenu:hide', {force: true}); 1400 } 1401 1402 try { 1403 if (menus[namespaces[o.selector]].$menu) { 1404 menus[namespaces[o.selector]].$menu.remove(); 1405 } 1406 1407 delete menus[namespaces[o.selector]]; 1408 } catch(e) { 1409 menus[namespaces[o.selector]] = null; 1410 } 1411 1412 $document.off(namespaces[o.selector]); 1413 } 1414 break; 1415 1416 case 'html5': 1417 // if <command> or <menuitem> are not handled by the browser, 1418 // or options was a bool true, 1419 // initialize $.contextMenu for them 1420 if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options == "boolean" && options)) { 1421 $('menu[type="context"]').each(function() { 1422 if (this.id) { 1423 $.contextMenu({ 1424 selector: '[contextmenu=' + this.id +']', 1425 items: $.contextMenu.fromMenu(this) 1426 }); 1427 } 1428 }).css('display', 'none'); 1429 } 1430 break; 1431 1432 default: 1433 throw new Error('Unknown operation "' + operation + '"'); 1434 } 1435 1436 return this; 1437 }; 1438 1439 // import values into <input> commands 1440 $.contextMenu.setInputValues = function(opt, data) { 1441 if (data === undefined) { 1442 data = {}; 1443 } 1444 1445 $.each(opt.inputs, function(key, item) { 1446 switch (item.type) { 1447 case 'text': 1448 case 'textarea': 1449 item.value = data[key] || ""; 1450 break; 1451 1452 case 'checkbox': 1453 item.selected = data[key] ? true : false; 1454 break; 1455 1456 case 'radio': 1457 item.selected = (data[item.radio] || "") == item.value ? true : false; 1458 break; 1459 1460 case 'select': 1461 item.selected = data[key] || ""; 1462 break; 1463 } 1464 }); 1465 }; 1466 1467 // export values from <input> commands 1468 $.contextMenu.getInputValues = function(opt, data) { 1469 if (data === undefined) { 1470 data = {}; 1471 } 1472 1473 $.each(opt.inputs, function(key, item) { 1474 switch (item.type) { 1475 case 'text': 1476 case 'textarea': 1477 case 'select': 1478 data[key] = item.$input.val(); 1479 break; 1480 1481 case 'checkbox': 1482 data[key] = item.$input.prop('checked'); 1483 break; 1484 1485 case 'radio': 1486 if (item.$input.prop('checked')) { 1487 data[item.radio] = item.value; 1488 } 1489 break; 1490 } 1491 }); 1492 1493 return data; 1494 }; 1495 1496 // find <label for="xyz"> 1497 function inputLabel(node) { 1498 return (node.id && $('label[for="'+ node.id +'"]').val()) || node.name; 1499 } 1500 1501 // convert <menu> to items object 1502 function menuChildren(items, $children, counter) { 1503 if (!counter) { 1504 counter = 0; 1505 } 1506 1507 $children.each(function() { 1508 var $node = $(this), 1509 node = this, 1510 nodeName = this.nodeName.toLowerCase(), 1511 label, 1512 item; 1513 1514 // extract <label><input> 1515 if (nodeName == 'label' && $node.find('input, textarea, select').length) { 1516 label = $node.text(); 1517 $node = $node.children().first(); 1518 node = $node.get(0); 1519 nodeName = node.nodeName.toLowerCase(); 1520 } 1521 1522 /* 1523 * <menu> accepts flow-content as children. that means <embed>, <canvas> and such are valid menu items. 1524 * Not being the sadistic kind, $.contextMenu only accepts: 1525 * <command>, <menuitem>, <hr>, <span>, <p> <input [text, radio, checkbox]>, <textarea>, <select> and of course <menu>. 1526 * Everything else will be imported as an html node, which is not interfaced with contextMenu. 1527 */ 1528 1529 // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command 1530 switch (nodeName) { 1531 // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element 1532 case 'menu': 1533 item = {name: $node.attr('label'), items: {}}; 1534 counter = menuChildren(item.items, $node.children(), counter); 1535 break; 1536 1537 // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command 1538 case 'a': 1539 // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command 1540 case 'button': 1541 item = { 1542 name: $node.text(), 1543 disabled: !!$node.attr('disabled'), 1544 callback: (function(){ return function(){ $node.click(); }; })() 1545 }; 1546 break; 1547 1548 // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command 1549 1550 case 'menuitem': 1551 case 'command': 1552 switch ($node.attr('type')) { 1553 case undefined: 1554 case 'command': 1555 case 'menuitem': 1556 item = { 1557 name: $node.attr('label'), 1558 disabled: !!$node.attr('disabled'), 1559 callback: (function(){ return function(){ $node.click(); }; })() 1560 }; 1561 break; 1562 1563 case 'checkbox': 1564 item = { 1565 type: 'checkbox', 1566 disabled: !!$node.attr('disabled'), 1567 name: $node.attr('label'), 1568 selected: !!$node.attr('checked') 1569 }; 1570 break; 1571 1572 case 'radio': 1573 item = { 1574 type: 'radio', 1575 disabled: !!$node.attr('disabled'), 1576 name: $node.attr('label'), 1577 radio: $node.attr('radiogroup'), 1578 value: $node.attr('id'), 1579 selected: !!$node.attr('checked') 1580 }; 1581 break; 1582 1583 default: 1584 item = undefined; 1585 } 1586 break; 1587 1588 case 'hr': 1589 item = '-------'; 1590 break; 1591 1592 case 'input': 1593 switch ($node.attr('type')) { 1594 case 'text': 1595 item = { 1596 type: 'text', 1597 name: label || inputLabel(node), 1598 disabled: !!$node.attr('disabled'), 1599 value: $node.val() 1600 }; 1601 break; 1602 1603 case 'checkbox': 1604 item = { 1605 type: 'checkbox', 1606 name: label || inputLabel(node), 1607 disabled: !!$node.attr('disabled'), 1608 selected: !!$node.attr('checked') 1609 }; 1610 break; 1611 1612 case 'radio': 1613 item = { 1614 type: 'radio', 1615 name: label || inputLabel(node), 1616 disabled: !!$node.attr('disabled'), 1617 radio: !!$node.attr('name'), 1618 value: $node.val(), 1619 selected: !!$node.attr('checked') 1620 }; 1621 break; 1622 1623 default: 1624 item = undefined; 1625 break; 1626 } 1627 break; 1628 1629 case 'select': 1630 item = { 1631 type: 'select', 1632 name: label || inputLabel(node), 1633 disabled: !!$node.attr('disabled'), 1634 selected: $node.val(), 1635 options: {} 1636 }; 1637 $node.children().each(function(){ 1638 item.options[this.value] = $(this).text(); 1639 }); 1640 break; 1641 1642 case 'textarea': 1643 item = { 1644 type: 'textarea', 1645 name: label || inputLabel(node), 1646 disabled: !!$node.attr('disabled'), 1647 value: $node.val() 1648 }; 1649 break; 1650 1651 case 'label': 1652 break; 1653 1654 default: 1655 item = {type: 'html', html: $node.clone(true)}; 1656 break; 1657 } 1658 1659 if (item) { 1660 counter++; 1661 items['key' + counter] = item; 1662 } 1663 }); 1664 1665 return counter; 1666 } 1667 1668 // convert html5 menu 1669 $.contextMenu.fromMenu = function(element) { 1670 var $this = $(element), 1671 items = {}; 1672 1673 menuChildren(items, $this.children()); 1674 1675 return items; 1676 }; 1677 1678 // make defaults accessible 1679 $.contextMenu.defaults = defaults; 1680 $.contextMenu.types = types; 1681 // export internal functions - undocumented, for hacking only! 1682 $.contextMenu.handle = handle; 1683 $.contextMenu.op = op; 1684 $.contextMenu.menus = menus; 1685 234 1686 })(jQuery); -
pro-bachkim-filespace/sourcecode/assets/js/manager.js
r66 r70 19 19 if( o.datasource == undefined ) o.datasource = 'ajax'; 20 20 21 var isDev = false; 22 var contextmenu = null; 21 23 var oContainer = this; 22 24 var tree = []; … … 25 27 var maxWidth = 0; 26 28 var treeCurrentNode = null; 29 var self = this; 30 var oClipBoard = {items:null, act:null}; 31 32 /** 33 * Toolbar defined 34 * */ 35 var btnNewFolder = $('#' + o.toolsbar + ' > DIV.btn-group.basic > #btnNewFolder'); 36 var btnDel = $('#' + o.toolsbar + ' > DIV.btn-group.basic > #btnDel'); 37 var btnCopy = $('#' + o.toolsbar + ' > DIV.btn-group.basic > #btnCopy'); 38 var btnCut = $('#' + o.toolsbar + ' > DIV.btn-group.basic > #btnCut'); 39 var btnPaste = $('#' + o.toolsbar + ' > DIV.btn-group.basic > #btnPaste'); 40 var btnShare = $('#' + o.toolsbar + ' > DIV.btn-group.social > #btnShare'); 41 var btnPreview = $('#' + o.toolsbar + ' > DIV.btn-group.social > #btnPreview'); 42 var btnDownload = $('#' + o.toolsbar + ' > DIV.btn-group.creation > #btnDownload'); 43 var btnUpload = $('#' + o.toolsbar + ' > DIV.btn-group.creation > #btnUpload'); 44 var btnRefresh = $('#' + o.toolsbar + ' > DIV.btn-group.control > #btnRefresh'); 27 45 28 46 var sendCommand = function (p) { … … 34 52 if( p.callbackAlways == undefined ) p.callbackAlways = null; 35 53 36 if (p.script != null && o.datasource == 'ajax') {54 if (p.script != null && (o.datasource == 'ajax' || isDev)) { 37 55 $.post(o.host + o.hostmodule + p.script, p.postdata, function (data){ 38 56 if (data) { … … 92 110 } 93 111 } 112 113 var buildTreeFromParent = function (dirID, node) { 114 var aryChildFiles = []; 115 var aryChildDirs = []; 116 var aryChildIDs = []; 117 var aryTmp = []; 118 var dir = o.data.DIRECTORIES[searchItemByID(dirID, 'directory')]; 119 aryChildDirs = searchItemsByParent(dirID, 'directory'); 120 aryChildFiles = searchItemsByParent(dirID, 'file'); 121 122 $(aryChildDirs).each(function (index) { 123 aryChildIDs[index] = this.id; 124 }); 125 126 if (node.id == undefined) node.id = dirID; 127 128 if ($(aryChildFiles).length > 0 ) { 129 if (node.files == undefined) node.files = []; 130 $(aryChildFiles).each(function (index) { 131 node.files[index] = this.id; 132 }); 133 } 134 135 if ($(aryChildDirs).length > 0) { 136 if (node.childs == undefined) node.childs = []; 137 $(aryChildIDs).each(function (index) { 138 node.childs[index] = new Object; 139 buildTreeFromParent(aryChildIDs[index], node.childs[index]); 140 }); 141 } 142 } 94 143 95 144 var checkChildExisted = function (id) { … … 100 149 101 150 var doneInit = function () { 151 bindEventToToolbars(); 152 documentEventsBinding(); 102 153 }; 103 154 … … 108 159 var init = function () { 109 160 layoutRender (); 110 111 161 $('#' + o.tree).parent().resizable({ 112 162 maxWidth: maxWidth, … … 117 167 } 118 168 }); 119 120 169 $(window).resize (function() {layoutRender ();$('#' + o.tree).parent().resizable({maxWidth: maxWidth});}); 121 122 170 sendCommand ({postdata:null,callbackSuccess:createFileManager,callbackDone:doneInit,callbackFail:failInit}); 123 171 }; … … 167 215 } 168 216 217 /************************** 218 * TOOLBAR EVENTS - START * 219 **************************/ 220 var btnRefreshClick = function (obj) { 221 $(o).find('i').addClass('icon-spin'); 222 sendCommand ({ postdata:null, 223 callbackSuccess:function (parseData) { 224 o.data = parseData; 225 self.updateData({updateAll:true}); 226 o.oTree.refeshTree(); 227 console.log(o); 228 }, 229 callbackDone:function () {$(o).find('i').removeClass('icon-spin');}, 230 callbackFail:failInit 231 }); 232 } 233 234 var btnNewFolderClick = function () { 235 createFolderStart(); 236 } 237 238 var btnDelClick = function () { 239 var items = o.oGrid.getHightLightItem(); 240 if ($(items).length == 0){ 241 var dirID = $(o.oTree.getSelectedNode()).attr('id'); 242 var item = o.data.DIRECTORIES[searchItemByID(dirID,'directory')]; 243 item.type = 'directory'; 244 items = [item]; 245 } 246 self.deleteItem(items); 247 } 248 249 var btnCopyClick = function () { 250 copy(); 251 } 252 253 var btnPasteClick = function () { 254 paste(); 255 } 256 257 258 var bindEventToToolbars = function () { 259 $(btnRefresh).click(function(e){btnRefreshClick(this)}); 260 261 $(btnNewFolder).click(function(e){btnNewFolderClick()}); 262 $(btnDel).click(function(e){btnDelClick()}); 263 $(btnCopy).click(function(e){btnCopyClick()}); 264 $(btnPaste).click(function(e){btnPasteClick()}) 265 266 /*btnCut 267 btnShare 268 btnPreview 269 btnDownload 270 btnUpload*/ 271 } 272 /************************ 273 * TOOLBAR EVENTS - END * 274 ************************/ 275 276 /*********************************** 277 * DOCUMENT EVENTS BINDING - START * 278 ***********************************/ 279 var documentEventsBinding = function () { 280 $(document).bind('keydown', function (e){ 281 switch( e.which ) { 282 case 113: 283 case 27: 284 var gridSelectedItems = o.oGrid.getHightLightItem(); 285 if ($(gridSelectedItems).length > 0) { 286 o.oGrid.rename(e.which); 287 }else { 288 o.oTree.rename(e.which); 289 } 290 break; 291 case 46: 292 //delete 293 btnDelClick(); 294 break; 295 case 65: 296 if (e.ctrlKey) { 297 o.oGrid.selectAllNode(); 298 } 299 break; 300 default: 301 break; 302 } 303 }); 304 } 305 /*********************************** 306 * DOCUMENT EVENTS BINDING - END * 307 ***********************************/ 308 169 309 /******************************* 170 310 * CREATE FOLDER - START * 171 311 *******************************/ 172 var createFolder = function (parent) { 173 var postdata = {delobj:delobj}; 312 var createFolderStart = function () { 313 var promptOptions = { 314 title: "Tạo thư mục má»i", 315 buttons: { 316 confirm: { 317 label: "Lưu" 318 }, 319 cancel: { 320 label: "Há»§y" 321 } 322 }, 323 callback: function(result) { 324 if (result === null) { 325 326 } else { 327 createFolder(treeCurrentNode, result); 328 } 329 } 330 }; 331 332 return bootbox.prompt(promptOptions); 333 } 334 335 var createFolder = function (parent, name) { 336 var postdata = {fname:name,fparentid:parent}; 174 337 var script = 'createdir'; 338 /*isDev = true;*/ 175 339 sendCommand ({ 176 340 postdata:postdata, 177 341 script:script, 178 callbackSuccess:function(parseData){c onsole.log(parseData);},342 callbackSuccess:function(parseData){createFolderFinish(parseData);}, 179 343 callbackFail: function(){} 180 344 }); 181 345 } 182 346 183 var createFolderCallBack = function (parent) { 184 347 var createFolderFinish = function (parseData) { 348 /*isDev = false;*/ 349 if (parseData.ERROR.errCode == 0) { 350 var node = {id:parseData.id, name:parseData.name,parentID:parseData.parentID}; 351 o.oTree.createNode(node); 352 o.data.DIRECTORIES[$(o.data.DIRECTORIES).length] = node; 353 if (o.oGrid) o.oGrid.reloadGrid(parseData.parentID); 354 } 185 355 } 186 356 /******************************* 187 * CREATE FOLDER - END *357 * CREATE FOLDER - END * 188 358 *******************************/ 189 190 /******************************* 191 * CLIPBOARD - START * 192 *******************************/ 193 var oClipBoard = {item:null, act:null}; 194 195 var addToClipboard = function (item, act) { 196 oClipboard = {item:item,act:act}; 197 } 198 199 var clearClipboard = function () { 200 oClipBoard = {item:null, act:null}; 201 } 202 203 /******************************* 204 * CLIPBOARD - START * 205 *******************************/ 359 /******************************** 360 * COPY & PASTE & MOVE - START * 361 ************=*******************/ 362 var copy = function (){ 363 //detect selected items 364 //push to clipboard 365 var items = o.oGrid.getHightLightItem(); 366 367 if ($(items).length == 0) { 368 var node = o.oTree.getSelectedNode(); 369 var itemID = $(node).attr('id'); 370 items[0] = o.data.DIRECTORIES[searchItemByID(itemID, 'directory')]; 371 items[0].type = 'directory'; 372 } 373 374 if ($(items).length > 0) { 375 oClipBoard.items = items; 376 oClipBoard.act = 'copy'; 377 } 378 } 379 380 var paste = function () { 381 if ((oClipBoard.act != 'copy' 382 && oClipBoard.act != 'move') 383 || oClipBoard.items == null) return; 384 385 var items = []; 386 var destination = self.getTreeCurrentNode(); 387 388 $(oClipBoard.items).each(function (index) { 389 var node = new Object; 390 buildTreeFromParent(this.id, node); 391 items[index] = node; 392 }); 393 394 var postdata = {destination:destination,data:JSON.stringify(items)}; 395 var script = 'copy'; 396 sendCommand ({ 397 postdata:postdata, 398 script:script, 399 callbackSuccess:function(parseData){ 400 console.log(parseData); 401 } 402 }); 403 404 } 405 406 var move = function () { 407 408 } 409 410 var copyTo = function () { 411 412 } 413 414 var moveTo = function () { 415 416 } 417 418 /***************************** 419 * COPY & PASTE & MOVE - END * 420 *****************************/ 421 206 422 this.deleteItem = function (item) { 423 207 424 var confirmText = 'Bạn có muá»n xóa '; 208 425 209 426 if ($.isArray(item) && item.length > 1) { 210 confirmText += 'các thư mục ( file) và các thư mục (file) con Äã chá»n?';427 confirmText += 'các thư mục (và files) Äã chá»n?'; 211 428 } 212 429 else if (item.length == 1) { … … 215 432 confirmText += ' <span style="font-weight:bold">' + item[0].name + "</span> khÃŽng?"; 216 433 } 434 435 confirmText += '<br /><div style="color:red">(hà nh Äá»ng nà y sẜ xóa tất cả thư mục con và các file trong các thư mục Äã chá»n)</div>'; 217 436 218 437 for (var i = 0; i < item.length; i++) { … … 262 481 postdata:postdata, 263 482 script:script, 264 callbackSuccess:function(parseData){console.log(parseData);}, 483 callbackSuccess:function(parseData){ 484 if($(parseData.DIRECTORIES).length > 0) { 485 for(var i = 0; i < $(parseData.DIRECTORIES).length; i++) { 486 o.oTree.deletion($(parseData.DIRECTORIES).get(i)); 487 o.oGrid.deletion($(parseData.DIRECTORIES).get(i), 'directory'); 488 } 489 } 490 491 if($(parseData.FILES).length > 0) { 492 for(var i = 0; i < $(parseData.FILES).length; i++) { 493 var id = $(parseData.FILES).get(i); 494 var file = o.data.FILES[searchItemByID(id)]; 495 o.oGrid.deletion(file.id, file.minetype); 496 } 497 } 498 499 }, 265 500 callbackFail: function(){} 266 501 }); … … 270 505 271 506 bootbox.confirm(confirmOptions); 272 273 507 } 274 508 … … 284 518 } 285 519 286 this.gridNodeClick = function (gridNode) { 287 //fire when click a node on Grid 288 //then fire action of Tree 520 this.gridNodeDblClick = function (node) { 521 if (node.minetype == 'directory') { 522 var treeNode = $('#' + o.tree).find('UL.vstree[rel^="node' + node.parentID + '"] > LI[rel^="folder"] > A#' + node.id); 523 o.oTree.activeNode(treeNode); 524 } 525 else { 526 //execute or preview file 527 } 289 528 }; 290 529 291 530 this.createNewFolder = function () { 292 var promptOptions = { 293 title: "Tạo thư mục má»i", 294 buttons: { 295 confirm: { 296 label: "Lưu" 297 }, 298 cancel: { 299 label: "Há»§y" 300 } 301 }, 302 callback: function(result) { 303 if (result === null) { 304 console.log("Prompt dismissed"); 305 } else { 306 console.log("Hi "+result); 307 } 308 } 309 }; 310 311 bootbox.prompt(promptOptions); 531 312 532 } 313 533 314 534 this.updateData = function (p) { 315 535 if( p.item == undefined ) p.item = null; 536 if( p.updateAll == undefined ) p.updateAll = false; 316 537 if( p.from == undefined ) p.from = null; 317 538 if( p.type == undefined ) p.type = null; 318 539 if( p.callback == undefined ) p.callback = null; 319 540 320 541 var obj = p.from == 'tree' ? o.oGrid : o.oTree; 321 322 var index = searchItemByID(p.item.id, p.type); 323 switch (p.type) { 324 case 'directory': 325 o.data.DIRECTORIES[index].name = p.item.name; 326 o.data.DIRECTORIES[index].parentID = p.item.parentID; 327 break; 328 case 'file': 329 o.data.FILES[index].name = p.item.name; 330 o.data.FILES[index].parentID = p.item.parentID; 331 o.data.FILES[index].minetype = p.item.minetype; 332 break; 333 default: 334 break; 542 if (!p.updateAll) { 543 var index = searchItemByID(p.item.id, p.type); 544 switch (p.type) { 545 case 'directory': 546 o.data.DIRECTORIES[index].name = p.item.name; 547 o.data.DIRECTORIES[index].parentID = p.item.parentID; 548 break; 549 case 'file': 550 o.data.FILES[index].name = p.item.name; 551 o.data.FILES[index].parentID = p.item.parentID; 552 o.data.FILES[index].minetype = p.item.minetype; 553 break; 554 default: 555 break; 556 } 335 557 } 336 558 … … 343 565 344 566 //call sendCommand 567 } 568 569 this.searchItemsByParent = function (parentID, type) { 570 return searchItemsByParent(parentID, type); 571 } 572 573 this.searchItemByID = function (parentID, type) { 574 return searchItemByID(parentID, type); 345 575 } 346 576 -
pro-bachkim-filespace/sourcecode/assets/js/tree.js
r66 r70 20 20 21 21 var createNode = function (node) { 22 22 23 if( !node ) var node = {}; 23 24 if( node.item == undefined ) node.item = null; //item.id, item.name, item.parentID … … 25 26 if( node.defaultStatus == undefined ) node.defaultStatus = 0; //0: collapsed; 1:expanded; 26 27 if( node.showContextMenu == undefined ) node.showContextMenu = true; //0: collapsed; 1:expanded; 27 28 var parentNode = node.isRoot == true ? oContainer : $(oContainer).find('UL.vstree > LI[rel="folder"] > A#' + node.item.parentID);28 if (node.callback == undefined ) node.callback = null; 29 29 30 var nodeClass = node.isRoot == true ? 'home':'directory'; 30 31 var hidden = (node.isRoot == true || node.item.parentID == 0 ) ? null:'display:none'; … … 32 33 33 34 var a = $('<a></a>',{id: node.item.id,href: '#',rel: node.item.name,text: node.item.name}); 34 var li = $('<li></li>',{'class': nodeClass,rel: 'folder'}) .append(a);35 var ul = $('<ul></ul>',{'class':'vstree',style:hidden}).append(li);36 $(li).addClass(defaulState);37 35 var li = $('<li></li>',{'class': nodeClass,rel: 'folder'}) 36 .addClass(defaulState) 37 .append(a); 38 var ul = null; 38 39 bindNodeEvents(a); 39 40 40 41 treeNodes[node.item.id] = {id:node.item.id, name:node.item.name, parentID:node.item.parentID, html:ul, status:defaulState, isRoot:node.isRoot}; 41 42 if (node.isRoot == false) $(parentNode).parent().append(ul); else $(parentNode).append(ul); 43 return (parentNode.length == 1); 42 if (node.isRoot == true) { 43 ul = $('<ul></ul>',{'class':'vstree',rel:'node-1',style:hidden}).append(li); 44 $(oContainer).append(ul); 45 } 46 else { 47 var preUL = $(oContainer).find('UL.vstree[rel^="node-1"]'); 48 49 if (preUL != null) { 50 if (node.item.parentID == 0) { 51 var groupUL = $(oContainer).find('UL.vstree[rel^="node0"]'); 52 if ($(groupUL).length == 0) { 53 ul = $('<ul></ul>',{'class':'vstree',rel:'node0',style:hidden}).append(li); 54 $(preUL).find('> LI.home').append(ul); 55 } 56 else { 57 $(groupUL).append(li); 58 } 59 } 60 else { 61 var liArray = $(oContainer).find('UL.vstree LI[rel="folder"]'); 62 for (var i = 0; i < $(liArray).length; i++) { 63 var id = $(liArray[i]).find('A').attr('id'); 64 65 if (id == node.item.parentID) { 66 var groupUL = $(oContainer).find('UL.vstree[rel^="node' + id + '"]'); 67 if ($(groupUL).length == 0) { 68 ul = $('<ul></ul>',{'class':'vstree',rel:'node'+node.item.parentID,style:hidden}).append(li); 69 $(liArray[i]).append(ul); 70 } 71 else { 72 $(groupUL).append(li); 73 } 74 } 75 } 76 } 77 } 78 } 79 80 if (node.callback != null) node.callback(this); 81 //return (parentNode.length == 1); 82 return true; 44 83 }; 45 84 … … 49 88 //click event 50 89 $(node).bind("click", function (e){if(e.button == 0)nodeClick(this);return false;}); 51 52 //Press F2 to rename, ESC to cancel rename53 if (documentBoundEvent == false) {54 $(oContainer).bind('keydown','keypress', function(e) {55 switch( e.keyCode ) {56 case 113:57 renameInit(selectedNode);58 break;59 case 27:60 renameCancel(selectedNode);61 break;62 case 46:63 var dirID = $(selectedNode).attr('id');64 var dirObj = {};65 dirObj.id = treeNodes[dirID].id;66 dirObj.name = treeNodes[dirID].name;67 dirObj.parentID = treeNodes[dirID].parentID;68 dirObj.type = 'directory';69 if (e.keyCode == 46) o.manager.deleteItem([dirObj]);70 break;71 case 13:72 default:73 break;74 }75 });76 77 documentBoundEvent = true;78 }79 80 disabledItemsList = [];81 82 $(node).contextMenu({menu: 'treeMenu',disabledItems: disabledItemsList},83 function(action, el, pos) {84 var dirID = $(el).attr('id');85 var item = $(treeNodes[dirID].html).find('> LI > A#' + dirID);86 var dirObj = {};87 dirObj.id = treeNodes[dirID].id;88 dirObj.name = treeNodes[dirID].name;89 dirObj.parentID = treeNodes[dirID].parentID;90 dirObj.type = 'directory';91 92 switch(action) {93 case 'rename':94 renameInit(item);95 break;96 case 'newfolder':97 o.manager.createNewFolder();98 break;99 case 'share':100 case 'copy':101 case 'cut':102 break;103 case 'delete':104 o.manager.deleteItem([dirObj]);105 break;106 default:107 break;108 }109 });110 111 90 }; 112 91 … … 124 103 } 125 104 else { 126 collap Node(node);105 collapseNode(node); 127 106 treeNodes[folderID].status = 'collapsed'; 128 107 } … … 142 121 }; 143 122 144 var collap Node = function (node) {123 var collapseNode = function (node) { 145 124 var li = $(node).parent(); 146 125 $(li).find('> UL').slideUp({ duration: o.expandSpeed, easing: o.expandEasing }); … … 148 127 }; 149 128 129 var isCollapsedNode = function (node) { 130 return $(node).parent().hasClass('collapsed'); 131 } 132 133 var nodeIsExisted = function (nodeID) { 134 var node = $(oContainer).find('UL.vstree LI.directory > A#' + nodeID); 135 return ($(node).length > 0) ? node : false; 136 } 137 150 138 var initTree = function () { 139 $(oContainer).attr('unselectable','on') 140 .css({'-moz-user-select':'-moz-none', 141 '-moz-user-select':'none', 142 '-o-user-select':'none', 143 '-khtml-user-select':'none', 144 '-webkit-user-select':'none', 145 '-ms-user-select':'none', 146 'user-select':'none' 147 }).bind('selectstart', function(){ return false;}); 148 151 149 if (o.data == null) { 152 150 console.error(o.data, 'No directory to display!'); 153 151 return; 154 152 } 153 154 o.data.sort(function (a,b) { 155 return a.parentID - b.parentID; 156 }); 157 155 158 $(oContainer).find ('.vstree').remove(); 156 159 var homeNode = createNode({ … … 164 167 } 165 168 o.manager.setTreeCurrentNode(0); 166 167 $(oContainer).attr('unselectable','on')168 .css({'-moz-user-select':'-moz-none',169 '-moz-user-select':'none',170 '-o-user-select':'none',171 '-khtml-user-select':'none',172 '-webkit-user-select':'none',173 '-ms-user-select':'none',174 'user-select':'none'175 }).bind('selectstart', function(){ return false;});176 169 }; 177 170 … … 179 172 * RENAME - START * 180 173 ******************/ 181 182 174 var renameInit = function (node) { 183 175 var editor = $('<input>',{class:'rename', type: 'text', value:$(node).text()}); 184 176 $(editor).bind('focusout', function(e){renameCancel(node)}); 185 $(editor).bind('key press', function(e){177 $(editor).bind('keydown', function(e){ 186 178 var keycode = (e.keyCode ? e.keyCode : e.which); 187 if(keycode == '13') renameComplete(node); 179 if (keycode == '27') { 180 renameCancel(node); 181 }else if(keycode == '13') renameComplete(node); 188 182 e.stopPropagation(); 189 183 }); … … 199 193 $(node).find('>INPUT').remove(); 200 194 $(node).text(treeNodes[dirID].name); 201 $(node). select();195 $(node).focus(); 202 196 } 203 197 … … 212 206 updateData(dirID, 'rename'); 213 207 } 214 215 208 /****************** 216 209 * RENAME - END * … … 222 215 var destroyNode = function (node) { 223 216 var dirID = $(node).attr('id'); 224 $(node).parent(). parent().remove();217 $(node).parent().remove(); 225 218 delete treeNodes[dirID]; 226 219 } 227 220 /****************** 228 * DELTE - END *221 * DELTE - END * 229 222 ******************/ 230 /*******************************231 * COPY & PASTE & MOVE - START *232 *******************************/233 var copyNode = function (source){234 235 }236 237 var pasteNode = function (destination) {238 239 }240 241 var moveNode = function (source, destination) {242 243 }244 /*****************************245 * COPY & PASTE & MOVE - END *246 *****************************/247 248 223 var updateData = function (id, act) { 249 224 for (var i = 0; i < o.data.length; i++) { … … 263 238 } 264 239 240 this.createNode = function (node) { 241 createNode ({item: node}); 242 o.data.sort(function (a,b) { 243 return a.parentID - b.parentID; 244 }); 245 if (node.parentID != 0) { 246 var parentNode = $(oContainer).find('UL LI > A#' + node.parentID); 247 expandNode(parentNode); 248 } 249 } 250 251 this.activeNode = function (node) { 252 var dirID = $(node).attr('id'); 253 selectedNode = hightlightNode(node); 254 o.manager.setTreeCurrentNode(dirID); 255 expandNode(node); 256 $(node).select(); 257 } 258 259 this.refeshTree = function () { 260 for (var i = 0; i < o.data.length; i++) { 261 var node = nodeIsExisted(o.data[i].id); 262 var defaulState = isCollapsedNode(node) ? 0:1; 263 if (node == false) 264 createNode ({item: o.data[i],defaulState:defaulState}); 265 } 266 267 o.manager.setTreeCurrentNode($(selectedNode).attr('id')); 268 } 269 270 this.getSelectedNode = function () { 271 return selectedNode; 272 } 273 265 274 this.deletion = function (id) { 266 275 destroyNode($(oContainer).find('A#'+id)); 276 } 277 278 this.findNodebyID = function (id) { 279 267 280 } 268 281 … … 279 292 return this; 280 293 }; 294 295 this.rename = function (key) { 296 if (selectedNode == null) return false; 297 if (key == 113) { 298 renameInit(selectedNode); 299 } 300 else if (key == 27) { 301 renameCancel(selectedNode); 302 } 303 304 return true; 305 } 281 306 282 307 return this.initialize();
Note: See TracChangeset
for help on using the changeset viewer.