├── .gitignore ├── LICENSE.txt ├── README.md ├── app.js ├── logs └── .gitignore ├── package.json ├── public └── node-zk │ ├── jquery.tree.js │ ├── lib │ ├── jquery.cookie.js │ ├── jquery.hotkeys.js │ ├── jquery.js │ ├── jquery.metadata.js │ └── sarissa.js │ ├── plugins │ ├── _jquery.tree.rtl.js │ ├── jquery.tree.checkbox.js │ ├── jquery.tree.contextmenu.js │ ├── jquery.tree.cookie.js │ ├── jquery.tree.hotkeys.js │ ├── jquery.tree.metadata.js │ ├── jquery.tree.themeroller.js │ ├── jquery.tree.xml_flat.js │ └── jquery.tree.xml_nested.js │ ├── stylesheets │ └── style.css │ └── themes │ ├── apple │ ├── bg.jpg │ ├── dot_for_ie.gif │ ├── icons.png │ ├── style.css │ └── throbber.gif │ ├── checkbox │ ├── dot_for_ie.gif │ ├── icons.png │ ├── style.css │ └── throbber.gif │ ├── classic │ ├── dot_for_ie.gif │ ├── icons.png │ ├── style.css │ └── throbber.gif │ ├── default │ ├── dot_for_ie.gif │ ├── icons.png │ ├── style.css │ └── throbber.gif │ └── themeroller │ ├── dot_for_ie.gif │ ├── icons.png │ ├── style.css │ └── throbber.gif ├── start.sh ├── test └── app.test.js ├── user.json ├── views ├── create.ejs ├── data.ejs ├── index.ejs ├── layout.ejs └── tree.ejs └── zk.js /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## node-zk-browser 2 | 3 | A zookeeper web administrator in node.js. It's based on [express.js](http://expressjs.com/) and [node-zookeeper](https://github.com/yfinkelstein/node-zookeeper).It will display zookeeper's data as a lazy loading tree,and display every path's stat info and data;and you can create,edit or delete path if you logon. 4 | 5 | ## Requirement 6 | 7 | You must install node.js 0.8.x from https://github.com/joyent/node/tags and [npm](https://github.com/isaacs/npm). 8 | 9 | ## Configure 10 | First,you must install dependencies with npm 11 | 12 | npm install -d 13 | 14 | Then edit app.js to configure your zk hosts 15 | 16 | var zkclient = new ZkClient("localhost:2181"); 17 | 18 | Or you can pass it by enviroment variable: 19 | 20 | export ZK_HOST="localhost:2181" 21 | 22 | in `start.sh`. 23 | 24 | And edit user.json to configure your administrator account: 25 | 26 | { "name" : "password"} 27 | 28 | ## Run 29 | Type command to start app 30 | 31 | ./start.sh 32 | 33 | You can visit node-zk now at 34 | 35 | http://localhost:3000 36 | 37 | # License 38 | Apache License Version 2.0 39 | 40 | See LICENSE.txt file in the top level folder. 41 | 42 | # Author 43 | Dennis Zhuang(killme2008@gmail.com) 44 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | /* 3 | * Licensed to the Apache Software Foundation (ASF) under one 4 | * or more contributor license agreements. See the NOTICE file 5 | * distributed with this work for additional information 6 | * regarding copyright ownership. The ASF licenses this file 7 | * to you under the Apache License, Version 2.0 (the 8 | * "License"); you may not use this file except in compliance 9 | * with the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, 14 | * software distributed under the License is distributed on an 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | * KIND, either express or implied. See the License for the 17 | * specific language governing permissions and limitations 18 | * under the License. 19 | * 20 | * node-zk-browser 21 | * author :dennis (killme2008@gmail.com) 22 | * 23 | */ 24 | 25 | var express = require('express'); 26 | require('express-namespace'); 27 | var path=require('path'); 28 | var fs=require('fs'); 29 | var util=require('util'); 30 | var ZkClient=require('./zk.js').ZkClient; 31 | 32 | var port = process.env.ZK_BROWSER_PORT || 3000; 33 | var host = process.env.ZK_HOST || 'localhost:2181'; 34 | var zkclient = new ZkClient(host); 35 | var users = JSON.parse(fs.readFileSync(path.join(__dirname,'user.json'), 'utf8')); 36 | var app = express(); 37 | 38 | process.on('uncaughtException', function (err) { 39 | console.error('Caught exception: ' + err); 40 | }); 41 | 42 | 43 | // Configuration 44 | app.configure(function(){ 45 | app.set('views', __dirname + '/views'); 46 | app.set('view engine', 'ejs'); 47 | app.use(express.cookieParser()); 48 | app.use(express.session({ secret: "node zk browser" })); 49 | app.use(express.bodyParser()); 50 | app.use(express.methodOverride()); 51 | app.use(app.router); 52 | app.use(express.static(__dirname + '/public')); 53 | }); 54 | 55 | app.configure('development', function(){ 56 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 57 | }); 58 | 59 | app.configure('production', function(){ 60 | app.use(express.errorHandler()); 61 | }); 62 | 63 | // Routes 64 | 65 | app.get('/', function(req, res){ 66 | res.redirect("/node-zk"); 67 | }); 68 | 69 | app.namespace("/node-zk",function(){ 70 | 71 | //index 72 | app.get('/', function(req, res){ 73 | res.render('index', { }); 74 | }); 75 | 76 | //display tree 77 | app.get('/tree', function(req, res){ 78 | var path=req.query.path || "/"; 79 | res.render('tree', {layout:false,'path':path, 'host':host}); 80 | }); 81 | 82 | //login 83 | app.post("/login",function(req,res){ 84 | var user=req.body.user; 85 | if(users[user.name]==user.password){ 86 | req.session.user=user.name 87 | req.session.cookie.maxAge=5*60*1000; 88 | } 89 | res.redirect(req.header('Referer')); 90 | }); 91 | 92 | //delete 93 | app.post("/delete",function(req,res){ 94 | if(req.session.user){ 95 | var path=req.body.path; 96 | var version=Number(req.body.version); 97 | zkclient.zk.a_delete_(path,version,function(rc,err){ 98 | if(rc!=0) 99 | res.send(err); 100 | else 101 | res.send("Delete ok"); 102 | }); 103 | }else{ 104 | res.send("Please logon"); 105 | } 106 | }); 107 | 108 | //create view 109 | app.get("/create",function(req,res){ 110 | res.render("create",{layout:false,user: req.session.user}); 111 | }); 112 | 113 | //create 114 | app.post("/create",function(req,res){ 115 | if(req.session.user){ 116 | var path=req.body.path; 117 | var data=req.body.data; 118 | var flag=Number(req.body.flag); 119 | zkclient.zk.a_create(path,data,flag,function(rc,err,path){ 120 | if(rc!=0) 121 | res.send(err); 122 | else 123 | res.send("Create ok"); 124 | }); 125 | }else{ 126 | res.send("Please logon"); 127 | } 128 | }); 129 | 130 | //edit 131 | app.post("/edit",function(req,res){ 132 | if(req.session.user){ 133 | var path=req.body.path; 134 | var new_data=req.body.new_data; 135 | var version=Number(req.body.version); 136 | zkclient.zk.a_set(path,new_data,version,function(rc,err,stat){ 137 | if(rc!=0){ 138 | res.send(err); 139 | }else 140 | res.send("set ok"); 141 | }); 142 | }else{ 143 | res.send("Please logon"); 144 | } 145 | }); 146 | 147 | //query data 148 | app.get("/get",function(req,res){ 149 | var path=req.query.path || "/"; 150 | zkclient.zk.a_get(path,null,function(rc,err,stat,data){ 151 | if(rc!=0){ 152 | res.send(err); 153 | }else{ 154 | res.render("data",{ layout: false, 'stat':stat,'data':data,'path':path,'user': req.session.user}); 155 | } 156 | }); 157 | }); 158 | 159 | //query children 160 | app.get('/children',function(req,res){ 161 | var parenPath=req.query.path || '/'; 162 | zkclient.zk.a_get_children(parenPath,null,function(rc,error,children){ 163 | res.header("Content-Type","application/json"); 164 | var result=[]; 165 | if(rc==0){ 166 | children.forEach(function(child){ 167 | realPath=path.join(parenPath,child); 168 | result.unshift({ 169 | attributes:{"path":realPath,"rel":"chv"}, 170 | data:{ 171 | title : child,icon:"ou.png", attributes: { "href" : ("/node-zk/get?path="+realPath) } 172 | }, 173 | state:"closed" 174 | }); 175 | }); 176 | } 177 | res.send(result); 178 | }); 179 | }); 180 | }); 181 | 182 | app.listen(port); 183 | console.log("Express server listening on port %d", port); 184 | -------------------------------------------------------------------------------- /logs/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/killme2008/node-zk-browser/99786f4e72fce94862539d06c1840eb6322dd7e9/logs/.gitignore -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-zk-browser", 3 | "version": "0.0.2", 4 | "dependencies": { 5 | "ejs": ">= 0.7.2", 6 | "express": "3.x", 7 | "zookeeper":"*", 8 | "express-namespace":">=0.1.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /public/node-zk/jquery.tree.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jsTree 0.9.9a 3 | * http://jstree.com/ 4 | * 5 | * Copyright (c) 2009 Ivan Bozhanov (vakata.com) 6 | * 7 | * Dual licensed under the MIT and GPL licenses: 8 | * http://www.opensource.org/licenses/mit-license.php 9 | * http://www.gnu.org/licenses/gpl.html 10 | * 11 | * Date: 2009-10-06 12 | * 13 | */ 14 | 15 | (function($) { 16 | // jQuery plugin 17 | $.tree = { 18 | datastores : { }, 19 | plugins : { }, 20 | defaults : { 21 | data : { 22 | async : false, // Are async requests used to load open_branch contents 23 | type : "html", // One of included datastores 24 | opts : { method: "GET", url: false } // Options passed to datastore 25 | }, 26 | selected : false, // FALSE or STRING or ARRAY 27 | opened : [], // ARRAY OF INITIALLY OPENED NODES 28 | languages : [], // ARRAY of string values (which will be used as CSS classes - so they must be valid) 29 | ui : { 30 | dots : true, // BOOL - dots or no dots 31 | animation : 0, // INT - duration of open/close animations in miliseconds 32 | scroll_spd : 4, 33 | theme_path : false, // Path to the theme CSS file - if set to false and theme_name is not false - will lookup jstree-path-here/themes/theme-name-here/style.css 34 | theme_name : "default",// if set to false no theme will be loaded 35 | selected_parent_close : "select_parent", // false, "deselect", "select_parent" 36 | selected_delete : "select_previous" // false, "select_previous" 37 | }, 38 | types : { 39 | "default" : { 40 | clickable : true, // can be function 41 | renameable : true, // can be function 42 | deletable : true, // can be function 43 | creatable : true, // can be function 44 | draggable : true, // can be function 45 | max_children : -1, // -1 - not set, 0 - no children, 1 - one child, etc // can be function 46 | max_depth : -1, // -1 - not set, 0 - no children, 1 - one level of children, etc // can be function 47 | valid_children : "all", // all, none, array of values // can be function 48 | icon : { 49 | image : false, 50 | position : false 51 | } 52 | } 53 | }, 54 | rules : { 55 | multiple : false, // FALSE | CTRL | ON - multiple selection off/ with or without holding Ctrl 56 | multitree : "none", // all, none, array of tree IDs to accept from 57 | type_attr : "rel", // STRING attribute name (where is the type stored as string) 58 | createat : "bottom", // STRING (top or bottom) new nodes get inserted at top or bottom 59 | drag_copy : "ctrl", // FALSE | CTRL | ON - drag to copy off/ with or without holding Ctrl 60 | drag_button : "left", // left, right or both 61 | use_max_children : true, 62 | use_max_depth : true, 63 | 64 | max_children: -1, 65 | max_depth : -1, 66 | valid_children : "all" 67 | }, 68 | lang : { 69 | new_node : "New folder", 70 | loading : "Loading ..." 71 | }, 72 | callback : { 73 | beforechange: function(NODE,TREE_OBJ) { return true }, 74 | beforeopen : function(NODE,TREE_OBJ) { return true }, 75 | beforeclose : function(NODE,TREE_OBJ) { return true }, 76 | beforemove : function(NODE,REF_NODE,TYPE,TREE_OBJ) { return true }, 77 | beforecreate: function(NODE,REF_NODE,TYPE,TREE_OBJ) { return true }, 78 | beforerename: function(NODE,LANG,TREE_OBJ) { return true }, 79 | beforedelete: function(NODE,TREE_OBJ) { return true }, 80 | beforedata : function(NODE,TREE_OBJ) { return { id : $(NODE).attr("id") || 0 } }, // PARAMETERS PASSED TO SERVER 81 | ondata : function(DATA,TREE_OBJ) { return DATA; }, // modify data before parsing it 82 | onparse : function(STR,TREE_OBJ) { return STR; }, // modify string before visualizing it 83 | onhover : function(NODE,TREE_OBJ) { }, // node hovered 84 | onselect : function(NODE,TREE_OBJ) { }, // node selected 85 | ondeselect : function(NODE,TREE_OBJ) { }, // node deselected 86 | onchange : function(NODE,TREE_OBJ) { }, // focus changed 87 | onrename : function(NODE,TREE_OBJ,RB) { }, // node renamed 88 | onmove : function(NODE,REF_NODE,TYPE,TREE_OBJ,RB) { }, // move completed 89 | oncopy : function(NODE,REF_NODE,TYPE,TREE_OBJ,RB) { }, // copy completed 90 | oncreate : function(NODE,REF_NODE,TYPE,TREE_OBJ,RB) { }, // node created 91 | ondelete : function(NODE,TREE_OBJ,RB) { }, // node deleted 92 | onopen : function(NODE,TREE_OBJ) { }, // node opened 93 | onopen_all : function(TREE_OBJ) { }, // all nodes opened 94 | onclose_all : function(TREE_OBJ) { }, // all nodes closed 95 | onclose : function(NODE,TREE_OBJ) { }, // node closed 96 | error : function(TEXT,TREE_OBJ) { }, // error occured 97 | ondblclk : function(NODE,TREE_OBJ) { TREE_OBJ.toggle_branch.call(TREE_OBJ, NODE); TREE_OBJ.select_branch.call(TREE_OBJ, NODE); }, 98 | onrgtclk : function(NODE,TREE_OBJ,EV) { }, // right click - to prevent use: EV.preventDefault(); EV.stopPropagation(); return false 99 | onload : function(TREE_OBJ) { }, 100 | oninit : function(TREE_OBJ) { }, 101 | onfocus : function(TREE_OBJ) { }, 102 | ondestroy : function(TREE_OBJ) { }, 103 | onsearch : function(NODES, TREE_OBJ) { NODES.addClass("search"); }, 104 | ondrop : function(NODE,REF_NODE,TYPE,TREE_OBJ) { }, 105 | check : function(RULE,NODE,VALUE,TREE_OBJ) { return VALUE; }, 106 | check_move : function(NODE,REF_NODE,TYPE,TREE_OBJ) { return true; } 107 | }, 108 | plugins : { } 109 | }, 110 | 111 | create : function () { return new tree_component(); }, 112 | focused : function () { return tree_component.inst[tree_component.focused]; }, 113 | reference : function (obj) { 114 | var o = $(obj); 115 | if(!o.size()) o = $("#" + obj); 116 | if(!o.size()) return null; 117 | o = (o.is(".tree")) ? o.attr("id") : o.parents(".tree:eq(0)").attr("id"); 118 | return tree_component.inst[o] || null; 119 | }, 120 | rollback : function (data) { 121 | for(var i in data) { 122 | if(!data.hasOwnProperty(i)) continue; 123 | var tmp = tree_component.inst[i]; 124 | var lock = !tmp.locked; 125 | 126 | // if not locked - lock the tree 127 | if(lock) tmp.lock(true); 128 | // Cancel ongoing rename 129 | tmp.inp = false; 130 | tmp.container.html(data[i].html).find(".dragged").removeClass("dragged").end().find(".hover").removeClass("hover"); 131 | 132 | if(data[i].selected) { 133 | tmp.selected = $("#" + data[i].selected); 134 | tmp.selected_arr = []; 135 | tmp.container 136 | .find("a.clicked").each( function () { 137 | tmp.selected_arr.push(tmp.get_node(this)); 138 | }); 139 | } 140 | // if this function set the lock - unlock 141 | if(lock) tmp.lock(false); 142 | 143 | delete lock; 144 | delete tmp; 145 | } 146 | }, 147 | drop_mode : function (opts) { 148 | opts = $.extend(opts, { show : false, type : "default", str : "Foreign node" }); 149 | tree_component.drag_drop.foreign = true; 150 | tree_component.drag_drop.isdown = true; 151 | tree_component.drag_drop.moving = true; 152 | tree_component.drag_drop.appended = false; 153 | tree_component.drag_drop.f_type = opts.type; 154 | tree_component.drag_drop.f_data = opts; 155 | 156 | 157 | if(!opts.show) { 158 | tree_component.drag_drop.drag_help = false; 159 | tree_component.drag_drop.drag_node = false; 160 | } 161 | else { 162 | tree_component.drag_drop.drag_help = $("
"); 163 | tree_component.drag_drop.drag_node = tree_component.drag_drop.drag_help.find("li:eq(0)"); 164 | } 165 | if($.tree.drag_start !== false) $.tree.drag_start.call(null, false); 166 | }, 167 | drag_start : false, 168 | drag : false, 169 | drag_end : false 170 | }; 171 | $.fn.tree = function (opts) { 172 | return this.each(function() { 173 | var conf = $.extend({},opts); 174 | if(tree_component.inst && tree_component.inst[$(this).attr('id')]) tree_component.inst[$(this).attr('id')].destroy(); 175 | if(conf !== false) new tree_component().init(this, conf); 176 | }); 177 | }; 178 | 179 | // core 180 | function tree_component () { 181 | return { 182 | cntr : ++tree_component.cntr, 183 | settings : $.extend({},$.tree.defaults), 184 | 185 | init : function(elem, conf) { 186 | var _this = this; 187 | this.container = $(elem); 188 | if(this.container.size == 0) return false; 189 | tree_component.inst[this.cntr] = this; 190 | if(!this.container.attr("id")) this.container.attr("id","jstree_" + this.cntr); 191 | tree_component.inst[this.container.attr("id")] = tree_component.inst[this.cntr]; 192 | tree_component.focused = this.cntr; 193 | this.settings = $.extend(true, {}, this.settings, conf); 194 | 195 | // DEAL WITH LANGUAGE VERSIONS 196 | if(this.settings.languages && this.settings.languages.length) { 197 | this.current_lang = this.settings.languages[0]; 198 | var st = false; 199 | var id = "#" + this.container.attr("id"); 200 | for(var ln = 0; ln < this.settings.languages.length; ln++) { 201 | st = tree_component.add_css(id + " ." + this.settings.languages[ln]); 202 | if(st !== false) st.style.display = (this.settings.languages[ln] == this.current_lang) ? "" : "none"; 203 | } 204 | } 205 | else this.current_lang = false; 206 | // THEMES 207 | this.container.addClass("tree"); 208 | if(this.settings.ui.theme_name !== false) { 209 | if(this.settings.ui.theme_path === false) { 210 | $("script").each(function () { 211 | if(this.src.toString().match(/jquery\.tree.*?js$/)) { _this.settings.ui.theme_path = this.src.toString().replace(/jquery\.tree.*?js$/, "") + "themes/" + _this.settings.ui.theme_name + "/style.css"; return false; } 212 | }); 213 | } 214 | if(this.settings.ui.theme_path != "" && $.inArray(this.settings.ui.theme_path, tree_component.themes) == -1) { 215 | tree_component.add_sheet({ url : this.settings.ui.theme_path }); 216 | tree_component.themes.push(this.settings.ui.theme_path); 217 | } 218 | this.container.addClass("tree-" + this.settings.ui.theme_name); 219 | } 220 | // TYPE ICONS 221 | var type_icons = ""; 222 | for(var t in this.settings.types) { 223 | if(!this.settings.types.hasOwnProperty(t)) continue; 224 | if(!this.settings.types[t].icon) continue; 225 | if( this.settings.types[t].icon.image || this.settings.types[t].icon.position) { 226 | if(t == "default") type_icons += "#" + this.container.attr("id") + " li > a ins { "; 227 | else type_icons += "#" + this.container.attr("id") + " li[rel=" + t + "] > a ins { "; 228 | if(this.settings.types[t].icon.image) type_icons += " background-image:url(" + this.settings.types[t].icon.image + "); "; 229 | if(this.settings.types[t].icon.position) type_icons += " background-position:" + this.settings.types[t].icon.position + "; "; 230 | type_icons += "} "; 231 | } 232 | } 233 | if(type_icons != "") tree_component.add_sheet({ str : type_icons }); 234 | 235 | if(this.settings.rules.multiple) this.selected_arr = []; 236 | this.offset = false; 237 | this.hovered = false; 238 | this.locked = false; 239 | 240 | if(tree_component.drag_drop.marker === false) tree_component.drag_drop.marker = $("
").attr({ id : "jstree-marker" }).hide().appendTo("body"); 241 | this.callback("oninit", [this]); 242 | this.refresh(); 243 | this.attach_events(); 244 | this.focus(); 245 | }, 246 | refresh : function (obj) { 247 | if(this.locked) return this.error("LOCKED"); 248 | var _this = this; 249 | if(obj && !this.settings.data.async) obj = false; 250 | this.is_partial_refresh = obj ? true : false; 251 | 252 | // SAVE OPENED 253 | this.opened = Array(); 254 | if(this.settings.opened != false) { 255 | $.each(this.settings.opened, function (i, item) { 256 | if(this.replace(/^#/,"").length > 0) { _this.opened.push("#" + this.replace(/^#/,"")); } 257 | }); 258 | this.settings.opened = false; 259 | } 260 | else { 261 | this.container.find("li.open").each(function (i) { if(this.id) { _this.opened.push("#" + this.id); } }); 262 | } 263 | 264 | // SAVE SELECTED 265 | if(this.selected) { 266 | this.settings.selected = Array(); 267 | if(obj) { 268 | $(obj).find("li:has(a.clicked)").each(function () { 269 | if(this.id) _this.settings.selected.push("#" + this.id); 270 | }); 271 | } 272 | else { 273 | if(this.selected_arr) { 274 | $.each(this.selected_arr, function () { 275 | if(this.attr("id")) _this.settings.selected.push("#" + this.attr("id")); 276 | }); 277 | } 278 | else { 279 | if(this.selected.attr("id")) this.settings.selected.push("#" + this.selected.attr("id")); 280 | } 281 | } 282 | } 283 | else if(this.settings.selected !== false) { 284 | var tmp = Array(); 285 | if((typeof this.settings.selected).toLowerCase() == "object") { 286 | $.each(this.settings.selected, function () { 287 | if(this.replace(/^#/,"").length > 0) tmp.push("#" + this.replace(/^#/,"")); 288 | }); 289 | } 290 | else { 291 | if(this.settings.selected.replace(/^#/,"").length > 0) tmp.push("#" + this.settings.selected.replace(/^#/,"")); 292 | } 293 | this.settings.selected = tmp; 294 | } 295 | 296 | if(obj && this.settings.data.async) { 297 | this.opened = Array(); 298 | obj = this.get_node(obj); 299 | obj.find("li.open").each(function (i) { _this.opened.push("#" + this.id); }); 300 | if(obj.hasClass("open")) obj.removeClass("open").addClass("closed"); 301 | if(obj.hasClass("leaf")) obj.removeClass("leaf"); 302 | obj.children("ul:eq(0)").html(""); 303 | return this.open_branch(obj, true, function () { _this.reselect.apply(_this); }); 304 | } 305 | 306 | var _this = this; 307 | var _datastore = new $.tree.datastores[this.settings.data.type](); 308 | if(this.container.children("ul").size() == 0) { 309 | this.container.html(""); 310 | } 311 | _datastore.load(this.callback("beforedata",[false,this]),this,this.settings.data.opts,function(data) { 312 | data = _this.callback("ondata",[data, _this]); 313 | _datastore.parse(data,_this,_this.settings.data.opts,function(str) { 314 | str = _this.callback("onparse", [str, _this]); 315 | _this.container.empty().append($("