sharkey-web 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
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,79 @@
1
+
2
+ require 'sharkey/models'
3
+ require 'deps/markio'
4
+
5
+ module Sharkey
6
+
7
+ # Knows how to import from and export to Netscape Bookmark HTML file.
8
+ #
9
+ # This format is commonly used when importing/exporting bookmarks
10
+ # from most browsers and tools such as Delicious.
11
+ #
12
+ # I'm using a modified version of the Ruby Gem Markio
13
+ # (https://github.com/spajus/markio)
14
+ #
15
+ module ImporterExporter
16
+ module_function
17
+
18
+ # Imports all Links, their Tags and Categories from a file.
19
+ #
20
+ def import filename
21
+
22
+ # TODO Before anything, I should make sure it's a HTML file
23
+ # and it's not corrupted or anything...
24
+
25
+ # Opening and parsing the temporary file, all at once
26
+ bookmarks = File.open(filename) { |file| Markio::parse(file) }
27
+
28
+ # Now we go through all of them, creating the Categories and Links
29
+ bookmarks.each do |b|
30
+
31
+ # First, we make sure the categories of this Link
32
+ # exist.
33
+ #
34
+ # "Folder" is to "Markio" as "Categories" is to "Sharkey"
35
+ #
36
+ # `b.folders` is an array of category names, like:
37
+ #
38
+ # ["grandparent", "parent", "child"]
39
+ #
40
+ # So all we need to do is keep creating from the
41
+ # first to the last and the whole category hierarchy
42
+ # will derive.
43
+ #
44
+ last_category = nil
45
+ last_category_parent = nil
46
+
47
+ b.folders.each do |category_name|
48
+ last_category_parent = last_category
49
+
50
+ last_category = Sharkey::Category.first_or_create(name: category_name)
51
+
52
+ if last_category_parent
53
+ # last_category_parent.add_child last_category
54
+ end
55
+ end
56
+
57
+ Sharkey::Link.create_link(b.title,
58
+ b.href,
59
+ b.add_date,
60
+ b.tags,
61
+ if last_category
62
+ then if last_category.id
63
+ then last_category.id
64
+ else nil
65
+ end
66
+ else nil
67
+ end,
68
+ "")
69
+ end
70
+
71
+ end
72
+
73
+ # Exports
74
+ def export filename
75
+ # Nothing for now
76
+ end
77
+ end
78
+ end
79
+
@@ -0,0 +1,295 @@
1
+ # This file contains all Models (database-thingies) of this
2
+ # Application.
3
+ #
4
+ # We're using DataMapper as the interface to raw SQLite
5
+ # databases.
6
+ #
7
+ # So here's what we do on this file:
8
+ #
9
+ # 1. Initialize DataMapper (settings 'n stuff)
10
+ # 2. Define the Models
11
+ # Specify things and columns in databases
12
+ # 3. Finalize DataMapper (_actually_ create the tables)
13
+ #
14
+
15
+ require 'data_mapper'
16
+ require 'addressable/uri'
17
+
18
+ module Sharkey
19
+
20
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
21
+ # Initializing DataMapper
22
+
23
+ # Full path to the database file
24
+ DATABASE_FILE = "#{Dir.pwd}/database.db"
25
+ DATABASE_PATH = "sqlite3://#{DATABASE_FILE}"
26
+
27
+ # By default Strings have at max 50 chars of length
28
+ # That's hideous! Come on!
29
+ DataMapper::Property::String.length 255
30
+
31
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
32
+ # Creating Models
33
+
34
+ # Represents a single HyperLink specified by the user.
35
+ #
36
+ class Link
37
+ # This tells that it is a thing that will get stored
38
+ # on the database
39
+ # Below are all the database elements
40
+ include DataMapper::Resource
41
+
42
+ property :id, Serial # Auto-incremented key
43
+ property :url, URI, :required => true # Actual URL
44
+ property :title, String # User-specified title
45
+ property :added_at, DateTime # When this link was added
46
+ property :comment, Text
47
+ property :favorite, Boolean, :default => false
48
+ property :visit_count, Integer, :default => 0
49
+ property :last_visit, DateTime
50
+
51
+ has n, :taggings
52
+ has n, :tags, :through => :taggings
53
+
54
+ has 1, :categorization
55
+ has 1, :category, :through => :categorization
56
+
57
+ # Creates a new Link with specified parameters.
58
+ #
59
+ # @note Link#create is a reserved method for DataMapper
60
+ # it actually inserts it on the database.
61
+ # This function differs from it on the sense that
62
+ # it also creates the Tags related to this Link
63
+ #
64
+ # @param tags Array of Strings as tag names
65
+ # @param added_at DateTime object or `nil` for DateTime.now
66
+ # @param category An ID of _existing_ category
67
+ #
68
+ def self.create_link(title, url, added_at, tags, category, comment)
69
+ # Silently fail
70
+ return if url.nil?
71
+
72
+ # Do not allow relative URLs!
73
+ # Always assume HTTP
74
+ if Addressable::URI.parse(url).relative?
75
+ url = "http://#{url}"
76
+ end
77
+
78
+ # This array will contain the Tags objects
79
+ # created here
80
+ the_tags = []
81
+ if (not tags.nil?) and (not tags.empty?)
82
+ tags.each do |tag|
83
+
84
+ # If Sharkey::Tag exists, return it.
85
+ # Otherwise, create it
86
+ the_tags << Sharkey::Tag.first_or_create(name: tag)
87
+ end
88
+ end
89
+
90
+ # Actually populating the database with
91
+ # a new Link
92
+ Sharkey::Link.create(title: title || "",
93
+ url: url,
94
+ added_at: added_at || DateTime.now,
95
+ tags: the_tags,
96
+ category: Sharkey::Category.get(category),
97
+ comment: comment || "")
98
+ end
99
+
100
+ def self.update_link(id, title, url, tags, category, comment)
101
+ return if id.nil? or url.nil?
102
+
103
+ # This array will contain the Tags objects
104
+ # created here
105
+ the_tags = []
106
+ if (not tags.nil?) and (not tags.empty?)
107
+ tags.each do |tag|
108
+
109
+ # If Sharkey::Tag exists, return it.
110
+ # Otherwise, create it
111
+ the_tags << Sharkey::Tag.first_or_create(name: tag)
112
+ end
113
+ end
114
+
115
+ Sharkey::Link.get(id).update(title: title,
116
+ url: url,
117
+ tags: the_tags,
118
+ category: Sharkey::Category.get(category),
119
+ comment: comment || "");
120
+
121
+ end
122
+
123
+ # Returns all Links that have a Tag with `tag_id`
124
+ def self.by_tag(tag_id)
125
+
126
+ # RANT: I don't know why I couldn't simply do something like
127
+ # `Sharkey::Link.all(:tag => Sharkey::Tag.get(tag_id))`
128
+ # it seems so strange!
129
+ # DataMapper's docs imply that we actually _can_,
130
+ # so why...?
131
+
132
+ taggings = Sharkey::Tagging.all(:tag_id => tag_id)
133
+
134
+ Sharkey::Link.all(:taggings => taggings)
135
+ end
136
+
137
+ # Returns all Links that have a Category with `category_id`
138
+ def self.by_category(category_id)
139
+
140
+ # RANT: I don't know why I couldn't simply do something like
141
+ # `Sharkey::Link.all(:category => Sharkey::Category.get(category_id))`
142
+ # it seems so strange!
143
+ # DataMapper's docs imply that we actually _can_,
144
+ # so why...?
145
+
146
+ categorizations = Sharkey::Categorization.all(:category_id => category_id)
147
+
148
+ Sharkey::Link.all(:categorization => categorizations)
149
+ end
150
+
151
+ def toggle_favorite
152
+ self.update(favorite: (not self.favorite));
153
+ end
154
+
155
+ # Tells if this link was ever visited
156
+ def visited?
157
+ self.visit_count != 0
158
+ end
159
+
160
+ # Increases the visit count by one
161
+ def visit
162
+ self.update(last_visit: DateTime.now);
163
+ self.update(visit_count: (self.visit_count + 1));
164
+ end
165
+ end
166
+
167
+ # Single textual tag Links can have
168
+ #
169
+ class Tag
170
+ include DataMapper::Resource
171
+
172
+ property :id, Serial
173
+ property :name, String, :required => true
174
+
175
+ has n, :taggings
176
+ has n, :links, :through => :taggings
177
+ end
178
+
179
+ # The actual action of tagging Links.
180
+ #
181
+ # This is necessary because we can query both
182
+ # of them:
183
+ # - All Links of a Tag
184
+ # - All Tags of a Link
185
+ #
186
+ class Tagging
187
+ include DataMapper::Resource
188
+
189
+ belongs_to :tag, :key => true
190
+ belongs_to :link, :key => true
191
+ end
192
+
193
+ # Category for Links.
194
+ #
195
+ # While a Link can have several Tags, it can only
196
+ # have a single Category.
197
+ #
198
+ # Think of it as a folder on your Bookmarks browser.
199
+ #
200
+ # A category can have _one_ parent and _many_ children.
201
+ #
202
+ class Category
203
+ include DataMapper::Resource
204
+
205
+ property :id, Serial
206
+ property :name, String, :required => true
207
+ property :description, Text
208
+
209
+ has n, :categorizations
210
+ has n, :links, :through => :categorizations
211
+
212
+ # Access the parent through Category.parent
213
+ has 1, :categoryParent, :child_key => [ :source_id ]
214
+ has 1, :parent, self, :through => :categoryParent, :via => :target
215
+
216
+ # Access the childs through Category.childs
217
+ has n, :categoryChilds, :child_key => [ :source_id ]
218
+ has n, :childs, self, :through => :categoryChilds, :via => :target
219
+
220
+ def add_child child
221
+ return if (child.nil?) or (child == self) or (has_child? child)
222
+ return if (child.parent) and (child.parent == self)
223
+
224
+ self.childs << child
225
+ child.parent = self
226
+
227
+ self.save
228
+ self
229
+ end
230
+
231
+ def has_child? child
232
+ self.childs.member? child
233
+ end
234
+
235
+ # Removes the parent/children relationship
236
+ # @note Does not remove any Categories!
237
+ def remove_child child
238
+ throw 'Removing self as child' if child == self
239
+
240
+ if self.categoryChilds
241
+ self.categoryChilds.all(target_id: child.id).destroy
242
+ end
243
+
244
+ if child.categoryParent
245
+ if child.categoryParent.source_id == self.id
246
+ child.categoryParent.destroy
247
+ end
248
+ end
249
+
250
+ self.reload
251
+ self
252
+ end
253
+
254
+ def self.orphans
255
+ all.select { |me| me.parent.nil? }
256
+ end
257
+ end
258
+
259
+ class CategoryParent
260
+ include DataMapper::Resource
261
+
262
+ belongs_to :source, 'Category', :key => true
263
+ belongs_to :target, 'Category', :key => true
264
+ end
265
+
266
+ class CategoryChild
267
+ include DataMapper::Resource
268
+
269
+ belongs_to :source, 'Category', :key => true
270
+ belongs_to :target, 'Category', :key => true
271
+ end
272
+
273
+ class Categorization
274
+ include DataMapper::Resource
275
+
276
+ belongs_to :category, :key => true
277
+ belongs_to :link, :key => true
278
+ end
279
+
280
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
281
+ # Create Tables
282
+
283
+ # This method must be called after ALL models
284
+ # have been created and BEFORE the app starts
285
+ DataMapper.finalize
286
+
287
+ # Starting out the SQLite Database
288
+ DataMapper.setup(:default,
289
+ ENV['DATABASE_URL'] || DATABASE_PATH)
290
+
291
+ # Creates Tables if they doesn't exist
292
+ # Tries to adapt new models to already-existing ones...
293
+ DataMapper.auto_upgrade!
294
+ end
295
+
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Multiple Selection Component for Bootstrap
3
+ * Check nicolasbize.github.io/magicsuggest/ for latest updates.
4
+ *
5
+ * Author: Nicolas Bize
6
+ * Created: Feb 8th 2013
7
+ * Last Updated: Jun 3rd 2014
8
+ * Version: 2.0.5
9
+ * Licence: MagicSuggest is licenced under MIT licence (http://opensource.org/licenses/MIT)
10
+ */
11
+ .ms-ctn{
12
+ position: relative;
13
+ padding: 5px 12px;
14
+ height: auto;
15
+ }
16
+ .ms-inv{
17
+ border: 1px solid #CC0000;
18
+ }
19
+ .ms-ctn-readonly{
20
+ cursor: pointer;
21
+ }
22
+ .ms-ctn-disabled{
23
+ cursor: not-allowed;
24
+ background-color: #eeeeee;
25
+ }
26
+ .ms-ctn-bootstrap-focus,
27
+ .ms-ctn-bootstrap-focus .ms-res-ctn{
28
+ border-color: rgba(82, 168, 236, 0.8) !important;
29
+ /* IE6-9 */
30
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6) !important;
31
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6) !important;
32
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6) !important;
33
+ border-bottom-left-radius: 0;
34
+ border-bottom-right-radius: 0;
35
+ }
36
+ .ms-ctn-focus{
37
+ border-color: #66afe9;
38
+ outline: 0;
39
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
40
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
41
+ }
42
+ .ms-ctn input{
43
+ border: 0;
44
+ box-shadow: none;
45
+ -webkit-transition: none;
46
+ outline: none;
47
+ display: block;
48
+ padding: 0;
49
+ line-height: 1.42857143;
50
+ margin: 1px 0;
51
+ width: 100%;
52
+ }
53
+ .ms-ctn .ms-sel-ctn input{
54
+ float: left;
55
+ }
56
+ .ms-ctn-disabled input{
57
+ cursor: not-allowed;
58
+ background-color: #eeeeee;
59
+ }
60
+ .ms-ctn .ms-input-readonly{
61
+ cursor: pointer;
62
+ }
63
+ .ms-ctn .ms-empty-text{
64
+ color: #DDD;
65
+ }
66
+ .ms-ctn input:focus{
67
+ border: 0;
68
+ box-shadow: none;
69
+ -webkit-transition: none;
70
+ background: #FFF;
71
+ }
72
+ .ms-ctn input::-ms-clear {
73
+ width: 0;
74
+ height: 0;
75
+ }
76
+ .ms-ctn .ms-trigger{
77
+ top: 0;
78
+ width: 25px;
79
+ height:100%;
80
+ position:absolute;
81
+ right:0;
82
+ background: transparent;
83
+ border-left: 1px solid #CCC;
84
+ cursor: pointer;
85
+ }
86
+ .ms-ctn .ms-trigger .ms-trigger-ico {
87
+ display: inline-block;
88
+ width: 0;
89
+ height: 0;
90
+ vertical-align: top;
91
+ border-top: 4px solid #333;
92
+ border-right: 4px solid transparent;
93
+ border-left: 4px solid transparent;
94
+ content: "";
95
+ margin-left: 8px;
96
+ margin-top: 15px;
97
+ }
98
+ .ms-ctn .ms-trigger:hover{
99
+ background-color: #e6e6e6;
100
+ }
101
+ .ms-ctn .ms-trigger:hover .ms-trigger-ico{
102
+ background-position: 0 -4px;
103
+ }
104
+ .ms-ctn-disabled .ms-trigger{
105
+ cursor: not-allowed;
106
+ background-color: #eeeeee;
107
+ }
108
+ .ms-ctn-bootstrap-focus{
109
+ border-bottom: 1px solid #CCC;
110
+ }
111
+ .ms-res-ctn{
112
+ width: 100%;
113
+ display: block;
114
+ overflow-y: auto;
115
+ }
116
+ .ms-res-ctn .ms-res-group{
117
+ line-height: 23px;
118
+ text-align: left;
119
+ padding: 2px 5px;
120
+ font-weight: bold;
121
+ border-bottom: 1px dotted #CCC;
122
+ border-top: 1px solid #CCC;
123
+ background: #f3edff;
124
+ color: #333;
125
+ }
126
+ .ms-res-ctn .ms-res-item{
127
+ line-height: 25px;
128
+ text-align: left;
129
+ padding: 2px 5px;
130
+ color: #666;
131
+ cursor: pointer;
132
+ }
133
+ .ms-res-ctn .ms-res-item-grouped{
134
+ padding-left: 15px;
135
+ }
136
+ .ms-res-ctn .ms-res-odd{
137
+ background: #FAFAFA;
138
+ }
139
+ .ms-res-ctn .ms-res-item-active{
140
+ background-color: #F5F5F5;
141
+ }
142
+ .ms-sel-ctn{
143
+ overflow: auto;
144
+ line-height: 18px;
145
+ padding-right: 25px;
146
+ }
147
+ .ms-no-trigger .ms-sel-ctn{
148
+ padding-right: 0;
149
+ }
150
+ /** Outer and global tags **/
151
+ .ms-sel-ctn .ms-sel-item{
152
+ background: #F3F3F3;
153
+ color: #999;
154
+ float: left;
155
+ font-size: 12px;
156
+ padding: 3px 5px;
157
+ border-radius: 3px;
158
+ border: 1px solid #DDD;
159
+ margin: 3px 0px 1px 0;
160
+ }
161
+ .ms-sel-ctn .ms-sel-invalid{
162
+ border-color: rgb(248, 165, 165) !important;
163
+ background: #FDF2F2 !important;
164
+ }
165
+ .ms-sel-ctn .ms-sel-item:hover{
166
+ border: 1px solid #BBB;
167
+ }
168
+ /** For inner tags **/
169
+ .ms-ctn .ms-sel-item{
170
+ background: #F3F3F3;
171
+ color: #999;
172
+ float: left;
173
+ font-size: 12px;
174
+ padding: 0 5px;
175
+ border-radius: 3px;
176
+ border: 1px solid #DDD;
177
+ margin: 1px 5px 1px 0;
178
+ }
179
+ .ms-ctn .ms-sel-item:hover{
180
+ border: 1px solid transparent;
181
+ }
182
+ .ms-ctn-focus .ms-sel-item:hover{
183
+ border: 1px solid #BBB;
184
+ }
185
+ .ms-sel-ctn .ms-sel-text{
186
+ background: #FFF;
187
+ color: #666;
188
+ padding-right: 0;
189
+ margin-left: 0;
190
+ font-size: 14px;
191
+ font-weight: normal;
192
+ }
193
+ .ms-as-string .ms-sel-text{
194
+ border-color: transparent;
195
+ }
196
+ .ms-res-ctn .ms-res-item em{
197
+ font-style: normal;
198
+ background: #565656;
199
+ color: #FFF;
200
+ }
201
+ .ms-sel-ctn .ms-sel-text:hover{
202
+ background: #FFF;
203
+ }
204
+ .ms-sel-ctn .ms-sel-item-active{
205
+ border: 1px solid red;
206
+ background: #757575;
207
+ }
208
+ .ms-stacked .ms-sel-item{
209
+ float: inherit;
210
+ }
211
+ .ms-sel-ctn .ms-sel-item .ms-close-btn{
212
+ width: 7px;
213
+ cursor: pointer;
214
+ height: 7px;
215
+ float: right;
216
+ margin: 6px 2px 0 10px;
217
+ background-image: url(data:image/png;base64,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);
218
+ background-position: 0 -7px;
219
+ }
220
+ .ms-sel-ctn .ms-sel-item .ms-close-btn:hover{
221
+ background-position: 0 0;
222
+ }
223
+ .ms-stacked .ms-sel-item .ms-close-btn {
224
+ margin-left: 0px;
225
+ }
226
+ .ms-helper{
227
+ color: #AAA;
228
+ font-size: 10px;
229
+ position: absolute;
230
+ top: -17px;
231
+ right: 0;
232
+ }