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,103 @@
|
|
1
|
+
/*global $*/
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Scripts for the "Edit Link" dialog.
|
5
|
+
*/
|
6
|
+
($(function(){
|
7
|
+
|
8
|
+
var clearEditLinkDialog = function() {
|
9
|
+
|
10
|
+
$('#edit-link input').each(function(a, element) {
|
11
|
+
$(element).val('');
|
12
|
+
});
|
13
|
+
|
14
|
+
// This is due to the awesome tag-input helper
|
15
|
+
$('#edit-link #input-link-tags input').val('');
|
16
|
+
|
17
|
+
$('#edit-link select').val(0);
|
18
|
+
};
|
19
|
+
|
20
|
+
/**
|
21
|
+
* When you click to "Edit Link", Bootstrap will automatically
|
22
|
+
* show the dialog (modal).
|
23
|
+
* But we need to fill it with this link's current attributes.
|
24
|
+
*/
|
25
|
+
$('button.link-edit').click(function() {
|
26
|
+
|
27
|
+
// Just in case we tried to edit a link before
|
28
|
+
// and bailed out
|
29
|
+
clearEditLinkDialog();
|
30
|
+
|
31
|
+
var link = $(
|
32
|
+
$(this).parents('li.link')
|
33
|
+
);
|
34
|
+
|
35
|
+
// First, we need to obtain the current data for the link
|
36
|
+
//
|
37
|
+
// I could make an AJAX request to the server, but I'd
|
38
|
+
// rather crawl everything from the current page.
|
39
|
+
//
|
40
|
+
// Unfortunately, if I change the layout then everything
|
41
|
+
// FUCKS UP
|
42
|
+
|
43
|
+
var link_id = link.attr('data-link-id');
|
44
|
+
|
45
|
+
var link_url = link.find('span.link-title > a').attr('href');
|
46
|
+
var link_title = link.find('span.link-title').text();
|
47
|
+
|
48
|
+
/* Creating an array of tag names */
|
49
|
+
var link_tags = link.find('.link-tags a');
|
50
|
+
if ((link_tags) && (link_tags.length > 0)) {
|
51
|
+
var tmp = [];
|
52
|
+
link_tags.each(function(a, element) {
|
53
|
+
tmp.push($(element).attr('title'));
|
54
|
+
});
|
55
|
+
link_tags = tmp;
|
56
|
+
}
|
57
|
+
else
|
58
|
+
link_tags = [];
|
59
|
+
|
60
|
+
var link_comments = link.find('span.link-description').text();
|
61
|
+
|
62
|
+
/* Creating an array of category names */
|
63
|
+
var link_category = link.find('.link-categories a');
|
64
|
+
if ((link_category) && (link_category.length > 0)) {
|
65
|
+
var tmp = link_category[0];
|
66
|
+
|
67
|
+
// It will be an <a> with href like `/category/id`
|
68
|
+
// When split it will become ["", "category", "id"]
|
69
|
+
link_category = $(tmp).attr('href').split('/')[2];
|
70
|
+
}
|
71
|
+
else
|
72
|
+
link_category = "";
|
73
|
+
|
74
|
+
// Now we fill the "Edit Link" form with the data
|
75
|
+
// we just got. Brace yourselves!
|
76
|
+
$('#edit-link #input-link-url').val(link_url);
|
77
|
+
$('#edit-link #input-link-title').val(link_title);
|
78
|
+
|
79
|
+
// Now here's an EXCEPTION
|
80
|
+
// Since we're using an awesome plugin to visually
|
81
|
+
// handle the tags, we must accommodate to it.
|
82
|
+
// It actually has a hidden input like this:
|
83
|
+
// THIS IS DUPLICATED CODE OMG WHAT DO I DO
|
84
|
+
$('#edit-link #input-link-tags').magicSuggest({
|
85
|
+
data: '/tags',
|
86
|
+
method: 'get',
|
87
|
+
typeDelay: 0,
|
88
|
+
noSuggestionText: 'No tags like this',
|
89
|
+
placeholder: 'Yeah',
|
90
|
+
useCommaKey: true,
|
91
|
+
maxSelection: null,
|
92
|
+
useZebraStyle: true
|
93
|
+
}).setValue(link_tags);
|
94
|
+
|
95
|
+
$('#edit-link #input-link-comment').val(link_comments);
|
96
|
+
$('#edit-link #input-link-category-one').val(link_category);
|
97
|
+
|
98
|
+
$( $('#edit-link form')[0]) .attr('action', '/update/link/' + link_id);
|
99
|
+
});
|
100
|
+
|
101
|
+
}));
|
102
|
+
|
103
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
/*global $*/
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Scripts specific to the Links page.
|
5
|
+
*
|
6
|
+
* Requires jQuery.
|
7
|
+
*/
|
8
|
+
($(function(){
|
9
|
+
|
10
|
+
/* Whenever you click on a link, let's make an AJAX request
|
11
|
+
* to add it's visit count
|
12
|
+
*/
|
13
|
+
$('.link-title > a').click(function(e) {
|
14
|
+
|
15
|
+
// Kinda ugly way of traversing the DOM
|
16
|
+
// It WILL mess up if I ever change the HTML
|
17
|
+
// structure... Maybe I should find a better way
|
18
|
+
var link_element = $(e.target).parents('li');
|
19
|
+
var link_id = link_element.attr('data-link-id');
|
20
|
+
|
21
|
+
$.ajax({
|
22
|
+
url: '/visit/' + link_id,
|
23
|
+
type: 'POST',
|
24
|
+
|
25
|
+
success: function(responseData, textStatus, jqXHR) {
|
26
|
+
|
27
|
+
// Updating the link's visited text with
|
28
|
+
// the new count.
|
29
|
+
var new_count = $.parseJSON(responseData).visitCount;
|
30
|
+
if (new_count) {
|
31
|
+
|
32
|
+
link_element.find('.link-visited-date').html(
|
33
|
+
'Last visited a moment ago'
|
34
|
+
);
|
35
|
+
link_element.find('.link-visited-count').html(
|
36
|
+
'<span class="glyphicon glyphicon-open"></span> ' + new_count
|
37
|
+
);
|
38
|
+
}
|
39
|
+
},
|
40
|
+
error: function(responseData, textStatus, jqXHR) {
|
41
|
+
console.log(
|
42
|
+
"Error! Couldn't set Link as visited!\n" +
|
43
|
+
"responseData: " + responseData + "\n" +
|
44
|
+
"textStatus: " + textStatus + "\n" +
|
45
|
+
"jqXHR: " + jqXHR
|
46
|
+
);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
});
|
50
|
+
|
51
|
+
}));
|
52
|
+
|
53
|
+
|
54
|
+
|
@@ -0,0 +1,93 @@
|
|
1
|
+
/*global $*/
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Scripts specific for the Settings page
|
5
|
+
*/
|
6
|
+
($(function(){
|
7
|
+
|
8
|
+
// *****************************************************
|
9
|
+
// CODE DUPLICATION
|
10
|
+
// (see `scripts.js`)
|
11
|
+
/**
|
12
|
+
* Returns a new cute spinner GIF for
|
13
|
+
* showing indeterminate progress.
|
14
|
+
*/
|
15
|
+
function newSpinner() {
|
16
|
+
return $('<img/>').attr("src", "/images/loader.gif");
|
17
|
+
}
|
18
|
+
/* Here we preload the image so it won't take long
|
19
|
+
* when creating it for the first time */
|
20
|
+
newSpinner().appendTo('body').hide();
|
21
|
+
// *****************************************************
|
22
|
+
|
23
|
+
|
24
|
+
/**
|
25
|
+
* AJAX requester for changing `input radio` settings.
|
26
|
+
*
|
27
|
+
* We need:
|
28
|
+
* - Parent `form`
|
29
|
+
* - Child `label` with class `ajax-radio`
|
30
|
+
*/
|
31
|
+
$('.ajax-radio').click(function() {
|
32
|
+
|
33
|
+
// The button we just pressed - will add `.active` class
|
34
|
+
// on success
|
35
|
+
var this_button = $(this);
|
36
|
+
|
37
|
+
var parent_form = this_button.parents('form');
|
38
|
+
|
39
|
+
// The button neighbor to the just pressed - will remove
|
40
|
+
// `.active` class on success
|
41
|
+
var that_button = parent_form.find('label:not(.active)');
|
42
|
+
|
43
|
+
/* URL for AJAX request: like '/delete_/DELETE_ID' */
|
44
|
+
var setting_url = this_button.parents('form').attr('action');
|
45
|
+
var setting_value = this_button.children('input').attr('value');
|
46
|
+
|
47
|
+
/* Remove button to prevent double request
|
48
|
+
* and create the cute loader image */
|
49
|
+
var spinner = $(newSpinner()).appendTo(parent_form);
|
50
|
+
|
51
|
+
$.ajax({
|
52
|
+
url: setting_url,
|
53
|
+
type: 'POST',
|
54
|
+
data: {
|
55
|
+
name: this_button.children('input').attr('name'),
|
56
|
+
value: setting_value
|
57
|
+
},
|
58
|
+
success: function(responseData, textStatus, jqXHR) {
|
59
|
+
|
60
|
+
// When this request succeds, Bootstrap JS already changed
|
61
|
+
// the buttons' .active classes.
|
62
|
+
// So the button we just pressed is this...
|
63
|
+
parent_form
|
64
|
+
.find('label.active')
|
65
|
+
.attr('class', 'btn btn-primary ajax-radio active');
|
66
|
+
|
67
|
+
// and the other one is that...
|
68
|
+
parent_form
|
69
|
+
.find('label:not(.active)')
|
70
|
+
.attr('class', 'btn btn-default ajax-radio');
|
71
|
+
},
|
72
|
+
error: function(responseData, textStatus, jqXHR) {
|
73
|
+
/* This error-handling function sucks! */
|
74
|
+
parent_form.append(
|
75
|
+
$("<div>Couldn't apply the setting - please reload the page</div>")
|
76
|
+
.attr('class', 'alert alert-danger')
|
77
|
+
.attr('role', 'alert')
|
78
|
+
);
|
79
|
+
},
|
80
|
+
complete: function() {
|
81
|
+
spinner.remove();
|
82
|
+
}
|
83
|
+
});
|
84
|
+
});
|
85
|
+
|
86
|
+
// It may take a while.
|
87
|
+
// Let's warn the user...!
|
88
|
+
$('#button-import').click(function() {
|
89
|
+
$(this).html('Hold on, this might take a while... ');
|
90
|
+
$(this).append(newSpinner());
|
91
|
+
});
|
92
|
+
}));
|
93
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/*global $,activateTagCloud*/
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Scripts specific to the TagCloud page.
|
5
|
+
*
|
6
|
+
* NOTE: we require jQuery TagCloud!
|
7
|
+
*/
|
8
|
+
|
9
|
+
$(function () {
|
10
|
+
|
11
|
+
/**
|
12
|
+
* This function activates the tag cloud functionality.
|
13
|
+
* Call it from the TagCloud page.
|
14
|
+
*`
|
15
|
+
* @note It's actually defined on the TagCloud page!
|
16
|
+
*
|
17
|
+
* @param smaller Tiniest possible font size allowed
|
18
|
+
* @param bigger Biggest possible font size allowed
|
19
|
+
*/
|
20
|
+
activateTagCloud = function(smaller, bigger) {
|
21
|
+
$.fn.tagcloud.defaults = {
|
22
|
+
size: {
|
23
|
+
start: smaller,
|
24
|
+
end: bigger,
|
25
|
+
unit: 'pt'
|
26
|
+
},
|
27
|
+
color: {
|
28
|
+
start: '#f5f5f5',
|
29
|
+
end: '#428bca'
|
30
|
+
}
|
31
|
+
};
|
32
|
+
$('#tagcloud a').tagcloud();
|
33
|
+
};
|
34
|
+
});
|
35
|
+
|
@@ -0,0 +1,287 @@
|
|
1
|
+
/**
|
2
|
+
* Scripts specific to the Tags page.
|
3
|
+
*
|
4
|
+
* Note that we assume jQuery, ajaxManager and other stuff.
|
5
|
+
*/
|
6
|
+
/*global $,ajaxManager*/
|
7
|
+
($(function() {
|
8
|
+
|
9
|
+
// When the page loads, focus on the Tag Browser
|
10
|
+
$('#tags .fancytree-container').focus();
|
11
|
+
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Initializing the tree view for all the Tags
|
15
|
+
*/
|
16
|
+
$('#tags').fancytree({
|
17
|
+
extensions: ["glyph", "childcounter"],
|
18
|
+
glyph: {
|
19
|
+
map: {
|
20
|
+
doc: "glyphicon glyphicon-link",
|
21
|
+
docOpen: "glyphicon glyphicon-link",
|
22
|
+
checkbox: "glyphicon glyphicon-unchecked",
|
23
|
+
checkboxSelected:"glyphicon glyphicon-check",
|
24
|
+
checkboxUnknown: "glyphicon glyphicon-edit",
|
25
|
+
expanderClosed: "glyphicon glyphicon-expand",
|
26
|
+
expanderLazy: "glyphicon glyphicon-expand",
|
27
|
+
expanderOpen: "glyphicon glyphicon-collapse-down",
|
28
|
+
folder: "glyphicon glyphicon-tag",
|
29
|
+
folderOpen: "glyphicon glyphicon-tag"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
keyboard: true,
|
33
|
+
|
34
|
+
/* when an item is clicked (either Tag or Link) */
|
35
|
+
click: function(event, data) {
|
36
|
+
|
37
|
+
// Clicked item on the list
|
38
|
+
//var node = data.node;
|
39
|
+
|
40
|
+
/* Whenever you click on a link, let's make an
|
41
|
+
* AJAX request to add it's visit count
|
42
|
+
*/
|
43
|
+
var element = event.toElement;
|
44
|
+
|
45
|
+
if ((element) && ($(element).attr('class') === 'link-link')) {
|
46
|
+
|
47
|
+
var link_id = $(element).attr('data-link-id');
|
48
|
+
|
49
|
+
$.ajax({
|
50
|
+
url: '/visit/' + link_id,
|
51
|
+
type: 'POST',
|
52
|
+
|
53
|
+
// Yeah, won't do anything if we succeed
|
54
|
+
|
55
|
+
success: function(responseData, textStatus, jqXHR) {
|
56
|
+
console.log($.parseJSON(responseData).visitCount);
|
57
|
+
},
|
58
|
+
|
59
|
+
error: function(responseData, textStatus, jqXHR) {
|
60
|
+
console.log(
|
61
|
+
"Error! Couldn't set Link as visited!\n" +
|
62
|
+
"responseData: " + responseData + "\n" +
|
63
|
+
"textStatus: " + textStatus + "\n" +
|
64
|
+
"jqXHR: " + jqXHR
|
65
|
+
);
|
66
|
+
}
|
67
|
+
});
|
68
|
+
}
|
69
|
+
},
|
70
|
+
/* when user presses ENTER or SPACE inside an item */
|
71
|
+
link: function(event, data) {
|
72
|
+
/* redirect to the internal link
|
73
|
+
* (either Category or Link) */
|
74
|
+
var href = $(
|
75
|
+
$.parseHTML(data.node.title)
|
76
|
+
).attr('href');
|
77
|
+
|
78
|
+
window.location.href = href;
|
79
|
+
}
|
80
|
+
});
|
81
|
+
/**
|
82
|
+
* Helper to ease applying things to every
|
83
|
+
* node inside the tree view.
|
84
|
+
*
|
85
|
+
* @param apply Function to apply to each node.
|
86
|
+
*/
|
87
|
+
var foreachNode = function(apply) {
|
88
|
+
$('#tags')
|
89
|
+
.fancytree('getTree')
|
90
|
+
.visit(function(node) {
|
91
|
+
apply(node);
|
92
|
+
});
|
93
|
+
};
|
94
|
+
/* Things using FancyTree's API */
|
95
|
+
$('#tags-expand').click(function() {
|
96
|
+
foreachNode(function(node) {
|
97
|
+
node.setExpanded(true);
|
98
|
+
});
|
99
|
+
});
|
100
|
+
|
101
|
+
$('#tags-collapse').click(function() {
|
102
|
+
foreachNode(function(node) {
|
103
|
+
node.setExpanded(false);
|
104
|
+
});
|
105
|
+
});
|
106
|
+
|
107
|
+
/*
|
108
|
+
* Now we define some functions that will handle the
|
109
|
+
* editing progress of the Tags.
|
110
|
+
*/
|
111
|
+
|
112
|
+
// Flag to tell if we're editing the Tags
|
113
|
+
var tagIsEditing = false;
|
114
|
+
|
115
|
+
// All the buttons for the editing process
|
116
|
+
var tagEditButtons = $(
|
117
|
+
'#tags-select-all, #tags-select-none, #tags-select-toggle, #tags-delete, #tags-delete-links'
|
118
|
+
);
|
119
|
+
// Initially we're not editing
|
120
|
+
tagEditButtons.hide();
|
121
|
+
|
122
|
+
/* Start editing, baby! */
|
123
|
+
$('#tags-edit').click(function() {
|
124
|
+
tagIsEditing = !tagIsEditing;
|
125
|
+
|
126
|
+
if (tagIsEditing) {
|
127
|
+
// Change the button's appearance
|
128
|
+
$(this).html(
|
129
|
+
"<button class='btn btn-default'><span class='glyphicon glyphicon-ban-circle' /> Cancel</button>"
|
130
|
+
);
|
131
|
+
|
132
|
+
/* Reinitializing the tree view, this time
|
133
|
+
* enabling checkboxes and multi-selection
|
134
|
+
* mode */
|
135
|
+
$('#tags').fancytree({
|
136
|
+
checkbox: true,
|
137
|
+
selectMode: 2,
|
138
|
+
|
139
|
+
// When double-clicking, toggle!
|
140
|
+
dblclick: function(event, data) {
|
141
|
+
data.node.toggleSelected();
|
142
|
+
}
|
143
|
+
});
|
144
|
+
tagEditButtons.show();
|
145
|
+
|
146
|
+
// Here we only let the user select TAGS,
|
147
|
+
// not LINKS!
|
148
|
+
// Tags are considered folders, because they
|
149
|
+
foreachNode(function(node) {
|
150
|
+
if (! node.isFolder())
|
151
|
+
node.unselectable = true;
|
152
|
+
});
|
153
|
+
|
154
|
+
}
|
155
|
+
else {
|
156
|
+
// Change the button's appearance AGAIN
|
157
|
+
$(this).html(
|
158
|
+
"<button class='btn btn-default'><span class='glyphicon glyphicon-pencil' /> Edit</a>"
|
159
|
+
);
|
160
|
+
$('#tags').fancytree({
|
161
|
+
checkbox:false
|
162
|
+
});
|
163
|
+
tagEditButtons.hide();
|
164
|
+
}
|
165
|
+
});
|
166
|
+
|
167
|
+
// And now, what happens when you click
|
168
|
+
// on each of those fancy buttons
|
169
|
+
|
170
|
+
$('#tags-select-all').click(function() {
|
171
|
+
foreachNode(function(node) {
|
172
|
+
node.setSelected(true);
|
173
|
+
});
|
174
|
+
});
|
175
|
+
$('#tags-select-none').click(function() {
|
176
|
+
foreachNode(function(node) {
|
177
|
+
node.setSelected(false);
|
178
|
+
});
|
179
|
+
});
|
180
|
+
$('#tags-select-toggle').click(function() {
|
181
|
+
foreachNode(function(node) {
|
182
|
+
node.toggleSelected();
|
183
|
+
});
|
184
|
+
});
|
185
|
+
|
186
|
+
// Now, when you click to delete tags, we musc
|
187
|
+
// communicate with the server
|
188
|
+
$('#tags-delete').click(function() {
|
189
|
+
|
190
|
+
// Will also destroy links that has these tags
|
191
|
+
var destroyLinks = $('#tags-delete-links input').is(':checked');
|
192
|
+
|
193
|
+
// We will send several DELETE requests
|
194
|
+
// to the server, each with a selected
|
195
|
+
// tag's ID
|
196
|
+
var maxSize = $('#tags')
|
197
|
+
.fancytree('getTree')
|
198
|
+
.getSelectedNodes()
|
199
|
+
.length;
|
200
|
+
|
201
|
+
// And we place a beautiful progress bar,
|
202
|
+
// to make the user not think we crashed
|
203
|
+
var progressbarParent = $(
|
204
|
+
"<div class='progress progress-striped active'></div>"
|
205
|
+
);
|
206
|
+
var progressbarText = $("<span>0%</span>");
|
207
|
+
var progressbar = $(
|
208
|
+
"<div class='progress-bar' " +
|
209
|
+
"role='progressbar' " +
|
210
|
+
"aria-valuenow='0' " +
|
211
|
+
"aria-valuemin='0' " +
|
212
|
+
"aria-valuemax='" + maxSize + "'></div>"
|
213
|
+
);
|
214
|
+
|
215
|
+
// Replace the buttons by the progress bar
|
216
|
+
$('#tags-buttons').html('');
|
217
|
+
progressbarParent.appendTo($('#tags-buttons'));
|
218
|
+
progressbar.appendTo(progressbarParent);
|
219
|
+
progressbarText.appendTo(progressbar);
|
220
|
+
|
221
|
+
var deletedCount = 0;
|
222
|
+
|
223
|
+
foreachNode(function(node) {
|
224
|
+
|
225
|
+
if (!node.selected)
|
226
|
+
return;
|
227
|
+
|
228
|
+
// Each node has a `title` element,
|
229
|
+
// with an href like '/tag/(ID)'
|
230
|
+
var href = $(
|
231
|
+
$.parseHTML(node.title)
|
232
|
+
).attr('href');
|
233
|
+
|
234
|
+
// Now, time for the AJAX request...!
|
235
|
+
ajaxManager.add({
|
236
|
+
url: href,
|
237
|
+
type: 'DELETE',
|
238
|
+
data: {
|
239
|
+
destroy_links: destroyLinks
|
240
|
+
},
|
241
|
+
/* Showing a red background on deleted tags */
|
242
|
+
success: function(responseData, textStatus, jqXHR) {
|
243
|
+
|
244
|
+
// Updating the progress bar
|
245
|
+
deletedCount += 1;
|
246
|
+
progressbar
|
247
|
+
.attr('style', 'width:' + (deletedCount/maxSize)*100 + '%')
|
248
|
+
.attr('aria-valuenow', deletedCount);
|
249
|
+
progressbarText
|
250
|
+
.text(Math.round( ((deletedCount/maxSize)*100) * 100)/100+ '%');
|
251
|
+
|
252
|
+
// This animation requires jQueryUI
|
253
|
+
$(node.span).animate({
|
254
|
+
backgroundColor: '#FFDFDF'
|
255
|
+
}, 1000);
|
256
|
+
|
257
|
+
// Removing the element on the raw list
|
258
|
+
// generated by sinatra
|
259
|
+
$('#tags')
|
260
|
+
.find("li span a[href='" + href + "']")
|
261
|
+
.each(function() {
|
262
|
+
$(this).parent('li').remove();
|
263
|
+
});
|
264
|
+
},
|
265
|
+
error: function(responseData, textStatus, jqXHR) {
|
266
|
+
/* This error-handling function sucks! */
|
267
|
+
alert("Couldn't remove tag! " + responseData + ', ' + textStatus);
|
268
|
+
}
|
269
|
+
});
|
270
|
+
});
|
271
|
+
// When everything's done...
|
272
|
+
ajaxManager.complete = function() {
|
273
|
+
|
274
|
+
// Now the progress bar is shown as completed
|
275
|
+
// and the user is advised to refresh the page
|
276
|
+
progressbarParent.removeClass('active');
|
277
|
+
progressbarParent.removeClass('progress-striped');
|
278
|
+
progressbar.addClass('progress-bar-success');
|
279
|
+
progressbarText.text("Done! Refresh page to see the changes");
|
280
|
+
};
|
281
|
+
|
282
|
+
// Now, do perform those requests for me!
|
283
|
+
ajaxManager.run();
|
284
|
+
});
|
285
|
+
|
286
|
+
}));
|
287
|
+
|