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