Commit 3d9edbbbb1f897685de1caf92f8b9d03c5e48171

Authored by Francisco Marcelo de Araújo Lima Júnior
1 parent 5d182131

change tagging ui

app/controllers/my_profile/cms_controller.rb
... ... @@ -4,6 +4,12 @@ class CmsController < MyProfileController
4 4  
5 5 include ArticleHelper
6 6  
  7 + def search_tags
  8 + arg = params[:term].downcase
  9 + result = ActsAsTaggableOn::Tag.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"])
  10 + render :text => prepare_to_token_input2(result).to_json
  11 + end
  12 +
7 13 def self.protect_if(*args)
8 14 before_filter(*args) do |c|
9 15 user, profile = c.send(:user), c.send(:profile)
... ...
app/helpers/article_helper.rb
... ... @@ -83,6 +83,10 @@ module ArticleHelper
83 83 array.map { |object| {:id => object.id, :name => object.name} }
84 84 end
85 85  
  86 + def prepare_to_token_input2(array)
  87 + array.map { |object| {:label => object.name, :value => object.name} }
  88 + end
  89 +
86 90 def cms_label_for_new_children
87 91 _('New article')
88 92 end
... ...
app/views/cms/edit.html.erb
... ... @@ -31,8 +31,18 @@
31 31  
32 32 <%= select_categories(:article, _('Categorize your article')) %>
33 33  
  34 + <br />
  35 +
  36 + <%= stylesheet_link_tag('inputosaurus') %>
  37 +
34 38 <%= f.text_field('tag_list', :size => 64) %>
35   - <%= content_tag( 'small', _('Separate tags with commas') ) %>
  39 +
  40 + <script>
  41 + jQuery('#article_tag_list').inputosaurus({
  42 + autoCompleteSource: <%= "'/myprofile/#{profile.identifier}/cms/search_tags'," %>
  43 + activateFinalResult : true
  44 + })
  45 + </script>
36 46  
37 47 <div id='edit-article-options'>
38 48 <%= options_for_article(@article, @tokenized_children) %>
... ...
app/views/layouts/_javascript.html.erb
... ... @@ -4,7 +4,7 @@
4 4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
5 5 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput',
6 6 'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow',
7   -'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', :cache => 'cache/application' %>
  7 +'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', 'inputosaurus.js', :cache => 'cache/application' %>
8 8  
9 9 <% language = FastGettext.locale %>
10 10 <% %w{messages methods}.each do |type| %>
... ...
public/javascripts/inputosaurus.js 0 → 100644
... ... @@ -0,0 +1,523 @@
  1 +/**
  2 + * Inputosaurus Text
  3 + *
  4 + * Must be instantiated on an <input> element
  5 + * Allows multiple input items. Each item is represented with a removable tag that appears to be inside the input area.
  6 + *
  7 + * @requires:
  8 + *
  9 + * jQuery 1.7+
  10 + * jQueryUI 1.8+ Core
  11 + *
  12 + * @version 0.1.6
  13 + * @author Dan Kielp <dan@sproutsocial.com>
  14 + * @created October 3,2012
  15 + *
  16 + */
  17 +
  18 +
  19 +(function($) {
  20 +
  21 + var inputosaurustext = {
  22 +
  23 + version: "0.1.6",
  24 +
  25 + eventprefix: "inputosaurus",
  26 +
  27 + options: {
  28 +
  29 + // bindable events
  30 + //
  31 + // 'change' - triggered whenever a tag is added or removed (should be similar to binding the the change event of the instantiated input
  32 + // 'keyup' - keyup event on the newly created input
  33 +
  34 + // while typing, the user can separate values using these delimiters
  35 + // the value tags are created on the fly when an inputDelimiter is detected
  36 + inputDelimiters : [',', ';'],
  37 +
  38 + // this separator is used to rejoin all input items back to the value of the original <input>
  39 + outputDelimiter : ',',
  40 +
  41 + allowDuplicates : false,
  42 +
  43 + parseOnBlur : false,
  44 +
  45 + // optional wrapper for widget
  46 + wrapperElement : null,
  47 +
  48 + width : null,
  49 +
  50 + // simply passing an autoComplete source (array, string or function) will instantiate autocomplete functionality
  51 + autoCompleteSource : '',
  52 +
  53 + // When forcing users to select from the autocomplete list, allow them to press 'Enter' to select an item if it's the only option left.
  54 + activateFinalResult : false,
  55 +
  56 + // manipulate and return the input value after parseInput() parsing
  57 + // the array of tag names is passed and expected to be returned as an array after manipulation
  58 + parseHook : null,
  59 +
  60 + // define a placeholder to display when the input is empty
  61 + placeholder: null,
  62 +
  63 + // when you check for duplicates it check for the case
  64 + caseSensitiveDuplicates: false
  65 + },
  66 +
  67 + _create: function() {
  68 + var widget = this,
  69 + els = {},
  70 + o = widget.options,
  71 + placeholder = o.placeholder || this.element.attr('placeholder') || null;
  72 +
  73 + this._chosenValues = [];
  74 +
  75 + // Create the elements
  76 + els.ul = $('<ul class="inputosaurus-container">');
  77 + els.input = $('<input type="text" />');
  78 + els.inputCont = $('<li class="inputosaurus-input inputosaurus-required"></li>');
  79 + els.origInputCont = $('<li class="inputosaurus-input-hidden inputosaurus-required">');
  80 +
  81 + // define starting placeholder
  82 + if (placeholder) {
  83 + o.placeholder = placeholder;
  84 + els.input.attr('placeholder', o.placeholder);
  85 + if (o.width) {
  86 + els.input.css('min-width', o.width - 50);
  87 + }
  88 + }
  89 +
  90 + o.wrapperElement && o.wrapperElement.append(els.ul);
  91 + this.element.replaceWith(o.wrapperElement || els.ul);
  92 + els.origInputCont.append(this.element).hide();
  93 +
  94 + els.inputCont.append(els.input);
  95 + els.ul.append(els.inputCont);
  96 + els.ul.append(els.origInputCont);
  97 +
  98 + o.width && els.ul.css('width', o.width);
  99 +
  100 + this.elements = els;
  101 +
  102 + widget._attachEvents();
  103 +
  104 + // if instantiated input already contains a value, parse that junk
  105 + if($.trim(this.element.val())){
  106 + els.input.val( this.element.val() );
  107 + this.parseInput();
  108 + }
  109 +
  110 + this._instAutocomplete();
  111 + },
  112 +
  113 + _instAutocomplete : function() {
  114 + if(this.options.autoCompleteSource){
  115 + var widget = this;
  116 +
  117 + this.elements.input.autocomplete({
  118 + position : {
  119 + of : this.elements.ul
  120 + },
  121 + source : this.options.autoCompleteSource,
  122 + minLength : 1,
  123 + select : function(ev, ui){
  124 + ev.preventDefault();
  125 + widget.elements.input.val(ui.item.value);
  126 + widget.parseInput();
  127 + },
  128 + open : function() {
  129 + // Older versions of jQueryUI have a different namespace
  130 + var auto = $(this).data('ui-autocomplete') || $(this).data('autocomplete');
  131 + var menu = auto.menu,
  132 + $menuItems;
  133 +
  134 +
  135 + // zIndex will force the element on top of anything (like a dialog it's in)
  136 + menu.element.zIndex && menu.element.zIndex($(this).zIndex() + 1);
  137 + menu.element.width(widget.elements.ul.outerWidth());
  138 +
  139 + // auto-activate the result if it's the only one
  140 + if(widget.options.activateFinalResult){
  141 + $menuItems = menu.element.find('li');
  142 +
  143 + // activate single item to allow selection upon pressing 'Enter'
  144 + if($menuItems.size() === 1){
  145 + menu[menu.activate ? 'activate' : 'focus']($.Event('click'), $menuItems);
  146 + }
  147 + }
  148 + }
  149 + });
  150 + }
  151 + },
  152 +
  153 + _autoCompleteMenuPosition : function() {
  154 + var widget;
  155 + if(this.options.autoCompleteSource){
  156 + widget = this.elements.input.data('ui-autocomplete') || this.elements.input.data('autocomplete');
  157 + widget && widget.menu.element.position({
  158 + of: this.elements.ul,
  159 + my: 'left top',
  160 + at: 'left bottom',
  161 + collision: 'none'
  162 + });
  163 + }
  164 + },
  165 +
  166 + /*_closeAutoCompleteMenu : function() {
  167 + if(this.options.autoCompleteSource){
  168 + this.elements.input.autocomplete('close');
  169 + }
  170 + },*/
  171 +
  172 + parseInput : function(ev) {
  173 + var widget = (ev && ev.data.widget) || this,
  174 + val,
  175 + delimiterFound = false,
  176 + values = [];
  177 +
  178 + val = widget.elements.input.val();
  179 +
  180 + val && (delimiterFound = widget._containsDelimiter(val));
  181 +
  182 + if(delimiterFound !== false){
  183 + values = val.split(delimiterFound);
  184 + } else if(!ev || ev.which === $.ui.keyCode.ENTER && !$('.ui-menu-item.ui-state-focus').size() && !$('.ui-menu-item .ui-state-focus').size() && !$('#ui-active-menuitem').size()){
  185 + values.push(val);
  186 + ev && ev.preventDefault();
  187 +
  188 + // prevent autoComplete menu click from causing a false 'blur'
  189 + } else if(ev.type === 'blur' && !$('#ui-active-menuitem').size()){
  190 + values.push(val);
  191 + }
  192 +
  193 + $.isFunction(widget.options.parseHook) && (values = widget.options.parseHook(values));
  194 +
  195 + if(values.length){
  196 + widget._setChosen(values);
  197 + widget.elements.input.val('');
  198 + widget._resizeInput();
  199 + }
  200 +
  201 + widget._resetPlaceholder();
  202 + },
  203 +
  204 + _inputFocus : function(ev) {
  205 + var widget = ev.data.widget || this;
  206 +
  207 + widget.elements.input.value || (widget.options.autoCompleteSource.length && widget.elements.input.autocomplete('search', ''));
  208 + },
  209 +
  210 + _inputKeypress : function(ev) {
  211 + var widget = ev.data.widget || this;
  212 +
  213 + ev.type === 'keyup' && widget._trigger('keyup', ev, widget);
  214 +
  215 + switch(ev.which){
  216 + case $.ui.keyCode.BACKSPACE:
  217 + ev.type === 'keydown' && widget._inputBackspace(ev);
  218 + break;
  219 +
  220 + case $.ui.keyCode.LEFT:
  221 + ev.type === 'keydown' && widget._inputBackspace(ev);
  222 + break;
  223 +
  224 + default :
  225 + widget.parseInput(ev);
  226 + widget._resizeInput(ev);
  227 + }
  228 +
  229 + // reposition autoComplete menu as <ul> grows and shrinks vertically
  230 + if(widget.options.autoCompleteSource){
  231 + setTimeout(function(){widget._autoCompleteMenuPosition.call(widget);}, 200);
  232 + }
  233 + },
  234 +
  235 + // the input dynamically resizes based on the length of its value
  236 + _resizeInput : function(ev) {
  237 + var widget = (ev && ev.data.widget) || this,
  238 + maxWidth = widget.elements.ul.width(),
  239 + val = widget.elements.input.val(),
  240 + txtWidth = 25 + val.length * 8;
  241 +
  242 + widget.elements.input.width(txtWidth < maxWidth ? txtWidth : maxWidth);
  243 + },
  244 +
  245 + // resets placeholder on representative input
  246 + _resetPlaceholder: function () {
  247 + var placeholder = this.options.placeholder,
  248 + input = this.elements.input,
  249 + width = this.options.width || 'inherit';
  250 + if (placeholder && this.element.val().length === 0) {
  251 + input.attr('placeholder', placeholder).css('min-width', width - 50)
  252 + }else {
  253 + input.attr('placeholder', '').css('min-width', 'inherit')
  254 + }
  255 + },
  256 +
  257 + // if our input contains no value and backspace has been pressed, select the last tag
  258 + _inputBackspace : function(ev) {
  259 + var widget = (ev && ev.data.widget) || this;
  260 + lastTag = widget.elements.ul.find('li:not(.inputosaurus-required):last');
  261 +
  262 + // IE goes back in history if the event isn't stopped
  263 + ev.stopPropagation();
  264 +
  265 + if((!$(ev.currentTarget).val() || (('selectionStart' in ev.currentTarget) && ev.currentTarget.selectionStart === 0 && ev.currentTarget.selectionEnd === 0)) && lastTag.size()){
  266 + ev.preventDefault();
  267 + lastTag.find('a').focus();
  268 + }
  269 +
  270 + },
  271 +
  272 + _editTag : function(ev) {
  273 + var widget = (ev && ev.data.widget) || this,
  274 + tagName = '',
  275 + $closest = $(ev.currentTarget).closest('li'),
  276 + tagKey = $closest.data('ui-inputosaurus') || $closest.data('inputosaurus');
  277 +
  278 + if(!tagKey){
  279 + return true;
  280 + }
  281 +
  282 + ev.preventDefault();
  283 +
  284 + $.each(widget._chosenValues, function(i,v) {
  285 + v.key === tagKey && (tagName = v.value);
  286 + });
  287 +
  288 + widget.elements.input.val(tagName);
  289 +
  290 + widget._removeTag(ev);
  291 + widget._resizeInput(ev);
  292 + },
  293 +
  294 + _tagKeypress : function(ev) {
  295 + var widget = ev.data.widget;
  296 + switch(ev.which){
  297 +
  298 + case $.ui.keyCode.BACKSPACE:
  299 + ev && ev.preventDefault();
  300 + ev && ev.stopPropagation();
  301 + $(ev.currentTarget).trigger('click');
  302 + break;
  303 +
  304 + // 'e' - edit tag (removes tag and places value into visible input
  305 + case 69:
  306 + widget._editTag(ev);
  307 + break;
  308 +
  309 + case $.ui.keyCode.LEFT:
  310 + ev.type === 'keydown' && widget._prevTag(ev);
  311 + break;
  312 +
  313 + case $.ui.keyCode.RIGHT:
  314 + ev.type === 'keydown' && widget._nextTag(ev);
  315 + break;
  316 +
  317 + case $.ui.keyCode.DOWN:
  318 + ev.type === 'keydown' && widget._focus(ev);
  319 + break;
  320 + }
  321 + },
  322 +
  323 + // select the previous tag or input if no more tags exist
  324 + _prevTag : function(ev) {
  325 + var widget = (ev && ev.data.widget) || this,
  326 + tag = $(ev.currentTarget).closest('li'),
  327 + previous = tag.prev();
  328 +
  329 + if(previous.is('li')){
  330 + previous.find('a').focus();
  331 + } else {
  332 + widget._focus();
  333 + }
  334 + },
  335 +
  336 + // select the next tag or input if no more tags exist
  337 + _nextTag : function(ev) {
  338 + var widget = (ev && ev.data.widget) || this,
  339 + tag = $(ev.currentTarget).closest('li'),
  340 + next = tag.next();
  341 +
  342 + if(next.is('li:not(.inputosaurus-input)')){
  343 + next.find('a').focus();
  344 + } else {
  345 + widget._focus();
  346 + }
  347 + },
  348 +
  349 + // return the inputDelimiter that was detected or false if none were found
  350 + _containsDelimiter : function(tagStr) {
  351 +
  352 + var found = false;
  353 +
  354 + $.each(this.options.inputDelimiters, function(k,v) {
  355 + if(tagStr.indexOf(v) !== -1){
  356 + found = v;
  357 + }
  358 + });
  359 +
  360 + return found;
  361 + },
  362 +
  363 + _setChosen : function(valArr) {
  364 + var self = this;
  365 +
  366 + if(!$.isArray(valArr)){
  367 + return false;
  368 + }
  369 +
  370 + $.each(valArr, function(k,v) {
  371 + var exists = false,
  372 + obj = {
  373 + key : '',
  374 + value : ''
  375 + };
  376 +
  377 + v = $.trim(v);
  378 +
  379 + $.each(self._chosenValues, function(kk,vv) {
  380 + if(!self.options.caseSensitiveDuplicates){
  381 + vv.value.toLowerCase() === v.toLowerCase() && (exists = true);
  382 + }
  383 + else{
  384 + vv.value === v && (exists = true);
  385 + }
  386 + });
  387 +
  388 + if(v !== '' && (!exists || self.options.allowDuplicates)){
  389 +
  390 + obj.key = 'mi_' + Math.random().toString( 16 ).slice( 2, 10 );
  391 + obj.value = v;
  392 + self._chosenValues.push(obj);
  393 +
  394 + self._renderTags();
  395 + }
  396 + });
  397 + self._setValue(self._buildValue());
  398 + },
  399 +
  400 + _buildValue : function() {
  401 + var widget = this,
  402 + value = '';
  403 +
  404 + $.each(this._chosenValues, function(k,v) {
  405 + value += value.length ? widget.options.outputDelimiter + v.value : v.value;
  406 + });
  407 +
  408 + return value;
  409 + },
  410 +
  411 + _setValue : function(value) {
  412 + var val = this.element.val();
  413 +
  414 + if(val !== value){
  415 + this.element.val(value);
  416 + this._trigger('change');
  417 + }
  418 + },
  419 +
  420 + // @name text for tag
  421 + // @className optional className for <li>
  422 + _createTag : function(name, key, className) {
  423 + className = className ? ' class="' + className + '"' : '';
  424 +
  425 + if(name !== undefined){
  426 + return $('<li' + className + ' data-inputosaurus="' + key + '"><span>' + name + '</span> <a href="javascript:void(0);" class="ficon">&#x2716;</a></li>');
  427 + }
  428 + },
  429 +
  430 + _renderTags : function() {
  431 + var self = this;
  432 +
  433 + this.elements.ul.find('li:not(.inputosaurus-required)').remove();
  434 +
  435 + $.each(this._chosenValues, function(k,v) {
  436 + var el = self._createTag(v.value, v.key);
  437 + self.elements.ul.find('li.inputosaurus-input').before(el);
  438 + });
  439 + },
  440 +
  441 + _removeTag : function(ev) {
  442 + var $closest = $(ev.currentTarget).closest('li'),
  443 + key = $closest.data('ui-inputosaurus') || $closest.data('inputosaurus'),
  444 + indexFound = false,
  445 + widget = (ev && ev.data.widget) || this;
  446 +
  447 +
  448 + $.each(widget._chosenValues, function(k,v) {
  449 + if(key === v.key){
  450 + indexFound = k;
  451 + }
  452 + });
  453 +
  454 + indexFound !== false && widget._chosenValues.splice(indexFound, 1);
  455 +
  456 + widget._setValue(widget._buildValue());
  457 +
  458 + $(ev.currentTarget).closest('li').remove();
  459 + widget.elements.input.focus();
  460 + },
  461 +
  462 + _focus : function(ev) {
  463 + var widget = (ev && ev.data.widget) || this,
  464 + $closest = $(ev.target).closest('li'),
  465 + $data = $closest.data('ui-inputosaurus') || $closest.data('inputosaurus');
  466 +
  467 + if(!ev || !$data){
  468 + widget.elements.input.focus();
  469 + }
  470 + },
  471 +
  472 + _tagFocus : function(ev) {
  473 + $(ev.currentTarget).parent()[ev.type === 'focusout' ? 'removeClass' : 'addClass']('inputosaurus-selected');
  474 + },
  475 +
  476 + refresh : function() {
  477 + var delim = this.options.outputDelimiter,
  478 + val = this.element.val(),
  479 + values = [];
  480 +
  481 + values.push(val);
  482 + delim && (values = val.split(delim));
  483 +
  484 + if(values.length){
  485 + this._chosenValues = [];
  486 +
  487 + $.isFunction(this.options.parseHook) && (values = this.options.parseHook(values));
  488 +
  489 + this._setChosen(values);
  490 + this._renderTags();
  491 + this.elements.input.val('');
  492 + this._resizeInput();
  493 + }
  494 + },
  495 +
  496 + _attachEvents : function() {
  497 + var widget = this;
  498 +
  499 + this.elements.input.on('keyup.inputosaurus', {widget : widget}, this._inputKeypress);
  500 + this.elements.input.on('keydown.inputosaurus', {widget : widget}, this._inputKeypress);
  501 + this.elements.input.on('change.inputosaurus', {widget : widget}, this._inputKeypress);
  502 + this.elements.input.on('focus.inputosaurus', {widget : widget}, this._inputFocus);
  503 + this.options.parseOnBlur && this.elements.input.on('blur.inputosaurus', {widget : widget}, this.parseInput);
  504 +
  505 + this.elements.ul.on('click.inputosaurus', {widget : widget}, this._focus);
  506 + this.elements.ul.on('click.inputosaurus', 'a', {widget : widget}, this._removeTag);
  507 + this.elements.ul.on('dblclick.inputosaurus', 'li', {widget : widget}, this._editTag);
  508 + this.elements.ul.on('focus.inputosaurus', 'a', {widget : widget}, this._tagFocus);
  509 + this.elements.ul.on('blur.inputosaurus', 'a', {widget : widget}, this._tagFocus);
  510 + this.elements.ul.on('keydown.inputosaurus', 'a', {widget : widget}, this._tagKeypress);
  511 + },
  512 +
  513 + _destroy: function() {
  514 + this.elements.input.unbind('.inputosaurus');
  515 +
  516 + this.elements.ul.replaceWith(this.element);
  517 +
  518 + }
  519 + };
  520 +
  521 + $.widget("ui.inputosaurus", inputosaurustext);
  522 +})(jQuery);
  523 +
... ...
public/stylesheets/inputosaurus.css 0 → 100644
... ... @@ -0,0 +1,74 @@
  1 +.inputosaurus-container {
  2 + /*background-color: #fff;*/
  3 + /*border: 1px solid #bcbec0;*/
  4 + margin: 0;
  5 + padding: 0;
  6 + display: inline-block;
  7 + cursor: text;
  8 + /**font-size: 14px;**/
  9 + /**font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;**/
  10 + max-height: 300px;
  11 + overflow: hidden;
  12 + overflow-y: auto;
  13 +}
  14 +.inputosaurus-container li {
  15 + display: block;
  16 + float: left;
  17 + overflow: hidden;
  18 + margin: 2px 2px 0;
  19 + padding: 2px 3px;
  20 + white-space: nowrap;
  21 + overflow: hidden;
  22 + -o-text-overflow: ellipsis;
  23 + -ms-text-overflow: ellipsis;
  24 + text-overflow: ellipsis;
  25 + background-color: #e5eff7;
  26 + border: #a9cae4 solid 1px;
  27 + -webkit-border-radius: 2px;
  28 + -moz-border-radius: 2px;
  29 + border-radius: 2px;
  30 + color: #5b9bcd;
  31 + -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.75) inset;
  32 + -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.75) inset;
  33 + box-shadow: 0 1px 0 rgba(255,255,255,0.75) inset;
  34 + line-height: 20px;
  35 + cursor: default;
  36 +}
  37 +.inputosaurus-container li.inputosaurus-selected { background-color: #bdd6eb; }
  38 +.inputosaurus-container li a {
  39 + font-size: 16px;
  40 + color: #5b9bcd;
  41 + padding: 1px;
  42 + text-decoration: none;
  43 + outline: none;
  44 +}
  45 +.inputosaurus-container .inputosaurus-input {
  46 + border: none;
  47 + box-shadow: none;
  48 + background-color: #fff;
  49 + margin-top: 3px;
  50 +}
  51 +.inputosaurus-container .inputosaurus-input input {
  52 + border: none;
  53 + height: 23px;
  54 + font-size: 14px;
  55 + line-height: 20px;
  56 + color: #555;
  57 + margin: 0;
  58 + outline: none;
  59 + padding: 0 0 1px 1px;
  60 + width: 25px;
  61 + -webkit-box-shadow: none;
  62 + -moz-box-shadow: none;
  63 + box-shadow: none;
  64 + background-image: none;
  65 + text-indent: 0px;
  66 +}
  67 +
  68 +.inputosaurus-container .inputosaurus-input input:hover {
  69 + -webkit-box-shadow: none;
  70 + -moz-box-shadow: none;
  71 + box-shadow: none;
  72 + border: 1px solid #c0c0c0;
  73 +}
  74 +.inputosaurus-input-hidden { display: none; }
... ...