├── .gitignore
├── main.js
├── test
├── test_abn_tree.jade
├── test_abn_tree.html
├── tests_page.jade
├── tests_page.html
├── bs3_ng115_test_page.html
├── bs2_ng115_test_page.html
├── bs3_ng120_test_page.html
├── bs2_ng120_test_page.html
├── test_page.jade
├── test_page.js
└── test_page.coffee
├── package.json
├── temp
├── _template.html
└── _directive.coffee
├── src
├── abn_tree_template.jade
├── abn_tree_directive.coffee
└── abn_tree_directive.js
├── bower.json
├── LICENSE
├── dist
├── abn_tree.css
└── abn_tree_directive.js
├── Gruntfile.coffee
├── Gruntfile.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/abn_tree_directive');
2 |
--------------------------------------------------------------------------------
/test/test_abn_tree.jade:
--------------------------------------------------------------------------------
1 |
2 | // this is the url of my original page,
3 | // so I am forwarding it to the new demo page:
4 |
5 | html
6 | head
7 | meta(
8 | http-equiv="refresh"
9 | content="0; url=http://nickperkinslondon.github.io/angular-bootstrap-nav-tree/test/bs2_ng115_test_page.html"
10 | )
--------------------------------------------------------------------------------
/test/test_abn_tree.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-bootstrap-nav-tree",
3 | "version": "0.2.1",
4 | "dependencies": {},
5 | "main": "main.js",
6 | "devDependencies": {
7 | "grunt": "~0.4.1",
8 | "grunt-contrib-coffee": "~0.7.0",
9 | "grunt-contrib-watch": "~0.5.1",
10 | "grunt-contrib-jade": "~0.8.0",
11 | "grunt-string-replace": "~0.2.7"
12 | },
13 | "keywords":["angularjs","bootstrap","tree","widget"],
14 | "licence":"MIT"
15 | }
16 |
--------------------------------------------------------------------------------
/test/tests_page.jade:
--------------------------------------------------------------------------------
1 |
2 | html
3 | head
4 | link(rel="stylesheet",href="//netdna.bootstrapcdn.com/bootstrap/3.0.1/css/bootstrap.min.css")
5 |
6 | body
7 |
8 | h1 angular-bootstrap-nav-tree
9 | h2 test pages:
10 | br
11 | br
12 | a(href='bs2_ng115_test_page.html') Bootstrap 2 / Angular 1.1.5
13 | br
14 | a(href='bs3_ng115_test_page.html') Bootstrap 3 / Angular 1.1.5
15 | br
16 | a(href='bs2_ng120_test_page.html') Bootstrap 2 / Angular 1.2.0
17 | br
18 | a(href='bs3_ng120_test_page.html') Bootstrap 3 / Angular 1.2.0
19 |
20 |
21 |
--------------------------------------------------------------------------------
/temp/_template.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/abn_tree_template.jade:
--------------------------------------------------------------------------------
1 |
2 | ul.nav.nav-list.nav-pills.nav-stacked.abn-tree
3 |
4 | li.abn-tree-row(
5 | ng-repeat='row in tree_rows | filter:{visible:true} track by row.branch.uid'
6 | ng-animate="'abn-tree-animate'"
7 | ng-class="'level-' + {{ row.level }} + (row.branch.selected ? ' active':'') + ' ' +row.classes.join(' ')"
8 | )
9 |
10 | a(ng-click="user_clicks_branch(row.branch)")
11 |
12 | i.indented.tree-icon(
13 | ng-class="row.tree_icon"
14 | ng-click="row.branch.expanded = !row.branch.expanded"
15 | )
16 |
17 | span.indented.tree-label {{ row.label }}
18 |
--------------------------------------------------------------------------------
/test/tests_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | angular-bootstrap-nav-tree
8 | test pages:
9 |
10 |
11 | Bootstrap 2 / Angular 1.1.5
12 |
13 | Bootstrap 3 / Angular 1.1.5
14 |
15 | Bootstrap 2 / Angular 1.2.0
16 |
17 | Bootstrap 3 / Angular 1.2.0
18 |
19 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-bootstrap-nav-tree",
3 | "version": "0.2.0",
4 | "description":"A Tree Control for AngularJS, using CSS animation and Bootstrap style",
5 | "main":["dist/abn_tree_directive.js","dist/abn_tree.css"],
6 | "licence":"MIT",
7 | "keywords":["angularjs","bootstrap","tree","widget"],
8 | "authors":["Nick Perkins"],
9 | "homepage" :"https://github.com/nickperkinslondon/angular-bootstrap-nav-tree",
10 | "repository":{
11 | "type":"git",
12 | "url":"git://github.com/nickperkinslondon/angular-bootstrap-nav-tree"
13 | },
14 | "ignore": [
15 | "src",
16 | "temp",
17 | "test",
18 | ".gitignore",
19 | "Gruntfile.coffee",
20 | "Gruntfile.js",
21 | "bower.json",
22 | "package.json",
23 | "README.md"
24 | ],
25 | "dependencies": {
26 | "angular": ">=1.0",
27 | "bootstrap": ">=2.3"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Nick Perkins
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dist/abn_tree.css:
--------------------------------------------------------------------------------
1 | /*
2 | abn-tree.css
3 |
4 | style for the angular-bootstrap-nav-tree
5 | for both Bootstrap 2 and Bootstrap 3
6 |
7 | */
8 |
9 |
10 |
11 | /* ------------------------------------------
12 | AngularJS Animations...
13 |
14 | The first selector is for Angular 1.1.5
15 | The second selector is for Angular 1.2.0
16 |
17 | */
18 | .abn-tree-animate-enter,
19 | li.abn-tree-row.ng-enter {
20 | transition: 200ms linear all;
21 | position: relative;
22 | display: block;
23 | opacity: 0;
24 | max-height:0px;
25 | }
26 | .abn-tree-animate-enter.abn-tree-animate-enter-active,
27 | li.abn-tree-row.ng-enter-active{
28 | opacity: 1;
29 | max-height:30px;
30 | }
31 |
32 | .abn-tree-animate-leave,
33 | li.abn-tree-row.ng-leave {
34 | transition: 200ms linear all;
35 | position: relative;
36 | display: block;
37 | height:30px;
38 | max-height: 30px;
39 | opacity: 1;
40 | }
41 | .abn-tree-animate-leave.abn-tree-animate-leave-active,
42 | li.abn-tree-row.ng-leave-active {
43 | height: 0px;
44 | max-height:0px;
45 | opacity: 0;
46 | }
47 |
48 |
49 | /*
50 | ------------------------------------------
51 | Angular 1.2.0 Animation
52 | */
53 |
54 |
55 | .abn-tree-animate.ng-enter{
56 |
57 | }
58 | .abn-tree-animate.ng-enter{
59 |
60 | }
61 |
62 |
63 |
64 |
65 | /*
66 | end animation stuff
67 | -----------------------------------------
68 | begin normal css stuff
69 | */
70 | ul.abn-tree li.abn-tree-row {
71 | padding: 0px;
72 | margin:0px;
73 | }
74 |
75 | ul.abn-tree li.abn-tree-row a {
76 | padding: 3px 10px;
77 | }
78 |
79 | ul.abn-tree i.indented {
80 | padding: 2px;
81 | }
82 |
83 | .abn-tree {
84 | cursor: pointer;
85 | }
86 | ul.nav.abn-tree .level-1 .indented {
87 | position: relative;
88 | left: 0px;
89 | }
90 | ul.nav.abn-tree .level-2 .indented {
91 | position: relative;
92 | left: 20px;
93 | }
94 | ul.nav.abn-tree .level-3 .indented {
95 | position: relative;
96 | left: 40px;
97 | }
98 | ul.nav.abn-tree .level-4 .indented {
99 | position: relative;
100 | left: 60px;
101 | }
102 | ul.nav.abn-tree .level-5 .indented {
103 | position: relative;
104 | left: 80px;
105 | }
106 | ul.nav.abn-tree .level-6 .indented {
107 | position: relative;
108 | left: 100px;
109 | }
110 | ul.nav.nav-list.abn-tree .level-7 .indented {
111 | position: relative;
112 | left: 120px;
113 | }
114 | ul.nav.nav-list.abn-tree .level-8 .indented {
115 | position: relative;
116 | left: 140px;
117 | }
118 | ul.nav.nav-list.abn-tree .level-9 .indented {
119 | position: relative;
120 | left: 160px;
121 | }
122 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 |
2 | module.exports = (grunt)->
3 |
4 | grunt.initConfig
5 |
6 | pkg: grunt.file.readJSON 'package.json'
7 |
8 | jade:
9 | dev:
10 | options:
11 | pretty:true
12 | files:
13 | 'temp/_template.html':'src/abn_tree_template.jade'
14 | 'test/tests_page.html':'test/tests_page.jade'
15 |
16 | #
17 | # Generate 4 test pages, for all combinations of:
18 | #
19 | # Bootstrap 2 and 3
20 | # Angular 1.1.5 and 1.2.0
21 | #
22 |
23 | bs2_ng115_test_page:
24 | files:
25 | 'test/bs2_ng115_test_page.html':'test/test_page.jade'
26 | options:
27 | pretty:true
28 | data:
29 | bs:"2"
30 | ng:"1.1.5"
31 |
32 | bs3_ng115_test_page:
33 | files:
34 | 'test/bs3_ng115_test_page.html':'test/test_page.jade'
35 | options:
36 | pretty:true
37 | data:
38 | bs:"3"
39 | ng:"1.1.5"
40 |
41 | bs2_ng120_test_page:
42 | files:
43 | 'test/bs2_ng120_test_page.html':'test/test_page.jade'
44 | options:
45 | pretty:true
46 | data:
47 | bs:"2"
48 | ng:"1.2.12"
49 |
50 | bs3_ng120_test_page:
51 | files:
52 | 'test/bs3_ng120_test_page.html':'test/test_page.jade'
53 | options:
54 | pretty:true
55 | data:
56 | bs:"3"
57 | ng:"1.2.12"
58 |
59 |
60 | "string-replace":
61 | dev:
62 | files:
63 | #
64 | # substitute the "template html" into an intermediate coffeescript file
65 | # ( to take advantage of triple-quoted strings )
66 | #
67 | 'temp/_directive.coffee':'src/abn_tree_directive.coffee'
68 | options:
69 | replacements:[
70 | pattern: "{html}"
71 | replacement_old: "i am the replacement! "
72 | replacement: (match, p1, offset, string)->
73 | grunt.file.read('temp/_template.html')
74 | ]
75 |
76 | coffee:
77 | dev:
78 | options:
79 | bare:false
80 | files:
81 | #
82 | # the _temp.coffee file has the "template html" baked-in by Grunt
83 | #
84 | 'dist/abn_tree_directive.js':'temp/_directive.coffee'
85 | 'test/test_page.js':'test/test_page.coffee'
86 |
87 |
88 |
89 |
90 | watch:
91 |
92 | jade:
93 | files:['**/*.jade']
94 | tasks:['jade','string-replace']
95 | options:
96 | livereload:true
97 |
98 | css:
99 | files:['**/*.css']
100 | tasks:[]
101 | options:
102 | livereload:true
103 |
104 | coffee:
105 | files:['**/*.coffee']
106 | tasks:['jade','string-replace','coffee']
107 | options:
108 | livereload:true
109 |
110 | grunt.loadNpmTasks 'grunt-contrib-jade'
111 | grunt.loadNpmTasks 'grunt-contrib-coffee'
112 | grunt.loadNpmTasks 'grunt-contrib-watch'
113 | grunt.loadNpmTasks 'grunt-string-replace'
114 |
115 |
116 | grunt.registerTask 'default', ['jade','string-replace','coffee','watch']
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.6.3
2 | module.exports = function(grunt) {
3 | grunt.initConfig({
4 | pkg: grunt.file.readJSON('package.json'),
5 | jade: {
6 | dev: {
7 | options: {
8 | pretty: true
9 | },
10 | files: {
11 | 'temp/_template.html': 'src/abn_tree_template.jade',
12 | 'test/tests_page.html': 'test/tests_page.jade'
13 | }
14 | },
15 | bs2_ng115_test_page: {
16 | files: {
17 | 'test/bs2_ng115_test_page.html': 'test/test_page.jade'
18 | },
19 | options: {
20 | pretty: true,
21 | data: {
22 | bs: "2",
23 | ng: "1.1.5"
24 | }
25 | }
26 | },
27 | bs3_ng115_test_page: {
28 | files: {
29 | 'test/bs3_ng115_test_page.html': 'test/test_page.jade'
30 | },
31 | options: {
32 | pretty: true,
33 | data: {
34 | bs: "3",
35 | ng: "1.1.5"
36 | }
37 | }
38 | },
39 | bs2_ng120_test_page: {
40 | files: {
41 | 'test/bs2_ng120_test_page.html': 'test/test_page.jade'
42 | },
43 | options: {
44 | pretty: true,
45 | data: {
46 | bs: "2",
47 | ng: "1.2.12"
48 | }
49 | }
50 | },
51 | bs3_ng120_test_page: {
52 | files: {
53 | 'test/bs3_ng120_test_page.html': 'test/test_page.jade'
54 | },
55 | options: {
56 | pretty: true,
57 | data: {
58 | bs: "3",
59 | ng: "1.2.12"
60 | }
61 | }
62 | }
63 | },
64 | "string-replace": {
65 | dev: {
66 | files: {
67 | 'temp/_directive.coffee': 'src/abn_tree_directive.coffee'
68 | },
69 | options: {
70 | replacements: [
71 | {
72 | pattern: "{html}",
73 | replacement_old: "i am the replacement! ",
74 | replacement: function(match, p1, offset, string) {
75 | return grunt.file.read('temp/_template.html');
76 | }
77 | }
78 | ]
79 | }
80 | }
81 | },
82 | coffee: {
83 | dev: {
84 | options: {
85 | bare: false
86 | },
87 | files: {
88 | 'dist/abn_tree_directive.js': 'temp/_directive.coffee',
89 | 'test/test_page.js': 'test/test_page.coffee'
90 | }
91 | }
92 | },
93 | watch: {
94 | jade: {
95 | files: ['**/*.jade'],
96 | tasks: ['jade', 'string-replace'],
97 | options: {
98 | livereload: true
99 | }
100 | },
101 | css: {
102 | files: ['**/*.css'],
103 | tasks: [],
104 | options: {
105 | livereload: true
106 | }
107 | },
108 | coffee: {
109 | files: ['**/*.coffee'],
110 | tasks: ['jade', 'string-replace', 'coffee'],
111 | options: {
112 | livereload: true
113 | }
114 | }
115 | }
116 | });
117 | grunt.loadNpmTasks('grunt-contrib-jade');
118 | grunt.loadNpmTasks('grunt-contrib-coffee');
119 | grunt.loadNpmTasks('grunt-contrib-watch');
120 | grunt.loadNpmTasks('grunt-string-replace');
121 | return grunt.registerTask('default', ['jade', 'string-replace', 'coffee', 'watch']);
122 | };
123 |
--------------------------------------------------------------------------------
/test/bs3_ng115_test_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | angular-bootstrap-nav-tree
24 |
25 |
26 |
27 | by Nick Perkins
28 | The code is on Github
29 |
30 |
31 |
32 |
46 |
47 |
48 |
49 | Bootstrap 3
50 | Angular 1.1.5
51 |
52 |
53 |
54 |
55 |
56 | Change The Tree Definition
57 |
58 | Load Tree Data Asynchronously
59 |
60 | Test the Tree Control API:
61 |
62 | First Branch
63 |
64 | Next Sibling
65 | Prev Sibling
66 |
67 | Next Branch
68 | Prev Branch
69 |
70 | Parent
71 |
72 | Expand
73 | Collapse
74 | Expand All
75 | Collapse All
76 |
77 | Add Branch
78 |
79 |
80 |
81 |
...loading...
82 |
83 |
84 |
85 |
86 | {{ output }}
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/test/bs2_ng115_test_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | angular-bootstrap-nav-tree
24 |
25 |
26 |
27 | by Nick Perkins
28 | The code is on Github
29 |
30 |
31 |
32 |
46 |
47 |
48 |
49 | Bootstrap 2
50 | Angular 1.1.5
51 |
52 |
53 |
54 |
55 |
56 | Change The Tree Definition
57 |
58 | Load Tree Data Asynchronously
59 |
60 | Test the Tree Control API:
61 |
62 | First Branch
63 |
64 | Next Sibling
65 | Prev Sibling
66 |
67 | Next Branch
68 | Prev Branch
69 |
70 | Parent
71 |
72 | Expand
73 | Collapse
74 | Expand All
75 | Collapse All
76 |
77 | Add Branch
78 |
79 |
80 |
81 |
...loading...
82 |
83 |
84 |
85 |
86 | {{ output }}
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | angular-bootstrap-nav-tree
2 | ==========================
3 |
4 | This is a Tree directive for Angular JS apps that use Bootstrap CSS.
5 |
6 | example: http://nickperkinslondon.github.io/angular-bootstrap-nav-tree/test/bs3_ng120_test_page.html
7 |
8 | The style is completely Bootstrap because the tree is actually just a Bootstrap "nav" list, with a few changes: Indentation is added, expand/collapse icons are added, and Angular CSS animations are used during expand/collapse.
9 |
10 | The abn-tree now works Bootsrap 2, or Bootstrap 3, and with Angular 1.1.5 or 1.2.0
11 |
12 | The normal Glyphicons work well, but they appear black instead of blue. Alternatively, you can use the Font Awesome icons, which look even better, and match the blue color of the text.
13 |
14 | You can change the icons used by specifying them in html attributes.
15 |
16 | This tree is developed using CoffeeScript and Jade, but you don't need to be using either of those to use this tree -- you just have to be using Angular and Bootsrap.
17 |
18 |
19 | How to use it:
20 | Just include the 2 files from "dist",
21 |
22 | abn_tree_directive.js
23 | abn_tree.css
24 |
25 | Add `'angularBootstrapNavTree'` to your module's list of dependencies.
26 |
27 | Then put an `` directive in your HTML.
28 | ( see the example in "test" )
29 |
30 | At a miniumum, you must supply `tree-data` :
31 |
32 |
33 |
34 | But there are other attributes to customize the tree:
35 |
36 |
45 | >
46 |
47 | The example uses Font-Awesome 3, but Font-Awsome 4 also works.
48 | Use the following syntax:
49 |
50 | icon-leaf = "fa fa-file"
51 |
52 | ( in general, use spaces to apply multiple classes to icon elements )
53 |
54 |
55 | The data to create the tree is defined in your controller, and could be as simple as this:
56 |
57 | $scope.my_data = [{
58 | label: 'Languages',
59 | children: ['Jade','Less','Coffeescript']
60 | }]
61 |
62 | There is a long-form for elements, in which each node is an object with a "label", and optionally other stuff like "data", and "children".
63 | There is a short-form for listing nodes children (as used for "children" above), where the "children" is just a list of strings.
64 | If you use the short-form for listing elements, then your "on-select" function will have to act based only upon the "branch.label". If you use the
65 | long-form, where is branch is an object, then you can also attach "data" to a branch.
66 |
67 | If you would like to add classes to a certain node, give it an array of classes like so:
68 |
69 | $scope.my_data = [{
70 | label: 'Languages',
71 | children: ['Jade','Less','Coffeescript']
72 | classes: ["special", "red"]
73 | }]
74 |
75 | Each element without children, or leaf, is automatically given a leaf class. If you would like to force certain nodes not to be leaves (won't get leaf class and will show expand/collapse icons), set noLeaf to true in a long-form listing like so:
76 |
77 | {
78 | label: 'Coffeescript',
79 | noLeaf: true
80 | }
81 |
82 | You can supply a single default "on-select" function for the whole tree -- it will be called whenever a branch is selected:
83 |
84 | $scope.my_tree_hander = function(branch){...}
85 |
86 |
87 | Or, you can put a custom "on-select" function on an individual branch:
88 |
89 | $scope.my_data = [{
90 | label: 'Languages',
91 | onSelect: function(branch){...},
92 | children: ['Jade','Less','Coffeescript']
93 | }]
94 |
95 | Each branch can have a "data" element which you can use to hold whatever data you want. It is not touched by the tree, and it is available to your "on-select" function as "branch.data". In the example, in the "test" folder, this technique is used in "my_tree_handler" to add extra info to "Dog","Cat", and "Hippo".
96 |
97 | Warning: If you attach extra attributes directly to a branch (instead of to "branch.data"), they could conflict with the internal workings of the tree, which adds branch attributes at runtime, like "expanded" and "selected".
98 |
99 | Tree-Control API:
100 | If you pass an empty object to the tree as "tree-control", it will be populated with a set of functions for navigating and controlling the tree. See the example page for a demo...
101 |
--------------------------------------------------------------------------------
/test/bs3_ng120_test_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | angular-bootstrap-nav-tree
25 |
26 |
27 |
28 | by Nick Perkins
29 | The code is on Github
30 |
31 |
32 |
33 |
47 |
48 |
49 |
50 | Bootstrap 3
51 | Angular 1.2.12
52 |
53 |
54 |
55 |
56 |
57 | Change The Tree Definition
58 |
59 | Load Tree Data Asynchronously
60 |
61 | Test the Tree Control API:
62 |
63 | First Branch
64 |
65 | Next Sibling
66 | Prev Sibling
67 |
68 | Next Branch
69 | Prev Branch
70 |
71 | Parent
72 |
73 | Expand
74 | Collapse
75 | Expand All
76 | Collapse All
77 |
78 | Add Branch
79 |
80 |
81 |
82 |
...loading...
83 |
84 |
85 |
86 |
87 | {{ output }}
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/test/bs2_ng120_test_page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | angular-bootstrap-nav-tree
25 |
26 |
27 |
28 | by Nick Perkins
29 | The code is on Github
30 |
31 |
32 |
33 |
47 |
48 |
49 |
50 | Bootstrap 2
51 | Angular 1.2.12
52 |
53 |
54 |
55 |
56 |
57 | Change The Tree Definition
58 |
59 | Load Tree Data Asynchronously
60 |
61 | Test the Tree Control API:
62 |
63 | First Branch
64 |
65 | Next Sibling
66 | Prev Sibling
67 |
68 | Next Branch
69 | Prev Branch
70 |
71 | Parent
72 |
73 | Expand
74 | Collapse
75 | Expand All
76 | Collapse All
77 |
78 | Add Branch
79 |
80 |
81 |
82 |
...loading...
83 |
84 |
85 |
86 |
87 | {{ output }}
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/test/test_page.jade:
--------------------------------------------------------------------------------
1 | !!!5
2 | html(ng-app='AbnTest')
3 | head
4 |
5 |
6 | ///
7 | // Bootstrap 2 or Bootstrap 3 ?
8 | //
9 | if bs == "2"
10 | link(rel="stylesheet",href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css")
11 | if bs == "3"
12 | link(rel="stylesheet",href="//netdna.bootstrapcdn.com/bootstrap/3.0.1/css/bootstrap.min.css")
13 |
14 |
15 | //
16 | // Angular 1.1.5 or 1.2.12 ?
17 | //
18 | if ng == "1.1.5"
19 | script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js")
20 | if ng == "1.2.12"
21 | script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js")
22 | script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular-animate.js")
23 |
24 |
25 | // Font Awesome (optional)
26 | //- link(href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css",rel="stylesheet")
27 | //- link(href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css",rel="stylesheet")
28 |
29 |
30 | // Live Reload ( for development )
31 | //- script(src="http://localhost:35729/livereload.js")
32 |
33 |
34 | ///
35 | // abn-tree ( the thing we are testing )
36 | //
37 | script(src="../dist/abn_tree_directive.js")
38 | link(rel='stylesheet',href='../dist/abn_tree.css')
39 |
40 |
41 | // js for this test page:
42 | script(src="test_page.js")
43 |
44 |
45 |
46 | body(
47 | ng-controller='AbnTestController'
48 | style="margin:20px"
49 | )
50 |
51 | h2 angular-bootstrap-nav-tree
52 | table
53 | tr
54 | td(style='vertical-align:top;padding:20px')
55 | //- Left Side
56 |
57 | h6 by Nick Perkins
58 | a(href='https://github.com/nickperkinslondon/angular-bootstrap-nav-tree') The code is on Github
59 |
60 |
61 | // All 4 of these pages are generated ( by Grunt )
62 | // from this one jade file
63 | hr
64 |
65 | ul.nav.nav-list.list-group
66 | li
67 | a(href='bs2_ng115_test_page.html') Bootstrap 2 / Angular 1.1
68 | li
69 | a(href='bs3_ng115_test_page.html') Bootstrap 3 / Angular 1.1
70 | li
71 | a(href='bs2_ng120_test_page.html') Bootstrap 2 / Angular 1.2
72 | li
73 | a(href='bs3_ng120_test_page.html') Bootstrap 3 / Angular 1.2
74 |
75 | td(style='vertical-align:top')
76 | //- Right Side
77 |
78 |
79 | hr
80 | h4 Bootstrap #{bs}
81 | h4 Angular #{ng}
82 | hr
83 |
84 |
85 | table
86 | tr
87 | td(style='vertical-align:top')
88 |
89 | br
90 | button.btn.btn-default.btn-sm(
91 | ng-click="try_changing_the_tree_data()"
92 | ) Change The Tree Definition
93 |
94 | br
95 | button.btn.btn-default.btn-sm(
96 | ng-click="try_async_load()"
97 | ) Load Tree Data Asynchronously
98 |
99 | hr
100 | h5 Test the Tree Control API:
101 |
102 | br
103 | button.btn.btn-default.btn-sm(
104 | ng-click='my_tree.select_first_branch()'
105 | ) First Branch
106 | br
107 | button.btn.btn-default.btn-sm(
108 | ng-click='my_tree.select_next_sibling()'
109 | ) Next Sibling
110 |
111 | button.btn.btn-default.btn-sm(
112 | ng-click='my_tree.select_prev_sibling()'
113 | ) Prev Sibling
114 |
115 | br
116 | button.btn.btn-default.btn-sm(
117 | ng-click='my_tree.select_next_branch()'
118 | ) Next Branch
119 |
120 |
121 | button.btn.btn-default.btn-sm(
122 | ng-click='my_tree.select_prev_branch()'
123 | ) Prev Branch
124 |
125 | br
126 | button.btn.btn-default.btn-sm(
127 | ng-click='my_tree.select_parent_branch()'
128 | ) Parent
129 |
130 | hr
131 | button.btn.btn-default.btn-sm(
132 | ng-click='my_tree.expand_branch()'
133 | ) Expand
134 |
135 | button.btn.btn-default.btn-sm(
136 | ng-click='my_tree.collapse_branch()'
137 | ) Collapse
138 |
139 | button.btn.btn-default.btn-sm(
140 | ng-click='my_tree.expand_all()'
141 | ) Expand All
142 |
143 | button.btn.btn-default.btn-sm(
144 | ng-click='my_tree.collapse_all()'
145 | ) Collapse All
146 |
147 | hr
148 | button.btn.btn-default.btn-sm(
149 | ng-click='try_adding_a_branch()'
150 | ) Add Branch
151 |
152 |
153 |
154 | td(style='vertical-align:top')
155 | div(
156 | style='width:250px;margin-left:100px;background:whitesmoke;border:1px solid lightgray;border-radius:5px;'
157 | )
158 |
159 | span(ng-if='doing_async') ...loading...
160 |
161 | abn-tree(
162 | tree-data = "my_data"
163 | tree-control = "my_tree"
164 | on-select = "my_tree_handler(branch)"
165 | expand-level = "2"
166 | initial-selection = "Granny Smith"
167 | )
168 |
169 | td(style="padding:20px;vertical-align:top;")
170 | .alert.alert-warning(
171 | style="width:300px"
172 | ) {{ output }}
173 |
--------------------------------------------------------------------------------
/test/test_page.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var app, deps;
3 |
4 | deps = ['angularBootstrapNavTree'];
5 |
6 | if (angular.version.full.indexOf("1.2") >= 0) {
7 | deps.push('ngAnimate');
8 | }
9 |
10 | app = angular.module('AbnTest', deps);
11 |
12 | app.controller('AbnTestController', function($scope, $timeout) {
13 | var apple_selected, tree, treedata_avm, treedata_geography;
14 | $scope.my_tree_handler = function(branch) {
15 | var _ref;
16 | $scope.output = "You selected: " + branch.label;
17 | if ((_ref = branch.data) != null ? _ref.description : void 0) {
18 | return $scope.output += '(' + branch.data.description + ')';
19 | }
20 | };
21 | apple_selected = function(branch) {
22 | return $scope.output = "APPLE! : " + branch.label;
23 | };
24 | treedata_avm = [
25 | {
26 | label: 'Animal',
27 | children: [
28 | {
29 | label: 'Dog',
30 | data: {
31 | description: "man's best friend"
32 | }
33 | }, {
34 | label: 'Cat',
35 | data: {
36 | description: "Felis catus"
37 | }
38 | }, {
39 | label: 'Hippopotamus',
40 | data: {
41 | description: "hungry, hungry"
42 | }
43 | }, {
44 | label: 'Chicken',
45 | children: ['White Leghorn', 'Rhode Island Red', 'Jersey Giant']
46 | }
47 | ]
48 | }, {
49 | label: 'Vegetable',
50 | data: {
51 | definition: "A plant or part of a plant used as food, typically as accompaniment to meat or fish, such as a cabbage, potato, carrot, or bean.",
52 | data_can_contain_anything: true
53 | },
54 | onSelect: function(branch) {
55 | return $scope.output = "Vegetable: " + branch.data.definition;
56 | },
57 | children: [
58 | {
59 | label: 'Oranges'
60 | }, {
61 | label: 'Apples',
62 | children: [
63 | {
64 | label: 'Granny Smith',
65 | onSelect: apple_selected
66 | }, {
67 | label: 'Red Delicous',
68 | onSelect: apple_selected
69 | }, {
70 | label: 'Fuji',
71 | onSelect: apple_selected
72 | }
73 | ]
74 | }
75 | ]
76 | }, {
77 | label: 'Mineral',
78 | children: [
79 | {
80 | label: 'Rock',
81 | children: ['Igneous', 'Sedimentary', 'Metamorphic']
82 | }, {
83 | label: 'Metal',
84 | children: ['Aluminum', 'Steel', 'Copper']
85 | }, {
86 | label: 'Plastic',
87 | children: [
88 | {
89 | label: 'Thermoplastic',
90 | children: ['polyethylene', 'polypropylene', 'polystyrene', ' polyvinyl chloride']
91 | }, {
92 | label: 'Thermosetting Polymer',
93 | children: ['polyester', 'polyurethane', 'vulcanized rubber', 'bakelite', 'urea-formaldehyde']
94 | }
95 | ]
96 | }
97 | ]
98 | }
99 | ];
100 | treedata_geography = [
101 | {
102 | label: 'North America',
103 | children: [
104 | {
105 | label: 'Canada',
106 | children: ['Toronto', 'Vancouver']
107 | }, {
108 | label: 'USA',
109 | children: ['New York', 'Los Angeles']
110 | }, {
111 | label: 'Mexico',
112 | children: ['Mexico City', 'Guadalajara']
113 | }
114 | ]
115 | }, {
116 | label: 'South America',
117 | children: [
118 | {
119 | label: 'Venezuela',
120 | children: ['Caracas', 'Maracaibo']
121 | }, {
122 | label: 'Brazil',
123 | children: ['Sao Paulo', 'Rio de Janeiro']
124 | }, {
125 | label: 'Argentina',
126 | children: ['Buenos Aires', 'Cordoba']
127 | }
128 | ]
129 | }
130 | ];
131 | $scope.my_data = treedata_avm;
132 | $scope.try_changing_the_tree_data = function() {
133 | if ($scope.my_data === treedata_avm) {
134 | return $scope.my_data = treedata_geography;
135 | } else {
136 | return $scope.my_data = treedata_avm;
137 | }
138 | };
139 | $scope.my_tree = tree = {};
140 | $scope.try_async_load = function() {
141 | $scope.my_data = [];
142 | $scope.doing_async = true;
143 | return $timeout(function() {
144 | if (Math.random() < 0.5) {
145 | $scope.my_data = treedata_avm;
146 | } else {
147 | $scope.my_data = treedata_geography;
148 | }
149 | $scope.doing_async = false;
150 | return tree.expand_all();
151 | }, 1000);
152 | };
153 | return $scope.try_adding_a_branch = function() {
154 | var b;
155 | b = tree.get_selected_branch();
156 | return tree.add_branch(b, {
157 | label: 'New Branch',
158 | data: {
159 | something: 42,
160 | "else": 43
161 | }
162 | });
163 | };
164 | });
165 |
166 | }).call(this);
167 |
--------------------------------------------------------------------------------
/test/test_page.coffee:
--------------------------------------------------------------------------------
1 |
2 |
3 | deps = ['angularBootstrapNavTree']
4 | if angular.version.full.indexOf("1.2")>=0
5 | deps.push('ngAnimate')
6 |
7 |
8 | app = angular.module 'AbnTest', deps
9 | app.controller 'AbnTestController',($scope,$timeout)->
10 |
11 | #
12 | # a default "on-select" handler can be specified
13 | # for the tree ( as attribute "on-select" )
14 | #
15 | $scope.my_tree_handler = (branch)->
16 | $scope.output = "You selected: "+branch.label
17 | if branch.data?.description
18 | $scope.output += '('+branch.data.description+')'
19 | #
20 | # This example handler just sets "output",
21 | # ...but your handler could do anything here...
22 | #
23 |
24 |
25 | #
26 | # Each branch can define an "on-select" handler,
27 | # which will be called instead of the default handler
28 | #
29 |
30 | #
31 | # a single handler can be used on several branches, like this:
32 | #
33 | apple_selected = (branch)->
34 | $scope.output = "APPLE! : "+branch.label
35 | #
36 | # ( your handler can do anything here )
37 | #
38 |
39 |
40 | #
41 | # Example TREE DATA : Animal,Vegetable,Mineral
42 | #
43 | # Each branch can have the following attributes:
44 | #
45 | # label : the displayed text for the branch
46 | # children : an array of branches ( or array of strings )
47 | # onSelect : a function to run when branch is selected
48 | # data : a place to put your own data -- can be anything
49 | #
50 |
51 | treedata_avm = [
52 | label:'Animal'
53 | children:[
54 | label:'Dog'
55 | data:
56 | #
57 | # "data" is yours -- put anything in here
58 | # you can read it back in your on-select handler
59 | # as "branch.data"
60 | #
61 | description:"man's best friend"
62 | ,
63 | label:'Cat'
64 | data:
65 | description:"Felis catus"
66 | ,
67 | label:'Hippopotamus'
68 | data:
69 | description:"hungry, hungry"
70 | ,
71 | label:'Chicken'
72 | children:['White Leghorn','Rhode Island Red','Jersey Giant']
73 | ]
74 | ,
75 |
76 | label:'Vegetable'
77 | data:
78 | definition:"A plant or part of a plant used as food, typically as accompaniment to meat or fish, such as a cabbage, potato, carrot, or bean."
79 | data_can_contain_anything:true
80 |
81 | onSelect:(branch)->
82 | # special "on-select" function for this branch
83 | $scope.output = "Vegetable: "+branch.data.definition
84 |
85 |
86 | children:[
87 | label:'Oranges'
88 | ,
89 | label:'Apples'
90 | children:[
91 | label:'Granny Smith'
92 | onSelect:apple_selected
93 | ,
94 | label:'Red Delicous'
95 | onSelect:apple_selected
96 | ,
97 | label:'Fuji'
98 | onSelect:apple_selected
99 | ]
100 | ]
101 | ,
102 | label:'Mineral'
103 | children:[
104 | label:'Rock'
105 | # children can be simply a list of strings
106 | # if you are in a hurry
107 | children:['Igneous','Sedimentary','Metamorphic']
108 | ,
109 | label:'Metal'
110 | children:['Aluminum','Steel','Copper']
111 | ,
112 | label:'Plastic'
113 | children:[
114 | label:'Thermoplastic'
115 | children:['polyethylene', 'polypropylene', 'polystyrene',' polyvinyl chloride']
116 | ,
117 | label:'Thermosetting Polymer'
118 | children:['polyester','polyurethane','vulcanized rubber','bakelite','urea-formaldehyde']
119 | ,
120 | ]
121 | ]
122 | ]
123 |
124 | treedata_geography = [
125 | label:'North America'
126 | children:[
127 | label:'Canada'
128 | children:['Toronto','Vancouver']
129 | ,
130 | label:'USA'
131 | children:['New York','Los Angeles']
132 | ,
133 | label:'Mexico'
134 | children:['Mexico City','Guadalajara']
135 | ]
136 | ,
137 | label:'South America'
138 | children:[
139 | label:'Venezuela'
140 | children:['Caracas','Maracaibo']
141 | ,
142 | label:'Brazil'
143 | children:['Sao Paulo','Rio de Janeiro']
144 | ,
145 | label:'Argentina'
146 | children:['Buenos Aires','Cordoba']
147 | ]
148 | ]
149 |
150 |
151 | $scope.my_data = treedata_avm
152 | $scope.try_changing_the_tree_data = ()->
153 | #
154 | # switch between 2 sets of "treedata"
155 | #
156 | if $scope.my_data is treedata_avm
157 | $scope.my_data = treedata_geography
158 | else
159 | $scope.my_data = treedata_avm
160 |
161 |
162 | #
163 | # TREE-CONTROL: the API for controlling the tree
164 | #
165 | $scope.my_tree = tree = {}
166 | # just create an empty object, and pass it to the abn-tree as "tree-control"
167 | # ...it will be populated with Tree API functions
168 |
169 |
170 | $scope.try_async_load = ()->
171 | $scope.my_data = []
172 | $scope.doing_async = true
173 | $timeout ->
174 | if Math.random() < 0.5
175 | $scope.my_data = treedata_avm
176 | else
177 | $scope.my_data = treedata_geography
178 | $scope.doing_async = false
179 | tree.expand_all()
180 | ,1000
181 |
182 | $scope.try_adding_a_branch = ->
183 | b = tree.get_selected_branch()
184 | tree.add_branch b,
185 | label:'New Branch'
186 | data:
187 | something:42
188 | else:43
189 |
190 |
191 |
--------------------------------------------------------------------------------
/src/abn_tree_directive.coffee:
--------------------------------------------------------------------------------
1 | module = angular.module 'angularBootstrapNavTree',[]
2 |
3 | module.directive 'abnTree',['$timeout',($timeout)->
4 | restrict:'E'
5 |
6 | #templateUrl: '../dist/abn_tree_template.html' # <--- another way to do this
7 |
8 | template: """{html}""" # will be replaced by Grunt, during build, with the actual Template HTML
9 | replace:true
10 | scope:
11 | treeData:'='
12 | onSelect:'&'
13 | initialSelection:'@'
14 | treeControl:'='
15 |
16 | link:(scope,element,attrs)->
17 |
18 |
19 | error = (s) ->
20 | console.log 'ERROR:'+s
21 | debugger
22 | return undefined
23 |
24 |
25 | # default values ( Font-Awesome 3 or 4 or Glyphicons )
26 | attrs.iconExpand ?= 'icon-plus glyphicon glyphicon-plus fa fa-plus'
27 | attrs.iconCollapse ?= 'icon-minus glyphicon glyphicon-minus fa fa-minus'
28 | attrs.iconLeaf ?= 'icon-file glyphicon glyphicon-file fa fa-file'
29 |
30 | attrs.expandLevel ?= '3'
31 |
32 | expand_level = parseInt attrs.expandLevel,10
33 |
34 | # check args
35 | if !scope.treeData
36 | alert 'no treeData defined for the tree!'
37 | return
38 |
39 | if !scope.treeData.length?
40 | if scope.treeData.label?
41 | scope.treeData = [ scope.treeData ]
42 | else
43 | alert 'treeData should be an array of root branches'
44 | return
45 |
46 |
47 | #
48 | # internal utilities...
49 | #
50 | for_each_branch = (f)->
51 | do_f = (branch,level)->
52 | f(branch,level)
53 | if branch.children?
54 | for child in branch.children
55 | do_f(child,level + 1)
56 | for root_branch in scope.treeData
57 | do_f(root_branch,1)
58 |
59 |
60 |
61 |
62 |
63 | #
64 | # only one branch can be selected at a time
65 | #
66 | selected_branch = null
67 | select_branch = (branch)->
68 |
69 | if not branch
70 | if selected_branch?
71 | selected_branch.selected = false
72 | selected_branch = null
73 | return
74 |
75 | if branch isnt selected_branch
76 | if selected_branch?
77 | selected_branch.selected = false
78 |
79 | branch.selected = true
80 | selected_branch = branch
81 | expand_all_parents(branch)
82 |
83 |
84 | #
85 | # check:
86 | # 1) branch.onSelect
87 | # 2) tree.onSelect
88 | #
89 | if branch.onSelect?
90 | #
91 | # use $timeout
92 | # so that the branch becomes fully selected
93 | # ( and highlighted )
94 | # before calling the "onSelect" function.
95 | #
96 | $timeout ->
97 | branch.onSelect(branch)
98 | else
99 | if scope.onSelect?
100 | $timeout ->
101 | scope.onSelect({branch:branch})
102 |
103 |
104 | scope.user_clicks_branch = (branch)->
105 | if branch isnt selected_branch
106 | select_branch(branch)
107 |
108 |
109 | get_parent = (child)->
110 | parent = undefined
111 | if child.parent_uid
112 | for_each_branch (b)->
113 | if b.uid == child.parent_uid
114 | parent = b
115 | return parent
116 |
117 | for_all_ancestors = ( child, fn )->
118 | parent = get_parent( child )
119 | if parent?
120 | fn( parent )
121 | for_all_ancestors( parent, fn )
122 |
123 |
124 | expand_all_parents = ( child )->
125 | for_all_ancestors child, (b)->
126 | b.expanded = true
127 |
128 |
129 | #
130 | # To make the Angular rendering simpler,
131 | # ( and to avoid recursive templates )
132 | # we transform the TREE of data into a LIST of data.
133 | # ( "tree_rows" )
134 | #
135 | # We do this whenever data in the tree changes.
136 | # The tree itself is bound to this list.
137 | #
138 | # Children of un-expanded parents are included,
139 | # but are set to "visible:false"
140 | # ( and then they filtered out during rendering )
141 | #
142 | scope.tree_rows = []
143 | on_treeData_change = ->
144 |
145 | #console.log 'tree-data-change!'
146 |
147 | #
148 | # if "children" is just a list of strings...
149 | # ...change them into objects:
150 | #
151 | for_each_branch (branch)->
152 | if branch.children
153 | if branch.children.length > 0
154 | # don't use Array.map ( old browsers don't have it )
155 | f = (e)->
156 | if typeof e == 'string'
157 | label:e
158 | children:[]
159 | else
160 | e
161 | branch.children = ( f(child) for child in branch.children )
162 |
163 | else
164 | branch.children = []
165 |
166 |
167 | # give each Branch a UID ( to keep AngularJS happy )
168 | for_each_branch (b,level)->
169 | if not b.uid
170 | b.uid = ""+Math.random()
171 | console.log 'UIDs are set.'
172 |
173 |
174 | # set all parents:
175 | for_each_branch (b)->
176 | if angular.isArray b.children
177 | for child in b.children
178 | child.parent_uid = b.uid
179 |
180 |
181 | scope.tree_rows = []
182 |
183 |
184 | #
185 | # add_branch_to_list: recursively add one branch
186 | # and all of it's children to the list
187 | #
188 | add_branch_to_list = (level,branch,visible)->
189 |
190 | if not branch.expanded?
191 | branch.expanded = false
192 |
193 | if not branch.classes?
194 | branch.classes = []
195 |
196 | #
197 | # icons can be Bootstrap or Font-Awesome icons:
198 | # they will be rendered like:
199 | #
200 | #
201 | if not branch.noLeaf and (not branch.children or branch.children.length == 0)
202 | tree_icon = attrs.iconLeaf
203 | branch.classes.push "leaf" if "leaf" not in branch.classes
204 | else
205 | if branch.expanded
206 | tree_icon = attrs.iconCollapse
207 | else
208 | tree_icon = attrs.iconExpand
209 |
210 |
211 | #
212 | # append to the list of "Tree Row" objects:
213 | #
214 | scope.tree_rows.push
215 | level : level
216 | branch : branch
217 | label : branch.label
218 | classes : branch.classes
219 | tree_icon : tree_icon
220 | visible : visible
221 |
222 | #
223 | # recursively add all children of this branch...( at Level+1 )
224 | #
225 | if branch.children?
226 | for child in branch.children
227 | #
228 | # all branches are added to the list,
229 | # but some are not visible
230 | # ( if parent is collapsed )
231 | #
232 | child_visible = visible and branch.expanded
233 | add_branch_to_list level+1, child, child_visible
234 |
235 | #
236 | # start with root branches,
237 | # and recursively add all children to the list
238 | #
239 | for root_branch in scope.treeData
240 | add_branch_to_list 1, root_branch, true
241 |
242 |
243 | #
244 | # make sure to do a "deep watch" on the tree data
245 | # ( by passing "true" as the third arg )
246 | #
247 | scope.$watch 'treeData',on_treeData_change,true
248 |
249 |
250 | #
251 | # initial-selection="Branch Label"
252 | # if specified, find and select the branch:
253 | #
254 | if attrs.initialSelection?
255 | for_each_branch (b)->
256 | if b.label == attrs.initialSelection
257 | $timeout ->
258 | select_branch b
259 |
260 | #
261 | # expand to the proper level
262 | #
263 | n = scope.treeData.length
264 | console.log 'num root branches = '+n
265 | for_each_branch (b,level)->
266 | b.level = level
267 | b.expanded = b.level < expand_level
268 | #b.expanded = false
269 |
270 |
271 | #
272 | # TREE-CONTROL : the API to the Tree
273 | #
274 | # if we have been given an Object for this,
275 | # then we attach all of tree-control functions
276 | # to that given object:
277 | #
278 | if scope.treeControl?
279 | if angular.isObject scope.treeControl
280 | tree = scope.treeControl
281 |
282 | tree.expand_all = ->
283 | for_each_branch (b,level)->
284 | b.expanded = true
285 |
286 | tree.collapse_all = ->
287 | for_each_branch (b,level)->
288 | b.expanded = false
289 |
290 | tree.get_first_branch = ->
291 | n = scope.treeData.length
292 | if n > 0
293 | return scope.treeData[0]
294 |
295 | tree.select_first_branch = ->
296 | b = tree.get_first_branch()
297 | tree.select_branch(b)
298 |
299 | tree.get_selected_branch = ->
300 | selected_branch
301 |
302 | tree.get_parent_branch = (b)->
303 | get_parent(b)
304 |
305 | tree.select_branch = (b)->
306 | select_branch(b)
307 | b
308 |
309 | tree.get_children = (b)->
310 | b.children
311 |
312 | tree.select_parent_branch = (b)->
313 | if not b?
314 | b = tree.get_selected_branch()
315 | if b?
316 | p = tree.get_parent_branch(b)
317 | if p?
318 | tree.select_branch(p)
319 | p
320 |
321 |
322 | tree.add_branch = (parent,new_branch)->
323 | if parent?
324 | parent.children.push new_branch
325 | parent.expanded = true
326 | else
327 | scope.treeData.push new_branch
328 | #tree.select new_branch
329 | new_branch
330 |
331 | tree.add_root_branch = (new_branch)->
332 | tree.add_branch null, new_branch
333 | new_branch
334 |
335 | tree.expand_branch = (b)->
336 | if not b?
337 | b = tree.get_selected_branch()
338 | if b?
339 | b.expanded = true
340 | b
341 |
342 | tree.collapse_branch = (b)->
343 | b ?= selected_branch
344 | if b?
345 | b.expanded = false
346 | b
347 |
348 |
349 | tree.get_siblings = (b)->
350 | b ?= selected_branch
351 | if b?
352 | p = tree.get_parent_branch(b)
353 | if p
354 | siblings = p.children
355 | else
356 | siblings = scope.treeData # the root branches
357 | return siblings
358 |
359 |
360 | tree.get_next_sibling = (b)->
361 | b ?= selected_branch
362 | if b?
363 | siblings = tree.get_siblings(b)
364 | n = siblings.length
365 | i = siblings.indexOf b
366 | if i < n
367 | return siblings[i+1]
368 |
369 |
370 | tree.get_prev_sibling = (b)->
371 | b ?= selected_branch
372 | siblings = tree.get_siblings(b)
373 | n = siblings.length
374 | i = siblings.indexOf b
375 | if i > 0
376 | return siblings[i-1]
377 |
378 |
379 | tree.select_next_sibling = (b)->
380 | b ?= selected_branch
381 | if b?
382 | next = tree.get_next_sibling(b)
383 | if next?
384 | tree.select_branch next
385 |
386 |
387 | tree.select_prev_sibling = (b)->
388 | b ?= selected_branch
389 | if b?
390 | prev = tree.get_prev_sibling(b)
391 | if prev?
392 | tree.select_branch prev
393 |
394 |
395 | tree.get_first_child = (b)->
396 | b ?= selected_branch
397 | if b?
398 | if b.children?.length > 0
399 | return b.children[0]
400 |
401 |
402 | tree.get_closest_ancestor_next_sibling = (b)->
403 | next = tree.get_next_sibling(b)
404 | if next?
405 | return next
406 | else
407 | parent = tree.get_parent_branch(b)
408 | return tree.get_closest_ancestor_next_sibling(parent)
409 |
410 |
411 |
412 |
413 | tree.get_next_branch = (b)->
414 | #
415 | # "next" in the sense of...vertically, from top to bottom
416 | #
417 | # try:
418 | # 1) next sibling
419 | # 2) first child
420 | # 3) parent.get_next() // recursive
421 | #
422 | b ?= selected_branch
423 | if b?
424 | next = tree.get_first_child(b)
425 | if next?
426 | return next
427 | else
428 | next = tree.get_closest_ancestor_next_sibling(b)
429 | return next
430 |
431 |
432 | tree.select_next_branch = (b)->
433 | b ?= selected_branch
434 | if b?
435 | next = tree.get_next_branch(b)
436 | if next?
437 | tree.select_branch(next)
438 | next
439 |
440 |
441 | tree.last_descendant = (b)->
442 | if not b?
443 | debugger
444 | n = b.children.length
445 | if n == 0
446 | return b
447 | else
448 | last_child = b.children[n-1]
449 | return tree.last_descendant(last_child)
450 |
451 |
452 | tree.get_prev_branch = (b)->
453 | b ?= selected_branch
454 | if b?
455 | prev_sibling = tree.get_prev_sibling(b)
456 | if prev_sibling?
457 | return tree.last_descendant prev_sibling
458 | else
459 | parent = tree.get_parent_branch(b)
460 | return parent
461 |
462 | tree.select_prev_branch = (b)->
463 | b ?= selected_branch
464 | if b?
465 | prev = tree.get_prev_branch(b)
466 | if prev?
467 | tree.select_branch(prev)
468 | return prev
469 | ]
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
--------------------------------------------------------------------------------
/temp/_directive.coffee:
--------------------------------------------------------------------------------
1 |
2 | module = angular.module 'angularBootstrapNavTree',[]
3 |
4 | module.directive 'abnTree',['$timeout',($timeout)->
5 | restrict:'E'
6 |
7 | #templateUrl: '../dist/abn_tree_template.html' # <--- another way to do this
8 |
9 | template: """
10 | """ # will be replaced by Grunt, during build, with the actual Template HTML
18 | replace:true
19 | scope:
20 | treeData:'='
21 | onSelect:'&'
22 | initialSelection:'@'
23 | treeControl:'='
24 |
25 | link:(scope,element,attrs)->
26 |
27 |
28 | error = (s) ->
29 | console.log 'ERROR:'+s
30 | debugger
31 | return undefined
32 |
33 |
34 | # default values ( Font-Awesome 3 or 4 or Glyphicons )
35 | attrs.iconExpand ?= 'icon-plus glyphicon glyphicon-plus fa fa-plus'
36 | attrs.iconCollapse ?= 'icon-minus glyphicon glyphicon-minus fa fa-minus'
37 | attrs.iconLeaf ?= 'icon-file glyphicon glyphicon-file fa fa-file'
38 |
39 | attrs.expandLevel ?= '3'
40 |
41 | expand_level = parseInt attrs.expandLevel,10
42 |
43 | # check args
44 | if !scope.treeData
45 | alert 'no treeData defined for the tree!'
46 | return
47 |
48 | if !scope.treeData.length?
49 | if treeData.label?
50 | scope.treeData = [ treeData ]
51 | else
52 | alert 'treeData should be an array of root branches'
53 | return
54 |
55 |
56 | #
57 | # internal utilities...
58 | #
59 | for_each_branch = (f)->
60 | do_f = (branch,level)->
61 | f(branch,level)
62 | if branch.children?
63 | for child in branch.children
64 | do_f(child,level + 1)
65 | for root_branch in scope.treeData
66 | do_f(root_branch,1)
67 |
68 |
69 |
70 |
71 |
72 | #
73 | # only one branch can be selected at a time
74 | #
75 | selected_branch = null
76 | select_branch = (branch)->
77 |
78 | if not branch
79 | if selected_branch?
80 | selected_branch.selected = false
81 | selected_branch = null
82 | return
83 |
84 | if branch isnt selected_branch
85 | if selected_branch?
86 | selected_branch.selected = false
87 |
88 | branch.selected = true
89 | selected_branch = branch
90 | expand_all_parents(branch)
91 |
92 |
93 | #
94 | # check:
95 | # 1) branch.onSelect
96 | # 2) tree.onSelect
97 | #
98 | if branch.onSelect?
99 | #
100 | # use $timeout
101 | # so that the branch becomes fully selected
102 | # ( and highlighted )
103 | # before calling the "onSelect" function.
104 | #
105 | $timeout ->
106 | branch.onSelect(branch)
107 | else
108 | if scope.onSelect?
109 | $timeout ->
110 | scope.onSelect({branch:branch})
111 |
112 |
113 | scope.user_clicks_branch = (branch)->
114 | if branch isnt selected_branch
115 | select_branch(branch)
116 |
117 |
118 | get_parent = (child)->
119 | parent = undefined
120 | if child.parent_uid
121 | for_each_branch (b)->
122 | if b.uid == child.parent_uid
123 | parent = b
124 | return parent
125 |
126 | for_all_ancestors = ( child, fn )->
127 | parent = get_parent( child )
128 | if parent?
129 | fn( parent )
130 | for_all_ancestors( parent, fn )
131 |
132 |
133 | expand_all_parents = ( child )->
134 | for_all_ancestors child, (b)->
135 | b.expanded = true
136 |
137 |
138 | #
139 | # To make the Angular rendering simpler,
140 | # ( and to avoid recursive templates )
141 | # we transform the TREE of data into a LIST of data.
142 | # ( "tree_rows" )
143 | #
144 | # We do this whenever data in the tree changes.
145 | # The tree itself is bound to this list.
146 | #
147 | # Children of un-expanded parents are included,
148 | # but are set to "visible:false"
149 | # ( and then they filtered out during rendering )
150 | #
151 | scope.tree_rows = []
152 | on_treeData_change = ->
153 |
154 | #console.log 'tree-data-change!'
155 |
156 | # give each Branch a UID ( to keep AngularJS happy )
157 | for_each_branch (b,level)->
158 | if not b.uid
159 | b.uid = ""+Math.random()
160 | console.log 'UIDs are set.'
161 |
162 |
163 | # set all parents:
164 | for_each_branch (b)->
165 | if angular.isArray b.children
166 | for child in b.children
167 | child.parent_uid = b.uid
168 |
169 |
170 | scope.tree_rows = []
171 |
172 | #
173 | # if "children" is just a list of strings...
174 | # ...change them into objects:
175 | #
176 | for_each_branch (branch)->
177 | if branch.children
178 | if branch.children.length > 0
179 | # don't use Array.map ( old browsers don't have it )
180 | f = (e)->
181 | if typeof e == 'string'
182 | label:e
183 | children:[]
184 | else
185 | e
186 | branch.children = ( f(child) for child in branch.children )
187 |
188 | else
189 | branch.children = []
190 |
191 |
192 | #
193 | # add_branch_to_list: recursively add one branch
194 | # and all of it's children to the list
195 | #
196 | add_branch_to_list = (level,branch,visible)->
197 |
198 | if not branch.expanded?
199 | branch.expanded = false
200 |
201 | #
202 | # icons can be Bootstrap or Font-Awesome icons:
203 | # they will be rendered like:
204 | #
205 | #
206 | if not branch.children or branch.children.length == 0
207 | tree_icon = attrs.iconLeaf
208 | else
209 | if branch.expanded
210 | tree_icon = attrs.iconCollapse
211 | else
212 | tree_icon = attrs.iconExpand
213 |
214 |
215 | #
216 | # append to the list of "Tree Row" objects:
217 | #
218 | scope.tree_rows.push
219 | level : level
220 | branch : branch
221 | label : branch.label
222 | tree_icon : tree_icon
223 | visible : visible
224 |
225 | #
226 | # recursively add all children of this branch...( at Level+1 )
227 | #
228 | if branch.children?
229 | for child in branch.children
230 | #
231 | # all branches are added to the list,
232 | # but some are not visible
233 | # ( if parent is collapsed )
234 | #
235 | child_visible = visible and branch.expanded
236 | add_branch_to_list level+1, child, child_visible
237 |
238 | #
239 | # start with root branches,
240 | # and recursively add all children to the list
241 | #
242 | for root_branch in scope.treeData
243 | add_branch_to_list 1, root_branch, true
244 |
245 |
246 | #
247 | # make sure to do a "deep watch" on the tree data
248 | # ( by passing "true" as the third arg )
249 | #
250 | scope.$watch 'treeData',on_treeData_change,true
251 |
252 |
253 | #
254 | # initial-selection="Branch Label"
255 | # if specified, find and select the branch:
256 | #
257 | if attrs.initialSelection?
258 | for_each_branch (b)->
259 | if b.label == attrs.initialSelection
260 | $timeout ->
261 | select_branch b
262 |
263 | #
264 | # expand to the proper level
265 | #
266 | n = scope.treeData.length
267 | console.log 'num root branches = '+n
268 | for_each_branch (b,level)->
269 | b.level = level
270 | b.expanded = b.level < expand_level
271 | #b.expanded = false
272 |
273 |
274 | #
275 | # TREE-CONTROL : the API to the Tree
276 | #
277 | # if we have been given an Object for this,
278 | # then we attach all of tree-control functions
279 | # to that given object:
280 | #
281 | if scope.treeControl?
282 | if angular.isObject scope.treeControl
283 | tree = scope.treeControl
284 |
285 | tree.expand_all = ->
286 | for_each_branch (b,level)->
287 | b.expanded = true
288 |
289 | tree.collapse_all = ->
290 | for_each_branch (b,level)->
291 | b.expanded = false
292 |
293 | tree.get_first_branch = ->
294 | n = scope.treeData.length
295 | if n > 0
296 | return scope.treeData[0]
297 |
298 | tree.select_first_branch = ->
299 | b = tree.get_first_branch()
300 | tree.select_branch(b)
301 |
302 | tree.get_selected_branch = ->
303 | selected_branch
304 |
305 | tree.get_parent_branch = (b)->
306 | get_parent(b)
307 |
308 | tree.select_branch = (b)->
309 | select_branch(b)
310 | b
311 |
312 | tree.get_children = (b)->
313 | b.children
314 |
315 | tree.select_parent_branch = (b)->
316 | if not b?
317 | b = tree.get_selected_branch()
318 | if b?
319 | p = tree.get_parent_branch(b)
320 | if p?
321 | tree.select_branch(p)
322 | p
323 |
324 |
325 | tree.add_branch = (parent,new_branch)->
326 | if parent?
327 | parent.children.push new_branch
328 | parent.expanded = true
329 | else
330 | scope.treeData.push new_branch
331 | #tree.select new_branch
332 | new_branch
333 |
334 | tree.add_root_branch = (new_branch)->
335 | tree.add_branch null, new_branch
336 | new_branch
337 |
338 | tree.expand_branch = (b)->
339 | if not b?
340 | b = tree.get_selected_branch()
341 | if b?
342 | b.expanded = true
343 | b
344 |
345 | tree.collapse_branch = (b)->
346 | b ?= selected_branch
347 | if b?
348 | b.expanded = false
349 | b
350 |
351 |
352 | tree.get_siblings = (b)->
353 | b ?= selected_branch
354 | if b?
355 | p = tree.get_parent_branch(b)
356 | if p
357 | siblings = p.children
358 | else
359 | siblings = scope.treeData # the root branches
360 | return siblings
361 |
362 |
363 | tree.get_next_sibling = (b)->
364 | b ?= selected_branch
365 | if b?
366 | siblings = tree.get_siblings(b)
367 | n = siblings.length
368 | i = siblings.indexOf b
369 | if i < n
370 | return siblings[i+1]
371 |
372 |
373 | tree.get_prev_sibling = (b)->
374 | b ?= selected_branch
375 | siblings = tree.get_siblings(b)
376 | n = siblings.length
377 | i = siblings.indexOf b
378 | if i > 0
379 | return siblings[i-1]
380 |
381 |
382 | tree.select_next_sibling = (b)->
383 | b ?= selected_branch
384 | if b?
385 | next = tree.get_next_sibling(b)
386 | if next?
387 | tree.select_branch next
388 |
389 |
390 | tree.select_prev_sibling = (b)->
391 | b ?= selected_branch
392 | if b?
393 | prev = tree.get_prev_sibling(b)
394 | if prev?
395 | tree.select_branch prev
396 |
397 |
398 | tree.get_first_child = (b)->
399 | b ?= selected_branch
400 | if b?
401 | if b.children?.length > 0
402 | return b.children[0]
403 |
404 |
405 | tree.get_closest_ancestor_next_sibling = (b)->
406 | next = tree.get_next_sibling(b)
407 | if next?
408 | return next
409 | else
410 | parent = tree.get_parent_branch(b)
411 | return tree.get_closest_ancestor_next_sibling(parent)
412 |
413 |
414 |
415 |
416 | tree.get_next_branch = (b)->
417 | #
418 | # "next" in the sense of...vertically, from top to bottom
419 | #
420 | # try:
421 | # 1) next sibling
422 | # 2) first child
423 | # 3) parent.get_next() // recursive
424 | #
425 | b ?= selected_branch
426 | if b?
427 | next = tree.get_first_child(b)
428 | if next?
429 | return next
430 | else
431 | next = tree.get_closest_ancestor_next_sibling(b)
432 | return next
433 |
434 |
435 | tree.select_next_branch = (b)->
436 | b ?= selected_branch
437 | if b?
438 | next = tree.get_next_branch(b)
439 | if next?
440 | tree.select_branch(next)
441 | next
442 |
443 |
444 | tree.last_descendant = (b)->
445 | if not b?
446 | debugger
447 | n = b.children.length
448 | if n == 0
449 | return b
450 | else
451 | last_child = b.children[n-1]
452 | return tree.last_descendant(last_child)
453 |
454 |
455 | tree.get_prev_branch = (b)->
456 | b ?= selected_branch
457 | if b?
458 | prev_sibling = tree.get_prev_sibling(b)
459 | if prev_sibling?
460 | return tree.last_descendant prev_sibling
461 | else
462 | parent = tree.get_parent_branch(b)
463 | return parent
464 |
465 | tree.select_prev_branch = (b)->
466 | b ?= selected_branch
467 | if b?
468 | prev = tree.get_prev_branch(b)
469 | if prev?
470 | tree.select_branch(prev)
471 | return prev
472 | ]
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
--------------------------------------------------------------------------------
/src/abn_tree_directive.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.6.3
2 | var module;
3 |
4 | module = angular.module('angularBootstrapNavTree', []);
5 |
6 | module.directive('abnTree', [
7 | '$timeout', function($timeout) {
8 | return {
9 | restrict: 'E',
10 | template: "{html}",
11 | replace: true,
12 | scope: {
13 | treeData: '=',
14 | onSelect: '&',
15 | initialSelection: '@',
16 | treeControl: '='
17 | },
18 | link: function(scope, element, attrs) {
19 | var error, expand_all_parents, expand_level, for_all_ancestors, for_each_branch, get_parent, n, on_treeData_change, select_branch, selected_branch, tree;
20 | error = function(s) {
21 | console.log('ERROR:' + s);
22 | debugger;
23 | return void 0;
24 | };
25 | if (attrs.iconExpand == null) {
26 | attrs.iconExpand = 'icon-plus glyphicon glyphicon-plus fa fa-plus';
27 | }
28 | if (attrs.iconCollapse == null) {
29 | attrs.iconCollapse = 'icon-minus glyphicon glyphicon-minus fa fa-minus';
30 | }
31 | if (attrs.iconLeaf == null) {
32 | attrs.iconLeaf = 'icon-file glyphicon glyphicon-file fa fa-file';
33 | }
34 | if (attrs.expandLevel == null) {
35 | attrs.expandLevel = '3';
36 | }
37 | expand_level = parseInt(attrs.expandLevel, 10);
38 | if (!scope.treeData) {
39 | alert('no treeData defined for the tree!');
40 | return;
41 | }
42 | if (scope.treeData.length == null) {
43 | if (treeData.label != null) {
44 | scope.treeData = [treeData];
45 | } else {
46 | alert('treeData should be an array of root branches');
47 | return;
48 | }
49 | }
50 | for_each_branch = function(f) {
51 | var do_f, root_branch, _i, _len, _ref, _results;
52 | do_f = function(branch, level) {
53 | var child, _i, _len, _ref, _results;
54 | f(branch, level);
55 | if (branch.children != null) {
56 | _ref = branch.children;
57 | _results = [];
58 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
59 | child = _ref[_i];
60 | _results.push(do_f(child, level + 1));
61 | }
62 | return _results;
63 | }
64 | };
65 | _ref = scope.treeData;
66 | _results = [];
67 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
68 | root_branch = _ref[_i];
69 | _results.push(do_f(root_branch, 1));
70 | }
71 | return _results;
72 | };
73 | selected_branch = null;
74 | select_branch = function(branch) {
75 | if (!branch) {
76 | if (selected_branch != null) {
77 | selected_branch.selected = false;
78 | }
79 | selected_branch = null;
80 | return;
81 | }
82 | if (branch !== selected_branch) {
83 | if (selected_branch != null) {
84 | selected_branch.selected = false;
85 | }
86 | branch.selected = true;
87 | selected_branch = branch;
88 | expand_all_parents(branch);
89 | if (branch.onSelect != null) {
90 | return $timeout(function() {
91 | return branch.onSelect(branch);
92 | });
93 | } else {
94 | if (scope.onSelect != null) {
95 | return $timeout(function() {
96 | return scope.onSelect({
97 | branch: branch
98 | });
99 | });
100 | }
101 | }
102 | }
103 | };
104 | scope.user_clicks_branch = function(branch) {
105 | if (branch !== selected_branch) {
106 | return select_branch(branch);
107 | }
108 | };
109 | get_parent = function(child) {
110 | var parent;
111 | parent = void 0;
112 | if (child.parent_uid) {
113 | for_each_branch(function(b) {
114 | if (b.uid === child.parent_uid) {
115 | return parent = b;
116 | }
117 | });
118 | }
119 | return parent;
120 | };
121 | for_all_ancestors = function(child, fn) {
122 | var parent;
123 | parent = get_parent(child);
124 | if (parent != null) {
125 | fn(parent);
126 | return for_all_ancestors(parent, fn);
127 | }
128 | };
129 | expand_all_parents = function(child) {
130 | return for_all_ancestors(child, function(b) {
131 | return b.expanded = true;
132 | });
133 | };
134 | scope.tree_rows = [];
135 | on_treeData_change = function() {
136 | var add_branch_to_list, root_branch, _i, _len, _ref, _results;
137 | for_each_branch(function(b, level) {
138 | if (!b.uid) {
139 | return b.uid = "" + Math.random();
140 | }
141 | });
142 | for_each_branch(function(b) {
143 | var child, _i, _len, _ref, _results;
144 | if (angular.isArray(b.children)) {
145 | _ref = b.children;
146 | _results = [];
147 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
148 | child = _ref[_i];
149 | _results.push(child.parent_uid = b.uid);
150 | }
151 | return _results;
152 | }
153 | });
154 | scope.tree_rows = [];
155 | for_each_branch(function(branch) {
156 | var child, f;
157 | if (branch.children) {
158 | if (branch.children.length > 0) {
159 | f = function(e) {
160 | if (typeof e === 'string') {
161 | return {
162 | label: e,
163 | children: []
164 | };
165 | } else {
166 | return e;
167 | }
168 | };
169 | return branch.children = (function() {
170 | var _i, _len, _ref, _results;
171 | _ref = branch.children;
172 | _results = [];
173 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
174 | child = _ref[_i];
175 | _results.push(f(child));
176 | }
177 | return _results;
178 | })();
179 | }
180 | } else {
181 | return branch.children = [];
182 | }
183 | });
184 | add_branch_to_list = function(level, branch, visible) {
185 | var child, child_visible, tree_icon, _i, _len, _ref, _results;
186 | if (branch.expanded == null) {
187 | branch.expanded = false;
188 | }
189 | if (!branch.children || branch.children.length === 0) {
190 | tree_icon = attrs.iconLeaf;
191 | } else {
192 | if (branch.expanded) {
193 | tree_icon = attrs.iconCollapse;
194 | } else {
195 | tree_icon = attrs.iconExpand;
196 | }
197 | }
198 | scope.tree_rows.push({
199 | level: level,
200 | branch: branch,
201 | label: branch.label,
202 | tree_icon: tree_icon,
203 | visible: visible
204 | });
205 | if (branch.children != null) {
206 | _ref = branch.children;
207 | _results = [];
208 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
209 | child = _ref[_i];
210 | child_visible = visible && branch.expanded;
211 | _results.push(add_branch_to_list(level + 1, child, child_visible));
212 | }
213 | return _results;
214 | }
215 | };
216 | _ref = scope.treeData;
217 | _results = [];
218 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
219 | root_branch = _ref[_i];
220 | _results.push(add_branch_to_list(1, root_branch, true));
221 | }
222 | return _results;
223 | };
224 | scope.$watch('treeData', on_treeData_change, true);
225 | if (attrs.initialSelection != null) {
226 | for_each_branch(function(b) {
227 | if (b.label === attrs.initialSelection) {
228 | return $timeout(function() {
229 | return select_branch(b);
230 | });
231 | }
232 | });
233 | }
234 | n = scope.treeData.length;
235 | for_each_branch(function(b, level) {
236 | b.level = level;
237 | return b.expanded = b.level < expand_level;
238 | });
239 | if (scope.treeControl != null) {
240 | if (angular.isObject(scope.treeControl)) {
241 | tree = scope.treeControl;
242 | tree.expand_all = function() {
243 | return for_each_branch(function(b, level) {
244 | return b.expanded = true;
245 | });
246 | };
247 | tree.collapse_all = function() {
248 | return for_each_branch(function(b, level) {
249 | return b.expanded = false;
250 | });
251 | };
252 | tree.get_first_branch = function() {
253 | n = scope.treeData.length;
254 | if (n > 0) {
255 | return scope.treeData[0];
256 | }
257 | };
258 | tree.select_first_branch = function() {
259 | var b;
260 | b = tree.get_first_branch();
261 | return tree.select_branch(b);
262 | };
263 | tree.get_selected_branch = function() {
264 | return selected_branch;
265 | };
266 | tree.get_parent_branch = function(b) {
267 | return get_parent(b);
268 | };
269 | tree.select_branch = function(b) {
270 | select_branch(b);
271 | return b;
272 | };
273 | tree.get_children = function(b) {
274 | return b.children;
275 | };
276 | tree.select_parent_branch = function(b) {
277 | var p;
278 | if (b == null) {
279 | b = tree.get_selected_branch();
280 | }
281 | if (b != null) {
282 | p = tree.get_parent_branch(b);
283 | if (p != null) {
284 | tree.select_branch(p);
285 | return p;
286 | }
287 | }
288 | };
289 | tree.add_branch = function(parent, new_branch) {
290 | if (parent != null) {
291 | parent.children.push(new_branch);
292 | parent.expanded = true;
293 | } else {
294 | scope.treeData.push(new_branch);
295 | }
296 | return new_branch;
297 | };
298 | tree.add_root_branch = function(new_branch) {
299 | tree.add_branch(null, new_branch);
300 | return new_branch;
301 | };
302 | tree.expand_branch = function(b) {
303 | if (b == null) {
304 | b = tree.get_selected_branch();
305 | }
306 | if (b != null) {
307 | b.expanded = true;
308 | return b;
309 | }
310 | };
311 | tree.collapse_branch = function(b) {
312 | if (b == null) {
313 | b = selected_branch;
314 | }
315 | if (b != null) {
316 | b.expanded = false;
317 | return b;
318 | }
319 | };
320 | tree.get_siblings = function(b) {
321 | var p, siblings;
322 | if (b == null) {
323 | b = selected_branch;
324 | }
325 | if (b != null) {
326 | p = tree.get_parent_branch(b);
327 | if (p) {
328 | siblings = p.children;
329 | } else {
330 | siblings = scope.treeData;
331 | }
332 | return siblings;
333 | }
334 | };
335 | tree.get_next_sibling = function(b) {
336 | var i, siblings;
337 | if (b == null) {
338 | b = selected_branch;
339 | }
340 | if (b != null) {
341 | siblings = tree.get_siblings(b);
342 | n = siblings.length;
343 | i = siblings.indexOf(b);
344 | if (i < n) {
345 | return siblings[i + 1];
346 | }
347 | }
348 | };
349 | tree.get_prev_sibling = function(b) {
350 | var i, siblings;
351 | if (b == null) {
352 | b = selected_branch;
353 | }
354 | siblings = tree.get_siblings(b);
355 | n = siblings.length;
356 | i = siblings.indexOf(b);
357 | if (i > 0) {
358 | return siblings[i - 1];
359 | }
360 | };
361 | tree.select_next_sibling = function(b) {
362 | var next;
363 | if (b == null) {
364 | b = selected_branch;
365 | }
366 | if (b != null) {
367 | next = tree.get_next_sibling(b);
368 | if (next != null) {
369 | return tree.select_branch(next);
370 | }
371 | }
372 | };
373 | tree.select_prev_sibling = function(b) {
374 | var prev;
375 | if (b == null) {
376 | b = selected_branch;
377 | }
378 | if (b != null) {
379 | prev = tree.get_prev_sibling(b);
380 | if (prev != null) {
381 | return tree.select_branch(prev);
382 | }
383 | }
384 | };
385 | tree.get_first_child = function(b) {
386 | var _ref;
387 | if (b == null) {
388 | b = selected_branch;
389 | }
390 | if (b != null) {
391 | if (((_ref = b.children) != null ? _ref.length : void 0) > 0) {
392 | return b.children[0];
393 | }
394 | }
395 | };
396 | tree.get_closest_ancestor_next_sibling = function(b) {
397 | var next, parent;
398 | next = tree.get_next_sibling(b);
399 | if (next != null) {
400 | return next;
401 | } else {
402 | parent = tree.get_parent_branch(b);
403 | return tree.get_closest_ancestor_next_sibling(parent);
404 | }
405 | };
406 | tree.get_next_branch = function(b) {
407 | var next;
408 | if (b == null) {
409 | b = selected_branch;
410 | }
411 | if (b != null) {
412 | next = tree.get_first_child(b);
413 | if (next != null) {
414 | return next;
415 | } else {
416 | next = tree.get_closest_ancestor_next_sibling(b);
417 | return next;
418 | }
419 | }
420 | };
421 | tree.select_next_branch = function(b) {
422 | var next;
423 | if (b == null) {
424 | b = selected_branch;
425 | }
426 | if (b != null) {
427 | next = tree.get_next_branch(b);
428 | if (next != null) {
429 | tree.select_branch(next);
430 | return next;
431 | }
432 | }
433 | };
434 | tree.last_descendant = function(b) {
435 | var last_child;
436 | if (b == null) {
437 | debugger;
438 | }
439 | n = b.children.length;
440 | if (n === 0) {
441 | return b;
442 | } else {
443 | last_child = b.children[n - 1];
444 | return tree.last_descendant(last_child);
445 | }
446 | };
447 | tree.get_prev_branch = function(b) {
448 | var parent, prev_sibling;
449 | if (b == null) {
450 | b = selected_branch;
451 | }
452 | if (b != null) {
453 | prev_sibling = tree.get_prev_sibling(b);
454 | if (prev_sibling != null) {
455 | return tree.last_descendant(prev_sibling);
456 | } else {
457 | parent = tree.get_parent_branch(b);
458 | return parent;
459 | }
460 | }
461 | };
462 | return tree.select_prev_branch = function(b) {
463 | var prev;
464 | if (b == null) {
465 | b = selected_branch;
466 | }
467 | if (b != null) {
468 | prev = tree.get_prev_branch(b);
469 | if (prev != null) {
470 | tree.select_branch(prev);
471 | return prev;
472 | }
473 | }
474 | };
475 | }
476 | }
477 | }
478 | };
479 | }
480 | ]);
481 |
--------------------------------------------------------------------------------
/dist/abn_tree_directive.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var module,
3 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
4 |
5 | module = angular.module('angularBootstrapNavTree', []);
6 |
7 | module.directive('abnTree', [
8 | '$timeout', function($timeout) {
9 | return {
10 | restrict: 'E',
11 | template: "",
12 | replace: true,
13 | scope: {
14 | treeData: '=',
15 | onSelect: '&',
16 | initialSelection: '@',
17 | treeControl: '='
18 | },
19 | link: function(scope, element, attrs) {
20 | var error, expand_all_parents, expand_level, for_all_ancestors, for_each_branch, get_parent, n, on_treeData_change, select_branch, selected_branch, tree;
21 | error = function(s) {
22 | console.log('ERROR:' + s);
23 | debugger;
24 | return void 0;
25 | };
26 | if (attrs.iconExpand == null) {
27 | attrs.iconExpand = 'icon-plus glyphicon glyphicon-plus fa fa-plus';
28 | }
29 | if (attrs.iconCollapse == null) {
30 | attrs.iconCollapse = 'icon-minus glyphicon glyphicon-minus fa fa-minus';
31 | }
32 | if (attrs.iconLeaf == null) {
33 | attrs.iconLeaf = 'icon-file glyphicon glyphicon-file fa fa-file';
34 | }
35 | if (attrs.expandLevel == null) {
36 | attrs.expandLevel = '3';
37 | }
38 | expand_level = parseInt(attrs.expandLevel, 10);
39 | if (!scope.treeData) {
40 | alert('no treeData defined for the tree!');
41 | return;
42 | }
43 | if (scope.treeData.length == null) {
44 | if (treeData.label != null) {
45 | scope.treeData = [treeData];
46 | } else {
47 | alert('treeData should be an array of root branches');
48 | return;
49 | }
50 | }
51 | for_each_branch = function(f) {
52 | var do_f, root_branch, _i, _len, _ref, _results;
53 | do_f = function(branch, level) {
54 | var child, _i, _len, _ref, _results;
55 | f(branch, level);
56 | if (branch.children != null) {
57 | _ref = branch.children;
58 | _results = [];
59 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
60 | child = _ref[_i];
61 | _results.push(do_f(child, level + 1));
62 | }
63 | return _results;
64 | }
65 | };
66 | _ref = scope.treeData;
67 | _results = [];
68 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
69 | root_branch = _ref[_i];
70 | _results.push(do_f(root_branch, 1));
71 | }
72 | return _results;
73 | };
74 | selected_branch = null;
75 | select_branch = function(branch) {
76 | if (!branch) {
77 | if (selected_branch != null) {
78 | selected_branch.selected = false;
79 | }
80 | selected_branch = null;
81 | return;
82 | }
83 | if (branch !== selected_branch) {
84 | if (selected_branch != null) {
85 | selected_branch.selected = false;
86 | }
87 | branch.selected = true;
88 | selected_branch = branch;
89 | expand_all_parents(branch);
90 | if (branch.onSelect != null) {
91 | return $timeout(function() {
92 | return branch.onSelect(branch);
93 | });
94 | } else {
95 | if (scope.onSelect != null) {
96 | return $timeout(function() {
97 | return scope.onSelect({
98 | branch: branch
99 | });
100 | });
101 | }
102 | }
103 | }
104 | };
105 | scope.user_clicks_branch = function(branch) {
106 | if (branch !== selected_branch) {
107 | return select_branch(branch);
108 | }
109 | };
110 | get_parent = function(child) {
111 | var parent;
112 | parent = void 0;
113 | if (child.parent_uid) {
114 | for_each_branch(function(b) {
115 | if (b.uid === child.parent_uid) {
116 | return parent = b;
117 | }
118 | });
119 | }
120 | return parent;
121 | };
122 | for_all_ancestors = function(child, fn) {
123 | var parent;
124 | parent = get_parent(child);
125 | if (parent != null) {
126 | fn(parent);
127 | return for_all_ancestors(parent, fn);
128 | }
129 | };
130 | expand_all_parents = function(child) {
131 | return for_all_ancestors(child, function(b) {
132 | return b.expanded = true;
133 | });
134 | };
135 | scope.tree_rows = [];
136 | on_treeData_change = function() {
137 | var add_branch_to_list, root_branch, _i, _len, _ref, _results;
138 | for_each_branch(function(b, level) {
139 | if (!b.uid) {
140 | return b.uid = "" + Math.random();
141 | }
142 | });
143 | for_each_branch(function(b) {
144 | var child, _i, _len, _ref, _results;
145 | if (angular.isArray(b.children)) {
146 | _ref = b.children;
147 | _results = [];
148 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
149 | child = _ref[_i];
150 | _results.push(child.parent_uid = b.uid);
151 | }
152 | return _results;
153 | }
154 | });
155 | scope.tree_rows = [];
156 | for_each_branch(function(branch) {
157 | var child, f;
158 | if (branch.children) {
159 | if (branch.children.length > 0) {
160 | f = function(e) {
161 | if (typeof e === 'string') {
162 | return {
163 | label: e,
164 | children: []
165 | };
166 | } else {
167 | return e;
168 | }
169 | };
170 | return branch.children = (function() {
171 | var _i, _len, _ref, _results;
172 | _ref = branch.children;
173 | _results = [];
174 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
175 | child = _ref[_i];
176 | _results.push(f(child));
177 | }
178 | return _results;
179 | })();
180 | }
181 | } else {
182 | return branch.children = [];
183 | }
184 | });
185 | add_branch_to_list = function(level, branch, visible) {
186 | var child, child_visible, tree_icon, _i, _len, _ref, _results;
187 | if (branch.expanded == null) {
188 | branch.expanded = false;
189 | }
190 | if (branch.classes == null) {
191 | branch.classes = [];
192 | }
193 | if (!branch.noLeaf && (!branch.children || branch.children.length === 0)) {
194 | tree_icon = attrs.iconLeaf;
195 | if (__indexOf.call(branch.classes, "leaf") < 0) {
196 | branch.classes.push("leaf");
197 | }
198 | } else {
199 | if (branch.expanded) {
200 | tree_icon = attrs.iconCollapse;
201 | } else {
202 | tree_icon = attrs.iconExpand;
203 | }
204 | }
205 | scope.tree_rows.push({
206 | level: level,
207 | branch: branch,
208 | label: branch.label,
209 | classes: branch.classes,
210 | tree_icon: tree_icon,
211 | visible: visible
212 | });
213 | if (branch.children != null) {
214 | _ref = branch.children;
215 | _results = [];
216 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
217 | child = _ref[_i];
218 | child_visible = visible && branch.expanded;
219 | _results.push(add_branch_to_list(level + 1, child, child_visible));
220 | }
221 | return _results;
222 | }
223 | };
224 | _ref = scope.treeData;
225 | _results = [];
226 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
227 | root_branch = _ref[_i];
228 | _results.push(add_branch_to_list(1, root_branch, true));
229 | }
230 | return _results;
231 | };
232 | scope.$watch('treeData', on_treeData_change, true);
233 | if (attrs.initialSelection != null) {
234 | for_each_branch(function(b) {
235 | if (b.label === attrs.initialSelection) {
236 | return $timeout(function() {
237 | return select_branch(b);
238 | });
239 | }
240 | });
241 | }
242 | n = scope.treeData.length;
243 | for_each_branch(function(b, level) {
244 | b.level = level;
245 | return b.expanded = b.level < expand_level;
246 | });
247 | if (scope.treeControl != null) {
248 | if (angular.isObject(scope.treeControl)) {
249 | tree = scope.treeControl;
250 | tree.expand_all = function() {
251 | return for_each_branch(function(b, level) {
252 | return b.expanded = true;
253 | });
254 | };
255 | tree.collapse_all = function() {
256 | return for_each_branch(function(b, level) {
257 | return b.expanded = false;
258 | });
259 | };
260 | tree.get_first_branch = function() {
261 | n = scope.treeData.length;
262 | if (n > 0) {
263 | return scope.treeData[0];
264 | }
265 | };
266 | tree.select_first_branch = function() {
267 | var b;
268 | b = tree.get_first_branch();
269 | return tree.select_branch(b);
270 | };
271 | tree.get_selected_branch = function() {
272 | return selected_branch;
273 | };
274 | tree.get_parent_branch = function(b) {
275 | return get_parent(b);
276 | };
277 | tree.select_branch = function(b) {
278 | select_branch(b);
279 | return b;
280 | };
281 | tree.get_children = function(b) {
282 | return b.children;
283 | };
284 | tree.select_parent_branch = function(b) {
285 | var p;
286 | if (b == null) {
287 | b = tree.get_selected_branch();
288 | }
289 | if (b != null) {
290 | p = tree.get_parent_branch(b);
291 | if (p != null) {
292 | tree.select_branch(p);
293 | return p;
294 | }
295 | }
296 | };
297 | tree.add_branch = function(parent, new_branch) {
298 | if (parent != null) {
299 | parent.children.push(new_branch);
300 | parent.expanded = true;
301 | } else {
302 | scope.treeData.push(new_branch);
303 | }
304 | return new_branch;
305 | };
306 | tree.add_root_branch = function(new_branch) {
307 | tree.add_branch(null, new_branch);
308 | return new_branch;
309 | };
310 | tree.expand_branch = function(b) {
311 | if (b == null) {
312 | b = tree.get_selected_branch();
313 | }
314 | if (b != null) {
315 | b.expanded = true;
316 | return b;
317 | }
318 | };
319 | tree.collapse_branch = function(b) {
320 | if (b == null) {
321 | b = selected_branch;
322 | }
323 | if (b != null) {
324 | b.expanded = false;
325 | return b;
326 | }
327 | };
328 | tree.get_siblings = function(b) {
329 | var p, siblings;
330 | if (b == null) {
331 | b = selected_branch;
332 | }
333 | if (b != null) {
334 | p = tree.get_parent_branch(b);
335 | if (p) {
336 | siblings = p.children;
337 | } else {
338 | siblings = scope.treeData;
339 | }
340 | return siblings;
341 | }
342 | };
343 | tree.get_next_sibling = function(b) {
344 | var i, siblings;
345 | if (b == null) {
346 | b = selected_branch;
347 | }
348 | if (b != null) {
349 | siblings = tree.get_siblings(b);
350 | n = siblings.length;
351 | i = siblings.indexOf(b);
352 | if (i < n) {
353 | return siblings[i + 1];
354 | }
355 | }
356 | };
357 | tree.get_prev_sibling = function(b) {
358 | var i, siblings;
359 | if (b == null) {
360 | b = selected_branch;
361 | }
362 | siblings = tree.get_siblings(b);
363 | n = siblings.length;
364 | i = siblings.indexOf(b);
365 | if (i > 0) {
366 | return siblings[i - 1];
367 | }
368 | };
369 | tree.select_next_sibling = function(b) {
370 | var next;
371 | if (b == null) {
372 | b = selected_branch;
373 | }
374 | if (b != null) {
375 | next = tree.get_next_sibling(b);
376 | if (next != null) {
377 | return tree.select_branch(next);
378 | }
379 | }
380 | };
381 | tree.select_prev_sibling = function(b) {
382 | var prev;
383 | if (b == null) {
384 | b = selected_branch;
385 | }
386 | if (b != null) {
387 | prev = tree.get_prev_sibling(b);
388 | if (prev != null) {
389 | return tree.select_branch(prev);
390 | }
391 | }
392 | };
393 | tree.get_first_child = function(b) {
394 | var _ref;
395 | if (b == null) {
396 | b = selected_branch;
397 | }
398 | if (b != null) {
399 | if (((_ref = b.children) != null ? _ref.length : void 0) > 0) {
400 | return b.children[0];
401 | }
402 | }
403 | };
404 | tree.get_closest_ancestor_next_sibling = function(b) {
405 | var next, parent;
406 | next = tree.get_next_sibling(b);
407 | if (next != null) {
408 | return next;
409 | } else {
410 | parent = tree.get_parent_branch(b);
411 | return tree.get_closest_ancestor_next_sibling(parent);
412 | }
413 | };
414 | tree.get_next_branch = function(b) {
415 | var next;
416 | if (b == null) {
417 | b = selected_branch;
418 | }
419 | if (b != null) {
420 | next = tree.get_first_child(b);
421 | if (next != null) {
422 | return next;
423 | } else {
424 | next = tree.get_closest_ancestor_next_sibling(b);
425 | return next;
426 | }
427 | }
428 | };
429 | tree.select_next_branch = function(b) {
430 | var next;
431 | if (b == null) {
432 | b = selected_branch;
433 | }
434 | if (b != null) {
435 | next = tree.get_next_branch(b);
436 | if (next != null) {
437 | tree.select_branch(next);
438 | return next;
439 | }
440 | }
441 | };
442 | tree.last_descendant = function(b) {
443 | var last_child;
444 | if (b == null) {
445 | debugger;
446 | }
447 | n = b.children.length;
448 | if (n === 0) {
449 | return b;
450 | } else {
451 | last_child = b.children[n - 1];
452 | return tree.last_descendant(last_child);
453 | }
454 | };
455 | tree.get_prev_branch = function(b) {
456 | var parent, prev_sibling;
457 | if (b == null) {
458 | b = selected_branch;
459 | }
460 | if (b != null) {
461 | prev_sibling = tree.get_prev_sibling(b);
462 | if (prev_sibling != null) {
463 | return tree.last_descendant(prev_sibling);
464 | } else {
465 | parent = tree.get_parent_branch(b);
466 | return parent;
467 | }
468 | }
469 | };
470 | return tree.select_prev_branch = function(b) {
471 | var prev;
472 | if (b == null) {
473 | b = selected_branch;
474 | }
475 | if (b != null) {
476 | prev = tree.get_prev_branch(b);
477 | if (prev != null) {
478 | tree.select_branch(prev);
479 | return prev;
480 | }
481 | }
482 | };
483 | }
484 | }
485 | }
486 | };
487 | }
488 | ]);
489 |
490 | }).call(this);
491 |
--------------------------------------------------------------------------------