js 代码
- /**
- * 如果调用者在传入的options参数中定义 evalScripts=true,同时xmlhttp返回值的html中包含<script>标签的话,执行该脚本
- */
- if (this.options.evalScripts && scripts) {
- /**
- * 注意前二十行左右还有一个 match 的声明
- * var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
- * 和此处的区别就是,正则表达式匹配标记多一个 "g"。
- * 多个g, 所以 scripts 是一个数组,数组中每个元素是一段 <script>...</script> 文本。
- * 没有g, scripts.match(match)[1] 匹配的就是 <script>标记中的 script 代码。
- * 关于正则表达式,请参考javascript的相关资料。
- */
- match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
- setTimeout((function() {
- for (var i = 0; i < scripts.length; i++)
- eval(scripts.match(match)[1]);
- }).bind(this), 10);
- }
- }
- });
- /**
- * 定期更新器
- */
- Ajax.PeriodicalUpdater = Class.create();
- Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
- initialize: function(container, url, options) {
- this.setOptions(options);
- this.onComplete = this.options.onComplete;
- this.frequency = (this.options.frequency || 2);
- // decay 可能是一个时间调整因素
- this.decay = 1;
- this.updater = {};
- this.container = container;
- this.url = url;
- this.start();
- },
- start: function() {
- this.options.onComplete = this.updateComplete.bind(this);
- this.onTimerEvent();
- },
- stop: function() {
- this.updater.onComplete = undefined;
- clearTimeout(this.timer);
- (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
- },
- updateComplete: function(request) {
- if (this.options.decay) {
- this.decay = (request.responseText == this.lastText ?
- this.decay * this.options.decay : 1);
- this.lastText = request.responseText;
- }
- this.timer = setTimeout(this.onTimerEvent.bind(this),
- this.decay * this.frequency * 1000);
- },
- onTimerEvent: function() {
- this.updater = new Ajax.Updater(this.container, this.url, this.options);
- }
- });
- /**
- * 根据 class attribute 的名字得到对象数组,支持 multiple class
- *
- */
- document.getElementsByClassName = function(className) {
- var children = document.getElementsByTagName('*') || document.all;
- var elements = new Array();
- for (var i = 0; i < children.length; i++) {
- var child = children;
- var classNames = child.className.split(' ');
- for (var j = 0; j < classNames.length; j++) {
- if (classNames[j] == className) {
- elements.push(child);
- break;
- }
- }
- }
- return elements;
- }
- /*--------------------------------------------------------------------------*/
- /**
- * Element 就象一个 java 的工具类,主要用来 隐藏/显示/销除 对象,以及获取对象的简单属性。
- *
- */
- if (!window.Element) {
- var Element = new Object();
- }
- Object.extend(Element, {
- /**
- * 切换 显示/隐藏
- */
- toggle: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments);
- element.style.display =
- (element.style.display == 'none' ? '' : 'none');
- }
- },
- hide: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments);
- element.style.display = 'none';
- }
- },
- show: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments);
- element.style.display = '';
- }
- },
- /**
- * 从父节点中移除
- */
- remove: function(element) {
- element = $(element);
- element.parentNode.removeChild(element);
- },
- getHeight: function(element) {
- element = $(element);
- return element.offsetHeight;
- },
- /**
- * 是否拥有 class 属性值
- */
- hasClassName: function(element, className) {
- element = $(element);
- if (!element)
- return;
- var a = element.className.split(' ');
- for (var i = 0; i < a.length; i++) {
- if (a == className)
- return true;
- }
- return false;
- },
- /**
- * 为对象添加 class 属性值
- */
- addClassName: function(element, className) {
- element = $(element);
- Element.removeClassName(element, className);
- element.className += ' ' + className;
- },
- /**
- * 为对象移除 class 属性值
- */
- removeClassName: function(element, className) {
- element = $(element);
- if (!element)
- return;
- var newClassName = '';
- var a = element.className.split(' ');
- for (var i = 0; i < a.length; i++) {
- if (a != className) {
- if (i > 0)
- newClassName += ' ';
- newClassName += a;
- }
- }
- element.className = newClassName;
- },
- // removes whitespace-only text node children
- cleanWhitespace: function(element) {
- var element = $(element);
- for (var i = 0; i < element.childNodes.length; i++) {
- var node = element.childNodes;
- if (node.nodeType == 3 && !/S/.test(node.nodeValue))
- Element.remove(node);
- }
- }
- });
- /**
- * 为 Element.toggle 做了一个符号连接,大概是兼容性的考虑
- */
- var Toggle = new Object();
- Toggle.display = Element.toggle;
- /**
- * 动态插入内容的实现,MS的Jscript实现中对象有一个 insertAdjacentHTML 方法(http://msdn.microsoft.com/workshop/...djacenthtml.asp)
- * 这里算是一个对象形式的封装。
- */
- Abstract.Insertion = function(adjacency) {
- this.adjacency = adjacency;
- }
- Abstract.Insertion.prototype = {
- initialize: function(element, content) {
- this.element = $(element);
- this.content = content;
- if (this.adjacency && this.element.insertAdjacentHTML) {
- this.element.insertAdjacentHTML(this.adjacency, this.content);
- } else {
- /**
- * gecko 不支持 insertAdjacentHTML 方法,但可以用如下代码代替
- */
- this.range = this.element.ownerDocument.createRange();
- /**
- * 如果定义了 initializeRange 方法,则实行,这里相当与定义了一个抽象的 initializeRange 方法
- */
- if (this.initializeRange) this.initializeRange();
- this.fragment = this.range.createContextualFragment(this.content);
- /**
- * insertContent 也是一个抽象方法,子类必须实现
- */
- this.insertContent();
- }
- }
- }
- /**
- * prototype 加深了我的体会,就是写js 如何去遵循 Don’t Repeat Yourself (DRY) 原则
- * 上文中 Abstract.Insertion 算是一个抽象类,定义了名为 initializeRange 的一个抽象方法
- * var Insertion = new Object() 建立一个命名空间
- * Insertion.Before|Top|Bottom|After 就象是四个java中的四个静态内部类,而它们分别继承于Abstract.Insertion,并实现了initializeRange方法。
- */
- var Insertion = new Object();
- /**
- * 将内容插入到指定节点的前面, 与指定节点同级
- */
- Insertion.Before = Class.create();
- Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
- initializeRange: function() {
- this.range.setStartBefore(this.element);
- },
- insertContent: function() {
- this.element.parentNode.insertBefore(this.fragment, this.element);
- }
- });
- /**
- * 将内容插入到指定节点的第一个子节点前,于是内容变为该节点的第一个子节点
- */
- Insertion.Top = Class.create();
- Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
- initializeRange: function() {
- this.range.selectNodeContents(this.element);
- this.range.collapse(true);
- },
- insertContent: function() {
- this.element.insertBefore(this.fragment, this.element.firstChild);
- }
- });
- /**
- * 将内容插入到指定节点的最后,于是内容变为该节点的最后一个子节点
- */
- Insertion.Bottom = Class.create();
- Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
- initializeRange: function() {
- this.range.selectNodeContents(this.element);
- this.range.collapse(this.element);
- },
- insertContent: function() {
- this.element.appendChild(this.fragment);
- }
- });
- /**
- * 将内容插入到指定节点的后面, 与指定节点同级
- */
- Insertion.After = Class.create();
- Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
- initializeRange: function() {
- this.range.setStartAfter(this.element);
- },
- insertContent: function() {
- this.element.parentNode.insertBefore(this.fragment,
- this.element.nextSibling);
- }
- });
- /**
- * 针对 页面元素对象(一般都是表单控件)的工具类,提供一些简单静态方法
- * 这些方法显然常用在表单处理中
- * 注意 Field 这种声明方式类似于 java 声明一个静态的 singleton 工具类
- * 等同于 :
- * var Field = new Object();
- * Field.extend({...});
- *
- * 后文中的 Form, Event, Position 对象声明方式如出一辙
- */
- var Field = {
- /**
- * 清除参数引用的对象的值
- */
- clear: function() {
- for (var i = 0; i < arguments.length; i++)
- $(arguments).value = '';
- },
- /**
- * 使参数引用对象获取焦点
- */
- focus: function(element) {
- $(element).focus();
- },
- /**
- * 判断参数引用对象是否有非空值,如为空值,返回false, 反之true
- */
- present: function() {
- for (var i = 0; i < arguments.length; i++)
- if ($(arguments).value == '') return false;
- return true;
- },
- /**
- * 使选中参数引用对象
- */
- select: function(element) {
- $(element).select();
- },
- /**
- * 使参数引用对象处于可编辑状态
- */
- activate: function(element) {
- $(element).focus();
- $(element).select();
- }
- }
- /**
- * 表单工具类
- */
- var Form = {
- /**
- * 将表单元素序列化后的值(其实就是 name=value 形式的名值配对)组合成 QueryString 的形式
- */
- serialize: function(form) {
- var elements = Form.getElements($(form));
- var queryComponents = new Array();
- for (var i = 0; i < elements.length; i++) {
- var queryComponent = Form.Element.serialize(elements);
- if (queryComponent)
- queryComponents.push(queryComponent);
- }
- return queryComponents.join('&');
- },
- /**
- * 得到表单的所有元素对象
- */
- getElements: function(form) {
- var form = $(form);
- var elements = new Array();
- for (tagName in Form.Element.Serializers) {
- var tagElements = form.getElementsByTagName(tagName);
- for (var j = 0; j < tagElements.length; j++)
- elements.push(tagElements[j]);
- }
- return elements;
- },
- /**
- * 根据 type 和 name 过滤得到表单中符合的 <input> 对象
- */
- getInputs: function(form, typeName, name) {
- var form = $(form);
- var inputs = form.getElementsByTagName('input');
- if (!typeName && !name)
- return inputs;
- var matchingInputs = new Array();
- for (var i = 0; i < inputs.length; i++) {
- var input = inputs;
- if ((typeName && input.type != typeName) ||
- (name && input.name != name))
- continue;
- matchingInputs.push(input);
- }
- return matchingInputs;
- },
- /**
- * 将指定表单的元素置于不可用状态
- */
- disable: function(form) {
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements;
- element.blur();
- element.disabled = 'true';
- }
- },
- /**
- * 将指定表单的元素置于可用状态
- */
- enable: function(form) {
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements;
- element.disabled = '';
- }
- },
- /**
- * 使表单的第一个非 hidden 类型而且处于可用状态的元素获得焦点
- */
- focusFirstElement: function(form) {
- var form = $(form);
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements;
- if (element.type != 'hidden' && !element.disabled) {
- Field.activate(element);
- break;
- }
- }
- },
- /*
- * 重置表单
- */
- reset: function(form) {
- $(form).reset();
- }
- }
- /**
- * 表单元素工具类
- */
- Form.Element = {
- /**
- * 返回表单元素的值先序列化, 其实就是 name=value 形式的名值配对
- */
- serialize: function(element) {
- var element = $(element);
- var method = element.tagName.toLowerCase();
- var parameter = Form.Element.Serializers[method](element);
- if (parameter)
- return encodeURIComponent(parameter[0]) + '=' +
- encodeURIComponent(parameter[1]);
- },
- /**
- * 返回表单元素的值
- */
- getValue: function(element) {
- var element = $(element);
- var method = element.tagName.toLowerCase();
- var parameter = Form.Element.Serializers[method](element);
- if (parameter)
- return parameter[1];
- }
- }
- /**
- * prototype 的所谓序列化其实就是将表单的名字和值组合成一个数组
- */
- Form.Element.Serializers = {
- input: function(element) {
- switch (element.type.toLowerCase()) {
- case 'submit':
- case 'hidden':
- case 'password':
- case 'text':
- return Form.Element.Serializers.textarea(element);
- case 'checkbox':
- case 'radio':
- return Form.Element.Serializers.inputSelector(element);
- }
- return false;
- },
- /**
- * 单/多选框 由此方法处理序列化
- */
- inputSelector: function(element) {
- if (element.checked)
- return [element.name, element.value];
- },
- /**
- * textarea 由此方法处理序列化
- */
- textarea: function(element) {
- return [element.name, element.value];
- },
- /**
- * select 下拉列表由此方法处理序列化
- */
- select: function(element) {
- var value = '';
- if (element.type == 'select-one') {
- var index = element.selectedIndex;
- if (index >= 0)
- value = element.options[index].value || element.options[index].text;
- } else {
- /**
- * 支持 select-mulitple 的下拉列表,返回的数组的第二个元素,是一个值数组
- */
- value = new Array();
- for (var i = 0; i < element.length; i++) {
- var opt = element.options;
- if (opt.selected)
- value.push(opt.value || opt.text);
- }
- }
- return [element.name, value];
- }
- }