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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.md +19 -0
  5. data/README.md +188 -0
  6. data/Rakefile +8 -0
  7. data/bin/sharkey-web +9 -0
  8. data/config.ru +3 -0
  9. data/lib/sharkey.rb +12 -0
  10. data/lib/sharkey/app.rb +526 -0
  11. data/lib/sharkey/importerexporter.rb +79 -0
  12. data/lib/sharkey/models.rb +295 -0
  13. data/lib/sharkey/public/css/loading.gif +0 -0
  14. data/lib/sharkey/public/css/magicsuggest.css +232 -0
  15. data/lib/sharkey/public/css/nprogress.css +74 -0
  16. data/lib/sharkey/public/css/styles.css +263 -0
  17. data/lib/sharkey/public/css/ui.fancytree.css +545 -0
  18. data/lib/sharkey/public/data/sentences.txt +5 -0
  19. data/lib/sharkey/public/fonts/Quadrata.eot +0 -0
  20. data/lib/sharkey/public/fonts/Quadrata.svg +613 -0
  21. data/lib/sharkey/public/fonts/Quadrata.ttf +0 -0
  22. data/lib/sharkey/public/fonts/Quadrata.woff +0 -0
  23. data/lib/sharkey/public/fonts/Quadrata.zip +0 -0
  24. data/lib/sharkey/public/images/loader.gif +0 -0
  25. data/lib/sharkey/public/images/sharkey-logo.png +0 -0
  26. data/lib/sharkey/public/images/sharkey.png +0 -0
  27. data/lib/sharkey/public/js/ajaxmanager.js +67 -0
  28. data/lib/sharkey/public/js/keybindings.js +92 -0
  29. data/lib/sharkey/public/js/lib/bootstrap.min.js +6 -0
  30. data/lib/sharkey/public/js/lib/jquery-1.9.1.min.js +5 -0
  31. data/lib/sharkey/public/js/lib/jquery-ui.js +16150 -0
  32. data/lib/sharkey/public/js/lib/jquery.bootstrap-autohidingnavbar.js +213 -0
  33. data/lib/sharkey/public/js/lib/jquery.fancytree-all.js +6424 -0
  34. data/lib/sharkey/public/js/lib/jquery.tagcloud.js +92 -0
  35. data/lib/sharkey/public/js/lib/magicsuggest.js +1468 -0
  36. data/lib/sharkey/public/js/lib/mousetrap.min.js +9 -0
  37. data/lib/sharkey/public/js/lib/nprogress.js +476 -0
  38. data/lib/sharkey/public/js/page-add-link-autofill.js +102 -0
  39. data/lib/sharkey/public/js/page-add-link.js +156 -0
  40. data/lib/sharkey/public/js/page-categories.js +348 -0
  41. data/lib/sharkey/public/js/page-edit-link.js +103 -0
  42. data/lib/sharkey/public/js/page-links.js +54 -0
  43. data/lib/sharkey/public/js/page-settings.js +93 -0
  44. data/lib/sharkey/public/js/page-tagcloud.js +35 -0
  45. data/lib/sharkey/public/js/page-tags.js +287 -0
  46. data/lib/sharkey/public/js/scripts.js +147 -0
  47. data/lib/sharkey/public/themes/amelia/style.css +7 -0
  48. data/lib/sharkey/public/themes/bootstrap/style.css +5785 -0
  49. data/lib/sharkey/public/themes/cerulean/style.css +7 -0
  50. data/lib/sharkey/public/themes/cosmo/style.css +7 -0
  51. data/lib/sharkey/public/themes/cyborg/style.css +7 -0
  52. data/lib/sharkey/public/themes/darkly/style.css +7 -0
  53. data/lib/sharkey/public/themes/facebook-like/README.md +6 -0
  54. data/lib/sharkey/public/themes/facebook-like/style.css +6085 -0
  55. data/lib/sharkey/public/themes/flatly/style.css +7 -0
  56. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.svg +229 -0
  58. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/lib/sharkey/public/themes/holo-like/README.md +5 -0
  61. data/lib/sharkey/public/themes/holo-like/style.css +5997 -0
  62. data/lib/sharkey/public/themes/journal/style.css +7 -0
  63. data/lib/sharkey/public/themes/lumen/style.css +7 -0
  64. data/lib/sharkey/public/themes/readable/style.css +7 -0
  65. data/lib/sharkey/public/themes/simplex/style.css +7 -0
  66. data/lib/sharkey/public/themes/slate/style.css +7 -0
  67. data/lib/sharkey/public/themes/spacelab/style.css +7 -0
  68. data/lib/sharkey/public/themes/superhero/style.css +7 -0
  69. data/lib/sharkey/public/themes/united/style.css +7 -0
  70. data/lib/sharkey/public/themes/yeti/style.css +7 -0
  71. data/lib/sharkey/setting.rb +74 -0
  72. data/lib/sharkey/version.rb +5 -0
  73. data/lib/sharkey/views/404.slim +4 -0
  74. data/lib/sharkey/views/about.slim +7 -0
  75. data/lib/sharkey/views/add_link.slim +157 -0
  76. data/lib/sharkey/views/categories.slim +101 -0
  77. data/lib/sharkey/views/category.slim +22 -0
  78. data/lib/sharkey/views/centered.slim +49 -0
  79. data/lib/sharkey/views/dashboard.slim +107 -0
  80. data/lib/sharkey/views/dashboard_index.slim +26 -0
  81. data/lib/sharkey/views/edit_link.slim +121 -0
  82. data/lib/sharkey/views/help.slim +74 -0
  83. data/lib/sharkey/views/keybindings.slim +58 -0
  84. data/lib/sharkey/views/link.slim +30 -0
  85. data/lib/sharkey/views/links.slim +22 -0
  86. data/lib/sharkey/views/navbar.slim +68 -0
  87. data/lib/sharkey/views/settings.slim +74 -0
  88. data/lib/sharkey/views/settings_index.slim +220 -0
  89. data/lib/sharkey/views/single_category.slim +37 -0
  90. data/lib/sharkey/views/single_link.slim +95 -0
  91. data/lib/sharkey/views/single_tag.slim +33 -0
  92. data/lib/sharkey/views/tag.slim +22 -0
  93. data/lib/sharkey/views/tagcloud.slim +54 -0
  94. data/lib/sharkey/views/tags.slim +99 -0
  95. data/sharkey-web.gemspec +44 -0
  96. metadata +324 -0
@@ -0,0 +1,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
+