Replace tagit with select2

+ Fix use of global app var
This commit is contained in:
Marcel Klehr 2018-02-02 21:30:48 +01:00
parent 240539eb50
commit 98f4de2380
16 changed files with 613 additions and 551 deletions

View File

@ -1,111 +0,0 @@
ul.tagit {
padding: 1px 5px;
overflow: auto;
margin-left: inherit; /* usually we don't want the regular ul margins. */
margin-right: inherit;
width: 91.5%;
}
ul.tagit li {
display: block;
float: left;
margin: 2px 5px 2px 0;
}
ul.tagit li.tagit-choice {
padding: .2em 18px .2em .5em;
position: relative;
line-height: inherit;
}
ul.tagit li.tagit-new {
padding: .25em 4px .25em 0;
}
ul.tagit li.tagit-choice a.tagit-label {
cursor: pointer;
text-decoration: none;
}
ul.tagit li.tagit-choice .close {
cursor: pointer;
position: absolute;
right: .1em;
top: 50%;
margin-top: -8px;
}
/* used for some custom themes that don't need image icons */
ul.tagit li.tagit-choice .close .text-icon {
display: none;
}
ul.tagit li.tagit-choice input {
display: block;
float: left;
margin: 2px 5px 2px 0;
}
ul.tagit input[type="text"] {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border: none;
margin: 0;
padding: 0;
width: inherit;
background-color: inherit;
outline: none;
}
/***** ZENDESK ***/
/* Optional scoped theme for tag-it which mimics the zendesk widget. */
ul.tagit {
border-style: solid;
border-width: 1px;
border-color: #C6C6C6;
background: inherit;
}
ul.tagit li.tagit-choice {
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-border-radius: 6px;
border: 1px solid #CAD8F3;
background: none;
background-color: #DEE7F8;
color: #555;
font-weight: normal;
}
ul.tagit li.tagit-choice a.close {
text-decoration: none;
}
ul.tagit li.tagit-choice .close {
right: .4em;
}
ul.tagit li.tagit-choice .ui-icon {
display: none;
}
ul.tagit li.tagit-choice .close .text-icon {
display: inline;
font-family: arial, sans-serif;
font-size: 16px;
line-height: 16px;
color: #777;
}
ul.tagit li.tagit-choice:hover, ul.tagit li.tagit-choice.remove {
background-color: #bbcef1;
border-color: #6d95e0;
}
ul.tagit li.tagit-choice a.tagLabel:hover,
ul.tagit li.tagit-choice a.close .text-icon:hover {
color: #222;
}
ul.tagit input[type="text"] {
color: #333333;
background: none;
}

View File

@ -95,7 +95,7 @@
display: block;
}
.bulk-actions.active + * {
margin-top: 1.5cm;
margin-top: 2.5cm;
}
@media only screen and (max-width: 768px) {
.bulk-actions {
@ -107,15 +107,17 @@
}
}
.bulk-actions > button {
vertical-align: text-bottom;
vertical-align: top;
}
.bulk-actions .tags {
display: inline-block;
width: 60%;
margin: 3px 3px 3px 0;
position: relative;
}
.bulk-actions .tagit {
background: white;
.bulk-actions .tags-selection {
display: block;
width: 100%;
}
.bulk-actions .selection-tools {
float: right;
@ -307,18 +309,18 @@ button span,
height: 1cm;
width: 1cm;
}
.bulk-actions ul.tagit li.tagit-choice,
.bookmark-detail ul.tagit li.tagit-choice,
.bookmark-detail .select2-container--default .select2-selection--multiple .select2-selection__choice,
.bulk-actions .select2-container--default .select2-selection--multiple .select2-selection__choice,
.bookmark-detail .tag-nav-item {
border-radius: .2cm;
border: none;
background: #ebebeb;
display: inline-block;
padding: .1cm .4cm;
padding: .1cm .2cm;
margin: .1cm;
}
.bulk-actions ul.tagit li.tagit-choice:hover,
.bookmark-detail ul.tagit li.tagit-choice:hover,
.bookmark-detail .select2-container--default .select2-selection--multiple .select2-selection__choice:hover,
.bulk-actions .select2-container--default .select2-selection--multiple .select2-selection__choice:hover,
.bookmark-detail .tag-nav-item:hover {
background: #e0e0e0;
}
@ -336,29 +338,16 @@ button span,
.bookmark-detail .input-url,
.bookmark-detail .input-title,
.bookmark-detail .input-desc,
.bookmarks-detail ul.tagit,
.bulk-actions ul.tagit {
.bookmark-detail .tags-selection {
width: 100%;
font-size: inherit;
box-sizing: border-box;
padding: 0 !important;
}
.bookmark-detail .input-url {
padding-left: 25px;
padding-left: 25px !important;
background-position: 5px;
}
.bookmatks-detail ul.tagit li.tagit-choice,
.bulk-actions ul.tagit li.tagit-choice {
padding: .1cm .5cm .1cm .2cm;
}
.bulk-actions ul.tagit li.tagit-new,
.bookmarks-detail ul.tagit li.tagit-new {
float: none;
display: inline-block;
padding: 0 !important;
cursor: auto;
margin: 0;
}
.bookmark-detail .input-desc {
height: 300px;
}

484
css/select2.css Normal file
View File

@ -0,0 +1,484 @@
.select2-container {
box-sizing: border-box;
display: inline-block;
margin: 0;
position: relative;
vertical-align: middle; }
.select2-container .select2-selection--single {
box-sizing: border-box;
cursor: pointer;
display: block;
height: 28px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--single .select2-selection__rendered {
display: block;
padding-left: 8px;
padding-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-selection--single .select2-selection__clear {
position: relative; }
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
padding-right: 8px;
padding-left: 20px; }
.select2-container .select2-selection--multiple {
box-sizing: border-box;
cursor: pointer;
display: block;
min-height: 32px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--multiple .select2-selection__rendered {
display: inline-block;
overflow: hidden;
padding-left: 8px;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-search--inline {
float: left; }
.select2-container .select2-search--inline .select2-search__field {
box-sizing: border-box;
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0; }
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-dropdown {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
box-sizing: border-box;
display: block;
position: absolute;
left: -100000px;
width: 100%;
z-index: 1051; }
.select2-results {
display: block; }
.select2-results__options {
list-style: none;
margin: 0;
padding: 0; }
.select2-results__option {
padding: 6px;
user-select: none;
-webkit-user-select: none; }
.select2-results__option[aria-selected] {
cursor: pointer; }
.select2-container--open .select2-dropdown {
left: 0; }
.select2-container--open .select2-dropdown--above {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--open .select2-dropdown--below {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-search--dropdown {
display: block;
padding: 4px; }
.select2-search--dropdown .select2-search__field {
padding: 4px;
width: 100%;
box-sizing: border-box; }
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-search--dropdown.select2-search--hide {
display: none; }
.select2-close-mask {
border: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 99;
background-color: #fff;
filter: alpha(opacity=0); }
.select2-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important; }
.select2-container--default .select2-selection--single {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 4px; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--default .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold; }
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px; }
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto; }
.select2-container--default.select2-container--disabled .select2-selection--single {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none; }
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--default .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 5px;
width: 100%; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
list-style: none; }
.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
color: #999;
margin-top: 5px;
float: left; }
.select2-container--default .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-top: 5px;
margin-right: 10px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #999;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #333; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: solid black 1px;
outline: 0; }
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
display: none; }
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa; }
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent;
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield; }
.select2-container--default .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--default .select2-results__option[role=group] {
padding: 0; }
.select2-container--default .select2-results__option[aria-disabled=true] {
color: #999; }
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd; }
.select2-container--default .select2-results__option .select2-results__option {
padding-left: 1em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em; }
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb;
color: white; }
.select2-container--default .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic .select2-selection--single {
background-color: #f7f7f7;
border: 1px solid #aaa;
border-radius: 4px;
outline: 0;
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic .select2-selection--single:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--classic .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-right: 10px; }
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--classic .select2-selection--single .select2-selection__arrow {
background-color: #ddd;
border: none;
border-left: 1px solid #aaa;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
border: none;
border-right: 1px solid #aaa;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
left: 1px;
right: auto; }
.select2-container--classic.select2-container--open .select2-selection--single {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
background: transparent;
border: none; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
.select2-container--classic .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
outline: 0; }
.select2-container--classic .select2-selection--multiple:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
list-style: none;
margin: 0;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
display: none; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
color: #888;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #555; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
float: right;
margin-left: 5px;
margin-right: auto; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--classic.select2-container--open .select2-selection--multiple {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--classic .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
outline: 0; }
.select2-container--classic .select2-search--inline .select2-search__field {
outline: 0;
box-shadow: none; }
.select2-container--classic .select2-dropdown {
background-color: white;
border: 1px solid transparent; }
.select2-container--classic .select2-dropdown--above {
border-bottom: none; }
.select2-container--classic .select2-dropdown--below {
border-top: none; }
.select2-container--classic .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--classic .select2-results__option[role=group] {
padding: 0; }
.select2-container--classic .select2-results__option[aria-disabled=true] {
color: grey; }
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
background-color: #3875d7;
color: white; }
.select2-container--classic .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }

365
js/3rdparty/tag-it.js vendored
View File

@ -1,365 +0,0 @@
/*
* jQuery UI Tag-it!
*
* @version v2.0 (06/2011)
*
* Copyright 2011, Levy Carneiro Jr.
* Released under the MIT license.
* http://aehlke.github.com/tag-it/LICENSE
*
* Homepage:
* http://aehlke.github.com/tag-it/
*
* Authors:
* Levy Carneiro Jr.
* Martin Rehfeld
* Tobias Schmidt
* Skylar Challand
* Alex Ehlke
*
* Maintainer:
* Alex Ehlke - Twitter: @aehlke
*
* Dependencies:
* jQuery v1.4+
* jQuery UI v1.8+
*/
(function ($) {
$.widget('ui.tagit', {
options: {
itemName: 'item',
fieldName: 'tags',
availableTags: [],
tagSource: null,
removeConfirmation: false,
caseSensitive: true,
placeholderText: null,
// When enabled, quotes are not neccesary
// for inputting multi-word tags.
allowSpaces: false,
// The below options are for using a single field instead of several
// for our form values.
//
// When enabled, will use a single hidden field for the form,
// rather than one per tag. It will delimit tags in the field
// with singleFieldDelimiter.
//
// The easiest way to use singleField is to just instantiate tag-it
// on an INPUT element, in which case singleField is automatically
// set to true, and singleFieldNode is set to that element. This
// way, you don't need to fiddle with these options.
singleField: false,
singleFieldDelimiter: ',',
// Set this to an input DOM node to use an existing form field.
// Any text in it will be erased on init. But it will be
// populated with the text of tags as they are created,
// delimited by singleFieldDelimiter.
//
// If this is not set, we create an input node for it,
// with the name given in settings.fieldName,
// ignoring settings.itemName.
singleFieldNode: null,
// Optionally set a tabindex attribute on the input that gets
// created for tag-it.
tabIndex: null,
// Event callbacks.
onTagAdded: null,
onTagRemoved: null,
onTagClicked: null
},
_create: function () {
// for handling static scoping inside callbacks
var that = this;
// There are 2 kinds of DOM nodes this widget can be instantiated on:
// 1. UL, OL, or some element containing either of these.
// 2. INPUT, in which case 'singleField' is overridden to true,
// a UL is created and the INPUT is hidden.
if (this.element.is('input')) {
this.tagList = $('<ul></ul>').insertAfter(this.element);
this.options.singleField = true;
this.options.singleFieldNode = this.element;
this.element.css('display', 'none');
} else {
this.tagList = this.element.find('ul, ol').andSelf().last();
}
this._tagInput = $('<input type="text">').addClass('ui-widget-content');
if (this.options.tabIndex) {
this._tagInput.attr('tabindex', this.options.tabIndex);
}
if (this.options.placeholderText) {
this._tagInput.attr('placeholder', this.options.placeholderText);
}
this.options.tagSource = this.options.tagSource || function (search, showChoices) {
var filter = search.term.toLowerCase();
var choices = $.grep(that.options.availableTags, function (element) {
// Only match autocomplete options that begin with the search term.
// (Case insensitive.)
return (element.toLowerCase().indexOf(filter) === 0);
});
showChoices(that._subtractArray(choices, that.assignedTags()));
};
this.tagList
.addClass('tagit')
.addClass('ui-widget ui-widget-content ui-corner-all')
// Create the input field.
.append($('<li class="tagit-new"></li>').append(this._tagInput))
.click(function (e) {
var target = $(e.target);
if (target.hasClass('tagit-label')) {
that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
} else {
// Sets the focus() to the input field, if the user
// clicks anywhere inside the UL. This is needed
// because the input field needs to be of a small size.
that._tagInput.focus();
}
});
// Add existing tags from the list, if any.
this.tagList.children('li').each(function () {
if (!$(this).hasClass('tagit-new')) {
that.createTag($(this).html(), $(this).attr('class'));
$(this).remove();
}
});
// Single field support.
if (this.options.singleField) {
if (this.options.singleFieldNode) {
// Add existing tags from the input field.
var node = $(this.options.singleFieldNode);
var tags = node.val().split(this.options.singleFieldDelimiter);
node.val('');
$.each(tags, function (index, tag) {
that.createTag(tag);
});
} else {
// Create our single field input after our list.
this.options.singleFieldNode = this.tagList.after('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '">');
}
}
// Events.
this._tagInput
.keydown(function (event) {
// Backspace is not detected within a keypress, so it must use keydown.
if (event.which == $.ui.keyCode.BACKSPACE && that._tagInput.val() === '') {
var tag = that._lastTag();
if (!that.options.removeConfirmation || tag.hasClass('remove')) {
// When backspace is pressed, the last tag is deleted.
that.removeTag(tag);
} else if (that.options.removeConfirmation) {
tag.addClass('remove ui-state-highlight');
}
} else if (that.options.removeConfirmation) {
that._lastTag().removeClass('remove ui-state-highlight');
}
// Comma/Space/Enter are all valid delimiters for new tags,
// except when there is an open quote or if setting allowSpaces = true.
// Tab will also create a tag, unless the tag input is empty, in which case it isn't caught.
if (
event.which == $.ui.keyCode.COMMA ||
event.which == $.ui.keyCode.ENTER ||
(
event.which == $.ui.keyCode.TAB &&
that._tagInput.val() !== ''
) ||
(
event.which == $.ui.keyCode.SPACE &&
that.options.allowSpaces !== true &&
(
$.trim(that._tagInput.val()).replace(/^s*/, '').charAt(0) != '"' ||
(
$.trim(that._tagInput.val()).charAt(0) == '"' &&
$.trim(that._tagInput.val()).charAt($.trim(that._tagInput.val()).length - 1) == '"' &&
$.trim(that._tagInput.val()).length - 1 !== 0
)
)
)
) {
event.preventDefault();
that.createTag(that._cleanedInput());
// The autocomplete doesn't close automatically when TAB is pressed.
// So let's ensure that it closes.
that._tagInput.autocomplete('close');
}
}).blur(function (e) {
//If autocomplete is enabled and suggestion was clicked, don't add it
if (that.options.tagSource && that._tagInput.data('autocomplete-open')) {
that._cleanedInput();
} else {
that.createTag(that._cleanedInput());
}
});
// Autocomplete.
if (this.options.availableTags || this.options.tagSource) {
this._tagInput.autocomplete({
source: this.options.tagSource,
open: function () {
that._tagInput.data('autocomplete-open', true)
},
close: function () {
that._tagInput.data('autocomplete-open', false)
},
select: function (event, ui) {
that.createTag(ui.item.value);
// Preventing the tag input to be updated with the chosen value.
return false;
}
});
}
},
_cleanedInput: function () {
// Returns the contents of the tag input, cleaned and ready to be passed to createTag
return $.trim(this._tagInput.val().replace(/^"(.*)"$/, '$1'));
},
_lastTag: function () {
return this.tagList.children('.tagit-choice:last');
},
assignedTags: function () {
// Returns an array of tag string values
var that = this;
var tags = [];
if (this.options.singleField) {
tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
if (tags[0] === '') {
tags = [];
}
} else {
this.tagList.children('.tagit-choice').each(function () {
tags.push(that.tagLabel(this));
});
}
return tags;
},
_updateSingleTagsField: function (tags) {
// Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
$(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter));
},
_subtractArray: function (a1, a2) {
var result = [];
for (var i = 0; i < a1.length; i++) {
if ($.inArray(a1[i], a2) == -1) {
result.push(a1[i]);
}
}
return result;
},
tagLabel: function (tag) {
// Returns the tag's string label.
if (this.options.singleField) {
return $(tag).children('.tagit-label').text();
} else {
return $(tag).children('input').val();
}
},
_isNew: function (value) {
var that = this;
var isNew = true;
this.tagList.children('.tagit-choice').each(function (i) {
if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
isNew = false;
return;
}
});
return isNew;
},
_formatStr: function (str) {
if (this.options.caseSensitive) {
return str;
}
return $.trim(str.toLowerCase());
},
createTag: function (value, additionalClass) {
that = this;
// Automatically trims the value of leading and trailing whitespace.
value = $.trim(value);
if (!this._isNew(value) || value === '') {
return false;
}
var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
// Create tag.
var tag = $('<li></li>')
.addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
.addClass(additionalClass)
.append(label);
// Button for removing the tag.
var removeTagIcon = $('<span></span>')
.addClass('ui-icon ui-icon-close');
var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
.addClass('close')
.append(removeTagIcon)
.click(function (e) {
// Removes a tag when the little 'x' is clicked.
that.removeTag(tag);
});
tag.append(removeTag);
// Unless options.singleField is set, each tag has a hidden input field inline.
if (this.options.singleField) {
var tags = this.assignedTags();
tags.push(value);
this._updateSingleTagsField(tags);
} else {
var escapedValue = label.html();
tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]">');
}
this._trigger('onTagAdded', null, tag);
// Cleaning the input.
this._tagInput.val('');
// insert tag
this._tagInput.parent().after(tag);
},
removeTag: function (tag, animate) {
if (typeof animate === 'undefined') {
animate = true;
}
tag = $(tag);
this._trigger('onTagRemoved', null, tag);
if (this.options.singleField) {
var tags = this.assignedTags();
var removedTagLabel = this.tagLabel(tag);
tags = $.grep(tags, function (el) {
return el != removedTagLabel;
});
this._updateSingleTagsField(tags);
}
// Animate the removal.
if (animate) {
tag.fadeOut('fast').hide('blind', {direction: 'horizontal'}, 'fast', function () {
tag.remove();
}).dequeue();
} else {
tag.remove();
}
this._trigger('onTagFinishRemoved', null, tag);
},
removeAll: function () {
// Removes all tags. Takes an optional `animate` argument.
var that = this;
this.tagList.children('.tagit-choice').each(function (index, tag) {
that.removeTag(tag, false);
});
}
});
})(jQuery);

View File

@ -1,5 +1,6 @@
import Backbone from 'backbone'
import Bookmarks from './models/Bookmarks'
import Tag from './models/Tag'
import Tags from './models/Tags'
import Router from './Router'
import AppView from './views/App'

View File

@ -1,6 +1,7 @@
import _ from 'underscore'
import Backbone from 'backbone'
import Marionette from 'backbone.marionette'
import select2 from 'select2'
import Tag from './models/Tag'
import Tags from './models/Tags'
import App from './Application'
@ -19,6 +20,9 @@ Backbone.sync = function(method, model, options) {
, error: function() {
console.log(arguments)
}
, headers: {
'requesttoken': oc_requesttoken
}
}
if (method === 'update' && model instanceof Tag) {
overrideOptions.url = model.urlRoot+'/'+model.previous('name')

View File

@ -2,7 +2,6 @@
<h1><input class="input-title" type="text" value="<%- title %>" placeholder="<%- t('bookmarks', 'The title of the page') %>" /></h1>
<h2><input type="type" class="input-url icon-external" value="<%- url %>" placeholder="<%- t('bookmarks', 'The address of the page') %>" /></h2>
<div class="tags">
<input type="text" />
</div>
<div class="description">
<textarea class="input-desc" placeholder="<%- t('bookmarks', 'A description of the page') %>"><%- description %></textarea>

View File

@ -7,5 +7,4 @@
<span><%- t('bookmarks', 'Delete') %></span>
</button>
<div class="tags">
<input type="text" />
</div>

View File

@ -1,5 +1,5 @@
<li data-id="all" class="all">
<a href="#"><span class="icon-home"></span><%- t('bookmarks', '>All bookmarks') %></a>
<a href="#"><span class="icon-home"></span><%- t('bookmarks', 'All bookmarks') %></a>
</li>
<li data-id="favorites" class="favorites">
<a href="#"><span class="icon-favorite"></span><%- t('bookmarks', 'Favorites') %></a>

View File

@ -1,5 +1,6 @@
import _ from 'underscore'
import Backbone from 'backbone'
import Tags from '../models/Tags'
import TagsNavigationView from './TagsNavigation'
import templateString from '../templates/BookmarkCard.html'
@ -32,7 +33,7 @@ export default Marionette.View.extend({
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "select", this.onSelect);
this.listenTo(this.model, "unselect", this.onUnselect);
this.listenTo(app.tags, 'sync', this.render)
this.listenTo(this.app.tags, 'sync', this.render)
this.listenTo(Radio.channel('documentClicked'), 'click', this.closeActions)
}
, onRender: function() {

View File

@ -1,8 +1,10 @@
import _ from 'underscore'
import Backbone from 'backbone'
import Tags from '../models/Tags'
import TagsNavigationView from './TagsNavigation'
import TagsSelectionView from './TagsSelection'
import templateStringDefault from '../templates/BookmarkDetail_default.html'
import templateStringEditing from '../templates/BookmarkDetail_default.html'
import templateStringEditing from '../templates/BookmarkDetail_editing.html'
const Marionette = Backbone.Marionette
const Radio = Backbone.Radio
@ -20,7 +22,6 @@ export default Marionette.View.extend({
regions: {
'tags': {
el: '.tags'
, replaceElement: true
}
},
ui: {
@ -38,26 +39,17 @@ export default Marionette.View.extend({
initialize: function(opts) {
this.app = opts.app
this.listenTo(this.model, "change", this.render);
this.listenTo(app.tags, 'sync', this.render)
this.listenTo(this.app.tags, 'sync', this.render)
},
onRender: function() {
var that = this
this.tags = new Tags(this.model.get('tags').map(function(id) {
return that.app.tags.findWhere({name: id})
}))
if (this.editing) {
this.$('.tags input')
.val(this.model.get('tags').join(','))
.tagit({
allowSpaces: true,
availableTags: this.app.tags.pluck('name'),
placeholderText: t('bookmarks', 'Enter tags'),
onTagRemoved: function() {},
onTagFinishRemoved: function() {},
onTagClicked: function(){}
})
this.showChildView('tags', new TagsSelectionView({collection: this.app.tags, selected: this.tags, app: this.app }))
}else{
var tags = new Tags(this.model.get('tags').map(function(id) {
return that.app.tags.findWhere({name: id})
}))
this.showChildView('tags', new TagsNavigationView({collection: tags}))
this.showChildView('tags', new TagsNavigationView({collection: this.tags}))
}
},
close: function() {
@ -81,7 +73,7 @@ export default Marionette.View.extend({
this.model.set({
'title': this.$('.input-title').val()
, 'url': this.$('.input-url').val()
, 'tags': this.$('.tags input').tagit("assignedTags")
, 'tags': this.tags.pluck('name')
, 'description': this.$('.input-desc').val()
})
this.model.save({wait: true})

View File

@ -1,5 +1,7 @@
import _ from 'underscore'
import Backbone from 'backbone'
import Tags from '../models/Tags'
import TagsSelectionView from './TagsSelection'
import templateString from '../templates/BulkActions.html'
const Marionette = Backbone.Marionette
@ -8,7 +10,12 @@ const Radio = Backbone.Radio
export default Marionette.View.extend({
className: 'bulk-actions'
, template: _.template(templateString)
, events: {
, regions: {
'tags': {
el: '.tags'
}
},
events: {
'click .delete': 'delete'
, 'click .select-all': 'selectAll'
, 'click .selection-tools .close': 'abort'
@ -17,36 +24,37 @@ export default Marionette.View.extend({
this.app = opts.app
this.all = this.app.bookmarks
this.selected = opts.selected
this.tags = new Tags
this.listenTo(this.tags, 'remove', this.onTagRemoved)
this.listenTo(this.tags, 'add', this.onTagAdded)
this.listenTo(this.selected, 'remove', this.onReduceSelection)
this.listenTo(this.selected, 'add', this.onExtendSelection)
}
, onRender: function() {
// hack to ignore events caused by tagit setup -- we should really get something else...
this.rendering = true
this.$('.tags input')
.val(_.intersection.apply(_, this.selected.pluck('tags')).join(','))
.tagit({
allowSpaces: true,
availableTags: this.app.tags.pluck('name'),
placeholderText: t('bookmarks', 'Enter tags'),
onTagRemoved: this.onTagRemoved.bind(this),
onTagAdded: this.onTagAdded.bind(this),
onTagFinishRemoved: function() {},
onTagClicked: function(){}
})
this.rendering = false
this.showChildView('tags', new TagsSelectionView({collection: this.app.tags, selected: this.tags, app: this.app }))
}
, updateTags: function() {
var that = this
this.triggeredByAlgo = true
this.tags.reset(
_.intersection.apply(_, this.selected.pluck('tags'))
.map(function(name) {
return that.app.tags.get(name)
})
)
this.triggeredByAlgo = false
}
, onReduceSelection: function() {
if (this.selected.length == 0) {
this.$el.removeClass('active')
}
this.render()
this.updateTags()
}
, onExtendSelection: function() {
if (this.selected.length == 1) {
this.$el.addClass('active')
}
this.render()
this.updateTags()
}
, delete: function() {
var that = this
@ -59,21 +67,19 @@ export default Marionette.View.extend({
})
})
}
, onTagAdded: function(e, el) {
if (this.rendering) return
var tagName = $('.tagit-label', el).text()
, onTagAdded: function(tag) {
if (this.triggeredByAlgo) return
this.selected.forEach(function(model) {
var tags = model.get('tags')
model.set('tags', _.union(tags, [tagName]))
model.set('tags', _.union(tags, [tag.get('name')]))
model.save()
})
}
, onTagRemoved: function(e, el) {
if (this.rendering) return
var tagName = $('.tagit-label', el).text()
, onTagRemoved: function(tag) {
if (this.triggeredByAlgo) return
this.selected.forEach(function(model) {
var tags = model.get('tags')
model.set('tags', _.without(tags, tagName))
model.set('tags', _.without(tags, tag.get('name')))
model.save()
})
}

View File

@ -1,4 +1,5 @@
import Backbone from 'backbone'
import Tags from '../models/Tags'
import TagView from './TagsManagementTag'
const Marionette = Backbone.Marionette

64
js/views/TagsSelection.js Normal file
View File

@ -0,0 +1,64 @@
import _ from 'underscore'
import Backbone from 'backbone'
const Marionette = Backbone.Marionette
const Radio = Backbone.Radio
export default Marionette.View.extend({
tagName: 'select'
, template: _.template('')
, className: 'tags-selection'
, events: {
'select2:select': 'onAdd'
, 'select2:unselect': 'onRemove'
}
, initialize: function(options) {
this.app = options.app
this.selected = options.selected || new Tags
this.selected.comparator = 'name'
this.listenTo(this.selected, 'add', this.onChangeByAlgo)
this.listenTo(this.selected, 'remove', this.onChangeByAlgo)
this.listenTo(this.selected, 'reset', this.onChangeByAlgo)
this.listenTo(this.collection, 'add', this.onAttach)
}
, onAttach: function() {
if (this.$el.hasClass("select2-hidden-accessible")) {
this.$el.select2('destroy')
}
this.$el.select2({
placeholder: t('bookmarks', 'Set tags')
, allowClear: true
, width: '100%'
, tags: true
, multiple: true
, tokenSeparators: [',', ' ']
, data: this.app.tags.pluck('name').map(function(name) {
return {id: name, text: name}
})
})
.val(this.selected.pluck('name'))
.trigger('change')
}
, onDetach: function() {
this.$el.select2('destroy')
}
, onAddByUser: function(e) {
if (this.triggeredByAlgo) {
return this.triggeredByAlgo = false
}
this.selected.add(this.app.tags.get(e.params.data.id))
}
, onRemoveByUser: function(e) {
if (this.triggeredByAlgo) {
return this.triggeredByAlgo = false
}
this.selected.remove(this.app.tags.get(e.params.data.id))
}
, onChangeByAlgo: function(e) {
this.$el
.val(this.selected.pluck('name'))
.trigger('change')
this.triggeredByAlgo = true
}
})

View File

@ -21,6 +21,7 @@
"backbone": "^1.3.3",
"backbone.marionette": "^3.5.1",
"jquery": "^3.3.1",
"select2": "^4.0.6-rc.1",
"underscore": "^1.8.3"
}
}

View File

@ -2,10 +2,7 @@
script('bookmarks', 'dist/bundle');
style('bookmarks', 'bookmarks');
script('bookmarks', '3rdparty/tag-it');
style('bookmarks', '3rdparty/jquery.tagit');
style('bookmarks', 'select2');
/**
* Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de>
* Copyright (c) 2011 Arthur Schiwon <blizzz@arthur-schiwon.de>