Public feed
#722198
/******************************************************************************
Bija - Javascript Library and Framework
[B]ija [I]sn't [J]ust [A]jax
@author: Matan Lurey
@version: 1.1a
@projectDescription: Bija is a multi-use, cross-browser JavaScript library
which enables the use of advanced DOM and Networking
techniques on a variety of systems.
The reason that something like Mootools or Prototype
is not used is to allow only the most efficient
and needed code to be available.
Fully Supported:
- Firefox 2.0 (Gecko)
- Netscape (Gecko)
- Safari 3 (WebKit 420)
- Opera 9
Fully Supported with Work-Arounds:
- Konqueror (Interval-Based checking of Comet data)
Untested, but Compatible:
- Safari 2 (However, uses Konqueror's method of 'push' data)
Pending Tests:
- Internet Explorer 5.5
- Internet Explorer 6
- Internet Explorer 7
*******************************************************************************/
window.Bija = {
version: "1.10a"
}
/** Browser Detection - Tries a non-sniffing method, if possible. *************/
var Browser = {
ie7: (document.all && !window.opera && window.XMLHttpRequest),
ie6: (document.all && !window.opera && !window.XMLHttpRequest),
ie5: (document.all && !window.opera && document.appendData),
ie: (document.all && !window.opera),
webkit: (document.childNodes && !document.all && !navigator.taintEnabled),
khtml: (navigator.userAgent.indexOf('Konqueror' != -1)),
gecko: (!!document.getBoxObjectFor),
opera: (!!window.opera)
}
// Detect WebKit 420 (Safari 3 Beta) -------------------------------------------
Browser.webkit420 = (Browser.webkit && document.evaluate);
/** Class Prototyping - A new face for JavaScript OOP
Ideas from Prototype, Base, and Mootools
Creating a Class: new Class( <methods> )
Extending a Class: Class.Extend( <class> <methods> )
Creating an Interface: new Interface( arguments = method names )
Implementing an Interface: myClass.Implement( <interface> )
Extending a Class Object: myClass.Extend( <methods> )
*/
/******************************************************************************
function: Extend
@param: destination [object]
the object to copy methods to
@param: source [object]
the object to copy methods from
@return: Object
*******************************************************************************/
function Extend(destination, source) {
if (!source && this) return Extend(this, destination);
var safeWrite = (arguments.length > 1);
for (method in source)
if (!safeWrite || !destination[method])
destination[method] = source[method];
return destination;
}
/******************************************************************************
function: Implement
@param: object [object]
the object to try the interface's methods on
@param: iface [Interface]
the Interface object
@return: Object
*******************************************************************************/
function Implement(object, iface) {
if (!iface && this) return Impelement(this, object);
for (var i = 0; i < iface.length; i++)
if (!isFunction(object[iface[i]]) && !isFunction(object.prototype[iface[i]]))
throw new Error("Object does not implement method Object." + iface[i]);
return object;
}
// Interfaces are constructed, and are identical to, Arrays --------------------
var Interface = Array;
/******************************************************************************
object: Class
@constructor:
@param: methods [object]
the object to copy methods from
@return: Class
*******************************************************************************/
function Class(methods) {
var Klass = Class.Empty();
Extend(Klass.prototype, methods);
return Klass;
}
/******************************************************************************
object: Class.Extend
@constructor:
@param: object [class]
the base class (parent class)
@param: methods [object]
the object to copy methods from
@return: Class
*******************************************************************************/
Extend(Class, {
Extend: function(object, methods) {
var Klass = Class.Empty();
Klass.prototype.Super = Klass.prototype = object.prototype;
Extend(Klass.prototype, object);
return Klass;
},
// Internally Used Function to Create a new 'Class' ----------------------------
Empty: function() {
return function() {
if (isFunction(this.init)) { this.init.apply(this, arguments); }
}
}
});
/******************************************************************************
object: Abstract
@constructor:
@param: object [object]
the object to give an abstract to, allowing it to be extended
@return: Object
*******************************************************************************/
function Abstract(object) {
object.Extend = Extend;
return object;
}
/** END CLASS PROTOTYPING *****************************************************/
/** START UTILITY FUNCTIONS
Utility functions are basic functions used throughout to speed up or improve
the usage of common tasks.
*/
/******************************************************************************
function: $
@param: el [element id]
Reads an element from the document DOM, by Id name.
We make sure the HTMLElement inherits HTMLElement.prototype. If it didn't,
it means that we cant extend the DOM, and have to inherit it on demand (IE).
@return: HTMLElement
*******************************************************************************/
function $(el) {
el = document.getElementById(el);
if (!el) return null;
if (!el.Bija) $e(el);
return el;
}
/******************************************************************************
function: $a
@param: collection [collection]
the object to convert to an Array object, to allow usage of it's methods.
@return: Array
*******************************************************************************/
function $a(collection) {
if (!collection) return [];
var arr = (collection.length > 1) ? Array.prototype.slice.call(collection, 0) : [ collection[0] ];
return arr;
}
/******************************************************************************
function: $e
@param: HTMLElement
the htmlelemnt to inherit bija's dom extensions.
@return: HTMLElement
*******************************************************************************/
function $e(htmlobject) {
return (htmlobject.Bija) ? htmlobject : Extend(htmlobject, Element.methods);
}
function $element(tag) {
return $e(document.createElement(tag));
}
/******************************************************************************
function: $Time
Generatese a Unix Timestamp
@return: Integer
*******************************************************************************/
function $time() {
return (new Date).getTime();
}
// isType functions -----------------------------------------------------------
function isArray(obj) {return (!!obj.sort);}
function isFunction(obj) {return (typeof obj == "function");}
function isString(obj) {return (typeof obj == "string");}
function isObject(obj) {return (typeof obj == "object");}
/** END UTILITY FUNCTIONS *****************************************************/
/** START NATIVE EXTENSIONS
We give several native objects additional functionality, or implement
functionality not yet available in their version of ECMAScript
*/
/******************************************************************************
function: $Native
@param: Arguments being Native objects
*******************************************************************************/
function $Native() {
var a = arguments;
for (var i = 0; i < a.length; i++)
a[i].extend = $Native.Extend;
}
// Internally Used, Special Extend Event for Native Objects -------------------
$Native.Extend = function(methods){
for (method in methods) if (!isFunction(this.prototype[method])) this.prototype[method] = methods[method];
}
// Give .Extend to all objects ------------------------------------------------
$Native(Function, String, Array, Number);
/** FUNCTION - Extended *******************************************************/
Function.extend({
// Extend Functionality
extend: Extend,
// Implement Functionality
implement: Implement,
// Delayed Execution
delay: function(ms) {
setTimeout(function() {
this.apply(this, $a(arguments).remove(0))
}, ms);
}
});
/** ARRAY - Extended **********************************************************/
Array.extend({
// Get an item using a method
item: function(index) {
return this[index];
},
// Remove an Item
remove: function(index) {
this.splice(index, 1);
return this;
},
// Enumerates through All
forEach: function(callback) {
var l = this.length;
for (var i = 0; i < l; i++)
callback(this[i]);
return this;
}
})
// Compatibility / Easy Access --------------------------------------------------
Array.prototype.each = Array.prototype.forEach;
/** STRING - Extended **********************************************************/
String.extend({
// Contains another string
contains: function(string) {
return (this.indexOf(string) != -1);
}
});
/** ELEMENT - Extended *********************************************************/
// Special for Cross Browser :(
// Expose Safari's Prototypes ---------------------------------------------------
if (window.webkit && !window.webkit420)
{
window.Document = {}
window.Event = {}
window.HTMLCollection = {}
window.HTMLElement = {}
window.Node = {}
window.Document.prototype = window["[[DOMDocument]]"];
window.Event.prototype = window["[[DOMEvent]]"];
window.HTMLCollection.prototype = window["[[HTMLCollection.prototype]]"];
window.HTMLElement.prototype = window["[[DOMElement.prototype]]"];
window.Node.prototype = window["[[DOMNode.prototype]]"];
}
// Allow IE to manually extend -------------------------------------------------
var Element = {
extend: function(methods) {
if (!Browser.ie) return Extend(HTMLElement.prototype, methods);
Extend(Element.methods, methods);
},
methods: {}
}
// ELEMENT - Extended ***********************************************************
Element.extend({
hide: function() {
this.style.display = "none";
return this;
},
show: function() {
this.style.display = "";
return this;
},
inject: function(el) {
var el = el || document.body;
el.appendChild(this);
return this;
},
remove: function() {
this.parentNode.removeChild(this);
}
});
/** Generate Document.Head for Inserting Scripts and Styles ********************/
document.head = document.getElementsByTagName("head").item(0);
/*******************************************************************************
'Class': Events
Passes a new set of events to another object, used like "abstract"
Events(object); or Events(object.prototype);
Remeber, when "watching" HTMLElement events (click etc..), the first
object returned is the EVENT object (e).
********************************************************************************/
function Events(object) {
// More than one object?
if (arguments.length > 1) {
for (var i = 0; i < arguments.length; i++)
Events(arguments[i]);
return this;
}
return Extend(object, {
events: [],
fire: function(id) {
if (!this.events[id]) return this;
// Fire's an event - return a literal of 'true' to block future events from accessing
// or return an object with a parameter of modargs: true to change the arguments reciveved
var args = $a(arguments).remove(0);
for (var i = 0; i < this.events[id].length; i++) {
var result = this.events[id][i].apply(this, args);
if (result){
if (result == true) return this;
if (isArray(result)) args = result;
}
}
return this;
},
observe: function(id, callback, top) {
if (!isString(id)) return this.observeAll(id);
var Self = this;
// Does it already exist?
if (!this.events[id]) {
// No, check for DOM-needed.
if (this.addEventListener) {
this.addEventListener(id, function(e) {
if (!e) var e = window.event;
Self.fire(id, e);
}, false);
} else if (this.attachEvent) {
this.attachEvent("on"+id, function(e) {
if (!e) var e = window.event;
// Use the same property for all browsers
e.target = e.target || e.srcElement;
e.relatedTarget = e.relatedTarget || e.fromElement || e.toElement;
e.stopPropogation = function() {
e.cancelBubble = true;
}
Self.fire(id, e);
});
} else {
// No, this isn't a DOM object. Do nothing.
}
// Setup default data
this.events[id] = [ callback ];
return this;
}
// No, add it (top or bottom)
if (!top) this.events[id].push(callback); else this.events[id].unshift(callback);
return this;
},
observeAll: function() {
for (id in obj) this.watch(id, obj[id]);
return this;
}
});
}
// Give Events to Window (watch load, for example) ------------------------------------------
Events(window);
// Also give a DOMReady functional callback -------------------------------------------------
// window.observe("DOMContentLoaded") -- works natively for mozilla/opera, lets fix others
// webkit, interval-based with document.readyState ------------------------------------------
if (Browser.webkit) {
// Interval based checking
var _timer = setInterval(function(){
if (/loaded|complete/.test(document.readyState)) {
clearInterval(_timer);
window.fire("DOMContentLoaded");
}
}, 10);
}
// ie, 'defered' blank javascript -----------------------------------------------------------
if (Browser.ie) {
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
$("__ie_onload").onreadystatechange = function() {
if (this.readyState == "complete") {
window.fire("DOMContentLoaded");
}
};
}
/** END EVENTS *****************************************************************************/
/** XMLHttpRequest(), a Cross-Browser Approach *********************************************/
if (!window.XMLHttpRequest && window.ActiveXObject)
window.XMLHttpRequest = function(){
return new ActiveXObject("Msxml3.XMLHTTP");
}
/** Ajax object, easily request and post data **********************************************/
var Ajax = new Class({
init: function() {
// Create XHR object to use
this._xhr = new XMLHttpRequest();
},
get: function(url, data) {
// Get default data
var data = data || " ";
var xhr = this._xhr;
// Make sure it's closed
if (xhr.readyState != 0) this.cancel();
// Open a connection
xhr.open( (data == " ") ? "GET" : "POST" , url, true );
// Check readyStates
xhr.onreadystatechange = function() {
if (xhr.readyState == 4)
if (xhr.status != 200 && xhr.status != 304)
xhr.fire("complete", xhr.responseText);
else
xhr.fire("error", xhr.responseText);
}
// Send data
xhr.send(data);
return this;
},
post: function(url, data) {
this._xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
return this.get(url, data);
},
cancel: function() {
this._xhr.abort();
}
});
/** Comet object, 'easily' get streaming data **********************************************/
/** Note:
You must do browser-side userAgent checking when passing data feeds to the Comet object,
this is due to the different formats that each take. I know, I know, we could just use
<script> tags, but that by far is the least-efficent method, and I've found a native
and fast method for every browser but IE and Older WebKits/KHTML.
Server-Side methods are shown in PHP, but could be used in any language or system.
On Rise or Fall, we use a custom coded HTTP Daemon, not even a real web server.
Methods used:
// Firefox and Gecko-Powered Browsers
* The XHR object has a property called 'multicast', meaing it can recieve multiple
parts of data natively, without polling, and without 'filling up' with data. Perfect.
* We use responseText, stripping off the last two characters (' <')
(SERVER-SIDE):
header("Content-Type: multipart/x-mixed-replace;boundary=comet");
flush();
while(true)
echo("--comet\nContent-Type: text/xml\n\n");
echo("<?xml version=\"1.0\" encoding=\"UTF-8\"?><data>");
echo(time());
echo("\n<data>");
flush();
sleep(1);
}
// Opera
* Opera supports multipart/x-mixed/replace, but NOT on an xhr. We could use an iFrame,
but we're trying to use non-hackish & efficient methods. Luckily, Opera 9 supports
an HTML5 element called <event-source />, which allows native http streaming.
(SERVER-SIDE):
header("Content-Type: application/x-dom-event-stream");
flush();
while(true)
echo("Event: comet\ndata:");
echo(time());
flush();
sleep(1);
*/
var Comet = new Class({
init: function() {
// Build the requesting object, no matter the method
var Self = this;
// GECKO *******************************************
if (Browser.gecko) {
var gadget = new XMLHttpRequest();
gadget.multipart = true;
gadget.onreadystatechange = function() {
switch (gadget.readyState) {
case 2:
Self.fire("connected");
break;
case 3:
Self.fire("data", gadget.responseText.substring(0, gadget.responseText.length - 2));
break;
case 4:
Self.fire("closed");
break;
}
}
this.gadget = gadget;
return this;
}
// OPERA ********************************************
if (Browser.opera) {
var gadget = $element("event-source");
gadget.addEventListener("comet", function(event){
Self.fire("data", event.data);
}, false);
this.gadget = gadget;
return this;
}
// SAFARI 3 BETA / WEBKIT 420 **********************
if (Browser.webkit420) {
var gadget = $element("iframe");
gadget.onload = function() {
Self.fire("data", gadget.contentDocument.innerHTML);
}
this.gadget = gadget;
return this;
}
},
connect: function(url) {
// GECKO *******************************************
if (Browser.gecko) {
this.gadget.open("GET", url, true);
this.gadget.send("");
return this;
}
// OPERA *******************************************
if (Browser.opera) {
this.gadget.setAttribute("src", url);
this.gadget.inject();
return this;
}
// SAFARI 3 BETA / WEBKIT 420 **********************
if (Browser.webkit420) {
this.gadget.setAttribute("src", url);
this.gadget.inject();
}
},
disconnect: function() {
// GECKO *******************************************
if (Browser.gecko) {
this.gadget.abort();
return this;
}
// OPERA *******************************************
if (Browser.opera) {
this.gadget.remove();
this.gadget.setAttribute("src", "about:blank");
}
}
});
Events(Comet.prototype);