cejs
Version:
A JavaScript module framework that is simple to use.
1,147 lines (939 loc) • 35.8 kB
JavaScript
/**
* @name CeL 下拉式表單 function
* @fileoverview 本檔案包含了下拉選擇式表單的 functions。
*
* @example<code>
TODO:
HTML 5 <datalist> Tag
date
http://plugins.jquery.com/project/timepicker
http://digitalbush.com/projects/masked-input-plugin/
理想:
http://gs.statcounter.com/
http://plugins.jquery.com/search/node/Autocomplete+type%3Aproject_project
http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
http://jsgears.com/thread-114-1-1.html
set focus/blue background-image instead of HTML 5 placeholder text
http://dev.w3.org/html5/spec/Overview.html#the-placeholder-attribute
The placeholder attribute represents a short hint (a word or short phrase) intended to aid the user with data entry.
e.g., background-image: url(http://www.google.com/cse/intl/en/images/google_custom_search_watermark.gif); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(255, 255, 255); background-position: 0% 50%; background-repeat: no-repeat no-repeat;
or
http://perldoc.perl.org/
usually show a <div>. show <input> only at focus.
http://miketaylr.com/pres/html5/forms2.html
http://people.opera.com/brucel/demo/html5-forms-demo.html
http://www.erichynds.com/examples/jquery-multiselect/examples.htm
http://x.wawooo.com/archives/891
http://www.google.com.tw/dictionary
鍵盤選擇時同時改變值
</code>
*
* @since
*/
//'use strict';
typeof CeL === 'function' && CeL.run(
{
name: 'interact.form.select_input',
// for new URL(url)
require: 'interact.DOM.get_node_offset|application.net.',
code: module_code
});
function module_code(library_namespace, load_arguments) {
// requiring
var get_node_offset = this.r('get_node_offset');
// 載入 CSS resource(s)。
// include resources of module.
library_namespace.run(library_namespace.get_module_path(this.id, 'select_input.css'));
/**
* 簡易型 interact.DOM.XML_node @ interact.form.select_input
* @param tag
* p.appendChild tag
* @param p
* parent node
* @param t
* text
* @param classN
* className
* @inner
* @ignore
* @return
*/
var create_DO = function (tag, p, t, classN) {
var _e;
if (t && (typeof t != 'object' || library_namespace.is_Object(t)))
t = document.createTextNode('' + t);
if (typeof tag === 'object') {
_e = tag;
} else if (tag) {
_e = document.createElement(tag);
if (classN)
_e.className = classN;
if (t
// 真有出錯應該改源頭…
// && t.nodeType
) {
_e.appendChild(t);
}
} else if (t)
_e = t;
if (p && _e)
p.appendChild(_e);
return _e;
};
/**
* get scrollbar height
*
* @return
* @since 2008/9/3 23:31:21
* @inner
* @ignore
* @see http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php<br />
* lazy evaluation
* http://peter.michaux.ca/articles/lazy-function-definition-pattern
*/
function scrollbar_width() {
var _f = scrollbar_width;
if (!_f.w) {
var w, p = create_DO('div', document.body), c = create_DO('div', p, ' '), s = p.style;
s.width = s.height = '80px';
// 有時沒這行才出得來
// c.style.width = '100%';
s.overflow = 'hidden';
w = c.offsetWidth;
s.overflow = 'scroll';
_f.w = w - c.offsetWidth;
library_namespace.debug('scrollbar_width: ' + w + '-' + c.offsetWidth + '=' + _f.w, 2);
document.body.removeChild(p);
}
return _f.w;
}
/**
* scroll 到可以看到 object TODO: 考慮可能沒 scrollbar 包括橫向
*
* @param o
* object
* @param [p]
* parentNode to scroll
* @return
* @since 2008/9/3 23:31:29
* @inner
* @ignore
*/
function scroll_to_show(o, p) {
if (!p) {
p = o;
while ((p = p.parentNode) && p.offsetHeight == p.scrollHeight)
;
}
library_namespace.debug('scroll_to_show: ' + p.scrollTop + ', ' + p.scrollHeight + ',' + p.offsetHeight + ', ' + o.offsetTop, 2);
var s, a;
if (a = o.offsetTop, a < p.scrollTop)
s = a;
else if (a = o.offsetTop + o.offsetHeight - p.offsetHeight
+ scrollbar_width(), a > p.scrollTop)
if (s = a, a = o.offsetTop, a < s)
s = a;
if (!isNaN(s))
p.scrollTop = s;
}
/**
* <code>
{
title: '',
name: '',
container: 'id' | obj,
list: [] | {} | {group1:{}, group2:[],.. },
default: '' | [],
type: 'select' | 'radio' | 'checkbox',
}
return <select>
TODO:
複選 <select>
<radio>
<checkbox>
+<label>
autocomplete: 假如所有備取 list 都有一樣的 prefix,則自動完成。
把後面的用反白自動完成。
在 list 上下安排三角,onmouseover 即可自動滾動。
color panel
http://www.itlearner.com/code/js_ref/choi3.htm
selectName.options[i]=new Options("option_value","option_Text", defaultSelected, selected);
</code>
*/
/**
* container object, list
* @param o
* @param l
* @return
* @inner
* @ignore
*/
function menu_creater(o, l) {
};
// ===================================================
var
// class private -----------------------------------
/* 可紀錄的 set class name,不過對大多數人來說,更常用的是 instance.setClassName
usage:
(item[, obj]) set obj to className item, return real className that assigned
(0,'prefix') set prefix & 重設(全部重跑)
*/
setClassName = function (i, o, noRec) { // (0, prefix) or (item, object)
var _t = this, s = _.classNameSet;
if (!_t.assignedClass) _t.assignedClass = [];
if (!o || typeof o === 'object') {
// 設定並回傳 className
library_namespace.debug('setClassName: test ' + 'class_' + i + ': ' + ('class_' + i in _t ? '<em>YES</em>' : 'none'), 2);
s = [i in s ?
s[i].charAt(0) === '~' ? s.prefix + s[i].slice(1) :
s[i] :
''
];
if ('class_' + i in _t)
if (i === 'error' || i === 'warning') s.unshift(_t['class_' + i]);
else s.push(_t['class_' + i]);
s = s.join(' ');
library_namespace.debug('setClassName: set ' + o + (s ? ' to [' + s + ']' : ', <em>There is no [' + i + '] in classNameSet or instance set.</em>'), 2);
if (o && (o.className = s, !noRec))
_t.assignedClass.push(s, o);
return s;
}
s.prefix = o;
o = _t.assignedClass;
// 重設(全部重跑)
for (var i = 0; i < o.length; i++)
_f.call(_t, o[0], o[1], 1);
},
funcButton = function (_t, t, f, title) { // add text t, function f to instance _t
var _p = pv(_t), o = create_DO('span', _p.listO, '['), b;
setClassName.call(_t, 'functionB', o);
b = create_DO('span', o, t);
setClassName.call(_t, 'functionT', b);
b.title = title, b.onclick = f;
create_DO(0, o, ']');
return b;
},
// 簡易型
removeAllChild = function (o) {
o.innerHTML = '';
return o;
},
// show/hide list
showList = function (show) { // ():get, 0:hide, 1:show
var _t = this, _p = pv(_t), o = _p.listO, s, c = 0;
if (!o) return;
s = o.style;
if (show) {
c = get_node_offset(_p.inputO);
s.top = c.top + c.height + 2 + 'px';
s.left = c.left + 'px';
s.width = c.width + 'px';
s.height = ''; // reset
c = s.display = 'block';
if (_t.maxListHeight && o.offsetHeight > _t.maxListHeight)
s.height = _t.maxListHeight + 'px';
} else if (typeof show != 'undefined')
c = s.display = 'none';
if (c)
create_DO(0, removeAllChild(_p.arrowO), _.textSet[c != 'none' ? 'hideList' : 'showList']);
else c = s.display;
return c != 'none';
},
/* 準備選擇清單的某一項
TODO:
自動完成
到最後若可能自動轉到全部
→
*/
cK = 0, // control key pressed
readyTo = function (e, o) {
if (!e) e = event;
var _t = this, _p = pv(_t), c, gI = function (o) {
return o &&
//(can_use_special_attribute ? o.getAttribute("sIndex") : o.sIndex)
o.sIndex;
};
library_namespace.debug('readyTo: ' + e.type + ', key: ' + (e.keyCode || e.which || e.charCode) + ', _p.listA: ' + (_p.listA && _p.listA.length), 2);
if (!_p.listA || !_p.listA.length) return;
if (e.type === 'mouseover' || e.type === 'mouseout') {
if (_p.readyItem) setClassName.call(_t, 'item', _p.readyItem, 0);
if (e.type === 'mouseover') c = 'item_select', _p.readyItem = o;
else if (c = 'item', _p.readyItem === o) _p.readyItem = 0;
// 需更改 _p.inputO.onkeyup 以防止重新 list!!
} else if ((c = e.keyCode || e.which || e.charCode) === 13) {
if (_p.readyItem) {
library_namespace.debug('readyTo: 以鍵盤選擇: ' + _p.readyItem.innerHTML, 4);
cK = c, _p.readyItem.onclick(); // 用 .click() 無效!
return false;
} else return;
// key input 用鍵盤控制上下 ←↑→↓: 37~40
} else if (c == 38 || c == 40) {
cK = c;
o = _p.readyItem;
library_namespace.debug('readyTo: 以鍵盤移至: ' + (o && (o.getAttribute("sIndex") + ',' + o.sIndex)), 4);
if (!o) o = _p.listA[c == 40 ? 0 : _p.listA.length - 1];
else {
// IE 可用 getAttribute,FF 或許在 appendChild 之後屬性重設?,得用 o.sIndex
if (!o.getAttribute) throw 1;
c = gI(o) + (c == 38 ? -1 : 1);
if (c < 0 || c >= _p.listA.length) return;
setClassName.call(_t, 'item', o, 0);
o = _p.listA[c];
}
_p.readyItem = o;
scroll_to_show(o, _p.listO);
c = 'item_select';
} else if (c == 35 || c == 36) { // 35: End, 36: Home
cK = c;
if (o = _p.readyItem) setClassName.call(_t, 'item', o, 0);
_p.readyItem = o = _p.listA[c == 36 ? 0 : _p.listA.length - 1];
scroll_to_show(o, _p.listO);
c = 'item_select';
} else if (c == 33 || c == 34) { // 33: PageUP, 34: PageDown
cK = c;
o = _p.readyItem;
if (!o) return;
setClassName.call(_t, 'item', o, 0);
var i = gI(o), t;
if (c == 33) {
t = _p.listO.scrollTop - 1;
while (i && _p.listA[i - 1].offsetTop > t) i--;
} else {
t = _p.listO.scrollTop + _p.listO.offsetHeight - scrollbar_width();
while (i < _p.listA.length - 1 && _p.listA[i + 1].offsetTop < t) i++;
}
library_namespace.debug('readyTo: Page: ' + i + ', top: ' + t + ', scroll: ' + _p.listO.scrollTop, 4);
if (i == gI(o))
if (c == 33) {
t -= _p.listO.offsetHeight;
if (t < 2) i = 0;
else while (i && _p.listA[i - 1].offsetTop > t) i--;
} else {
t += _p.listO.offsetHeight;
while (i < _p.listA.length - 1 && _p.listA[i + 1].offsetTop < t) i++;
}
library_namespace.debug('readyTo: Page: ' + i + ', top: ' + t + ', height: ' + _p.listO.offsetHeight, 4);
_p.readyItem = o = _p.listA[i];
scroll_to_show(o, _p.listO);
c = 'item_select';
} else return;
setClassName.call(_t, c, o, 0);
return false;
},
//can_use_special_attribute,
// 顯示清單的工具函數
setList = function (l, force, limit, f) {
var _t = this, _p = pv(_t), i, c = 0, k, o;
if (isNaN(limit)) limit = isNaN(_t.maxList) ? _.maxList : _t.maxList || Number.MAX_VALUE;
if (!f) f = function (l, i) {
var a = _t.onList(l, i), o;
if (!a) return;
// a[0] 可能是 Array 之類。
o = create_DO('div', 0, String(a[0]));
setClassName.call(_t, 'item', o);
o.title = a[1];
k = a[2] || a[1];
o.onmouseover = o.onmouseout = function (e) { readyTo.call(_t, e, o); };
o.onclick = function () { var v = _t.onSelect(l, i); _t.setValue(v); _t.onInput(v); };
// 這邊本來放在下面 for 的地方
c++, _p.listO.appendChild(o);
//if (!can_use_special_attribute) { o.setAttribute("sIndex", 1); can_use_special_attribute = o.getAttribute("sIndex") ? 1 : -1; }
//if (can_use_special_attribute == 1) o.setAttribute("sIndex", _p.listA.length); else o.sIndex = _p.listA.length;
//o.setAttribute("sIndex", o.sIndex = _p.listA.length);
o.sIndex = _p.listA.length;
_p.listA.push(o);
return o;
};
//_t.showList(0);
_p.listO = removeAllChild(_p.listO);
_p.listA = [];
_p.readyItem = 0;
if (Array.isArray(l)) {
for (i = 0; i < l.length && c < limit; i++)
f(l, i);
} else
for (i in l)
if (c < limit) {
f(l, i);
} else break;
library_namespace.debug('setList: list ' + c + ' items, key ' + k + '=?' + _t.setValue(), 2);
if (c == 1 && _t.setValue() == k) {
// 僅有一個且與 key 相同
c = 0;
}
if (!force && !c) {
// 無 list
return;
}
// add function
if (c != _t.allListCount) funcButton(_t, _.textSet.allBtn, function () { _t.doFunc = 1; _t.focus(); _t.setList(_t.setAllList(), 1, Number.MAX_VALUE); }, '顯示全部 ' + _t.allListCount + ' 列表。');
if (_t.setValue()) funcButton(_t, _.textSet.clearBtn, function () { _t.doFunc = 2; _t.focus(); _t.onInput(_t.setValue('')); }, '清除輸入內容,重新列表。');
funcButton(_t, _.textSet.closeBtn, function () { _t.doFunc = 3; _t.showList(0); }, 'close menu \n關閉列表');
showList.call(_t, 1);
return _t.listCount = c;
},
// return verify 之後的 key(<input>) 值
do_verify = function (k) {
var _t = this, c = _t.verify(k || _t.setValue());
library_namespace.debug('do_verify: input status: ' + (c == 1 ? 'warning' : c == 2 ? 'error' : 'OK'), 2);
if (typeof c === 'string') {
// 可以設定 key 值!
k = c;
_t.setValue(k, do_verify);
} else {
setClassName.call(_t, c == 1 ? 'warning' : c == 2 ? 'error' : 'input', pv(_t).inputO, 1);
}
return k;
},
// 簡易設定常用的 onInput 型式
searchInList = function (f, o) { // o: 傳入 (list, index, key)
var _t = this;
if (typeof f === 'string' && (f in _.searchFunctionSet)) f = _.searchFunctionSet[f];
// 因為允許傳入 list,所以不能在這邊用 _t.setAllList() 判別函數,而得要寫一個泛用的。
return _t.onInput = function (key, list, force) {
library_namespace.debug('searchInList, onInput: search [' + key + '] use ' + f, 2);
if (!list) list = _t.setAllList();
key = do_verify.call(_t, key || '');
var l, i;
library_namespace.debug('searchInList: search ' + (list instanceof Array ? 'array' : 'object'), 2);
if (Array.isArray(list)) {
l = [];//new list.constructor();
for (i = 0; i < list.length; i++)
if (o ? f(list, i, key) : list[i] && f(list[i], key)) l.push(list[i]); // search value
} else {
l = {};
for (i in list)
if (o ? f(list, i, key) : i && f(i, key) || list[i] && f(list[i], key)) l[i] = list[i]; // search key+value
}
_t.setList(l, force);
};
},
/**
* 切換 [input] / inputted [span]
* @param {Boolean|undefined} to_input 切換至 input or not. default: 切換至 [input]
* @return
* @private
* @inner
* @ignore
*/
toggleToInput = function (to_input) {
var _t = this, _p = pv(_t);
if (to_input || typeof to_input === 'undefined') {
// to <input>
_p.inputtedO.style.display = 'none';
if (_t.allListCount)
_p.arrowO.style.display = 'inline';
_p.inputO.style.display = 'inline';
return 1;
} else {
// to inputted <span>
_t.showList(0);
_t.setInputted();
if (to_input = library_namespace.get_style
&& parseInt(library_namespace.get_style(_p.inputO, 'width'))) {
//library_namespace.debug(to_input);
// TODO: +16, +10: magic number
try {
// .get_style(_p.arrowO, 'width') 可能回傳 'auto' @ IE8
_p.inputtedO.style.width = (to_input + parseInt(library_namespace.get_style(_p.arrowO, 'width')) + 16) + 'px';
_p.inputtedO.style.height = (parseInt(library_namespace.get_style(_p.inputO, 'height')) + 10) + 'px';
} catch (e) {
// TODO: handle exception
}
}
_p.arrowO.style.display = _p.inputO.style.display = 'none';
if (!_p.inputtedO.innerHTML)
_p.inputtedO.innerHTML = ' ';
//_p.inputtedO.style.border = '3px #aaa dotted';
_p.inputtedO.style.display = to_input ? 'inline-block' : 'inline';
}
},
// TODO: http://blog.xuite.net/sugopili/computerblog/17695447
set_source = function (URL) {
;
},
/* 配置元件
本函數會配置/增加:
<div> .container
<input> .inputO
<span> .inputtedO
<span> .arrowO
<div> .listO
arguments:
<input> 會被當作 inputO 主元件
<select> 會被當作選項
others: container
*/
layout = function (o) {
var _t = this, _p = pv(_t), t;
if (typeof o !== 'object')
o = document.getElementById(o);
if (!o || (o.tagName.toLowerCase() in { hr: 1, br: 1 })) return; // ** 這邊應該檢查 o 是不是 <hr /> 等不能加 child 的!
//library_namespace.debug(('layout: use <' + o.tagName + (o.id ? '#' + o.id : '') + '>: ' + o.innerHTML).replace(/</g, '<'));
// TODO: 這邊應該有一個更完善的刪除策略
if (_t.loaded) {
t = _p.container;
// 不必多做功,已經達到所需配置了。
if (t === o.parentNode) return;
for (var i = 0, e = 'inputO,inputtedO,arrowO,listO'.split(','); i < e.length; i++) {
library_namespace.debug('layout: removeChild ' + e[i]);
//t.removeChild(_p[e[i]]);
_p[e[i]].parentNode.removeChild(_p[e[i]]);
}
if (!t.childNodes.length) t.parentNode.removeChild(t);
}
// 依照各種不同的傳入 object 作出應對
t = o.tagName.toLowerCase();
if (t === 'input') {
try {
// http://www.w3.org/TR/html5/forms.html#the-pattern-attribute
// http://www.whatwg.org/specs/web-apps/current-work/#attr-input-pattern
// http://www.w3school.com.cn/html5/html5_input.asp
t = o.pattern ||
//o.getAttribute&&
o.getAttribute('pattern');
// compiled as a JavaScript regular expression with the global, ignoreCase, and multiline flags disabled
// somewhat as if it implied a ^(?: at the start of the pattern and a )$ at the end
if (t && (t = new RegExp('^(' + t + ')?$'))) {
//library_namespace.debug('set verify pattern of [' + o.id + ']: ' + t);
_t.set_verify(t);
}
} catch (e) {
library_namespace.error('error pattern: [' + t + ']');
library_namespace.error(e);
}
o.parentNode.insertBefore(
t = _p.container = create_DO('span'),
_p.inputO = o
);
setClassName.call(_t, 'container', t);
if (!o.className) setClassName.call(_t, 'input', o);
t.appendChild(o.parentNode.removeChild(o));
o = t;
} else if (t === 'select') {
o.parentNode.insertBefore(t = _p.container = create_DO('span'), o);
_p.inputO = create_DO('input', t);
setClassName.call(_t, 'input', _p.inputO);
_p.inputO.name = o.name;
if (o.selectedIndex >= 0) _p.inputO.value = o.options[o.selectedIndex].value;
var l = {}, opt = o.options, i = 0;
for (; i < opt.length; i++)
l[opt[i].value || opt[i].innerHTML] = opt[i].innerHTML;
// list setting
_t.setAllList(l);
o.parentNode.removeChild(removeAllChild(o));
o = t;
} else {
// 容器
_p.container = o;
if (!o.className) setClassName.call(_t, 'container', o);
_p.inputO = create_DO('input', o);
setClassName.call(_t, 'input', _p.inputO);
}
// 補足其他的設定
_p.inputO.setAttribute("autocomplete", "off");
_p.inputtedO = create_DO('span', o);
setClassName.call(_t, 'inputted', _p.inputtedO);
_p.inputtedO.style.display = 'none';
_p.inputtedO.onclick = function () {
_t.clickNow = 1;
_t.toggleToInput();
_p.inputO.focus();
_t.clickNow = 0;
};
(_p.arrowO = create_DO('span', o, _.textSet.showList))
.title = _.textSet.arrowTitle;
setClassName.call(_t, 'arrow', _p.arrowO);
_p.listO = create_DO('div', o);
_p.arrowO.onmouseover = _p.listO.onmouseover = function () { _t.clickNow = 1; };
_p.arrowO.onmouseout = _p.listO.onmouseout = function () { _t.clickNow = 0; };
setClassName.call(_t, 'list', _p.listO);
_t.showList(0);
// event setting
//_p.inputO.onmouseover =
_p.inputO.onkeydown = function (e) { readyTo.call(_t, e); };
_p.inputO.onmouseup = _p.inputO.onkeyup = _p.inputO.ondragend = function (e) {
if (!e) e = event;
var c = e.keyCode || e.which || e.charCode;
library_namespace.debug('up: ' + e.type + ', key: ' + c + ', _p.listA: ' + (_p.listA && _p.listA.length), 2);
if (cK && cK == c) { cK = 0; return false; }
// Esc
if (c == 27) { _t.showList(0); return false; }
_t.clickNow = 1; _t.onInput(_t.setValue());
};
_p.inputO.onmouseout = function () { _t.clickNow = 0; };
if (_p.inputO.addEventListener) _p.inputO.addEventListener('dragdrop', _p.inputO.ondragend, false);
//if (window.addEventListener) window.addEventListener('click', function () { _t.showList(0); }, true);
//addListener(0, 'click', function () { sl('close..'); _t.showList(0); sl('close done.'); })
_p.inputO.onblur = function () {
// 這在 Firefox 似乎沒啥效果..
//if (_t.verify(_t.setValue()) == 2) { alert('Wrong input!'); return false; }
/* 設定這項在按 _p.arrowO 的時候會出問題,所以建議在其他地方自訂。
if (_t.setValue() && (_t.setValue() in _t.setAllList()))
_t.toggleToInput(0);
*/
// TODO: 假如以鍵盤離開,應該也 showList(0);
//library_namespace.debug('clickNow=' + _t.clickNow, 1, '_p.inputO.onblur');
if (_t.clickNow) _t.clickNow = 0;
else _t.showList(0);
};
// show/hide by press arrow
_p.arrowO.onclick = function () {
library_namespace.debug('arrowO.onclick start', 3);
_t.clickNow = 1;
if (_t.showList())
// 正在顯示就把他關起來
_t.showList(0);
else
// 沒在顯示就把他開起來: setValue 設定完 list,onInput 模擬 key-down
_t.onInput(_t.setValue(), 0, 1);
_t.clickNow = 0;
library_namespace.debug('arrowO.onclick end', 3);
};
// ondblclick: double click
//_p.inputO.ondblclick = function () { _t.onInput(_p.inputO.value, 0, 1); };
_t.loaded = 1; // isLoaded
},
// instance constructor ---------------------------
instanceL = [],
initI = function (o, l, s) { // (HTML object, list: Array or Object, onInput handler)
var _t = this, _p;
// objects setting
if (typeof o != 'object') {
//library_namespace.debug('Use object [' + o + ']');
o = document.getElementById(o);
}
_p = pv(_t); // also do initial
instanceL.push(_t); // for destructor
if (o) {
layout.call(this, o);
} else if (false) {
throw new Error(1, 'Cannot get document object' + (o ? ' [' + o + ']' : '') + '!');
return;
}
// list setting
if (l && !_t.allListCount) _t.setAllList(l);
if (_p.arrowO)
_p.arrowO.style.display = _t.allListCount ? 'inline' : 'none'; // 無 list 的話先不顯示,等有 list 再說。
// setup default inputted value
_t.dInputted = _t.setValue;
if (s)
_t.setSearch(s);
//return _t;
};
//===================================================
/**<code>
_ = this
TODO:
浮水印 background-image:url();
HISTORY:
2008/7/22 0:38:14 create
7/27 22:55:18 verify()
8/7 21:18:47 attach()
</code>*/
/**
* 提供有選單的 input
* @class form 的 functions
* @see
* http://dojocampus.org/explorer/#Dijit_Form%20Controls_Filtering%20Select_Basic
*/
var _// JSDT:_module_
= function () { initI.apply(this, arguments); typeof load_arguments === 'function' && load_arguments.apply(this, arguments); },
// (instance private handle) 不要 instance private 的把這函數刪掉即可。
_p = '_' + (Math.random() + '').replace(/\./, ''),
// get private variables (instance[,destroy]), init private variables (instance[,access function list[, instance destructor]])
pv = function (i, d, k) { var V, K = _p('k'); return arguments.callee.caller === _p('i') ? (V = _p(i[K] = _p()), V.O = i, V.L = {}) : (K in i) && (V = _p(i[K])) && i === V.O ? d ? (_p(i[K], 1), delete i[K]) : V.L : {}; };
// (for inherit) 不要 inherit 的把這段刪掉即可。
//(_.clone = arguments.callee).toString = function () { return '[class_template]'; };
// class destructor ---------------------------
/**<code>
please call at last (e.g., window.unload)
// usage:
classT = classT.destroy();
// or if you has something more to do:
classT.destroy() && classT= null;
</code>*/
_.destroy = function () { for (var i = 0; i < instanceL.length; i++)instanceL[i].destroy(); _p(); };
// (instance private handle, continue)
eval('_p=(function(_,pv,initI){var ' + _p + '={a:pv,d:_.destroy,c:0,k:"+pv+' + Math.random() + '",i:initI};return function(i,d){var f=arguments.callee.caller;if(f===' + _p + '.a){if(!d)return i in ' + _p + '?' + _p + '[i]:(' + _p + '[i=' + _p + '.c++]={},i);' + _p + '[i]={};}if(f===' + _p + '.d)' + _p + '={};}})(_,pv,initI);');
_p.toString = function () { return ''; };
/**<code>
// 測試是否可用自訂之屬性
var o = document.createElement('div');
o.setAttribute('testA', 2);
can_use_special_attribute = o.getAttribute('testA');
sl('can_use_special_attribute: ' + can_use_special_attribute);
</code>*/
// class public interface ---------------------------
// 預設清單最大顯示數
_.maxList = 10;
// searchInList 常用到的函數
_.searchFunctionSet = {
allTheSame: function (i, k) {
return (i + '') === k;
},
startWith: function (i, k) {
return (i + '').slice(0, k.length) === k;
},
// 不管大小寫 Whether the case
startWithWC: function (i, k) {
return (i + '').slice(0, k.length).toLowerCase() === k.toLowerCase();
},
includeKey: function (i, k) {
return (i + '').toLowerCase().indexOf(k.toLowerCase()) !== -1;
},
includeKeyWC: function (i, k) {
return (i + '').toLowerCase().indexOf((k + '').toLowerCase()) !== -1;
},
always: function () {
return true;
}
};
// 預設 className 前有 ~ 的會轉成 prefix
_.classNameSet = {
prefix: 'si_',
container: '~container',
input: '~input',
inputted: '~inputted',
arrow: '~arrow',
list: '~list',
item: '~item',
item_select: '~item_select',
functionB: '~function',
functionT: '~functionText',
error: '~error',
warning: '~warning'
};
// 預設顯示文字
_.textSet = {
showList: '▼', // 4 way: [▴▸▾◂]
hideList: '▲',
arrowTitle: 'toggle list \n切換顯示查詢列表',
allBtn: '全部',
clearBtn: '清除',
closeBtn: '關閉'//'×'
};
// default 欄位驗證 pattern
// http://blog.wu-boy.com/2009/06/16/1454/
// TODO: ID, Age, 電話, 地址, 性別, ...
// TODO: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern
_.default_verify_pattern = {
'|word': /^\w*$/,
'word': /^\w+$/,
// 整數
'|integer': /^(?:[+\-]\d)?\d*$/,
'integer': /^[+\-]?\d+$/,
// 自然數
'|natural': /^(?:[1-9]\d*)?$/,
'natural': /^[1-9]\d*$/,
// 十進位小數
'|decimal': /^(?:[+\-]\d)?\d*(\.\d+)?$/,
'decimal': /^[+\-]?(?:\d+|\d*\.\d+)$/,
// 數字
'|digit': /^\d*$/,
'digit': /^\d+$/,
IPv4: /^[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}$/,
URI: function (k) {
//return !!library_namespace.URI(k);
try {
return !!new URL(k);
} catch (e) { }
},
// RFC 2822
// http://regexlib.com/DisplayPatterns.aspx
//'RFC2822' : /^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/,
// http://www.regular-expressions.info/email.html
//'email' : /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+([a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b$/i,
email: /^[a-z0-9+_~-]+(\.[a-z0-9+_~-]+)*@([a-z\d]([a-z\d-]*[a-z\d])?\.)+([a-z]{2}|com|org|net)\b$/i,
any: function (k) { return k === '' ? 2 : 0; }
};
_.prototype = {
// 應該盡量把東西放在 class,instance少一點?
// instance public interface -------------------
/* click 事件進行中
TODO:
用更好的方法取代
*/
clickNow: 0,
// instance 的 <input>,.. 之 className, override _.classNameSet.input,..
//class_input : '~',
//class_item : '~',
//..
//maxList : \d,
// 預設清單 height (px)
maxListHeight: 200,
// 設定/取得所有可選的 list
setAllList: function (l) {
var _t = this, _p = pv(_t), i, c = 0, s = _p.arrowO;
_t.showList(0);
if (typeof l === 'object') {
_p.list = l;
if (Array.isArray(l)) {
// 這不準,得用 onList 測試。
c = _t.allListCount = l.length;
} else { for (i in l) c++; _t.allListCount = c; }
library_namespace.debug('setAllList: Get about ' + _t.allListCount + ' items.', 2);
if (s)
if (s = s.style, !c) s.display = 'none';
else if (_t.autoShowArrow) s.display = '';
}
return _p.list;
},
// 自動於有 list 時 show arrow,無時 hide
autoShowArrow: 0,
// 設定要顯現的 list,會回傳 list,需注意可能被更改!
setList: function (l) { // key
return setList.apply(this, arguments);
},
showList: function (show) {
return showList.apply(this, arguments);
},
/*
showArrow: function (show) {
var a = pv(this).arrowO.style;
if (typeof show != 'undefined') a.display = show ? '' : 'none';
return a.display;
},
*/
// 每次 input 就會被 call 一次。可用 instance.setSearch('includeKey') 簡易設定
onInput: function (k) { // key
},
// 設定表單文字欄位的欄位驗證 return 1: warning, 2: error ('Suffering from a pattern mismatch'), string: 將輸入改為回傳值, else OK
// 另外可設定 onkeypress(){return true/false;} 來對每一次按鍵作 check。但這不能處理 paste。 http://irw.ncut.edu.tw/peterju/jscript.html#skill
verify: function (k) { // key
},
set_verify: function (v) {
var m = _.default_verify_pattern;
if (library_namespace.is_Object(m) && (v in m))
v = m[v];
if (library_namespace.is_RegExp(v))
this.verify = function (k) {
return v.test(k) ? 0 : 2;
};
else if (typeof v === 'function')
this.verify = v;
else if (typeof v === 'string' && (m = v.match(/^(\d*)-(\d*)$/)) && (m[1] || m[2]))
this.verify = new Function('k', 'return isNaN(k)' + (m[1] ? '||k<' + m[1] : '') + (m[2] ? '||k>' + m[2] : '') + ' ? 2 : 0;');
else
library_namespace.error('error verify condition of [' + pv(this).inputO.id + ']: [' + v + ']');
return this;
},
// input: (list, index), return [value, title[, key=title||value]]
onList: function (l, i) {
return [l[i] || i, Array.isArray(l) ? l[i] : i];
},
// input: (list, index), return value to set as input key
onSelect: function (l, i) {
return Array.isArray(l) ? l[i] : i;
},
/* searchInList 的減縮版
_.searchInList.call(_instance_, 'includeKey');
eq
_instance_.setSearch('includeKey');
*/
setSearch: function (f) {
return searchInList.call(this, f);
},
setClassName: function (n) {
var t = this;
if (n) t.class_input = t.class_error = t.class_warning = n;
else if (typeof n != 'undefined') { delete t.class_input; delete t.class_error; delete t.class_warning; }
return setClassName.call(this, 'input', pv(this).inputO);
},
setProperty: function (p, v) {
var i = pv(this).inputO;
library_namespace.debug('setProperty: ' + p + '=' + i[p] + '→' + v, 3);
if (typeof v != 'undefined' && v != null) i[p] = v;
return i[p];
},
// set/get input value
setValue: function (v, caller) {
if (arguments.length > 0)
this.toggleToInput();
//library_namespace.log('setValue: ' + this.setProperty('value', v));
v = this.setProperty('value', v);
//library_namespace.log('setValue: ' + v);
if (arguments.length > 0 && caller !== do_verify) {
//library_namespace.log('setValue: call do_verify(' + v + '), list: [' + this.allListCount + ']' + this.setAllList());
do_verify.call(this, v);
}
return v;
},
// set inputted value: 轉換成輸入過的 <span> 時,設定其之值。
setInputted: function (v) {
var _p = pv(this), i = _p.inputO;
if (typeof v === 'undefined') v = this.dInputted(); // dInputted: default inputted value, ===setValue
create_DO(0, removeAllChild(_p.inputtedO), v);
return v;
},
setMaxLength: function (l) {
library_namespace.debug('setMaxLength: set length ' + (l > 0 ? l : null), 3);
return this.setProperty('maxLength', l > 0 ? l : null);
},
setName: function (n) {
this.setProperty('id', n);
return this.setProperty('name', n);
},
setTitle: function (t) {
if (t) pv(this).inputtedO.title = t;
return this.setProperty('title', t || null);
},
// 切換 inputted span/input
toggleToInput: function () {
return toggleToInput.apply(this, arguments);
},
/*
for Unobtrusive JavaScript: 為未啟用JavaScript的情況提供替代方案。
接上 <input> 或 <select>
*/
attach: function (o) { // (input or select object)
library_namespace.debug('attach: ' + o, 4);
//o.replaceNode(_p.inputO);
o = layout.call(this, o);
this.setAllList(this.setAllList());
return o;
},
// (focus or blur, 不驅動 onfocus/onblur)
focus: function (f) { // ,noE
var i = pv(this).inputO;
if (false) {
sl('focus: ' + (f ? 'focus' : 'blur') + (noE ? ' and no event' : ''));
if (f || typeof f === 'undefined') {
if (noE) noE = i.onfocus, i.onfocus = null;
i.focus();
//if(noE)i.onfocus = noE;
} else {
if (noE) noE = i.onblur, i.onblur = null; else this.showList(0);
i.blur();
//if(noE)i.onblur = noE;
}
}
if (f || typeof f === 'undefined')
try {
// @IE5-8 initial: Error @CeL: 2110 [Error] (facility code 10): 控制項不可見、未啟動或無法接受焦點,因此無法將焦點移到控制項上。
// Error @CeL: 2110 [Error] (facility code 10): Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus.
i.focus();
} catch (e) { }
else
this.showList(0),
i.blur();
},
// instance destructor ---------------------------
/*
// usage:
instance = instance.destroy();
// or if you has something more to do:
instance.destroy() && instance = null;
*/
destroy: function () {
pv(this, 1);
}
}; // _.prototype=
// ===================================================
// prevent re-use. 防止再造
//delete _.clone;
//_.allow_inherit = true;
return (
_// JSDT:_module_
);
}