月曜日, 8月 04, 2008

【JavaScript】 prototype.jsを抜粋して作ったとても小さなAJAXライブラリ このエントリーを含むはてなブックマーク


最近、モッサリ感たっぷりな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 件のコメント:

 
© 2006-2015 Virtual Technology
当サイトではGoogle Analyticsを使ってウェブサイトのトラフィック情報を収集しています。詳しくは、プライバシーポリシーを参照してください。