sharkey-web 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|