Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Updated latest version for assets

  • Loading branch information...
commit 41f37eda4650df0a104a7cfe6731fa95d1269177 1 parent b551525
@boriscy authored
View
2  lib/assets/version.rb
@@ -1,3 +1,3 @@
module Assets
- VERSION = "1.0.4"
+ VERSION = "1.0.5"
end
View
2  vendor/assets/javascripts/bootstrap/bootstrap-affix.js
@@ -1,5 +1,5 @@
/* ==========================================================
- * bootstrap-affix.js v2.2.2
+ * bootstrap-affix.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#affix
* ==========================================================
* Copyright 2012 Twitter, Inc.
View
2  vendor/assets/javascripts/bootstrap/bootstrap-alert.js
@@ -1,5 +1,5 @@
/* ==========================================================
- * bootstrap-alert.js v2.2.2
+ * bootstrap-alert.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
View
2  vendor/assets/javascripts/bootstrap/bootstrap-button.js
@@ -1,5 +1,5 @@
/* ============================================================
- * bootstrap-button.js v2.2.2
+ * bootstrap-button.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
View
40 vendor/assets/javascripts/bootstrap/bootstrap-carousel.js
@@ -1,5 +1,5 @@
/* ==========================================================
- * bootstrap-carousel.js v2.2.2
+ * bootstrap-carousel.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
@@ -28,6 +28,7 @@
var Carousel = function (element, options) {
this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
this.options = options
this.options.pause == 'hover' && this.$element
.on('mouseenter', $.proxy(this.pause, this))
@@ -38,19 +39,24 @@
cycle: function (e) {
if (!e) this.paused = false
+ if (this.interval) clearInterval(this.interval);
this.options.interval
&& !this.paused
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
+ , getActiveIndex: function () {
+ this.$active = this.$element.find('.item.active')
+ this.$items = this.$active.parent().children()
+ return this.$items.index(this.$active)
+ }
+
, to: function (pos) {
- var $active = this.$element.find('.item.active')
- , children = $active.parent().children()
- , activePos = children.index($active)
+ var activeIndex = this.getActiveIndex()
, that = this
- if (pos > (children.length - 1) || pos < 0) return
+ if (pos > (this.$items.length - 1) || pos < 0) return
if (this.sliding) {
return this.$element.one('slid', function () {
@@ -58,11 +64,11 @@
})
}
- if (activePos == pos) {
+ if (activeIndex == pos) {
return this.pause().cycle()
}
- return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
+ return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
}
, pause: function (e) {
@@ -103,10 +109,19 @@
e = $.Event('slide', {
relatedTarget: $next[0]
+ , direction: direction
})
if ($next.hasClass('active')) return
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ this.$element.one('slid', function () {
+ var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+ $nextIndicator && $nextIndicator.addClass('active')
+ })
+ }
+
if ($.support.transition && this.$element.hasClass('slide')) {
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
@@ -151,7 +166,7 @@
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (action) data[action]()
- else if (options.interval) data.cycle()
+ else if (options.interval) data.pause().cycle()
})
}
@@ -174,11 +189,18 @@
/* CAROUSEL DATA-API
* ================= */
- $(document).on('click.carousel.data-api', '[data-slide]', function (e) {
+ $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, options = $.extend({}, $target.data(), $this.data())
+ , slideIndex
+
$target.carousel(options)
+
+ if (slideIndex = $this.attr('data-slide-to')) {
+ $target.data('carousel').pause().to(slideIndex).cycle()
+ }
+
e.preventDefault()
})
View
8 vendor/assets/javascripts/bootstrap/bootstrap-collapse.js
@@ -1,5 +1,5 @@
/* =============================================================
- * bootstrap-collapse.js v2.2.2
+ * bootstrap-collapse.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -52,7 +52,7 @@
, actives
, hasData
- if (this.transitioning) return
+ if (this.transitioning || this.$element.hasClass('in')) return
dimension = this.dimension()
scroll = $.camelCase(['scroll', dimension].join('-'))
@@ -72,7 +72,7 @@
, hide: function () {
var dimension
- if (this.transitioning) return
+ if (this.transitioning || !this.$element.hasClass('in')) return
dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', $.Event('hide'), 'hidden')
@@ -129,7 +129,7 @@
return this.each(function () {
var $this = $(this)
, data = $this.data('collapse')
- , options = typeof option == 'object' && option
+ , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
View
24 vendor/assets/javascripts/bootstrap/bootstrap-dropdown.js
@@ -1,5 +1,5 @@
/* ============================================================
- * bootstrap-dropdown.js v2.2.2
+ * bootstrap-dropdown.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
@@ -81,7 +81,10 @@
isActive = $parent.hasClass('open')
- if (!isActive || (isActive && e.keyCode == 27)) return $this.click()
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
$items = $('[role=menu] li:not(.divider):visible a', $parent)
@@ -115,8 +118,9 @@
selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
- $parent = $(selector)
- $parent.length || ($parent = $this.parent())
+ $parent = selector && $(selector)
+
+ if (!$parent || !$parent.length) $parent = $this.parent()
return $parent
}
@@ -152,10 +156,10 @@
* =================================== */
$(document)
- .on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus)
- .on('click.dropdown touchstart.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('touchstart.dropdown.data-api', '.dropdown-menu', function (e) { e.stopPropagation() })
- .on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
- .on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+ .on('click.dropdown.data-api', clearMenus)
+ .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('.dropdown-menu', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
-}(window.jQuery);
+}(window.jQuery);
View
24 vendor/assets/javascripts/bootstrap/bootstrap-modal.js
@@ -1,5 +1,5 @@
/* =========================================================
- * bootstrap-modal.js v2.2.2
+ * bootstrap-modal.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
@@ -60,8 +60,7 @@
that.$element.appendTo(document.body) //don't move modals dom position
}
- that.$element
- .show()
+ that.$element.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
@@ -139,12 +138,13 @@
})
}
- , hideModal: function (that) {
- this.$element
- .hide()
- .trigger('hidden')
-
- this.backdrop()
+ , hideModal: function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.removeBackdrop()
+ that.$element.trigger('hidden')
+ })
}
, removeBackdrop: function () {
@@ -172,6 +172,8 @@
this.$backdrop.addClass('in')
+ if (!callback) return
+
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
@@ -180,8 +182,8 @@
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
- this.$backdrop.one($.support.transition.end, $.proxy(this.removeBackdrop, this)) :
- this.removeBackdrop()
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
} else if (callback) {
callback()
View
10 vendor/assets/javascripts/bootstrap/bootstrap-popover.js
@@ -1,5 +1,5 @@
/* ===========================================================
- * bootstrap-popover.js v2.2.2
+ * bootstrap-popover.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
@@ -58,8 +58,8 @@
, $e = this.$element
, o = this.options
- content = $e.attr('data-content')
- || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
+ content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
+ || $e.attr('data-content')
return content
}
@@ -99,7 +99,7 @@
placement: 'right'
, trigger: 'click'
, content: ''
- , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"></div></div></div>'
+ , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
})
@@ -111,4 +111,4 @@
return this
}
-}(window.jQuery);
+}(window.jQuery);
View
4 vendor/assets/javascripts/bootstrap/bootstrap-scrollspy.js
@@ -1,5 +1,5 @@
/* =============================================================
- * bootstrap-scrollspy.js v2.2.2
+ * bootstrap-scrollspy.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -59,7 +59,7 @@
, $href = /^#\w/.test(href) && $(href)
return ( $href
&& $href.length
- && [[ $href.position().top + self.$scrollElement.scrollTop(), href ]] ) || null
+ && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
})
.sort(function (a, b) { return a[0] - b[0] })
.each(function () {
View
2  vendor/assets/javascripts/bootstrap/bootstrap-tab.js
@@ -1,5 +1,5 @@
/* ========================================================
- * bootstrap-tab.js v2.2.2
+ * bootstrap-tab.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
View
124 vendor/assets/javascripts/bootstrap/bootstrap-tooltip.js
@@ -1,5 +1,5 @@
/* ===========================================================
- * bootstrap-tooltip.js v2.2.2
+ * bootstrap-tooltip.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
@@ -38,19 +38,27 @@
, init: function (type, element, options) {
var eventIn
, eventOut
+ , triggers
+ , trigger
+ , i
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
- if (this.options.trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (this.options.trigger != 'manual') {
- eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
- eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ triggers = this.options.trigger.split(' ')
+
+ for (i = triggers.length; i--;) {
+ trigger = triggers[i]
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
+ eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
}
this.options.selector ?
@@ -59,7 +67,7 @@
}
, getOptions: function (options) {
- options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
+ options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
if (options.delay && typeof options.delay == 'number') {
options.delay = {
@@ -97,14 +105,16 @@
, show: function () {
var $tip
- , inside
, pos
, actualWidth
, actualHeight
, placement
, tp
+ , e = $.Event('show')
if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
$tip = this.tip()
this.setContent()
@@ -116,19 +126,18 @@
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
- inside = /in/.test(placement)
-
$tip
.detach()
.css({ top: 0, left: 0, display: 'block' })
- .insertAfter(this.$element)
- pos = this.getPosition(inside)
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ pos = this.getPosition()
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
- switch (inside ? placement.split(' ')[1] : placement) {
+ switch (placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
@@ -143,11 +152,56 @@
break
}
- $tip
- .offset(tp)
- .addClass(placement)
- .addClass('in')
+ this.applyPlacement(tp, placement)
+ this.$element.trigger('shown')
+ }
+ }
+
+ , applyPlacement: function(offset, placement){
+ var $tip = this.tip()
+ , width = $tip[0].offsetWidth
+ , height = $tip[0].offsetHeight
+ , actualWidth
+ , actualHeight
+ , delta
+ , replace
+
+ $tip
+ .offset(offset)
+ .addClass(placement)
+ .addClass('in')
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
+ replace = true
}
+
+ if (placement == 'bottom' || placement == 'top') {
+ delta = 0
+
+ if (offset.left < 0){
+ delta = offset.left * -2
+ offset.left = 0
+ $tip.offset(offset)
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+ }
+
+ this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+ } else {
+ this.replaceArrow(actualHeight - height, actualHeight, 'top')
+ }
+
+ if (replace) $tip.offset(offset)
+ }
+
+ , replaceArrow: function(delta, dimension, position){
+ this
+ .arrow()
+ .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
}
, setContent: function () {
@@ -161,6 +215,10 @@
, hide: function () {
var that = this
, $tip = this.tip()
+ , e = $.Event('hide')
+
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
$tip.removeClass('in')
@@ -179,13 +237,15 @@
removeWithAnimation() :
$tip.detach()
+ this.$element.trigger('hidden')
+
return this
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
}
}
@@ -193,11 +253,12 @@
return this.getTitle()
}
- , getPosition: function (inside) {
- return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
- width: this.$element[0].offsetWidth
- , height: this.$element[0].offsetHeight
- })
+ , getPosition: function () {
+ var el = this.$element[0]
+ return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+ width: el.offsetWidth
+ , height: el.offsetHeight
+ }, this.$element.offset())
}
, getTitle: function () {
@@ -215,6 +276,10 @@
return this.$tip = this.$tip || $(this.options.template)
}
+ , arrow: function(){
+ return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
+ }
+
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
@@ -236,8 +301,8 @@
}
, toggle: function (e) {
- var self = $(e.currentTarget)[this.type](this._options).data(this.type)
- self[self.tip().hasClass('in') ? 'hide' : 'show']()
+ var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
+ self.tip().hasClass('in') ? self.hide() : self.show()
}
, destroy: function () {
@@ -269,10 +334,11 @@
, placement: 'top'
, selector: false
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
- , trigger: 'hover'
+ , trigger: 'hover focus'
, title: ''
, delay: 0
, html: false
+ , container: false
}
@@ -284,4 +350,4 @@
return this
}
-}(window.jQuery);
+}(window.jQuery);
View
2  vendor/assets/javascripts/bootstrap/bootstrap-transition.js
@@ -1,5 +1,5 @@
/* ===================================================
- * bootstrap-transition.js v2.2.2
+ * bootstrap-transition.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
View
20 vendor/assets/javascripts/bootstrap/bootstrap-typeahead.js
@@ -1,5 +1,5 @@
/* =============================================================
- * bootstrap-typeahead.js v2.2.2
+ * bootstrap-typeahead.js v2.3.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -172,6 +172,7 @@
, listen: function () {
this.$element
+ .on('focus', $.proxy(this.focus, this))
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
@@ -183,6 +184,7 @@
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
+ .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
}
, eventSupported: function(eventName) {
@@ -256,22 +258,33 @@
e.preventDefault()
}
+ , focus: function (e) {
+ this.focused = true
+ }
+
, blur: function (e) {
- var that = this
- setTimeout(function () { that.hide() }, 150)
+ this.focused = false
+ if (!this.mousedover && this.shown) this.hide()
}
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
+ this.$element.focus()
}
, mouseenter: function (e) {
+ this.mousedover = true
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
+ , mouseleave: function (e) {
+ this.mousedover = false
+ if (!this.focused && this.shown) this.hide()
+ }
+
}
@@ -316,7 +329,6 @@
$(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
- e.preventDefault()
$this.typeahead($this.data())
})
View
1,120 vendor/assets/javascripts/jqplugins/select2.js 100755 → 100644
@@ -1,17 +1,23 @@
/*
- Copyright 2012 Igor Vaynberg
+Copyright 2012 Igor Vaynberg
- Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
+Version: 3.3.1 Timestamp: Wed Feb 20 09:57:22 PST 2013
- 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:
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
- http://www.apache.org/licenses/LICENSE-2.0
+You may obtain a copy of the Apache License and the GPL License at:
- 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.
- */
+ http://www.apache.org/licenses/LICENSE-2.0
+ http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the
+Apache License or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
+the specific language governing permissions and limitations under the Apache License and the GPL License.
+*/
(function ($) {
if(typeof $.fn.each2 == "undefined"){
$.fn.extend({
@@ -40,7 +46,8 @@
return;
}
- var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer;
+ var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
+ lastMousePosition, $document;
KEY = {
TAB: 9,
@@ -90,32 +97,20 @@
}
};
+ $document = $(document);
+
nextUid=(function() { var counter=1; return function() { return counter++; }; }());
function indexOf(value, array) {
- var i = 0, l = array.length, v;
-
- if (typeof value === "undefined") {
- return -1;
- }
-
- if (value.constructor === String) {
- for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i;
- } else {
- for (; i < l; i = i + 1) {
- v = array[i];
- if (v.constructor === String) {
- if (v.localeCompare(value) === 0) return i;
- } else {
- if (v === value) return i;
- }
- }
+ var i = 0, l = array.length;
+ for (; i < l; i = i + 1) {
+ if (equal(value, array[i])) return i;
}
return -1;
}
/**
- * Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used
+ * Compares equality of a and b
* @param a
* @param b
*/
@@ -123,8 +118,8 @@
if (a === b) return true;
if (a === undefined || b === undefined) return false;
if (a === null || b === null) return false;
- if (a.constructor === String) return a.localeCompare(b) === 0;
- if (b.constructor === String) return b.localeCompare(a) === 0;
+ if (a.constructor === String) return a === b+'';
+ if (b.constructor === String) return b === a+'';
return false;
}
@@ -143,7 +138,7 @@
}
function getSideBorderPadding(element) {
- return element.outerWidth() - element.width();
+ return element.outerWidth(false) - element.width();
}
function installKeyUpChangeEvent(element) {
@@ -162,8 +157,8 @@
});
}
- $(document).delegate("body", "mousemove", function (e) {
- $.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY});
+ $document.bind("mousemove", function (e) {
+ lastMousePosition = {x: e.pageX, y: e.pageY};
});
/**
@@ -174,7 +169,7 @@
*/
function installFilteredMouseMove(element) {
element.bind("mousemove", function (e) {
- var lastpos = $.data(document, "select2-lastpos");
+ var lastpos = lastMousePosition;
if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
$(e.target).trigger("mousemove-filtered", e);
}
@@ -223,15 +218,47 @@
});
}
+ function focus($el) {
+ if ($el[0] === document.activeElement) return;
+
+ /* set the focus in a 0 timeout - that way the focus is set after the processing
+ of the current event has finished - which seems like the only reliable way
+ to set focus */
+ window.setTimeout(function() {
+ var el=$el[0], pos=$el.val().length, range;
+
+ $el.focus();
+
+ /* after the focus is set move the caret to the end, necessary when we val()
+ just before setting focus */
+ if(el.setSelectionRange)
+ {
+ el.setSelectionRange(pos, pos);
+ }
+ else if (el.createTextRange) {
+ range = el.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', pos);
+ range.moveStart('character', pos);
+ range.select();
+ }
+
+ }, 0);
+ }
+
function killEvent(event) {
event.preventDefault();
event.stopPropagation();
}
+ function killEventImmediately(event) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
function measureTextWidth(e) {
if (!sizer){
var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
- sizer = $("<div></div>").css({
+ sizer = $(document.createElement("div")).css({
position: "absolute",
left: "-10000px",
top: "-10000px",
@@ -244,26 +271,53 @@
textTransform: style.textTransform,
whiteSpace: "nowrap"
});
+ sizer.attr("class","select2-sizer");
$("body").append(sizer);
}
sizer.text(e.val());
return sizer.width();
}
- function markMatch(text, term, markup) {
+ function syncCssClasses(dest, src, adapter) {
+ var classes, replacements = [], adapted;
+
+ classes = dest.attr("class");
+ if (typeof classes === "string") {
+ $(classes.split(" ")).each2(function() {
+ if (this.indexOf("select2-") === 0) {
+ replacements.push(this);
+ }
+ });
+ }
+ classes = src.attr("class");
+ if (typeof classes === "string") {
+ $(classes.split(" ")).each2(function() {
+ if (this.indexOf("select2-") !== 0) {
+ adapted = adapter(this);
+ if (typeof adapted === "string" && adapted.length > 0) {
+ replacements.push(this);
+ }
+ }
+ });
+ }
+ dest.attr("class", replacements.join(" "));
+ }
+
+
+ function markMatch(text, term, markup, escapeMarkup) {
var match=text.toUpperCase().indexOf(term.toUpperCase()),
tl=term.length;
if (match<0) {
- markup.push(text);
+ markup.push(escapeMarkup(text));
return;
}
- markup.push(text.substring(0, match));
+ markup.push(escapeMarkup(text.substring(0, match)));
markup.push("<span class='select2-match'>");
- markup.push(text.substring(match, match + tl));
+ markup.push(escapeMarkup(text.substring(match, match + tl)));
markup.push("</span>");
- markup.push(text.substring(match + tl, text.length));
+ markup.push(escapeMarkup(text.substring(match + tl, text.length)));
}
/**
@@ -286,7 +340,9 @@
var timeout, // current scheduled but not yet executed request
requestSequence = 0, // sequence used to drop out-of-order responses
handler = null,
- quietMillis = options.quietMillis || 100;
+ quietMillis = options.quietMillis || 100,
+ ajaxUrl = options.url,
+ self = this;
return function (query) {
window.clearTimeout(timeout);
@@ -294,29 +350,40 @@
requestSequence += 1; // increment the sequence
var requestNumber = requestSequence, // this request's sequence number
data = options.data, // ajax data function
+ url = ajaxUrl, // ajax url string or function
transport = options.transport || $.ajax,
- traditional = options.traditional || false,
- type = options.type || 'GET'; // set type of request (GET or POST)
+ type = options.type || 'GET', // set type of request (GET or POST)
+ params = {};
- data = data.call(this, query.term, query.page, query.context);
+ data = data ? data.call(self, query.term, query.page, query.context) : null;
+ url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
if( null !== handler) { handler.abort(); }
- handler = transport.call(null, {
- url: options.url,
+ if (options.params) {
+ if ($.isFunction(options.params)) {
+ $.extend(params, options.params.call(self));
+ } else {
+ $.extend(params, options.params);
+ }
+ }
+
+ $.extend(params, {
+ url: url,
dataType: options.dataType,
data: data,
type: type,
- traditional: traditional,
+ cache: false,
success: function (data) {
if (requestNumber < requestSequence) {
return;
}
- // TODO 3.0 - replace query.page with query so users have access to term, page, etc.
+ // TODO - replace query.page with query so users have access to term, page, etc.
var results = options.results(data, query.page);
query.callback(results);
}
});
+ handler = transport.call(self, params);
}, quietMillis);
};
}
@@ -338,22 +405,33 @@
function local(options) {
var data = options, // data elements
dataText,
+ tmp,
text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
- if (!$.isArray(data)) {
- text = data.text;
+ if ($.isArray(data)) {
+ tmp = data;
+ data = { results: tmp };
+ }
+
+ if ($.isFunction(data) === false) {
+ tmp = data;
+ data = function() { return tmp; };
+ }
+
+ var dataItem = data();
+ if (dataItem.text) {
+ text = dataItem.text;
// if text is not a function we assume it to be a key name
if (!$.isFunction(text)) {
- dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
- text = function (item) { return item[dataText]; };
+ dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
+ text = function (item) { return item[dataText]; };
}
- data = data.results;
}
return function (query) {
var t = query.term, filtered = { results: [] }, process;
if (t === "") {
- query.callback({results: data});
+ query.callback(data());
return;
}
@@ -367,34 +445,27 @@
}
group.children=[];
$(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
- if (group.children.length) {
+ if (group.children.length || query.matcher(t, text(group), datum)) {
collection.push(group);
}
} else {
- if (query.matcher(t, text(datum))) {
+ if (query.matcher(t, text(datum), datum)) {
collection.push(datum);
}
}
};
- $(data).each2(function(i, datum) { process(datum, filtered.results); });
+ $(data().results).each2(function(i, datum) { process(datum, filtered.results); });
query.callback(filtered);
};
}
// TODO javadoc
function tags(data) {
- // TODO even for a function we should probably return a wrapper that does the same object/string check as
- // the function for arrays. otherwise only functions that return objects are supported.
- if ($.isFunction(data)) {
- return data;
- }
-
- // if not a function we assume it to be an array
-
+ var isFunc = $.isFunction(data);
return function (query) {
var t = query.term, filtered = {results: []};
- $(data).each(function () {
+ $(isFunc ? data() : data).each(function () {
var isObject = this.text !== undefined,
text = isObject ? this.text : this;
if (t === "" || query.matcher(t, text)) {
@@ -485,39 +556,10 @@
}
}
- if (original.localeCompare(input) != 0) return input;
+ if (original!==input) return input;
}
/**
- * blurs any Select2 container that has focus when an element outside them was clicked or received focus
- *
- * also takes care of clicks on label tags that point to the source element
- */
- $(document).ready(function () {
- $(document).delegate("body", "mousedown touchend", function (e) {
- var target = $(e.target).closest("div.select2-container").get(0), attr;
- if (target) {
- $(document).find("div.select2-container-active").each(function () {
- if (this !== target) $(this).data("select2").blur();
- });
- } else {
- target = $(e.target).closest("div.select2-drop").get(0);
- $(document).find("div.select2-drop-active").each(function () {
- if (this !== target) $(this).data("select2").blur();
- });
- }
-
- target=$(e.target);
- attr = target.attr("for");
- if ("LABEL" === e.target.tagName && attr && attr.length > 0) {
- target = $("#"+attr);
- target = target.data("select2");
- if (target !== undefined) { target.focus(); e.preventDefault();}
- }
- });
- });
-
- /**
* Creates a new class
*
* @param superClass
@@ -544,7 +586,7 @@
// abstract
init: function (opts) {
- var results, search, resultsSelector = ".select2-results";
+ var results, search, resultsSelector = ".select2-results", mask;
// prepare options
this.opts = opts = this.prepareOpts(opts);
@@ -567,17 +609,19 @@
// cache the body so future lookups are cheap
this.body = thunk(function() { return opts.element.closest("body"); });
- if (opts.element.attr("class") !== undefined) {
- this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, ''));
- }
+ syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
this.container.css(evaluate(opts.containerCss));
this.container.addClass(evaluate(opts.containerCssClass));
+ this.elementTabIndex = this.opts.element.attr("tabIndex");
+
// swap container for the element
this.opts.element
.data("select2", this)
- .hide()
+ .addClass("select2-offscreen")
+ .bind("focus.select2", function() { $(this).select2("focus"); })
+ .attr("tabIndex", "-1")
.before(this.container);
this.container.data("select2", this);
@@ -588,17 +632,16 @@
this.results = results = this.container.find(resultsSelector);
this.search = search = this.container.find("input.select2-input");
- search.attr("tabIndex", this.opts.element.attr("tabIndex"));
+ search.attr("tabIndex", this.elementTabIndex);
this.resultsPage = 0;
this.context = null;
// initialize the container
this.initContainer();
- this.initContainerWidth();
installFilteredMouseMove(this.results);
- this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
+ this.dropdown.delegate(resultsSelector, "mousemove-filtered touchstart touchmove touchend", this.bind(this.highlightUnderEvent));
installDebouncedScroll(80, this.results);
this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
@@ -618,18 +661,15 @@
}
installKeyUpChangeEvent(search);
- search.bind("keyup-change", this.bind(this.updateResults));
- search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); });
+ search.bind("keyup-change input paste", this.bind(this.updateResults));
+ search.bind("focus", function () { search.addClass("select2-focused"); });
search.bind("blur", function () { search.removeClass("select2-focused");});
this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) {
- if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) {
+ if ($(e.target).closest(".select2-result-selectable").length > 0) {
this.highlightUnderEvent(e);
this.selectHighlighted(e);
- } else {
- this.focusSearch();
}
- killEvent(e);
}));
// trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
@@ -652,12 +692,18 @@
// abstract
destroy: function () {
var select2 = this.opts.element.data("select2");
+
+ if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
+
if (select2 !== undefined) {
+
select2.container.remove();
select2.dropdown.remove();
select2.opts.element
+ .removeClass("select2-offscreen")
.removeData("select2")
.unbind(".select2")
+ .attr({"tabIndex": this.elementTabIndex})
.show();
}
},
@@ -687,26 +733,33 @@
populate=function(results, container, depth) {
- var i, l, result, selectable, compound, node, label, innerContainer, formatted;
+ var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
+
+ results = opts.sortResults(results, container, query);
+
for (i = 0, l = results.length; i < l; i = i + 1) {
result=results[i];
- selectable=id(result) !== undefined;
+
+ disabled = (result.disabled === true);
+ selectable = (!disabled) && (id(result) !== undefined);
+
compound=result.children && result.children.length > 0;
node=$("<li></li>");
node.addClass("select2-results-dept-"+depth);
node.addClass("select2-result");
node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
+ if (disabled) { node.addClass("select2-disabled"); }
if (compound) { node.addClass("select2-result-with-children"); }
node.addClass(self.opts.formatResultCssClass(result));
- label=$("<div></div>");
+ label=$(document.createElement("div"));
label.addClass("select2-result-label");
- formatted=opts.formatResult(result, label, query);
+ formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
if (formatted!==undefined) {
- label.html(self.opts.escapeMarkup(formatted));
+ label.html(formatted);
}
node.append(label);
@@ -733,6 +786,13 @@
opts.id = function (e) { return e[idKey]; };
}
+ if ($.isArray(opts.element.data("select2Tags"))) {
+ if ("tags" in opts) {
+ throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
+ }
+ opts.tags=opts.element.attr("data-select2-tags");
+ }
+
if (select) {
opts.query = this.bind(function (query) {
var data = { results: [], more: false },
@@ -743,7 +803,7 @@
var group;
if (element.is("option")) {
if (query.matcher(term, element.text(), element)) {
- collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class")});
+ collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class"), disabled: equal(element.attr("disabled"), "disabled") });
}
} else if (element.is("optgroup")) {
group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")};
@@ -770,31 +830,36 @@
});
// this is needed because inside val() we construct choices from options and there id is hardcoded
opts.id=function(e) { return e.id; };
- opts.formatResultCssClass = function(data) { return data.css; }
+ opts.formatResultCssClass = function(data) { return data.css; };
} else {
if (!("query" in opts)) {
+
if ("ajax" in opts) {
ajaxUrl = opts.element.data("ajax-url");
if (ajaxUrl && ajaxUrl.length > 0) {
opts.ajax.url = ajaxUrl;
}
- opts.query = ajax(opts.ajax);
+ opts.query = ajax.call(opts.element, opts.ajax);
} else if ("data" in opts) {
opts.query = local(opts.data);
} else if ("tags" in opts) {
opts.query = tags(opts.tags);
- opts.createSearchChoice = function (term) { return {id: term, text: term}; };
- opts.initSelection = function (element, callback) {
- var data = [];
- $(splitVal(element.val(), opts.separator)).each(function () {
- var id = this, text = this, tags=opts.tags;
- if ($.isFunction(tags)) tags=tags();
- $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } });
- data.push({id: id, text: text});
- });
-
- callback(data);
- };
+ if (opts.createSearchChoice === undefined) {
+ opts.createSearchChoice = function (term) { return {id: term, text: term}; };
+ }
+ if (opts.initSelection === undefined) {
+ opts.initSelection = function (element, callback) {
+ var data = [];
+ $(splitVal(element.val(), opts.separator)).each(function () {
+ var id = this, text = this, tags=opts.tags;
+ if ($.isFunction(tags)) tags=tags();
+ $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } });
+ data.push({id: id, text: text});
+ });
+
+ callback(data);
+ };
+ }
}
}
}
@@ -810,11 +875,52 @@
*/
// abstract
monitorSource: function () {
- this.opts.element.bind("change.select2", this.bind(function (e) {
+ var el = this.opts.element, sync;
+
+ el.bind("change.select2", this.bind(function (e) {
if (this.opts.element.data("select2-change-triggered") !== true) {
this.initSelection();
}
}));
+
+ sync = this.bind(function () {
+
+ var enabled, readonly, self = this;
+
+ // sync enabled state
+
+ enabled = this.opts.element.attr("disabled") !== "disabled";
+ readonly = this.opts.element.attr("readonly") === "readonly";
+
+ enabled = enabled && !readonly;
+
+ if (this.enabled !== enabled) {
+ if (enabled) {
+ this.enable();
+ } else {
+ this.disable();
+ }
+ }
+
+
+ syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
+ this.container.addClass(evaluate(this.opts.containerCssClass));
+
+ syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
+ this.dropdown.addClass(evaluate(this.opts.dropdownCssClass));
+
+ });
+
+ // mozilla and IE
+ el.bind("propertychange.select2 DOMAttrModified.select2", sync);
+ // safari and chrome
+ if (typeof WebKitMutationObserver !== "undefined") {
+ if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
+ this.propertyObserver = new WebKitMutationObserver(function (mutations) {
+ mutations.forEach(sync);
+ });
+ this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
+ }
},
/**
@@ -840,13 +946,13 @@
this.opts.element.blur();
},
-
// abstract
enable: function() {
if (this.enabled) return;
this.enabled=true;
this.container.removeClass("select2-container-disabled");
+ this.opts.element.removeAttr("disabled");
},
// abstract
@@ -857,6 +963,7 @@
this.enabled=false;
this.container.addClass("select2-container-disabled");
+ this.opts.element.attr("disabled", "disabled");
},
// abstract
@@ -867,21 +974,24 @@
// abstract
positionDropdown: function() {
var offset = this.container.offset(),
- height = this.container.outerHeight(),
- width = this.container.outerWidth(),
- dropHeight = this.dropdown.outerHeight(),
- viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight,
+ height = this.container.outerHeight(false),
+ width = this.container.outerWidth(false),
+ dropHeight = this.dropdown.outerHeight(false),
+ viewPortRight = $(window).scrollLeft() + $(window).width(),
+ viewportBottom = $(window).scrollTop() + $(window).height(),
dropTop = offset.top + height,
dropLeft = offset.left,
enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),
+ dropWidth = this.dropdown.outerWidth(false),
+ enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight,
aboveNow = this.dropdown.hasClass("select2-drop-above"),
bodyOffset,
above,
css;
- // console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
- // console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);
+ //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
+ //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);
// fix positioning when body has an offset and is not position: static
@@ -901,6 +1011,10 @@
if (!enoughRoomBelow && enoughRoomAbove) above = true;
}
+ if (!enoughRoomOnRight) {
+ dropLeft = offset.left + width - dropWidth;
+ }
+
if (above) {
dropTop = offset.top - dropHeight;
this.container.addClass("select2-drop-above");
@@ -926,7 +1040,7 @@
if (this.opened()) return false;
- event = $.Event("open");
+ event = $.Event("opening");
this.opts.element.trigger(event);
return !event.isDefaultPrevented();
},
@@ -959,46 +1073,74 @@
*/
// abstract
opening: function() {
- var cid = this.containerId, selector = this.containerSelector,
- scroll = "scroll." + cid, resize = "resize." + cid;
-
- this.container.parents().each(function() {
- $(this).bind(scroll, function() {
- var s2 = $(selector);
- if (s2.length == 0) {
- $(this).unbind(scroll);
- }
- s2.select2("close");
- });
- });
-
- $(window).bind(resize, function() {
- var s2 = $(selector);
- if (s2.length == 0) {
- $(window).unbind(resize);
- }
- s2.select2("close");
- });
+ var cid = this.containerId,
+ scroll = "scroll." + cid,
+ resize = "resize."+cid,
+ orient = "orientationchange."+cid,
+ mask;
this.clearDropdownAlignmentPreference();
- if (this.search.val() === " ") { this.search.val(""); }
-
this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
- this.updateResults(true);
if(this.dropdown[0] !== this.body().children().last()[0]) {
this.dropdown.detach().appendTo(this.body());
}
- this.dropdown.show();
+ this.updateResults(true);
+ // create the dropdown mask if doesnt already exist
+ mask = $("#select2-drop-mask");
+ if (mask.length == 0) {
+ mask = $(document.createElement("div"));
+ mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
+ mask.hide();
+ mask.appendTo(this.body());
+ mask.bind("mousedown touchstart", function (e) {
+ var dropdown = $("#select2-drop"), self;
+ if (dropdown.length > 0) {
+ self=dropdown.data("select2");
+ if (self.opts.selectOnBlur) {
+ self.selectHighlighted({noFocus: true});
+ }
+ self.close();
+ }
+ });
+ }
+
+ // ensure the mask is always right before the dropdown
+ if (this.dropdown.prev()[0] !== mask[0]) {
+ this.dropdown.before(mask);
+ }
+
+ // move the global id to the correct dropdown
+ $("#select2-drop").removeAttr("id");
+ this.dropdown.attr("id", "select2-drop");
+
+ // show the elements
+ mask.css({
+ width: document.documentElement.scrollWidth,
+ height: document.documentElement.scrollHeight});
+ mask.show();
+ this.dropdown.show();
this.positionDropdown();
- this.dropdown.addClass("select2-drop-active");
+ this.dropdown.addClass("select2-drop-active");
this.ensureHighlightVisible();
+ // attach listeners to events that can change the position of the container and thus require
+ // the position of the dropdown to be updated as well so it does not come unglued from the container
+ var that = this;
+ this.container.parents().add(window).each(function () {
+ $(this).bind(resize+" "+scroll+" "+orient, function (e) {
+ $("#select2-drop-mask").css({
+ width:document.documentElement.scrollWidth,
+ height:document.documentElement.scrollHeight});
+ that.positionDropdown();
+ });
+ });
+
this.focusSearch();
},
@@ -1006,17 +1148,20 @@
close: function () {
if (!this.opened()) return;
- var self = this;
+ var cid = this.containerId,
+ scroll = "scroll." + cid,
+ resize = "resize."+cid,
+ orient = "orientationchange."+cid;
- this.container.parents().each(function() {
- $(this).unbind("scroll." + self.containerId);
- });
- $(window).unbind("resize." + this.containerId);
+ // unbind event listeners
+ this.container.parents().add(window).each(function () { $(this).unbind(scroll).unbind(resize).unbind(orient); });
this.clearDropdownAlignmentPreference();
+ $("#select2-drop-mask").hide();
+ this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
this.dropdown.hide();
- this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
+ this.container.removeClass("select2-dropdown-open");
this.results.empty();
this.clearSearch();
@@ -1028,6 +1173,11 @@
},
+ //abstract
+ getMaximumSelectionSize: function() {
+ return evaluate(this.opts.maximumSelectionSize);
+ },
+
// abstract
ensureHighlightVisible: function () {
var results = this.results, children, index, child, hb, rb, y, more;
@@ -1046,41 +1196,47 @@
return;
}
- children = results.find(".select2-result-selectable");
+ children = this.findHighlightableChoices();
child = $(children[index]);
- hb = child.offset().top + child.outerHeight();
+ hb = child.offset().top + child.outerHeight(true);
// if this is the last child lets also make sure select2-more-results is visible
if (index === children.length - 1) {
more = results.find("li.select2-more-results");
if (more.length > 0) {
- hb = more.offset().top + more.outerHeight();
+ hb = more.offset().top + more.outerHeight(true);
}
}
- rb = results.offset().top + results.outerHeight();
+ rb = results.offset().top + results.outerHeight(true);
if (hb > rb) {
results.scrollTop(results.scrollTop() + (hb - rb));
}
y = child.offset().top - results.offset().top;
// make sure the top of the element is visible
- if (y < 0) {
+ if (y < 0 && child.css('display') != 'none' ) {
results.scrollTop(results.scrollTop() + y); // y is negative
}
},
// abstract
+ findHighlightableChoices: function() {
+ var h=this.results.find(".select2-result-selectable:not(.select2-selected):not(.select2-disabled)");
+ return this.results.find(".select2-result-selectable:not(.select2-selected):not(.select2-disabled)");
+ },
+
+ // abstract
moveHighlight: function (delta) {
- var choices = this.results.find(".select2-result-selectable"),
+ var choices = this.findHighlightableChoices(),
index = this.highlight();
while (index > -1 && index < choices.length) {
index += delta;
var choice = $(choices[index]);
- if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled")) {
+ if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
this.highlight(index);
break;
}
@@ -1089,7 +1245,9 @@
// abstract
highlight: function (index) {
- var choices = this.results.find(".select2-result-selectable").not(".select2-disabled");
+ var choices = this.findHighlightableChoices(),
+ choice,
+ data;
if (arguments.length === 0) {
return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
@@ -1098,23 +1256,29 @@
if (index >= choices.length) index = choices.length - 1;
if (index < 0) index = 0;
- choices.removeClass("select2-highlighted");
+ this.results.find(".select2-highlighted").removeClass("select2-highlighted");
+
+ choice = $(choices[index]);
+ choice.addClass("select2-highlighted");
- $(choices[index]).addClass("select2-highlighted");
this.ensureHighlightVisible();
+ data = choice.data("select2-data");
+ if (data) {
+ this.opts.element.trigger({ type: "highlight", val: this.id(data), choice: data });
+ }
},
// abstract
countSelectableResults: function() {
- return this.results.find(".select2-result-selectable").not(".select2-disabled").length;
+ return this.findHighlightableChoices().length;
},
// abstract
highlightUnderEvent: function (event) {
var el = $(event.target).closest(".select2-result-selectable");
if (el.length > 0 && !el.is(".select2-highlighted")) {
- var choices = this.results.find('.select2-result-selectable');
+ var choices = this.findHighlightableChoices();
this.highlight(choices.index(el));
} else if (el.length == 0) {
// if we are over an unselectable item remove al highlights
@@ -1136,9 +1300,10 @@
if (more.length === 0) return;
below = more.offset().top - results.offset().top - results.height();
- if (below <= 0) {
+ if (below <= this.opts.loadMorePadding) {
more.addClass("select2-active");
this.opts.query({
+ element: this.opts.element,
term: term,
page: page,
context: context,
@@ -1159,6 +1324,7 @@
}
self.positionDropdown();
self.resultsPage = page;
+ self.context = data.context;
})});
}
},
@@ -1191,26 +1357,40 @@
}
function render(html) {
- results.html(self.opts.escapeMarkup(html));
+ results.html(html);
postRender();
}
- if (opts.maximumSelectionSize >=1) {
+ var maxSelSize = this.getMaximumSelectionSize();
+ if (maxSelSize >=1) {
data = this.data();
- if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
- render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "</li>");
+ if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
+ render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(maxSelSize) + "</li>");
return;
}
}
- if (search.val().length < opts.minimumInputLength && checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
- render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
+ if (search.val().length < opts.minimumInputLength) {
+ if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
+ render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
+ } else {
+ render("");
+ }
return;
}
- else {
+ else if (opts.formatSearching() && initial===true) {
render("<li class='select2-searching'>" + opts.formatSearching() + "</li>");
}
+ if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
+ if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
+ render("<li class='select2-no-results'>" + opts.formatInputTooLong(search.val(), opts.maximumInputLength) + "</li>");
+ } else {
+ render("");
+ }
+ return;
+ }
+
// give the tokenizer a chance to pre-process the input
input = this.tokenize();
if (input != undefined && input != null) {
@@ -1218,7 +1398,9 @@
}
this.resultsPage = 1;
+
opts.query({
+ element: opts.element,
term: search.val(),
page: this.resultsPage,
context: null,
@@ -1231,7 +1413,6 @@
// save context, if any
this.context = (data.context===undefined) ? null : data.context;
-
// create a default choice and prepend it to the list
if (this.opts.createSearchChoice && search.val() !== "") {
def = this.opts.createSearchChoice.call(null, search.val(), data.results);
@@ -1271,9 +1452,12 @@
// abstract
blur: function () {
+ // if selectOnBlur == true, select the currently highlighted option
+ if (this.opts.selectOnBlur)
+ this.selectHighlighted({noFocus: true});
+
this.close();
this.container.removeClass("select2-container-active");
- this.dropdown.removeClass("select2-drop-active");
// synonymous to .is(':focus'), which is available in jquery >= 1.6
if (this.search[0] === document.activeElement) { this.search.blur(); }
this.clearSearch();
@@ -1282,29 +1466,18 @@
// abstract
focusSearch: function () {
- // need to do it here as well as in timeout so it works in IE
- this.search.show();
- this.search.focus();
-
- /* we do this in a timeout so that current event processing can complete before this code is executed.
- this makes sure the search field is focussed even if the current event would blur it */
- window.setTimeout(this.bind(function () {
- // reset the value so IE places the cursor at the end of the input box
- this.search.show();
- this.search.focus();
- this.search.val(this.search.val());
- }), 10);
+ focus(this.search);
},
// abstract
- selectHighlighted: function () {
+ selectHighlighted: function (options) {
var index=this.highlight(),
- highlighted=this.results.find(".select2-highlighted").not(".select2-disabled"),
- data = highlighted.closest('.select2-result-selectable').data("select2-data");
+ highlighted=this.results.find(".select2-highlighted"),
+ data = highlighted.closest('.select2-result').data("select2-data");
+
if (data) {
- highlighted.addClass("select2-disabled");
this.highlight(index);
- this.onSelect(data);
+ this.onSelect(data, options);
}
},
@@ -1330,7 +1503,7 @@
if (this.opts.width === "off") {
return null;
} else if (this.opts.width === "element"){
- return this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px';
+ return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
} else if (this.opts.width === "copy" || this.opts.width === "resolve") {
// check if there is inline style on the element that contains width
style = this.opts.element.attr('style');
@@ -1351,7 +1524,7 @@
if (style.indexOf("%") > 0) return style;
// finally, fallback on the calculated width of the element
- return (this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px');
+ return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
}
return null;
@@ -1364,7 +1537,7 @@
var width = resolveContainerWidth.call(this);
if (width !== null) {
- this.container.attr("style", "width: "+width);
+ this.container.css("width", width);
}
}
});
@@ -1374,14 +1547,15 @@
// single
createContainer: function () {
- var container = $("<div></div>", {
+ var container = $(document.createElement("div")).attr({
"class": "select2-container"
}).html([
- " <a href='#' onclick='return false;' class='select2-choice'>",
+ "<a href='javascript:void(0)' onclick='return false;' class='select2-choice' tabindex='-1'>",
" <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
" <div><b></b></div>" ,
"</a>",
- " <div class='select2-drop select2-offscreen'>" ,
+ "<input class='select2-focusser select2-offscreen' type='text'/>",
+ "<div class='select2-drop' style='display:none'>" ,
" <div class='select2-search'>" ,
" <input type='text' autocomplete='off' class='select2-input'/>" ,
" </div>" ,
@@ -1392,34 +1566,59 @@
},
// single
+ disable: function() {
+ if (!this.enabled) return;
+
+ this.parent.disable.apply(this, arguments);
+
+ this.focusser.attr("disabled", "disabled");
+ },
+
+ // single
+ enable: function() {
+ if (this.enabled) return;
+
+ this.parent.enable.apply(this, arguments);
+
+ this.focusser.removeAttr("disabled");
+ },
+
+ // single
opening: function () {
- this.search.show();
this.parent.opening.apply(this, arguments);
- this.dropdown.removeClass("select2-offscreen");
+ this.focusser.attr("disabled", "disabled");
+
+ this.opts.element.trigger($.Event("open"));
},
// single
close: function () {
if (!this.opened()) return;
this.parent.close.apply(this, arguments);
- this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show();
+ this.focusser.removeAttr("disabled");
+ focus(this.focusser);
},
// single
focus: function () {
- this.close();
- this.selection.focus();
+ if (this.opened()) {
+ this.close();
+ } else {
+ this.focusser.removeAttr("disabled");
+ this.focusser.focus();
+ }
},
// single
isFocused: function () {
- return this.selection[0] === document.activeElement;
+ return this.container.hasClass("select2-container-active");
},
// single
cancel: function () {
this.parent.cancel.apply(this, arguments);
- this.selection.focus();
+ this.focusser.removeAttr("disabled");
+ this.focusser.focus();
},
// single
@@ -1430,8 +1629,12 @@
dropdown = this.dropdown,
clickingInside = false;
+ this.showSearch(this.opts.minimumResultsForSearch >= 0);
+
this.selection = selection = container.find(".select2-choice");
+ this.focusser = container.find(".select2-focusser");
+
this.search.bind("keydown", this.bind(function (e) {
if (!this.enabled) return;
@@ -1441,154 +1644,118 @@
return;
}
- if (this.opened()) {
- switch (e.which) {
- case KEY.UP:
- case KEY.DOWN:
- this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
- killEvent(e);
- return;
- case KEY.TAB:
- case KEY.ENTER:
- this.selectHighlighted();
- killEvent(e);
- return;
- case KEY.ESC:
- this.cancel(e);
- killEvent(e);
- return;
- }
- } else {
-
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
+ switch (e.which) {
+ case KEY.UP:
+ case KEY.DOWN:
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+ killEvent(e);
return;
- }
-
- if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
+ case KEY.TAB:
+ case KEY.ENTER:
+ this.selectHighlighted();
+ killEvent(e);
return;
- }
-
- this.open();
-
- if (e.which === KEY.ENTER) {
- // do not propagate the event otherwise we open, and propagate enter which closes
+ case KEY.ESC:
+ this.cancel(e);
+ killEvent(e);
return;
- }
}
}));
- this.search.bind("focus", this.bind(function() {
- this.selection.attr("tabIndex", "-1");
- }));
- this.search.bind("blur", this.bind(function() {
- if (!this.opened()) this.container.removeClass("select2-container-active");
- window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10);
- }));
-
- selection.bind("mousedown", this.bind(function (e) {
- clickingInside = true;
-
- if (this.opened()) {
- this.close();
- this.selection.focus();
- } else if (this.enabled) {
- this.open();
- }
-
- clickingInside = false;
- }));
-
- dropdown.bind("mousedown", this.bind(function() { this.search.focus(); }));
-
- selection.bind("focus", this.bind(function() {
- this.container.addClass("select2-container-active");
- // hide the search so the tab key does not focus on it
- this.search.attr("tabIndex", "-1");
- }));
-
- selection.bind("blur", this.bind(function() {
- if (!this.opened()) {
- this.container.removeClass("select2-container-active");
- }
- window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10);
- }));
-
- selection.bind("keydown", this.bind(function(e) {
+ this.focusser.bind("keydown", this.bind(function (e) {
if (!this.enabled) return;
- if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
- // prevent the page from scrolling
- killEvent(e);
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {