sharkey-web 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.md +19 -0
  5. data/README.md +188 -0
  6. data/Rakefile +8 -0
  7. data/bin/sharkey-web +9 -0
  8. data/config.ru +3 -0
  9. data/lib/sharkey.rb +12 -0
  10. data/lib/sharkey/app.rb +526 -0
  11. data/lib/sharkey/importerexporter.rb +79 -0
  12. data/lib/sharkey/models.rb +295 -0
  13. data/lib/sharkey/public/css/loading.gif +0 -0
  14. data/lib/sharkey/public/css/magicsuggest.css +232 -0
  15. data/lib/sharkey/public/css/nprogress.css +74 -0
  16. data/lib/sharkey/public/css/styles.css +263 -0
  17. data/lib/sharkey/public/css/ui.fancytree.css +545 -0
  18. data/lib/sharkey/public/data/sentences.txt +5 -0
  19. data/lib/sharkey/public/fonts/Quadrata.eot +0 -0
  20. data/lib/sharkey/public/fonts/Quadrata.svg +613 -0
  21. data/lib/sharkey/public/fonts/Quadrata.ttf +0 -0
  22. data/lib/sharkey/public/fonts/Quadrata.woff +0 -0
  23. data/lib/sharkey/public/fonts/Quadrata.zip +0 -0
  24. data/lib/sharkey/public/images/loader.gif +0 -0
  25. data/lib/sharkey/public/images/sharkey-logo.png +0 -0
  26. data/lib/sharkey/public/images/sharkey.png +0 -0
  27. data/lib/sharkey/public/js/ajaxmanager.js +67 -0
  28. data/lib/sharkey/public/js/keybindings.js +92 -0
  29. data/lib/sharkey/public/js/lib/bootstrap.min.js +6 -0
  30. data/lib/sharkey/public/js/lib/jquery-1.9.1.min.js +5 -0
  31. data/lib/sharkey/public/js/lib/jquery-ui.js +16150 -0
  32. data/lib/sharkey/public/js/lib/jquery.bootstrap-autohidingnavbar.js +213 -0
  33. data/lib/sharkey/public/js/lib/jquery.fancytree-all.js +6424 -0
  34. data/lib/sharkey/public/js/lib/jquery.tagcloud.js +92 -0
  35. data/lib/sharkey/public/js/lib/magicsuggest.js +1468 -0
  36. data/lib/sharkey/public/js/lib/mousetrap.min.js +9 -0
  37. data/lib/sharkey/public/js/lib/nprogress.js +476 -0
  38. data/lib/sharkey/public/js/page-add-link-autofill.js +102 -0
  39. data/lib/sharkey/public/js/page-add-link.js +156 -0
  40. data/lib/sharkey/public/js/page-categories.js +348 -0
  41. data/lib/sharkey/public/js/page-edit-link.js +103 -0
  42. data/lib/sharkey/public/js/page-links.js +54 -0
  43. data/lib/sharkey/public/js/page-settings.js +93 -0
  44. data/lib/sharkey/public/js/page-tagcloud.js +35 -0
  45. data/lib/sharkey/public/js/page-tags.js +287 -0
  46. data/lib/sharkey/public/js/scripts.js +147 -0
  47. data/lib/sharkey/public/themes/amelia/style.css +7 -0
  48. data/lib/sharkey/public/themes/bootstrap/style.css +5785 -0
  49. data/lib/sharkey/public/themes/cerulean/style.css +7 -0
  50. data/lib/sharkey/public/themes/cosmo/style.css +7 -0
  51. data/lib/sharkey/public/themes/cyborg/style.css +7 -0
  52. data/lib/sharkey/public/themes/darkly/style.css +7 -0
  53. data/lib/sharkey/public/themes/facebook-like/README.md +6 -0
  54. data/lib/sharkey/public/themes/facebook-like/style.css +6085 -0
  55. data/lib/sharkey/public/themes/flatly/style.css +7 -0
  56. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.svg +229 -0
  58. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/lib/sharkey/public/themes/holo-like/README.md +5 -0
  61. data/lib/sharkey/public/themes/holo-like/style.css +5997 -0
  62. data/lib/sharkey/public/themes/journal/style.css +7 -0
  63. data/lib/sharkey/public/themes/lumen/style.css +7 -0
  64. data/lib/sharkey/public/themes/readable/style.css +7 -0
  65. data/lib/sharkey/public/themes/simplex/style.css +7 -0
  66. data/lib/sharkey/public/themes/slate/style.css +7 -0
  67. data/lib/sharkey/public/themes/spacelab/style.css +7 -0
  68. data/lib/sharkey/public/themes/superhero/style.css +7 -0
  69. data/lib/sharkey/public/themes/united/style.css +7 -0
  70. data/lib/sharkey/public/themes/yeti/style.css +7 -0
  71. data/lib/sharkey/setting.rb +74 -0
  72. data/lib/sharkey/version.rb +5 -0
  73. data/lib/sharkey/views/404.slim +4 -0
  74. data/lib/sharkey/views/about.slim +7 -0
  75. data/lib/sharkey/views/add_link.slim +157 -0
  76. data/lib/sharkey/views/categories.slim +101 -0
  77. data/lib/sharkey/views/category.slim +22 -0
  78. data/lib/sharkey/views/centered.slim +49 -0
  79. data/lib/sharkey/views/dashboard.slim +107 -0
  80. data/lib/sharkey/views/dashboard_index.slim +26 -0
  81. data/lib/sharkey/views/edit_link.slim +121 -0
  82. data/lib/sharkey/views/help.slim +74 -0
  83. data/lib/sharkey/views/keybindings.slim +58 -0
  84. data/lib/sharkey/views/link.slim +30 -0
  85. data/lib/sharkey/views/links.slim +22 -0
  86. data/lib/sharkey/views/navbar.slim +68 -0
  87. data/lib/sharkey/views/settings.slim +74 -0
  88. data/lib/sharkey/views/settings_index.slim +220 -0
  89. data/lib/sharkey/views/single_category.slim +37 -0
  90. data/lib/sharkey/views/single_link.slim +95 -0
  91. data/lib/sharkey/views/single_tag.slim +33 -0
  92. data/lib/sharkey/views/tag.slim +22 -0
  93. data/lib/sharkey/views/tagcloud.slim +54 -0
  94. data/lib/sharkey/views/tags.slim +99 -0
  95. data/sharkey-web.gemspec +44 -0
  96. metadata +324 -0
@@ -0,0 +1,102 @@
1
+ /*global $*/
2
+
3
+ ($(function(){
4
+
5
+ // *****************************************************
6
+ // CODE DUPLICATION
7
+ // (see `scripts.js`)
8
+ /**
9
+ * Returns a new cute spinner GIF for
10
+ * showing indeterminate progress.
11
+ */
12
+ function newSpinner() {
13
+ return $('<img/>').attr("src", "/images/loader.gif");
14
+ }
15
+ /* Here we preload the image so it won't take long
16
+ * when creating it for the first time */
17
+ newSpinner().appendTo('body').hide();
18
+ // *****************************************************
19
+
20
+
21
+
22
+ /* When the user finishes entering a URL, we make an
23
+ * AJAX request to know that URL's title and description.
24
+ *
25
+ * We'll use that to automatically fill the <inputs>
26
+ * on the "Add Link" dialog.
27
+ *
28
+ * But if the user starts typing something on the title
29
+ * input then we abort that request.
30
+ */
31
+
32
+ // This will contain the AJAX request for when we ask
33
+ // for the link info.
34
+ var linkMetadataRequest;
35
+
36
+ // If the user starts typing on the title input
37
+ // we cancel the AJAX request
38
+ //
39
+ $('#input-link-title').keypress(function() {
40
+ if (typeof(linkMetadataRequest) !== 'undefined')
41
+ linkMetadataRequest.abort();
42
+ });
43
+
44
+ // And here we make it so that when the user leaves
45
+ // the URL input, we start making that request.
46
+ //
47
+ // When it starts it'll place a cute spinner to show
48
+ // progress.
49
+ //
50
+ // @note: It will only put things on the input if
51
+ // there's nothing there!
52
+ // In other words, it won't override existing
53
+ // values!
54
+ $('#add-link #input-link-url').focusout(function() {
55
+
56
+ // Don't request title for empty URL
57
+ var text = $(this).val();
58
+ if (text === "")
59
+ return;
60
+
61
+ // Won't do anything if all the fields
62
+ // are already filled
63
+ var title_input = $('#add-link #input-link-title');
64
+ var comment_input = $('#add-link .input-links-comment');
65
+ if ((title_input.val() !== '') && (comment_input.val() !== ''))
66
+ return;
67
+
68
+ var spinner_place = $('#single-link span.spinner-placeholder');
69
+ var spinner = $(newSpinner()).appendTo(spinner_place);
70
+
71
+ linkMetadataRequest = $.ajax({
72
+ url: '/metadata',
73
+ method: 'GET',
74
+ data: {
75
+ url: text
76
+ },
77
+
78
+ success: function(responseData, textStatus, jqXHR) {
79
+ spinner.remove();
80
+
81
+ var response = $.parseJSON(responseData);
82
+
83
+ var title = response.pageTitle;
84
+ var description = response.pageDescription;
85
+
86
+ if (title_input.val() === '')
87
+ title_input.val(title);
88
+
89
+ if (comment_input.val() === '')
90
+ comment_input.val(description);
91
+ },
92
+
93
+ complete: function() {
94
+ spinner.remove();
95
+ }
96
+ });
97
+
98
+ });
99
+
100
+ }));
101
+
102
+
@@ -0,0 +1,156 @@
1
+ /*global $*/
2
+
3
+ /**
4
+ * Scripts for both the "Add Link" dialog and the "Edit Link".
5
+ *
6
+ * They're Bootstrap Modals that initially appear hidden and only
7
+ * get shown when requested.
8
+ */
9
+ ($(function(){
10
+
11
+ // *****************************************************
12
+ // CODE DUPLICATION
13
+ // (see `scripts.js`)
14
+ /**
15
+ * Returns a new cute spinner GIF for
16
+ * showing indeterminate progress.
17
+ */
18
+ function newSpinner() {
19
+ return $('<img/>').attr("src", "/images/loader.gif");
20
+ }
21
+ /* Here we preload the image so it won't take long
22
+ * when creating it for the first time */
23
+ newSpinner().appendTo('body').hide();
24
+ // *****************************************************
25
+
26
+
27
+
28
+ // Prevent user from double-submitting a <form>
29
+ // 1. Make buttons hide when user submits
30
+ // (either by clicking on 'Add Link' or pressing Enter)
31
+ // 2. As soon as a <form> starts being submitted,
32
+ // disable further submissions.
33
+ var submitting = false;
34
+ $('.input-link form').submit(function() {
35
+ // By returning false we prevent further submissions
36
+ if (submitting)
37
+ return false;
38
+
39
+ // <input> validation!
40
+ // Only submit if we have an URL on the <form>
41
+ if ($('.input-link-url').val() !== "") {
42
+
43
+ $('.input-link form button').addClass('disabled');
44
+ submitting = true;
45
+ }
46
+ // Makes the form start submitting
47
+ return true;
48
+ });
49
+
50
+
51
+ /**
52
+ * The awesome new category form.
53
+ * We have a button "New Category" that shows
54
+ * a form when clicked.
55
+ *
56
+ * When that form is filled, we hide it and
57
+ * show the button again.
58
+ */
59
+ $('.new-category-form').hide();
60
+
61
+ $('.new-category').click(function(e) {
62
+ /* otherwise it would trigger the form validation */
63
+ e.preventDefault();
64
+
65
+ $(this).hide();
66
+ $('.new-category-form').show();
67
+ });
68
+
69
+ /* ...and when this form is submitted, we send an AJAX
70
+ * request for a new category.
71
+ *
72
+ * If successful it refreshes both <select> fields on
73
+ * the page.
74
+ */
75
+ $('.new-category-form').submit(function(e) {
76
+ /* Don't refresh the page! */
77
+ e.preventDefault();
78
+
79
+ /* Will replace the button text with a nice
80
+ * progress spinner */
81
+ var form = $(this);
82
+ var button = $('.new-category-button');
83
+
84
+ var buttonText = button.html();
85
+ var spinner = $(newSpinner());
86
+
87
+ button.html(spinner);
88
+
89
+ $.ajax({
90
+ url: form.attr('action'),
91
+ type: 'POST',
92
+ data: {
93
+ name: $('.new-category-name').val(),
94
+ parent: $('.new-category-parent').val()
95
+ },
96
+
97
+ /* Will add the latest Category to the top of the
98
+ * dropdown menu and mark it as selected
99
+ */
100
+ success: function(responseData, textStatus, jqXHR) {
101
+ var dropdown1 = $('.input-link-category-one');
102
+ var dropdown2 = $('.input-link-category-two');
103
+
104
+ var parsedResponse = $.parseJSON(responseData);
105
+ var value = parsedResponse['id'];
106
+ var name = parsedResponse['name'];
107
+
108
+ dropdown1
109
+ .prepend('<option value="' + value + '">' + name + '</option>')
110
+ .val(value);
111
+ dropdown2
112
+ .prepend('<option value="' + value + '">' + name + '</option>')
113
+ .val(value);
114
+ },
115
+ error: function(responseData, textStatus, jqXHR) {
116
+ /* This error-handling function sucks! */
117
+ // form.append(
118
+ // $("<div>Couldn't apply the setting - please reload the page</div>")
119
+ // .attr('class', 'alert alert-danger')
120
+ // .attr('role', 'alert')
121
+ // );
122
+ alert('no!');
123
+ },
124
+ complete: function() {
125
+ /* taking out the spinner image */
126
+ button.html(buttonText);
127
+ spinner.remove();
128
+
129
+ form.hide();
130
+ $('.new-category').show();
131
+ }
132
+ });
133
+ });
134
+
135
+ /* Beautiful tag-handling stuff */
136
+ $('.tagsinput').magicSuggest({
137
+ /* URL to make an AJAX call to get results */
138
+ data: '/tags',
139
+ /* by default it uses POST, what a bummer */
140
+ method: 'get',
141
+ /* how much it waits before triggering the AJAX query */
142
+ typeDelay: 0,
143
+ noSuggestionText: 'No tags like this',
144
+ placeholder: 'Yeah',
145
+ /* use , to add a tag
146
+ * (why does it not work?
147
+ * it keeps accepting tags ending with commas!) */
148
+ useCommaKey: true,
149
+ /* choose how many tags you want */
150
+ maxSelection: null,
151
+ /* show lines with alternated colors */
152
+ useZebraStyle: true
153
+ });
154
+
155
+ }));
156
+
@@ -0,0 +1,348 @@
1
+ /*global $,ajaxManager*/
2
+
3
+ /**
4
+ * Scripts specific to the Categories page
5
+ *
6
+ * Requires FancyTree
7
+ */
8
+ ($(function(){
9
+
10
+
11
+ // When the page loads, focus on the Category Browser
12
+ $('#categories .fancytree-container').focus();
13
+
14
+
15
+ /**
16
+ * Initializing the tree view for all the Categories
17
+ */
18
+ $('#categories').fancytree({
19
+ extensions: ["glyph", "childcounter"],
20
+ glyph: {
21
+ map: {
22
+ doc: "glyphicon glyphicon-link",
23
+ docOpen: "glyphicon glyphicon-link",
24
+ checkbox: "glyphicon glyphicon-unchecked",
25
+ checkboxSelected:"glyphicon glyphicon-check",
26
+ checkboxUnknown: "glyphicon glyphicon-edit",
27
+ expanderClosed: "glyphicon glyphicon-expand",
28
+ expanderLazy: "glyphicon glyphicon-expand",
29
+ expanderOpen: "glyphicon glyphicon-collapse-down",
30
+ folder: "glyphicon glyphicon-book",
31
+ folderOpen: "glyphicon glyphicon-book"
32
+ }
33
+ },
34
+ keyboard: true,
35
+
36
+ /* when an item is clicked (either Tag or Link) */
37
+ click: function(event, data) {
38
+
39
+ // Clicked item on the list
40
+ //var node = data.node;
41
+
42
+ /* Whenever you click on a link, let's make an
43
+ * AJAX request to add it's visit count
44
+ */
45
+ var element = event.toElement;
46
+
47
+ if ((element) && ($(element).attr('class') === 'link-link')) {
48
+
49
+ var link_id = $(element).attr('data-link-id');
50
+
51
+ $.ajax({
52
+ url: '/visit/' + link_id,
53
+ type: 'POST',
54
+
55
+ // Yeah, won't do anything if we succeed
56
+
57
+ success: function(responseData, textStatus, jqXHR) {
58
+ console.log($.parseJSON(responseData).visitCount);
59
+ },
60
+
61
+ error: function(responseData, textStatus, jqXHR) {
62
+ console.log(
63
+ "Error! Couldn't set Link as visited!\n" +
64
+ "responseData: " + responseData + "\n" +
65
+ "textStatus: " + textStatus + "\n" +
66
+ "jqXHR: " + jqXHR
67
+ );
68
+ }
69
+ });
70
+ }
71
+ },
72
+ /* when user presses ENTER or SPACE inside an item */
73
+ link: function(event, data) {
74
+ /* redirect to the internal link
75
+ * (either Category or Link) */
76
+ var href = $(
77
+ $.parseHTML(data.node.title)
78
+ ).attr('href');
79
+
80
+ window.location.href = href;
81
+ }
82
+ });
83
+ /**
84
+ * Helper to ease applying things to every
85
+ * node inside the tree view.
86
+ *
87
+ * @param apply Function to apply to each node.
88
+ */
89
+ var foreachNode = function(apply) {
90
+ $('#categories')
91
+ .fancytree('getTree')
92
+ .visit(function(node) {
93
+ apply(node);
94
+ });
95
+ };
96
+ /* Things using FancyTree's API */
97
+ $('#categories-expand').click(function() {
98
+ foreachNode(function(node) {
99
+ node.setExpanded(true);
100
+ });
101
+ });
102
+
103
+ $('#categories-collapse').click(function() {
104
+ foreachNode(function(node) {
105
+ node.setExpanded(false);
106
+ });
107
+ });
108
+
109
+ /*
110
+ * Now we define some functions that will handle the
111
+ * editing progress of the Categories.
112
+ */
113
+
114
+ // Flag to tell if we're editing the Categories
115
+ var categoryIsEditing = false;
116
+
117
+ // All the buttons for the editing process
118
+ var categoryEditButtons = $(
119
+ '#categories-select-all, #categories-select-none, #categories-select-toggle, #categories-delete, #categories-delete-links'
120
+ );
121
+ // Initially we're not editing
122
+ categoryEditButtons.hide();
123
+
124
+ /* Start editing, baby! */
125
+ $('#categories-edit').click(function() {
126
+ categoryIsEditing = !categoryIsEditing;
127
+
128
+ if (categoryIsEditing) {
129
+ // Change the button's appearance
130
+ $(this).html(
131
+ "<button class='btn btn-default'><span class='glyphicon glyphicon-ban-circle' /> Cancel</button>"
132
+ );
133
+
134
+ /* Reinitializing the tree view, this time
135
+ * enabling checkboxes and multi-selection
136
+ * mode */
137
+ $('#categories').fancytree({
138
+ checkbox: true,
139
+ selectMode: 2,
140
+
141
+ // When double-clicking, toggle!
142
+ dblclick: function(event, data) {
143
+ data.node.toggleSelected();
144
+ }
145
+ });
146
+ categoryEditButtons.show();
147
+
148
+ // Here we only let the user select CATEGORIES,
149
+ // not LINKS!
150
+ // Categories are considered folders, because they
151
+ foreachNode(function(node) {
152
+ if (! node.isFolder())
153
+ node.unselectable = true;
154
+ });
155
+
156
+ }
157
+ else {
158
+ // Change the button's appearance AGAIN
159
+ $(this).html(
160
+ "<button class='btn btn-default'><span class='glyphicon glyphicon-pencil' /> Edit</button>"
161
+ );
162
+ $('#categories').fancytree({
163
+ checkbox:false
164
+ });
165
+ categoryEditButtons.hide();
166
+ }
167
+ });
168
+
169
+ // And now, what happens when you click
170
+ // on each of those fancy buttons
171
+
172
+ $('#categories-select-all').click(function() {
173
+ foreachNode(function(node) {
174
+ node.setSelected(true);
175
+ });
176
+ });
177
+ $('#categories-select-none').click(function() {
178
+ foreachNode(function(node) {
179
+ node.setSelected(false);
180
+ });
181
+ });
182
+ $('#categories-select-toggle').click(function() {
183
+ foreachNode(function(node) {
184
+ node.toggleSelected();
185
+ });
186
+ });
187
+
188
+ // Now, when you click to delete categories, we musc
189
+ // communicate with the server
190
+ $('#categories-delete').click(function() {
191
+
192
+ // Will also destroy links that has these categories
193
+ var destroyLinks = $('#categories-delete-links input').is(':checked');
194
+
195
+ // We will send several DELETE requests
196
+ // to the server, each with a selected
197
+ // category's ID
198
+ var maxSize = $('#categories')
199
+ .fancytree('getTree')
200
+ .getSelectedNodes()
201
+ .length;
202
+
203
+ // And we place a beautiful progress bar,
204
+ // to make the user not think we crashed
205
+ var progressbarParent = $(
206
+ "<div class='progress progress-striped active'></div>"
207
+ );
208
+ var progressbarText = $("<span>0%</span>");
209
+ var progressbar = $(
210
+ "<div class='progress-bar' " +
211
+ "role='progressbar' " +
212
+ "aria-valuenow='0' " +
213
+ "aria-valuemin='0' " +
214
+ "aria-valuemax='" + maxSize + "'></div>"
215
+ );
216
+
217
+ // Replace the buttons by the progress bar
218
+ $('#categories-buttons').html('');
219
+ progressbarParent.appendTo($('#categories-buttons'));
220
+ progressbar.appendTo(progressbarParent);
221
+ progressbarText.appendTo(progressbar);
222
+
223
+ var deletedCount = 0;
224
+
225
+ foreachNode(function(node) {
226
+
227
+ if (!node.selected)
228
+ return;
229
+
230
+ // Each node has a `title` element,
231
+ // with an href like '/category/(ID)'
232
+ var href = $(
233
+ $.parseHTML(node.title)
234
+ ).attr('href');
235
+
236
+ // Now, time for the AJAX request...!
237
+ ajaxManager.add({
238
+ url: href,
239
+ type: 'DELETE',
240
+ data: {
241
+ destroy_links: destroyLinks
242
+ },
243
+ /* Showing a red background on deleted categories */
244
+ success: function(responseData, textStatus, jqXHR) {
245
+
246
+ // Updating the progress bar
247
+ deletedCount += 1;
248
+ progressbar
249
+ .attr('style', 'width:' + (deletedCount/maxSize)*100 + '%')
250
+ .attr('aria-valuenow', deletedCount);
251
+ progressbarText
252
+ .text(Math.round( ((deletedCount/maxSize)*100) * 100)/100+ '%');
253
+
254
+ // This animation requires jQueryUI
255
+ $(node.span).animate({
256
+ backgroundColor: '#FFDFDF'
257
+ }, 1000);
258
+
259
+ // Removing the element on the raw list
260
+ // generated by sinatra
261
+ $('#categories')
262
+ .find("li span a[href='" + href + "']")
263
+ .each(function() {
264
+ $(this).parent('li').remove();
265
+ });
266
+ },
267
+ error: function(responseData, textStatus, jqXHR) {
268
+ /* This error-handling function sucks! */
269
+ alert("Couldn't remove category! " + responseData + ', ' + textStatus);
270
+ }
271
+ });
272
+ });
273
+ // When everything's done...
274
+ ajaxManager.complete = function() {
275
+
276
+ // Now the progress bar is shown as completed
277
+ // and the user is advised to refresh the page
278
+ progressbarParent.removeClass('active');
279
+ progressbarParent.removeClass('progress-striped');
280
+ progressbar.addClass('progress-bar-success');
281
+ progressbarText.text("Done! Refresh page to see the changes");
282
+ };
283
+
284
+ // Now, do perform those requests for me!
285
+ ajaxManager.run();
286
+ });
287
+
288
+
289
+
290
+
291
+ // // When the page loads, focus on the categories browser
292
+ // $('#categories .fancytree-container').focus();
293
+
294
+ // /**
295
+ // * FancyTree makes easy to show a tree view, just like
296
+ // * a file browser on a file system.
297
+ // *
298
+ // * We use it on the Categories page.
299
+ // */
300
+ // $('#categories').fancytree({
301
+ // extensions: ["glyph", "childcounter"],
302
+ // glyph: {
303
+ // map: {
304
+ // doc: "glyphicon glyphicon-link",
305
+ // folder: "glyphicon glyphicon-book",
306
+ // expanderClosed: "glyphicon glyphicon-expand",
307
+ // expanderLazy: "glyphicon glyphicon-expand",
308
+ // expanderOpen: "glyphicon glyphicon-collapse-down"
309
+ // }
310
+ // },
311
+ // keyboard: true,
312
+
313
+ // /* when an item is clicked (either Category or Link) */
314
+ // activate: function(event, data) {
315
+ // /* Nothing for now... */
316
+ // //var node = data.node;
317
+ // //console.log(data);
318
+ // },
319
+ // /* when user presses ENTER or SPACE inside an item */
320
+ // link: function(event, data) {
321
+ // /* redirect to the internal link
322
+ // * (either Category or Link) */
323
+ // var href = $(
324
+ // $.parseHTML(data.node.title)
325
+ // ).attr('href');
326
+
327
+ // window.location.href = href;
328
+ // }
329
+ // });
330
+ // /* Things using FancyTree's API */
331
+ // $('#categories-expand').click(function() {
332
+ // // For each node, expand it!
333
+ // $('#categories')
334
+ // .fancytree('getTree')
335
+ // .visit(function(node) {
336
+ // node.setExpanded(true);
337
+ // });
338
+ // });
339
+ // $('#categories-collapse').click(function() {
340
+ // // TODO: Remove this code repetition!
341
+ // $('#categories')
342
+ // .fancytree('getTree')
343
+ // .visit(function(node) {
344
+ // node.setExpanded(false);
345
+ // });
346
+ // });
347
+ }));
348
+