Commit 890863fe authored by Serge S. Koval's avatar Serge S. Koval

AJAX foreign key loading for SQLa backend

parent bb5d8efc
......@@ -121,6 +121,11 @@ class PostAdmin(sqla.ModelView):
text=dict(label='Big Text', validators=[validators.required()])
)
form_ajax_refs = {
'user': (User.username, User.email),
'tags': (Tag.name,)
}
def __init__(self, session):
# Just call parent class with predefined model.
super(PostAdmin, self).__init__(Post, session)
......
......@@ -6,6 +6,7 @@ from flask.ext.admin.form import Select2Field
from flask.ext.admin.model.form import (converts, ModelConverterBase,
InlineFormAdmin, InlineModelConverterBase,
FieldPlaceholder)
from flask.ext.admin.model.fields import AjaxSelectField, AjaxSelectMultipleField
from flask.ext.admin.model.helpers import prettify_name
from flask.ext.admin._backwards import get_property
from flask.ext.admin._compat import iteritems
......@@ -70,6 +71,31 @@ class AdminModelConverter(ModelConverterBase):
return None
def _model_select_field(self, prop, multiple, remote_model, **kwargs):
loader = self.view._form_ajax_refs.get(prop.key)
if loader:
if multiple:
return AjaxSelectMultipleField(loader, **kwargs)
else:
return AjaxSelectField(loader, **kwargs)
if 'query_factory' not in kwargs:
kwargs['query_factory'] = lambda: self.session.query(remote_model)
if 'widget' not in kwargs:
if prop.direction.name == 'MANYTOONE':
kwargs['widget'] = form.Select2Widget()
elif prop.direction.name == 'ONETOMANY':
kwargs['widget'] = form.Select2Widget(multiple=True)
elif prop.direction.name == 'MANYTOMANY':
kwargs['widget'] = form.Select2Widget(multiple=True)
if multiple:
return QuerySelectMultipleField(**kwargs)
else:
return QuerySelectField(**kwargs)
def _convert_relation(self, prop, kwargs):
remote_model = prop.mapper.class_
local_column = prop.local_remote_pairs[0][0]
......@@ -85,16 +111,6 @@ class AdminModelConverter(ModelConverterBase):
# Contribute model-related parameters
if 'allow_blank' not in kwargs:
kwargs['allow_blank'] = local_column.nullable
if 'query_factory' not in kwargs:
kwargs['query_factory'] = lambda: self.session.query(remote_model)
if 'widget' not in kwargs:
if prop.direction.name == 'MANYTOONE':
kwargs['widget'] = form.Select2Widget()
elif prop.direction.name == 'ONETOMANY':
kwargs['widget'] = form.Select2Widget(multiple=True)
elif prop.direction.name == 'MANYTOMANY':
kwargs['widget'] = form.Select2Widget(multiple=True)
# Override field type if necessary
override = self._get_field_override(prop.key)
......@@ -102,15 +118,15 @@ class AdminModelConverter(ModelConverterBase):
return override(**kwargs)
if prop.direction.name == 'MANYTOONE':
return QuerySelectField(**kwargs)
return self._model_select_field(prop, False, remote_model, **kwargs)
elif prop.direction.name == 'ONETOMANY':
# Skip backrefs
if not local_column.foreign_keys and getattr(self.view, 'column_hide_backrefs', True):
return None
return QuerySelectMultipleField(**kwargs)
return self._model_select_field(prop, True, remote_model, **kwargs)
elif prop.direction.name == 'MANYTOMANY':
return QuerySelectMultipleField(**kwargs)
return self._model_select_field(prop, True, remote_model, **kwargs)
def convert(self, model, mapper, prop, field_args, hidden_pk):
# Properly handle forced fields
......
......@@ -16,6 +16,8 @@ from flask.ext.admin._backwards import ObsoleteAttr
from flask.ext.admin.contrib.sqla import form, filters, tools
from .typefmt import DEFAULT_FORMATTERS
from .tools import is_inherited_primary_key, get_column_for_current_model, get_query_for_ids
from .ajax import QueryAjaxModelLoader
class ModelView(BaseModelView):
"""
......@@ -585,6 +587,33 @@ class ModelView(BaseModelView):
return joined
# AJAX foreignkey support
def _create_ajax_loader(self, name, fields):
attr = getattr(self.model, name, None)
if attr is None:
raise ValueError('Model %s does not have field %s.' % (self.model, name))
if not hasattr(attr, 'property') or not hasattr(attr.property, 'direction'):
raise ValueError('%s.%s is not a relation.' % (self.model, name))
remote_model = attr.prop.mapper.class_
remote_fields = []
for field in fields:
if isinstance(field, string_types):
attr = getattr(remote_model, field, None)
if not attr:
raise ValueError('%s.%s does not exist.' % (remote_model, field))
remote_fields.append(attr)
else:
# TODO: Figure out if it is valid SQLAlchemy property?
remote_fields.append(field)
return QueryAjaxModelLoader(name, self.session, remote_model, remote_fields)
# Database-related API
def get_query(self):
"""
......
......@@ -30,7 +30,7 @@ class Select2TagsWidget(widgets.TextInput):
You must include select2.js, form.js and select2 stylesheet for it to work.
"""
def __call__(self, field, **kwargs):
kwargs['data-role'] = u'select'
kwargs['data-role'] = u'select2'
kwargs['data-tags'] = u'1'
return super(Select2TagsWidget, self).__call__(field, **kwargs)
......
import warnings
from flask import request, url_for, redirect, flash
from flask import request, url_for, redirect, flash, abort, json, Response
from jinja2 import contextfunction
......@@ -15,6 +15,7 @@ from flask.ext.admin.tools import rec_getattr
from flask.ext.admin._backwards import ObsoleteAttr
from flask.ext.admin._compat import iteritems, as_unicode
from .helpers import prettify_name, get_mdict_item_or_list
from .ajax import AjaxModelLoader
class BaseModelView(BaseView, ActionsMixin):
......@@ -362,6 +363,21 @@ class BaseModelView(BaseView, ActionsMixin):
In this case, password field will be put between email and secret fields that are autogenerated.
"""
form_ajax_refs = None
"""
Use AJAX for foreign key model loading.
Should contain dictionary, where key is field name and value is enumerable with list to fields
to check against (in remote model).
For example, it can look like::
class MyModelView(BaseModelView):
form_ajax_refs = {
'user': ('first_name', 'last_name', 'email')
}
"""
# Actions
action_disallowed_list = ObsoleteAttr('action_disallowed_list',
'disallowed_actions',
......@@ -434,12 +450,14 @@ class BaseModelView(BaseView, ActionsMixin):
self.column_labels = {}
# Forms
self._create_form_class = self.get_create_form()
self._edit_form_class = self.get_edit_form()
self._form_ajax_refs = self._process_ajax_references()
if self.form_widget_args is None:
self.form_widget_args = {}
self._create_form_class = self.get_create_form()
self._edit_form_class = self.get_edit_form()
# Search
self._search_supported = self.init_search()
......@@ -971,6 +989,31 @@ class BaseModelView(BaseView, ActionsMixin):
return value
# AJAX references
def _process_ajax_references(self):
"""
Process `form_ajax_refs` and generate model loaders that
will be used by the `ajax_lookup` view.
"""
result = {}
if self.form_ajax_refs:
for name, value in iteritems(self.form_ajax_refs):
if isinstance(value, AjaxModelLoader):
result[name] = value
elif isinstance(value, (list, tuple)):
result[name] = self._create_ajax_loader(name, value)
else:
raise ValueError('%s.form_ajax_refs can not handle %s types' % (self, type(value)))
return result
def _create_ajax_loader(self, name, fields):
"""
Model backend will override this to implement AJAX model loading.
"""
raise NotImplemented()
# Views
@expose('/')
def index_view(self):
......@@ -1157,3 +1200,18 @@ class BaseModelView(BaseView, ActionsMixin):
Mass-model action view.
"""
return self.handle_action()
@expose('/ajax/lookup/')
def ajax_lookup(self):
name = request.args.get('name')
query = request.args.get('query')
offset = request.args.get('offset', type=int)
limit = request.args.get('limit', 10, type=int)
loader = self._form_ajax_refs.get(name)
if not loader:
abort(404)
data = [loader.format(m) for m in loader.get_models(query, offset, limit)]
return Response(json.dumps(data), mimetype='application/json')
import itertools
from wtforms.fields import FieldList, FormField
from wtforms.validators import ValidationError
from wtforms.fields import FieldList, FormField, SelectFieldBase
from flask.ext.admin._compat import iteritems
from .widgets import InlineFieldListWidget, InlineFormWidget
from .widgets import InlineFieldListWidget, InlineFormWidget, AjaxSelect2Widget
class InlineFieldList(FieldList):
......@@ -113,3 +114,96 @@ class InlineFormField(FormField):
Inline version of the ``FormField`` widget.
"""
widget = InlineFormWidget()
class AjaxSelectField(SelectFieldBase):
"""
Ajax Model Select Field
"""
widget = AjaxSelect2Widget()
def __init__(self, loader, label=None, validators=None, allow_blank=False, blank_text=u'', **kwargs):
super(AjaxSelectField, self).__init__(label, validators, **kwargs)
self.loader = loader
self.allow_blank = allow_blank
self.blank_text = blank_text
def _get_data(self):
if self._formdata is not None:
model = self.loader.get_one(self._formdata)
if model is not None:
self._set_data(model)
return self._data
def _set_data(self, data):
self._data = data
self._formdata = None
data = property(_get_data, _set_data)
def _format_item(self, item):
value = self.loader.format(self.data)
return (value[0], value[1], True)
def process_formdata(self, valuelist):
if valuelist:
if self.allow_blank and valuelist[0] == u'__None':
self.data = None
else:
self._data = None
self._formdata = valuelist[0]
def pre_validate(self, form):
if not self.allow_blank and self.data is None:
raise ValidationError(self.gettext(u'Not a valid choice'))
class AjaxSelectMultipleField(AjaxSelectField):
"""
Ajax-enabled model multi-select field.
"""
widget = AjaxSelect2Widget(multiple=True)
def __init__(self, loader, label=None, validators=None, default=None, **kwargs):
if default is None:
default = []
super(AjaxSelectMultipleField, self).__init__(loader, label, validators, default=default, **kwargs)
self._invalid_formdata = False
def _get_data(self):
formdata = self._formdata
if formdata is not None:
data = []
# TODO: Optimize?
for item in formdata:
model = self.loader.get_one(item)
if model:
data.append(model)
else:
self._invalid_formdata = True
self._set_data(data)
return self._data
def _set_data(self, data):
self._data = data
self._formdata = None
data = property(_get_data, _set_data)
def process_formdata(self, valuelist):
self._formdata = set(valuelist)
def pre_validate(self, form):
if self._invalid_formdata:
raise ValidationError(self.gettext(u'Not a valid choice'))
elif self.data:
for item in self.data:
if not self.loader.get_one(item):
raise ValidationError(self.gettext(u'Not a valid choice'))
from flask import url_for, json
from wtforms.widgets import HTMLString, html_params
from flask.ext.admin.form import RenderTemplateWidget
......@@ -9,3 +12,32 @@ class InlineFieldListWidget(RenderTemplateWidget):
class InlineFormWidget(RenderTemplateWidget):
def __init__(self):
super(InlineFormWidget, self).__init__('admin/model/inline_form.html')
class AjaxSelect2Widget(object):
def __init__(self, multiple=False):
self.multiple = multiple
def __call__(self, field, **kwargs):
kwargs['data-role'] = u'select2-ajax'
kwargs['data-url'] = url_for('.ajax_lookup', name=field.loader.name)
allow_blank = getattr(field, 'allow_blank', False)
if allow_blank and not self.multiple:
kwargs['data-allow-blank'] = u'1'
kwargs.setdefault('id', field.id)
kwargs.setdefault('type', 'hidden')
if self.multiple:
result = []
for value in field.data:
result.append(field.loader.format(value))
kwargs['value'] = json.dumps(result)
kwargs['data-multiple'] = u'1'
else:
kwargs['value'] = json.dumps(field.loader.format(field.data))
return HTMLString('<input %s>' % html_params(name=field.name, **kwargs))
......@@ -3,6 +3,70 @@
// Field converters
var fieldConverters = [];
/**
* Process AJAX fk-widget
*/
function processAjaxWidget($el, name) {
var multiple = $el.attr('data-multiple') == '1';
var opts = {
width: 'resolve',
minimumInputLength: 1,
ajax: {
url: $el.attr('data-url'),
data: function(term, page) {
return {
query: term,
offset: (page - 1) * 10,
limit: 10
};
},
results: function(data, page) {
var results = [];
for (var k in data) {
var v = data[k];
results.push({id: v[0], text: v[1]});
}
return {
results: results,
more: results.length == 10
};
}
},
initSelection: function(element, callback) {
var value = jQuery.parseJSON(element.val());
var result = null;
if (value) {
if (multiple) {
result = [];
for (var k in value) {
var v = value[k];
result.push({id: v[0], text: v[1]});
}
callback(result);
} else {
result = {id: value[0], text: value[1]};
}
}
callback(result);
}
};
if ($el.attr('data-allow-blank'))
opts['allowClear'] = true;
opts['multiple'] = multiple;
$el.select2(opts);
}
/**
* Process data-role attribute for the given input element. Feel free to override
*
......@@ -20,7 +84,7 @@
switch (name) {
case 'select2':
opts = {
var opts = {
width: 'resolve'
};
......@@ -36,6 +100,9 @@
$el.select2(opts);
return true;
case 'select2-ajax':
processAjaxWidget($el, name);
return true;
case 'datepicker':
$el.datepicker();
return true;
......
File mode changed from 100644 to 100755
/*
Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
Version: 3.4.2 Timestamp: Mon Aug 12 15:04:12 PDT 2013
*/
.select2-container {
margin: 0;
......@@ -14,7 +14,7 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
.select2-container,
.select2-drop,
.select2-search,
.select2-search input{
.select2-search input {
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
......@@ -22,9 +22,7 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
More Info : http://www.quirksmode.org/css/box.html
*/
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
box-sizing: border-box; /* css3 */
}
......@@ -41,13 +39,9 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
color: #444;
text-decoration: none;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border-radius: 4px;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
background-clip: padding-box;
-webkit-touch-callout: none;
-webkit-user-select: none;
......@@ -57,45 +51,41 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
user-select: none;
background-color: #fff;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
background-image: -ms-linear-gradient(top, #ffffff 0%, #eeeeee 50%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff));
background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%);
background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%);
background-image: -o-linear-gradient(bottom, #eee 0%, #fff 50%);
background-image: -ms-linear-gradient(top, #fff 0%, #eee 50%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
background-image: linear-gradient(top, #ffffff 0%, #eeeeee 50%);
background-image: linear-gradient(top, #fff 0%, #eee 50%);
}
.select2-container.select2-drop-above .select2-choice {
border-bottom-color: #aaa;
-webkit-border-radius:0 0 4px 4px;
-moz-border-radius:0 0 4px 4px;
border-radius:0 0 4px 4px;
border-radius: 0 0 4px 4px;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white));
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%);
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%);
background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%);
background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff));
background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%);
background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%);
background-image: -o-linear-gradient(bottom, #eee 0%, #fff 90%);
background-image: -ms-linear-gradient(top, #eee 0%, #fff 90%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);
background-image: linear-gradient(top, #eee 0%, #fff 90%);
}
.select2-container.select2-allowclear .select2-choice span {
.select2-container.select2-allowclear .select2-choice .select2-chosen {
margin-right: 42px;
}
.select2-container .select2-choice span {
.select2-container .select2-choice > .select2-chosen {
margin-right: 26px;
display: block;
overflow: hidden;
white-space: nowrap;
-ms-text-overflow: ellipsis;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
text-overflow: ellipsis;
}
.select2-container .select2-choice abbr {
......@@ -125,15 +115,27 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
}
.select2-drop-mask {
position: absolute;
border: 0;
margin: 0;
padding: 0;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 9998;
/* styles required for IE to work */
background-color: #fff;
opacity: 0;
filter: alpha(opacity=0);
}
.select2-drop {
width: 100%;
margin-top:-1px;
margin-top: -1px;
position: absolute;
z-index: 9999;
top: 100%;
......@@ -143,12 +145,9 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
border: 1px solid #aaa;
border-top: 0;
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
}
......@@ -166,16 +165,22 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
border-top: 1px solid #aaa;
border-bottom: 0;
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
-webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
}
.select2-container .select2-choice div {
.select2-drop-active {
border: 1px solid #5897fb;
border-top: none;
}
.select2-drop.select2-drop-above.select2-drop-active {
border-top: 1px solid #5897fb;
}
.select2-container .select2-choice .select2-arrow {
display: inline-block;
width: 18px;
height: 100%;
......@@ -184,25 +189,21 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
top: 0;
border-left: 1px solid #aaa;
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
background-clip: padding-box;
background: #ccc;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
background-image: -ms-linear-gradient(top, #ccc 0%, #eee 60%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
background-image: linear-gradient(top, #ccc 0%, #eee 60%);
}
.select2-container .select2-choice div b {
.select2-container .select2-choice .select2-arrow b {
display: block;
width: 100%;
height: 100%;
......@@ -235,21 +236,18 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
font-size: 1em;
border: 1px solid #aaa;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
border-radius: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
background: #fff url('select2.png') no-repeat 100% -22px;
background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, #fff 85%, #eee 99%);
background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #fff 85%, #eee 99%);
background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #fff 85%, #eee 99%);
}
.select2-drop.select2-drop-above .select2-search input {
......@@ -258,12 +256,12 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
.select2-search input.select2-active {
background: #fff url('select2-spinner.gif') no-repeat 100%;
background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, #fff 85%, #eee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #fff 85%, #eee 99%);
background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #fff 85%, #eee 99%);
}
.select2-container-active .select2-choice,
......@@ -271,33 +269,26 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
border: 1px solid #5897fb;
outline: none;
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow: 0 0 5px rgba(0,0,0,.3);
box-shadow: 0 0 5px rgba(0,0,0,.3);
-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
}
.select2-dropdown-open .select2-choice {
border-bottom-color: transparent;
-webkit-box-shadow: 0 1px 0 #fff inset;
-moz-box-shadow: 0 1px 0 #fff inset;
box-shadow: 0 1px 0 #fff inset;
-webkit-border-bottom-left-radius: 0;
-moz-border-radius-bottomleft: 0;
border-bottom-left-radius: 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomright: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-color: #eee;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee));
background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%);
background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%);
background-image: -o-linear-gradient(bottom, #fff 0%, #eee 50%);
background-image: -ms-linear-gradient(top, #fff 0%, #eee 50%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
background-image: linear-gradient(top, #fff 0%, #eee 50%);
}
.select2-dropdown-open.select2-drop-above .select2-choice,
......@@ -305,21 +296,21 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
border: 1px solid #5897fb;
border-top-color: transparent;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(0.5, #eeeeee));
background-image: -webkit-linear-gradient(center top, white 0%, #eeeeee 50%);
background-image: -moz-linear-gradient(center top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -ms-linear-gradient(bottom, #ffffff 0%,#eeeeee 50%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
background-image: linear-gradient(bottom, #ffffff 0%,#eeeeee 50%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee));
background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%);
background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%);
background-image: -o-linear-gradient(top, #fff 0%, #eee 50%);
background-image: -ms-linear-gradient(bottom, #fff 0%, #eee 50%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
background-image: linear-gradient(bottom, #fff 0%, #eee 50%);
}
.select2-dropdown-open .select2-choice div {
.select2-dropdown-open .select2-choice .select2-arrow {
background: transparent;
border-left: none;
filter: none;
}
.select2-dropdown-open .select2-choice div b {
.select2-dropdown-open .select2-choice .select2-arrow b {
background-position: -18px 1px;
}
......@@ -331,7 +322,7 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
position: relative;
overflow-x: hidden;
overflow-y: auto;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.select2-results ul.select2-result-sub {
......@@ -387,7 +378,7 @@ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
}
.select2-results .select2-highlighted ul {
background: white;
background: #fff;
color: #000;
}
......@@ -436,7 +427,7 @@ disabled look for disabled choices in the results dropdown
cursor: default;
}
.select2-container.select2-container-disabled .select2-choice div {
.select2-container.select2-container-disabled .select2-choice .select2-arrow {
background-color: #f4f4f4;
background-image: none;
border-left: 0;
......@@ -461,12 +452,12 @@ disabled look for disabled choices in the results dropdown
overflow: hidden;
background-color: #fff;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff));
background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%);
background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%);
background-image: -o-linear-gradient(top, #eee 1%, #fff 15%);
background-image: -ms-linear-gradient(top, #eee 1%, #fff 15%);
background-image: linear-gradient(top, #eee 1%, #fff 15%);
}
.select2-locked {
......@@ -481,9 +472,8 @@ disabled look for disabled choices in the results dropdown
border: 1px solid #5897fb;
outline: none;
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow: 0 0 5px rgba(0,0,0,.3);
box-shadow: 0 0 5px rgba(0,0,0,.3);
-webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
}
.select2-container-multi .select2-choices li {
float: left;
......@@ -505,7 +495,6 @@ disabled look for disabled choices in the results dropdown
outline: 0;
border: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
background: transparent !important;
}
......@@ -528,17 +517,12 @@ disabled look for disabled choices in the results dropdown
cursor: default;
border: 1px solid #aaaaaa;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-moz-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
background-clip: padding-box;
-webkit-touch-callout: none;
-webkit-user-select: none;
......@@ -548,15 +532,15 @@ disabled look for disabled choices in the results dropdown
user-select: none;
background-color: #e4e4e4;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0 );
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
}
.select2-container-multi .select2-choices .select2-search-choice span {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0);
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee));
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
}
.select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
cursor: default;
}
.select2-container-multi .select2-choices .select2-search-choice-focus {
......@@ -588,7 +572,7 @@ disabled look for disabled choices in the results dropdown
}
/* disabled styles */
.select2-container-multi.select2-container-disabled .select2-choices{
.select2-container-multi.select2-container-disabled .select2-choices {
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
......@@ -603,7 +587,7 @@ disabled look for disabled choices in the results dropdown
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
background:none;
background: none;
}
/* end multiselect */
......@@ -614,16 +598,17 @@ disabled look for disabled choices in the results dropdown
}
.select2-offscreen, .select2-offscreen:focus {
clip: rect(0 0 0 0);
width: 1px;
height: 1px;
border: 0;
margin: 0;
padding: 0;
overflow: hidden;
position: absolute;
outline: 0;
left: 0px;
clip: rect(0 0 0 0) !important;
width: 1px !important;
height: 1px !important;
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
position: absolute !important;
outline: 0 !important;
left: 0px !important;
top: 0px !important;
}
.select2-display-none {
......@@ -641,7 +626,7 @@ disabled look for disabled choices in the results dropdown
/* Retina-ize icons */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) {
.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b {
.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice .select2-arrow b {
background-image: url('select2x2.png') !important;
background-repeat: no-repeat !important;
background-size: 60px 40px !important;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment