sharkey-web 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE.md +19 -0
- data/README.md +188 -0
- data/Rakefile +8 -0
- data/bin/sharkey-web +9 -0
- data/config.ru +3 -0
- data/lib/sharkey.rb +12 -0
- data/lib/sharkey/app.rb +526 -0
- data/lib/sharkey/importerexporter.rb +79 -0
- data/lib/sharkey/models.rb +295 -0
- data/lib/sharkey/public/css/loading.gif +0 -0
- data/lib/sharkey/public/css/magicsuggest.css +232 -0
- data/lib/sharkey/public/css/nprogress.css +74 -0
- data/lib/sharkey/public/css/styles.css +263 -0
- data/lib/sharkey/public/css/ui.fancytree.css +545 -0
- data/lib/sharkey/public/data/sentences.txt +5 -0
- data/lib/sharkey/public/fonts/Quadrata.eot +0 -0
- data/lib/sharkey/public/fonts/Quadrata.svg +613 -0
- data/lib/sharkey/public/fonts/Quadrata.ttf +0 -0
- data/lib/sharkey/public/fonts/Quadrata.woff +0 -0
- data/lib/sharkey/public/fonts/Quadrata.zip +0 -0
- data/lib/sharkey/public/images/loader.gif +0 -0
- data/lib/sharkey/public/images/sharkey-logo.png +0 -0
- data/lib/sharkey/public/images/sharkey.png +0 -0
- data/lib/sharkey/public/js/ajaxmanager.js +67 -0
- data/lib/sharkey/public/js/keybindings.js +92 -0
- data/lib/sharkey/public/js/lib/bootstrap.min.js +6 -0
- data/lib/sharkey/public/js/lib/jquery-1.9.1.min.js +5 -0
- data/lib/sharkey/public/js/lib/jquery-ui.js +16150 -0
- data/lib/sharkey/public/js/lib/jquery.bootstrap-autohidingnavbar.js +213 -0
- data/lib/sharkey/public/js/lib/jquery.fancytree-all.js +6424 -0
- data/lib/sharkey/public/js/lib/jquery.tagcloud.js +92 -0
- data/lib/sharkey/public/js/lib/magicsuggest.js +1468 -0
- data/lib/sharkey/public/js/lib/mousetrap.min.js +9 -0
- data/lib/sharkey/public/js/lib/nprogress.js +476 -0
- data/lib/sharkey/public/js/page-add-link-autofill.js +102 -0
- data/lib/sharkey/public/js/page-add-link.js +156 -0
- data/lib/sharkey/public/js/page-categories.js +348 -0
- data/lib/sharkey/public/js/page-edit-link.js +103 -0
- data/lib/sharkey/public/js/page-links.js +54 -0
- data/lib/sharkey/public/js/page-settings.js +93 -0
- data/lib/sharkey/public/js/page-tagcloud.js +35 -0
- data/lib/sharkey/public/js/page-tags.js +287 -0
- data/lib/sharkey/public/js/scripts.js +147 -0
- data/lib/sharkey/public/themes/amelia/style.css +7 -0
- data/lib/sharkey/public/themes/bootstrap/style.css +5785 -0
- data/lib/sharkey/public/themes/cerulean/style.css +7 -0
- data/lib/sharkey/public/themes/cosmo/style.css +7 -0
- data/lib/sharkey/public/themes/cyborg/style.css +7 -0
- data/lib/sharkey/public/themes/darkly/style.css +7 -0
- data/lib/sharkey/public/themes/facebook-like/README.md +6 -0
- data/lib/sharkey/public/themes/facebook-like/style.css +6085 -0
- data/lib/sharkey/public/themes/flatly/style.css +7 -0
- data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/sharkey/public/themes/holo-like/README.md +5 -0
- data/lib/sharkey/public/themes/holo-like/style.css +5997 -0
- data/lib/sharkey/public/themes/journal/style.css +7 -0
- data/lib/sharkey/public/themes/lumen/style.css +7 -0
- data/lib/sharkey/public/themes/readable/style.css +7 -0
- data/lib/sharkey/public/themes/simplex/style.css +7 -0
- data/lib/sharkey/public/themes/slate/style.css +7 -0
- data/lib/sharkey/public/themes/spacelab/style.css +7 -0
- data/lib/sharkey/public/themes/superhero/style.css +7 -0
- data/lib/sharkey/public/themes/united/style.css +7 -0
- data/lib/sharkey/public/themes/yeti/style.css +7 -0
- data/lib/sharkey/setting.rb +74 -0
- data/lib/sharkey/version.rb +5 -0
- data/lib/sharkey/views/404.slim +4 -0
- data/lib/sharkey/views/about.slim +7 -0
- data/lib/sharkey/views/add_link.slim +157 -0
- data/lib/sharkey/views/categories.slim +101 -0
- data/lib/sharkey/views/category.slim +22 -0
- data/lib/sharkey/views/centered.slim +49 -0
- data/lib/sharkey/views/dashboard.slim +107 -0
- data/lib/sharkey/views/dashboard_index.slim +26 -0
- data/lib/sharkey/views/edit_link.slim +121 -0
- data/lib/sharkey/views/help.slim +74 -0
- data/lib/sharkey/views/keybindings.slim +58 -0
- data/lib/sharkey/views/link.slim +30 -0
- data/lib/sharkey/views/links.slim +22 -0
- data/lib/sharkey/views/navbar.slim +68 -0
- data/lib/sharkey/views/settings.slim +74 -0
- data/lib/sharkey/views/settings_index.slim +220 -0
- data/lib/sharkey/views/single_category.slim +37 -0
- data/lib/sharkey/views/single_link.slim +95 -0
- data/lib/sharkey/views/single_tag.slim +33 -0
- data/lib/sharkey/views/tag.slim +22 -0
- data/lib/sharkey/views/tagcloud.slim +54 -0
- data/lib/sharkey/views/tags.slim +99 -0
- data/sharkey-web.gemspec +44 -0
- 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
|
+
|