Skip to content

Instantly share code, notes, and snippets.

@soerennielsen
Forked from nowhereman/README.md
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save soerennielsen/86c0792a5482ca1b79ff to your computer and use it in GitHub Desktop.
Save soerennielsen/86c0792a5482ca1b79ff to your computer and use it in GitHub Desktop.

Pimp my Drop Down List

Instant auto-completion on demand for any <select> drop down lists on any page. With the help of jQuery Comb'O plugin.

Prerequisites

Or

Installation

Usage

  • Middle click or <CTRL>+Right click or <SHIFT>+<SPACE> on the drop down list :
  • Pimp your select tag into a combo box.

Then

  • Right click on the combo box button :
  • Back to the original select tag and vice versa.
  • Access to the options menu.

Known issues

  • With Firefox, you SHOULD disable Autoscrolling option :

Firefox Options panel

  • With Chrome, sometimes combo box is displayed at the next line.

License

Pimp my Drop Down List is Copyright © 2012 Nowhere Man.

It's free software under GPL licence, same as jQuery Comb'O plugin.

/*wrapper of all elements*/
div.combo {
position:relative;
left: 0px;
top: 0px;
}
/*text input*/
.combo input {
position: absolute;
}
/*icon*/
.combo div.icon {
position:absolute;
}
/*list wrapper*/
.combo div.list-wrapper {
position: absolute;
overflow: hidden;
/*we should set height and max-height explicitly*/
height: 200px;
max-height: 200px;
/*should be always at the top*/
z-index: 99999;
}
#IE6 .combo div.list-wrapper {
/*max-height fix for IE6*/
/*height: expression(this.scrollHeight < 200 ? true : "200px");*/
}
/*"drop-up" list wrapper*/
.combo div.list-wrapper-up {}
/*dropdown list*/
.combo ul {}
/*dropdown list item*/
.combo li {
height: 20px;
}
/*active (hovered) list item*/
.combo li.active {}
.combo .visible {
display: block;
}
.combo .invisible {
display: none;
}
/*used when emptyText config opt is set. Applied to text input*/
.combo input.empty {}
// ==UserScript==
// @name Pimp my Drop Down List
// @description Instant auto-completion on demand for any drop down lists (<select>) on any page
// @author Nowhere Man
// @version 0.3.3dev
// @license GPL License
// @homepage https://gist.github.com/954934/#file_readme.md
// @updateURL https://raw.github.com/gist/954934/combo.user.js
// @include https://*
// @include http://*
// @include file://*
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js
// @require https://raw.github.com/gist/954934/jquery.contextMenu.js
// @require https://raw.github.com/gist/954934/jquery.combo.dependencies.js
// @require https://raw.github.com/gist/954934/jquery.combo.js
// @resource text-bg.b64 https://raw.github.com/gist/954934/text-bg.b64
// @resource trigger.b64 https://raw.github.com/gist/954934/trigger.b64
// @resource default.css https://raw.github.com/gist/954934/default.css
// @resource combo.css https://raw.github.com/gist/954934/combo.css
// @resource usage.css https://raw.github.com/gist/954934/usage.css
// ==/UserScript==
// TODO in jquery.combo.js plugin, enhancement of "lazyLoading" option :
// add an "auto" feature, so "lazyLoading" will be activated if the select tag has too many options tags (determine the "maxNumber" of options tags)
// and "lazyLoading" will be desactivated if the number of options tags is below to "maxNumber".
// if "showList" option is set to true ignore the "auto" feature of "lazyLoading" option
// TODO preserve options for each selectbox tag, e.g. 'fuzzy-search' option
jQuery.noConflict();
(function($) {
var head = document.getElementsByTagName("head")[0];
var imgs = {};
function requireImg(resourceName) {
var img = GM_getResourceURL(resourceName);
imgs[resourceName] = img;
}
function requireBase64Img(resourceName, type) {
if (!type)
type = 'gif';
var img = "data:image/" + type + ";base64," + GM_getResourceText(resourceName);
resourceName = resourceName.replace(/^(.+\.)b64$/,'$1' + type);
imgs[resourceName] = img;
}
function requireCSS(resourceName) {
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
var css = GM_getResourceText(resourceName);
$.each(imgs, function(imgName, imgBase64) {
css = css.replace(imgName, imgBase64);
});
style.innerHTML = css;
head.appendChild(style);
}
requireBase64Img('text-bg.b64');
requireBase64Img('trigger.b64');
requireCSS('combo.css');
requireCSS('default.css');
requireCSS('usage.css');
// Utilities functions
// Remove duplicate items from an array
// Source : http://shamasis.net/fast-algorithm-to-find-unique-items-in-javascript-array
// TODO move it in a plugin and don't extends Array
Array.prototype.unique = function() {
var o = {}, i, l = this.length, r = [];
for(i=0; i<l;i+=1) o[this[i]] = this[i];
for(i in o) r.push(o[i]);
return r;
};
// parseUri 1.2.3
// author : (c) Steven Levithan <stevenlevithan.com>
// contributor: Nowhere Man
// source: http://blog.stevenlevithan.com/archives/parseuri
// license: MIT License
function parseUri (str) {
var o = parseUri.options,
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
uri = {},
i = 14;
while (i--) uri[o.key[i]] = m[i] || "";
uri[o.q.name] = {};
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
if ($1) uri[o.q.name][$1] = $2;
});
if(uri.protocol) uri.protocol += ":";
if(uri.search) uri.search = "?" + uri.search;
if(uri.hash) uri.hash = "#" + uri.hash;
return uri;
};
parseUri.options = {
strictMode: false,
key: ["href","protocol","host","userInfo","user","password","hostname","port","relative","pathname","directory","file","search","hash"],
q: {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
}
};
// Transforms URL like "http://hostname:port/controller/id/action?param1=value1#hash" to "hostname/controller//action"
// E.g. fuzzyUri("http://localhost/people/58#adress") == fuzzyUri("https://localhost/people/45/")
// If your location.href = "http://google.com" :
// fuzzyUri() != fuzzyUri("https://localhost/people/45/")
// fuzzyUri() == fuzzyUri("http://google.com:80 /?q=test")
function fuzzyUri(url) {
if(url && typeof url == "string") {
url = parseUri(url);
} else {
url = location;
}
url = url.hostname + url.pathname;
if(url[url.length-1]!="/") url += "/";
return url.replace(/\/[0-9]+\//g,"//");
}
// Persistence stuff
function deserialize(name) {
var s = GM_getValue(name);
if (s === undefined) {
return {};
}
else {
return JSON.parse(s, function(k,v) {
if(v && typeof v === 'string' && v[0] === "[" && v[v.length -1] === "]" )
//it's an stringify array
return JSON.parse(v);
else
return v;
});
}
}
function serialize(name, value) {
GM_setValue(name, JSON.stringify(value, null, 2));
}
// Default config of Comb'O plugin
var conf = {
accentsSensitive: false,
punctuationSensitive: false,
triggerSelected: true,
highlightTerm: true,
liquidFilter: true,
liquidSorting: false,
keyPressDelay: 200,
showList: true,
lazyLoading: false
};
// If Firefox version is below 4.0 (Gecko engine below to 2.0), we adjust the config to preserve performances
if ($.browser.mozilla && parseInt($.browser.version) < 2) {
conf.liquidFilter = false;
conf.liquidSorting = false;
conf.keyPressDelay = 300;
}
// $.extend(conf);//debug
// $.extend(conf, deserialize('config')); // Disabled for the moment
// TODO make a dynamic form to update the global config
serialize('config', conf);
// GM_deleteValue('selectTags');//debug
// Transform select element to a combobox
function select2Combo(element) {
// Save select tag id to restore it next time
var selectId = element.attr('id');
var url = fuzzyUri();
var selectTags = deserialize('selectTags');
selectTags[url] = (selectTags[url] || []).concat([selectId]).unique();
serialize('selectTags', selectTags);
return element.combo(conf);
}
function comboize() {
// Add id attribute to select elements who haven't
$("select:not([id])[name]").attr("id", function(index, attr) {
return this.name;
});
// Restore combo box transformed last time
var selectTags = deserialize('selectTags');
if(selectTags && selectTags[fuzzyUri()]){
var selectIds = selectTags[fuzzyUri()];
$.each(selectIds, function(i,s) {
$("#" + s + "[multiple!=true]:visible:not('.comboize')").combo(conf);
});
}
// Usage of Comb'O plugin
var shifted, controled, spaced, clicked;
$(document).keydown(function (e) {
if (e.keyCode === 17) {
controled = true;
}
}).keyup(function (e) {
if (e.keyCode === 17) {
controled = false;
}
});
// Add a context menu to all select tag who have a 'comboize' class
$("select[id][multiple!=true]:visible:not('.comboize')").addClass("comboize")
.click(function(e) {
// bind middle click of the select tag
if(e.which==2 && $.browser.webkit) {
select2Combo($(this)).showListItems(); // show combobox list items
e.preventDefault();
}
})
.bind("keydown mousedown", function (e) {
// bind middle click of the select tag
if(e.which===2 && $.browser.mozilla) {
select2Combo($(this)).showListItems(); // show combobox list items
e.preventDefault();
} else if (e.keyCode === 16) {
shifted = true;
} else if (e.keyCode === 32) {
spaced = true;
if(shifted) {
select2Combo($(this)).showListItems(); // show combobox list items
e.preventDefault();
}
} else if (e.which === 1) {
clicked = true;
// mouseup event should not hide the list items
if(controled) {
select2Combo($(this));
e.preventDefault();
}
}
}).bind("keyup mouseup", function (e) {
shifted = spaced = clicked = false;
})
.contextMenu('restore-combo', {
'Pimp this Drop Down List': {
click: function(element){
element.data("context-menu").remove();
element.data("context-menu", null);
select2Combo(element);
},
klass: "combo-context-menu"
}
});
// Usage of Comb'O plugin
// Transform all select tag, who have a 'comboize' class, to combobox
// $("select.comboize[id][multiple!=true]:visible");
// Context menu stuff
$(".combo .icon").contextMenu('restore-select', {
'Restore Drop Down List': {
click: function(element){
element.data("context-menu").remove();
element.data("context-menu", null);
var selectbox = element.parent(".combo").find("select:first");
// Remove select tag id to not restore it next time
var selectId = selectbox.attr('id');
var url = fuzzyUri();
var selectTags = deserialize('selectTags');
if(selectTags[url]) {
var idIndex = selectTags[url].indexOf(selectId);
if(idIndex >= 0) {
selectTags[url].splice(idIndex,1);//remove select
}
}
serialize('selectTags', selectTags);
selectbox.removeClass("comboize").combo().destroy();
},
klass: "combo-context-menu"
},
'Options': {
click: function(element){
var comboBox = element.parent(".combo").find("select:first");
// include margin so it can be used to offset from page border.
var elPos = element.offset();
var mWidth = element.outerWidth(true),
mHeight = element.outerHeight(true),
xPos = ((elPos.left - window.scrollX) + mWidth < window.innerWidth) ? elPos.left : elPos.left - mWidth,
yPos = ((elPos.top - window.scrollY) + mHeight < window.innerHeight) ? elPos.top : elPos.top - mHeight;
var formId = comboBox.attr('id') + "__options";
var formOptions = $("#" + formId);// search if form is already present
if(formOptions.length === 0) {
formOptions = $("<form id='" + formId + "' class='combo-options'><fieldset><legend>Options</legend><input type='checkbox' name='fuzzy-search' /> <label for='fuzzy-search'>Fuzzy Search <small>(slow)</small></label><br /><br /><button name='delete-all-data' title='Delete all data store by this user script on any sites visited'>Delete all data</button> (globally)<br /><br /><input type='submit' value='Save'> or <a href='#' class='close-form-options'>close</a></fieldset></form>").appendTo('body');
} else {
formOptions.show();
}
formOptions.find('[name=fuzzy-search]').attr('checked', comboBox.combo().config.liquidSorting);
$('form.combo-options').submit(function(e){
var fuzzySearch = $(this).find('[name=fuzzy-search]').attr('checked');
var fuzzySearchOption = (fuzzySearch) ? true : false;
// GM_log(fuzzySearchOption); //debug
comboBox.combo().config.liquidSorting = fuzzySearchOption;
comboBox.combo().config.liquidFilter = fuzzySearchOption;
// comboBox.combo({liquidSorting: fuzzySearchOption, liquidFilter: fuzzySearchOption}); // Don't work !
// $.extend(comboBox.combo.config,{liquidSorting: fuzzySearchOption, liquidFilter: fuzzySearchOption}); // TODO test this instead
// GM_log(comboBox.combo().config.liquidFilter);//debug
$(this).hide();
e.preventDefault();
})
.css({
top: yPos + 'px',
left: xPos + 'px'
})
.find('.close-form-options').click(function(e) {
$(this).parent("fieldset").parent("form").hide();
e.preventDefault();
})
.end().find('[name=delete-all-data]').click(function(e) {
// Delete all data store by this user script on any sites visited (globally)
if(confirm("Are you sure to delete all data store by 'Pimp my Drop Down List' script ?")) {
GM_deleteValue('config');
$("select.comboize").removeClass("comboize");
GM_deleteValue('selectTags');
}
e.preventDefault();
});
}
}
});
}
// Sanitize select tags when DOM content is loaded
function loadedHandler(event) {
window.setTimeout( function() {
comboize();
// Check for new select tags to sanitize them
var timer;
function modifiedHandler(event) {
if (timer != null) {
clearTimeout(timer);
}
timer = window.setTimeout(comboize, 750);
}
document.addEventListener("DOMNodeInserted", modifiedHandler, false);
document.addEventListener("mouseup", modifiedHandler, false);
document.addEventListener("keyup", modifiedHandler, false);
}, 1000);
}
if(document.readyState) {
if($.browser.webkit) {
document.addEventListener("DOMContentLoaded", loadedHandler, false);
} else {
loadedHandler();
}
} else {
window.addEventListener("load", loadedHandler, false);
}
})(jQuery);
div.default {
white-space: nowrap;
height: 21px;
border: 0;
margin: 0;
padding: 0;
width: 146px;
}
div.default input {
margin: 0 0 0 0;
font:normal 0.9em tahoma, arial, helvetica, sans-serif;
padding:1px 3px;
background:#fff url(text-bg.gif) repeat-x 0 0;
border:1px solid #B5B8C8;
height: 18px;
line-height:18px;
vertical-align:middle;
left: 0px;
top: 0px;
width: 129px;
}
div.default div.icon {
width:17px;
height:21px;
border: 0;
background:transparent url(trigger.gif) no-repeat 0 0;
cursor:pointer;
border-bottom: 1px solid #B5B8C8;
top:0px;
left: 129px;
}
div.default div.list-wrapper {
left: 0px;
top: 21px;
border: 1px solid #D9D9D9;
background-color: #FFFFFF;
padding: 0;
margin: 0;
width: 146px;
bottom: auto;
}
div.default div.list-wrapper-up {
top: auto;
bottom: 21px;
}
div.default ul {
list-style-type: none;
padding: 0;
margin: 0;
height: 200px;
}
div.default ul.optgroup {
font:italic 14px tahoma, arial, helvetica, sans-serif;
font-weight: bold;
padding-left: 5px;
margin: 0;
height: auto;
}
div.default li {
padding: 0;
padding-left: 5px;
font:normal 14px tahoma, arial, helvetica, sans-serif;
background-color: #FFFFFF;
cursor: pointer;
margin: 0;
}
div.default li.selected {
border:1px solid lightSteelBlue;
}
div.default li.active {
background-color: rgb(223, 232, 246);
}
div.default ul.optgroup li {
padding-left: 10px;
}
div.default input.empty {
color: gray;
}
var LiquidMetal = (function() {
var SCORE_NO_MATCH = 0.0;
var SCORE_MATCH = 1.0;
var SCORE_TRAILING = 0.8;
var SCORE_TRAILING_BUT_STARTED = 0.9;
var SCORE_BUFFER = 0.85;
return {
score: function(string, abbreviation) {
// Short circuits
if (abbreviation.length === 0) return SCORE_TRAILING;
if (abbreviation.length > string.length) return SCORE_NO_MATCH;
var scores = this.buildScoreArray(string, abbreviation);
// miss, score is 0
if ( scores === false ) return 0;
// hit, average scores:
for (var i=0, sum=0, length=scores.length; i < length; i++)
sum += scores[i];
return sum / length;
},
buildScoreArray: function(string, abbreviation) {
var scores = new Array(string.length);
var lower = string.toLowerCase();
var chars = abbreviation.toLowerCase().split("");
var lastIndex = -1;
var started = false;
var c, index, trailingScore;
for (var i=0, length=chars.length; i<length; i++) {
c = chars[i];
index = lower.indexOf(c, lastIndex+1);
if (index === -1) return false; // return no match
if (index === 0) started = true;
// is new word:
if ( /\s/.test( string.charAt(index-1) ) ) {
scores[index-1] = 1;
fillArray(scores, SCORE_BUFFER, lastIndex+1, index-1);
}
// is upper:
else if ( /[A-Z]/.test( string.charAt(index) ) )
fillArray(scores, SCORE_BUFFER, lastIndex+1, index);
else
fillArray(scores, SCORE_NO_MATCH, lastIndex+1, index);
scores[index] = SCORE_MATCH;
lastIndex = index;
}
trailingScore = started ? SCORE_TRAILING_BUT_STARTED : SCORE_TRAILING;
fillArray(scores, trailingScore, lastIndex+1, scores.length);
return scores;
}
};
function fillArray(array, value, from, to) {
for (var i = from; i < to; i++) { array[i] = value; }
return array;
}
})();
(function ($) {
if(typeof $.fn.each2 == "undefined"){
$.fn.extend({
/*
* $.quickEach() (renamed each2) replicates the functionality of $.each() but allows 'this'
* to be used as a jQuery object without the need to wrap it using '$(this)'.
* The performance boost comes from internally recycling a single jQuery
* object instead of wrapping each iteration in a brand new one.
* source: https://gist.github.com/1352993
*/
each2 : function(f) {
var j = $([0]), i = -1, l = this.length, c;
while(
++i < l
&& (c = j[0] = this[i])
&& f.call(j, i, c) !== false
);
return this;
}
});
}
})(jQuery);
(function($){
// jQuery getText Plugin
// Get the text of a string, form elements or other HTML elements like div, ul, li ,span ...
// Copyright 2010, Nowhere Man
// Dual licensed under the MIT or GPL Version 2 licenses.
$.getText = $.fn.getText = function (element, method) {
if (!element || element == null) {
if (typeof(this)!='function')
element = $(this);
else
return '';
} else if (typeof(element) == 'function') {
element = $.fn.call(element);
}
if (typeof(element) != 'function') {
try {
var text = '';
if (!method && ( typeof(element) == 'string' || typeof(element) == 'number') ) {
text = element;
} else if(method == 'text') {
text = element.text();
} else if (method == 'val') {
text = element.val();
} else {
return this.getText(element, 'text');
}
return text;
} catch(err) {
// console.error(err.toString());
if (!method) {
return this.getText(element, 'text');
} else if (method == 'text') {
return this.getText(element, 'val');
} else {
return ''
}
}
} else {
return '';
}
};
})(jQuery);
(function($){
// jQuery Accent Folding Plugin
// Conversion of jQuery UI Autocomplete Accent Folding Extension
// to a very basic jQuery plugin.
// Dependent of jquery.gettext.js plugin
// Copyright 2010, Scott González (http://scottgonzalez.com)
// Copyright 2010, Nowhere Man (jQuery plugin conversion)
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://github.com/scottgonzalez/jquery-ui-extensions
$.accentFolding = $.fn.accentFolding = function(term){
var map = {
"ẚ": "a",
"Á": "a",
"á": "a",
"À": "a",
"à": "a",
"Ă": "a",
"ă": "a",
"Ắ": "a",
"ắ": "a",
"Ằ": "a",
"ằ": "a",
"Ẵ": "a",
"ẵ": "a",
"Ẳ": "a",
"ẳ": "a",
"Â": "a",
"â": "a",
"Ấ": "a",
"ấ": "a",
"Ầ": "a",
"ầ": "a",
"Ẫ": "a",
"ẫ": "a",
"Ẩ": "a",
"ẩ": "a",
"Ǎ": "a",
"ǎ": "a",
"Å": "a",
"å": "a",
"Ǻ": "a",
"ǻ": "a",
"Ä": "a",
"ä": "a",
"Ǟ": "a",
"ǟ": "a",
"Ã": "a",
"ã": "a",
"Ȧ": "a",
"ȧ": "a",
"Ǡ": "a",
"ǡ": "a",
"Ą": "a",
"ą": "a",
"Ā": "a",
"ā": "a",
"Ả": "a",
"ả": "a",
"Ȁ": "a",
"ȁ": "a",
"Ȃ": "a",
"ȃ": "a",
"Ạ": "a",
"ạ": "a",
"Ặ": "a",
"ặ": "a",
"Ậ": "a",
"ậ": "a",
"Ḁ": "a",
"ḁ": "a",
"Ⱥ": "a",
"ⱥ": "a",
"Ǽ": "a",
"ǽ": "a",
"Ǣ": "a",
"ǣ": "a",
"Ḃ": "b",
"ḃ": "b",
"Ḅ": "b",
"ḅ": "b",
"Ḇ": "b",
"ḇ": "b",
"Ƀ": "b",
"ƀ": "b",
"ᵬ": "b",
"Ɓ": "b",
"ɓ": "b",
"Ƃ": "b",
"ƃ": "b",
"Ć": "c",
"ć": "c",
"Ĉ": "c",
"ĉ": "c",
"Č": "c",
"č": "c",
"Ċ": "c",
"ċ": "c",
"Ç": "c",
"ç": "c",
"Ḉ": "c",
"ḉ": "c",
"Ȼ": "c",
"ȼ": "c",
"Ƈ": "c",
"ƈ": "c",
"ɕ": "c",
"Ď": "d",
"ď": "d",
"Ḋ": "d",
"ḋ": "d",
"Ḑ": "d",
"ḑ": "d",
"Ḍ": "d",
"ḍ": "d",
"Ḓ": "d",
"ḓ": "d",
"Ḏ": "d",
"ḏ": "d",
"Đ": "d",
"đ": "d",
"ᵭ": "d",
"Ɖ": "d",
"ɖ": "d",
"Ɗ": "d",
"ɗ": "d",
"Ƌ": "d",
"ƌ": "d",
"ȡ": "d",
"ð": "d",
"É": "e",
"Ə": "e",
"Ǝ": "e",
"ǝ": "e",
"é": "e",
"È": "e",
"è": "e",
"Ĕ": "e",
"ĕ": "e",
"Ê": "e",
"ê": "e",
"Ế": "e",
"ế": "e",
"Ề": "e",
"ề": "e",
"Ễ": "e",
"ễ": "e",
"Ể": "e",
"ể": "e",
"Ě": "e",
"ě": "e",
"Ë": "e",
"ë": "e",
"Ẽ": "e",
"ẽ": "e",
"Ė": "e",
"ė": "e",
"Ȩ": "e",
"ȩ": "e",
"Ḝ": "e",
"ḝ": "e",
"Ę": "e",
"ę": "e",
"Ē": "e",
"ē": "e",
"Ḗ": "e",
"ḗ": "e",
"Ḕ": "e",
"ḕ": "e",
"Ẻ": "e",
"ẻ": "e",
"Ȅ": "e",
"ȅ": "e",
"Ȇ": "e",
"ȇ": "e",
"Ẹ": "e",
"ẹ": "e",
"Ệ": "e",
"ệ": "e",
"Ḙ": "e",
"ḙ": "e",
"Ḛ": "e",
"ḛ": "e",
"Ɇ": "e",
"ɇ": "e",
"ɚ": "e",
"ɝ": "e",
"Ḟ": "f",
"ḟ": "f",
"ᵮ": "f",
"Ƒ": "f",
"ƒ": "f",
"Ǵ": "g",
"ǵ": "g",
"Ğ": "g",
"ğ": "g",
"Ĝ": "g",
"ĝ": "g",
"Ǧ": "g",
"ǧ": "g",
"Ġ": "g",
"ġ": "g",
"Ģ": "g",
"ģ": "g",
"Ḡ": "g",
"ḡ": "g",
"Ǥ": "g",
"ǥ": "g",
"Ɠ": "g",
"ɠ": "g",
"Ĥ": "h",
"ĥ": "h",
"Ȟ": "h",
"ȟ": "h",
"Ḧ": "h",
"ḧ": "h",
"Ḣ": "h",
"ḣ": "h",
"Ḩ": "h",
"ḩ": "h",
"Ḥ": "h",
"ḥ": "h",
"Ḫ": "h",
"ḫ": "h",
"̱": "h",
"ẖ": "h",
"Ħ": "h",
"ħ": "h",
"Ⱨ": "h",
"ⱨ": "h",
"Í": "i",
"í": "i",
"Ì": "i",
"ì": "i",
"Ĭ": "i",
"ĭ": "i",
"Î": "i",
"î": "i",
"Ǐ": "i",
"ǐ": "i",
"Ï": "i",
"ï": "i",
"Ḯ": "i",
"ḯ": "i",
"Ĩ": "i",
"ĩ": "i",
"İ": "i",
"Į": "i",
"į": "i",
"Ī": "i",
"ī": "i",
"Ỉ": "i",
"ỉ": "i",
"Ȉ": "i",
"ȉ": "i",
"Ȋ": "i",
"ȋ": "i",
"Ị": "i",
"ị": "i",
"Ḭ": "i",
"ḭ": "i",
"ı": "i",
"Ɨ": "i",
"ɨ": "i",
"Ĵ": "j",
"ĵ": "j",
"̌": "j",
"ǰ": "j",
"ȷ": "j",
"Ɉ": "j",
"ɉ": "j",
"ʝ": "j",
"ɟ": "j",
"ʄ": "j",
"Ḱ": "k",
"ḱ": "k",
"Ǩ": "k",
"ǩ": "k",
"Ķ": "k",
"ķ": "k",
"Ḳ": "k",
"ḳ": "k",
"Ḵ": "k",
"ḵ": "k",
"Ƙ": "k",
"ƙ": "k",
"Ⱪ": "k",
"ⱪ": "k",
"Ĺ": "a",
"ĺ": "l",
"Ľ": "l",
"ľ": "l",
"Ļ": "l",
"ļ": "l",
"Ḷ": "l",
"ḷ": "l",
"Ḹ": "l",
"ḹ": "l",
"Ḽ": "l",
"ḽ": "l",
"Ḻ": "l",
"ḻ": "l",
"Ł": "l",
"ł": "l",
"Ł": "l",
"̣": "l",
"ł": "l",
"̣": "l",
"Ŀ": "l",
"ŀ": "l",
"Ƚ": "l",
"ƚ": "l",
"Ⱡ": "l",
"ⱡ": "l",
"Ɫ": "l",
"ɫ": "l",
"ɬ": "l",
"ɭ": "l",
"ȴ": "l",
"Ḿ": "m",
"ḿ": "m",
"Ṁ": "m",
"ṁ": "m",
"Ṃ": "m",
"ṃ": "m",
"ɱ": "m",
"Ń": "n",
"ń": "n",
"Ǹ": "n",
"ǹ": "n",
"Ň": "n",
"ň": "n",
"Ñ": "n",
"ñ": "n",
"Ṅ": "n",
"ṅ": "n",
"Ņ": "n",
"ņ": "n",
"Ṇ": "n",
"ṇ": "n",
"Ṋ": "n",
"ṋ": "n",
"Ṉ": "n",
"ṉ": "n",
"Ɲ": "n",
"ɲ": "n",
"Ƞ": "n",
"ƞ": "n",
"ɳ": "n",
"ȵ": "n",
"̈": "n",
"̈": "n",
"Ó": "o",
"ó": "o",
"Ò": "o",
"ò": "o",
"Ŏ": "o",
"ŏ": "o",
"Ô": "o",
"ô": "o",
"Ố": "o",
"ố": "o",
"Ồ": "o",
"ồ": "o",
"Ỗ": "o",
"ỗ": "o",
"Ổ": "o",
"ổ": "o",
"Ǒ": "o",
"ǒ": "o",
"Ö": "o",
"ö": "o",
"Ȫ": "o",
"ȫ": "o",
"Ő": "o",
"ő": "o",
"Õ": "o",
"õ": "o",
"Ṍ": "o",
"ṍ": "o",
"Ṏ": "o",
"ṏ": "o",
"Ȭ": "o",
"ȭ": "o",
"Ȯ": "o",
"ȯ": "o",
"Ȱ": "o",
"ȱ": "o",
"Ø": "o",
"ø": "o",
"Ǿ": "o",
"ǿ": "o",
"Ǫ": "o",
"ǫ": "o",
"Ǭ": "o",
"ǭ": "o",
"Ō": "o",
"ō": "o",
"Ṓ": "o",
"ṓ": "o",
"Ṑ": "o",
"ṑ": "o",
"Ỏ": "o",
"ỏ": "o",
"Ȍ": "o",
"ȍ": "o",
"Ȏ": "o",
"ȏ": "o",
"Ơ": "o",
"ơ": "o",
"Ớ": "o",
"ớ": "o",
"Ờ": "o",
"ờ": "o",
"Ỡ": "o",
"ỡ": "o",
"Ở": "o",
"ở": "o",
"Ợ": "o",
"ợ": "o",
"Ọ": "o",
"ọ": "o",
"Ộ": "o",
"ộ": "o",
"Ɵ": "o",
"ɵ": "o",
"Ṕ": "p",
"ṕ": "p",
"Ṗ": "p",
"ṗ": "p",
"Ᵽ": "p",
"Ƥ": "p",
"ƥ": "p",
"̃": "p",
"̃": "p",
"ʠ": "q",
"Ɋ": "q",
"ɋ": "q",
"Ŕ": "r",
"ŕ": "r",
"Ř": "r",
"ř": "r",
"Ṙ": "r",
"ṙ": "r",
"Ŗ": "r",
"ŗ": "r",
"Ȑ": "r",
"ȑ": "r",
"Ȓ": "r",
"ȓ": "r",
"Ṛ": "r",
"ṛ": "r",
"Ṝ": "r",
"ṝ": "r",
"Ṟ": "r",
"ṟ": "r",
"Ɍ": "r",
"ɍ": "r",
"ᵲ": "r",
"ɼ": "r",
"Ɽ": "r",
"ɽ": "r",
"ɾ": "r",
"ᵳ": "r",
"ß": "s",
"Ś": "s",
"ś": "s",
"Ṥ": "s",
"ṥ": "s",
"Ŝ": "s",
"ŝ": "s",
"Š": "s",
"š": "s",
"Ṧ": "s",
"ṧ": "s",
"Ṡ": "s",
"ṡ": "s",
"ẛ": "s",
"Ş": "s",
"ş": "s",
"Ṣ": "s",
"ṣ": "s",
"Ṩ": "s",
"ṩ": "s",
"Ș": "s",
"ș": "s",
"ʂ": "s",
"̩": "s",
"̩": "s",
"Þ": "t",
"þ": "t",
"Ť": "t",
"ť": "t",
"̈": "t",
"ẗ": "t",
"Ṫ": "t",
"ṫ": "t",
"Ţ": "t",
"ţ": "t",
"Ṭ": "t",
"ṭ": "t",
"Ț": "t",
"ț": "t",
"Ṱ": "t",
"ṱ": "t",
"Ṯ": "t",
"ṯ": "t",
"Ŧ": "t",
"ŧ": "t",
"Ⱦ": "t",
"ⱦ": "t",
"ᵵ": "t",
"ƫ": "t",
"Ƭ": "t",
"ƭ": "t",
"Ʈ": "t",
"ʈ": "t",
"ȶ": "t",
"Ú": "u",
"ú": "u",
"Ù": "u",
"ù": "u",
"Ŭ": "u",
"ŭ": "u",
"Û": "u",
"û": "u",
"Ǔ": "u",
"ǔ": "u",
"Ů": "u",
"ů": "u",
"Ü": "u",
"ü": "u",
"Ǘ": "u",
"ǘ": "u",
"Ǜ": "u",
"ǜ": "u",
"Ǚ": "u",
"ǚ": "u",
"Ǖ": "u",
"ǖ": "u",
"Ű": "u",
"ű": "u",
"Ũ": "u",
"ũ": "u",
"Ṹ": "u",
"ṹ": "u",
"Ų": "u",
"ų": "u",
"Ū": "u",
"ū": "u",
"Ṻ": "u",
"ṻ": "u",
"Ủ": "u",
"ủ": "u",
"Ȕ": "u",
"ȕ": "u",
"Ȗ": "u",
"ȗ": "u",
"Ư": "u",
"ư": "u",
"Ứ": "u",
"ứ": "u",
"Ừ": "u",
"ừ": "u",
"Ữ": "u",
"ữ": "u",
"Ử": "u",
"ử": "u",
"Ự": "u",
"ự": "u",
"Ụ": "u",
"ụ": "u",
"Ṳ": "u",
"ṳ": "u",
"Ṷ": "u",
"ṷ": "u",
"Ṵ": "u",
"ṵ": "u",
"Ʉ": "u",
"ʉ": "u",
"Ṽ": "v",
"ṽ": "v",
"Ṿ": "v",
"ṿ": "v",
"Ʋ": "v",
"ʋ": "v",
"Ẃ": "w",
"ẃ": "w",
"Ẁ": "w",
"ẁ": "w",
"Ŵ": "w",
"ŵ": "w",
"̊": "w",
"ẘ": "w",
"Ẅ": "w",
"ẅ": "w",
"Ẇ": "w",
"ẇ": "w",
"Ẉ": "w",
"ẉ": "w",
"Ẍ": "x",
"ẍ": "x",
"Ẋ": "x",
"ẋ": "x",
"Ý": "y",
"ý": "y",
"Ỳ": "y",
"ỳ": "y",
"Ŷ": "y",
"ŷ": "y",
"̊": "y",
"ẙ": "y",
"Ÿ": "y",
"ÿ": "y",
"Ỹ": "y",
"ỹ": "y",
"Ẏ": "y",
"ẏ": "y",
"Ȳ": "y",
"ȳ": "y",
"Ỷ": "y",
"ỷ": "y",
"Ỵ": "y",
"ỵ": "y",
"ʏ": "y",
"Ɏ": "y",
"ɏ": "y",
"Ƴ": "y",
"ƴ": "y",
"Ź": "z",
"ź": "z",
"Ẑ": "z",
"ẑ": "z",
"Ž": "z",
"ž": "z",
"Ż": "z",
"ż": "z",
"Ẓ": "z",
"ẓ": "z",
"Ẕ": "z",
"ẕ": "z",
"Ƶ": "z",
"ƶ": "z",
"Ȥ": "z",
"ȥ": "z",
"ʐ": "z",
"ʑ": "z",
"Ⱬ": "z",
"ⱬ": "z",
"Ǯ": "z",
"ǯ": "z",
"ƺ": "z",
"2": "2",
"6": "6",
"B": "B",
"F": "F",
"J": "J",
"N": "N",
"R": "R",
"V": "V",
"Z": "Z",
"b": "b",
"f": "f",
"j": "j",
"n": "n",
"r": "r",
"v": "v",
"z": "z",
"1": "1",
"5": "5",
"9": "9",
"A": "A",
"E": "E",
"I": "I",
"M": "M",
"Q": "Q",
"U": "U",
"Y": "Y",
"a": "a",
"e": "e",
"i": "i",
"m": "m",
"q": "q",
"u": "u",
"y": "y",
"0": "0",
"4": "4",
"8": "8",
"D": "D",
"H": "H",
"L": "L",
"P": "P",
"T": "T",
"X": "X",
"d": "d",
"h": "h",
"l": "l",
"p": "p",
"t": "t",
"x": "x",
"3": "3",
"7": "7",
"C": "C",
"G": "G",
"K": "K",
"O": "O",
"S": "S",
"W": "W",
"c": "c",
"g": "g",
"k": "k",
"o": "o",
"s": "s",
"w": "w"
};
function fold(term) {
var ret = "";
for ( var i = 0; i < term.length; i++ ) {
ret += map[ term.charAt(i) ] || term.charAt(i);
}
return ret;
}
var term = term || (typeof(this)!='function' && $(this).getText());
return fold(term);
};
})(jQuery);
(function($){
$.punctuationFolding = $.fn.punctuationFolding = function(term){
var punctuation_regexp = new RegExp(/[\s-+*.,;:/\|_)(\]\\[}{><]{1,}/g);
return term.replace(punctuation_regexp," ");
}
})(jQuery);
(function($){
/*
* jQuery delayed event execution.
*
* http://ihatecode.blogspot.com/2008/04/jquery-time-delay-event-binding-plugin.html
* Usage :
$("#my-input").eventDelay({
delay: 500,
event: 'keyup',
fn: function(){
alert(this.value);
}
});
*/
$.fn.eventDelay = function(options) {
var timer;
var delayImpl = function(domElem,eventObj) {
if (timer != null) {
clearTimeout(timer);
}
var newFn = function() {
options.fn.call(domElem, eventObj);
};
timer = setTimeout(newFn, options.delay);
}
return this.each2(function() {
var obj = $(this);
obj.bind(options.event, function(eventObj) {
delayImpl(this,eventObj);
});
});
};
})(jQuery);
(function($) {
// Authors: Andreas Bjarlestam and Nowhere Man
// Sources: http://stackoverflow.com/questions/119441/highlight-a-word-with-jquery#2676556
// jQuery UI Autocomplete
$.fn.highlight = function(term, className) {
var regex = new RegExp(term, "gi");
if (!className)
className = "";
return this.each2(function() {
var $this = $(this);
$this.html($this.html().replace(regex, function(matched) {return "<strong class=\"" + className + "\">" + matched + "</strong>";}));
});
};
$.fn.highlightEach = function(term, className) {
var self = this;
if (!className)
className = "";
var $terms = $(term.split(new RegExp("[ ,-.;:\\\/]")));
$terms.each2(function(i,v){
var regex = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + v.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "i");
return self.each2(function() {
var $this = $(this);
if (i == 0) {
$this.html($this.text());
}
$this.html($this.html().replace(regex, "<strong class=\"" + className + "\">$1</strong>"));
});
});
return this;
};
})(jQuery);
/***************************************************************************
* Copyright (C) 2009 by Vladimir Kadalashvili
* Kadalashvili.Vladimir@gmail.com
* Copyright (C) 2012 by Nowhere Man
* https://github.com/nowhereman
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
(function($) {
$.fn.combo = function(config) {
// Check if Comb'O is already activated
var api = $(this).eq(typeof conf == 'number' ? conf : 0).data("combo");
$(this).hide();// Ensure that the selectbox is hidden
if(api) return api;
//default config options
var defaultConf = {
//skin name
skin: "default",
//this suffix will be appended to the selectbox's name and will be text input's name
suffix: "__combo",
//the same as the previous, but for hidden input
hiddenSuffix: "__comboHidden",
//initial / default hidden field value.
//Also applied when user types something that is not in the options list
initialHiddenValue: "",
//if provided, will be the value of the text input when it has no value and focus
emptyText: "",
//this suffix will be appended to the lazy selectbox's name
lazySuffix: "__lazy",
//if true, don't parse the selectbox options until combobox get the focus
lazyLoading: false,
//if true, autofilling will be enabled
autoFill: false,
//if true, selected option of the selectbox will be the initial value of the combo
triggerSelected: false,
//function for options filtering
filterFn: null,
//if true, the options list will be placed above text input
dropUp: false,
//set the max height for the list, by default it take the css value of max-height attribute
maxHeight:false,
//if true, search term will be highlighted in the result list
highlightTerm: false,
//delay to trigger the keyPress function
keyPressDelay: 300,
//if true, filter function will be sensitive to the case
//Warning: if liquidFilter option is true, filtering function will be always case insensitive
caseSensitive: false,
//if false, filter function will be insensitive to accents characters
accentsSensitive: true,
//if false, filter function will be insensitive to punctuation characters
punctuationSensitive: true,
//if true, search term will be filtered by Quicksilver scoring algorithm clone, require liquidmetal.js plugin
liquidFilter: false,
//if true liquidFilter will be sort by score instead of items text
liquidSorting: true,
//if true all items of the list will be always show, even when you filtering the comboBox
showAllMode: false,
//if true all items of the list will be show only if you press non-alpha-numeric keys (e.g. Up, Down, Tab...)
showMixMode: true,
//current mode, if true show all the list, if false show only the filtering items
showMode: false,
//if true show all the items when we click or focus in the input
showList: true,
//default last text value
lastTextValue: "",
//separator for values of multiple combos
separator: ",",
//all callback functions are called in the scope of the current combo instance
//called after dropdown list appears
showListCallback: null,
//called after dropdown list disappears
hideListCallback: null,
//called at the end of constructor
initCallback: null,
//called at the end of initEvents function
initEventsCallback: null,
//called when both text and hidden inputs values are changed
changeCallback: null,
//called when text input's value is changed
textChangeCallback: null
};
$.extend(defaultConf, config);
this.each2(function() {
var selectbox = $(this);
var regex = new RegExp("^(.+)" + defaultConf.lazySuffix + "$", "i");
if (!selectbox.attr("id").match(regex) && defaultConf.lazyLoading) {
var optionSelected = selectbox.find("option:selected:first");
var data = [];
if (optionSelected)
data.push({value: optionSelected.val(), text: optionSelected.text()});
// TODO DRY Already exist in $.combo constructor but we also need it here
var wrapper = selectbox.wrap("<div>").
hide().
parent().
addClass("combo").
addClass(defaultConf.skin);
$.extend(defaultConf, {
// name: selectbox.attr('name'),
id: selectbox.attr('id'),
container: wrapper,
data: data});
$cb.create(defaultConf);
} else {
var el = new $cb(this[0], defaultConf);
selectbox.data("combo", el);
if (el.config.lazyLoading) {
selectbox.hide();
var targetSelectbox = $("#" + selectbox.attr("id").replace(regex, "$1"));
targetSelectbox.data('combo', el);
var $selectbox = targetSelectbox;//.clone();
$selectbox.data('combo', el);
el.wrapper.prepend($selectbox);
//targetSelectbox.remove();
}
}
});
if (this.length == 1) {
return this.data("combo");
}
};
//constructor
//creates initial markup and does some initialization
$.combo = function(selectbox, config) {
if (selectbox.nodeName != "SELECT")
return;
this.config = ( config || {});
this.selectbox = $(selectbox);
var $selectbox;
if (this.config.lazyLoading) {
var regex = new RegExp("^(.+)" + this.config.lazySuffix + "$", "i");
$selectbox = $("#" + this.selectbox.attr("id").replace(regex, "$1"));
} else {
$selectbox = this.selectbox;
}
this.selectHash = $selectbox.get(0).length + $selectbox.find('option:first').text() + $selectbox.find('option:last').text();
this.options = this.selectbox.find("option");
if (config.lazyLoading) {
var regex2 = new RegExp("^(.+)" + this.config.lazySuffix + "$", "i");
// this.config.name = this.selectbox.attr("name").replace(regex2, "$1");
this.config.id = this.selectbox.attr("id").replace(regex2, "$1");
//console.log(this.config.lazySuffix + ":" + this.config.name + "/" + this.config.id);//debug
} else {
this.config.name = this.selectbox.attr("name");
this.config.id = this.selectbox.attr("id");
}
// Check if the wrapper already exist before created it, usefull for lazy loading system
if ((this.wrapper = this.selectbox.parent("div.combo." + this.config.skin)).length == 0) {
this.wrapper = this.selectbox.wrap("<div>").
hide().
parent().
addClass("combo").
addClass(this.config.skin);
}
this.input = $("<input type='text'></input>").
appendTo(this.wrapper).
attr("autocomplete", "off").
attr("id", this.config.id + this.config.suffix).
val("");
this.inputOffset = this.input.offset();
this.hidden = $("<input type='hidden'></input>").
appendTo(this.wrapper).
attr("autocomplete", "off").
attr("id", this.config.id + this.config.hiddenSuffix).
val(this.config.initialHiddenValue);
this.icon = $("<div></div>").
appendTo(this.wrapper).
addClass("icon");
this.listWrapper = $("<div></div>").
appendTo(this.wrapper).
addClass("invisible").
addClass("list-wrapper");
this.updateDrop();
this.createListItems();
if ($.browser.opera) {
this.wrapper.css({
position: "relative",
left: "0",
top: "0"
});
}
this.filterFn = ("function" == typeof(this.config.filterFn)) ? this.config.filterFn : this.filterFn;
this.config.caseSensitive = (this.config.liquidFilter) ? false : this.config.caseSensitive;
this.config.liquidSorting = (!this.config.liquidFilter) ? false : this.config.liquidSorting;
this.config.showMixMode = (this.config.showAllMode) ? true : this.config.showMixMode;
this.lastKey = null;
this.lastPageY = null;
// this.overflowCSS = ($.browser.opera) ? "overflow" : "overflowY";
this.overflowCSS = "overflowY"; // Tested successfully with Opera 9.6 and 10
this.setListMaxHeight();
this.multiple = this.selectbox.attr("multiple");
this.notify("init");
this.initEvents();
};
//shortcuts
$cb = $.combo;
$cb.fn = $cb.prototype = {};
$cb.fn.extend = $cb.extend = $.extend;
$cb.fn.extend({
// Trigger native event because jQuery doesn't trigger somes
triggerEvent: function(element, eventName) {
var evt;
// Firefox and others
if (document.createEvent)
{
evt = document.createEvent('HTMLEvents');
evt.initEvent(eventName, true, true);
//console.log("change");//debug
return element.dispatchEvent(evt);
}
// IEs
if (element.fireEvent && document.createEventObject) {
evt = document.createEventObject();
return element.fireEvent('on' + eventName, evt);
}
},
//TOC of our plugin
//initializes all event listeners
//it would be more correct to call it initEvents
initEvents: function() {
var self = this;
//Refresh ListItems
//TODO make the interval seconds as a config option
setInterval(function(){
var $selectbox;
if (self.config.lazyLoading) {
var regex = new RegExp("^(.+)" + self.config.lazySuffix + "$", "i");
$selectbox = $("#" + self.selectbox.attr("id").replace(regex, "$1"));
} else
$selectbox = self.selectbox;
var selectHash = $selectbox.get(0).length + $selectbox.find('option:first').text() + $selectbox.find('option:last').text();
if (selectHash != self.selectHash) {
if (self.config.lazyLoading)
self.loadListItems();
else
self.refreshListItems();
}
}, 1500);
//this.selectbox.change(function(){
//console.log("selectBox changeEvent");
//self.refreshListItems();
//});
this.icon.bind("click", function() {
self.iconClick();
});
$(document).bind("mouseup", function(e) {
if (self.icon.get(0) == e.target || self.input.get(0) == e.target || self.listWrapper.get(0) == e.target) {
return;
}
self.hideList();
});
this.list.bind("mouseover", function(e) {
if ($(e.target).hasClass("li-bold")) {
return $(e.target).parent('li').trigger('mouseover');
}
if (e.target.tagName == 'LI') {
var target = $(e.target);
//console.log( Math.abs(self.lastPageY - e.pageY) + " > " + target.height()*1.25);//debug
if ( Math.abs(self.lastPageY - e.pageY) > target.height()*1.25 ) {
self.lastKey = null;
self.lastPageY = null;
}
if(self.lastKey === null){
self.lastPageY = e.pageY;
}
self.highlight(target);
} else {
e.stopPropagation();
}
})
.bind("mouseup", function(e) {
// bind for left click only
if(e.which==1) {
if ($(e.target).hasClass("li-bold")) {
return $(e.target).parent('li').trigger('mouseup');
}
if (e.target.tagName == 'LI') {
self.listItemClick($(e.target));
} else {
e.stopPropagation();
}
}
})
.bind("mouseleave", function(e) {
if(self.listVisible())
self.lastKey = null;
});
this.input
.bind("keydown", function(e) {
self.keyPress(e);
})
.bind("keypress", function(e) {
if ($cb.KEY.RETURN == e.keyCode) {
e.preventDefault();
}
if ($cb.KEY.SHIFT == e.keyCode) {
e.preventDefault();
}
if ($cb.KEY.TAB == e.keyCode) {
if (self.listVisible()) {
e.preventDefault();
}
}
})
.eventDelay({
delay: self.config.keyPressDelay,
event: 'keyup',
fn: function(e){
self.keyPress(e);
}
})
.eventDelay({
delay: self.config.keyPressDelay,
event: 'paste',
fn: function(e){
self.keyPress(e);
}
})
.eventDelay({
delay: self.config.keyPressDelay,
event: 'drop',
fn: function(e){
self.keyPress(e);
}
}).bind("mouseup", function(e) {
// bind for left click only
if(e.which==1) {
//console.log("left click in input!");//debug
if(!self.listVisible())
self.loadListItems();
e.preventDefault();
}
});
this.wrapper.focusin(function(e) {
//console.log("focusIn() wrapper");//debug
if(!self.listVisible())
self.loadListItems();//FIXME make noise with the input text selection in IE
});
this.triggerSelected();
this.applyEmptyText();
this.notify("initEvents");
},
results: [],
getTextValue: function() {
return this.__getValue("input");
},
getCurrentTextValue: function() {
return this.__getCurrentValue("input");
},
setLastTextValue: function(val){
if(val!=this.config.lastTextValue) {
this.config.lastTextValue=val;
}
},
getHiddenValue: function() {
return this.__getValue("hidden");
},
getCurrentHiddenValue: function() {
return this.__getCurrentValue("hidden");
},
__getValue: function(prop) {
prop = this[prop];
if (!this.multiple)
return $.trim(prop.val());
var tmpVals = prop.val().split(this.config.separator);
var vals = [];
for (var i = 0, len = tmpVals.length; i < len; ++i) {
vals.push($.trim(tmpVals[i]));
}
vals = $cb.normalizeArray(vals);
return vals;
},
__getCurrentValue: function(prop) {
prop = this[prop];
if (!this.multiple)
return $.trim(prop.val());
return $.trim(prop.val().split(this.config.separator).pop());
},
//icon click event listener
iconClick: function() {
this.inputFocus();
if (this.listVisible()) {
this.showList(this.config.showMixMode);
if(this.config.highlightTerm) {
this.highlightTermFn();
}
} else {
this.loadListItems();//TODO DRY wrapper.focusIn() already do that!
if( !this.config.showList ) {
if (this.listVisible()) {
this.hideList();
} else {
this.showList(this.config.showMixMode);
if(this.config.highlightTerm) {
this.highlightTermFn();
}
}
}
}
return this;
},
//returns true when dropdown list is visible
listVisible: function() {
return this.listWrapper.hasClass("visible");
},
//shows dropdown list
showList: function(showMode) {
if(!showMode) {
showMode = this.config.showAllMode;
}
if (!showMode && this.list.find(".visible").length == 0) {
return;
}
this.config.showMode = showMode;
this.listWrapper.removeClass("invisible").
addClass("visible");
this.wrapper.css("zIndex", "99999");
this.listWrapper.css("zIndex", "99999");
if(showMode) {
this.list.find("li").removeClass("invisible").
addClass("visible");
if(this.config.liquidSorting) {
this.restoreListItems("all");
}
this.setOverflow();
this.setListHeight();
} else{
this.setOverflow();
this.setListHeight();
this.highlightFirst();
this.listWrapper.scrollTop(0);
}
// Determine automatically whether options list should go down or up, backported from Sexy Combo 2.0.7
var listHeight = this.listWrapper.height();
var inputHeight = this.wrapper.height();
var bottomPos = parseInt(this.inputOffset.top) + inputHeight + listHeight;
var maxShow = $(window).height() + $(document).scrollTop();
if (bottomPos > maxShow) {
this.setDropUp(true);
}
else {
this.setDropUp(false);
}
if((showMode || this.getTextValue().length == 0)) {
//console.info("highlight");//debug
this.selectedItem();//debug
//console.info(this.getSelected().text());//debug
this.highlight(this.getSelected(), showMode);
}
this.list.find("ul.optgroup span.label").each2(function(){
if($(this).nextAll().hasClass("invisible")) {
$(this).removeClass("visible").addClass("invisible");
} else if($(this).nextAll().hasClass("visible")) {
$(this).removeClass("invisible").addClass("visible");
}
});
if($.browser.msie && $.browser.version < 9){
this.list.find("ul.optgroup").each2(function(){
var els= $(this).find(".visible");
var optHeight=0;
$.each(els, function(i,n){
optHeight+= $(n).height();
});
$(this).height(optHeight);
});
}
//console.log(this.listWrapper.scrollTop());//debug
var maxScroll = this.getActiveIndex() * this.list.find("ul.optgroup span.label, li").height();
//console.log(maxScroll);//debug
(this.listWrapper.scrollTop() > maxScroll) ? this.scrollUp(): this.scrollDown();
this.notify("showList");
return this;
},
//hides dropdown list
hideList: function() {
if (this.listWrapper.hasClass("invisible"))
return;
this.listWrapper.removeClass("visible").
addClass("invisible");
this.wrapper.css("zIndex", "0");
this.listWrapper.css("zIndex", "99999");
this.lastKey = null;
this.notify("hideList");
return this;
},
//returns sum of all visible items height
getListItemsHeight: function() {
return this.list.find("li:first").height() * this.visibleItemsLen();
},
//changes list wrapper's overflow from hidden to scroll and vice versa (depending on list items height))
setOverflow: function() {
if (this.getListItemsHeight() > this.getListMaxHeight())
this.listWrapper.css(this.overflowCSS, "scroll");
else
this.listWrapper.css(this.overflowCSS, "hidden");
},
//highlights active item of the dropdown list
highlight: function(activeItem, force) {
//Prevent mouse noising
if (!force && (($cb.KEY.DOWN == this.lastKey) || ($cb.KEY.UP == this.lastKey) || ($cb.KEY.TAB == this.lastKey)))
return;
//Old behaviour to scroll to the last active item and not to the last selected item
/*if(!force){
this.listItems.removeClass("active");
$(activeItem).addClass("active");
}
if(this.listItems.not("li.selected").filter("li.active").length == 0)
this.list.find("li.selected").addClass("active");*/
this.listItems.removeClass("active");
$(activeItem).addClass("active");
if(this.listItems.filter("li.active").length == 0)
this.list.find("li.selected").addClass("active");
},
highlightTermFn: function(term) {
if(!term)
term = this.getCurrentTextValue();
var showMode = this.config.showMode;
//console.log(showMode);//debug
// if(term.length == 0) {
this.clearHighlightTerms();
// } else
if(term.length > 1) {
if(showMode) {
this.list.find("li.visible").highlight(term,'li-bold');
} else {
this.list.find("li.visible").highlightEach(term,'li-bold');
}
}
this.notify("highlightTermFn");
},
clearHighlightTerms: function(){
// TODO Use a CSS class to highlight terms and remove the class here
this.list.find("li").each2(function(){
$(this).text($(this).text());//remove HTML tags
});
},
//sets text and hidden inputs value
setComboValue: function(val, pop, hideList) {
//console.log("setComboValue:" + val + ";" + this.input.val() + ";"+this.getHiddenValue());//debug
//console.log(this.getSelected().text());//debug
var self = this;
var oldVal = this.input.val();
var oldHiddenVal = this.getHiddenValue();
var v = "";
if (this.multiple) {
v = this.getTextValue();
if (pop)
v.pop();
v.push($.trim(val));
v = $cb.normalizeArray(v);
v = v.join(this.config.separator) + this.config.separator;
}
else {
v = $.trim(val);
}
this.input.val(v);
this.setHiddenValue(val);
// this.filter();//Disable because it's slowdown and already call by inputChanged()
if (hideList)
this.hideList();
this.input.removeClass("empty");
if (this.multiple)
this.inputFocus();
if (this.input.val() != oldVal || oldHiddenVal != this.getHiddenValue()){
// NOTICE removeAttr() is buggy with with jQuery 1.6.x when 'this.config.lazyLoading' option is set to true
this.options.filter("option:selected").attr("selected", undefined); // Removing all old selected options
//selecting options in the selectbox
if (this.multiple) {
$(this.getHiddenValue()).each2(function(i,v){
self.options.each2(function(){
if($(this).val() == v)
$(this).attr("selected",true);
});
});
}
else {
// GM_log(this.config.lazyLoading + "/" + this.selectbox.attr("id") + "/set selectbox.val with :" + this.getCurrentHiddenValue());//debug
//console.log(this.getHiddenValue());//debug
this.selectbox.val(this.getHiddenValue());
}
// GM_log(this.selectbox.attr("id") + "/selectbox.val:" + this.selectbox.val());//debug
if (this.config.triggerSelected && oldHiddenVal != this.getHiddenValue() && oldHiddenVal.length > 0) {
//console.log(oldHiddenVal + '|' + this.getHiddenValue() + '/ trigger change'); // debug
// this.selectbox.trigger('change');
var selectbox = this.selectbox;
var selectPosition = selectbox.css("position");
var selectVisibility = selectbox.css("visibility");
// Show temporary the select box otherwise some websites with javascript events attached to it don't work
//E.g. https://spreadsheets.google.com/spreadsheet/lv?key=0Ak5XBUTLMpdocG1TS25OYVNvTHVOcUxqU0NTazhyZmc&type=view
selectbox.prependTo( this.wrapper.parent() ).css("position","absolute").css("visibility","hidden").show();
// TODO Trigger all events of SELECT tag with this function ?
this.triggerEvent(this.selectbox.get(0),"change");// TODO Make a config option ?
// Hide the select box and restore css values
selectbox.prependTo(this.wrapper).hide().css("visibility",selectVisibility).css("position",selectPosition);
// this.selectbox.get(0).element.onchange();
//console.log(this.selectbox);//debug
//var eventName = "change";
//this.selectbox.get(0).fireEvent('on' + eventName);
}
this.notify("textChange");
}
},
//sets hidden inputs value
//takes text input's value as a param
setHiddenValue: function(val) {
var set = false;
val = $.trim(val);
var oldVal = this.hidden.val();
if (!this.multiple) {
for (var i = 0, len = this.options.length; i < len; ++i) {
if (val == this.options.eq(i).text()) {
this.hidden.val(this.options.eq(i).val());
set = true;
break;
}
}
}
else {
var comboVals = this.getTextValue();
var hiddenVals = [];
for (var i = 0, len = comboVals.length; i < len; ++i) {
for (var j = 0, len1 = this.options.length; j < len1; ++j) {
if (comboVals[i] == this.options.eq(j).text()) {
hiddenVals.push(this.options.eq(j).val());
}
}
}
if (hiddenVals.length) {
set = true;
this.hidden.val(hiddenVals.join(this.config.separator));
}
}
if (!set) {
this.hidden.val(this.config.initialHiddenValue);
}
if (oldVal != this.hidden.val())
this.notify("change");
},
listItemClick: function(item) {
this.inputFocus();
this.setComboValue(item.text(), true, true);
if(!this.multiple) {
this.confirmSelection(this.input.get(0));
}
},
//adds / removes items to / from the dropdown list depending on combo's current value
filter: function() {
var comboValue = this.input.val();
this.setLastTextValue(comboValue);
var self = this;
/*if(this.config.highlightTerm)
this.clearHighlightTerms();*/
this.results = [];
// Better cache but activeItem isn't persistent
// var listItems;
// if (this.config.liquidSorting) {
// listItems = this.cacheListItems.clone();
// } else {
// listItems = this.list.find("li");
// }
this.list.find("li").each2(function() {
var $this = $(this);
var itemValue = $this.text();
var score;
if ((score=self.filterFn.call(self, self.getCurrentTextValue(), itemValue, self.getTextValue())) > 0.1) {
$this.removeClass("invisible").
addClass("visible");
//if(self.config.highlightTerm){
//$this.highlightEach(self.formatText(self.getCurrentTextValue()));
//self.notify("highlightTerm");
//itemValue=self.highlightTermFn(itemValue, self.getCurrentTextValue());
//$this.html(itemValue);
//}
if(self.config.showAllMode) {
self.highlight($this);
}
} else {
$this.removeClass("visible").
addClass("invisible");
}
if(self.config.liquidSorting) {
self.results.push({
//name: $.trim($(this).text()),
//index: i,
value: $(this).html(),
klass: $(this).attr("class"),
score: score,
optgroup: $(this).parent('ul.optgroup').length == 1
});
}
});
if(this.config.liquidSorting)
{
if(this.getTextValue().length > 0) {
this.restoreListItems();
} else {
this.restoreListItems("all");
}
}
this.setOverflow();
this.setListHeight();
},
//default dropdown list filtering function
filterFn: function(currentComboValue, itemValue, allComboValues) {
// TODO Improve performance with a cached array who store of all item values and combo values
var formatItemValue = this.formatText(itemValue);
currentComboValue = this.formatText(currentComboValue);
if (this.multiple) {
//exclude values that are already selected
for (var i = 0, len = allComboValues.length; i < len; ++i) {
if (formatItemValue == this.formatText(allComboValues[i])) {
return 0;
}
}
}
var result = null;
if(this.config.liquidFilter){
result = LiquidMetal.score(formatItemValue, currentComboValue);//LiquidMetal
// score = formatItemValue.score(currentComboValue);//Quicksilver, buggy with long string
} else {
/*var nativeLiquid = formatItemValue.localeCompare(currentComboValue);
if (nativeLiquid > -1 && ) {
result = 1.0;
} else*/ if(formatItemValue.search(currentComboValue) == 0) {
result = 1.0;
}
}
return result;
},
createListItems: function(){
var self = this;
if((this.list=this.listWrapper.children("ul:not(.optgroup)")).length === 0) {
this.list = $("<ul></ul>").appendTo(this.listWrapper);
}
this.options.each2(function(i) {
//if(i == 0)
//console.log("create option!");//debug
var optionText = $.trim($(this).text());
var parent=self.list;
var optGroup;
if((optGroup=$(this).parent('optgroup')).length == 1){
var groups=parent.find("span").filter(function(){
var $this=$(this);
if($this.text() == optGroup.attr('label'))
{
parent=$this.parent("ul");
return $this;
}
});
if(!groups.length)
{
parent=parent=$("<ul></ul>").addClass("optgroup").appendTo(parent);
$("<span></span>").text(optGroup.attr('label')).addClass("label visible").appendTo(parent);
}
}
$("<li></li>").
appendTo(parent).
text(optionText).
addClass("visible");
});
this.listItems = this.list.find("*");
this.cacheList = this.list.filter("*").clone(true).get();
this.cacheListItems = $(this.cacheList).find("li");
// Auto width
var selectbox = this.selectbox;
if (this.config.lazyLoading) {
var regex = new RegExp("^(.+)" + this.config.lazySuffix + "$", "i");
selectbox = $("#" + this.selectbox.attr("id").replace(regex, "$1"));
}
// To get the correct width, we need to display the selectbox temporarily in his original place
var maxWidth = selectbox.prependTo( this.wrapper.parent() ).show().outerWidth();
var floatStyle = selectbox.css('float');
selectbox.prependTo(this.wrapper).hide();
// if (maxWidth > this.listWrapper.width()) {
this.input.width(maxWidth-this.icon.width());
this.icon.css('left',maxWidth-this.icon.width());
//this.listWrapper.width(maxWrapperWidth);
this.wrapper.width(maxWidth);
// }
this.wrapper.css("float",floatStyle);
this.listWrapper.css("min-width",this.wrapper.width());
// IE fix
if($.browser.msie && $.browser.version > 6 && $.browser.version < 9) {
var maxWrapperWidth = selectbox.get(0).scrollWidth;
if (this.config.highlightTerm) {
maxWrapperWidth *= 1.10; // increase the width to 10% if highligthing term option is enabled
}
if (this.list.find("ul.optgroup").length > 0) {
var optLi = this.list.find('ul.optgroup li:first');
maxWrapperWidth += parseInt(optLi.css('padding-left'), 10); // increase the width there are some optgroup in the list
//console.log(maxWrapperWidth);//debug
}
this.listWrapper.width(maxWrapperWidth);
} else {
this.listWrapper.width("auto");
}
//this.list.width(this.listWrapper.width());
// Fix overflow of parents elements
this.listWrapper.parents().each2( function() {
var el = $(this);
if(el.css("overflow") == "hidden") {
el.css("overflow","inherit");
}
});
// Detect if browser is in Quirks mode
if(document.compatMode != 'CSS1Compat') {
this.input.height(this.icon.outerHeight());// Fix input height
}
if(!this.config.triggerSelected && this.getActive().length == 0){
this.highlightFirst();
}
},
// Lazy loading related
loadListItems: function(){
if (this.config.lazyLoading) {
this.config.lazyLoading = false;
var lazySelectBox = this.selectbox;
var regex = new RegExp("^(.+)" + this.config.lazySuffix + "$", "i");
this.selectbox = $("#" + this.selectbox.attr("id").replace(regex, "$1"));
// var options = this.selectbox.find("option");//debug
this.refreshListItems();
// Remove the lazy select element
lazySelectBox.remove();
if(this.config.emptyText)
this.applyEmptyText();
}
//console.log("loadListItems()!");//debug
this.showListItems();
},
refreshListItems: function(){
this.selectHash = this.selectbox.get(0).length + this.selectbox.find('option:first').text() + this.selectbox.find('option:last').text();
this.options = this.selectbox.find("option");//refresh selectBox options
this.list.empty();
this.createListItems();
this.triggerSelected();
},
showListItems: function() {
//console.log("showListItems()");//debug
if (this.config.showList && !this.listVisible()) {
this.showList(this.config.showMixMode).inputFocus().selection(this.input.get(0), 0, this.input.val().length);
if(this.config.highlightTerm) {
this.highlightTermFn();
}
}
return this;
},
restoreListItems: function(type) {
if(!type) {
type = "match";
}
switch (type) {
case "all":
if(this.config.liquidSorting)
this.selectedItem();
var activeIndex = this.getItemIndex();
//console.log("restoreListItems:" + activeIndex);//debug
this.list.empty().html($(this.cacheList).clone().children());
// $.each(this.results,function(k,v){
// var li = $("<li />").html(v.value).addClass(v.klass);
// if(v.optgroup)
// li = $("<ul class='optgroup' />").append(li);
// list.append(li);
// });
//this.refreshListItems();
break;
case "match":
this.results.sort(function(a, b) {
return b.score - a.score;
});
var $results = $(this.results);
//Slow system who fill list with sorted results but preserve selected/activeItem
var list = this.list.empty();
$results.each2(function(k,v){
var li = $("<li></li>").html(v.value).addClass(v.klass);
if(v.optgroup) {
li = $("<ul class='optgroup'></ul>").append(li);
}
list.append(li);
});
//End
//WIP Better performance but buggy
/*var sortedList = $("<ul></ul>");
var self = this;
$results.each2(function(k,v){
//console.log(v.index + " " + v.score);//debug
var li = self.cacheListItems.eq(v.index).clone().removeClass().addClass(v.klass);
if(v.optgroup) {
li = $("<ul class='optgroup'></ul>").append(li);
}
sortedList.append(li);
});
this.list.empty().html(sortedList.children());*/
//End
break;
default:
break;
}
this.listItems = this.list.find("*");
if(type == "all") {
this.listItems.removeClass("active");
var activeItem = this.list.find("li").eq(activeIndex);
if(activeItem) {
activeItem.addClass("active");
//activeItem.highlightEach(this.formatText(this.getCurrentTextValue()));
//self.notify("highlightTerm");
//activeItem.html(this.highlightTermFn(activeItem.text(), this.getCurrentTextValue()));
}
// if(activeItem.text() != this.getCurrentTextValue()){
// this.list.find("li.visible").each2(function(){
// $(this).html(self.highlightTermFn($(this).text(), self.getCurrentTextValue()));
// });
// }
}
this.notify("restoreListItems");
},
//remove the combobox and the selectbox
remove: function(){
this.wrapper.remove();
},
//remove the combobox and restore the selectbox
destroy: function(){
var $selectbox;
if(this.config.lazyLoading) {
var regex = new RegExp("^(.+)" + this.config.lazySuffix + "$", "i");
$selectbox = $("#" + this.selectbox.attr("id").replace(regex, "$1"));//.clone();
} else
$selectbox = this.selectbox;//.clone();
if(this.wrapper.get(0).previousSibling) {
$(this.wrapper.get(0).previousSibling).after($selectbox.show());
} else {
this.wrapper.parent().append($selectbox.show());
}
this.wrapper.remove();
$selectbox.removeData('combo');
},
//apply formatting options on text
formatText: function(val){
if(val && val.length > 0) {
if(!this.config.accentsSensitive) {
val = $.accentFolding(val);
// val = this.removeAccents(val);
}
if(!this.config.punctuationSensitive) {
val = $.punctuationFolding(val);
}
if(!this.config.caseSensitive) {
val = val.toLowerCase();
}
}
return val;
},
//returns integer value of list wrapper's max-height attribute
getListMaxHeight: function() {
if(typeof(this.config.maxHeight) != 'number') {
//console.log("No max-height value set, used the listItems height");//debug
this.setListMaxHeight(this.getListItemsHeight());
}
return this.config.maxHeight;
},
//set integer value of list wrapper's max-height
setListMaxHeight: function(maxHeight) {
if(!maxHeight) {
if($.browser.msie && $.browser.version <= 6) {
//console.log("IE6 maxHeight:" + this.listWrapper.css("height"));//debug
maxHeight = this.listWrapper.css("height");
} else {
maxHeight = this.listWrapper.css("max-height");
}
}
this.config.maxHeight = parseInt(maxHeight);
},
//corrects list wrapper's height depending on list items height
setListHeight: function() {
var liHeight = this.getListItemsHeight();
var maxHeight = this.getListMaxHeight();
var listHeight = this.listWrapper.height();
//console.log("liHeight:" + liHeight);
//console.log("maxHeight:" + maxHeight);
//console.log("listHeight:" + listHeight);
var newHeight;
if (liHeight < listHeight) {
newHeight = liHeight;
//this.list.find("ul.optgroup").height(liHeight);
} else if (liHeight > listHeight) {
newHeight = Math.min(maxHeight, liHeight);
}
if (newHeight) {
if ($.browser.msie && $.browser.version < 9) {
newHeight*=1.05;// Increase the height to 5% because Internet Explorer is buggy
}
this.listWrapper.height(newHeight);
}
},
keyPress: function(e) {
var k = $cb.KEY;
var blindKeys = [ k.SHIFT, k.CTRL, k.ALT, k.PAUSE, k.CAPS_LOCK,
k.SUPER, k.HYPER, k.CONTEXT, k.F1, k.F2, k.F3, k.F4, k.F5, k.F6,
k.F7, k.F8, k.F9, k.F10, k.F11, k.F12, k.INSERT, k.HOME, k.END,
k.NUM_LOCK, k.SCROLL_LOCK, k.UNKNOWN ]
var KEYDOWN = e.type == "keydown";
var KEYUP = e.type == "keyup";
var DROP = e.type == "drop";
var PASTE = e.type == "paste";
if (KEYDOWN) {
this.lastKey = e.keyCode;
}
switch (e.keyCode) {
case k.RETURN:
if (KEYDOWN)
{
// submit current form with the RETURN key only if the list isn't visible
if (this.listVisible()) {
e.preventDefault();
e.stopPropagation();
}
this.setComboValue(this.getActive().text(), true, true);
// if (!this.multiple && !this.config.autoFill) {
// this.input.blur();
// }
if(!this.multiple) {
this.confirmSelection(this.input.get(0));
}
}
break;
case k.DOWN:
if (KEYDOWN)
{
if (this.listVisible()) {
this.highlightNext();
} else
{
//this.lastKey = null;
this.showList(this.config.showMixMode);
if (this.config.highlightTerm) {
this.clearHighlightTerms();
this.highlightTermFn();
}
}
}
break;
case k.UP:
if (KEYDOWN)
{
if (this.listVisible()) {
this.highlightPrev();
} else {
//this.lastKey = null;
this.showList(this.config.showMixMode);
if(this.config.highlightTerm) {
this.clearHighlightTerms();
this.highlightTermFn();
}
}
}
break;
case k.ESC:
if (KEYDOWN){
this.hideList();
e.preventDefault();
}
break;
case k.SHIFT:
if (KEYDOWN) {
e.preventDefault();
}
break;
case k.TAB:
if (KEYDOWN) {
if (this.listVisible()) {
if(e.shiftKey == true) {
this.highlightPrev();
} else {
this.highlightNext();
}
e.preventDefault();
}
}
// else {
// e.preventDefault();// IE6 doesn't like this
// }
break;
default:
if (KEYUP || DROP || PASTE) {
if (this.config.lastTextValue !== this.input.val() && this.lastKey) {
this.inputChanged();
} else if (!this.listVisible() && KEYUP && !(jQuery.inArray(e.keyCode,blindKeys)>-1) ) {
// TODO || (e.keyCode != k.DELETE) && cursor==last)
this.showList();
}
}
break;
}
},
//returns number of currently visible list items
visibleItemsLen: function() {
return this.list.find(".visible").length;
},
//returns number of currently visible LI list items
visibleLiLen: function() {
return this.list.find("li.visible").length;
},
//triggered when the user changes combo value by typing
inputChanged: function() {
this.filter();
if (this.visibleItemsLen()) {
this.showList();
if(this.config.highlightTerm) {
this.highlightTermFn();
}
this.setOverflow();
this.setListHeight();
} else{
this.hideList();
}
//this.setHiddenValue(this.input.val());
this.notify("textChange");
},
//highlights first item of the dropdown list
highlightFirst: function() {
this.listItems.removeClass("active").filter("li.visible:eq(0)").addClass("active");
this.autoFill();
},
//highlights list item before currently active item
highlightPrev: function() {
//console.log("Up:" + this.getActiveItemIndex() + " > 0");//debug
if(this.getActiveItemIndex() > 0) {
var $prev = this.list.find("li.visible").eq(this.getActiveItemIndex()-1);
if ($prev.length) {
this.getActive().removeClass("active");
$prev.addClass("active");
this.scrollUp();
}
if(this.config.autoFill) {
this.autoFill();
}
}
},
//highlights item of the dropdown list next to the currently active item
highlightNext: function() {
//console.log("Down:" + (this.getActiveItemIndex()+1) + " < " + this.visibleLiLen());//debug
if(this.getActiveItemIndex()+1 < this.visibleLiLen()) {
var $next = this.list.find("li.visible").eq(this.getActiveItemIndex()+1);
if ($next.length) {
this.listItems.removeClass("active");
$next.addClass("active");
this.scrollDown();
}
if(this.config.autoFill) {
this.autoFill();
}
}
},
//scrolls list wrapper up when needed
scrollUp: function() {
if ("scroll" != this.listWrapper.css(this.overflowCSS))
return;
//console.log("scrollUp!");//debug
var maxScroll = this.getActiveIndex() * this.list.find("ul.optgroup span.label, li").height();
if (this.listWrapper.scrollTop() > maxScroll) {
this.listWrapper.scrollTop(maxScroll);
}
},
//scrolls list wrapper down when needed
scrollDown: function() {
if ("scroll" != this.listWrapper.css(this.overflowCSS))
return;
//console.log("scrollDown!");//debug
//Restore scroll position of active item, otherwise fallback to selected item
var beforeActive = (this.getActive().length > 0)? this.getActiveIndex() + 1 : this.getSelectedIndex() + 1;
if ($.browser.opera) {
++beforeActive;
}
var minScroll = this.list.find("li").height() * beforeActive - this.listWrapper.height();
if ($.browser.msie && $.browser.version < 9)
minScroll += beforeActive;
if (this.listWrapper.scrollTop() < minScroll)
this.listWrapper.scrollTop(minScroll);
},
//returns active (hovered) element(s) of the dropdown list
getActive: function() {
return this.list.find(".active");
},
//returns index of currently active list elements
getActiveIndex: function() {
return $.inArray(this.getActive().get(0), this.list.find(".visible").get());
},
//returns index of currently active list items
getActiveItemIndex: function() {
return $.inArray(this.getActive().get(0), this.list.find("li.visible").get());
},
//returns selected element(s) of the dropdown list
getSelected: function() {
return this.list.find(".selected");
},
//returns index of currently selected list elements
getSelectedIndex: function() {
return $.inArray(this.getSelected().get(0), this.list.find(".visible").get());
},
//returns index of currently selected list items
getSelectedItemIndex: function() {
return $.inArray(this.getSelected().get(0), this.list.find("li.visible").get());
},
//returns index of currently cacheList items
getItemIndex: function() {
/*var self=this;
console.log($.inArray(this.getActive().get(0), cacheList.find("li")));
var el=$("li:contains('" + this.getActive().text() + "')",cacheList);
console.log(cacheList.index(el));
return cacheList.find("li").map(function(i){
if(self.getActive().text() == $(this).text()){
console.log("Index: " + i);
return i;
}
})[0]; */
var activeItem = this.getActive().clone();
//Fallback to selected item if active item is empty
if (activeItem.length == 0) {
//console.log("getItemIndex:" + this.getSelected().text());//debug
activeItem = this.getSelected().clone();
}
activeItem.html(activeItem.text()).removeClass().addClass("visible");
var result=null;
this.cacheListItems.each2(function(k,v){
if(activeItem.html() == $(v).html()) {
result = k;
return;
}
});
return result;
// return $.inArray(this.getActive().get(0), this.cacheListItems)
//return $.inArray(this.getActive().get(0), this.list.find("li.visible").get());
},
//emptyText stuff
applyEmptyText: function() {
if (!this.config.emptyText || !this.config.emptyText.length)
return;
//TODO move this events in initEvents function ?
var self = this;
this.input.bind("focus", function() {
self.inputFocus();
}).
bind("blur", function() {
self.inputBlur();
});
if (this.input.val() == "")
this.input.val(this.config.emptyText);
if (this.input.val() == this.config.emptyText)
this.input.addClass("empty");
},
inputFocus: function() {
//console.log("inputFocus()");//debug
if (this.input.hasClass("empty")) {
this.input.removeClass("empty").
val("");
}
this.input.get(0).focus();
return this;
},
inputBlur: function() {
if (this.input.val() == "")
this.input.val(this.config.emptyText);
if (this.input.val() == this.config.emptyText)
this.input.addClass("empty");
this.input.get(0).blur();
},
//triggerSelected stuff
triggerSelected: function() {
if (!this.config.triggerSelected)
return;
var self = this;
//self.setComboValue(this.options.filter("option:selected").text(), false, true);
this.options.filter("option:selected").each2(function() {
self.setComboValue($(this).text(), false, true);
});
},
//Find corresponding item of selected option
selectedItem: function() {
var self = this;
var selectedItems = this.options.filter("option:selected");
this.listItems.removeClass("selected");
selectedItems.each(function(){
var text = $.trim($(this).text());
var currentItem;
return self.listItems.
map(function(){
if( (currentItem=$(this)) && $.trim(currentItem.text()) == text)
return currentItem;
}).get(0).addClass("selected");
});
},
//autofill stuff
autoFill: function() {
if (!this.config.autoFill || $cb.KEY.BACKSPACE == this.lastKey || this.multiple || this.input.val() == this.config.emptyText)
return;
var curVal = this.input.val();
var newVal = this.getActive().text();
this.input.val(newVal);
this.selection(this.input.get(0), curVal.length, newVal.length);
},
//provides selection for autofilling
//borrowed from jCarousel
selection: function(field, start, end) {
//console.log("selection();start:"+start+";end:"+end);//debug
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();
},
// Confirm the text selection of the given field
confirmSelection: function(field) {
//console.log("confirmSelection!");//debug
this.selection(field, this.getTextValue().length, this.getTextValue().length);
},
//for internal use
updateDrop: function() {
if (this.config.dropUp) {
this.listWrapper.addClass("list-wrapper-up");
} else {
this.listWrapper.removeClass("list-wrapper-up");
}
},
//updates dropUp config option
setDropUp: function(drop) {
this.config.dropUp = drop;
this.updateDrop();
},
notify: function(evt) {
if (!$.isFunction(this.config[evt + "Callback"]))
return;
this.config[evt + "Callback"].call(this);
}
});
$cb.extend({
//key codes
//from jCarousel and http://cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx
KEY: {
UP: 38,
DOWN: 40,
DEL: 46,
TAB: 9,
RETURN: 13,
ESC: 27,
COMMA: 188,
SPACE: 32,
PAGEUP: 33,
PAGEDOWN: 34,
BACKSPACE: 8,
SHIFT: 16,
CTRL: 17,
ALT: 18,
PAUSE: 19,
CAPS_LOCK: 20,
END: 35,
HOME: 36,
INSERT: 45,
DELETE: 46,
SUPER: 91,
HYPER: 92,
CONTEXT: 93,
F1: 112,
F2: 113,
F3: 114,
F4: 115,
F5: 116,
F6: 117,
F7: 118,
F8: 119,
F9: 120,
F10: 121,
F11: 122,
F12: 123,
NUM_LOCK: 144,
SCROLL_LOCK: 145,
UNKNOWN: 0
},
//for debugging
log: function(msg) {
var $log = $("#log");
$log.html($log.html() + msg + "<br />");
},
createSelectbox: function(config) {
if (config.lazyLoading) {
var lazySuffix = "__lazy"; // TODO DRY already set in defaultConf
// config.name += lazySuffix;
config.id += lazySuffix;
}
var $selectbox = $("<select></select>").
appendTo(config.container).
attr({
name: config.name,
id: config.id,
size: "1"
});
if (config.multiple)
$selectbox.attr("multiple", true);
var data = config.data;
var selected = false;
for (var i = 0, len = data.length; i < len; ++i) {
selected = data[i].selected || false;
$("<option></option>").appendTo($selectbox).
val(data[i].value || null).
attr("selected", selected).
text(data[i].text || null);
}
return $selectbox.get(0);
},
create: function(config) {
var defaults = {
//the name of the selectbox
name: "",
//the ID of the selectbox
id: "",
//data for the options
/*
This is an array of objects. The objects should contain the following properties:
(string)value - the value of the <option>
(string) text - text of the <option>
(bool) selected - if set to true, "selected" attribute of this <option> will be set to true
*/
data: [],
//if true, combo with multiple choice will be created
multiple: false,
//an element that will contain the widget
container: $(document),
//url that contains JSON object for options data
//format is the same as in data config option
//if passed, "data" config option will be ignored
url: "",
//params for AJAX request
ajaxData: {}
};
config = $.extend({}, defaults, config || {});
if (config.url) {
return $.getJSON(config.url, config.ajaxData, function(data) {
delete config.url;
delete config.ajaxData;
config.data = data || [];
return $cb.create(config);
});
}
config.container = $(config.container);
var selectbox = $cb.createSelectbox(config);
return new $(selectbox).combo(config);
},
normalizeArray: function(arr) {
var result = [];
for (var i = 0, len =arr.length; i < len; ++i) {
if ("" == arr[i])
continue;
result.push(arr[i]);
}
return result;
}
});
})(jQuery);
/**
* jQuery.contextMenu - Show a custom context when right clicking something
* Jonas Arnklint, http://github.com/arnklint/jquery-contextMenu
* Released into the public domain
* Date: July 11, 2011
* @author Jonas Arnklint
* @author Nowhere Man
* @version 1.7
*
*/
// Making a local '$' alias of jQuery to support jQuery.noConflict
(function($) {
$.fn.contextMenu = function(name, actions, options) {
if(this.length === 0)
return;
this.each(function() {
// Check if contextMenu is already activated
var api = $(this).data("context-menu");
if(api) return api;
var me = this, menu = $('<ul class="context-menu"></ul>').hide().appendTo('body'), activeElement = null, // last clicked element that responds with contextMenu
hideMenu = function() {
$('.context-menu:visible').each(function() {
$(this).trigger("closed");
$(this).hide();
$('body').unbind('click', hideMenu);
});
}, default_options = {
disable_native_context_menu : false, // disables the native contextmenu everywhere you click
leftClick : false // show menu on left mouse click instead of right
}, options = $.extend(default_options, options);
$(document).bind('contextmenu', function(e) {
if(options.disable_native_context_menu) {
e.preventDefault();
}
hideMenu();
});
$.each(actions, function(me, itemOptions) {
var menuItem = $('<li><a href="#">' + me + '</a></li>');
if(itemOptions.klass) {
menuItem.addClass(itemOptions.klass);
}
menuItem.appendTo(menu).bind('click', function(e) {
itemOptions.click(activeElement);
e.preventDefault();
});
});
menu.addClass(name);
$(this).data("context-menu", menu);
$(this).bind('contextmenu click', function(e) {
// Hide any existing context menus
hideMenu();
if((options.leftClick && e.button == 0) || (options.leftClick == false && e.button == 2)) {
activeElement = $(this);
// set clicked element
if(options.showMenu) {
options.showMenu.call(menu, activeElement);
}
// Bind to the closed event if there is a hideMenu handler specified
if(options.hideMenu) {
menu.bind("closed", function() {
options.hideMenu.call(menu, activeElement);
});
}
menu.css({
visibility : 'hidden',
position : 'absolute',
zIndex : 99999
});
// include margin so it can be used to offset from page border.
var mWidth = menu.outerWidth(true), mHeight = menu.outerHeight(true), xPos = ((e.pageX - window.scrollX) + mWidth < window.innerWidth) ? e.pageX : e.pageX - mWidth, yPos = ((e.pageY - window.scrollY) + mHeight < window.innerHeight) ? e.pageY : e.pageY - mHeight;
menu.show(0, function() {
$('body').bind('click', hideMenu);
}).css({
visibility : 'visible',
top : yPos + 'px',
left : xPos + 'px',
zIndex : 99999
});
return false;
}
});
});
return this;
}
})(jQuery);
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
/***CONTEXT-MENU***/
ul.context-menu {
list-style-type: none;
list-style-position: inside;
margin: 0;
padding: 0;
}
/* all context menus have this class */
.context-menu {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-icab-border-radius: 4px;
-khtml-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
background-color: #f2f2f2;
border: 1px solid #999;
font-family: 'lucida grande', tahoma, verdana;
font-size: 11px;
list-style-type: none;
margin: 0;
padding: 0;
}
.context-menu a {
display: block;
padding: 3px;
text-decoration: none;
color: #333;
}
.context-menu a:hover {
background-color: #666;
color: white;
}
/******/
/***COMBO-OPTIONS***/
.combo-options{
position: absolute;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-icab-border-radius: 4px;
-khtml-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
background-color: #f2f2f2;
border: 1px solid #999;
font-family: 'lucida grande', tahoma, verdana;
font-size: 11px;
padding: 0px 5px 5px 5px;
width: auto;
z-index: 99999;
}
/******/
/***USAGE-FOR-COMBO***/
div.combo {
/* TODO Backport to jquery.combo.js */
display: -moz-inline-block; /* For Firefox 1-2 */
display: inline-block;
/* End of TODO */
}
div.default div.list-wrapper {
width: inherit;/* TODO ? Backport to jquery.combo.js ? */
}
div.default div.icon {
padding: 0px;/* TODO Backport to jquery.combo.js */
}
div.default li {
color: #000000;/* TODO Backport to jquery.combo.js */
text-align: left;/* TODO Backport to jquery.combo.js */
margin: 0 !important;/* TODO Test before backporting */
padding: 0 0 0 5px !important;/* TODO Test before backporting */
}
div.default li .li-bold {
font-weight: bold !important;/* TODO Test before backporting */
}
div.default div.list-wrapper {
border-color: #B5B8C8;/* TODO Backport to jquery.combo.js */
}
div.default input {
padding: 1px 3px !important;/* TODO Test before backporting */
}
/******/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment