summaryrefslogtreecommitdiff
path: root/src/main/webapp/js/jquery.mcdropdown.js
diff options
context:
space:
mode:
authorLeif Johansson <leifj@sunet.se>2011-07-27 23:07:26 +0200
committerLeif Johansson <leifj@sunet.se>2011-07-27 23:07:26 +0200
commit031f1648f2a1207d0b88ce3e536b9b5163c7024c (patch)
treee5c6fbd534a990ce90a601a5cebb5de0c9f1786a /src/main/webapp/js/jquery.mcdropdown.js
parenta1bc160918abbf07c51f583d63d0074fe0a84ff7 (diff)
new ui and clean out mcdropdown
Diffstat (limited to 'src/main/webapp/js/jquery.mcdropdown.js')
-rw-r--r--src/main/webapp/js/jquery.mcdropdown.js1139
1 files changed, 0 insertions, 1139 deletions
diff --git a/src/main/webapp/js/jquery.mcdropdown.js b/src/main/webapp/js/jquery.mcdropdown.js
deleted file mode 100644
index a99c66b..0000000
--- a/src/main/webapp/js/jquery.mcdropdown.js
+++ /dev/null
@@ -1,1139 +0,0 @@
-(function ($){
-/*!
- * mcDropdown jQuery Plug-in
- *
- * Copyright 2011 Giva, Inc. (http://www.givainc.com/labs/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License 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.
- *
- * Date: 2011-07-25
- * Rev: 1.3.1
- */
- $.fn.mcDropdown = function(list, options) {
- // track the dropdown object
- var dd;
-
- // create a dropdown for each match
- this.each(function() {
- dd = $.data(this, "mcDropdown");
-
- // we're already a dropdown, return a reference to myself
- if( dd ) return false;
-
- new $.mcDropDownMenu(this, list, options);
- });
-
- // return either the dropdown object or the jQuery object reference
- return dd || this;
- };
-
- // set default options
- $.mcDropdown = {
- version: "1.3.1",
- setDefaults: function(options){
- $.extend(defaults, options);
- }
- };
-
- // set the defaults
- var defaults = {
- minRows: 8 // specify the minimum rows before creating a new column
- , maxRows: 25 // specify the maximum rows in a column
- , targetColumnSize: 2 // specify the default target column size (it'll attempt to create this many columns by default, unless the min/max row rules are not being met)
- , openFx: "slideDown" // the fx to use for showing the root menu
- , openSpeed: 250 // the speed of the openFx
- , closeFx: "slideUp" // the fx to use for hiding the root menu
- , closeSpeed: 250 // the speed of the closeFx
- , hoverOverDelay: 200 // the delay before opening a submenu
- , hoverOutDelay: 0 // the delay before closing a submenu
- , showFx: "show" // the fx to use when showing a submenu
- , showSpeed: 0 // the speed of the showFx
- , hideFx: "hide" // the fx to use when closing a submenu
- , hideSpeed: 0 // the speed of the hideFx
- , dropShadow: true // determine whether drop shadows should be shown on the submenus
- , autoHeight: true // always uses the lineHeight options (much faster than calculating height)
- , lineHeight: 19 // the base height of each list item (li) this is normally calculated automatically, but in some cases the value can't be determined and you'll need to set it manually
- , screenPadding: 10 // the padding to use around the border of the screen -- this is used to make sure items stay on the screen
- , allowParentSelect: false // determines if parent items are allowed to be selected (by default only end nodes can be selected)
- , delim: ":" // the delimited to use when showing the display string
- , showACOnEmptyFocus: false // show the autocomplete box on focus when input is empty
- , valueAttr: "rel" // the attribute that contains the value to use in the hidden field
- , click: null // callback that occurs when the user clicks on a menu item
- , select: null // callback that occurs when a value is selected
- , init: null // callback that occurs when the control is fully initialized
- };
-
- // check to see if the browser is IE6
- var isIE6 = ($.browser.version && $.browser.version <= 6);
-
- $.mcDropDownMenu = function(el, list, options){
- var $self, thismenu = this, $list, $divInput, settings, typedText = "", matchesCache, oldCache, $keylist, $keylistiframe, bInput, bDisabled = false;
-
- // create a reference to the dropdown
- $self = $(el);
-
- // is the field and input element
- bInput = $self.is(":input");
-
- // get the settings for this instance
- settings = $.extend({}, defaults, options);
-
- // set the default click behavior
- if( settings.click == null ) {
- settings.click = function (e, dropdown, settings){
- if( this.attr(settings.valueAttr) ){
- dropdown.setValue(this.attr(settings.valueAttr));
- } else {
- dropdown.setValue($(this.parents("li")[0]).attr(settings.valueAttr));
- }
- };
- }
-
- // attach window behaviors
- $(document)
- // Bind a click event to hide all visible menus when the document is clicked
- .bind("click", function(e){
- // get the target that was clicked
- var $target = $(e.target);
- var $ul = $target.parents().filter(function (){ return this === $list[0] || (!!$keylist && $keylist[0] === this); });
- // check to make sure the clicked element was inside the list
- if( $ul.length ){
- var bIsParent = $target.is(".mc_parent");
-
- // if we've clicked a parent item in the autocomplete box, we must adjust the current value
- if( bIsParent && $keylist && $ul[0] === $keylist[0] ){
- updateValue($target.find("> ul > li:first"), false);
- e.stopPropagation();
- return false;
- }
- // check to see if the user can click on parent items
- else if( !settings.allowParentSelect && bIsParent ) return false;
-
- // make sure to hide the parent branch if we're not the root
- if( $target.not(".mc_root") ) hideBranch.apply($target.parent().parent()[0], [e]);
-
- if( settings.click != null && settings.click.apply($target, [e, thismenu, settings]) == false ){
- return false;
- }
- }
-
- // close the menu
- thismenu.closeMenu();
- });
-
- // store a reference to the list, if it's not already a jQuery object make it one
- $list = (((typeof list == "object") && !!list.jquery)) ? list : $(list);
-
- // we need to calculate the visual width for each nested list
- $list
- // move list to body -- this allows us to always calculate the correct position & width of the elements
- .appendTo("body")
- // move the list way off screen
- .css({position: "absolute", top: -10000, left: -10000})
- // find all the ul tags
- .find("ul")
- // add the root ul tag to the array
- .andSelf()
- // make all the nodes visible
- .css("display", "block")
- // loop through each node
- .each(function (){
- var $el = $(this);
- // calculate the width of the element -- using clientWidth is 2x as fast as width()
- $el.data("width", $el[0].clientWidth);
- })
- // now that we've gotten the widths, hide all the lists and move them to x:0, y:0
- .css({top: 0, left: 0, display: "none"});
-
- // mark the root children items
- $list.find("> li").addClass("mc_root");
- // add parent class
- $("li > ul", $list).parent().addClass("mc_parent");
-
- // create the div to wrap everything in
- $divInput = $('<div class="mcdropdown"><a href="#" tabindex="-1"></a><input type="hidden" name="' + (el.name || el.id) + '" id="' + (el.id || el.name) + '" /></div>')
- .appendTo($('<div style="position: relative;"></div>'))
- .parent();
-
- // get a reference to the input element and remove it from the DOM
- var $input = $self.replaceWith($divInput).attr({id: "", name: ""});
- // get a reference to the hidden form field
- var $hidden = $divInput.find(":input");
-
- // put the input element back in the div.mcdropdown layer
- $divInput = $divInput.find(".mcdropdown").prepend($input);
-
- // make a visible copy of the element so we can get the correct sizes, then delete it
- var $divInputClone = $divInput.clone().css({position: "absolute", top: -9999999, left: -999999, visibility: "visible"}).show().appendTo("body");
- var di = {width: $divInputClone.width() - $("a", $divInputClone).width(), height: $divInputClone.outerHeight()}
- $divInputClone.remove();
-
- // store a reference to this link select
- $.data($hidden[0], "mcDropdown", thismenu);
-
- // update the height of the outer relative div, this allows us to
- // correctly anchor the dropdown
- $divInput.parent().height(di.height);
-
- // safari will not get the correct width until after everything has rendered
- if( $.browser.safari ){
- setTimeout(function (){
- $self
- .width($divInput.width() - $("a", $divInput).width());
- }, 100);
- }
-
- // adjust the width of the new input element
- $self
- .width(di.width)
- // make sure we only attach the next events if we're in input element
- .filter(":input")
- // turn autocomplete off
- .attr("autocomplete", "off")
- // add key stroke bindings (IE6 requires keydown)
- .bind("keypress", checkKeypress)
- // prevent user from selecting text
- .bind("mousedown", function (e){ $(this).triggerHandler("focus"); e.stopPropagation(); return false; })
- // disable context menu
- .bind("contextmenu", function (){ return false; })
- // select the text when the cursor is placed in the field
- .bind("focus", onFocus)
- // when the user leaves the text field
- .bind("blur", onBlur);
-
- // IE6 doesn't register certain keypress events, so we must catch them during the keydown event
- if( $.browser.msie || $.browser.safari) $self.bind("keydown", function (e){
- // check to see if a key was pressed that IE6 doesn't trigger a keypress event for
- if( ",8,9,37,38,39,40,".indexOf("," + e.keyCode + ",") > -1 ) return checkKeypress(e);
- });
-
- // attach a click event to the anchor
- $("a", $divInput).bind("click", function (e){
- // if disabled, skip processing
- if( bDisabled ) return false;
- thismenu.openMenu(e);
- return false;
- });
-
- // set the value of the field
- this.setValue = function (value, skipCallback){
- // update the hidden value
- $hidden.val(value);
- // get the display name
- var name = displayString(value);
-
- // run the select callback (some keyboard entry methods will manage this callback manually)
- if( settings.select != null && skipCallback != true ) settings.select.apply(thismenu, [value, name]);
-
- // update the display value and return the jQuery object
- return $self[bInput ? "val" : "text"](name);
- };
-
- // set the default value (but don't run callback)
- if( bInput ) this.setValue($self[0].defaultValue, true);
-
- // get the value of the field (returns array)
- this.getValue = function (value){
- return [$hidden.val(), $self[bInput ? "val" : "text"]()];
- };
-
- // open the menu programmatically
- this.openMenu = function (e){
- // if the menu is open, kill processing
- if( $list.is(":visible") ){
- // on a mouse click, close the menu, otherwise just cancel
- return (!!e) ? thismenu.closeMenu() : false;
- }
-
- function open(){
- // columnize the root list
- columnizeList($list).hide();
- // add the bindings to the menu
- addBindings($list);
-
- // anchor the menu relative parent
- anchorTo($divInput.parent(), $list, true);
-
- // remove existing hover classes, which might exist from keyboard entry
- $list.find(".mc_hover").removeClass("mc_hover");
-
- // show the menu
- $list[settings.openFx](settings.openSpeed, function (){
- // scroll the list into view
- scrollToView($list);
- });
-
- // if the bgIframe exists, use the plug-in
- if( isIE6 && !!$.fn.bgIframe ) $list.bgIframe();
- }
-
- // if this is triggered via an event, just open the menu
- if( e ) open();
- // otherwise we need to open the menu asynchronously to avoid collision with $(document).click event
- else setTimeout(open, 1);
- };
-
- // close the menu programmatically
- this.closeMenu = function (e){
- // hide any open menus
- $list.find("ul:visible").parent().each(function (){
- hideBranch.apply(this);
- });
-
- // remove the bindings
- removeBindings($list);
-
- // close the menu
- $list[settings.closeFx](settings.closeSpeed);
- };
-
- // place focus in the input box
- this.focus = function (){
- $self.focus();
- };
-
- // disable the element
- this.disable = function (status){
- // change the disabled status
- bDisabled = !!status;
-
- $divInput[bDisabled ? "addClass" : "removeClass"]("mcdropdownDisabled");
- $input.attr("disabled", bDisabled);
- };
-
- function getNodeText($el){
- var nodeContent;
- var nContents = $el.contents().filter(function() {
- // remove empty text nodes and comments
- return (this.nodeType == 1) || (this.nodeType == 3 && $.trim(this.nodeValue).length>0);
- });
- // return either an empty string or the node's value
- if (nContents[0] && nContents[0].nodeType == 3) {
- // Text node : take it's value
- nodeContent = nContents[0].nodeValue;
- } else if (nContents[0] && nContents[0].nodeType == 1) {
- // Element node : take the contents
- nodeContent = $(nContents[0]).text();
- } else {
- nodeContent = "";
- }
- return $.trim(nodeContent);
- };
-
- function getTreePath($li){
- if( $li.length == 0 ) return [];
-
- var name = [getNodeText($li)];
- // loop through the parents and get the value
- $li.parents().each(function (){
- var $el = $(this);
- // break when we get to the main list element
- if( this === $list[0] ) return false;
- else if( $el.is("li") ) name.push(getNodeText($el));
- });
-
- // return the display name
- return name.reverse();
- };
-
- function displayValue(value){
- // return the path as an array
- return getTreePath(getListItem(value));
- };
-
- function displayString(value){
- // return the display name
- return displayValue(value).join(settings.delim);
- };
-
- function parseTree($selector){
- var s = [], level = (arguments.length > 1) ? ++arguments[1] : 1;
-
- // loop through all the children and store information about the tree
- $("> li", $selector).each(
- function (){
- // get a reference to the current object
- var $self = $(this);
-
- // look for a ul tag as a direct child
- var $ul = $("> ul", this);
-
- // push a reference to the element to the tree array
- s.push({
- // get the name of the node
- name: getNodeText($self)
- // store a reference to the current element
- , element: this
- // parse and store any children items
- , children: ($ul.length) ? parseTree($ul, level) : []
- });
-
- }
- );
-
- return s;
- };
-
- function addBindings(el){
- removeBindings(el);
- $("> li", el)
- .bind("mouseover", hoverOver)
- .bind("mouseout", hoverOut);
- };
-
- function removeBindings(el){
- $("> li", el)
- .unbind("mouseover", hoverOver)
- .unbind("mouseout", hoverOut);
- };
-
- // scroll the current element into view
- function scrollToView($el){
- // get the current position
- var p = position($el, true);
- // get the screen dimensions
- var sd = getScreenDimensions();
-
- // if we're hidden off the bottom of the page, move up
- if( p.bottom > sd.y ){
- $("html,body").animate({"scrollTop": "+=" + ((p.bottom - sd.y) + settings.screenPadding) + "px" })
- }
- };
-
- function hoverOver(e){
- var self = this;
- var timer = $.data(self, "timer");
-
- // if the timer exists, clear it
- if( !isNaN(timer) ) clearTimeout(timer);
-
- // if IE6, add the hover class
- $(this).addClass("mc_hover");
-
- // show the branch
- $.data(self, "timer", setTimeout(function(){
- showBranch.apply(self);
- }, settings.hoverOverDelay)
- );
- };
-
- function hoverOut(e){
- var self = this;
- var timer = $.data(self, "timer");
-
- // if the timer exists, clear it
- if( !isNaN(timer) ) clearTimeout(timer);
-
- // if IE6, remove the hover class
- $(this).removeClass("mc_hover");
-
- // hide the branch
- $.data(self, "timer", setTimeout(function(){
- var $li = $(self);
- setTimeout(function (){
- // if no children selected, we must close the parent menus
- if( $li.parent().find("> li.mc_hover").length == 0 ){
- $li.parents("li").each(function (){
- var self = this;
- clearTimeout($.data(self, "timer"));
- hideBranch.apply(self);
- // check to see if we've hovered over a parent item
- if( $(this).siblings().filter(".mc_hover").length > 0 ) return false;
- });
- }
-
- }, settings.hoverOverDelay);
-
- hideBranch.apply(self);
- }, settings.hoverOutDelay)
- );
-
- // this will stop flickering in IE6, but it leaves mc_hover classes behind
- if( isIE6 ) e.stopPropagation();
- };
-
- function getShadow(depth){
- var shadows = $self.data("shadows");
-
- // if the shadows don't exist, create an object to track them
- if( !shadows )
- shadows = {};
-
- // if the shadow doesn't exist, create it
- if( !shadows[depth] ){
- // create shadow
- shadows[depth] = $('<div class="mcdropdown_shadow"></div>').appendTo('body');
- // if the bgIframe exists, use the plug-in
- if( !!$.fn.bgIframe ) shadows[depth].bgIframe();
- // update the shadows cache
- $self.data("shadows", shadows);
- }
-
- return shadows[depth];
- };
-
- function showBranch(){
- var self = this;
- // the child menu
- var $ul = $("> ul", this);
-
- // if the menu is already visible or there is no submenu, cancel
- if( $ul.is(":visible") || ($ul.length == 0) ) return false;
-
- // hide any visible sibling menus
- $(this).parent().find('> li ul:visible').not($ul).parent().each(function(){
- hideBranch.apply(this);
- });
-
- // columnize the list
- columnizeList($ul);
-
- // add new bindings
- addBindings($ul);
-
- var depth = $ul.parents("ul").length;
-
- // get the screen dimensions
- var sd = getScreenDimensions();
-
- // get the coordinates for the menu item
- var li_coords = position($(this));
-
- // move the menu to the correct position and show the menu || ((depth)*2)
- $ul.css({top: li_coords.bottom, left: li_coords.marginLeft/*, zIndex: settings.baseZIndex + ((depth)*2)*/}).show();
-
- // get the bottom of the menu
- var menuBottom = $ul.outerHeight() + $ul.offset().top;
-
- // if we're hidden off the bottom of the page, move up
- if( menuBottom > sd.y ){
- // adjust the menu by subtracting the bottom edge by the screen offset
- $ul.css("top", li_coords.bottom - (menuBottom - sd.y) - settings.screenPadding);
- }
-
- var showShadow = function (){
- // if using drop shadows, then show them
- if( settings.dropShadow ){
- // get a reference to the current shadow
- var $shadow = getShadow(depth);
- // get the position of the parent element
- var pos = position($ul);
-
- // move the shadow to the correct visual & DOM position
- $shadow.css({
- top: pos.top + pos.marginTop
- , left: pos.left + pos.marginLeft
- , width: pos.width
- , height: pos.height
- /*, zIndex: settings.baseZIndex + ((2*depth)-1)*/
- }).insertAfter($ul).show();
-
- // store a reference to the shadow so we can hide it
- $.data(self, "shadow", $shadow);
- }
- }
-
- // columnize the list and then show it using the defined effect
- // if the menu has a zero delay, just open it and then draw the
- // shadow, otherwise show the effect and the draw the shadow
- // after you're done.
- if( settings.showSpeed <= 0 ){
- showShadow();
- } else {
- $ul.hide()[settings.showFx](settings.showSpeed, showShadow);
- }
- };
-
- function hideBranch(){
- var $ul = $("> ul", this);
- // if the menu is already visible or there is no submenu, cancel
- if( $ul.is(":hidden") || ($ul.length == 0) ) return false;
-
- // if using drop shadows, then hide
- if( settings.dropShadow && $.data(this, "shadow") ) $.data(this, "shadow").hide();
-
- // if we're IE6, we need to set the visiblity to "hidden" so child
- // menus are correctly hidden and remove the .mc_hover class due to
- // the e.stopPropagation() call in the hoverOut() call
- if( isIE6 )
- $ul.css("visibility", "hidden").parent().removeClass("mc_hover");
-
- // hide the menu
- $ul.stop()[settings.hideFx](settings.hideSpeed);
- };
-
- function position($el, bUseOffset){
- var bHidden = false;
- // if the element is hidden we must make it visible to the DOM to get
- if ($el.is(":hidden")) {
- bHidden = !!$el.css("visibility", "hidden").show();
- }
-
- var pos = $.extend($el[bUseOffset === true ? "offset" : "position"](),{
- width: $el.outerWidth()
- , height: $el.outerHeight()
- , marginLeft: parseInt($.curCSS($el[0], "marginLeft", true), 10) || 0
- , marginRight: parseInt($.curCSS($el[0], "marginRight", true), 10) || 0
- , marginTop: parseInt($.curCSS($el[0], "marginTop", true), 10) || 0
- , marginBottom: parseInt($.curCSS($el[0], "marginBottom", true), 10) || 0
- });
-
- if( pos.marginTop < 0 ) pos.top += pos.marginTop;
- if( pos.marginLeft < 0 ) pos.left += pos.marginLeft;
-
- pos["bottom"] = pos.top + pos.height;
- pos["right"] = pos.left + pos.width;
-
- // hide the element again
- if( bHidden ) $el.hide().css("visibility", "visible");
-
- return pos;
- };
-
- function anchorTo($anchor, $target, bUseOffset){
- var pos = position($anchor, bUseOffset);
-
- $target.css({
- position: "absolute"
- , top: pos.bottom
- , left: pos.left
- });
-
- /*
- * we need to return the top edge of the core drop down menu, because
- * the top:0 starts at this point when repositioning items absolutely
- * this means we have to offset everything by the offset of the top menu
- */
-
- return pos.bottom;
- };
-
- function getScreenDimensions(){
- var d = {
- scrollLeft: $(window).scrollLeft()
- , scrollTop: $(window).scrollTop()
- , width: $(window).width() // changed from innerWidth
- , height: $(window).height() // changed from innerHeight
- };
-
- // calculate the correct x/y positions
- d.x = d.scrollLeft + d.width;
- d.y = d.scrollTop + d.height;
-
- return d;
- };
-
- function getPadding(el, name){
- var torl = name == 'height' ? 'Top' : 'Left', // top or left
- borr = name == 'height' ? 'Bottom' : 'Right'; // bottom or right
-
- return (
- // we add "0" to each string to make sure parseInt() returns a number
- parseInt("0"+$.curCSS(el, "border"+torl+"Width", true), 10)
- + parseInt("0"+$.curCSS(el, "border"+borr+"Width", true), 10)
- + parseInt("0"+$.curCSS(el, "padding"+torl, true), 10)
- + parseInt("0"+$.curCSS(el, "padding"+borr, true), 10)
- + parseInt("0"+$.curCSS(el, "margin"+torl, true), 10)
- + parseInt("0"+$.curCSS(el, "margin"+borr, true), 10)
- );
- };
-
- function getListDimensions($el, cols){
- if( !$el.data("dimensions") ){
- // get the width of the dropdown menu
- var ddWidth = $divInput.outerWidth();
- // if showing the root item, then try to make sure the width of the menu is sized to the drop down menu
- var width = ( ($el === $list) && ($el.data("width") * cols < ddWidth) ) ? Math.floor(ddWidth/cols) : $el.data("width");
-
- $el.data("dimensions", {
- // get the original width of the list item
- column: width
- // subtract the padding from the first list item from the width to get the width of the items
- , item: width - getPadding($el.children().eq(0)[0], "width")
- // get the original height
- , height: $el.height()
- });
- }
-
- return $el.data("dimensions");
- };
-
- function getHeight($el){
- // skip height calculation and use lineHeight
- if( settings.autoHeight === false ) return settings.lineHeight;
- // if we haven't cached our height, do so now
- if( !$el.data("height") ) $el.data("height", $el.outerHeight());
-
- // return the cached value
- return $el.data("height");
- };
-
- function columnizeList($el){
- // get the children items
- var $children = $el.find("> li");
- // get the total number of items
- var items = $children.length;
-
- // calculate how many columns we think we should have based on the max rows
- var calculatedCols = Math.ceil(items/settings.maxRows);
- // get the number of columns, don't columnize if we don't have enough rows
- // if the height of the column is bigger than the screen, we automatically try
- // moving to a new column
- var cols = !!arguments[1] ? arguments[1] : ( items <= settings.minRows ) ? 1 : (calculatedCols > settings.targetColumnSize) ? calculatedCols : settings.targetColumnSize;
- // get the dimension of this element
- var widths = getListDimensions($el, cols);
- var prevColumn = 0;
- var columnHeight = 0;
- var maxColumnHeight = 0;
- var maxRows = Math.ceil(items/cols);
-
- // get the width of the parent item
- var parentLIWidth = $el.parent("li").width();
-
- // we need to draw the list element, but hide it so we can correctly calculate it's information
- $el.css({"visibility": "hidden", "display": "block"});
-
- // loop through each child item
- $children.each(function (i){
- var currentItem = i+1;
- var nextItemColumn = Math.floor((currentItem/items) * cols);
- // calculate the column we're in
- var column = Math.floor((i/items) * cols);
- // reference the current item
- var $li = $(this);
- // variable to track margin-top
- var marginTop;
-
- // if we're in the same column
- if( prevColumn != column ){
- // move to the top of the next column
- marginTop = (columnHeight+1) * -1;
- // reset column height
- columnHeight = 0;
- // if we're in a new column
- } else {
- marginTop = 0;
- }
-
- // increase the column height based on it's current height (calculate this before adding classes)
- columnHeight += (getHeight($li) || settings.lineHeight);
-
- // update the css settings
- $li.css({
- "marginLeft": (widths.column * column)
- , "marginTop": marginTop
- , "width": widths.item
- })
- [((nextItemColumn > column) || (currentItem == items)) ? "addClass" : "removeClass"]("mc_endcol")
- [(marginTop != 0) ? "addClass" : "removeClass"]("mc_firstrow")
- ;
- // get the height of the longest column
- if( columnHeight > maxColumnHeight ) maxColumnHeight = columnHeight;
-
- // update the previous column
- prevColumn = column;
- });
-
- // if the menu is too tall to fit on the screen, try adding another column
- if( ($el !== $list) && (maxColumnHeight + (settings.screenPadding*2) >= getScreenDimensions().height) ){
- return columnizeList($el, cols+1);
- }
-
- /*
- * set the height of the list to the max column height. this fixes
- * display problems in FF when the last column is not full.
- *
- * we also need to set the visiblity to "visible" to make sure that
- * the element will show up
- */
- $el.css("visibility", "visible").height(maxColumnHeight);
-
- return $el;
- };
-
- function getListItem(value){
- return $list.find("li[" + settings.valueAttr + "='"+ value +"']");
- };
-
- function getCurrentListItem(){
- return getListItem($hidden.val());
- };
-
- function onFocus(e){
- var $current = getCurrentListItem();
- var value = $self.val().toLowerCase();
- var treePath = value.toLowerCase().split(settings.delim);
- var currentNode = treePath.pop();
- var lastDelim = value.lastIndexOf(settings.delim) + 1;
-
- // reset the typed text
- typedText = treePath.join(settings.delim) + (treePath.length > 0 ? settings.delim : "");
-
- // we need to set the selection asynchronously so that when user TABs to field the pre-select isn't overwritten
- setTimeout(function (){
- // preselect the last child node
- setSelection($self[0], lastDelim, lastDelim+currentNode.length);
- }, 0);
-
- // create the keyboard hint list
- if( !$keylist ){
- $keylist = $('<ul class="mcdropdown_autocomplete"></ul>').appendTo("body");
- // if IE6 we need an iframe to hide the scrolling list
- if( isIE6 && !!$.fn.bgIframe ) $keylistiframe = $('<div></div>').bgIframe().appendTo("body");
- }
-
- // should we show matches?
- var hideResults = !(settings.showACOnEmptyFocus && (typedText.length == 0));
-
- // get the siblings for the current item
- var $siblings = ($current.length == 0 || $current.hasClass("mc_root")) ? $list.find("> li") : $current.parent().find("> li");
- // show all matches
- showMatches($siblings, hideResults);
- };
-
- var iBlurTimeout = null;
- function onBlur(e){
- // only run the last blur event
- if( iBlurTimeout ) clearTimeout(iBlurTimeout);
- // we may need to cancel this blur event, so we run it asynchronously
- iBlurTimeout = setTimeout(function (){
- // get the current item
- var $current = getCurrentListItem();
-
- // if we must select a child item, then update to the first child we can find
- if( !settings.allowParentSelect && $current.is(".mc_parent") ){
- // grab the first end child item we can find for the current path
- var value = $current.find("li:not('.mc_parent'):first").attr(settings.valueAttr);
- // update the value
- thismenu.setValue(value, true);
- }
-
- // run the select callback
- if( settings.select != null ) settings.select.apply(thismenu, thismenu.getValue());
-
- // hide matches
- hideMatches();
-
- // mark event as having run
- iBlurTimeout = null;
- }, 200);
- };
-
- function showMatches($li, hideResults){
- var bCached = ($li === oldCache), $items = bCached ? $keylist.find("> li").removeClass("mc_hover mc_hover_parent mc_firstrow") : $li.clone().removeAttr("style").removeClass("mc_hover mc_hover_parent mc_firstrow mc_endcol").filter(":last").addClass("mc_endcol").end();
-
- // only do the following if we've updated the cache or the list is hidden
- if( !bCached || $keylist.is(":hidden") ){
- // update the matches
- $keylist.empty().append($items).width($divInput.outerWidth() - getPadding($keylist[0], "width")).css("height", "auto");
-
- // anchor the menu relative parent
- anchorTo($divInput.parent(), $keylist, true);
-
- // show hover on mouseover
- $items.hover(function (){$keylist.find("> li").removeClass("mc_hover_parent mc_hover"); $(this).addClass("mc_hover")}, function (){$(this).removeClass("mc_hover")});
-
- // make sure the the ul's are hidden (so the li's are sized correctly)
- $items.find("> ul").css("display", "none");
-
- // show the list
- $keylist.show().css("visibility", (hideResults === true) ? "hidden" : "visible");
-
- // if we're IE6, ensure we enforce the "max-height" CSS property
- if( isIE6 ){
- var maxHeight = parseInt($keylist.css("max-height"), 10) || 0;
- if( (maxHeight > 0) && (maxHeight < $keylist.outerHeight()) ) $keylist.height(maxHeight);
-
- // anchor the iframe behind the scrollable list
- if( !!$.fn.bgIframe ) anchorTo($divInput.parent(), $keylistiframe.css({height: $keylist.outerHeight(), width: $keylist.width()}, true).show())
- }
-
- // scroll the list into view
- if( hideResults !== true ) scrollToView($keylist);
- }
-
- // do not show the list on screen
- if( hideResults === true ){
- // hide the results and move them offscreen (so it doesn't hide the cursor in FF2)
- $keylist.css({top: "-10000px", left: "-10000px"});
- // hiden the iframe overlay
- if( isIE6 && !!$.fn.bgIframe ) $keylistiframe.css("display", "none");
- }
-
- // get the currently selected item
- var $current = $keylist.find("li[" + settings.valueAttr + "='"+ $hidden.val() +"']");
-
- // make sure the last match is still highlighted
- $current.addClass("mc_hover" + ($current.is(".mc_parent")? "_parent" : ""));
-
- // scroll the item into view
- if( $current.length > 0 && (hideResults != true) ) scrollIntoView($current);
-
- // update the cache
- oldCache = matchesCache = $li;
- };
-
- function hideMatches(){
- // hide the bgiframe
- if( isIE6 && !!$.fn.bgIframe && $keylistiframe ) $keylistiframe.hide();
- if( $keylist ) $keylist.hide();
- };
-
- // check the user's keypress
- function checkKeypress(e){
- var key = String.fromCharCode(e.keyCode || e.charCode).toLowerCase();
- var $current = getCurrentListItem();
- var $lis = ($current.length == 0 || $current.hasClass("mc_root")) ? $list.find("> li") : $current.parent().find("> li");
- var treePath = typedText.split(settings.delim);
- var currentNode = treePath.pop();
- var compare = currentNode + key;
- var selectedText = getSelection($self[0]).toLowerCase();
- var value = $self.val().toLowerCase();
-
- // if the up arrow was pressed
- if( e.keyCode == 38 ){
- moveMatch(-1);
- return false;
-
- // if the down arrow was pressed
- } else if( e.keyCode == 40 ){
- moveMatch(1);
- return false;
-
- // if the [ESC] was pressed
- } else if( e.keyCode == 27 ){
- // clear typedText
- typedText = "";
- // clear the value
- thismenu.setValue("");
- // show the root level
- showMatches($list.find("> li"));
-
- return false;
-
- // if user pressed [DEL] or [LEFT ARROW], go remove last typed character
- } else if( e.keyCode == 8 || e.keyCode == 37 ){
- // if left arrow, go back to previous parent
- compare = (e.keyCode == 37) ? "" : currentNode.substring(0, currentNode.length - 1);
-
- // if all the text is highlighted we just came from a delete
- if( selectedText == currentNode ){
- currentNode = "";
- }
- // we're going backwards to the last parent, move backwards
- if( treePath.length > 0 && currentNode.length == 0){
- updateValue($current.parent().parent());
- return false;
- // if all the text is selected, remove everything
- } else if( selectedText == value ){
- typedText = "";
- thismenu.setValue("");
- return false;
- }
- // if the user pressed [ENTER], [TAB], [RIGHT ARROW] or the delimiter--go to next level
- } else if( e.keyCode == 9 || e.keyCode == 13 || e.keyCode == 39 || key == settings.delim ){
- // get the first child item if there is one
- var $first = $current.find("> ul > li:first");
-
- // update with the next child branch
- if( $first.length > 0 ){
- updateValue($first);
- // leave the field
- } else {
- // if IE6, we must deselect the selection
- if( $.browser.msie ) setSelection($self[0], 0, 0);
- if( e.keyCode == 9 ){
- // blur out of the field
- $self.triggerHandler("blur");
- // hide the matches
- hideMatches();
- // allow the tab
- return true;
- } else {
- // blur out of the field
- $self.trigger("blur");
- // hide the matches
- hideMatches();
- }
- }
-
- return false;
- // if all the text is highlighted then we need to delete everything
- } else if( selectedText == value ){
- typedText = "";
- compare = key;
- }
-
- // update the match cache with all the matches
- matchesCache = findMatches($lis, compare);
-
- // if we have some matches, populate autofill and show matches
- if( matchesCache.length > 0 ){
- // update the a reference to what the user's typed
- typedText = treePath.join(settings.delim) + (treePath.length > 0 ? settings.delim : "") + compare;
- updateValue(matchesCache.eq(0), true);
- } else {
- // find the previous compare string
- compare = compare.length ? compare.substring(0, compare.length-1) : "";
-
- // since we have no matches, get the previous matches
- matchesCache = findMatches($lis, compare);
-
- // if we have some matches, show them
- if( matchesCache.length > 0 )
- showMatches(matchesCache);
- // hide the matches
- else
- hideMatches();
- }
-
- // stop default behavior
- e.preventDefault();
-
- return false;
- };
-
- function moveMatch(step){
- // find the current item in the matches cache
- var $current = getCurrentListItem(), $next, pos = 0;
-
- // if nothing selected, look for the item with the hover class
- if( $current.length == 0 ) $current = matchesCache.filter(".mc_hover, .mc_hover_parent");
- // if still nothing, grab the first item in the cache
- if( $current.length == 0 || $keylist.is(":hidden") ){
- // grab the first item
- $current = matchesCache.eq(0);
- // since nothing is selected, don't step forward/back
- step = 0;
- }
-
- // find the current position of the element
- matchesCache.each(function (i){
- if( this === $current[0]){
- pos = i;
- return false;
- }
- });
-
- // if no matches, cancel
- if( !matchesCache || matchesCache.length == 0 || $current.length == 0 ) return false;
-
- // adjust by the step count
- pos = pos + step;
-
- // make sure pos is in valid bounds
- if( pos < 0 ) pos = matchesCache.length-1;
- else if( pos >= matchesCache.length ) pos = 0;
-
- // get the next item
- $next = matchesCache.eq(pos);
-
- updateValue($next, true);
- };
-
- function findMatches($lis, compare){
- var matches = $([]); // $([]) = empty jquery object
-
- $lis.each(function (){
- // get the current list item and it's label
- var $li = $(this), label = getNodeText($li);
-
- // label matches what the user typed, add it to the queue
- if( label.substring(0, compare.length).toLowerCase() == compare ){
- // store a copy to this jQuery item
- matches = matches.add($li);
- }
- });
-
- // return the matches found
- return matches;
- };
-
- function updateValue($li, keepTypedText){
- // grab all direct children items
- var $siblings = keepTypedText ? matchesCache : ($li.length == 0 || $li.hasClass("mc_root")) ? $list.find("> li") : $li.parent().find("> li");
- var treePath = getTreePath($li);
- var currentNode = treePath.pop().toLowerCase();
-
- // update the a reference to what the user's typed
- if( !keepTypedText ) typedText = treePath.join(settings.delim).toLowerCase() + (treePath.length > 0 ? settings.delim : "");
-
- // update form field and display with the updated value
- thismenu.setValue($li.attr(settings.valueAttr), true);
-
- // pre-select the last node
- setSelection($self[0], typedText.length, currentNode.length+typedText.length);
-
- // remove any currently selected items
- $siblings.filter(".mc_hover,.mc_hover_parent").removeClass("mc_hover mc_hover_parent");
- // add the hover class
- $li.addClass("mc_hover" + ($li.is(".mc_parent")? "_parent" : ""));
-
- // show all the matches
- showMatches($siblings);
- };
-
- // get the text currently selected by the user in a text field
- function getSelection(field){
- var text = "";
- if( field.setSelectionRange ){
- text = field.value.substring(field.selectionStart, field.selectionEnd);
- } else if( document.selection ){
- var range = document.selection.createRange();
- if( range.parentElement() == field ){
- text = range.text;
- }
- }
- return text;
- };
-
- // set the text selected in a text field
- function setSelection(field, start, end) {
- if( field.createTextRange ){
- var selRange = field.createTextRange();
- selRange.collapse(true);
- selRange.moveStart("character", start);
- selRange.moveEnd("character", end);
- selRange.select();
- } else if( field.setSelectionRange ){
- field.setSelectionRange(start, end);
- } else {
- if( field.selectionStart ){
- field.selectionStart = start;
- field.selectionEnd = end;
- }
- }
- field.focus();
- };
-
- function scrollIntoView($el, center){
- var el = $el[0];
- var scrollable = $keylist[0];
- // get the padding which is need to adjust the scrollTop
- var s = {pTop: parseInt($keylist.css("paddingTop"), 10)||0, pBottom: parseInt($keylist.css("paddingBottom"), 10)||0, bTop: parseInt($keylist.css("borderTopWidth"), 10)||0, bBottom: parseInt($keylist.css("borderBottomWidth"), 10)||0};
-
- // scrolling down
- if( (el.offsetTop + el.offsetHeight) > (scrollable.scrollTop + scrollable.clientHeight) ){
- scrollable.scrollTop = $el.offset().top + (scrollable.scrollTop - $keylist.offset().top) - ((scrollable.clientHeight/((center == true) ? 2 : 1)) - ($el.outerHeight() + s.pBottom));
- // scrolling up
- } else if( el.offsetTop - s.bTop - s.bBottom <= (scrollable.scrollTop + s.pTop + s.pBottom) ){
- scrollable.scrollTop = $el.offset().top + (scrollable.scrollTop - $keylist.offset().top) - s.pTop;
- }
- };
-
- // run the init callback (some keyboard entry methods will manage this callback manually)
- if( settings.init != null ) settings.init.apply(thismenu, [$input, $hidden, $list]);
-
- };
-
-})(jQuery);