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,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
|
+
|