最近、モッサリ感たっぷりなAJAXライブラリを使うのがとても苦痛になってきたので、必要なところ以外バッサリ切り捨ててみた。このデモ(ここをクリック)
を見てもらうと分かるとは思うが、XMLHttp通信、JSON読込、Object.Extend、bind()、Event.observe()/stopObserving()、Element.show()/hide()、キャッシュ付$()、styleをセットできるBuilder.node()、配列のeach()、any()、$H()、$H(x).toXML()、$H(x).toJSON()などが使える。
これでなんと526行しかない。(ちょっと増えました8/9)
とっても短いAJAXライブラリ extractive.js
デモは左にテキストボックスが現れるので、何か入力してEnterを押すと、右にBOXが描画される。Deleteキーで描画OFF、Insertキーで描画ON、Homeキーで連続表示(each())、ESCキーでAJAX読込、TABキーでJSON読込が実行される。
<extractiveソース>
/**
* extractive.js
*
* Functions taken from Prototype library, Log4js
* didn't want to require for just few functions.
* More info at {@link http://prototype.conio.net/}
*
*/
var Builder = {
initialize: function() {
},
camelize: function(name) {
var oStringList = name.split('-');
if (oStringList.length == 1) return oStringList[0];
var camelizedString = name.indexOf('-') == 0
? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
: oStringList[0];
for (var i = 1, len = oStringList.length; i < len; i++) {
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
return camelizedString;
},
node: function(elementname, param, text) {
var element = document.createElement(elementname);
if (param.id) element.id = param.id;
if (param.className) element.className = param.className;
if (param.style) {
for (name in param.style) {
element.style[this.camelize(name)] = param.style[name];
}
}
if (text) {
var value = document.createTextNode(text);
element.appendChild(value);
}
if (param.src) element.src = param.src;
return element;
},
// http://bmky.net/text/note/javascript-css.html
//
getActiveStyle: function ( element, property, pseudo ) {
if( element.currentStyle ) { //IE or Opera
if( property.indexOf( "-" ) != -1 ) property = property.camelize( );
return element.currentStyle[ property ];
}
else if( getComputedStyle ) { //Mozilla or Opera
if( property.indexOf( "-" ) == -1 ) property = property.deCamelize( );
return getComputedStyle( element, pseudo ).getPropertyValue( property );
}
return "";
}
}
var Prototype = {
Version: '1.4.0',
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
emptyFunction: function() {},
K: function(x) {return x}
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
function $(e){return $[e]||($[e]=(document.getElementById(e)||e))}
var Element = {
hide: function(argument) {
var element = $(argument);
element.style.display = 'none';
},
show: function(argument) {
var element = $(argument);
element.style.display = '';
}
}
/**
* Object extention (OO) methods if no prototype avaliable.
*
* @private
* @ignore
*/
if(!Object.prototype.extend) {
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
};
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
};
}
if(!Function.prototype.bind) {
Function.prototype.bind = function(object) {
var __method = this;
return function() {
return __method.apply(object, arguments);
};
};
}
var $break = new Object();
var $continue = new Object();
var Enumerable = {
K: function(x) {return x},
_each: function(iterator) {
for (var i = 0; i < this.length; i++)
iterator(this[i]);
},
each: function(iterator) {
var index = 0;
try {
this._each(function(value) {
try {
iterator(value, index++);
} catch (e) {
if (e != $continue) throw e;
}
});
} catch (e) {
if (e != $break) throw e;
}
},
all: function(iterator) {
var result = true;
this.each(function(value, index) {
result = result && !!(iterator || Prototype.K)(value, index);
if (!result) throw $break;
});
return result;
},
any: function(iterator) {
var result = true;
this.each(function(value, index) {
if (result = !!(iterator || Prototype.K)(value, index))
throw $break;
});
return result;
},
collect: function(iterator) {
var results = [];
this.each(function(value, index) {
results.push(iterator(value, index));
});
return results;
},
detect: function (iterator) {
var result;
this.each(function(value, index) {
if (iterator(value, index)) {
result = value;
throw $break;
}
});
return result;
},
findAll: function(iterator) {
var results = [];
this.each(function(value, index) {
if (iterator(value, index))
results.push(value);
});
return results;
},
grep: function(pattern, iterator) {
var results = [];
this.each(function(value, index) {
var stringValue = value.toString();
if (stringValue.match(pattern))
results.push((iterator || Prototype.K)(value, index));
})
return results;
},
max: function(iterator) {
var result;
this.each(function(value, index) {
value = (iterator || Prototype.K)(value, index);
if (value >= (result || value))
result = value;
});
return result;
},
min: function(iterator) {
var result;
this.each(function(value, index) {
value = (iterator || Prototype.K)(value, index);
if (value <= (result || value))
result = value;
});
return result;
},
pluck: function(property) {
var results = [];
this.each(function(value, index) {
results.push(value[property]);
});
return results;
},
sortBy: function(iterator) {
return this.collect(function(value, index) {
return {value: value, criteria: iterator(value, index)};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
},
toArray: function() {
return this.collect(Prototype.K);
},
include: function(object) {
var found = false;
this.each(function(value) {
if (value == object) {
found = true;
throw $break;
}
});
return found;
},
inject: function(memo, iterator) {
this.each(function(value, index) {
memo = iterator(memo, value, index);
});
return memo;
}
}
Object.inspect = function(object) {
try {
if (object === undefined) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : object.toString();
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
}
Object.extend(Enumerable, {
map: Enumerable.collect,
find: Enumerable.detect,
select: Enumerable.findAll,
member: Enumerable.include,
entries: Enumerable.toArray
});
Object.extend(Array.prototype, Enumerable);
var Hash = {
_each: function(iterator) {
for (key in this) {
var value = this[key];
if (typeof value == 'function') continue;
var pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
},
keys: function() {
return this.pluck('key');
},
values: function() {
return this.pluck('value');
},
merge: function(hash) {
return $H(hash).inject($H(this), function(mergedHash, pair) {
mergedHash[pair.key] = pair.value;
return mergedHash;
});
},
toQueryString: function() {
return this.map(function(pair) {
return pair.map(encodeURIComponent).join('=');
}).join('&');
},
inspect: function() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
},
toXML: function() {
return this.map(function(pair) {
return '<'+pair.key+'>'+ (
typeof pair.value == 'string' ? pair.value : $H(pair.value).toXML()
) +'</'+pair.key+'>';
}).join('');
},
toJSON: function() {
return '{' + this.map(function(pair) {
if ((typeof pair.value == 'undefined')||(typeof pair.value == 'unknown')) return pair.key+': ""';
return pair.key+': '+ (
(typeof pair.value == 'string')||(typeof pair.value == 'number') ? '"'+pair.value+'"' : $H(pair.value).toJSON()
);
}).join(', ') + '}';
}
}
function $H(object) {
var hash = Object.extend({}, object || {});
Object.extend(hash, Enumerable);
Object.extend(hash, Hash);
return hash;
}
/**
* Attatch an observer function to an elements event browser independent.
*
* @param element element to attach event
* @param name name of event
* @param observer observer method to be called
* @private
*/
var Event = {
KEY_BACKSPACE: 8,
KEY_TAB: 9,
KEY_RETURN: 13,
KEY_ESC: 27,
KEY_LEFT: 37,
KEY_UP: 38,
KEY_RIGHT: 39,
KEY_DOWN: 40,
KEY_DELETE: 46,
element: function(event) {
return event.target || event.srcElement;
},
isLeftClick: function(event) {
return (((event.which) && (event.which == 1)) ||
((event.button) && (event.button == 1)));
},
pointerX: function(event) {
return event.pageX || (event.clientX +
(document.documentElement.scrollLeft || document.body.scrollLeft));
},
pointerY: function(event) {
return event.pageY || (event.clientY +
(document.documentElement.scrollTop || document.body.scrollTop));
},
stop: function(event) {
if (event.preventDefault) {
event.preventDefault();
event.stopPropagation();
} else {
event.returnValue = false;
event.cancelBubble = true;
}
},
// find the first node with the given tagName, starting from the
// node the event was triggered on; traverses the DOM upwards
findElement: function(event, tagName) {
var element = Event.element(event);
while (element.parentNode && (!element.tagName ||
(element.tagName.toUpperCase() != tagName.toUpperCase())))
element = element.parentNode;
return element;
},
observers: false,
_observeAndCache: function(element, name, observer, useCapture) {
if (!this.observers) this.observers = [];
if (element.addEventListener) {
this.observers.push([element, name, observer, useCapture]);
element.addEventListener(name, observer, useCapture);
} else if (element.attachEvent) {
this.observers.push([element, name, observer, useCapture]);
element.attachEvent('on' + name, observer);
}
},
unloadCache: function() {
if (!Event.observers) return;
for (var i = 0; i < Event.observers.length; i++) {
Event.stopObserving.apply(this, Event.observers[i]);
Event.observers[i][0] = null;
}
Event.observers = false;
},
observe: function (element, name, observer) {
if (element.addEventListener) { //DOM event model
element.addEventListener(name, observer, false);
} else if (element.attachEvent) { //M$ event model
element.attachEvent('on' + name, observer);
}
},
stopObserving: function(element, name, observer, useCapture) {
if (element.removeEventListener) {
element.removeEventListener(name, observer, useCapture);
} else if (element.detachEvent) {
element.detachEvent('on' + name, observer);
}
}
}
var Requester = {
createXmlHttp : function() {
var x = null;
if (window.XMLHttpRequest) { // IE7,Firefox, Safari
x = new XMLHttpRequest();
return x;
}
try {
x = new ActiveXObject("Msxml2.XMLHTTP"); // IE6
}
catch(e) {
try {
x = new ActiveXObject("Microsoft.XMLHTTP"); // IE5
}
catch(e) {
x = null;
}
}
return x;
},
/**
* Load a JS-script dynamically.
* @param {String} src
*/
loadjson : function(src) {
var documentScripts = document.getElementsByTagName("script");
for (index = 0; index < documentScripts.length; ++index)
{
var documentScript = documentScripts[index];
if (documentScript.src == src) {
return false;
}
}
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
document.getElementsByTagName('head')[0].appendChild(script);
return true;
}
}
/* prevent memory leaks in IE */
Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
// set to true if needed, warning: firefox performance problems
// NOT neeeded for page scrolling, only if draggable contained in
// scrollable elements
includeScrollOffsets: false,
// must be called before calling withinIncludingScrolloffset, every time the
// page is scrolled
prepare: function() {
this.deltaX = window.pageXOffset
|| document.documentElement.scrollLeft
|| document.body.scrollLeft
|| 0;
this.deltaY = window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop
|| 0;
},
realOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.scrollTop || 0;
valueL += element.scrollLeft || 0;
element = element.parentNode;
} while (element);
return [valueL, valueT];
},
cumulativeOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
}
}
<デモソース>
<html>
<head>
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<style type="text/css">
.content1 {
height:10;
position:absolute;
margin-bottom:5px;
background:#FFCC99;
border:1px solid #FF0000;
padding:1px 1 20px 1;
font-size:10px;
width:100px;
}
</style>
<script type="text/javascript" src="extractive.js"></script>
<script type="text/javascript">
var initial = new Array();
initial.push("1");
initial.push("2");
initial.push("3");
var Editable = function() {
this.editbox = $('EditBox');
Event.observe(this.editbox, "keypress", this.onKeyPress.bind(this));
}
Editable.prototype = {
onKeyPress: function(event) {
switch(event.keyCode) {
case 9: // tab
Requester.loadjson("http://localhost:8081/json.txt");
try {
alert("test:"+testjson);
}catch (e) {}
return;
case 13: // Enter
var element1 = Builder.node('div', { className : 'content1',style :{ top : '25',left :'300' }});
element1.innerHTML = $('EditBox').innerHTML.replace(/<\/?[^>]+>/gi, '')
$('entity').appendChild(element1);
alert($('entity').innerHTML);
return;
case 27: // ESC (AjaxRequest)
this.req = Requester.createXmlHttp();
var url = "http://localhost:8081/";
this.req.onreadystatechange = this.callback.bind(this);
this.req.open("GET", url, true);
this.req.send("");
return;
case 45: // insert
Element.show('entity');
return;
case 46: // delete
Element.hide('entity');
return;
case 36: // home
var contentmain = $('entity');
initial.each(
function(value,index) {
var content = Builder.node('div', { id : 'id'+index, className : 'content1',style :{ top : 100 + index*30 +'px' ,left :'300' } },value );
contentmain.appendChild(content);
}.bind(this)
);
}
},
callback: function () {
if(this.req.readyState == 4) { // 読込完了
if(this.req.status == 200) { // HTTP OK
alert(this.req.responseText);
}
else {
alert(this.req.status);
}
}
}
}
</script>
<title></title>
</head>
<body>
<DIV id="EditBox" style="float:left;width:200;height:500;background:white;border:silver 1px solid;padding:3px" contenteditable>
<BR><BR>
</DIV>
<div id="right" style="float:right;">
<div id="entity"></div>
</div>
<script type="text/javascript">
new Editable();
</script>
</body>
</html>
0 件のコメント:
コメントを投稿