📄 selectbox.js
字号:
/**
* $Id: selectbox.js 247 2007-04-03 16:59:49Z wingedfox $
* $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.008/dom/selectbox.js $
*
* Extends the selectbox interface
*
* @author Ilya Lebedev <ilya@lebedev.net>
* @modified $Date: 2007-04-03 20:59:49 +0400 (Втр, 03 Апр 2007) $
* @version $Rev: 247 $
* @title Selectbox
* @license LGPL 2.1 or later
*/
// ===================================================================
//
// Author: Matt Kruse <matt@mattkruse.com>
// Author: Ilya Lebedev <ilya@lebedev.net>
// http://debugger.ru
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download.
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================
// HISTORY
// ------------------------------------------------------------------
// October 2, 2006: Rewritten in the OOP style and added number of useful features
// June 12, 2003: Modified up and down functions to support more than
// one selected option
/*
COMPATIBILITY: These are fairly basic functions - they should work on
all browsers that support Javascript.
*/
/**
* Provides control component for the select box
*
* @class
* @param {String} id select box to attach control to
* @scope public
*/
Selectbox = function (id /*: String*/) {
var self = this;
/**
* Selectbox node
*
* @type HTMLSelectElement
* @scope private
*/
var node;
/**
* List of all the options in the list
* Format of the array elements
* { 'value' : option value,
* 'text' : option text,
* 'defaultSelected' : default selection flag,
* 'selected' : selection flag,
* 'visible' : visible flag,
* 'fixed' : fixes position in the list
* }
*
* @type Array
* @scope private
*/
var options = [];
/**
* Trackers does keep a track of the options
*
* @type Object
* @scope private
*/
var trackers = {
'added' : [],
'deleted' : []
}
/**
* Visible options list will not exceed this value
*
* @type Number
* @scope private
*/
var maxlistlen = 1000;
/**
* @constructor
*/
var __construct = function (id) /* :void */{
node = document.getElementById(id) || id;
if (node && node.tagName.toLowerCase() != 'select')
throw new Error('Node with supplied id="'+id+'" is not the <select> box.')
/*
* if node is not exists, create it.
*/
if (!node) {
node = document.createElement('select');
node.id = 'selectbox'+(new Date).valueOf();
} else if (node.options.length>0) {
self.addOptionsList(node.options, false, false);
}
node.onblur = __storeOptionsState;
}
//---------------------------------------------------------------------------
// Private methods
//---------------------------------------------------------------------------
/**
* Special 'toString' method, allowing to have a free array sorting
*
* @return {String}
* @scope protected
*/
var __toString = function() {
return String(this.text);
}
/**
* Check all visible options and set the state for them
*
* @param {RegExp, String, Array} reg expression to match option text against
* @param {String} type state to be changed, currently 'selected' and 'visible' supported
* @param {Boolean} state true means 'set selected'
* @param {Boolean} pre true means 'preserve already selected options state'
* @param {String} match match needle from: 'start', 'end', 'exact', 'any'. Default is 'start'. Has no meaning, when regexp is supplied.
* @param {String} attr attribute to do the check against, either 'text' or 'value'
* @return {Number} number of changed options
*/
var __setOptionsState = function ( reg /* :RegExp, String, Array */
,type /* :String */
,state /* :Boolean */
,pre /* :Boolean */
,match /* :String */
,attr /* :String */ ) /* :Number */ {
var res = 0;
/*
* either 'text' or 'value' is allowed
*/
if (attr != 'value') attr = 'text';
/*
* don't touch options with empty match
*/
if (!isRegExp(reg)) {
reg = RegExp.escape(reg);
if (!isString(match)) match = 'start';
switch (match.toLowerCase()) {
case "any" :
reg = new RegExp(reg,"i");
break;
case "end" :
reg = new RegExp(reg+"$","i");
break;
case "exact" :
reg = new RegExp("^"+reg+"$","i");
break;
case "start" :
case "default" :
reg = new RegExp("^"+reg,"i");
break;
}
}
for (var i=0, oL=options.length; i<oL; i++) {
/*
* skip already options with matching state, if allowed
*/
if (pre && options[i][type] == state) continue;
/*
* flag that some options are processed successfully
*/
if ( reg.test(options[i][attr]) ) {
res += (options[i][type] != state);
options[i][type] = state;
} else {
if ( options[i][type] == state && !pre) {
res += (options[i][type] == state);
options[i][type] = ! state;
}
}
}
/*
* if something has changed, do the update
*/
if (res) self.updateOptions();
return res;
}
/**
* Stores actual selection state, made by user
*
* @scope protected
*/
var __storeOptionsState = function () /* :void */{
for (var i=0, oL=this.options.length; i<oL; i++) {
if (!isUndefined(this.options[i].__idx) && options[this.options[i].__idx]) {
options[this.options[i].__idx].selected = this.options[i].selected;
options[this.options[i].__idx].defaultSelected = this.options[i].defaultSelected;
}
}
}
/**
* Stores option name in the tracker
*
* @see #trackers
* @param {String} track name of the tracker
* @param {String} text option text
* @param {String} val option value
* @return {Boolean}
* @scope private
*/
var __saveOptionsTrack = function (track /* :String */, text /* :String */, val /* :String */) /* :Boolean */{
if (!trackers[track]) return false;
if (trackers[track].fixed) {
trackers[track] = [];
trackers[track].fixed = false;
}
trackers[track][trackers[track].length] = {'text' :text, 'value' :val};
return true;
}
/**
* Fixes current tracking state in the past
*
* @scope private
*/
var __fixOptionsTrack = function () {
trackers.added.fixed = true;
trackers.deleted.fixed = true;
}
//---------------------------------------------------------------------------
// Public methods
//---------------------------------------------------------------------------
/**
* Return the number of options in the list
*
* @return {Number}
* @scope public
*/
this.hasOptions = function () /* :Number */{
return options.length;
}
/**
* Redraw options list
*
* @scope public
*/
this.updateOptions = function () /* :void */ {
/*
* detach box to speedup conversion
*/
var nxt = node.nextSibling
,prn = node.parentNode;
if (prn) prn.removeChild(node);
for (var i=ndx=0, oL=options.length; i<oL; i++) {
/*
* don't process options beyond the borders
*/
if (ndx>=maxlistlen) break;
if (options[i].visible) {
if (!node.options[ndx]) {
node.options[ndx] = new Option(options[i].text
,options[i].value
,options[i].defaultSelected
,options[i].selected);
} else {
/*
* firefox behavior - it does reset selectbox,
* when even single label has been touched
* so, try to keep its scroll on the place
*/
if (node.options[ndx].text != options[i].text)
node.options[ndx].text = options[i].text;
node.options[ndx].value = options[i].value;
node.options[ndx].defaultSelected = options[i].defaultSelected;
node.options[ndx].selected = options[i].selected;
}
node.options[ndx].__idx = i;
ndx++;
}
}
node.options.length=ndx;
__fixOptionsTrack();
/*
* set box back on its place
*/
if (prn) {
prn.insertBefore(node, nxt);
prn = nxt = null;
}
}
//-------------------------------------------
// SELECTION OPERATION
//-------------------------------------------
/**
* Selects all available options
*
* It's useful, when do the transfer from one select box to another and before submitting a form
*
* @scope public
*/
this.selectAllOptions = function () /* :void */ {
for (var i=0; i<options.length; i++) {
if (options[i].visible) {
options[i].selected = true;
}
}
this.updateOptions();
}
/**
* Selects all matching options
*
* @param {String} needle search phrase
* @param {String} match match needle from: 'start', 'end', 'exact', 'any'. Default is 'start'. Has no meaning, when regexp is supplied.
* @param {String} attr attribute to do the check against, either 'text' or 'value'
* @return {Number} number of selected options
* @scope public
*/
this.selectMatchingOptions = function (needle /* :String */, match /* :String */, attr /* :String */) /* :Number */ {
return __setOptionsState (needle, 'selected', true, true, match, attr);
}
/**
* Selects all matching options and reset selection for others
*
* @param {String} needle search phrase
* @param {String} match match needle from: 'start', 'end', 'exact', 'any'. Default is 'start'. Has no meaning, when regexp is supplied.
* @param {String} attr attribute to do the check against, either 'text' or 'value'
* @return {Number} number of selected options
* @scope public
*/
this.selectOnlyMatchingOptions = function (needle /* :String */, match /* :String */, attr /* :String */) /* :Number */ {
return __setOptionsState (needle, 'selected', true, false, match, attr);
}
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -