Merge branch 'release-1.4.1'

This commit is contained in:
vitalets 2013-01-18 14:50:58 +04:00
commit 64b1dedd09
33 changed files with 4299 additions and 170 deletions

@ -2,6 +2,17 @@ X-editable changelog
=============================
Version 1.4.1 Jan 18, 2013
----------------------------
[enh #62] new option `selector` to work with delegated targets (vitalets)
[enh] new option `unsavedclass` to set css class when value was not sent to server (vitalets)
[enh] new option `emptyclass` to set css class when element is empty (vitalets)
[enh #59] select2 input (vitalets)
[enh #17] typeahead input (vitalets)
[enh] select: support of OPTGROUP via `children` key in source (vitalets)
[enh] checklist: set checked via prop instead of attr (vitalets)
Version 1.4.0 Jan 11, 2013
----------------------------
[enh] added new input type: combodate (vitalets)

@ -14,7 +14,9 @@ function getFiles() {
inputs: [
inputs+'date/date.js',
inputs+'date/datefield.js',
inputs+'date/bootstrap-datepicker/js/bootstrap-datepicker.js'],
inputs+'date/bootstrap-datepicker/js/bootstrap-datepicker.js',
inputs+'typeahead.js'
],
css: [inputs+'date/bootstrap-datepicker/css/datepicker.css']
},
jqueryui: {
@ -52,6 +54,7 @@ function getFiles() {
inputs+'select.js',
inputs+'checklist.js',
inputs+'html5types.js',
inputs+'select2/select2.js',
inputs+'combodate/lib/combodate.js',
inputs+'combodate/combodate.js'
];
@ -162,6 +165,7 @@ module.exports = function(grunt) {
'src/inputs/date/*.js',
'src/inputs/dateui/*.js',
'src/inputs/combodate/*.js',
'src/inputs/select2/*.js',
'src/inputs-ext/address/*.js',
'src/inputs-ext/wysihtml5/*.js'

@ -2,7 +2,7 @@
"name": "X-editable",
"title": "X-editable",
"description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery",
"version": "1.4.0",
"version": "1.4.1",
"homepage": "http://github.com/vitalets/x-editable",
"author": {
"name": "Vitaliy Potapov",

@ -23,8 +23,8 @@ Applied as jQuery method.
innerCss: null, //tbd in child class
init: function(element, options) {
this.$element = $(element);
//todo: what is in priority: data or js?
this.options = $.extend({}, $.fn.editableContainer.defaults, $.fn.editableutils.getConfigData(this.$element), options);
//since 1.4.1 container do not use data-* directly as they already merged into options.
this.options = $.extend({}, $.fn.editableContainer.defaults, options);
this.splitOptions();
//set scope of form callbacks to element
@ -116,6 +116,7 @@ Applied as jQuery method.
cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
rendered: $.proxy(function(){
/**
Fired when container is shown and form is rendered (for select will wait for loading dropdown options)
@ -242,7 +243,6 @@ Applied as jQuery method.
},
save: function(e, params) {
this.hide('save');
/**
Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
@ -263,6 +263,9 @@ Applied as jQuery method.
});
**/
this.$element.triggerHandler('save', params);
//hide must be after trigger, as saving value may require methods od plugin, applied to input
this.hide('save');
},
/**

@ -38,12 +38,14 @@
innerHide: function () {
this.$tip.hide(this.options.anim, $.proxy(function() {
this.$element.show();
this.tip().empty().remove();
this.innerDestroy();
}, this));
},
innerDestroy: function() {
this.tip().remove();
if(this.tip()) {
this.tip().empty().remove();
}
}
});

@ -132,21 +132,34 @@
/*
returns array items from sourceData having value property equal or inArray of 'value'
*/
itemsByValue: function(value, sourceData) {
itemsByValue: function(value, sourceData, valueProp) {
if(!sourceData || value === null) {
return [];
}
//convert to array
if(!$.isArray(value)) {
value = [value];
}
valueProp = valueProp || 'value';
/*jslint eqeq: true*/
var result = $.grep(sourceData, function(o){
return $.grep(value, function(v){ return v == o.value; }).length;
var isValArray = $.isArray(value),
result = [],
that = this;
$.each(sourceData, function(i, o) {
if(o.children) {
result = result.concat(that.itemsByValue(value, o.children));
} else {
/*jslint eqeq: true*/
if(isValArray) {
if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? o[valueProp] : o); }).length) {
result.push(o);
}
} else {
if(value == (o && typeof o === 'object' ? o[valueProp] : o)) {
result.push(o);
}
}
/*jslint eqeq: false*/
}
});
/*jslint eqeq: false*/
return result;
},

@ -110,18 +110,4 @@
.editable-clear-x:hover {
opacity: 1;
}
/*
.editable-clear-x1 {
background: url('../img/clear.png') center center no-repeat;
display: inline-block;
zoom: 1;
*display: inline;
width: 13px;
height: 13px;
vertical-align: middle;
position: relative;
margin-left: -20px;
}
*/
}

@ -11,7 +11,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
var EditableForm = function (div, options) {
this.options = $.extend({}, $.fn.editableform.defaults, options);
this.$div = $(div); //div, containing form. Not form tag! Not editable-element.
this.$div = $(div); //div, containing form. Not form tag. Not editable-element.
if(!this.options.scope) {
this.options.scope = this;
}
@ -25,6 +25,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
this.input = this.options.input;
//set initial value
//todo: may be add check: typeof str === 'string' ?
this.value = this.input.str2value(this.options.value);
},
initTemplate: function() {
@ -227,6 +228,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
}
//if success callback returns object like {newValue: <something>} --> use that value instead of submitted
//it is usefull if you want to chnage value in url-function
if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
newValue = res.newValue;
}
@ -382,18 +384,25 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
type: 'text',
/**
Url for submit, e.g. <code>'/post'</code>
If function - it will be called instead of ajax. Function can return deferred object to run fail/done callbacks.
If function - it will be called instead of ajax. Function should return deferred object to run fail/done callbacks.
@property url
@type string|function
@default null
@example
url: function(params) {
var d = new $.Deferred;
if(params.value === 'abc') {
var d = new $.Deferred;
return d.reject('error message'); //returning error via deferred object
} else {
someModel.set(params.name, params.value); //save data in some js model
//async saving data in js model
someModel.asyncSaveMethod({
...,
success: function(){
d.resolve();
}
});
return d.promise();
}
}
**/

@ -8,8 +8,13 @@ Makes editable any HTML element on the page. Applied as jQuery method.
var Editable = function (element, options) {
this.$element = $(element);
this.options = $.extend({}, $.fn.editable.defaults, $.fn.editableutils.getConfigData(this.$element), options);
this.init();
//data-* has more priority over js options: because dynamically created elements may change data-*
this.options = $.extend({}, $.fn.editable.defaults, options, $.fn.editableutils.getConfigData(this.$element));
if(this.options.selector) {
this.initLive();
} else {
this.init();
}
};
Editable.prototype = {
@ -52,8 +57,10 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.options.toggle !== 'manual') {
this.$element.addClass('editable-click');
this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
//prevent following link
e.preventDefault();
//stop propagation not required anymore because in document click handler it checks event target
//stop propagation not required because in document click handler it checks event target
//e.stopPropagation();
if(this.options.toggle === 'mouseenter') {
@ -95,6 +102,24 @@ Makes editable any HTML element on the page. Applied as jQuery method.
}, this));
},
/*
Initializes parent element for live editables
*/
initLive: function() {
//store selector
var selector = this.options.selector;
//modify options for child elements
this.options.selector = false;
this.options.autotext = 'never';
//listen toggle events
this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){
var $target = $(e.target);
if(!$target.data('editable')) {
$target.editable(this.options).trigger(e);
}
}, this));
},
/*
Renders value into element's text.
Can call custom display method from options.
@ -108,8 +133,8 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
//if it is input with source, we pass callback in third param to be called when source is loaded
if(this.input.options.hasOwnProperty('source')) {
//if input has `value2htmlFinal` method, we pass callback in third param to be called when source is loaded
if(this.input.value2htmlFinal) {
return this.input.value2html(this.value, this.$element[0], this.options.display, response);
//if display method defined --> use it
} else if(typeof this.options.display === 'function') {
@ -127,7 +152,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
enable: function() {
this.options.disabled = false;
this.$element.removeClass('editable-disabled');
this.handleEmpty();
this.handleEmpty(this.isEmpty);
if(this.options.toggle !== 'manual') {
if(this.$element.attr('tabindex') === '-1') {
this.$element.removeAttr('tabindex');
@ -143,7 +168,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
this.options.disabled = true;
this.hide();
this.$element.addClass('editable-disabled');
this.handleEmpty();
this.handleEmpty(this.isEmpty);
//do not stop focus on this element
this.$element.attr('tabindex', -1);
},
@ -204,27 +229,33 @@ Makes editable any HTML element on the page. Applied as jQuery method.
},
/*
* set emptytext if element is empty (reverse: remove emptytext if needed)
* set emptytext if element is empty
*/
handleEmpty: function () {
handleEmpty: function (isEmpty) {
//do not handle empty if we do not display anything
if(this.options.display === false) {
return;
}
var emptyClass = 'editable-empty';
this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === '';
//emptytext shown only for enabled
if(!this.options.disabled) {
if ($.trim(this.$element.text()) === '') {
this.$element.addClass(emptyClass).text(this.options.emptytext);
} else {
this.$element.removeClass(emptyClass);
if (this.isEmpty) {
this.$element.text(this.options.emptytext);
if(this.options.emptyclass) {
this.$element.addClass(this.options.emptyclass);
}
} else if(this.options.emptyclass) {
this.$element.removeClass(this.options.emptyclass);
}
} else {
//below required if element disable property was changed
if(this.$element.hasClass(emptyClass)) {
if(this.isEmpty) {
this.$element.empty();
this.$element.removeClass(emptyClass);
if(this.options.emptyclass) {
this.$element.removeClass(this.options.emptyclass);
}
}
}
},
@ -246,6 +277,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
input: this.input //pass input to form (as it is already created)
});
this.$element.editableContainer(containerOptions);
//listen `save` event
this.$element.on("save.internal", $.proxy(this.save, this));
this.container = this.$element.data('editableContainer');
} else if(this.container.tip().is(':visible')) {
@ -283,13 +315,29 @@ Makes editable any HTML element on the page. Applied as jQuery method.
* called when form was submitted
*/
save: function(e, params) {
//if url is not user's function and value was not sent to server and value changed --> mark element with unsaved css.
if(typeof this.options.url !== 'function' && this.options.display !== false && params.response === undefined && this.input.value2str(this.value) !== this.input.value2str(params.newValue)) {
this.$element.addClass('editable-unsaved');
} else {
this.$element.removeClass('editable-unsaved');
//mark element with unsaved class if needed
if(this.options.unsavedclass) {
/*
Add unsaved css to element if:
- url is not user's function
- value was not sent to server
- params.response === undefined, that means data was not sent
- value changed
*/
var sent = false;
sent = sent || typeof this.options.url === 'function';
sent = sent || this.options.display === false;
sent = sent || params.response !== undefined;
sent = sent || (this.options.savenochange && this.input.value2str(this.value) !== this.input.value2str(params.newValue));
if(sent) {
this.$element.removeClass(this.options.unsavedclass);
} else {
this.$element.addClass(this.options.unsavedclass);
}
}
//set new value
this.setValue(params.newValue, false, params.response);
/**
@ -381,7 +429,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
url: '/post',
pk: 1
});
**/
**/
$.fn.editable = function (option) {
//special API methods returning non-jquery object
var result = {}, args = arguments, datakey = 'editable';
@ -398,7 +446,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
username: "username is required",
fullname: "fullname should be minimum 3 letters length"
}
**/
**/
case 'validate':
this.each(function () {
var $this = $(this), data = $this.data(datakey), error;
@ -419,7 +467,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
username: "superuser",
fullname: "John"
}
**/
**/
case 'getValue':
this.each(function () {
var $this = $(this), data = $this.data(datakey);
@ -438,11 +486,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
@param {object} options
@param {object} options.url url to submit data
@param {object} options.data additional data to submit
@param {object} options.ajaxOptions additional ajax options
@param {object} options.ajaxOptions additional ajax options
@param {function} options.error(obj) error handler
@param {function} options.success(obj,config) success handler
@returns {Object} jQuery object
**/
**/
case 'submit': //collects value, validate and submit to server for creating new record
var config = arguments[1] || {},
$elems = this,
@ -593,7 +641,51 @@ Makes editable any HTML element on the page. Applied as jQuery method.
}
}
**/
display: null
display: null,
/**
Css class applied when editable text is empty.
@property emptyclass
@type string
@since 1.4.1
@default editable-empty
**/
emptyclass: 'editable-empty',
/**
Css class applied when value was stored but not sent to server (`pk` is empty or `send = 'never'`).
You may set it to `null` if you work with editables locally and submit them together.
@property unsavedclass
@type string
@since 1.4.1
@default editable-unsaved
**/
unsavedclass: 'editable-unsaved',
/**
If a css selector is provided, editable will be delegated to the specified targets.
Usefull for dynamically generated DOM elements.
**Please note**, that delegated targets can't use `emptytext` and `autotext` options,
as they are initialized after first click.
@property selector
@type string
@since 1.4.1
@default null
@example
<div id="user">
<a href="#" data-name="username" data-type="text" title="Username">awesome</a>
<a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" title="Group">Operator</a>
</div>
<script>
$('#user').editable({
selector: 'a',
url: '/post',
pk: 1
});
</script>
**/
selector: null
};
}(window.jQuery));

@ -186,15 +186,7 @@ To create your own input you can inherit from this class.
@type string
@default input-medium
**/
inputclass: 'input-medium',
/**
Name attribute of input
@property name
@type string
@default null
**/
name: null
inputclass: 'input-medium'
};
$.extend($.fn.editabletypes, {abstractinput: AbstractInput});

@ -42,8 +42,7 @@ $(function(){
for(var i=0; i<this.sourceData.length; i++) {
$label = $('<label>').append($('<input>', {
type: 'checkbox',
value: this.sourceData[i].value,
name: this.options.name
value: this.sourceData[i].value
}))
.append($('<span>').text(' '+this.sourceData[i].text));
@ -72,7 +71,7 @@ $(function(){
//set checked on required checkboxes
value2input: function(value) {
this.$input.removeAttr('checked');
this.$input.prop('checked', false);
if($.isArray(value) && value.length) {
this.$input.each(function(i, el) {
var $el = $(el);
@ -81,7 +80,7 @@ $(function(){
/*jslint eqeq: true*/
if($el.val() == val) {
/*jslint eqeq: false*/
$el.attr('checked', 'checked');
$el.prop('checked', true);
}
});
});

@ -144,7 +144,7 @@ List - abstract class for inputs that have source option loaded from js array or
}, this)
});
} else { //options as json/array/function
if (typeof this.options.source === 'function') {
if ($.isFunction(this.options.source)) {
this.sourceData = this.makeArray(this.options.source());
} else {
this.sourceData = this.makeArray(this.options.source);
@ -200,35 +200,45 @@ List - abstract class for inputs that have source option loaded from js array or
* convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
*/
makeArray: function(data) {
var count, obj, result = [], iterateEl;
var count, obj, result = [], item, iterateItem;
if(!data || typeof data === 'string') {
return null;
}
if($.isArray(data)) { //array
iterateEl = function (k, v) {
/*
function to iterate inside item of array if item is object.
Caclulates count of keys in item and store in obj.
*/
iterateItem = function (k, v) {
obj = {value: k, text: v};
if(count++ >= 2) {
return false;// exit each if object has more than one value
return false;// exit from `each` if item has more than one key.
}
};
for(var i = 0; i < data.length; i++) {
if(typeof data[i] === 'object') {
count = 0;
$.each(data[i], iterateEl);
if(count === 1) {
item = data[i];
if(typeof item === 'object') {
count = 0; //count of keys inside item
$.each(item, iterateItem);
//case: [{val1: 'text1'}, {val2: 'text2} ...]
if(count === 1) {
result.push(obj);
} else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) {
result.push(data[i]);
} else {
//data contains incorrect objects
//case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]
} else if(count > 1) {
//removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text')
if(item.children) {
item.children = this.makeArray(item.children);
}
result.push(item);
}
} else {
result.push({value: data[i], text: data[i]});
//case: ['text1', 'text2' ...]
result.push({value: item, text: item});
}
}
} else { //object
} else { //case: {val1: 'text1', val2: 'text2, ...}
$.each(data, function (k, v) {
result.push({value: k, text: v});
});
@ -251,12 +261,16 @@ List - abstract class for inputs that have source option loaded from js array or
List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
/**
Source data for list.
If **array** - it should be in format: `[{value: 1, text: "text1"}, {...}]`
If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`
For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order.
If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option.
If **function**, it should return data in format above (since 1.4.0).
Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).
`[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`
@property source
@type string | array | object | function
@ -280,8 +294,8 @@ List - abstract class for inputs that have source option loaded from js array or
**/
sourceError: 'Error when loading list',
/**
if <code>true</code> and source is **string url** - results will be cached for fields with the same source and name.
Usefull for editable grids.
if <code>true</code> and source is **string url** - results will be cached for fields with the same source.
Usefull for editable column in grid to prevent extra requests.
@property sourceCache
@type boolean

@ -31,14 +31,21 @@ $(function(){
$.extend(Select.prototype, {
renderList: function() {
this.$input.empty();
if(!$.isArray(this.sourceData)) {
return;
}
for(var i=0; i<this.sourceData.length; i++) {
this.$input.append($('<option>', {value: this.sourceData[i].value}).text(this.sourceData[i].text));
}
var fillItems = function($el, data) {
if($.isArray(data)) {
for(var i=0; i<data.length; i++) {
if(data[i].children) {
$el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children));
} else {
$el.append($('<option>', {value: data[i].value}).text(data[i].text));
}
}
}
return $el;
};
fillItems(this.$input, this.sourceData);
this.setClass();

@ -0,0 +1,12 @@
Copyright 2012 Igor Vaynberg
Version: @@ver@@ Timestamp: @@timestamp@@
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.

@ -0,0 +1,68 @@
Select2
=================
Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. Look and feel of Select2 is based on the excellent [Chosen](http://harvesthq.github.com/chosen/) library.
To get started -- checkout http://ivaynberg.github.com/select2!
What Does Select2 Support That Chosen Does Not?
-------------------------------------------------
* Working with large datasets: Chosen requires the entire dataset to be loaded as `option` tags in the DOM, which limits
it to working with small-ish datasets. Select2 uses a function to find results on-the-fly, which allows it to partially
load results.
* Paging of results: Since Select2 works with large datasets and only loads a small amount of matching results at a time
it has to support paging. Select2 will call the search function when the user scrolls to the bottom of currently loaded
result set allowing for the 'infinite scrolling' of results.
* Custom markup for results: Chosen only supports rendering text results because that is the only markup supported by
`option` tags. Select2 provides an extension point which can be used to produce any kind of markup to represent results.
* Ability to add results on the fly: Select2 provides the ability to add results from the search term entered by the user, which allows it to be used for
tagging.
Browser Compatibility
--------------------
* IE 8+ (7 mostly works except for [issue with z-index](https://github.com/ivaynberg/select2/issues/37))
* Chrome 8+
* Firefox 3.5+
* Safari 3+
* Opera 10.6+
Integrations
------------
* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / Apache Wicket)
* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
* [Django](https://github.com/applegrew/django-select2)
Bug tracker
-----------
Have a bug? Please create an issue here on GitHub!
https://github.com/ivaynberg/select2/issues
Mailing list
------------
Have a question? Ask on our mailing list!
select2@googlegroups.com
https://groups.google.com/d/forum/select2
Copyright and License
---------------------
Copyright 2012 Igor Vaynberg
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.

@ -0,0 +1,63 @@
#!/bin/bash
set -e
echo -n "Enter the version for this release: "
read ver
if [ ! $ver ]; then
echo "Invalid version."
exit
fi
name="select2"
js="$name.js"
mini="$name.min.js"
css="$name.css"
release="$name-$ver"
tag="release-$ver"
branch="build-$ver"
curbranch=`git branch | grep "*" | sed "s/* //"`
timestamp=$(date)
tokens="s/@@ver@@/$ver/g;s/\@@timestamp@@/$timestamp/g"
remote="github"
git branch "$branch"
git checkout "$branch"
echo "Tokenizing..."
find . -name "$js" | xargs -I{} sed -e "$tokens" -i "" {}
find . -name "$css" | xargs -I{} sed -e "$tokens" -i "" {}
git add "$js"
git add "$css"
echo "Minifying..."
echo "/*" > "$mini"
cat LICENSE | sed "$tokens" >> "$mini"
echo "*/" >> "$mini"
curl -s \
-d compilation_level=SIMPLE_OPTIMIZATIONS \
-d output_format=text \
-d output_info=compiled_code \
--data-urlencode "js_code@$js" \
http://closure-compiler.appspot.com/compile \
>> "$mini"
git add "$mini"
git commit -m "release $ver"
echo "Tagging..."
git tag -a "$tag" -m "tagged version $ver"
git push "$remote" --tags
echo "Cleaning Up..."
git checkout "$curbranch"
git branch -D "$branch"
echo "Done"

524
src/inputs/select2/lib/select2.css vendored Normal file

@ -0,0 +1,524 @@
/*
Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
*/
.select2-container {
position: relative;
display: inline-block;
/* inline-block for ie7 */
zoom: 1;
*display: inline;
vertical-align: top;
}
.select2-container,
.select2-drop,
.select2-search,
.select2-search input{
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
box-sizing: border-box; /* css3 */
}
.select2-container .select2-choice {
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, #eeeeee 0%, #ffffff 50%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0);
background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #aaa;
display: block;
overflow: hidden;
white-space: nowrap;
position: relative;
height: 26px;
line-height: 26px;
padding: 0 0 0 8px;
color: #444;
text-decoration: none;
}
.select2-container.select2-drop-above .select2-choice
{
border-bottom-color: #aaa;
-webkit-border-radius:0px 0px 4px 4px;
-moz-border-radius:0px 0px 4px 4px;
border-radius:0px 0px 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='#eeeeee', endColorstr='#ffffff',GradientType=0 );
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
}
.select2-container .select2-choice span {
margin-right: 26px;
display: block;
overflow: hidden;
white-space: nowrap;
-o-text-overflow: ellipsis;
-ms-text-overflow: ellipsis;
text-overflow: ellipsis;
}
.select2-container .select2-choice abbr {
display: block;
position: absolute;
right: 26px;
top: 8px;
width: 12px;
height: 12px;
font-size: 1px;
background: url('select2.png') right top no-repeat;
cursor: pointer;
text-decoration: none;
border:0;
outline: 0;
}
.select2-container .select2-choice abbr:hover {
background-position: right -11px;
cursor: pointer;
}
.select2-drop {
background: #fff;
color: #000;
border: 1px solid #aaa;
border-top: 0;
position: absolute;
top: 100%;
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
z-index: 9999;
width:100%;
margin-top:-1px;
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
}
.select2-drop.select2-drop-above {
-webkit-border-radius: 4px 4px 0px 0px;
-moz-border-radius: 4px 4px 0px 0px;
border-radius: 4px 4px 0px 0px;
margin-top:1px;
border-top: 1px solid #aaa;
border-bottom: 0;
-webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-o-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 {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
-moz-background-clip: padding;
-webkit-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%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0);
background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
border-left: 1px solid #aaa;
position: absolute;
right: 0;
top: 0;
display: block;
height: 100%;
width: 18px;
}
.select2-container .select2-choice div b {
background: url('select2.png') no-repeat 0 1px;
display: block;
width: 100%;
height: 100%;
}
.select2-search {
display: inline-block;
white-space: nowrap;
z-index: 10000;
min-height: 26px;
width: 100%;
margin: 0;
padding-left: 4px;
padding-right: 4px;
}
.select2-search-hidden {
display: block;
position: absolute;
left: -10000px;
}
.select2-search input {
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%);
padding: 4px 20px 4px 5px;
outline: 0;
border: 1px solid #aaa;
font-family: sans-serif;
font-size: 1em;
width:100%;
margin:0;
height:auto !important;
min-height: 26px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
border-radius: 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
}
.select2-drop.select2-drop-above .select2-search input
{
margin-top:4px;
}
.select2-search input.select2-active {
background: #fff url('spinner.gif') no-repeat 100%;
background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
}
.select2-container-active .select2-choice,
.select2-container-active .select2-choices {
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
box-shadow : 0 0 5px rgba(0,0,0,.3);
border: 1px solid #5897fb;
outline: none;
}
.select2-dropdown-open .select2-choice {
border: 1px solid #aaa;
border-bottom-color: transparent;
-webkit-box-shadow: 0 1px 0 #fff inset;
-moz-box-shadow : 0 1px 0 #fff inset;
-o-box-shadow : 0 1px 0 #fff inset;
box-shadow : 0 1px 0 #fff inset;
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='#ffffff', endColorstr='#eeeeee',GradientType=0 );
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
-webkit-border-bottom-left-radius : 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomleft : 0;
-moz-border-radius-bottomright: 0;
border-bottom-left-radius : 0;
border-bottom-right-radius: 0;
}
.select2-dropdown-open .select2-choice div {
background: transparent;
border-left: none;
}
.select2-dropdown-open .select2-choice div b {
background-position: -18px 1px;
}
/* results */
.select2-results {
margin: 4px 4px 4px 0;
padding: 0 0 0 4px;
position: relative;
overflow-x: hidden;
overflow-y: auto;
max-height: 200px;
}
.select2-results ul.select2-result-sub {
margin: 0 0 0 0;
}
.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
.select2-results li {
list-style: none;
display: list-item;
}
.select2-results li.select2-result-with-children > .select2-result-label {
font-weight: bold;
}
.select2-results .select2-result-label {
padding: 3px 7px 4px;
margin: 0;
cursor: pointer;
}
.select2-results .select2-highlighted {
background: #3875d7;
color: #fff;
}
.select2-results li em {
background: #feffde;
font-style: normal;
}
.select2-results .select2-highlighted em {
background: transparent;
}
.select2-results .select2-no-results,
.select2-results .select2-searching,
.select2-results .select2-selection-limit {
background: #f4f4f4;
display: list-item;
}
/*
disabled look for already selected choices in the results dropdown
.select2-results .select2-disabled.select2-highlighted {
color: #666;
background: #f4f4f4;
display: list-item;
cursor: default;
}
.select2-results .select2-disabled {
background: #f4f4f4;
display: list-item;
cursor: default;
}
*/
.select2-results .select2-disabled {
display: none;
}
.select2-more-results.select2-active {
background: #f4f4f4 url('spinner.gif') no-repeat 100%;
}
.select2-more-results {
background: #f4f4f4;
display: list-item;
}
/* disabled styles */
.select2-container.select2-container-disabled .select2-choice {
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.select2-container.select2-container-disabled .select2-choice div {
background-color: #f4f4f4;
background-image: none;
border-left: 0;
}
/* multiselect */
.select2-container-multi .select2-choices {
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%);
border: 1px solid #aaa;
margin: 0;
padding: 0;
cursor: text;
overflow: hidden;
height: auto !important;
height: 1%;
position: relative;
}
.select2-container-multi .select2-choices {
min-height: 26px;
}
.select2-container-multi.select2-container-active .select2-choices {
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
box-shadow : 0 0 5px rgba(0,0,0,.3);
border: 1px solid #5897fb;
outline: none;
}
.select2-container-multi .select2-choices li {
float: left;
list-style: none;
}
.select2-container-multi .select2-choices .select2-search-field {
white-space: nowrap;
margin: 0;
padding: 0;
}
.select2-container-multi .select2-choices .select2-search-field input {
color: #666;
background: transparent !important;
font-family: sans-serif;
font-size: 100%;
height: 15px;
padding: 5px;
margin: 1px 0;
outline: 0;
border: 0;
-webkit-box-shadow: none;
-moz-box-shadow : none;
-o-box-shadow : none;
box-shadow : none;
}
.select2-container-multi .select2-choices .select2-search-field input.select2-active {
background: #fff url('spinner.gif') no-repeat 100% !important;
}
.select2-default {
color: #999 !important;
}
.select2-container-multi .select2-choices .select2-search-choice {
-webkit-border-radius: 3px;
-moz-border-radius : 3px;
border-radius : 3px;
-moz-background-clip : padding;
-webkit-background-clip: padding-box;
background-clip : padding-box;
background-color: #e4e4e4;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', 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%);
-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);
color: #333;
border: 1px solid #aaaaaa;
line-height: 13px;
padding: 3px 5px 3px 18px;
margin: 3px 0 3px 5px;
position: relative;
cursor: default;
}
.select2-container-multi .select2-choices .select2-search-choice span {
cursor: default;
}
.select2-container-multi .select2-choices .select2-search-choice-focus {
background: #d4d4d4;
}
.select2-search-choice-close {
display: block;
position: absolute;
right: 3px;
top: 4px;
width: 12px;
height: 13px;
font-size: 1px;
background: url('select2.png') right top no-repeat;
outline: none;
}
.select2-container-multi .select2-search-choice-close {
left: 3px;
}
.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
background-position: right -11px;
}
.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
background-position: right -11px;
}
/* disabled styles */
.select2-container-multi.select2-container-disabled .select2-choices{
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
background-image: none;
background-color: #f4f4f4;
border: 1px solid #ddd;
padding: 3px 5px 3px 5px;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {
display: none;
}
/* end multiselect */
.select2-result-selectable .select2-match,
.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; }
.select2-result-unselectable .select2-match { text-decoration: none; }
.select2-offscreen { position: absolute; left: -10000px; }
/* Retina-ize icons */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b {
background-image: url(select2x2.png) !important;
background-repeat: no-repeat !important;
background-size: 60px 40px !important;
}
.select2-search input {
background-position: 100% -21px !important;
}
}

2407
src/inputs/select2/lib/select2.js vendored Normal file

File diff suppressed because it is too large Load Diff

82
src/inputs/select2/lib/select2.min.js vendored Normal file

@ -0,0 +1,82 @@
/*
Copyright 2012 Igor Vaynberg
Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
*/
(function(e){"undefined"==typeof e.fn.each2&&e.fn.extend({each2:function(g){for(var i=e([0]),m=-1,s=this.length;++m<s&&(i.context=i[0]=this[m])&&!1!==g.call(i[0],m,i););return this}})})(jQuery);
(function(e,g){function i(a,b){var c=0,d=b.length,j;if("undefined"===typeof a)return-1;if(a.constructor===String)for(;c<d;c+=1){if(0===a.localeCompare(b[c]))return c}else for(;c<d;c+=1)if(j=b[c],j.constructor===String){if(0===j.localeCompare(a))return c}else if(j===a)return c;return-1}function m(a,b){return a===b?!0:a===g||b===g||null===a||null===b?!1:a.constructor===String?0===a.localeCompare(b):b.constructor===String?0===b.localeCompare(a):!1}function s(a,b){var c,d,j;if(null===a||1>a.length)return[];
c=a.split(b);d=0;for(j=c.length;d<j;d+=1)c[d]=e.trim(c[d]);return c}function A(a,b,c){var c=c||g,d;return function(){var j=arguments;window.clearTimeout(d);d=window.setTimeout(function(){b.apply(c,j)},a)}}function l(a){a.preventDefault();a.stopPropagation()}function B(a,b,c){var d=a.toUpperCase().indexOf(b.toUpperCase()),b=b.length;0>d?c.push(a):(c.push(a.substring(0,d)),c.push("<span class='select2-match'>"),c.push(a.substring(d,d+b)),c.push("</span>"),c.push(a.substring(d+b,a.length)))}function C(a){var b,
c=0,d=null,j=a.quietMillis||100;return function(h){window.clearTimeout(b);b=window.setTimeout(function(){var b=c+=1,j=a.data,n=a.transport||e.ajax,f=a.traditional||!1,g=a.type||"GET",j=j.call(this,h.term,h.page,h.context);null!==d&&d.abort();d=n.call(null,{url:a.url,dataType:a.dataType,data:j,type:g,traditional:f,success:function(d){b<c||(d=a.results(d,h.page),h.callback(d))}})},j)}}function D(a){var b=a,c,d=function(a){return""+a.text};e.isArray(b)||(d=b.text,e.isFunction(d)||(c=b.text,d=function(a){return a[c]}),
b=b.results);return function(a){var c=a.term,f={results:[]},k;if(c==="")a.callback({results:b});else{k=function(b,f){var g,t,b=b[0];if(b.children){g={};for(t in b)b.hasOwnProperty(t)&&(g[t]=b[t]);g.children=[];e(b.children).each2(function(a,b){k(b,g.children)});g.children.length&&f.push(g)}else a.matcher(c,d(b))&&f.push(b)};e(b).each2(function(a,b){k(b,f.results)});a.callback(f)}}}function E(a){return e.isFunction(a)?a:function(b){var c=b.term,d={results:[]};e(a).each(function(){var a=this.text!==
g,e=a?this.text:this;if(""===c||b.matcher(c,e))d.results.push(a?this:{id:this,text:this})});b.callback(d)}}function u(a){if(e.isFunction(a))return!0;if(!a)return!1;throw Error("formatterName must be a function or a falsy value");}function v(a){return e.isFunction(a)?a():a}function F(a){var b=0;e.each(a,function(a,d){d.children?b+=F(d.children):b++});return b}function H(a,b,c,d){var e=a,h=!1,f,k,n,o;if(!d.createSearchChoice||!d.tokenSeparators||1>d.tokenSeparators.length)return g;for(;;){h=-1;k=0;
for(n=d.tokenSeparators.length;k<n&&!(o=d.tokenSeparators[k],h=a.indexOf(o),0<=h);k++);if(0>h)break;f=a.substring(0,h);a=a.substring(h+o.length);if(0<f.length&&(f=d.createSearchChoice(f,b),f!==g&&null!==f&&d.id(f)!==g&&null!==d.id(f))){h=!1;k=0;for(n=b.length;k<n;k++)if(m(d.id(f),d.id(b[k]))){h=!0;break}h||c(f)}}if(0!=e.localeCompare(a))return a}function x(a,b){var c=function(){};c.prototype=new a;c.prototype.constructor=c;c.prototype.parent=a.prototype;c.prototype=e.extend(c.prototype,b);return c}
if(window.Select2===g){var f,w,y,z,G,q;f={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){a=a.which?a.which:a;switch(a){case f.LEFT:case f.RIGHT:case f.UP:case f.DOWN:return!0}return!1},isControl:function(a){switch(a.which){case f.SHIFT:case f.CTRL:case f.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){a=a.which?a.which:a;return 112<=a&&123>=a}};var I=1;G=function(){return I++};
e(document).delegate("body","mousemove",function(a){e.data(document,"select2-lastpos",{x:a.pageX,y:a.pageY})});e(document).ready(function(){e(document).delegate("body","mousedown touchend",function(a){var b=e(a.target).closest("div.select2-container").get(0),c;b?e(document).find("div.select2-container-active").each(function(){this!==b&&e(this).data("select2").blur()}):(b=e(a.target).closest("div.select2-drop").get(0),e(document).find("div.select2-drop-active").each(function(){this!==b&&e(this).data("select2").blur()}));
b=e(a.target);c=b.attr("for");"LABEL"===a.target.tagName&&(c&&0<c.length)&&(b=e("#"+c),b=b.data("select2"),b!==g&&(b.focus(),a.preventDefault()))})});w=x(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(a){var b,c;this.opts=a=this.prepareOpts(a);this.id=a.id;a.element.data("select2")!==g&&null!==a.element.data("select2")&&this.destroy();this.enabled=!0;this.container=this.createContainer();this.containerId="s2id_"+(a.element.attr("id")||"autogen"+G());this.containerSelector=
"#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1");this.container.attr("id",this.containerId);var d=!1,j;this.body=function(){!1===d&&(j=a.element.closest("body"),d=!0);return j};a.element.attr("class")!==g&&this.container.addClass(a.element.attr("class").replace(/validate\[[\S ]+] ?/,""));this.container.css(v(a.containerCss));this.container.addClass(v(a.containerCssClass));this.opts.element.data("select2",this).hide().before(this.container);this.container.data("select2",
this);this.dropdown=this.container.find(".select2-drop");this.dropdown.addClass(v(a.dropdownCssClass));this.dropdown.data("select2",this);this.results=b=this.container.find(".select2-results");this.search=c=this.container.find("input.select2-input");c.attr("tabIndex",this.opts.element.attr("tabIndex"));this.resultsPage=0;this.context=null;this.initContainer();this.initContainerWidth();this.results.bind("mousemove",function(a){var b=e.data(document,"select2-lastpos");(b===g||b.x!==a.pageX||b.y!==a.pageY)&&
e(a.target).trigger("mousemove-filtered",a)});this.dropdown.delegate(".select2-results","mousemove-filtered",this.bind(this.highlightUnderEvent));var h=this.results,f=A(80,function(a){h.trigger("scroll-debounced",a)});h.bind("scroll",function(a){0<=i(a.target,h.get())&&f(a)});this.dropdown.delegate(".select2-results","scroll-debounced",this.bind(this.loadMoreIfNeeded));e.fn.mousewheel&&b.mousewheel(function(a,c,d,e){c=b.scrollTop();0<e&&0>=c-e?(b.scrollTop(0),l(a)):0>e&&b.get(0).scrollHeight-b.scrollTop()+
e<=b.height()&&(b.scrollTop(b.get(0).scrollHeight-b.height()),l(a))});c.bind("keydown",function(){e.data(c,"keyup-change-value")===g&&e.data(c,"keyup-change-value",c.val())});c.bind("keyup",function(){var a=e.data(c,"keyup-change-value");a!==g&&c.val()!==a&&(e.removeData(c,"keyup-change-value"),c.trigger("keyup-change"))});c.bind("keyup-change",this.bind(this.updateResults));c.bind("focus",function(){c.addClass("select2-focused");" "===c.val()&&c.val("")});c.bind("blur",function(){c.removeClass("select2-focused")});
this.dropdown.delegate(".select2-results","mouseup",this.bind(function(a){0<e(a.target).closest(".select2-result-selectable:not(.select2-disabled)").length?(this.highlightUnderEvent(a),this.selectHighlighted(a)):this.focusSearch();l(a)}));this.dropdown.bind("click mouseup mousedown",function(a){a.stopPropagation()});e.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource());(a.element.is(":disabled")||a.element.is("[readonly='readonly']"))&&this.disable()},destroy:function(){var a=
this.opts.element.data("select2");a!==g&&(a.container.remove(),a.dropdown.remove(),a.opts.element.removeData("select2").unbind(".select2").show())},prepareOpts:function(a){var b,c,d;b=a.element;"select"===b.get(0).tagName.toLowerCase()&&(this.select=c=a.element);c&&e.each("id multiple ajax query createSearchChoice initSelection data tags".split(" "),function(){if(this in a)throw Error("Option '"+this+"' is not allowed for Select2 when attached to a <select> element.");});a=e.extend({},{populateResults:function(b,
c,d){var f,n=this.opts.id,o=this;f=function(b,c,j){var h,l,i,m,r,p,q;h=0;for(l=b.length;h<l;h=h+1){i=b[h];m=n(i)!==g;r=i.children&&i.children.length>0;p=e("<li></li>");p.addClass("select2-results-dept-"+j);p.addClass("select2-result");p.addClass(m?"select2-result-selectable":"select2-result-unselectable");r&&p.addClass("select2-result-with-children");p.addClass(o.opts.formatResultCssClass(i));m=e("<div></div>");m.addClass("select2-result-label");q=a.formatResult(i,m,d);q!==g&&m.html(o.opts.escapeMarkup(q));
p.append(m);if(r){r=e("<ul></ul>");r.addClass("select2-result-sub");f(i.children,r,j+1);p.append(r)}p.data("select2-data",i);c.append(p)}};f(c,b,0)}},e.fn.select2.defaults,a);"function"!==typeof a.id&&(d=a.id,a.id=function(a){return a[d]});if(c)a.query=this.bind(function(a){var c={results:[],more:false},d=a.term,f,n,o;o=function(b,c){var e;if(b.is("option"))a.matcher(d,b.text(),b)&&c.push({id:b.attr("value"),text:b.text(),element:b.get(),css:b.attr("class")});else if(b.is("optgroup")){e={text:b.attr("label"),
children:[],element:b.get(),css:b.attr("class")};b.children().each2(function(a,b){o(b,e.children)});e.children.length>0&&c.push(e)}};f=b.children();if(this.getPlaceholder()!==g&&f.length>0){n=f[0];e(n).text()===""&&(f=f.not(n))}f.each2(function(a,b){o(b,c.results)});a.callback(c)}),a.id=function(a){return a.id},a.formatResultCssClass=function(a){return a.css};else if(!("query"in a))if("ajax"in a){if((c=a.element.data("ajax-url"))&&0<c.length)a.ajax.url=c;a.query=C(a.ajax)}else"data"in a?a.query=D(a.data):
"tags"in a&&(a.query=E(a.tags),a.createSearchChoice=function(a){return{id:a,text:a}},a.initSelection=function(b,c){var d=[];e(s(b.val(),a.separator)).each(function(){var b=this,c=this,j=a.tags;e.isFunction(j)&&(j=j());e(j).each(function(){if(m(this.id,b)){c=this.text;return false}});d.push({id:b,text:c})});c(d)});if("function"!==typeof a.query)throw"query function not defined for Select2 "+a.element.attr("id");return a},monitorSource:function(){this.opts.element.bind("change.select2",this.bind(function(){!0!==
this.opts.element.data("select2-change-triggered")&&this.initSelection()}))},triggerChange:function(a){a=a||{};a=e.extend({},a,{type:"change",val:this.val()});this.opts.element.data("select2-change-triggered",!0);this.opts.element.trigger(a);this.opts.element.data("select2-change-triggered",!1);this.opts.element.click();this.opts.blurOnChange&&this.opts.element.blur()},enable:function(){this.enabled||(this.enabled=!0,this.container.removeClass("select2-container-disabled"))},disable:function(){this.enabled&&
(this.close(),this.enabled=!1,this.container.addClass("select2-container-disabled"))},opened:function(){return this.container.hasClass("select2-dropdown-open")},positionDropdown:function(){var a=this.container.offset(),b=this.container.outerHeight(),c=this.container.outerWidth(),d=this.dropdown.outerHeight(),j=e(window).scrollTop()+document.documentElement.clientHeight,b=a.top+b,f=a.left,j=b+d<=j,g=a.top-d>=this.body().scrollTop(),k=this.dropdown.hasClass("select2-drop-above"),n;"static"!==this.body().css("position")&&
(n=this.body().offset(),b-=n.top,f-=n.left);k?(k=!0,!g&&j&&(k=!1)):(k=!1,!j&&g&&(k=!0));k?(b=a.top-d,this.container.addClass("select2-drop-above"),this.dropdown.addClass("select2-drop-above")):(this.container.removeClass("select2-drop-above"),this.dropdown.removeClass("select2-drop-above"));a=e.extend({top:b,left:f,width:c},v(this.opts.dropdownCss));this.dropdown.css(a)},shouldOpen:function(){var a;if(this.opened())return!1;a=e.Event("open");this.opts.element.trigger(a);return!a.isDefaultPrevented()},
clearDropdownAlignmentPreference:function(){this.container.removeClass("select2-drop-above");this.dropdown.removeClass("select2-drop-above")},open:function(){if(!this.shouldOpen())return!1;window.setTimeout(this.bind(this.opening),1);return!0},opening:function(){var a=this.containerId,b=this.containerSelector,c="scroll."+a,d="resize."+a;this.container.parents().each(function(){e(this).bind(c,function(){var a=e(b);0==a.length&&e(this).unbind(c);a.select2("close")})});e(window).bind(d,function(){var a=
e(b);0==a.length&&e(window).unbind(d);a.select2("close")});this.clearDropdownAlignmentPreference();" "===this.search.val()&&this.search.val("");this.container.addClass("select2-dropdown-open").addClass("select2-container-active");this.updateResults(!0);this.dropdown[0]!==this.body().children().last()[0]&&this.dropdown.detach().appendTo(this.body());this.dropdown.show();this.positionDropdown();this.dropdown.addClass("select2-drop-active");this.ensureHighlightVisible();this.focusSearch()},close:function(){if(this.opened()){var a=
this;this.container.parents().each(function(){e(this).unbind("scroll."+a.containerId)});e(window).unbind("resize."+this.containerId);this.clearDropdownAlignmentPreference();this.dropdown.hide();this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");this.results.empty();this.clearSearch();this.opts.element.trigger(e.Event("close"))}},clearSearch:function(){},ensureHighlightVisible:function(){var a=this.results,b,c,d,f;c=this.highlight();0>c||(0==c?a.scrollTop(0):
(b=a.find(".select2-result-selectable"),d=e(b[c]),f=d.offset().top+d.outerHeight(),c===b.length-1&&(b=a.find("li.select2-more-results"),0<b.length&&(f=b.offset().top+b.outerHeight())),b=a.offset().top+a.outerHeight(),f>b&&a.scrollTop(a.scrollTop()+(f-b)),d=d.offset().top-a.offset().top,0>d&&a.scrollTop(a.scrollTop()+d)))},moveHighlight:function(a){for(var b=this.results.find(".select2-result-selectable"),c=this.highlight();-1<c&&c<b.length;){var c=c+a,d=e(b[c]);if(d.hasClass("select2-result-selectable")&&
!d.hasClass("select2-disabled")){this.highlight(c);break}}},highlight:function(a){var b=this.results.find(".select2-result-selectable").not(".select2-disabled");if(0===arguments.length)return i(b.filter(".select2-highlighted")[0],b.get());a>=b.length&&(a=b.length-1);0>a&&(a=0);b.removeClass("select2-highlighted");e(b[a]).addClass("select2-highlighted");this.ensureHighlightVisible()},countSelectableResults:function(){return this.results.find(".select2-result-selectable").not(".select2-disabled").length},
highlightUnderEvent:function(a){a=e(a.target).closest(".select2-result-selectable");if(0<a.length&&!a.is(".select2-highlighted")){var b=this.results.find(".select2-result-selectable");this.highlight(b.index(a))}else 0==a.length&&this.results.find(".select2-highlighted").removeClass("select2-highlighted")},loadMoreIfNeeded:function(){var a=this.results,b=a.find("li.select2-more-results"),c,d=this.resultsPage+1,e=this,f=this.search.val(),g=this.context;0!==b.length&&(c=b.offset().top-a.offset().top-
a.height(),0>=c&&(b.addClass("select2-active"),this.opts.query({term:f,page:d,context:g,matcher:this.opts.matcher,callback:this.bind(function(c){e.opened()&&(e.opts.populateResults.call(this,a,c.results,{term:f,page:d,context:g}),!0===c.more?(b.detach().appendTo(a).text(e.opts.formatLoadMore(d+1)),window.setTimeout(function(){e.loadMoreIfNeeded()},10)):b.remove(),e.positionDropdown(),e.resultsPage=d)})})))},tokenize:function(){},updateResults:function(a){function b(){f.scrollTop(0);d.removeClass("select2-active");
k.positionDropdown()}function c(a){f.html(k.opts.escapeMarkup(a));b()}var d=this.search,f=this.results,h=this.opts,i,k=this;if(!(!0!==a&&(!1===this.showSearchInput||!this.opened()))){d.addClass("select2-active");if(1<=h.maximumSelectionSize&&(i=this.data(),e.isArray(i)&&i.length>=h.maximumSelectionSize&&u(h.formatSelectionTooBig,"formatSelectionTooBig"))){c("<li class='select2-selection-limit'>"+h.formatSelectionTooBig(h.maximumSelectionSize)+"</li>");return}d.val().length<h.minimumInputLength&&u(h.formatInputTooShort,
"formatInputTooShort")?c("<li class='select2-no-results'>"+h.formatInputTooShort(d.val(),h.minimumInputLength)+"</li>"):(c("<li class='select2-searching'>"+h.formatSearching()+"</li>"),i=this.tokenize(),i!=g&&null!=i&&d.val(i),this.resultsPage=1,h.query({term:d.val(),page:this.resultsPage,context:null,matcher:h.matcher,callback:this.bind(function(i){var l;this.opened()&&((this.context=i.context===g?null:i.context,this.opts.createSearchChoice&&""!==d.val()&&(l=this.opts.createSearchChoice.call(null,
d.val(),i.results),l!==g&&null!==l&&k.id(l)!==g&&null!==k.id(l)&&0===e(i.results).filter(function(){return m(k.id(this),k.id(l))}).length&&i.results.unshift(l)),0===i.results.length&&u(h.formatNoMatches,"formatNoMatches"))?c("<li class='select2-no-results'>"+h.formatNoMatches(d.val())+"</li>"):(f.empty(),k.opts.populateResults.call(this,f,i.results,{term:d.val(),page:this.resultsPage,context:null}),!0===i.more&&u(h.formatLoadMore,"formatLoadMore")&&(f.append("<li class='select2-more-results'>"+k.opts.escapeMarkup(h.formatLoadMore(this.resultsPage))+
"</li>"),window.setTimeout(function(){k.loadMoreIfNeeded()},10)),this.postprocessResults(i,a),b()))})}))}},cancel:function(){this.close()},blur:function(){this.close();this.container.removeClass("select2-container-active");this.dropdown.removeClass("select2-drop-active");this.search[0]===document.activeElement&&this.search.blur();this.clearSearch();this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus")},focusSearch:function(){this.search.show();this.search.focus();
window.setTimeout(this.bind(function(){this.search.show();this.search.focus();this.search.val(this.search.val())}),10)},selectHighlighted:function(){var a=this.highlight(),b=this.results.find(".select2-highlighted").not(".select2-disabled"),c=b.closest(".select2-result-selectable").data("select2-data");c&&(b.addClass("select2-disabled"),this.highlight(a),this.onSelect(c))},getPlaceholder:function(){return this.opts.element.attr("placeholder")||this.opts.element.attr("data-placeholder")||this.opts.element.data("placeholder")||
this.opts.placeholder},initContainerWidth:function(){var a=function(){var a,c,d,f;if("off"===this.opts.width)return null;if("element"===this.opts.width)return 0===this.opts.element.outerWidth()?"auto":this.opts.element.outerWidth()+"px";if("copy"===this.opts.width||"resolve"===this.opts.width){a=this.opts.element.attr("style");if(a!==g){a=a.split(";");d=0;for(f=a.length;d<f;d+=1)if(c=a[d].replace(/\s/g,"").match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/),null!==c&&1<=c.length)return c[1]}return"resolve"===
this.opts.width?(a=this.opts.element.css("width"),0<a.indexOf("%")?a:0===this.opts.element.outerWidth()?"auto":this.opts.element.outerWidth()+"px"):null}return e.isFunction(this.opts.width)?this.opts.width():this.opts.width}.call(this);null!==a&&this.container.attr("style","width: "+a)}});y=x(w,{createContainer:function(){return e("<div></div>",{"class":"select2-container"}).html(" <a href='#' onclick='return false;' class='select2-choice'> <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr> <div><b></b></div></a> <div class='select2-drop select2-offscreen'> <div class='select2-search'> <input type='text' autocomplete='off' class='select2-input'/> </div> <ul class='select2-results'> </ul></div>")},
opening:function(){this.search.show();this.parent.opening.apply(this,arguments);this.dropdown.removeClass("select2-offscreen")},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show())},focus:function(){this.close();this.selection.focus()},isFocused:function(){return this.selection[0]===document.activeElement},cancel:function(){this.parent.cancel.apply(this,arguments);this.selection.focus()},
initContainer:function(){var a,b=this.dropdown;this.selection=a=this.container.find(".select2-choice");this.search.bind("keydown",this.bind(function(a){if(this.enabled)if(a.which===f.PAGE_UP||a.which===f.PAGE_DOWN)l(a);else if(this.opened())switch(a.which){case f.UP:case f.DOWN:this.moveHighlight(a.which===f.UP?-1:1);l(a);break;case f.TAB:case f.ENTER:this.selectHighlighted();l(a);break;case f.ESC:this.cancel(a),l(a)}else a.which===f.TAB||f.isControl(a)||f.isFunctionKey(a)||a.which===f.ESC||!1===
this.opts.openOnEnter&&a.which===f.ENTER||this.open()}));this.search.bind("focus",this.bind(function(){this.selection.attr("tabIndex","-1")}));this.search.bind("blur",this.bind(function(){this.opened()||this.container.removeClass("select2-container-active");window.setTimeout(this.bind(function(){this.selection.attr("tabIndex",this.opts.element.attr("tabIndex"))}),10)}));a.bind("mousedown",this.bind(function(){this.opened()?(this.close(),this.selection.focus()):this.enabled&&this.open()}));b.bind("mousedown",
this.bind(function(){this.search.focus()}));a.bind("focus",this.bind(function(){this.container.addClass("select2-container-active");this.search.attr("tabIndex","-1")}));a.bind("blur",this.bind(function(){this.opened()||this.container.removeClass("select2-container-active");window.setTimeout(this.bind(function(){this.search.attr("tabIndex",this.opts.element.attr("tabIndex"))}),10)}));a.bind("keydown",this.bind(function(a){if(this.enabled)if(a.which===f.PAGE_UP||a.which===f.PAGE_DOWN)l(a);else if(!(a.which===
f.TAB||f.isControl(a)||f.isFunctionKey(a)||a.which===f.ESC)&&!(!1===this.opts.openOnEnter&&a.which===f.ENTER))if(a.which==f.DELETE)this.opts.allowClear&&this.clear();else{this.open();if(a.which!==f.ENTER&&!(48>a.which)){var b=String.fromCharCode(a.which).toLowerCase();a.shiftKey&&(b=b.toUpperCase());this.search.focus();this.search.val(b)}l(a)}}));a.delegate("abbr","mousedown",this.bind(function(a){this.enabled&&(this.clear(),l(a),this.close(),this.triggerChange(),this.selection.focus())}));this.setPlaceholder();
this.search.bind("focus",this.bind(function(){this.container.addClass("select2-container-active")}))},clear:function(){this.opts.element.val("");this.selection.find("span").empty();this.selection.removeData("select2-data");this.setPlaceholder()},initSelection:function(){if(""===this.opts.element.val())this.close(),this.setPlaceholder();else{var a=this;this.opts.initSelection.call(null,this.opts.element,function(b){b!==g&&null!==b&&(a.updateSelection(b),a.close(),a.setPlaceholder())})}},prepareOpts:function(){var a=
this.parent.prepareOpts.apply(this,arguments);"select"===a.element.get(0).tagName.toLowerCase()&&(a.initSelection=function(a,c){var d=a.find(":selected");e.isFunction(c)&&c({id:d.attr("value"),text:d.text()})});return a},setPlaceholder:function(){var a=this.getPlaceholder();""===this.opts.element.val()&&a!==g&&!(this.select&&""!==this.select.find("option:first").text())&&(this.selection.find("span").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.selection.find("abbr").hide())},
postprocessResults:function(a,b){var c=0,d=this,f=!0;this.results.find(".select2-result-selectable").each2(function(a,b){if(m(d.id(b.data("select2-data")),d.opts.element.val()))return c=a,!1});this.highlight(c);!0===b&&(f=this.showSearchInput=F(a.results)>=this.opts.minimumResultsForSearch,this.dropdown.find(".select2-search")[f?"removeClass":"addClass"]("select2-search-hidden"),e(this.dropdown,this.container)[f?"addClass":"removeClass"]("select2-with-searchbox"))},onSelect:function(a){var b=this.opts.element.val();
this.opts.element.val(this.id(a));this.updateSelection(a);this.close();this.selection.focus();m(b,this.id(a))||this.triggerChange()},updateSelection:function(a){var b=this.selection.find("span");this.selection.data("select2-data",a);b.empty();a=this.opts.formatSelection(a,b);a!==g&&b.append(this.opts.escapeMarkup(a));this.selection.removeClass("select2-default");this.opts.allowClear&&this.getPlaceholder()!==g&&this.selection.find("abbr").show()},val:function(){var a,b=null,c=this;if(0===arguments.length)return this.opts.element.val();
a=arguments[0];if(this.select)this.select.val(a).find(":selected").each2(function(a,c){b={id:c.attr("value"),text:c.text()};return!1}),this.updateSelection(b),this.setPlaceholder();else{if(this.opts.initSelection===g)throw Error("cannot call val() if initSelection() is not defined");a?(this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){c.opts.element.val(!a?"":c.id(a));c.updateSelection(a);c.setPlaceholder()})):this.clear()}},clearSearch:function(){this.search.val("")},
data:function(a){var b;if(0===arguments.length)return b=this.selection.data("select2-data"),b==g&&(b=null),b;!a||""===a?this.clear():(this.opts.element.val(!a?"":this.id(a)),this.updateSelection(a))}});z=x(w,{createContainer:function(){return e("<div></div>",{"class":"select2-container select2-container-multi"}).html(" <ul class='select2-choices'> <li class='select2-search-field'> <input type='text' autocomplete='off' class='select2-input'> </li></ul><div class='select2-drop select2-drop-multi' style='display:none;'> <ul class='select2-results'> </ul></div>")},
prepareOpts:function(){var a=this.parent.prepareOpts.apply(this,arguments);"select"===a.element.get(0).tagName.toLowerCase()&&(a.initSelection=function(a,c){var d=[];a.find(":selected").each2(function(a,b){d.push({id:b.attr("value"),text:b.text()})});e.isFunction(c)&&c(d)});return a},initContainer:function(){var a;this.searchContainer=this.container.find(".select2-search-field");this.selection=a=this.container.find(".select2-choices");this.search.bind("keydown",this.bind(function(b){if(this.enabled){if(b.which===
f.BACKSPACE&&""===this.search.val()){this.close();var c;c=a.find(".select2-search-choice-focus");if(0<c.length){this.unselect(c.first());this.search.width(10);l(b);return}c=a.find(".select2-search-choice");0<c.length&&c.last().addClass("select2-search-choice-focus")}else a.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");if(this.opened())switch(b.which){case f.UP:case f.DOWN:this.moveHighlight(b.which===f.UP?-1:1);l(b);return;case f.ENTER:case f.TAB:this.selectHighlighted();
l(b);return;case f.ESC:this.cancel(b);l(b);return}if(!(b.which===f.TAB||f.isControl(b)||f.isFunctionKey(b)||b.which===f.BACKSPACE||b.which===f.ESC)&&!(!1===this.opts.openOnEnter&&b.which===f.ENTER))this.open(),(b.which===f.PAGE_UP||b.which===f.PAGE_DOWN)&&l(b)}}));this.search.bind("keyup",this.bind(this.resizeSearch));this.search.bind("blur",this.bind(function(a){this.container.removeClass("select2-container-active");this.search.removeClass("select2-focused");this.clearSearch();a.stopImmediatePropagation()}));
this.container.delegate(".select2-choices","mousedown",this.bind(function(a){this.enabled&&!(0<e(a.target).closest(".select2-search-choice").length)&&(this.clearPlaceholder(),this.open(),this.focusSearch(),a.preventDefault())}));this.container.delegate(".select2-choices","focus",this.bind(function(){this.enabled&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())}));this.clearSearch()},enable:function(){this.enabled||(this.parent.enable.apply(this,
arguments),this.search.removeAttr("disabled"))},disable:function(){this.enabled&&(this.parent.disable.apply(this,arguments),this.search.attr("disabled",!0))},initSelection:function(){""===this.opts.element.val()&&(this.updateSelection([]),this.close(),this.clearSearch());if(this.select||""!==this.opts.element.val()){var a=this;this.opts.initSelection.call(null,this.opts.element,function(b){if(b!==g&&b!==null){a.updateSelection(b);a.close();a.clearSearch()}})}},clearSearch:function(){var a=this.getPlaceholder();
a!==g&&0===this.getVal().length&&!1===this.search.hasClass("select2-focused")?(this.search.val(a).addClass("select2-default"),this.resizeSearch()):this.search.val(" ").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")?this.search.val("").removeClass("select2-default"):" "===this.search.val()&&this.search.val("")},opening:function(){this.parent.opening.apply(this,arguments);this.clearPlaceholder();this.resizeSearch();this.focusSearch()},close:function(){this.opened()&&
this.parent.close.apply(this,arguments)},focus:function(){this.close();this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(a){var b=[],c=[],d=this;e(a).each(function(){0>i(d.id(this),b)&&(b.push(d.id(this)),c.push(this))});a=c;this.selection.find(".select2-search-choice").remove();e(a).each(function(){d.addSelectedChoice(this)});d.postprocessResults()},tokenize:function(){var a=this.search.val(),a=this.opts.tokenizer(a,this.data(),this.bind(this.onSelect),
this.opts);null!=a&&a!=g&&(this.search.val(a),0<a.length&&this.open())},onSelect:function(a){this.addSelectedChoice(a);this.select&&this.postprocessResults();this.opts.closeOnSelect?(this.close(),this.search.width(10)):0<this.countSelectableResults()?(this.search.width(10),this.resizeSearch(),this.positionDropdown()):this.close();this.triggerChange({added:a});this.focusSearch()},cancel:function(){this.close();this.focusSearch()},addSelectedChoice:function(a){var b=e("<li class='select2-search-choice'> <div></div> <a href='#' onclick='return false;' class='select2-search-choice-close' tabindex='-1'></a></li>"),
c=this.id(a),d=this.getVal(),f;f=this.opts.formatSelection(a,b);b.find("div").replaceWith("<div>"+this.opts.escapeMarkup(f)+"</div>");b.find(".select2-search-choice-close").bind("mousedown",l).bind("click dblclick",this.bind(function(a){this.enabled&&(e(a.target).closest(".select2-search-choice").fadeOut("fast",this.bind(function(){this.unselect(e(a.target));this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");this.close();this.focusSearch()})).dequeue(),
l(a))})).bind("focus",this.bind(function(){this.enabled&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))}));b.data("select2-data",a);b.insertBefore(this.searchContainer);d.push(c);this.setVal(d)},unselect:function(a){var b=this.getVal(),c,d,a=a.closest(".select2-search-choice");if(0===a.length)throw"Invalid argument: "+a+". Must be .select2-search-choice";c=a.data("select2-data");d=i(this.id(c),b);0<=d&&(b.splice(d,1),this.setVal(b),this.select&&
this.postprocessResults());a.remove();this.triggerChange({removed:c})},postprocessResults:function(){var a=this.getVal(),b=this.results.find(".select2-result-selectable"),c=this.results.find(".select2-result-with-children"),d=this;b.each2(function(b,c){var e=d.id(c.data("select2-data"));0<=i(e,a)?c.addClass("select2-disabled").removeClass("select2-result-selectable"):c.removeClass("select2-disabled").addClass("select2-result-selectable")});c.each2(function(a,b){0==b.find(".select2-result-selectable").length?
b.addClass("select2-disabled"):b.removeClass("select2-disabled")});b.each2(function(a,b){if(!b.hasClass("select2-disabled")&&b.hasClass("select2-result-selectable"))return d.highlight(0),!1})},resizeSearch:function(){var a,b,c,d,f=this.search.outerWidth()-this.search.width();a=this.search;q||(c=a[0].currentStyle||window.getComputedStyle(a[0],null),q=e("<div></div>").css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,
fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),e("body").append(q));q.text(a.val());a=q.width()+10;b=this.search.offset().left;c=this.selection.width();d=this.selection.offset().left;b=c-(b-d)-f;b<a&&(b=c-f);40>b&&(b=c-f);this.search.width(b)},getVal:function(){var a;if(this.select)return a=this.select.val(),null===a?[]:a;a=this.opts.element.val();return s(a,this.opts.separator)},setVal:function(a){var b;this.select?this.select.val(a):(b=
[],e(a).each(function(){0>i(this,b)&&b.push(this)}),this.opts.element.val(0===b.length?"":b.join(this.opts.separator)))},val:function(){var a,b=[],c=this;if(0===arguments.length)return this.getVal();if(a=arguments[0])if(this.setVal(a),this.select)this.select.find(":selected").each(function(){b.push({id:e(this).attr("value"),text:e(this).text()})}),this.updateSelection(b);else{if(this.opts.initSelection===g)throw Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,
function(a){var b=e(a).map(c.id);c.setVal(b);c.updateSelection(a);c.clearSearch()})}else this.opts.element.val(""),this.updateSelection([]);this.clearSearch()},onSortStart:function(){if(this.select)throw Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");this.search.width(0);this.searchContainer.hide()},onSortEnd:function(){var a=[],b=this;this.searchContainer.show();this.searchContainer.appendTo(this.searchContainer.parent());this.resizeSearch();
this.selection.find(".select2-search-choice").each(function(){a.push(b.opts.id(e(this).data("select2-data")))});this.setVal(a);this.triggerChange()},data:function(a){var b=this,c;if(0===arguments.length)return this.selection.find(".select2-search-choice").map(function(){return e(this).data("select2-data")}).get();a||(a=[]);c=e.map(a,function(a){return b.opts.id(a)});this.setVal(c);this.updateSelection(a);this.clearSearch()}});e.fn.select2=function(){var a=Array.prototype.slice.call(arguments,0),b,
c,d,f,h="val destroy opened open close focus isFocused container onSortStart onSortEnd enable disable positionDropdown data".split(" ");this.each(function(){if(0===a.length||"object"===typeof a[0])b=0===a.length?{}:e.extend({},a[0]),b.element=e(this),"select"===b.element.get(0).tagName.toLowerCase()?f=b.element.attr("multiple"):(f=b.multiple||!1,"tags"in b&&(b.multiple=f=!0)),c=f?new z:new y,c.init(b);else if("string"===typeof a[0]){if(0>i(a[0],h))throw"Unknown method: "+a[0];d=g;c=e(this).data("select2");
if(c!==g&&(d="container"===a[0]?c.container:c[a[0]].apply(c,a.slice(1)),d!==g))return!1}else throw"Invalid arguments to select2 plugin: "+a;});return d===g?this:d};e.fn.select2.defaults={width:"copy",closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c){b=[];B(a.text,c.term,b);return b.join("")},formatSelection:function(a){return a?a.text:g},formatResultCssClass:function(){return g},formatNoMatches:function(){return"No matches found"},
formatInputTooShort:function(a,b){return"Please enter "+(b-a.length)+" more characters"},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results..."},formatSearching:function(){return"Searching..."},minimumResultsForSearch:0,minimumInputLength:0,maximumSelectionSize:0,id:function(a){return a.id},matcher:function(a,b){return 0<=b.toUpperCase().indexOf(a.toUpperCase())},separator:",",tokenSeparators:[],tokenizer:H,
escapeMarkup:function(a){return a&&"string"===typeof a?a.replace(/&/g,"&amp;"):a},blurOnChange:!1};window.Select2={query:{ajax:C,local:D,tags:E},util:{debounce:A,markMatch:B},"class":{"abstract":w,single:y,multi:z}}}})(jQuery);

Binary file not shown.

After

(image error) Size: 613 B

Binary file not shown.

After

(image error) Size: 845 B

Binary file not shown.

After

(image error) Size: 1.8 KiB

193
src/inputs/select2/select2.js vendored Normal file

@ -0,0 +1,193 @@
/**
Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
Please see [original docs](http://ivaynberg.github.com/select2) for detailed description and options.
You should manually include select2 distributive:
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
<script src="select2/select2.js"></script>
@class select2
@extends abstractinput
@since 1.4.1
@final
@example
<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-original-title="Select country"></a>
<script>
$(function(){
$('#country').editable({
source: [
{id: 'gb', text: 'Great Britain'},
{id: 'us', text: 'United States'},
{id: 'ru', text: 'Russia'}
],
select2: {
multiple: true
}
});
});
</script>
**/
(function ($) {
var Constructor = function (options) {
this.init('select2', options, Constructor.defaults);
options.select2 = options.select2 || {};
var that = this,
mixin = {
placeholder: options.placeholder
};
//detect whether it is multi-valued
this.isMultiple = options.select2.tags || options.select2.multiple;
//if not `tags` mode, we need define init set data from source
if(!options.select2.tags) {
if(options.source) {
mixin.data = options.source;
}
//this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710
mixin.initSelection = function (element, callback) {
var val = that.str2value(element.val()),
data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id');
//for single-valued mode should not use array. Take first element instead.
if($.isArray(data) && data.length && !that.isMultiple) {
data = data[0];
}
callback(data);
};
}
//overriding objects in config (as by default jQuery extend() is not recursive)
this.options.select2 = $.extend({}, Constructor.defaults.select2, mixin, options.select2);
};
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
$.extend(Constructor.prototype, {
render: function() {
this.setClass();
//apply select2
this.$input.select2(this.options.select2);
//trigger resize of editableform to re-position container in multi-valued mode
if(this.isMultiple) {
this.$input.on('change', function() {
$(this).closest('form').parent().triggerHandler('resize');
});
}
},
value2html: function(value, element) {
var text = '', data;
if(this.$input) { //when submitting form
data = this.$input.select2('data');
} else { //on init (autotext)
//here select2 instance not created yet and data may be even not loaded.
//we can check data/tags property of select config and if exist lookup text
if(this.options.select2.tags) {
data = value;
} else if(this.options.select2.data) {
data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id');
}
}
if($.isArray(data)) {
//collect selected data and show with separator
text = [];
$.each(data, function(k, v){
text.push(v && typeof v === 'object' ? v.text : v);
});
} else if(data) {
text = data.text;
}
text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
$(element).text(text);
},
html2value: function(html) {
return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null;
},
value2input: function(value) {
this.$input.val(value).trigger('change');
},
input2value: function() {
return this.$input.select2('val');
},
str2value: function(str, separator) {
if(typeof str !== 'string' || !this.isMultiple) {
return str;
}
separator = separator || this.options.select2.separator || $.fn.select2.defaults.separator;
var val, i, l;
if (str === null || str.length < 1) {
return null;
}
val = str.split(separator);
for (i = 0, l = val.length; i < l; i = i + 1) {
val[i] = $.trim(val[i]);
}
return val;
}
});
Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
/**
@property tpl
@default <input type="hidden">
**/
tpl:'<input type="hidden">',
/**
Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2).
@property select2
@type object
@default null
**/
select2: null,
/**
Placeholder attribute of select
@property placeholder
@type string
@default null
**/
placeholder: null,
/**
Source data for select. It will be assigned to select2 `data` property and kept here just for convenience.
Please note, that format is different from simple `select` input: use 'id' instead of 'value'.
E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
@property source
@type array
@default null
**/
source: null,
/**
Separator used to display tags.
@property viewseparator
@type string
@default ', '
**/
viewseparator: ', '
});
$.fn.editabletypes.select2 = Constructor;
}(window.jQuery));

@ -48,10 +48,7 @@ $(function(){
.keyup($.proxy(this.toggleClear, this))
.parent().css('position', 'relative');
this.$clear.click($.proxy(function(){
this.$clear.hide();
this.$input.val('').focus();
}, this));
this.$clear.click($.proxy(this.clear, this));
}
},
@ -76,12 +73,17 @@ $(function(){
return;
}
if(this.$input.val()) {
if(this.$input.val().length) {
this.$clear.show();
} else {
this.$clear.hide();
}
}
},
clear: function() {
this.$clear.hide();
this.$input.val('').focus();
}
});
Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {

221
src/inputs/typeahead.js Normal file

@ -0,0 +1,221 @@
/**
Typeahead input (bootstrap only). Based on Twitter Bootstrap [typeahead](http://twitter.github.com/bootstrap/javascript.html#typeahead).
Depending on `source` format typeahead operates in two modes:
* **strings**:
When `source` defined as array of strings, e.g. `['text1', 'text2', 'text3' ...]`.
User can submit one of these strings or any text entered in input (even if it is not matching source).
* **objects**:
When `source` defined as array of objects, e.g. `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`.
User can submit only values that are in source (otherwise `null` is submitted). This is more like *dropdown* behavior.
@class typeahead
@extends list
@since 1.4.1
@final
@example
<a href="#" id="country" data-type="typeahead" data-pk="1" data-url="/post" data-original-title="Input country"></a>
<script>
$(function(){
$('#country').editable({
value: 'ru',
source: [
{value: 'gb', text: 'Great Britain'},
{value: 'us', text: 'United States'},
{value: 'ru', text: 'Russia'}
]
}
});
});
</script>
**/
(function ($) {
var Constructor = function (options) {
this.init('typeahead', options, Constructor.defaults);
//overriding objects in config (as by default jQuery extend() is not recursive)
this.options.typeahead = $.extend({}, Constructor.defaults.typeahead, {
//set default methods for typeahead to work with objects
matcher: this.matcher,
sorter: this.sorter,
highlighter: this.highlighter,
updater: this.updater
}, options.typeahead);
};
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.list);
$.extend(Constructor.prototype, {
renderList: function() {
this.$input = this.$tpl.is('input') ? this.$tpl : this.$tpl.find('input[type="text"]');
//set source of typeahead
this.options.typeahead.source = this.sourceData;
//apply typeahead
this.$input.typeahead(this.options.typeahead);
//attach own render method
this.$input.data('typeahead').render = $.proxy(this.typeaheadRender, this.$input.data('typeahead'));
this.renderClear();
this.setClass();
this.setAttr('placeholder');
},
value2htmlFinal: function(value, element) {
if(this.getIsObjects()) {
var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
$(element).text(items.length ? items[0].text : '');
} else {
$(element).text(value);
}
},
html2value: function (html) {
return html ? html : null;
},
value2input: function(value) {
if(this.getIsObjects()) {
var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
this.$input.data('value', value).val(items.length ? items[0].text : '');
} else {
this.$input.val(value);
}
},
input2value: function() {
if(this.getIsObjects()) {
var value = this.$input.data('value'),
items = $.fn.editableutils.itemsByValue(value, this.sourceData);
if(items.length && items[0].text.toLowerCase() === this.$input.val().toLowerCase()) {
return value;
} else {
return null; //entered string not found in source
}
} else {
return this.$input.val();
}
},
/*
if in sourceData values <> texts, typeahead in "objects" mode:
user must pick some value from list, otherwise `null` returned.
if all values == texts put typeahead in "strings" mode:
anything what entered is submited.
*/
getIsObjects: function() {
if(this.isObjects === undefined) {
this.isObjects = false;
for(var i=0; i<this.sourceData.length; i++) {
if(this.sourceData[i].value !== this.sourceData[i].text) {
this.isObjects = true;
break;
}
}
}
return this.isObjects;
},
/*
Methods borrowed from text input
*/
activate: $.fn.editabletypes.text.prototype.activate,
renderClear: $.fn.editabletypes.text.prototype.renderClear,
postrender: $.fn.editabletypes.text.prototype.postrender,
toggleClear: $.fn.editabletypes.text.prototype.toggleClear,
clear: function() {
$.fn.editabletypes.text.prototype.clear.call(this);
this.$input.data('value', '');
},
/*
Typeahead option methods used as defaults
*/
/*jshint eqeqeq:false, curly: false, laxcomma: true*/
matcher: function (item) {
return $.fn.typeahead.Constructor.prototype.matcher.call(this, item.text);
},
sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
, text;
while (item = items.shift()) {
text = item.text;
if (!text.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item);
else if (~text.indexOf(this.query)) caseSensitive.push(item);
else caseInsensitive.push(item);
}
return beginswith.concat(caseSensitive, caseInsensitive);
},
highlighter: function (item) {
return $.fn.typeahead.Constructor.prototype.highlighter.call(this, item.text);
},
updater: function (item) {
item = this.$menu.find('.active').data('item');
this.$element.data('value', item.value);
return item.text;
},
/*
Overwrite typeahead's render method to store objects.
There are a lot of disscussion in bootstrap repo on this point and still no result.
See https://github.com/twitter/bootstrap/issues/5967
This function just store item in via jQuery data() method instead of attr('data-value')
*/
typeaheadRender: function (items) {
var that = this;
items = $(items).map(function (i, item) {
// i = $(that.options.item).attr('data-value', item)
i = $(that.options.item).data('item', item);
i.find('a').html(that.highlighter(item));
return i[0];
});
items.first().addClass('active');
this.$menu.html(items);
return this;
}
/*jshint eqeqeq: true, curly: true, laxcomma: false*/
});
Constructor.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
/**
@property tpl
@default <input type="text">
**/
tpl:'<input type="text">',
/**
Configuration of typeahead. [Full list of options](http://twitter.github.com/bootstrap/javascript.html#typeahead).
@property typeahead
@type object
@default null
**/
typeahead: null,
/**
Whether to show `clear` button
@property clear
@type boolean
@default true
**/
clear: true
});
$.fn.editabletypes.typeahead = Constructor;
}(window.jQuery));

@ -21,7 +21,7 @@
<div>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<div id="async-fixture"></div>
<div id="async-fixture" style="padding-left:300px"></div>
</div>
</body>
</html>

@ -40,7 +40,8 @@ define(function () {
'inputs/checklist',
'inputs/html5types',
'inputs/combodate/combodate',
'inputs-ext/address/address'],
'inputs-ext/address/address',
'inputs/select2/select2'],
init: function(require) {
loadCss(require.toUrl("./editable-form.css"));
}
@ -52,7 +53,8 @@ define(function () {
'inputs/textarea': ['inputs/abstract'],
'inputs/abstract': ['editable-form/editable-form-utils'],
'inputs/html5types': ['inputs/text'],
'inputs/combodate/combodate': ['inputs/abstract', 'inputs/combodate/lib/combodate', 'inputs/combodate/lib/moment.min'],
'inputs/combodate/combodate': ['inputs/abstract', 'inputs/combodate/lib/combodate', 'inputs/combodate/lib/moment.min'],
'inputs/typeahead': ['inputs/list'],
/*
bootstrap
@ -94,6 +96,17 @@ define(function () {
}
},
//select2
'inputs/select2/select2': {
deps: ['require',
'inputs/select2/lib/select2',
'inputs/abstract'],
init: function(require) {
loadCss(require.toUrl("./lib/select2.css"));
}
},
//datefield
'inputs/date/datefield': ['inputs/date/date'],
@ -155,6 +168,7 @@ define(function () {
//bootstrap
shim['editable-form/editable-form'].deps.push('inputs/date/datefield');
shim['editable-form/editable-form'].deps.push('inputs-ext/wysihtml5/wysihtml5');
shim['editable-form/editable-form'].deps.push('inputs/typeahead');
shim['element/editable-element'].deps.push('editable-form/editable-form-bootstrap');
shim['element/editable-element'].deps.push('containers/editable-popover');
} else if(f === 'jqueryui') {

@ -31,7 +31,11 @@ require(["loader", jqurl], function(loader) {
switch(params.f) {
case 'bootstrap':
custom = ['test/unit/datefield', 'test/unit/date', 'test/unit/wysihtml5'];
custom = ['test/unit/datefield',
'test/unit/date',
'test/unit/wysihtml5',
'test/unit/typeahead'
];
break;
default:
@ -45,7 +49,8 @@ require(["loader", jqurl], function(loader) {
'test/unit/textarea',
'test/unit/select',
'test/unit/checklist',
'test/unit/combodate'
'test/unit/combodate',
'test/unit/select2'
];
tests = tests.concat(custom);
tests.push('test/unit/api');

@ -155,13 +155,13 @@
ok(!p.is(':visible'), 'popover1 closed');
ok(!p2.is(':visible'), 'popover2 closed');
});
test("onblur: submit", function () {
var oldValue = 'abc',
newValue = 'cde',
e = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="a">'+oldValue+'</a>').appendTo('#qunit-fixture').editable({
e = $('<a href="#" data-type="text" data-pk="1" id="a">'+oldValue+'</a>').appendTo('#qunit-fixture').editable({
onblur: 'submit',
url: function() {}
send: 'never'
}),
e2 = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();
@ -179,7 +179,7 @@
$('#qunit-fixture').click();
ok(!p.is(':visible'), 'popover1 closed');
equal(e.data('editable').value, newValue, 'new value saved');
//click on another editable
e.click();
p = tip(e);
@ -545,5 +545,156 @@
ok(p.is(':visible'), 'inline visible visible');
ok(p.hasClass('editable-inline'), 'has inline class');
});
test("option 'inputclass'", function () {
var e = $('<a href="#" id="a" data-inputclass="span4"> </a>').appendTo('#qunit-fixture').editable();
e.click();
var p = tip(e);
ok(p.find('input[type=text]').hasClass('span4'), 'class set correctly');
p.find('.editable-cancel').click();
ok(!p.is(':visible'), 'popover was removed');
});
test("emptytext, emptyclass", function () {
var emptytext = 'empty!',
emptyclass = 'abc',
e = $('<a href="#" id="a"> </a>').appendTo('#qunit-fixture').editable({
emptytext: emptytext,
emptyclass: emptyclass,
send: 'never'
});
equal(e.text(), emptytext, 'emptytext shown on init');
ok(e.hasClass(emptyclass), 'emptyclass added');
e.click();
var p = tip(e);
equal(p.find('input[type="text"]').val(), '', 'input val is empty string');
// p.find('.editable-cancel').click();
//set non-empty value
p.find('input[type="text"]').val('abc');
p.find('form').submit();
ok(!p.is(':visible'), 'popover was removed');
ok(e.text() != emptytext, 'emptytext not shown');
ok(!e.hasClass(emptyclass), 'emptyclass removed');
e.click();
p = tip(e);
p.find('input[type="text"]').val('');
p.find('form').submit();
ok(!p.is(':visible'), 'popover was removed');
equal(e.text(), emptytext, 'emptytext shown');
ok(e.hasClass(emptyclass), 'emptyclass added');
e.editable('disable');
equal(e.text(), '', 'emptytext removed');
ok(!e.hasClass(emptyclass), 'emptyclass removed');
e.editable('enable');
e.editable('enable');
equal(e.text(), emptytext, 'emptytext shown');
ok(e.hasClass(emptyclass), 'emptyclass added');
});
asyncTest("submit to url defined as function", function () {
expect(10);
var newText = 'qwe',
pass = false;
//should be called even without pk!
e = $('<a href="#" data-pk1="1" id="a"></a>').appendTo(fx).editable({
url: function(params) {
ok(this === e[0], 'scope is ok');
ok(params.value, newText, 'new text passed in users function');
if(!pass) {
var d = new $.Deferred;
return d.reject('my error');
}
}
});
e.click();
var p = tip(e);
ok(p.find('input[type=text]').length, 'input exists')
p.find('input').val(newText);
p.find('form').submit();
setTimeout(function() {
ok(p.is(':visible'), 'popover visible');
equal(p.find('.editable-error-block').text(), 'my error', 'error shown correctly');
pass = true;
newText = 'dfgd';
p.find('input').val(newText);
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popover closed');
equal(e.text(), newText, 'element text ok');
ok(!e.hasClass($.fn.editable.defaults.unsavedclass), 'no unsaved class');
e.remove();
start();
}, timeout);
}, timeout);
});
test("`selector` option", function () {
var parent = $('<div><a href="#" id="a" data-type="text">123</a></div>').appendTo('#qunit-fixture').editable({
selector: 'a',
url: 'post.php',
source: groups
}),
b = $('<a href="#" id="b" data-type="select" data-value="1"></a>'),
e = $('#a'),
selected = 2;
ok(!e.hasClass('editable'), 'no editable class applied');
e.click();
var p = tip(e);
ok(e.hasClass('editable'), 'editable class applied');
ok(e.data('editable'), 'data(editable) ok');
ok(!e.data('editable').selector, 'selector cleared');
equal(e.data('editable').options.url, 'post.php', 'url ok');
equal(e.data('editable').options.type, 'text', 'type text ok');
ok(p.is(':visible'), 'popover visible');
ok(p.find('input[type=text]').length, 'input exists');
equal(p.find('input[type=text]').val(), '123', 'input contain correct value');
//dynamically add second element
b.appendTo(parent);
e = b;
e.click();
ok(!p.is(':visible'), 'first popover closed');
ok(e.data('editable'), 'data(editable) ok');
ok(!e.data('editable').selector, 'selector cleared');
equal(e.data('editable').options.url, 'post.php', 'url ok');
equal(e.data('editable').options.type, 'select', 'type select ok');
p = tip(e);
ok(p.is(':visible'), 'second popover visible');
ok(p.find('select').length, 'select exists');
equal(p.find('select').find('option').length, size, 'options loaded');
equal(p.find('select').val(), e.data('editable').value, 'selected value correct');
p.find('select').val(selected);
p.find('form').submit();
ok(!p.is(':visible'), 'popover closed');
equal(e.data('editable').value, selected, 'new value saved');
equal(e.text(), groups[selected], 'new text shown');
});
}(jQuery));

@ -689,6 +689,43 @@ $(function () {
}, timeout);
}, timeout);
});
});
asyncTest("optgroup", function () {
var
selected = 2,
e = $('<a href="#" data-type="select" data-value="'+selected+'" data-url="post.php"></a>').appendTo(fx).editable({
pk: 1,
source: [
{text: 'groups', children: groups},
{value: 'v1', text: 't1', children: ['a', 'b', 'c']},
{value: 'v2', text: 't2'}
]
});
equal(e.text(), groups[selected], 'text shown');
e.click();
var p = tip(e);
ok(p.is(':visible'), 'container visible');
ok(p.find('select').length, 'select exists');
equal(p.find('select').find('option').length, size + 3 + 1, 'options loaded');
equal(p.find('select').val(), e.data('editable').value, 'selected value correct');
equal(p.find('select').find('optgroup').length, 2, 'optgroup loaded');
equal(p.find('select').find('optgroup').eq(0).children().length, size, 'optgroup items ok');
selected = 'a';
p.find('select').val(selected);
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popover closed')
equal(e.data('editable').value, selected, 'new value saved')
equal(e.text(), 'a', 'new text shown')
e.remove();
start();
}, timeout);
});
});

146
test/unit/select2.js Normal file

@ -0,0 +1,146 @@
$(function () {
module("select2", {
setup: function(){
sfx = $('#qunit-fixture'),
fx = $('#async-fixture');
$.support.transition = false;
}
});
asyncTest("data (single)", function () {
var s = 2, text = 'text2',
e = $('<a href="#" data-type="select2" data-name="select2" data-value="'+s+'"></a>').appendTo(fx).editable({
source: [{id: 1, text: 'text1'}, {id: s, text: text}, {id: 3, text: 'text3'}],
select2: {}
});
//autotext
equal(e.data('editable').value, s, 'initial value ok');
equal(e.text(), text, 'intial text ok');
e.click();
var p = tip(e);
ok(p.is(':visible'), 'popover visible');
var $input = p.find('input[type="hidden"]');
ok($input.length, 'input exists');
ok($input.select2, 'select2 applied');
equal($input.val(), e.data('editable').value, 'selected value correct');
equal(p.find('.select2-choice span').text(), text, 'selected text correct');
//select new value
s = 1;
text = 'text1';
$input.select2('val', s);
equal($input.val(), s, 'new value ok');
equal(p.find('.select2-choice span').text(), text, 'new text ok');
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popover closed');
equal(e.data('editable').value, s, 'new value ok');
equal(e.text(), text, 'new text ok');
e.remove();
start();
}, timeout);
});
asyncTest("data (multiple)", function () {
var s = '2,3', text = 'text2, text3',
e = $('<a href="#" data-type="select2" data-name="select2" data-value="'+s+'"></a>').appendTo(fx).editable({
source: [{id: 1, text: 'text1'}, {id: 2, text: 'text2'}, {id: 3, text: 'text3'}],
viewseparator: ', ',
select2: {
multiple: true
}
});
//autotext
equal(e.data('editable').value.join(','), s, 'initial value ok');
equal(e.text(), text, 'intial text ok');
e.click();
var p = tip(e);
ok(p.is(':visible'), 'popover visible');
var $input = p.find('input[type="hidden"]');
ok($input.length, 'input exists');
ok($input.select2, 'select2 applied');
equal($input.val(), s, 'selected value ok');
equal(p.find('.select2-search-choice > div').length, 2, 'selected text ok');
equal(p.find('.select2-search-choice > div').eq(0).text(), 'text2', 'text2 ok');
equal(p.find('.select2-search-choice > div').eq(1).text(), 'text3', 'text3 ok');
//select new value
s = '1,2';
text = 'text1, text2';
$input.select2('val', [1, 2]);
equal($input.val(), s, 'new value ok');
equal(p.find('.select2-search-choice > div').length, 2, 'new text ok');
equal(p.find('.select2-search-choice > div').eq(0).text(), 'text1', 'text1 ok');
equal(p.find('.select2-search-choice > div').eq(1).text(), 'text2', 'text2 ok');
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popover closed');
equal(e.data('editable').value, s, 'new value ok');
equal(e.text(), text, 'new text ok');
e.remove();
start();
}, timeout);
});
asyncTest("tags", function () {
var s = 'text2,abc', text = 'text2, abc',
e = $('<a href="#" data-type="select2" data-name="select2">'+text+'</a>').appendTo(fx).editable({
viewseparator: ', ',
select2: {
tags: ['text1', 'text2']
}
});
equal(e.data('editable').value.join(','), s, 'initial value ok');
e.click();
var p = tip(e);
ok(p.is(':visible'), 'popover visible');
var $input = p.find('input[type="hidden"]');
ok($input.length, 'input exists');
ok($input.select2, 'select2 applied');
equal($input.val(), s, 'selected value ok');
equal(p.find('.select2-search-choice > div').length, 2, 'selected text ok');
equal(p.find('.select2-search-choice > div').eq(0).text(), 'text2', 'text2 ok');
equal(p.find('.select2-search-choice > div').eq(1).text(), 'abc', 'abc ok');
//select new value
s = 'text1,cde';
text = 'text1, cde';
$input.select2('val', ['text1', 'cde']);
equal($input.val(), s, 'new value ok');
equal(p.find('.select2-search-choice > div').length, 2, 'new text ok');
equal(p.find('.select2-search-choice > div').eq(0).text(), 'text1', 'text1 ok');
equal(p.find('.select2-search-choice > div').eq(1).text(), 'cde', 'cde ok');
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popover closed');
equal(e.data('editable').value, s, 'new value ok');
equal(e.text(), text, 'new text ok');
e.remove();
start();
}, timeout);
});
});

@ -7,19 +7,6 @@ $(function () {
}
});
test("if element originally empty: emptytext should be shown and input should contain ''", function () {
var emptytext = 'empty!',
e = $('<a href="#" id="a"> </a>').appendTo('#qunit-fixture').editable({emptytext: emptytext});
equal(e.text(), emptytext, 'emptytext shown on init');
e.click();
var p = tip(e);
equal(p.find('input[type="text"]').val(), '', 'input val is empty string')
p.find('.editable-cancel').click();
ok(!p.is(':visible'), 'popover was removed')
})
test("option 'placeholder'", function () {
var e = $('<a href="#" id="a" data-placeholder="abc"> </a>').appendTo('#qunit-fixture').editable();
@ -29,16 +16,6 @@ $(function () {
p.find('.editable-cancel').click();
ok(!p.is(':visible'), 'popover was removed');
});
test("option 'inputclass'", function () {
var e = $('<a href="#" id="a" data-inputclass="span4"> </a>').appendTo('#qunit-fixture').editable();
e.click();
var p = tip(e);
ok(p.find('input[type=text]').hasClass('span4'), 'class set correctly');
p.find('.editable-cancel').click();
ok(!p.is(':visible'), 'popover was removed');
});
asyncTest("should load correct value and save new entered text (and value)", function () {
var v = 'ab<b>"',
@ -242,35 +219,6 @@ $(function () {
}, timeout);
});
asyncTest("submit to url defined as function", function () {
expect(4);
var newText = 'qwe',
//should be called even without pk!
e = $('<a href="#" data-pk1="1" id="a"></a>').appendTo(fx).editable({
url: function(params) {
ok(this === e[0], 'scope is ok');
ok(params.value, newText, 'new text passed in users function');
var d = new $.Deferred;
return d.reject('my error');
}
});
e.click();
var p = tip(e);
ok(p.find('input[type=text]').length, 'input exists')
p.find('input').val(newText);
p.find('form').submit();
setTimeout(function() {
equal(p.find('.editable-error-block').text(), 'my error', 'error shown correctly');
e.remove();
start();
}, timeout);
});
asyncTest("should show emptytext if entered text is empty", function () {
var emptytext = 'blabla',
@ -364,13 +312,14 @@ $(function () {
ok(!p.is(':visible'), 'popover was removed');
equal(e.data('editable').value, newText, 'new text saved to value');
equal(e.text(), newText, 'new text shown');
ok(e.hasClass('editable-unsaved'), 'has class editable-unsaved');
ok(e.hasClass($.fn.editable.defaults.unsavedclass), 'has class editable-unsaved');
});
test("send = 'never'. if pk defined --> should save new entered text and value, but no ajax", function () {
var e = $('<a href="#" data-name="text1">abc</a>').appendTo('#qunit-fixture').editable({
pk: 123,
send: 'never'
send: 'never',
unsavedclass: 'qq'
}),
newText = 'cde';
@ -383,7 +332,7 @@ $(function () {
ok(!p.is(':visible'), 'popover was removed');
equal(e.data('editable').value, newText, 'new text saved to value');
equal(e.text(), newText, 'new text shown');
ok(e.hasClass('editable-unsaved'), 'has class editable-unsaved');
ok(e.hasClass('qq'), 'has class editable-unsaved');
});
test("if name not defined --> should be taken from id", function () {
@ -436,6 +385,7 @@ $(function () {
ok(!p.is(':visible'), 'popover was removed');
ok(!e.text().length, 'element still empty, new value was not displayed');
equal(e.data('editable').value, newText, 'new text saved to value');
ok(!e.hasClass($.fn.editable.defaults.unsavedclass), 'no unsaved css');
e.remove();
start();
}, timeout);

122
test/unit/typeahead.js Normal file

@ -0,0 +1,122 @@
$(function () {
module("typeahead", {
setup: function(){
sfx = $('#qunit-fixture'),
fx = $('#async-fixture');
$.support.transition = false;
}
});
asyncTest("should load correct value and save new entered text (source as objects)", function () {
var v = 2,
e = $('<a href="#" data-pk="1" data-name="text1" data-type="typeahead" data-value="'+v+'" data-url="post.php"></a>').appendTo(fx).editable({
source: groups,
typeahead: {
items: 5
}
}),
newText = 'adm';
equal(e.text(), groups[v], 'autotext ok');
equal(e.data().editable.value, v, 'initial value ok');
e.click();
var p = tip(e),
$input = p.find('input[type=text]');
ok(p.is(':visible'), 'popup visible');
ok($input.length, 'input exists');
equal($input.val(), groups[v], 'input contain correct text');
equal($input.data('value'), v, 'input contain correct data-value');
ok($input.typeahead, 'typeahead applied to input');
$input.val(newText).keyup();
ok(p.find('.typeahead.dropdown-menu').is(':visible'), 'dropdown visible');
//select `Admin`
v = 5;
p.find('.typeahead.dropdown-menu').find('.active').click();
equal($input.val(), groups[v], 'input contain correct text');
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popup closed');
equal(e.data('editable').value, v, 'new text saved to value');
equal(e.text(), groups[v], 'new text shown');
e.click();
p = tip(e),
$input = p.find('input[type=text]');
$input.val('not_matched_text').keyup();
ok(!p.find('.typeahead.dropdown-menu').is(':visible'), 'dropdown not visible');
p.find('form').submit();
setTimeout(function() {
equal(e.data('editable').value, null, 'null saved to value');
equal(e.text(), e.data().editable.options.emptytext, 'emptytext shown');
e.remove();
start();
}, timeout);
}, timeout);
});
asyncTest("should load correct value and save new entered text (source as strings)", function () {
var v = 'a',
e = $('<a href="#" data-pk="1" data-name="text1" data-type="typeahead" data-value="'+v+'" data-url="post.php"></a>').appendTo(fx).editable({
source: ['a', 'ab', 'c']
});
equal(e.text(), v, 'autotext ok');
equal(e.data().editable.value, v, 'initial value ok');
e.click();
var p = tip(e),
$input = p.find('input[type=text]');
ok(p.is(':visible'), 'popup visible');
ok($input.length, 'input exists');
equal($input.val(), v, 'input contain correct text');
equal($input.data('value'), undefined, 'input not contain data-value');
ok($input.typeahead, 'typeahead applied to input');
$input.val('b').keyup();
ok(p.find('.typeahead.dropdown-menu').is(':visible'), 'dropdown visible');
//select `ab`
v = 'ab';
p.find('.typeahead.dropdown-menu').find('.active').click();
equal($input.val(), v, 'input contain correct text');
p.find('form').submit();
setTimeout(function() {
ok(!p.is(':visible'), 'popup closed');
equal(e.data('editable').value, v, 'new text saved to value');
equal(e.text(), v, 'new text shown');
e.click();
p = tip(e),
$input = p.find('input[type=text]');
v = 'not_matched_text';
$input.val(v).keyup();
ok(!p.find('.typeahead.dropdown-menu').is(':visible'), 'dropdown not visible');
p.find('form').submit();
setTimeout(function() {
equal(e.data('editable').value, v, 'new text saved to value');
equal(e.text(), v, 'new text shown');
e.remove();
start();
}, timeout);
}, timeout);
});
});