git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@92557 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-11-21 02:33:06 +00:00
parent 46b0f0c959
commit dca8c0cb6f
94 changed files with 18745 additions and 383 deletions

View File

@ -28,13 +28,7 @@ class CalendarDateField extends DateField {
}
// javascript: concrete
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.class.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.specifity.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.matches.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/src/jquery.dat.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/src/jquery.concrete.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/dist/jquery.concrete-latest.js');
// javascript: custom
Requirements::javascript(SAPPHIRE_DIR . '/javascript/CalendarDateField.js');

View File

@ -1073,7 +1073,7 @@ class ComplexTableField_Popup extends Form {
Requirements::css(CMS_DIR . '/css/typography.css');
Requirements::css(CMS_DIR . '/css/cms_right.css');
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/prototype/prototype.js");
Requirements::javascript(SAPPHIRE_dir . "/thirdparty/behaviour/behaviour.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/scriptaculous.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/scriptaculous/controls.js");

View File

@ -58,13 +58,7 @@ class TabSet extends CompositeField {
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.all.css');
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.tabs.css');
// concrete
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.class.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.specifity.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.matches.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/src/jquery.dat.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/src/jquery.concrete.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/dist/jquery.concrete-latest.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js');

View File

@ -1,4 +1,5 @@
(function($) {
$.concrete('ss', function($){
/**
* Formats a <input type="text"> field with a jQuery UI datepicker.
*
@ -6,17 +7,15 @@
*
* @author Ingo Schommer, SilverStripe Ltd.
*/
$('.calendardate').concrete('ss', function($){
return {
$('.calendardate').concrete({
onmatch: function() {
this.find('input').each(function() {
var conf = $(this).metadata();
if(conf.minDate) conf.minDate = new Date(Date.parse(conf.minDate));
//
$(this).datepicker(conf);
});
}
};
});
});
}(jQuery));

View File

@ -1,4 +1,5 @@
(function($){
$.concrete('ss', function($){
/**
* Lightweight wrapper around jQuery UI tabs.
* Ensures that anchor links are set properly,
@ -6,8 +7,7 @@
* their height explicitly set. This is important
* for forms inside the CMS layout.
*/
$('.ss-tabset').concrete('ss', function($){
return {
$('.ss-tabset').concrete({
onmatch: function() {
this.rewriteHashlinks();
@ -15,6 +15,8 @@
this.tabs({
cookie: $.cookie ? { expires: 30, path: '/', name: 'ui-tabs-' + this.attr('id') } : false
});
this._super();
},
/**
@ -29,6 +31,6 @@
if(href) $(this).attr('href', href);
});
}
};
});
});
})(jQuery);

View File

@ -342,13 +342,7 @@ class Security extends Controller {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/ui.core.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/ui.tabs.js');
// concrete
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.class.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.specifity.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-selector/src/jquery.selector.matches.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/src/jquery.dat.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/src/jquery.concrete.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/dist/jquery.concrete-latest.js');
Requirements::css(THIRDPARTY_DIR . '/jquery/themes/smoothness/ui.all.css');
Requirements::css(THIRDPARTY_DIR . '/jquery/themes/smoothness/ui.tabs.css');

BIN
thirdparty/jquery-concrete/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,8 +1,8 @@
---
format: 1
handler:
commit: c857ed5d684031115a2bb369f601c7147a2c69bc
commit: 488376e8aa3c046cbf60adf43c42c76b5031fe20
branch: master
lock: false
repository_url: git://github.com/hafriedlander/jquery.concrete.git
repository_class: Piston::Git::Repository
repository_url: git://github.com/hafriedlander/jquery.concrete.git

View File

@ -1,8 +1,16 @@
h1. Concrete - Support for ConcreteUI programming in jQuery
A basic desire for jQuery programming is some sort of OO or other organisational method for code. For your consideration,
we provide a library for ConcreteUI style programming. In ConcreteUI you attach behavioral code to DOM objects. Concrete extends this
concept beyond what is provided by other libraries to provide a very easy to use system with class like, ploymorphic, namespaced properties
By Hamish Friedlander, with thanks to "SilverStripe":http://www.silverstripe.com/
Concrete tries to provide a new model of code organisation - a replacement for Object Oriented programming that is focused on adding functions to groups of DOM elements based on the structure and contents of those DOM elements. It's a merging of the model and view layer that initially seems weird, but can give very powerful results.
We're standing on the shoulders of giants here - combining ideas from Prototype's behaviour & lowpro and jQuery's effen & livequery (who themselves stole ideals from Self's Morphic UI and others), but extending & combining the concepts presented in those tools to provide a complete alternative to traditional OO concepts - self-aware methods, inheritance, polymorphisim and namespacing without a single class definition.
h2. Getting Started
* Walk through the "Tutorial":http://hafriedlander.github.com/jquery.concrete/tutorial/
* Watch the "Screencast":http://www.vimeo.com/6353390 (shot during a introductory developer meeting at SilverStripe)
* Join the "Google Group":http://groups.google.com/group/jquery-concrete and let us know what you think, or what other features you'd like to see
h2. Basic use
@ -226,17 +234,46 @@ And the concrete definitions
Then doing $('div').bar(); will _not_ display b. Even though the span rule could never match a div, because baz is defined for some rule in the foo namespace, the base namespace will never be checked
h4. Forcing base
h4. Cleaner namespace blocks
In some situations (such as the last example) you may want to force using the base namespace. In this case you can call concrete without any arguments. For example, if the first definition in the previous example was
When declaring a lot of namespaces, you can declare the namespace seperately from the concrete definitions, like so
<pre><code>
$('div').concrete('foo', {
bar: function() { this.concrete().baz(); }
$.concrete('foo', function($){
$('div').concrete({
bar: function() { .. }
})
})
</code></pre>
Then b _would_ be output to the console. It is up to the developer to decide when they need to be explicit about using the base namespace
You can also nest declarations. In this next example, we're defining the functions $().concrete('zap').bar() and $().concrete('zap.pow').baz()
<pre><code>
$.concrete('zap', function($){
$('div').concrete({
bar: function() { .. }
})
$.concrete('pow', function($){
$('div').concrete({
baz: function() { .. }
})
})
})
</code></pre>
h4. Calling to another namespace (and forcing base)
Inside a namespace, namespace lookups are by default relative to the current namespace.
In some situations (such as the last example) you may want to force using the base namespace. In this case you can call concrete with the first argument being the base namespace code '.'. For example, if the first definition in the previous example was
<pre><code>
$('div').concrete('foo', {
bar: function() { this.concrete('.').baz(); }
})
</code></pre>
Then b _would_ be output to the console.
h4. Using
@ -252,8 +289,19 @@ Sometimes a block outside of a namespace will need to refer to that namespace re
});
</code></pre>
This equivilent to /with/ in javascript, and just like /with/, care should be taken to only use this construct in situations that merit it.
This equivalent to /with/ in javascript, and just like /with/, care should be taken to only use this construct in situations that merit it.
h2. License
Copyright (C) 2009 Hamish Friedlander (hamish@silverstripe.com) and SilverStripe Limited (www.silverstripe.com). All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Hamish Friedlander nor SilverStripe nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

54
thirdparty/jquery-concrete/build.sh vendored Executable file
View File

@ -0,0 +1,54 @@
#!/bin/sh
VER=$1
# Get the version - a tag if possible, otherwise a short ref (not well tested code)
if [ "$VER " = " " ] ; then \
VER=`git rev-parse --abbrev-ref=strict HEAD`
fi
if [ "$VER" = "master" ] ; then \
VER=`git show --pretty=format:"%h" --quiet`
fi
# Specify the output file's name
FILE="dist/jquery.concrete-$VER.js"
mkdir -p dist
rm dist/*.js
echo "/* jQuery.Concrete - Copyright 2009 Hamish Friedlander and SilverStripe. Version $VER. */" > $FILE
for x in \
vendor/jquery.selector/jquery.class.js \
vendor/jquery.selector/jquery.selector.js \
vendor/jquery.selector/jquery.selector.specifity.js \
vendor/jquery.selector/jquery.selector.matches.js \
src/jquery.focusinout.js \
src/jquery.concrete.js \
src/jquery.concrete.dommaybechanged.js \
src/jquery.concrete.events.js \
src/jquery.concrete.ctors.js \
src/jquery.concrete.properties.js
do \
echo >> $FILE
echo "/* $x */" >> $FILE
echo >> $FILE
cat $x >> $FILE
echo ';' >> $FILE
echo >> $FILE
done
ln -s `basename "$FILE"` dist/jquery.concrete-latest.js
# cp LICENSE /tmp/
# cp $FILE /tmp/
# git checkout dist
# mv /tmp/$FILE .
# mv /tmp/LICENSE .
# git add $FILE
# git add LICENSE
# git commit -m "Update dist to master version $VER"
# git checkout master

View File

@ -0,0 +1,1490 @@
/* jQuery.Concrete - Copyright 2009 Hamish Friedlander and SilverStripe. Version 0.9. */
/* vendor/jquery.selector/jquery.class.js */
/**
* Very basic Class utility. Based on base and jquery.class.
*
* Class definition: var Foo = Base.extend({ init: function(){ Constructor }; method_name: function(){ Method } });
*
* Inheritance: var Bar = Foo.extend({ method_name: function(){ this._super(); } });
*
* new-less Constructor: new Foo(arg) <-same as-> Foo(arg)
*/
var Base;
(function(){
var marker = {}, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Base = function(){};
Base.addMethod = function(name, func) {
var _super = this._super;
if (_super && fnTest.test(func)) {
this.prototype[name] = function(){
var tmp = this._super;
this._super = _super[name];
try {
var ret = func.apply(this, arguments);
}
finally {
this._super = tmp;
}
return ret;
}
}
else this.prototype[name] = func;
}
Base.addMethods = function(props) {
for (var name in props) {
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
else this.prototype[name] = props[name];
}
}
// Create a new Class that inherits from this class
Base.extend = function(props) {
// The dummy class constructor
var Kls = function() {
if (arguments[0] === marker) return;
if (this instanceof Kls) {
if (this.init) this.init.apply(this, arguments);
}
else {
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
}
}
// Add the common class variables and methods
Kls.constructor = Kls;
Kls.extend = Base.extend;
Kls.addMethod = Base.addMethod;
Kls.addMethods = Base.addMethods;
Kls._super = this.prototype;
// Attach the parent object to the inheritance chain
Kls.prototype = new this(marker);
// Copy the properties over onto the new prototype
Kls.addMethods(props);
return Kls;
};
})();;
/* vendor/jquery.selector/jquery.selector.js */
(function($){
var tokens = {
UNICODE: /\\[0-9a-f]{1,6}(?:\r\n|[ \n\r\t\f])?/,
ESCAPE: /(?:UNICODE)|\\[^\n\r\f0-9a-f]/,
NONASCII: /[^\x00-\x7F]/,
NMSTART: /[_a-z]|(?:NONASCII)|(?:ESCAPE)/,
NMCHAR: /[_a-z0-9-]|(?:NONASCII)|(?:ESCAPE)/,
IDENT: /-?(?:NMSTART)(?:NMCHAR)*/,
NL: /\n|\r\n|\r|\f/,
STRING: /(?:STRING1)|(?:STRING2)|(?:STRINGBARE)/,
STRING1: /"(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\"])*"/,
STRING2: /'(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\'])*'/,
STRINGBARE: /(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\]])*/,
FUNCTION: /(?:IDENT)\(\)/,
INTEGER: /[0-9]+/,
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
WITHOUTN: /([-+])?(INTEGER)/
}
var rx = {
not: /:not\(/,
not_end: /\)/,
tag: /((?:IDENT)|\*)/,
id: /#(IDENT)/,
cls: /\.(IDENT)/,
attr: /\[\s*(IDENT)\s*(?:([^=]?=)\s*(STRING)\s*)?\]/,
pseudo_el: /(?::(first-line|first-letter|before|after))|(?:::((?:FUNCTION)|(?:IDENT)))/,
pseudo_cls_nth: /:nth-child\(\s*(?:(?:WITHN)|(?:WITHOUTN)|(odd|even))\s*\)/,
pseudo_cls: /:(IDENT)/,
comb: /\s*(\+|~|>)\s*|\s+/,
comma: /\s*,\s*/,
important: /\s+!important\s*$/
}
/* Replace placeholders with actual regex, and mark all as case insensitive */
var token = /[A-Z][A-Z0-9]+/;
for (var k in rx) {
var src = rx[k].source;
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
rx[k] = new RegExp(src, 'gi');
}
/**
* A string that matches itself against regexii, and keeps track of how much of itself has been matched
*/
var ConsumableString = Base.extend({
init: function(str) {
this.str = str;
this.pos = 0;
},
match: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) {
this.pos = rx.lastIndex ? rx.lastIndex : this.str.length ;
return m;
}
return null;
},
peek: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) return m;
return null;
},
showpos: function() {
return this.str.slice(0,this.pos)+'<HERE>' + this.str.slice(this.pos);
},
done: function() {
return this.pos == this.str.length;
}
})
/* A base class that all Selectors inherit off */
var SelectorBase = Base.extend({});
/**
* A class representing a Simple Selector, as per the CSS3 selector spec
*/
var SimpleSelector = SelectorBase.extend({
init: function() {
this.tag = null;
this.id = null;
this.classes = [];
this.attrs = [];
this.nots = [];
this.pseudo_classes = [];
this.pseudo_els = [];
},
parse: function(selector) {
var m;
/* Pull out the initial tag first, if there is one */
if (m = selector.match(rx.tag)) this.tag = m[1];
/* Then for each selection type, try and find a match */
do {
if (m = selector.match(rx.not)) {
this.nots[this.nots.length] = SelectorsGroup().parse(selector)
if (!(m = selector.match(rx.not_end))) {
throw 'Invalid :not term in selector';
}
}
else if (m = selector.match(rx.id)) this.id = m[1];
else if (m = selector.match(rx.cls)) this.classes[this.classes.length] = m[1];
else if (m = selector.match(rx.attr)) this.attrs[this.attrs.length] = [ m[1], m[2], m[3] ];
else if (m = selector.match(rx.pseudo_el)) this.pseudo_els[this.pseudo_els.length] = m[1] || m[2];
else if (m = selector.match(rx.pseudo_cls_nth)) {
if (m[3]) {
var a = parseInt((m[1]||'')+(m[2]||'1'));
var b = parseInt((m[4]||'')+(m[5]||'0'));
}
else {
var a = m[8] ? 2 : 0;
var b = m[8] ? (4-m[8].length) : parseInt((m[6]||'')+m[7]);
}
this.pseudo_classes[this.pseudo_classes.length] = ['nth-child', [a, b]];
}
else if (m = selector.match(rx.pseudo_cls)) this.pseudo_classes[this.pseudo_classes.length] = [m[1]];
} while(m && !selector.done());
return this;
}
})
/**
* A class representing a Selector, as per the CSS3 selector spec
*/
var Selector = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = SimpleSelector().parse(cons);
while (!cons.done() && !cons.peek(rx.comma) && (m = cons.match(rx.comb))) {
this.parts[this.parts.length] = m[1] || ' ';
this.parts[this.parts.length] = SimpleSelector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
/**
* A class representing a sequence of selectors, as per the CSS3 selector spec
*/
var SelectorsGroup = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = Selector().parse(cons);
while (!cons.done() && (m = cons.match(rx.comma))) {
this.parts[this.parts.length] = Selector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
$.selector = function(s){
var cons = ConsumableString(s);
var res = SelectorsGroup().parse(cons);
res.selector = s;
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
else return res;
}
$.selector.SelectorBase = SelectorBase;
$.selector.SimpleSelector = SimpleSelector;
$.selector.Selector = Selector;
$.selector.SelectorsGroup = SelectorsGroup;
})(jQuery)
;
/* vendor/jquery.selector/jquery.selector.specifity.js */
(function($) {
$.selector.SimpleSelector.addMethod('specifity', function() {
if (this.spec) return this.spec;
var spec = [
this.id ? 1 : 0,
this.classes.length + this.attrs.length + this.pseudo_classes.length,
((this.tag && this.tag != '*') ? 1 : 0) + this.pseudo_els.length
];
$.each(this.nots, function(i,not){
var ns = not.specifity(); spec[0] += ns[0]; spec[1] += ns[1]; spec[2] += ns[2];
});
return this.spec = spec;
})
$.selector.Selector.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
if (i%2) return;
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
})
$.selector.SelectorsGroup.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
})
})(jQuery);
;
/* vendor/jquery.selector/jquery.selector.matches.js */
/*
This attempts to do the opposite of Sizzle.
Sizzle is good for finding elements for a selector, but not so good for telling if an individual element matches a selector
*/
(function($) {
/**** CAPABILITY TESTS ****/
var div = document.createElement('div');
div.innerHTML = '<form id="test"><input name="id" type="text"/></form>';
// In IE 6-7, getAttribute often does the wrong thing (returns similar to el.attr), so we need to use getAttributeNode on that browser
var getAttributeDodgy = div.firstChild.getAttribute('id') !== 'test';
// Does browser support Element.firstElementChild, Element.previousElementSibling, etc.
var hasElementTraversal = div.firstElementChild && div.firstElementChild.tagName == 'FORM';
// Does browser support Element.children
var hasChildren = div.children && div.children[0].tagName == 'FORM';
var FUNC_IN = /^\s*function\s*\([^)]*\)\s*{/;
var FUNC_OUT = /}\s*$/;
var funcToString = function(f) {
return (''+f).replace(FUNC_IN,'').replace(FUNC_OUT,'');
}
// Can we use Function#toString ?
try {
var testFunc = function(){ return 'good' };
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
}
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
/**** INTRO ****/
var GOOD = /GOOD/g;
var BAD = /BAD/g;
var STARTS_WITH_QUOTES = /^['"]/g;
var join = function(js) {
return js.join('\n');
}
var join_complex = function(js) {
code = new String(js.join('\n')); // String objects can have properties set. strings can't
code.complex = true;
return code;
}
/**** ATTRIBUTE ACCESSORS ****/
var getAttr;
// Good browsers
if (!getAttributeDodgy) {
getAttr = function(attr){ return 'var _'+attr+' = el.getAttribute("'+attr+'");' ; }
}
// IE 6, 7
else {
// On IE 6 + 7, getAttribute still has to be called with DOM property mirror name, not attribute name. Map attributes to those names
var getAttrIEMap = { 'class': 'className', 'for': 'htmlFor' };
getAttr = function(attr) {
var ieattr = getAttrIEMap[attr] || attr;
return 'var _'+attr+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
}
}
/**** ATTRIBUTE COMPARITORS ****/
var attrchecks = {
'-': '!_K',
'=': '_K != "V"',
'!=': '_K == "V"',
'~=': '__K.indexOf(" V ") == -1',
'^=': '!_K || _K.indexOf("V") != 0',
'*=': '!_K || _K.indexOf("V") == -1',
'$=': '!_K || _K.substr(_K.length-"V".length) != "V"'
}
/**** STATE TRACKER ****/
var State = $.selector.State = Base.extend({
init: function(){
this.reset();
},
reset: function() {
this.attrs = {}; this.wsattrs = {};
},
prev: function(){
this.reset();
if (hasElementTraversal) return 'el = el.previousElementSibling';
return 'while((el = el.previousSibling) && el.nodeType != 1) {}';
},
next: function() {
this.reset();
if (hasElementTraversal) return 'el = el.nextElementSibling';
return 'while((el = el.nextSibling) && el.nodeType != 1) {}';
},
prevLoop: function(body){
this.reset();
if (hasElementTraversal) return join([ 'while(el = el.previousElementSibling){', body]);
return join([
'while(el = el.previousSibling){',
'if (el.nodeType != 1) continue;',
body,
]);
},
parent: function() {
this.reset();
return 'el = el.parentNode;';
},
parentLoop: function(body) {
this.reset();
return join([
'while((el = el.parentNode) && el.nodeType == 1){',
body,
'}'
]);
},
uses_attr: function(attr) {
if (this.attrs[attr]) return;
this.attrs[attr] = true;
return getAttr(attr);
},
uses_wsattr: function(attr) {
if (this.wsattrs[attr]) return;
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var __'+attr+' = " "+_'+attr+'+" ";']);
},
save: function(lbl) {
return 'var el'+lbl+' = el;';
},
restore: function(lbl) {
this.reset();
return 'el = el'+lbl+';';
}
});
/**** PSEUDO-CLASS DETAILS ****/
var pseudoclschecks = {
'first-child': join([
'var cel = el;',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }',
]),
'last-child': join([
'var cel = el;',
'while(cel = cel.nextSibling){ if (cel.nodeType === 1) BAD; }'
]),
'nth-child': function(a,b) {
var get_i = join([
'var i = 1, cel = el;',
'while(cel = cel.previousSibling){',
'if (cel.nodeType === 1) i++;',
'}',
]);
if (a == 0) return join([
get_i,
'if (i- '+b+' != 0) BAD;'
]);
else if (b == 0 && a >= 0) return join([
get_i,
'if (i%'+a+' != 0 || i/'+a+' < 0) BAD;'
]);
else if (b == 0 && a < 0) return join([
'BAD;'
]);
else return join([
get_i,
'if ((i- '+b+')%'+a+' != 0 || (i- '+b+')/'+a+' < 0) BAD;'
]);
}
};
// Needs to refence contents of object, so must be injected after definition
pseudoclschecks['only-child'] = join([
pseudoclschecks['first-child'],
pseudoclschecks['last-child']
]);
/**** SimpleSelector ****/
$.selector.SimpleSelector.addMethod('compile', function(el) {
var js = [];
/* Check against element name */
if (this.tag && this.tag != '*') {
js[js.length] = 'if (el.tagName != "'+this.tag.toUpperCase()+'") BAD;';
}
/* Check against ID */
if (this.id) {
js[js.length] = el.uses_attr('id');
js[js.length] = 'if (_id !== "'+this.id+'") BAD;';
}
/* Build className checking variable */
if (this.classes.length) {
js[js.length] = el.uses_wsattr('class');
/* Check against class names */
$.each(this.classes, function(i, cls){
js[js.length] = 'if (__class.indexOf(" '+cls+' ") == -1) BAD;';
})
}
/* Check against attributes */
$.each(this.attrs, function(i, attr){
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
var check = attrchecks[ attr[1] || '-' ];
check = check.replace( /K/g, attr[0]).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
js[js.length] = 'if ('+check+') BAD;';
});
/* Check against nots */
$.each(this.nots, function(i, not){
var lbl = ++lbl_id;
var func = join([
'l'+lbl+':{',
not.compile(el).replace(BAD, 'break l'+lbl).replace(GOOD, 'BAD'),
'}'
]);
if (!(not instanceof $.selector.SimpleSelector)) func = join([
el.save(lbl),
func,
el.restore(lbl)
])
js[js.length] = func;
});
/* Check against pseudo-classes */
$.each(this.pseudo_classes, function(i, pscls){
var check = pseudoclschecks[pscls[0]];
if (check) {
js[js.length] = ( typeof check == 'function' ? check.apply(this, pscls[1]) : check );
}
else if (check = $.find.selectors.filters[pscls[0]]) {
if (funcToString) {
js[js.length] = funcToString(check).replace(/elem/g,'el').replace(/return([^;]+);/,'if (!($1)) BAD;');
}
else {
js[js.length] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;'
}
}
});
js[js.length] = 'GOOD';
/* Pass */
return join(js);
});
var lbl_id = 0;
/** Turns an compiled fragment into the first part of a combination */
function as_subexpr(f) {
if (f.complex)
return join([
'l'+(++lbl_id)+':{',
f.replace(GOOD, 'break l'+lbl_id),
'}',
]);
else
return f.replace(GOOD, '');
}
var combines = {
' ': function(el, f1, f2) {
return join_complex([
f2,
'while(true){',
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el).replace(BAD, 'continue'),
'}'
]);
},
'>': function(el, f1, f2) {
return join([
f2,
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el)
]);
},
'~': function(el, f1, f2) {
return join_complex([
f2,
el.prevLoop(),
f1.compile(el).replace(BAD, 'continue'),
'}',
'BAD;'
]);
},
'+': function(el, f1, f2) {
return join([
f2,
el.prev(),
'if (!el) BAD;',
f1.compile(el)
]);
}
};
$.selector.Selector.addMethod('compile', function(el) {
l = this.parts.length;
expr = this.parts[--l].compile(el);
while (l) {
combinator = this.parts[--l];
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
}
return expr;
});
$.selector.SelectorsGroup.addMethod('compile', function(el) {
var expr = [], lbl = ++lbl_id;
for (var i=0; i < this.parts.length; i++) {
expr[expr.length] = join([
i == 0 ? el.save(lbl) : el.restore(lbl),
'l'+lbl+'_'+i+':{',
this.parts[i].compile(el).replace(BAD, 'break l'+lbl+'_'+i),
'}'
]);
}
expr[expr.length] = 'BAD;';
return join(expr);
});
$.selector.SelectorBase.addMethod('matches', function(el){
this.matches = new Function('el', join([
'if (!el) return false;',
this.compile(new State()).replace(BAD, 'return false').replace(GOOD, 'return true')
]));
return this.matches(el);
});
})(jQuery);
;
/* src/jquery.focusinout.js */
(function($){
/**
* Add focusin and focusout support to bind and live for browers other than IE. Designed to be usable in a delegated fashion (like $.live)
* Copyright (c) 2007 Jörn Zaefferer
*/
$.support.focusInOut = !!($.browser.msie);
if (!$.support.focusInOut) {
// Emulate focusin and focusout by binding focus and blur in capturing mode
$.each({focus: 'focusin', blur: 'focusout'}, function(original, fix){
$.event.special[fix] = {
setup: function(){
if (!this.addEventListener) return false;
this.addEventListener(original, $.event.special[fix].handler, true);
},
teardown: function(){
if (!this.removeEventListener) return false;
this.removeEventListener(original, $.event.special[fix].handler, true);
},
handler: function(e){
arguments[0] = $.event.fix(e);
arguments[0].type = fix;
return $.event.handle.apply(this, arguments);
}
};
});
}
(function(){
//IE has some trouble with focusout with select and keyboard navigation
var activeFocus = null;
$(document)
.bind('focusin', function(e){
var target = e.realTarget || e.target;
if (activeFocus && activeFocus !== target) {
e.type = 'focusout';
$(activeFocus).trigger(e);
e.type = 'focusin';
e.target = target;
}
activeFocus = target;
})
.bind('focusout', function(e){
activeFocus = null;
});
})();
})(jQuery);;
/* src/jquery.concrete.js */
var console;
(function($) {
var namespaces = {};
$.concrete = function() {
$.fn.concrete.apply(null, arguments);
}
/**
* A couple of utility functions for accessing the store outside of this closure, and for making things
* operate in a little more easy-to-test manner
*/
$.extend($.concrete, {
/**
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: namespaces,
/**
* Remove all concrete rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].concrete) delete $.fn[k] ; }
// Remove namespaces, and start over again
namespaces = $.concrete.namespaces = {};
},
WARN_LEVEL_NONE: 0,
WARN_LEVEL_IMPORTANT: 1,
WARN_LEVEL_BESTPRACTISE: 2,
/**
* Warning level. Set to a higher level to get warnings dumped to console.
*/
warningLevel: 0,
/** Utility to optionally display warning messages depending on level */
warn: function(message, level) {
if (level <= $.concrete.warningLevel && console && console.log) {
console.warn(message);
if (console.trace) console.trace();
}
}
});
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
var rulecount = 0;
var Rule = Base.extend({
init: function(selector, name) {
this.selector = selector;
this.specifity = selector.specifity();
this.important = 0;
this.name = name;
this.rulecount = rulecount++;
}
});
Rule.compare = function(a, b) {
var as = a.specifity, bs = b.specifity;
return (a.important - b.important) ||
(as[0] - bs[0]) ||
(as[1] - bs[1]) ||
(as[2] - bs[2]) ||
(a.rulecount - b.rulecount) ;
}
$.concrete.RuleList = function() {
var list = [];
list.addRule = function(selector, name){
var rule = Rule(selector, name);
list[list.length] = rule;
list.sort(Rule.compare);
return rule;
};
return list;
}
var handlers = [];
/**
* A Namespace holds all the information needed for adding concrete methods to a namespace (including the _null_ namespace)
*/
$.concrete.Namespace = Base.extend({
init: function(name){
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.concrete.warn('Concrete namespace '+name+' is not formatted as period seperated identifiers', $.concrete.WARN_LEVEL_BESTPRACTISE);
name = name || '__base';
this.name = name;
this.store = {};
namespaces[name] = this;
if (name == "__base") {
this.injectee = $.fn
this.$ = $;
}
else {
// We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into
var subfn = function(){}
this.injectee = subfn.prototype = new $();
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
var bound$ = this.$ = function(a) {
// Try the simple way first
var jq = $.fn.init.apply(new subfn(), arguments);
if (jq instanceof subfn) return jq;
// That didn't return a bound object, so now we need to copy it
var rv = new subfn();
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
while (i--) rv[i] = jq[i];
return rv;
}
this.injectee.pushStack = function(elems, name, selector){
var ret = bound$(elems);
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) ret.selector = this.selector + (this.selector ? " " : "") + selector;
else if ( name ) ret.selector = this.selector + "." + name + "(" + selector + ")";
// Return the newly-formed element set
return ret;
}
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
$.extend(this.$, $);
// We override concrete to inject the name of this namespace when defining blocks inside this namespace
var concrete_wrapper = this.injectee.concrete = function(spacename) {
var args = arguments;
if (!spacename || typeof spacename != 'string') { args = $.makeArray(args); args.unshift(name); }
else if (spacename.charAt(0) != '.') args[0] = name+'.'+spacename;
return $.fn.concrete.apply(this, args);
}
this.$.concrete = function() {
concrete_wrapper.apply(null, arguments);
}
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i], builder;
// Inject jQuery object method overrides
if (builder = handler.namespaceMethodOverrides) {
var overrides = builder(this);
for (var k in overrides) this.injectee[k] = overrides[k];
}
// Inject $.concrete function overrides
if (builder = handler.namespaceStaticOverrides) {
var overrides = builder(this);
for (var k in overrides) this.$.concrete[k] = overrides[k];
}
}
}
},
/**
* Returns a function that does selector matching against the function list for a function name
* Used by proxy for all calls, and by ctorProxy to handle _super calls
* @param {String} name - name of the function as passed in the construction object
* @param {String} funcprop - the property on the Rule object that gives the actual function to call
*/
one: function(name, funcprop) {
var namespace = this;
var funcs = this.store[name];
var one = function(el, args, i){
if (i === undefined) i = funcs.length;
while (i--) {
if (funcs[i].selector.matches(el)) {
var ret, tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { ret = funcs[i][funcprop].apply(namespace.$(el), args); }
finally { el.i = tmp_i; el.f = tmp_f; }
return ret;
}
}
}
return one;
},
/**
* A proxy is a function attached to a callable object (either the base jQuery.fn or a subspace object) which handles
* finding and calling the correct function for each member of the current jQuery context
* @param {String} name - name of the function as passed in the construction object
*/
build_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function() {
var rv, ctx = $(this);
var i = ctx.length;
while (i--) rv = one(ctx[i], arguments);
return rv;
};
return prxy;
},
bind_proxy: function(selector, name, func) {
var rulelist = this.store[name] || (this.store[name] = $.concrete.RuleList());
var rule = rulelist.addRule(selector, name); rule.func = func;
if (!this.injectee.hasOwnProperty(name)) {
this.injectee[name] = this.build_proxy(name);
this.injectee[name].concrete = true;
}
if (!this.injectee[name].concrete) {
$.concrete.warn('Warning: Concrete function '+name+' clashes with regular jQuery function - concrete function will not be callable directly on jQuery object', $.concrete.WARN_LEVEL_IMPORTANT);
}
},
add: function(selector, data) {
// For every item in the hash, try ever method handler, until one returns true
for (var k in data) {
var v = data[k];
for (var i = 0; i < handlers.length; i++) {
if (handlers[i].bind && handlers[i].bind.call(this, selector, k, v)) break;
}
}
},
has: function(ctx, name) {
var rulelist = this.store[name];
if (!rulelist) return false;
/* We go forward this time, since low specifity is likely to knock out a bunch of elements quickly */
for (var i = 0 ; i < rulelist.length; i++) {
ctx = ctx.not(rulelist[i].selector);
if (!ctx.length) return true;
}
return false;
}
});
/**
* A handler is some javascript code that adds support for some time of key / value pair passed in the hash to the Namespace add method.
* The default handlers provided (and included by default) are event, ctor and properties
*/
$.concrete.Namespace.addHandler = function(handler) {
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
handlers.splice(i, 0, handler);
}
$.concrete.Namespace.addHandler({
order: 50,
bind: function(selector, k, v){
if ($.isFunction(v)) {
this.bind_proxy(selector, k, v);
return true;
}
}
});
$.extend($.fn, {
/**
* Main concrete function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
*
*/
concrete: function(spacename) {
var i = 0;
var selector = this.selector ? $.selector(this.selector) : null;
/* By default we operator on the base namespace */
var namespace = namespaces.__base || $.concrete.Namespace();
/* If the first argument is a string, then it's the name of a namespace. Look it up */
if (typeof spacename == 'string') {
if (spacename.charAt('0') == '.') spacename = spacename.substr(1);
if (spacename) namespace = namespaces[spacename] || $.concrete.Namespace(spacename);
i=1;
}
/* All remaining arguments should either be using blocks or definition hashs */
while (i < arguments.length) {
var res = arguments[i++];
// If it's a function, call it - either it's a using block or it's a namespaced concrete definition
if ($.isFunction(res)) {
if (res.length != 1) $.concrete.warn('Function block inside concrete definition does not take $ argument properly', $.concrete.WARN_LEVEL_IMPORTANT);
res = res.call(namespace.$(this), namespace.$);
}
// If we have a concrete definition hash, inject it into namespace
if (res) {
if (selector) namespace.add(selector, res);
else $.concrete.warn('Concrete block given to concrete call without selector. Make sure you call $(selector).concrete when defining blocks', $.concrete.WARN_LEVEL_IMPORTANT);
}
}
/* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */
return namespace.$(this);
},
/**
* Calls the next most specific version of the current concrete method
*/
_super: function(){
var rv, i = this.length;
while (i--) {
var el = this[0];
rv = el.f(el, arguments, el.i);
}
return rv;
}
});
})(jQuery);
;
/* src/jquery.concrete.dommaybechanged.js */
(function($){
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
var runSoon = window.setTimeout;
/** The timer handle for the asyncronous matching call */
var check_id = null;
/** Fire the change event. Only fires on the document node, so bind to that */
var triggerEvent = function() {
$(document).triggerHandler('DOMMaybeChanged');
check_id = null;
}
$.extend($.concrete, {
/**
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
* the DOM manipulation that made them match. This is only really useful for during testing, since it's pretty slow
* (otherwise we'd make it the default).
*/
synchronous_mode: function() {
if (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; }
},
/**
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
* Called automatically on document.ready
*/
triggerMatching: function() {
matching();
}
});
function registerMutateFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function() {
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
}
function registerSetterGetterFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function(a, b) {
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
}
// Register core DOM manipulation methods
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ triggerEvent(); })
})(jQuery);;
/* src/jquery.concrete.events.js */
(function($) {
/* If we are any browser other than IE or Safari, we don't have to do anything special to handle
* onchange delegation */
$.support.bubblingChange = !($.browser.msie || $.browser.safari);
/* Return true if node b is the same as, or is a descendant of, node a */
if (document.compareDocumentPosition) {
var is_or_contains = function(a, b) {
return a && b && (a == b || !!(a.compareDocumentPosition(b) & 16));
}
}
else {
var is_or_contains = function(a, b) {
return a && b && (a == b || (a.contains ? a.contains(b) : true));
}
}
/* Add the methods to handle event binding to the Namespace class */
$.concrete.Namespace.addMethods({
build_event_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e, originalevent) {
e = originalevent || e;
var el = e.target;
while (el && el != document && !e.isPropagationStopped()) {
one(el, arguments);
el = el.parentNode;
}
};
return prxy;
},
build_mouseenterleave_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e) {
var el = e.target;
var rel = e.relatedTarget;
while (el && el != document && !e.isPropagationStopped()) {
/* We know el contained target. If it also contains relatedTarget then we didn't mouseenter / leave. What's more, every ancestor will also
contan el and rel, and so we can just stop bubbling */
if (is_or_contains(el, rel)) break;
one(el, arguments);
el = el.parentNode;
}
};
return prxy;
},
build_change_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e) {
var el = e.target;
// If this is a keydown event, only worry about the enter key, since browsers only trigger onchange on enter or focus loss
if (e.type === 'keydown' && e.keyCode !== 13) return;
// Make sure this is event is for an input type we're interested in
if (el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA' && el.tagName !== 'SELECT') return;
var $el = $(el), nowVal, oldVal = $el.data('changeVal');
// Detect changes on checkboxes & radiobuttons, which have different value logic. We don't use el.value, since el is part
// of a set, and we only want to raise onchange once for a single user action.
if (el.type == 'checkbox' || el.type == 'radio') {
if (!el.disabled && e.type === 'click') {
nowVal = el.checked;
// If radio, we get two changes - the activation, and the deactivation. We only want to fire one change though
if ((el.type === 'checkbox' || nowVal === true) && oldVal !== nowVal) e.type = 'change';
}
}
// Detect changes on other input types. In this case value is OK.
else {
nowVal = el.value;
if (oldVal !== undefined && oldVal !== nowVal) e.type = 'change';
}
// Save the current value for next time
if (nowVal !== undefined) $el.data('changeVal', nowVal);
// And if we decided that a change happened, do the actual triggering
if (e.type == 'change') {
while (el && el != document && !e.isPropagationStopped()) {
one(el, arguments);
el = el.parentNode;
}
}
};
return prxy;
},
bind_event: function(selector, name, func, event) {
var funcs = this.store[name] || (this.store[name] = $.concrete.RuleList()) ;
var proxies = funcs.proxies || (funcs.proxies = {});
var rule = funcs.addRule(selector, name); rule.func = func;
if (!proxies[name]) {
switch (name) {
case 'onmouseenter':
proxies[name] = this.build_mouseenterleave_proxy(name);
event = 'mouseover';
break;
case 'onmouseleave':
proxies[name] = this.build_mouseenterleave_proxy(name);
event = 'mouseout';
break;
case 'onchange':
if (!$.support.bubblingChange) {
proxies[name] = this.build_change_proxy(name);
event = 'click focusin focusout keydown';
}
break;
case 'onsubmit':
event = 'delegated_submit';
case 'onfocus':
case 'onblur':
$.concrete.warn('Event '+event+' not supported - using focusin / focusout instead', $.concrete.WARN_LEVEL_IMPORTANT);
}
if (!proxies[name]) proxies[name] = this.build_event_proxy(name);
$(document).bind(event, proxies[name]);
}
}
});
$.concrete.Namespace.addHandler({
order: 40,
bind: function(selector, k, v){
if ($.isFunction(v) && (match = k.match(/^on(.*)/))) {
event = match[1];
this.bind_event(selector, k, v, event);
return true;
}
}
});
// Find all forms and bind onsubmit to trigger on the document too. This is the only event that can't be grabbed via delegation.
var form_binding_cache = $([]); // A cache for already-handled form elements
var delegate_submit = function(e){ $(document).triggerHandler('delegated_submit', e); } // The function that handles the delegation
$(document).bind('DOMMaybeChanged', function(){
var forms = $('form');
// Only bind to forms we haven't processed yet
forms.not(form_binding_cache).bind('submit', delegate_submit);
// Then remember the current set of forms
form_binding_cache = forms;
});
})(jQuery);
;
/* src/jquery.concrete.ctors.js */
(function($) {
/* Add the methods to handle constructor & destructor binding to the Namespace class */
$.concrete.Namespace.addMethods({
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = $.concrete.RuleList()) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
if (ctors[i].selector.selector == selector.selector) {
rule = ctors[i]; break;
}
}
if (!rule) {
rule = ctors.addRule(selector, 'ctors');
}
rule[name] = func;
if (!ctors[name+'proxy']) {
var one = this.one('ctors', name);
var namespace = this;
var proxy = function(els, i, func) {
var j = els.length;
while (j--) {
var el = els[j];
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { el.i = tmp_i; el.f = tmp_f; }
}
}
ctors[name+'proxy'] = proxy;
}
}
});
$.concrete.Namespace.addHandler({
order: 30,
bind: function(selector, k, v) {
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
this.bind_condesc(selector, k, v);
return true;
}
}
});
/**
* Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate
*
* Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that
* a series of DOM manipulations will only trigger this once.
*
* The downside of this is that things like:
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
* won't work.
*/
$(document).bind('DOMMaybeChanged', function(){
// For every namespace
for (var k in $.concrete.namespaces) {
// That has constructors or destructors
var ctors = $.concrete.namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), match, add, rem;
// Stepping through each selector from most to least specific
var j = ctors.length;
while (j--) {
// Build some quick-acccess variables
var sel = ctors[j].selector.selector, ctor = ctors[j].onmatch; dtor = ctors[j].onunmatch;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (ctors[j].cache) {
// Find the ones that are extra this time
add = res.not(ctors[j].cache);
// Find the ones that are gone this time
rem = ctors[j].cache.not(res);
// And call the desctructor on them
if (rem.length && dtor) ctors.onunmatchproxy(rem, j, dtor);
}
// Call the constructor on the newly matched ones
if (add.length && ctor) ctors.onmatchproxy(add, j, ctor);
// Add these matched ones to the list tracking all elements matched so far
matched = matched.add(res);
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
ctors[j].cache = res;
}
}
}
})
})(jQuery);
;
/* src/jquery.concrete.properties.js */
(function($) {
var concrete_prepend = '__concrete!';
var getConcreteData = function(el, namespace, property) {
return el.data(concrete_prepend + namespace + '!' + property);
}
var setConcreteData = function(el, namespace, property, value) {
return el.data(concrete_prepend + namespace + '!' + property, value);
}
var getConcreteDataAsHash = function(el, namespace) {
var hash = {};
var id = jQuery.data(el[0]);
var matchstr = concrete_prepend + namespace + '!';
var matchlen = matchstr.length;
var cache = jQuery.cache[id];
for (var k in cache) {
if (k.substr(0,matchlen) == matchstr) hash[k.substr(matchlen)] = cache[k];
}
return hash;
}
var setConcreteDataFromHash = function(el, namespace, hash) {
for (var k in hash) setConcreteData(namespace, k, hash[k]);
}
var concreteData = function(el, namespace, args) {
switch (args.length) {
case 0:
return getConcreteDataAsHash(el, namespace);
case 1:
if (typeof args[0] == 'string') return getConcreteData(el, namespace, args[0]);
else return setConcreteDataFromHash(el, namespace, args[0]);
default:
return setConcreteData(el, namespace, args[0], args[1]);
}
}
$.extend($.fn, {
concreteData: function() {
return concreteData(this, '__base', arguments);
}
});
$.concrete.Namespace.addHandler({
order: 60,
bind: function(selector, k, v) {
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.concrete.warn('Concrete property '+k+' does not start with a capital letter', $.concrete.WARN_LEVEL_BESTPRACTISE);
var namespace = this;
g = function() { return this.concreteData(k) || v ; }
s = function(v){ return this.concreteData(k, v); }
g.pname = s.pname = k;
this.bind_proxy(selector, 'get'+k, g);
this.bind_proxy(selector, 'set'+k, s);
return true;
},
namespaceMethodOverrides: function(namespace){
return {
concreteData: function() {
return concreteData(this, namespace.name, arguments);
}
};
}
});
})(jQuery);
;

View File

@ -0,0 +1,1490 @@
/* jQuery.Concrete - Copyright 2009 Hamish Friedlander and SilverStripe. Version 0.9. */
/* vendor/jquery.selector/jquery.class.js */
/**
* Very basic Class utility. Based on base and jquery.class.
*
* Class definition: var Foo = Base.extend({ init: function(){ Constructor }; method_name: function(){ Method } });
*
* Inheritance: var Bar = Foo.extend({ method_name: function(){ this._super(); } });
*
* new-less Constructor: new Foo(arg) <-same as-> Foo(arg)
*/
var Base;
(function(){
var marker = {}, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Base = function(){};
Base.addMethod = function(name, func) {
var _super = this._super;
if (_super && fnTest.test(func)) {
this.prototype[name] = function(){
var tmp = this._super;
this._super = _super[name];
try {
var ret = func.apply(this, arguments);
}
finally {
this._super = tmp;
}
return ret;
}
}
else this.prototype[name] = func;
}
Base.addMethods = function(props) {
for (var name in props) {
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
else this.prototype[name] = props[name];
}
}
// Create a new Class that inherits from this class
Base.extend = function(props) {
// The dummy class constructor
var Kls = function() {
if (arguments[0] === marker) return;
if (this instanceof Kls) {
if (this.init) this.init.apply(this, arguments);
}
else {
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
}
}
// Add the common class variables and methods
Kls.constructor = Kls;
Kls.extend = Base.extend;
Kls.addMethod = Base.addMethod;
Kls.addMethods = Base.addMethods;
Kls._super = this.prototype;
// Attach the parent object to the inheritance chain
Kls.prototype = new this(marker);
// Copy the properties over onto the new prototype
Kls.addMethods(props);
return Kls;
};
})();;
/* vendor/jquery.selector/jquery.selector.js */
(function($){
var tokens = {
UNICODE: /\\[0-9a-f]{1,6}(?:\r\n|[ \n\r\t\f])?/,
ESCAPE: /(?:UNICODE)|\\[^\n\r\f0-9a-f]/,
NONASCII: /[^\x00-\x7F]/,
NMSTART: /[_a-z]|(?:NONASCII)|(?:ESCAPE)/,
NMCHAR: /[_a-z0-9-]|(?:NONASCII)|(?:ESCAPE)/,
IDENT: /-?(?:NMSTART)(?:NMCHAR)*/,
NL: /\n|\r\n|\r|\f/,
STRING: /(?:STRING1)|(?:STRING2)|(?:STRINGBARE)/,
STRING1: /"(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\"])*"/,
STRING2: /'(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\'])*'/,
STRINGBARE: /(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\]])*/,
FUNCTION: /(?:IDENT)\(\)/,
INTEGER: /[0-9]+/,
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
WITHOUTN: /([-+])?(INTEGER)/
}
var rx = {
not: /:not\(/,
not_end: /\)/,
tag: /((?:IDENT)|\*)/,
id: /#(IDENT)/,
cls: /\.(IDENT)/,
attr: /\[\s*(IDENT)\s*(?:([^=]?=)\s*(STRING)\s*)?\]/,
pseudo_el: /(?::(first-line|first-letter|before|after))|(?:::((?:FUNCTION)|(?:IDENT)))/,
pseudo_cls_nth: /:nth-child\(\s*(?:(?:WITHN)|(?:WITHOUTN)|(odd|even))\s*\)/,
pseudo_cls: /:(IDENT)/,
comb: /\s*(\+|~|>)\s*|\s+/,
comma: /\s*,\s*/,
important: /\s+!important\s*$/
}
/* Replace placeholders with actual regex, and mark all as case insensitive */
var token = /[A-Z][A-Z0-9]+/;
for (var k in rx) {
var src = rx[k].source;
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
rx[k] = new RegExp(src, 'gi');
}
/**
* A string that matches itself against regexii, and keeps track of how much of itself has been matched
*/
var ConsumableString = Base.extend({
init: function(str) {
this.str = str;
this.pos = 0;
},
match: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) {
this.pos = rx.lastIndex ? rx.lastIndex : this.str.length ;
return m;
}
return null;
},
peek: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) return m;
return null;
},
showpos: function() {
return this.str.slice(0,this.pos)+'<HERE>' + this.str.slice(this.pos);
},
done: function() {
return this.pos == this.str.length;
}
})
/* A base class that all Selectors inherit off */
var SelectorBase = Base.extend({});
/**
* A class representing a Simple Selector, as per the CSS3 selector spec
*/
var SimpleSelector = SelectorBase.extend({
init: function() {
this.tag = null;
this.id = null;
this.classes = [];
this.attrs = [];
this.nots = [];
this.pseudo_classes = [];
this.pseudo_els = [];
},
parse: function(selector) {
var m;
/* Pull out the initial tag first, if there is one */
if (m = selector.match(rx.tag)) this.tag = m[1];
/* Then for each selection type, try and find a match */
do {
if (m = selector.match(rx.not)) {
this.nots[this.nots.length] = SelectorsGroup().parse(selector)
if (!(m = selector.match(rx.not_end))) {
throw 'Invalid :not term in selector';
}
}
else if (m = selector.match(rx.id)) this.id = m[1];
else if (m = selector.match(rx.cls)) this.classes[this.classes.length] = m[1];
else if (m = selector.match(rx.attr)) this.attrs[this.attrs.length] = [ m[1], m[2], m[3] ];
else if (m = selector.match(rx.pseudo_el)) this.pseudo_els[this.pseudo_els.length] = m[1] || m[2];
else if (m = selector.match(rx.pseudo_cls_nth)) {
if (m[3]) {
var a = parseInt((m[1]||'')+(m[2]||'1'));
var b = parseInt((m[4]||'')+(m[5]||'0'));
}
else {
var a = m[8] ? 2 : 0;
var b = m[8] ? (4-m[8].length) : parseInt((m[6]||'')+m[7]);
}
this.pseudo_classes[this.pseudo_classes.length] = ['nth-child', [a, b]];
}
else if (m = selector.match(rx.pseudo_cls)) this.pseudo_classes[this.pseudo_classes.length] = [m[1]];
} while(m && !selector.done());
return this;
}
})
/**
* A class representing a Selector, as per the CSS3 selector spec
*/
var Selector = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = SimpleSelector().parse(cons);
while (!cons.done() && !cons.peek(rx.comma) && (m = cons.match(rx.comb))) {
this.parts[this.parts.length] = m[1] || ' ';
this.parts[this.parts.length] = SimpleSelector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
/**
* A class representing a sequence of selectors, as per the CSS3 selector spec
*/
var SelectorsGroup = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = Selector().parse(cons);
while (!cons.done() && (m = cons.match(rx.comma))) {
this.parts[this.parts.length] = Selector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
$.selector = function(s){
var cons = ConsumableString(s);
var res = SelectorsGroup().parse(cons);
res.selector = s;
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
else return res;
}
$.selector.SelectorBase = SelectorBase;
$.selector.SimpleSelector = SimpleSelector;
$.selector.Selector = Selector;
$.selector.SelectorsGroup = SelectorsGroup;
})(jQuery)
;
/* vendor/jquery.selector/jquery.selector.specifity.js */
(function($) {
$.selector.SimpleSelector.addMethod('specifity', function() {
if (this.spec) return this.spec;
var spec = [
this.id ? 1 : 0,
this.classes.length + this.attrs.length + this.pseudo_classes.length,
((this.tag && this.tag != '*') ? 1 : 0) + this.pseudo_els.length
];
$.each(this.nots, function(i,not){
var ns = not.specifity(); spec[0] += ns[0]; spec[1] += ns[1]; spec[2] += ns[2];
});
return this.spec = spec;
})
$.selector.Selector.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
if (i%2) return;
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
})
$.selector.SelectorsGroup.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
})
})(jQuery);
;
/* vendor/jquery.selector/jquery.selector.matches.js */
/*
This attempts to do the opposite of Sizzle.
Sizzle is good for finding elements for a selector, but not so good for telling if an individual element matches a selector
*/
(function($) {
/**** CAPABILITY TESTS ****/
var div = document.createElement('div');
div.innerHTML = '<form id="test"><input name="id" type="text"/></form>';
// In IE 6-7, getAttribute often does the wrong thing (returns similar to el.attr), so we need to use getAttributeNode on that browser
var getAttributeDodgy = div.firstChild.getAttribute('id') !== 'test';
// Does browser support Element.firstElementChild, Element.previousElementSibling, etc.
var hasElementTraversal = div.firstElementChild && div.firstElementChild.tagName == 'FORM';
// Does browser support Element.children
var hasChildren = div.children && div.children[0].tagName == 'FORM';
var FUNC_IN = /^\s*function\s*\([^)]*\)\s*{/;
var FUNC_OUT = /}\s*$/;
var funcToString = function(f) {
return (''+f).replace(FUNC_IN,'').replace(FUNC_OUT,'');
}
// Can we use Function#toString ?
try {
var testFunc = function(){ return 'good' };
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
}
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
/**** INTRO ****/
var GOOD = /GOOD/g;
var BAD = /BAD/g;
var STARTS_WITH_QUOTES = /^['"]/g;
var join = function(js) {
return js.join('\n');
}
var join_complex = function(js) {
code = new String(js.join('\n')); // String objects can have properties set. strings can't
code.complex = true;
return code;
}
/**** ATTRIBUTE ACCESSORS ****/
var getAttr;
// Good browsers
if (!getAttributeDodgy) {
getAttr = function(attr){ return 'var _'+attr+' = el.getAttribute("'+attr+'");' ; }
}
// IE 6, 7
else {
// On IE 6 + 7, getAttribute still has to be called with DOM property mirror name, not attribute name. Map attributes to those names
var getAttrIEMap = { 'class': 'className', 'for': 'htmlFor' };
getAttr = function(attr) {
var ieattr = getAttrIEMap[attr] || attr;
return 'var _'+attr+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
}
}
/**** ATTRIBUTE COMPARITORS ****/
var attrchecks = {
'-': '!_K',
'=': '_K != "V"',
'!=': '_K == "V"',
'~=': '__K.indexOf(" V ") == -1',
'^=': '!_K || _K.indexOf("V") != 0',
'*=': '!_K || _K.indexOf("V") == -1',
'$=': '!_K || _K.substr(_K.length-"V".length) != "V"'
}
/**** STATE TRACKER ****/
var State = $.selector.State = Base.extend({
init: function(){
this.reset();
},
reset: function() {
this.attrs = {}; this.wsattrs = {};
},
prev: function(){
this.reset();
if (hasElementTraversal) return 'el = el.previousElementSibling';
return 'while((el = el.previousSibling) && el.nodeType != 1) {}';
},
next: function() {
this.reset();
if (hasElementTraversal) return 'el = el.nextElementSibling';
return 'while((el = el.nextSibling) && el.nodeType != 1) {}';
},
prevLoop: function(body){
this.reset();
if (hasElementTraversal) return join([ 'while(el = el.previousElementSibling){', body]);
return join([
'while(el = el.previousSibling){',
'if (el.nodeType != 1) continue;',
body,
]);
},
parent: function() {
this.reset();
return 'el = el.parentNode;';
},
parentLoop: function(body) {
this.reset();
return join([
'while((el = el.parentNode) && el.nodeType == 1){',
body,
'}'
]);
},
uses_attr: function(attr) {
if (this.attrs[attr]) return;
this.attrs[attr] = true;
return getAttr(attr);
},
uses_wsattr: function(attr) {
if (this.wsattrs[attr]) return;
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var __'+attr+' = " "+_'+attr+'+" ";']);
},
save: function(lbl) {
return 'var el'+lbl+' = el;';
},
restore: function(lbl) {
this.reset();
return 'el = el'+lbl+';';
}
});
/**** PSEUDO-CLASS DETAILS ****/
var pseudoclschecks = {
'first-child': join([
'var cel = el;',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }',
]),
'last-child': join([
'var cel = el;',
'while(cel = cel.nextSibling){ if (cel.nodeType === 1) BAD; }'
]),
'nth-child': function(a,b) {
var get_i = join([
'var i = 1, cel = el;',
'while(cel = cel.previousSibling){',
'if (cel.nodeType === 1) i++;',
'}',
]);
if (a == 0) return join([
get_i,
'if (i- '+b+' != 0) BAD;'
]);
else if (b == 0 && a >= 0) return join([
get_i,
'if (i%'+a+' != 0 || i/'+a+' < 0) BAD;'
]);
else if (b == 0 && a < 0) return join([
'BAD;'
]);
else return join([
get_i,
'if ((i- '+b+')%'+a+' != 0 || (i- '+b+')/'+a+' < 0) BAD;'
]);
}
};
// Needs to refence contents of object, so must be injected after definition
pseudoclschecks['only-child'] = join([
pseudoclschecks['first-child'],
pseudoclschecks['last-child']
]);
/**** SimpleSelector ****/
$.selector.SimpleSelector.addMethod('compile', function(el) {
var js = [];
/* Check against element name */
if (this.tag && this.tag != '*') {
js[js.length] = 'if (el.tagName != "'+this.tag.toUpperCase()+'") BAD;';
}
/* Check against ID */
if (this.id) {
js[js.length] = el.uses_attr('id');
js[js.length] = 'if (_id !== "'+this.id+'") BAD;';
}
/* Build className checking variable */
if (this.classes.length) {
js[js.length] = el.uses_wsattr('class');
/* Check against class names */
$.each(this.classes, function(i, cls){
js[js.length] = 'if (__class.indexOf(" '+cls+' ") == -1) BAD;';
})
}
/* Check against attributes */
$.each(this.attrs, function(i, attr){
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
var check = attrchecks[ attr[1] || '-' ];
check = check.replace( /K/g, attr[0]).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
js[js.length] = 'if ('+check+') BAD;';
});
/* Check against nots */
$.each(this.nots, function(i, not){
var lbl = ++lbl_id;
var func = join([
'l'+lbl+':{',
not.compile(el).replace(BAD, 'break l'+lbl).replace(GOOD, 'BAD'),
'}'
]);
if (!(not instanceof $.selector.SimpleSelector)) func = join([
el.save(lbl),
func,
el.restore(lbl)
])
js[js.length] = func;
});
/* Check against pseudo-classes */
$.each(this.pseudo_classes, function(i, pscls){
var check = pseudoclschecks[pscls[0]];
if (check) {
js[js.length] = ( typeof check == 'function' ? check.apply(this, pscls[1]) : check );
}
else if (check = $.find.selectors.filters[pscls[0]]) {
if (funcToString) {
js[js.length] = funcToString(check).replace(/elem/g,'el').replace(/return([^;]+);/,'if (!($1)) BAD;');
}
else {
js[js.length] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;'
}
}
});
js[js.length] = 'GOOD';
/* Pass */
return join(js);
});
var lbl_id = 0;
/** Turns an compiled fragment into the first part of a combination */
function as_subexpr(f) {
if (f.complex)
return join([
'l'+(++lbl_id)+':{',
f.replace(GOOD, 'break l'+lbl_id),
'}',
]);
else
return f.replace(GOOD, '');
}
var combines = {
' ': function(el, f1, f2) {
return join_complex([
f2,
'while(true){',
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el).replace(BAD, 'continue'),
'}'
]);
},
'>': function(el, f1, f2) {
return join([
f2,
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el)
]);
},
'~': function(el, f1, f2) {
return join_complex([
f2,
el.prevLoop(),
f1.compile(el).replace(BAD, 'continue'),
'}',
'BAD;'
]);
},
'+': function(el, f1, f2) {
return join([
f2,
el.prev(),
'if (!el) BAD;',
f1.compile(el)
]);
}
};
$.selector.Selector.addMethod('compile', function(el) {
l = this.parts.length;
expr = this.parts[--l].compile(el);
while (l) {
combinator = this.parts[--l];
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
}
return expr;
});
$.selector.SelectorsGroup.addMethod('compile', function(el) {
var expr = [], lbl = ++lbl_id;
for (var i=0; i < this.parts.length; i++) {
expr[expr.length] = join([
i == 0 ? el.save(lbl) : el.restore(lbl),
'l'+lbl+'_'+i+':{',
this.parts[i].compile(el).replace(BAD, 'break l'+lbl+'_'+i),
'}'
]);
}
expr[expr.length] = 'BAD;';
return join(expr);
});
$.selector.SelectorBase.addMethod('matches', function(el){
this.matches = new Function('el', join([
'if (!el) return false;',
this.compile(new State()).replace(BAD, 'return false').replace(GOOD, 'return true')
]));
return this.matches(el);
});
})(jQuery);
;
/* src/jquery.focusinout.js */
(function($){
/**
* Add focusin and focusout support to bind and live for browers other than IE. Designed to be usable in a delegated fashion (like $.live)
* Copyright (c) 2007 Jörn Zaefferer
*/
$.support.focusInOut = !!($.browser.msie);
if (!$.support.focusInOut) {
// Emulate focusin and focusout by binding focus and blur in capturing mode
$.each({focus: 'focusin', blur: 'focusout'}, function(original, fix){
$.event.special[fix] = {
setup: function(){
if (!this.addEventListener) return false;
this.addEventListener(original, $.event.special[fix].handler, true);
},
teardown: function(){
if (!this.removeEventListener) return false;
this.removeEventListener(original, $.event.special[fix].handler, true);
},
handler: function(e){
arguments[0] = $.event.fix(e);
arguments[0].type = fix;
return $.event.handle.apply(this, arguments);
}
};
});
}
(function(){
//IE has some trouble with focusout with select and keyboard navigation
var activeFocus = null;
$(document)
.bind('focusin', function(e){
var target = e.realTarget || e.target;
if (activeFocus && activeFocus !== target) {
e.type = 'focusout';
$(activeFocus).trigger(e);
e.type = 'focusin';
e.target = target;
}
activeFocus = target;
})
.bind('focusout', function(e){
activeFocus = null;
});
})();
})(jQuery);;
/* src/jquery.concrete.js */
var console;
(function($) {
var namespaces = {};
$.concrete = function() {
$.fn.concrete.apply(null, arguments);
}
/**
* A couple of utility functions for accessing the store outside of this closure, and for making things
* operate in a little more easy-to-test manner
*/
$.extend($.concrete, {
/**
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: namespaces,
/**
* Remove all concrete rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].concrete) delete $.fn[k] ; }
// Remove namespaces, and start over again
namespaces = $.concrete.namespaces = {};
},
WARN_LEVEL_NONE: 0,
WARN_LEVEL_IMPORTANT: 1,
WARN_LEVEL_BESTPRACTISE: 2,
/**
* Warning level. Set to a higher level to get warnings dumped to console.
*/
warningLevel: 0,
/** Utility to optionally display warning messages depending on level */
warn: function(message, level) {
if (level <= $.concrete.warningLevel && console && console.log) {
console.warn(message);
if (console.trace) console.trace();
}
}
});
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
var rulecount = 0;
var Rule = Base.extend({
init: function(selector, name) {
this.selector = selector;
this.specifity = selector.specifity();
this.important = 0;
this.name = name;
this.rulecount = rulecount++;
}
});
Rule.compare = function(a, b) {
var as = a.specifity, bs = b.specifity;
return (a.important - b.important) ||
(as[0] - bs[0]) ||
(as[1] - bs[1]) ||
(as[2] - bs[2]) ||
(a.rulecount - b.rulecount) ;
}
$.concrete.RuleList = function() {
var list = [];
list.addRule = function(selector, name){
var rule = Rule(selector, name);
list[list.length] = rule;
list.sort(Rule.compare);
return rule;
};
return list;
}
var handlers = [];
/**
* A Namespace holds all the information needed for adding concrete methods to a namespace (including the _null_ namespace)
*/
$.concrete.Namespace = Base.extend({
init: function(name){
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.concrete.warn('Concrete namespace '+name+' is not formatted as period seperated identifiers', $.concrete.WARN_LEVEL_BESTPRACTISE);
name = name || '__base';
this.name = name;
this.store = {};
namespaces[name] = this;
if (name == "__base") {
this.injectee = $.fn
this.$ = $;
}
else {
// We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into
var subfn = function(){}
this.injectee = subfn.prototype = new $();
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
var bound$ = this.$ = function(a) {
// Try the simple way first
var jq = $.fn.init.apply(new subfn(), arguments);
if (jq instanceof subfn) return jq;
// That didn't return a bound object, so now we need to copy it
var rv = new subfn();
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
while (i--) rv[i] = jq[i];
return rv;
}
this.injectee.pushStack = function(elems, name, selector){
var ret = bound$(elems);
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) ret.selector = this.selector + (this.selector ? " " : "") + selector;
else if ( name ) ret.selector = this.selector + "." + name + "(" + selector + ")";
// Return the newly-formed element set
return ret;
}
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
$.extend(this.$, $);
// We override concrete to inject the name of this namespace when defining blocks inside this namespace
var concrete_wrapper = this.injectee.concrete = function(spacename) {
var args = arguments;
if (!spacename || typeof spacename != 'string') { args = $.makeArray(args); args.unshift(name); }
else if (spacename.charAt(0) != '.') args[0] = name+'.'+spacename;
return $.fn.concrete.apply(this, args);
}
this.$.concrete = function() {
concrete_wrapper.apply(null, arguments);
}
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i], builder;
// Inject jQuery object method overrides
if (builder = handler.namespaceMethodOverrides) {
var overrides = builder(this);
for (var k in overrides) this.injectee[k] = overrides[k];
}
// Inject $.concrete function overrides
if (builder = handler.namespaceStaticOverrides) {
var overrides = builder(this);
for (var k in overrides) this.$.concrete[k] = overrides[k];
}
}
}
},
/**
* Returns a function that does selector matching against the function list for a function name
* Used by proxy for all calls, and by ctorProxy to handle _super calls
* @param {String} name - name of the function as passed in the construction object
* @param {String} funcprop - the property on the Rule object that gives the actual function to call
*/
one: function(name, funcprop) {
var namespace = this;
var funcs = this.store[name];
var one = function(el, args, i){
if (i === undefined) i = funcs.length;
while (i--) {
if (funcs[i].selector.matches(el)) {
var ret, tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { ret = funcs[i][funcprop].apply(namespace.$(el), args); }
finally { el.i = tmp_i; el.f = tmp_f; }
return ret;
}
}
}
return one;
},
/**
* A proxy is a function attached to a callable object (either the base jQuery.fn or a subspace object) which handles
* finding and calling the correct function for each member of the current jQuery context
* @param {String} name - name of the function as passed in the construction object
*/
build_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function() {
var rv, ctx = $(this);
var i = ctx.length;
while (i--) rv = one(ctx[i], arguments);
return rv;
};
return prxy;
},
bind_proxy: function(selector, name, func) {
var rulelist = this.store[name] || (this.store[name] = $.concrete.RuleList());
var rule = rulelist.addRule(selector, name); rule.func = func;
if (!this.injectee.hasOwnProperty(name)) {
this.injectee[name] = this.build_proxy(name);
this.injectee[name].concrete = true;
}
if (!this.injectee[name].concrete) {
$.concrete.warn('Warning: Concrete function '+name+' clashes with regular jQuery function - concrete function will not be callable directly on jQuery object', $.concrete.WARN_LEVEL_IMPORTANT);
}
},
add: function(selector, data) {
// For every item in the hash, try ever method handler, until one returns true
for (var k in data) {
var v = data[k];
for (var i = 0; i < handlers.length; i++) {
if (handlers[i].bind && handlers[i].bind.call(this, selector, k, v)) break;
}
}
},
has: function(ctx, name) {
var rulelist = this.store[name];
if (!rulelist) return false;
/* We go forward this time, since low specifity is likely to knock out a bunch of elements quickly */
for (var i = 0 ; i < rulelist.length; i++) {
ctx = ctx.not(rulelist[i].selector);
if (!ctx.length) return true;
}
return false;
}
});
/**
* A handler is some javascript code that adds support for some time of key / value pair passed in the hash to the Namespace add method.
* The default handlers provided (and included by default) are event, ctor and properties
*/
$.concrete.Namespace.addHandler = function(handler) {
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
handlers.splice(i, 0, handler);
}
$.concrete.Namespace.addHandler({
order: 50,
bind: function(selector, k, v){
if ($.isFunction(v)) {
this.bind_proxy(selector, k, v);
return true;
}
}
});
$.extend($.fn, {
/**
* Main concrete function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
*
*/
concrete: function(spacename) {
var i = 0;
var selector = this.selector ? $.selector(this.selector) : null;
/* By default we operator on the base namespace */
var namespace = namespaces.__base || $.concrete.Namespace();
/* If the first argument is a string, then it's the name of a namespace. Look it up */
if (typeof spacename == 'string') {
if (spacename.charAt('0') == '.') spacename = spacename.substr(1);
if (spacename) namespace = namespaces[spacename] || $.concrete.Namespace(spacename);
i=1;
}
/* All remaining arguments should either be using blocks or definition hashs */
while (i < arguments.length) {
var res = arguments[i++];
// If it's a function, call it - either it's a using block or it's a namespaced concrete definition
if ($.isFunction(res)) {
if (res.length != 1) $.concrete.warn('Function block inside concrete definition does not take $ argument properly', $.concrete.WARN_LEVEL_IMPORTANT);
res = res.call(namespace.$(this), namespace.$);
}
// If we have a concrete definition hash, inject it into namespace
if (res) {
if (selector) namespace.add(selector, res);
else $.concrete.warn('Concrete block given to concrete call without selector. Make sure you call $(selector).concrete when defining blocks', $.concrete.WARN_LEVEL_IMPORTANT);
}
}
/* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */
return namespace.$(this);
},
/**
* Calls the next most specific version of the current concrete method
*/
_super: function(){
var rv, i = this.length;
while (i--) {
var el = this[0];
rv = el.f(el, arguments, el.i);
}
return rv;
}
});
})(jQuery);
;
/* src/jquery.concrete.dommaybechanged.js */
(function($){
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
var runSoon = window.setTimeout;
/** The timer handle for the asyncronous matching call */
var check_id = null;
/** Fire the change event. Only fires on the document node, so bind to that */
var triggerEvent = function() {
$(document).triggerHandler('DOMMaybeChanged');
check_id = null;
}
$.extend($.concrete, {
/**
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
* the DOM manipulation that made them match. This is only really useful for during testing, since it's pretty slow
* (otherwise we'd make it the default).
*/
synchronous_mode: function() {
if (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; }
},
/**
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
* Called automatically on document.ready
*/
triggerMatching: function() {
matching();
}
});
function registerMutateFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function() {
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
}
function registerSetterGetterFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function(a, b) {
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
}
// Register core DOM manipulation methods
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ triggerEvent(); })
})(jQuery);;
/* src/jquery.concrete.events.js */
(function($) {
/* If we are any browser other than IE or Safari, we don't have to do anything special to handle
* onchange delegation */
$.support.bubblingChange = !($.browser.msie || $.browser.safari);
/* Return true if node b is the same as, or is a descendant of, node a */
if (document.compareDocumentPosition) {
var is_or_contains = function(a, b) {
return a && b && (a == b || !!(a.compareDocumentPosition(b) & 16));
}
}
else {
var is_or_contains = function(a, b) {
return a && b && (a == b || (a.contains ? a.contains(b) : true));
}
}
/* Add the methods to handle event binding to the Namespace class */
$.concrete.Namespace.addMethods({
build_event_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e, originalevent) {
e = originalevent || e;
var el = e.target;
while (el && el != document && !e.isPropagationStopped()) {
one(el, arguments);
el = el.parentNode;
}
};
return prxy;
},
build_mouseenterleave_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e) {
var el = e.target;
var rel = e.relatedTarget;
while (el && el != document && !e.isPropagationStopped()) {
/* We know el contained target. If it also contains relatedTarget then we didn't mouseenter / leave. What's more, every ancestor will also
contan el and rel, and so we can just stop bubbling */
if (is_or_contains(el, rel)) break;
one(el, arguments);
el = el.parentNode;
}
};
return prxy;
},
build_change_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e) {
var el = e.target;
// If this is a keydown event, only worry about the enter key, since browsers only trigger onchange on enter or focus loss
if (e.type === 'keydown' && e.keyCode !== 13) return;
// Make sure this is event is for an input type we're interested in
if (el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA' && el.tagName !== 'SELECT') return;
var $el = $(el), nowVal, oldVal = $el.data('changeVal');
// Detect changes on checkboxes & radiobuttons, which have different value logic. We don't use el.value, since el is part
// of a set, and we only want to raise onchange once for a single user action.
if (el.type == 'checkbox' || el.type == 'radio') {
if (!el.disabled && e.type === 'click') {
nowVal = el.checked;
// If radio, we get two changes - the activation, and the deactivation. We only want to fire one change though
if ((el.type === 'checkbox' || nowVal === true) && oldVal !== nowVal) e.type = 'change';
}
}
// Detect changes on other input types. In this case value is OK.
else {
nowVal = el.value;
if (oldVal !== undefined && oldVal !== nowVal) e.type = 'change';
}
// Save the current value for next time
if (nowVal !== undefined) $el.data('changeVal', nowVal);
// And if we decided that a change happened, do the actual triggering
if (e.type == 'change') {
while (el && el != document && !e.isPropagationStopped()) {
one(el, arguments);
el = el.parentNode;
}
}
};
return prxy;
},
bind_event: function(selector, name, func, event) {
var funcs = this.store[name] || (this.store[name] = $.concrete.RuleList()) ;
var proxies = funcs.proxies || (funcs.proxies = {});
var rule = funcs.addRule(selector, name); rule.func = func;
if (!proxies[name]) {
switch (name) {
case 'onmouseenter':
proxies[name] = this.build_mouseenterleave_proxy(name);
event = 'mouseover';
break;
case 'onmouseleave':
proxies[name] = this.build_mouseenterleave_proxy(name);
event = 'mouseout';
break;
case 'onchange':
if (!$.support.bubblingChange) {
proxies[name] = this.build_change_proxy(name);
event = 'click focusin focusout keydown';
}
break;
case 'onsubmit':
event = 'delegated_submit';
case 'onfocus':
case 'onblur':
$.concrete.warn('Event '+event+' not supported - using focusin / focusout instead', $.concrete.WARN_LEVEL_IMPORTANT);
}
if (!proxies[name]) proxies[name] = this.build_event_proxy(name);
$(document).bind(event, proxies[name]);
}
}
});
$.concrete.Namespace.addHandler({
order: 40,
bind: function(selector, k, v){
if ($.isFunction(v) && (match = k.match(/^on(.*)/))) {
event = match[1];
this.bind_event(selector, k, v, event);
return true;
}
}
});
// Find all forms and bind onsubmit to trigger on the document too. This is the only event that can't be grabbed via delegation.
var form_binding_cache = $([]); // A cache for already-handled form elements
var delegate_submit = function(e){ $(document).triggerHandler('delegated_submit', e); } // The function that handles the delegation
$(document).bind('DOMMaybeChanged', function(){
var forms = $('form');
// Only bind to forms we haven't processed yet
forms.not(form_binding_cache).bind('submit', delegate_submit);
// Then remember the current set of forms
form_binding_cache = forms;
});
})(jQuery);
;
/* src/jquery.concrete.ctors.js */
(function($) {
/* Add the methods to handle constructor & destructor binding to the Namespace class */
$.concrete.Namespace.addMethods({
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = $.concrete.RuleList()) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
if (ctors[i].selector.selector == selector.selector) {
rule = ctors[i]; break;
}
}
if (!rule) {
rule = ctors.addRule(selector, 'ctors');
}
rule[name] = func;
if (!ctors[name+'proxy']) {
var one = this.one('ctors', name);
var namespace = this;
var proxy = function(els, i, func) {
var j = els.length;
while (j--) {
var el = els[j];
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { el.i = tmp_i; el.f = tmp_f; }
}
}
ctors[name+'proxy'] = proxy;
}
}
});
$.concrete.Namespace.addHandler({
order: 30,
bind: function(selector, k, v) {
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
this.bind_condesc(selector, k, v);
return true;
}
}
});
/**
* Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate
*
* Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that
* a series of DOM manipulations will only trigger this once.
*
* The downside of this is that things like:
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
* won't work.
*/
$(document).bind('DOMMaybeChanged', function(){
// For every namespace
for (var k in $.concrete.namespaces) {
// That has constructors or destructors
var ctors = $.concrete.namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), match, add, rem;
// Stepping through each selector from most to least specific
var j = ctors.length;
while (j--) {
// Build some quick-acccess variables
var sel = ctors[j].selector.selector, ctor = ctors[j].onmatch; dtor = ctors[j].onunmatch;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (ctors[j].cache) {
// Find the ones that are extra this time
add = res.not(ctors[j].cache);
// Find the ones that are gone this time
rem = ctors[j].cache.not(res);
// And call the desctructor on them
if (rem.length && dtor) ctors.onunmatchproxy(rem, j, dtor);
}
// Call the constructor on the newly matched ones
if (add.length && ctor) ctors.onmatchproxy(add, j, ctor);
// Add these matched ones to the list tracking all elements matched so far
matched = matched.add(res);
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
ctors[j].cache = res;
}
}
}
})
})(jQuery);
;
/* src/jquery.concrete.properties.js */
(function($) {
var concrete_prepend = '__concrete!';
var getConcreteData = function(el, namespace, property) {
return el.data(concrete_prepend + namespace + '!' + property);
}
var setConcreteData = function(el, namespace, property, value) {
return el.data(concrete_prepend + namespace + '!' + property, value);
}
var getConcreteDataAsHash = function(el, namespace) {
var hash = {};
var id = jQuery.data(el[0]);
var matchstr = concrete_prepend + namespace + '!';
var matchlen = matchstr.length;
var cache = jQuery.cache[id];
for (var k in cache) {
if (k.substr(0,matchlen) == matchstr) hash[k.substr(matchlen)] = cache[k];
}
return hash;
}
var setConcreteDataFromHash = function(el, namespace, hash) {
for (var k in hash) setConcreteData(namespace, k, hash[k]);
}
var concreteData = function(el, namespace, args) {
switch (args.length) {
case 0:
return getConcreteDataAsHash(el, namespace);
case 1:
if (typeof args[0] == 'string') return getConcreteData(el, namespace, args[0]);
else return setConcreteDataFromHash(el, namespace, args[0]);
default:
return setConcreteData(el, namespace, args[0], args[1]);
}
}
$.extend($.fn, {
concreteData: function() {
return concreteData(this, '__base', arguments);
}
});
$.concrete.Namespace.addHandler({
order: 60,
bind: function(selector, k, v) {
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.concrete.warn('Concrete property '+k+' does not start with a capital letter', $.concrete.WARN_LEVEL_BESTPRACTISE);
var namespace = this;
g = function() { return this.concreteData(k) || v ; }
s = function(v){ return this.concreteData(k, v); }
g.pname = s.pname = k;
this.bind_proxy(selector, 'get'+k, g);
this.bind_proxy(selector, 'set'+k, s);
return true;
},
namespaceMethodOverrides: function(namespace){
return {
concreteData: function() {
return concreteData(this, namespace.name, arguments);
}
};
}
});
})(jQuery);
;

View File

@ -0,0 +1,30 @@
<html>
<head>
<!-- spec-dist is the same as spec, but runs on the distribution version of the library, to make sure no problems were introduced during building -->
<link type="text/css" rel="stylesheet" href="../vendor/jspec/lib/jspec.css" />
<script src="../vendor/jquery-1.3.2.js"></script>
<script src="../vendor/jspec/lib/jspec.js"></script>
<script src="../vendor/jspec/lib/jspec.jquery.js"></script>
<script src="../dist/jquery.concrete-latest.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.concrete.basics.js')
.exec('spec.concrete.namespaces.js')
.exec('spec.concrete.super.js')
.exec('spec.concrete.events.js')
.exec('spec.concrete.ctors.js')
.exec('spec.concrete.properties.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"><div class="loading"></div></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -0,0 +1,72 @@
describe 'Concrete'
describe 'Basics'
before
$.concrete.warningLevel = $.concrete.WARN_LEVEL_BESTPRACTISE;
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>')
end
it 'can attach and call a base function'
$('#a').concrete({
foo: function(){return this.attr('id');}
});
$('.a').foo().should.equal 'a'
end
it 'can attach and call several base functions'
$('#a').concrete({
foo: function(){return 'foo_' + this.attr('id');},
bar: function(){return 'bar_' + this.attr('id');}
});
$('.a').foo().should.equal 'foo_a'
$('.a').bar().should.equal 'bar_a'
end
it 'can attach and call a namespaced function'
$('#a').concrete('bar', function($){return{
foo: function(){return this.attr('id');}
}});
$('.a').concrete('bar').foo().should.equal 'a'
end
it 'can attach and call a nested namespaced function'
$('#a').concrete('qux.baz.bar', function($){return{
foo: function(){return this.attr('id');}
}});
$('.a').concrete('qux.baz.bar').foo().should.equal 'a'
end
it 'can call two functions on two elements'
var res = []
$('#a').concrete({
foo: function(){res.push(this.attr('id'));},
})
$('#b.c').concrete({
foo: function(){res.push(this.attr('id'));},
})
$('#dom_test div').foo();
res.should.eql ['b', 'a']
end
it 'can call two namespaced functions on two elements'
var res = []
$('#a').concrete('bar', function($){return{
foo: function(){res.push(this.attr('id'));},
}})
$('#b.c').concrete('bar', function($){return{
foo: function(){res.push(this.attr('id'));},
}})
$('#dom_test div').concrete('bar').foo();
res.should.eql ['b', 'a']
end
end
end

View File

@ -0,0 +1,68 @@
describe 'Concrete'
describe 'Ctors'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.concrete.synchronous_mode();
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div>')
end
it 'calls onmatch when new element created'
var a = false;
$('#b').concrete({onmatch: function(){a = true;} });
a.should.be_false
$('#a').after('<div id="b"></div>');
a.should.be_true
end
it 'calls onunmatch when new element deleted'
var a = 0;
$('#b').concrete({onmatch: function(){a = 1;}, onunmatch: function(){a = 2;} });
a.should.equal 0
$('#a').after('<div id="b"></div>');
a.should.equal 1
$('#b').remove();
a.should.equal 2
end
it 'calls onmatch when ruleset matches after class added'
var a = 0;
$('#a.foo').concrete({onmatch: function(){a = 1;} });
a.should.equal 0
$('#a').addClass('foo');
a.should.equal 1
end
it 'calls onmatch in both direct and namespaced onmatch, does not call less specific onmatch'
var a = 0, b=0, c=0, d=0;
$('.foo').concrete({onmatch: function(){a = 1;}})
$('.foo').concrete('bar', function($){return{onmatch: function(){b = 1;}}})
$('#a.foo').concrete({onmatch: function(){c = 1;}})
$('#a.foo').concrete('bar', function($){return{onmatch: function(){d = 1}}})
[a, b, c, d].should.eql [0, 0, 0, 0]
$('#a').addClass('foo');
[a, b, c, d].should.eql [0, 0, 1, 1]
end
it 'calls onmatch in both direct and namespaced onmatch, super works as expected'
var a = 0, b=0, c=0, d=0;
$('.foo').concrete({onmatch: function(){a += 1;}})
$('.foo').concrete('bar', function($){return{onmatch: function(){b += 1;}}})
$('#a.foo').concrete({onmatch: function(){this._super(); c = 1; this._super();}})
$('#a.foo').concrete('bar', function($){return{onmatch: function(){this._super(); d = 1; this._super();}}})
[a, b, c, d].should.eql [0, 0, 0, 0]
$('#a').addClass('foo');
[a, b, c, d].should.eql [2, 2, 1, 1]
end
end
end

View File

@ -0,0 +1,68 @@
describe 'Concrete'
describe 'Events'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.concrete.synchronous_mode();
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div>')
end
it 'calls onfoo when foo triggered'
var a = 0;
$('#a').concrete({onfoo: function(){a = 1;} });
a.should.equal 0
$('#a').trigger('foo');
a.should.equal 1
end
it 'only calls most specific onfoo when foo triggered'
var a = 0, b = 0;
$('#a.a').concrete({onfoo: function(){a = 1;} });
$('#a').concrete({onfoo: function(){b = 1;} });
a.should.equal 0
b.should.equal 0
$('#a').trigger('foo');
a.should.equal 1
b.should.equal 0
end
it 'calls namespaced onfoo when foo triggered'
var a = 0;
$('#a').concrete('bar', function($){return{onfoo: function(){a = 1;} }});
a.should.equal 0
$('#a').trigger('foo');
a.should.equal 1
end
it 'calls most specific namespaced onfoo and most specific non-namespaced onfoo when foo triggered'
var a = 0, b = 0, c = 0, d = 0;
$('#a.a').concrete({onfoo: function(){a = 1;} });
$('#a').concrete({onfoo: function(){b = 1;} });
$('#a.a').concrete('bar', function($){return{onfoo: function(){c = 1;} }});
$('#a').concrete('bar', function($){return{onfoo: function(){d = 1;} }});
[a, b, c, d].should.eql [0, 0, 0, 0]
$('#a').trigger('foo');
[a, b, c, d].should.eql [1, 0, 1, 0]
end
it 'calls up correctly on _super'
var a = 0, b = 0;
$('#a').concrete({onfoo: function(){a += 1;} });
$('#a.a').concrete({onfoo: function(){this._super(); b += 1; this._super();} });
[a, b].should.eql [0, 0]
$('#a').trigger('foo')
[a, b].should.eql [2, 1]
end
end
end

View File

@ -0,0 +1,246 @@
describe 'Concrete'
describe 'Namespaces'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.concrete.synchronous_mode();
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>')
end
it 'namespaced functions work (single definition mode)'
$('#a').concrete('bar', function($){return{
bar: function(){return 'a';}
}})
$('#a').concrete('bar').bar().should.equal 'a'
end
it 'namespaced functions work (block definition mode)'
$.concrete('zap', function($){
$('#a').concrete({
bar: function(){return 'a';}
})
});
$('#a').concrete('zap').bar().should.equal 'a'
end
it 'double-namespaced functions work (block definition mode)'
$.concrete('zap', function($){
$.concrete('pow', function($){
$('#a').concrete({
bar: function(){return 'a';}
})
})
})
$('#a').concrete('zap.pow').bar().should.equal 'a'
end
it 'revert to base namespacing work (block definition mode)'
$.concrete('zap', function($){
$.concrete('.pow', function($){
$('#a').concrete({
bar: function(){return 'a';}
})
})
})
$('#a').concrete('pow').bar().should.equal 'a'
end
it 'internal to namespace, will look up functions in namespace before in base'
var res = []
$('#a').concrete({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').concrete('bar', function($){return{
foo: function(){res.push(3);},
bar: function(){res.push(4); $(this).foo();}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#dom_test div').concrete('bar').bar();
res.should.eql [2, 1, 4, 3]
end
it 'internal to namespace, will look up functions in namespace before in base, even in closure'
var res = []
$('#a').concrete({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').concrete('bar', function($){return{
foo: function(){res.push(3);},
bar: function(){res.push(4); $('#a').each(function(){ $(this).foo(); })}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#dom_test div').concrete('bar').bar();
res.should.eql [2, 1, 4, 3]
end
it 'internal to namespace, will look up functions in namespace before in base, even in onmatch'
var res = []
$('#a').concrete({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').concrete('bar', function($){return{
foo: function(){res.push(3);}
}})
$('#a.d').concrete('bar', function($){return{
onmatch: function(){res.push(4); this.foo();}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#a').addClass('d');
res.should.eql [2, 1, 4, 3]
end
it 'internal to namespace, will look up functions in base when not present in namespace'
var res = []
$('#a').concrete({
foo: function(){res.push(1);},
})
$('#a').concrete('bar', function($){return{
bar: function(){res.push(2); this.foo();}
}})
$('#dom_test div').concrete('bar').bar();
res.should.eql [2, 1]
end
it 'internal to namespace, will not look up functions in base if present in namespace, even when not applicable to selector'
var res = []
$('#a').concrete('bar', function($){return{
foo: function(){this.bar();},
}})
$('#a').concrete({
bar: function(){res.push(1);},
})
$('span').concrete('bar', function($){return{
bar: function(){res.push(2);}
}})
$('#a').concrete('bar').foo()
res.should.eql []
end
it 'internal to namespace, can be directed to base namespace'
var res = []
$('#a').concrete({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').concrete('bar', function($){return{
foo: function(){res.push(3);},
bar: function(){res.push(4); this.foo(); this.concrete('.').foo();}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#dom_test div').concrete('bar').bar();
res.should.eql [2, 1, 4, 3, 1]
end
it 'internal to namespace, will look up functions in namespace called the same as a regular jQuery base function'
var res = []
$('#a').concrete('bar', function($){return{
load: function(){res.push(1);},
bar: function(){res.push(2); this.load();}
}})
$('#dom_test div').concrete('bar').bar();
res.should.eql [2, 1]
end
it 'internal to namespace, can be directed to regular jQuery base function'
var res = []
$.fn.testy = function(){ res.push(1); }
$('#a').concrete('bar', function($){return{
testy: function(){res.push(3);},
bar: function(){res.push(2); this.concrete('.').testy();}
}})
$('#dom_test div').concrete('bar').bar();
res.should.eql [2, 1]
end
it 'internal to namespace, can be directed to sub namespace'
var res = []
$.concrete('zap', function($){
$('#a').concrete({
foo: function(){ res.push(1); this.concrete('pow').bar(); }
})
$.concrete('pow', function($){
$('#a').concrete({
bar: function(){ res.push(2); }
})
})
})
$('#dom_test div').concrete('zap').foo();
res.should.eql [1, 2]
end
it 'internal to namespace, can be directed to unrelated namespace'
var res = []
$.concrete('zap', function($){
$('#a').concrete({
foo: function(){ res.push(1); this.concrete('.pow').bar(); }
})
$.concrete('pow', function($){
$('#a').concrete({
bar: function(){ res.push(2); }
})
})
})
$.concrete('pow', function($){
$('#a').concrete({
bar: function(){ res.push(3); }
})
})
$('#dom_test div').concrete('zap').foo();
res.should.eql [1, 3]
end
it 'a function passed out of a namespace will remember its namespace'
var res = []
var func = function(func) {
func.call($('#a, #b'));
}
$('#a, #b').concrete('bar', function($){return{
zap: function(){res.push($(this).attr('id'));},
bar: function(){res.push(2); func(this.zap);}
}})
$('#dom_test #a').concrete('bar').bar();
res.should.eql [2, 'b', 'a']
end
it 'using block functions'
var res = []
$('#a').concrete({
foo: function(){res.push(1);},
})
$('#a').concrete('bar', function($){return{
foo: function(){res.push(3);},
}})
$('#dom_test div').foo();
res.should.eql [1]
$('#dom_test div').concrete('bar', function($){
$(this).foo();
})
res.should.eql [1, 3]
end
end
end

View File

@ -0,0 +1,77 @@
describe 'Concrete'
describe 'Properties'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="b c"></div>')
end
it 'can define and get a basic property'
$('#a').concrete({
Foo: null
});
$('.a').getFoo().should.be_null
end
it 'can define and set a basic property'
$('#a').concrete({
Foo: null
});
$('.a').setFoo(1);
$('.a').getFoo().should.equal 1
end
it 'can define a default value'
$('#a').concrete({
Foo: 1
});
$('.a').getFoo().should.equal 1
end
it 'should manage proprties in namespaces without clashing'
$('#a').concrete({
Foo: 1
});
$.concrete('test', function($){
$('#a').concrete({
Foo: 2
});
});
$('.a').getFoo().should.equal 1
$('.a').concrete('test').getFoo().should.equal 2
$('.a').setFoo(4);
$('.a').concrete('test').setFoo(8);
$('.a').getFoo().should.equal 4
$('.a').concrete('test').getFoo().should.equal 8
end
it 'should manage directly setting proprties in namespaces without clashing'
$('#a').concrete({
Foo: null
});
$.concrete('test', function($){
$('#a').concrete({
Foo: null
});
});
$('.a').concreteData('Foo', 4);
$('.a').concrete('test').concreteData('Foo', 8);
$('.a').concreteData('Foo').should.equal 4
$('.a').concrete('test').concreteData('Foo').should.equal 8
end
end
end

View File

@ -0,0 +1,54 @@
describe 'Concrete'
describe 'Super'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>')
end
it 'can call the super function'
var a = 1;
$('#a').concrete({
foo: function(){a *= 2;}
});
$('#a.a').concrete({
foo: function(){a += 2; this._super();}
});
$('#a').foo();
a.should.equal 6
end
it 'super to a non-existant class should be ignored'
var a = 1;
$('#a').concrete({
foo: function(){a *= 2; this._super();}
});
$('#a.a').concrete({
foo: function(){a += 2; this._super();}
});
$('#a').foo();
a.should.equal 6
end
it 'can call super from two different functions without screwing up what super points to'
var list = [];
$('#a').concrete({
foo: function(){ list.push('foo'); this.bar(); },
bar: function(){ list.push('bar'); }
});
$('#a.a').concrete({
foo: function(){ list.push('foo2'); this._super(); list.push('foo2'); this._super(); },
bar: function(){ list.push('bar2'); this._super(); }
});
$('#a').foo();
list.should.eql [ 'foo2', 'foo', 'bar2', 'bar', 'foo2', 'foo', 'bar2', 'bar' ]
end
end
end

View File

@ -0,0 +1,38 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="../vendor/jspec/lib/jspec.css" />
<script src="../vendor/jquery-1.3.2.js"></script>
<script src="../vendor/jspec/lib/jspec.js"></script>
<script src="../vendor/jspec/lib/jspec.jquery.js"></script>
<script src="../vendor/jquery.selector/jquery.class.js"></script>
<script src="../vendor/jquery.selector/jquery.selector.js"></script>
<script src="../vendor/jquery.selector/jquery.selector.specifity.js"></script>
<script src="../vendor/jquery.selector/jquery.selector.matches.js"></script>
<script src="../src/jquery.concrete.js"></script>
<script src="../src/jquery.concrete.dommaybechanged.js"></script>
<script src="../src/jquery.concrete.events.js"></script>
<script src="../src/jquery.concrete.ctors.js"></script>
<script src="../src/jquery.concrete.properties.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.concrete.basics.js')
.exec('spec.concrete.namespaces.js')
.exec('spec.concrete.super.js')
.exec('spec.concrete.events.js')
.exec('spec.concrete.ctors.js')
.exec('spec.concrete.properties.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"><div class="loading"></div></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -0,0 +1,102 @@
(function($) {
/* Add the methods to handle constructor & destructor binding to the Namespace class */
$.concrete.Namespace.addMethods({
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = $.concrete.RuleList()) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
if (ctors[i].selector.selector == selector.selector) {
rule = ctors[i]; break;
}
}
if (!rule) {
rule = ctors.addRule(selector, 'ctors');
}
rule[name] = func;
if (!ctors[name+'proxy']) {
var one = this.one('ctors', name);
var namespace = this;
var proxy = function(els, i, func) {
var j = els.length;
while (j--) {
var el = els[j];
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { el.i = tmp_i; el.f = tmp_f; }
}
}
ctors[name+'proxy'] = proxy;
}
}
});
$.concrete.Namespace.addHandler({
order: 30,
bind: function(selector, k, v) {
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
this.bind_condesc(selector, k, v);
return true;
}
}
});
/**
* Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate
*
* Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that
* a series of DOM manipulations will only trigger this once.
*
* The downside of this is that things like:
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
* won't work.
*/
$(document).bind('DOMMaybeChanged', function(){
// For every namespace
for (var k in $.concrete.namespaces) {
// That has constructors or destructors
var ctors = $.concrete.namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), match, add, rem;
// Stepping through each selector from most to least specific
var j = ctors.length;
while (j--) {
// Build some quick-acccess variables
var sel = ctors[j].selector.selector, ctor = ctors[j].onmatch; dtor = ctors[j].onunmatch;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (ctors[j].cache) {
// Find the ones that are extra this time
add = res.not(ctors[j].cache);
// Find the ones that are gone this time
rem = ctors[j].cache.not(res);
// And call the desctructor on them
if (rem.length && dtor) ctors.onunmatchproxy(rem, j, dtor);
}
// Call the constructor on the newly matched ones
if (add.length && ctor) ctors.onmatchproxy(add, j, ctor);
// Add these matched ones to the list tracking all elements matched so far
matched = matched.add(res);
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
ctors[j].cache = res;
}
}
}
})
})(jQuery);

View File

@ -0,0 +1,64 @@
(function($){
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
var runSoon = window.setTimeout;
/** The timer handle for the asyncronous matching call */
var check_id = null;
/** Fire the change event. Only fires on the document node, so bind to that */
var triggerEvent = function() {
$(document).triggerHandler('DOMMaybeChanged');
check_id = null;
}
$.extend($.concrete, {
/**
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
* the DOM manipulation that made them match. This is only really useful for during testing, since it's pretty slow
* (otherwise we'd make it the default).
*/
synchronous_mode: function() {
if (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; }
},
/**
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
* Called automatically on document.ready
*/
triggerMatching: function() {
matching();
}
});
function registerMutateFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function() {
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
}
function registerSetterGetterFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function(a, b) {
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
}
// Register core DOM manipulation methods
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ triggerEvent(); })
})(jQuery);

View File

@ -0,0 +1,169 @@
(function($) {
/* If we are any browser other than IE or Safari, we don't have to do anything special to handle
* onchange delegation */
$.support.bubblingChange = !($.browser.msie || $.browser.safari);
/* Return true if node b is the same as, or is a descendant of, node a */
if (document.compareDocumentPosition) {
var is_or_contains = function(a, b) {
return a && b && (a == b || !!(a.compareDocumentPosition(b) & 16));
}
}
else {
var is_or_contains = function(a, b) {
return a && b && (a == b || (a.contains ? a.contains(b) : true));
}
}
/* Add the methods to handle event binding to the Namespace class */
$.concrete.Namespace.addMethods({
build_event_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e, originalevent) {
e = originalevent || e;
var el = e.target;
while (el && el != document && !e.isPropagationStopped()) {
var ret = one(el, arguments);
if (ret !== undefined) e.result = ret;
if (ret === false) { e.preventDefault(); e.stopPropagation(); }
el = el.parentNode;
}
};
return prxy;
},
build_mouseenterleave_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e) {
var el = e.target;
var rel = e.relatedTarget;
while (el && el != document && !e.isPropagationStopped()) {
/* We know el contained target. If it also contains relatedTarget then we didn't mouseenter / leave. What's more, every ancestor will also
contan el and rel, and so we can just stop bubbling */
if (is_or_contains(el, rel)) break;
var ret = one(el, arguments);
if (ret !== undefined) e.result = ret;
if (ret === false) { e.preventDefault(); e.stopPropagation(); }
el = el.parentNode;
}
};
return prxy;
},
build_change_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e) {
var el = e.target;
// If this is a keydown event, only worry about the enter key, since browsers only trigger onchange on enter or focus loss
if (e.type === 'keydown' && e.keyCode !== 13) return;
// Make sure this is event is for an input type we're interested in
if (el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA' && el.tagName !== 'SELECT') return;
var $el = $(el), nowVal, oldVal = $el.data('changeVal');
// Detect changes on checkboxes & radiobuttons, which have different value logic. We don't use el.value, since el is part
// of a set, and we only want to raise onchange once for a single user action.
if (el.type == 'checkbox' || el.type == 'radio') {
if (!el.disabled && e.type === 'click') {
nowVal = el.checked;
// If radio, we get two changes - the activation, and the deactivation. We only want to fire one change though
if ((el.type === 'checkbox' || nowVal === true) && oldVal !== nowVal) e.type = 'change';
}
}
// Detect changes on other input types. In this case value is OK.
else {
nowVal = el.value;
if (oldVal !== undefined && oldVal !== nowVal) e.type = 'change';
}
// Save the current value for next time
if (nowVal !== undefined) $el.data('changeVal', nowVal);
// And if we decided that a change happened, do the actual triggering
if (e.type == 'change') {
while (el && el != document && !e.isPropagationStopped()) {
var ret = one(el, arguments);
if (ret !== undefined) e.result = ret;
if (ret === false) { e.preventDefault(); e.stopPropagation(); }
el = el.parentNode;
}
}
};
return prxy;
},
bind_event: function(selector, name, func, event) {
var funcs = this.store[name] || (this.store[name] = $.concrete.RuleList()) ;
var proxies = funcs.proxies || (funcs.proxies = {});
var rule = funcs.addRule(selector, name); rule.func = func;
if (!proxies[name]) {
switch (name) {
case 'onmouseenter':
proxies[name] = this.build_mouseenterleave_proxy(name);
event = 'mouseover';
break;
case 'onmouseleave':
proxies[name] = this.build_mouseenterleave_proxy(name);
event = 'mouseout';
break;
case 'onchange':
if (!$.support.bubblingChange) {
proxies[name] = this.build_change_proxy(name);
event = 'click focusin focusout keydown';
}
break;
case 'onsubmit':
event = 'delegated_submit';
case 'onfocus':
case 'onblur':
$.concrete.warn('Event '+event+' not supported - using focusin / focusout instead', $.concrete.WARN_LEVEL_IMPORTANT);
}
if (!proxies[name]) proxies[name] = this.build_event_proxy(name);
$(document).bind(event, proxies[name]);
}
}
});
$.concrete.Namespace.addHandler({
order: 40,
bind: function(selector, k, v){
if ($.isFunction(v) && (match = k.match(/^on(.*)/))) {
event = match[1];
this.bind_event(selector, k, v, event);
return true;
}
}
});
// Find all forms and bind onsubmit to trigger on the document too. This is the only event that can't be grabbed via delegation.
var form_binding_cache = $([]); // A cache for already-handled form elements
var delegate_submit = function(e){ $(document).triggerHandler('delegated_submit', e); } // The function that handles the delegation
$(document).bind('DOMMaybeChanged', function(){
var forms = $('form');
// Only bind to forms we haven't processed yet
forms.not(form_binding_cache).bind('submit', delegate_submit);
// Then remember the current set of forms
form_binding_cache = forms;
});
})(jQuery);

View File

@ -2,70 +2,54 @@ var console;
(function($) {
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
var runSoon = window.setTimeout;
var namespaces = {};
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
var rulecount = 0;
$.concrete = function() {
$.fn.concrete.apply(null, arguments);
}
/**
* A couple of utility functions for accessing the store outside of this closure, and for making things
* operate in a little more easy-to-test manner
*/
$.extend($.concrete, {
/**
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: namespaces,
/**
* Remove all concrete rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].concrete) delete $.fn[k] ; }
// Remove namespaces, and start over again
namespaces = $.concrete.namespaces = {};
},
WARN_LEVEL_NONE: 0,
WARN_LEVEL_IMPORTANT: 1,
WARN_LEVEL_BESTPRACTISE: 2,
/**
* Warning level. Set to a higher level to get warnings dumped to console.
*/
warningLevel: 0,
/** Utility to optionally display warning messages depending on level */
var warn = function(message, level) {
warn: function(message, level) {
if (level <= $.concrete.warningLevel && console && console.log) {
console.warn(message);
if (console.trace) console.trace();
}
}
/** A property definition */
$.property = function(options) {
if (this instanceof $.property) this.options = options;
else return new $.property(options);
}
$.extend($.property, {
/**
* Strings for how to cast a value to a specific type. Used in some nasty meta-programming stuff below to try and
* keep property access as fast as possible
*/
casters: {
'int': 'Math.round(parseFloat(v));',
'float': 'parseFloat(v);',
'string': '""+v;'
},
getter: function(options) {
options = options || {};
if (options.initial === undefined) return function(){ return this.d()[arguments.callee.pname] };
var getter = function(){
var d = this.d(); var k = arguments.callee.pname;
return d.hasOwnProperty(k) ? d[k] : (d[k] = arguments.callee.initial);
};
var v = options.initial;
getter.initial = options.restrict ? eval($.property.casters[options.restrict]) : v;
return getter;
},
setter: function(options){
options = options || {};
if (options.restrict) {
var restrict = options.restrict;
return new Function('v', 'return this.d()[arguments.callee.pname] = ' + $.property.casters[options.restrict]);
}
return function(v){ return this.d()[arguments.callee.pname] = v; }
}
});
$.extend($.property.prototype, {
getter: function(){
return $.property.getter(this.options);
},
setter: function(){
return $.property.setter(this.options);
}
});
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
var rulecount = 0;
var Rule = Base.extend({
init: function(selector, name) {
this.selector = selector;
@ -86,20 +70,29 @@ var console;
(a.rulecount - b.rulecount) ;
}
$.fn._super = function(){
var rv, i = this.length;
while (i--) {
var el = this[0];
rv = el.f(el, arguments, el.i);
}
return rv;
$.concrete.RuleList = function() {
var list = [];
list.addRule = function(selector, name){
var rule = Rule(selector, name);
list[list.length] = rule;
list.sort(Rule.compare);
return rule;
};
return list;
}
var namespaces = {};
var handlers = [];
var Namespace = Base.extend({
/**
* A Namespace holds all the information needed for adding concrete methods to a namespace (including the _null_ namespace)
*/
$.concrete.Namespace = Base.extend({
init: function(name){
if (name && !name.match(/^[A-Za-z0-9.]+$/)) warn('Concrete namespace '+name+' is not formatted as period seperated identifiers', $.concrete.WARN_LEVEL_BESTPRACTISE);
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.concrete.warn('Concrete namespace '+name+' is not formatted as period seperated identifiers', $.concrete.WARN_LEVEL_BESTPRACTISE);
name = name || '__base';
this.name = name;
@ -113,18 +106,68 @@ var console;
}
else {
// We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into
var subfn = function(jq){
this.selector = jq.selector; this.context = jq.context; this.setArray($.makeArray(jq));
}
var subfn = function(){}
this.injectee = subfn.prototype = new $();
// And then we provide an overriding $ that returns objects of our new Class
this.$ = function() {
return new subfn($.apply(window, arguments));
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
var bound$ = this.$ = function(a) {
// Try the simple way first
var jq = $.fn.init.apply(new subfn(), arguments);
if (jq instanceof subfn) return jq;
// That didn't return a bound object, so now we need to copy it
var rv = new subfn();
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
while (i--) rv[i] = jq[i];
return rv;
}
this.injectee.pushStack = function(elems, name, selector){
var ret = bound$(elems);
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) ret.selector = this.selector + (this.selector ? " " : "") + selector;
else if ( name ) ret.selector = this.selector + "." + name + "(" + selector + ")";
// Return the newly-formed element set
return ret;
}
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
$.extend(this.$, $);
// We override concrete to inject the name of this namespace when defining blocks inside this namespace
var concrete_wrapper = this.injectee.concrete = function(spacename) {
var args = arguments;
if (!spacename || typeof spacename != 'string') { args = $.makeArray(args); args.unshift(name); }
else if (spacename.charAt(0) != '.') args[0] = name+'.'+spacename;
return $.fn.concrete.apply(this, args);
}
this.$.concrete = function() {
concrete_wrapper.apply(null, arguments);
}
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i], builder;
// Inject jQuery object method overrides
if (builder = handler.namespaceMethodOverrides) {
var overrides = builder(this);
for (var k in overrides) this.injectee[k] = overrides[k];
}
// Inject $.concrete function overrides
if (builder = handler.namespaceStaticOverrides) {
var overrides = builder(this);
for (var k in overrides) this.$.concrete[k] = overrides[k];
}
}
}
},
@ -174,10 +217,9 @@ var console;
},
bind_proxy: function(selector, name, func) {
var funcs = this.store[name] || (this.store[name] = []) ;
var rulelist = this.store[name] || (this.store[name] = $.concrete.RuleList());
var rule = funcs[funcs.length] = Rule(selector, name); rule.func = func;
funcs.sort(Rule.compare);
var rule = rulelist.addRule(selector, name); rule.func = func;
if (!this.injectee.hasOwnProperty(name)) {
this.injectee[name] = this.build_proxy(name);
@ -185,102 +227,17 @@ var console;
}
if (!this.injectee[name].concrete) {
warn('Warning: Concrete function '+name+' clashes with regular jQuery function - concrete function will not be callable directly on jQuery object', $.concrete.WARN_LEVEL_IMPORTANT);
}
},
bind_event: function(selector, name, func, event) {
var funcs = this.store[name] || (this.store[name] = []) ;
var rule = funcs[funcs.length] = Rule(selector, name); rule.func = func;
funcs.sort(Rule.compare);
if (!funcs.proxy) {
funcs.proxy = this.build_proxy(name);
$(selector.selector).live(event, funcs.proxy);
}
},
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = []) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
if (ctors[i].selector.selector == selector.selector) {
rule = ctors[i]; break;
}
}
if (!rule) {
rule = ctors[ctors.length] = Rule(selector, 'ctors');
ctors.sort(Rule.compare);
}
rule[name] = func;
if (!ctors[name+'proxy']) {
var one = this.one('ctors', name);
var namespace = this;
var proxy = function(els, i, func) {
var j = els.length;
while (j--) {
var el = els[j];
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { el.i = tmp_i; el.f = tmp_f; }
}
}
ctors[name+'proxy'] = proxy;
$.concrete.warn('Warning: Concrete function '+name+' clashes with regular jQuery function - concrete function will not be callable directly on jQuery object', $.concrete.WARN_LEVEL_IMPORTANT);
}
},
add: function(selector, data) {
var k, v, match, event;
// For every item in the hash, try ever method handler, until one returns true
for (var k in data) {
var v = data[k];
for (k in data) {
v = data[k];
if ($.isFunction(v)) {
if (k == 'onmatch' || k == 'onunmatch') {
this.bind_condesc(selector, k, v);
}
else if (match = k.match(/^on(.*)/)) {
event = match[1];
if (!$.fn.liveHover && $.concrete.event_needs_extensions[event]) {
warn('Event '+event+' requires live-extensions to function, which does not seem to be present', $.concrete.WARN_LEVEL_IMPORTANT);
}
else if (event == 'submit') {
warn('Event submit not currently supported', $.concrete.WARN_LEVEL_IMPORTANT);
}
else if (event == 'focus' || event == 'blur') {
warn('Event '+event+' not supported - use focusin / focusout instead', $.concrete.WARN_LEVEL_IMPORTANT);
}
this.bind_event(selector, k, v, event);
}
else {
this.bind_proxy(selector, k, v);
}
}
else {
var g, s, p;
if (k.charAt(0) != k.charAt(0).toUpperCase()) warn('Concrete property '+k+' does not start with a capital letter', $.concrete.WARN_LEVEL_BESTPRACTISE);
if (v == $.property || v instanceof $.property) {
g = v.getter(); s = v.setter();
}
else {
p = $.property({initial: v}); g = p.getter(); s = p.setter();
}
g.pname = s.pname = k;
this.bind_proxy(selector, k, g);
this.bind_proxy(selector, 'set'+k, s);
for (var i = 0; i < handlers.length; i++) {
if (handlers[i].bind && handlers[i].bind.call(this, selector, k, v)) break;
}
}
},
@ -298,171 +255,79 @@ var console;
}
});
/**
* A handler is some javascript code that adds support for some time of key / value pair passed in the hash to the Namespace add method.
* The default handlers provided (and included by default) are event, ctor and properties
*/
$.concrete.Namespace.addHandler = function(handler) {
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
handlers.splice(i, 0, handler);
}
$.concrete.Namespace.addHandler({
order: 50,
bind: function(selector, k, v){
if ($.isFunction(v)) {
this.bind_proxy(selector, k, v);
return true;
}
}
});
$.extend($.fn, {
/**
* Main concrete function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
*
*/
$.fn.concrete = function() {
concrete: function(spacename) {
var i = 0;
var selector = $.selector(this.selector);
var selector = this.selector ? $.selector(this.selector) : null;
var namespace = namespaces.__base || Namespace();
if (typeof arguments[i] == 'string') {
namespace = namespaces[arguments[i]] || Namespace(arguments[i]);
i++;
/* By default we operator on the base namespace */
var namespace = namespaces.__base || $.concrete.Namespace();
/* If the first argument is a string, then it's the name of a namespace. Look it up */
if (typeof spacename == 'string') {
if (spacename.charAt('0') == '.') spacename = spacename.substr(1);
if (spacename) namespace = namespaces[spacename] || $.concrete.Namespace(spacename);
i=1;
}
/* All remaining arguments should either be using blocks or definition hashs */
while (i < arguments.length) {
var res = arguments[i];
// If it's a function, call it - either it's a using block or it's a concrete definition builder
var res = arguments[i++];
// If it's a function, call it - either it's a using block or it's a namespaced concrete definition
if ($.isFunction(res)) {
if (res.length != 1) warn('Function block inside concrete definition does not take $ argument properly', $.concrete.WARN_LEVEL_IMPORTANT);
res = res.call(this, namespace.$);
}
else if (namespace.name != '__base') warn('Raw object inside namespaced ('+namespace.name+') concrete definition - namespace lookup will not work properly', $.concrete.WARN_LEVEL_IMPORTANT);
// Now if we still have a concrete definition object, inject it into namespace
if (res) namespace.add(selector, res);
i++
if (res.length != 1) $.concrete.warn('Function block inside concrete definition does not take $ argument properly', $.concrete.WARN_LEVEL_IMPORTANT);
res = res.call(namespace.$(this), namespace.$);
}
// If we have a concrete definition hash, inject it into namespace
if (res) {
if (selector) namespace.add(selector, res);
else $.concrete.warn('Concrete block given to concrete call without selector. Make sure you call $(selector).concrete when defining blocks', $.concrete.WARN_LEVEL_IMPORTANT);
}
}
/* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */
return namespace.$(this);
}
/**
* A couple of utility functions for accessing the store outside of this closure, and for making things
* operate in a little more easy-to-test manner
*/
$.concrete = {
/**
* Get all the namespaces. Usefull for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: function() { return namespaces; },
/**
* Remove all concrete rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].concrete) delete $.fn[k] ; }
// Remove namespaces, and start over again
namespaces = [];
},
/**
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
* the DOM manipulation that made them match. This is only really useful for during testing, since it's pretty slow
* (otherwise we'd make it the default).
* Calls the next most specific version of the current concrete method
*/
synchronous_mode: function() {
if (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; }
},
/**
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
* Called automatically on document.ready
*/
triggerMatching: function() {
matching();
},
WARN_LEVEL_NONE: 0,
WARN_LEVEL_IMPORTANT: 1,
WARN_LEVEL_BESTPRACTISE: 2,
/**
* Warning level. Set to a higher level to get warnings dumped to console.
*/
warningLevel: 0,
/**
* These events need the live-extensions plugin
*/
event_needs_extensions: { mouseenter: true, mouseleave: true, change: true, focusin: true, focusout: true }
_super: function(){
var rv, i = this.length;
while (i--) {
var el = this[0];
rv = el.f(el, arguments, el.i);
}
var check_id = null;
/**
* Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate
*
* Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that
* a series of DOM manipulations will only trigger this once.
*
* The downside of this is that things like:
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
* won't work.
*/
function matching() {
// For every namespace
for (var k in namespaces) {
// That has constructors or destructors
var ctors = namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), match, add, rem;
// Stepping through each selector from most to least specific
var j = ctors.length;
while (j--) {
// Build some quick-acccess variables
var sel = ctors[j].selector.selector, ctor = ctors[j].onmatch; dtor = ctors[j].onunmatch;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (ctors[j].cache) {
// Find the ones that are extra this time
add = res.not(ctors[j].cache);
// Find the ones that are gone this time
rem = ctors[j].cache.not(res);
// And call the desctructor on them
if (rem.length && dtor) ctors.onunmatchproxy(rem, j, dtor);
}
// Call the constructor on the newly matched ones
if (add.length && ctor) ctors.onmatchproxy(add, j, ctor);
// Add these matched ones to the list tracking all elements matched so far
matched = matched.add(res);
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
ctors[j].cache = res;
}
}
}
check_id = null;
}
function registerMutateFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function() {
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(matching, 100);
return rv;
}
})
}
function registerSetterGetterFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function(a, b) {
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(matching, 100);
return rv;
}
})
}
// Register core DOM manipulation methods
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ matching(); })
});
})(jQuery);

View File

@ -0,0 +1,77 @@
(function($) {
var concrete_prepend = '__concrete!';
var getConcreteData = function(el, namespace, property) {
return el.data(concrete_prepend + namespace + '!' + property);
}
var setConcreteData = function(el, namespace, property, value) {
return el.data(concrete_prepend + namespace + '!' + property, value);
}
var getConcreteDataAsHash = function(el, namespace) {
var hash = {};
var id = jQuery.data(el[0]);
var matchstr = concrete_prepend + namespace + '!';
var matchlen = matchstr.length;
var cache = jQuery.cache[id];
for (var k in cache) {
if (k.substr(0,matchlen) == matchstr) hash[k.substr(matchlen)] = cache[k];
}
return hash;
}
var setConcreteDataFromHash = function(el, namespace, hash) {
for (var k in hash) setConcreteData(namespace, k, hash[k]);
}
var concreteData = function(el, namespace, args) {
switch (args.length) {
case 0:
return getConcreteDataAsHash(el, namespace);
case 1:
if (typeof args[0] == 'string') return getConcreteData(el, namespace, args[0]);
else return setConcreteDataFromHash(el, namespace, args[0]);
default:
return setConcreteData(el, namespace, args[0], args[1]);
}
}
$.extend($.fn, {
concreteData: function() {
return concreteData(this, '__base', arguments);
}
});
$.concrete.Namespace.addHandler({
order: 60,
bind: function(selector, k, v) {
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.concrete.warn('Concrete property '+k+' does not start with a capital letter', $.concrete.WARN_LEVEL_BESTPRACTISE);
var namespace = this;
g = function() { return this.concreteData(k) || v ; }
s = function(v){ return this.concreteData(k, v); }
g.pname = s.pname = k;
this.bind_proxy(selector, 'get'+k, g);
this.bind_proxy(selector, 'set'+k, s);
return true;
},
namespaceMethodOverrides: function(namespace){
return {
concreteData: function() {
return concreteData(this, namespace.name, arguments);
}
};
}
});
})(jQuery);

View File

@ -0,0 +1,49 @@
(function($){
/**
* Add focusin and focusout support to bind and live for browers other than IE. Designed to be usable in a delegated fashion (like $.live)
* Copyright (c) 2007 Jörn Zaefferer
*/
$.support.focusInOut = !!($.browser.msie);
if (!$.support.focusInOut) {
// Emulate focusin and focusout by binding focus and blur in capturing mode
$.each({focus: 'focusin', blur: 'focusout'}, function(original, fix){
$.event.special[fix] = {
setup: function(){
if (!this.addEventListener) return false;
this.addEventListener(original, $.event.special[fix].handler, true);
},
teardown: function(){
if (!this.removeEventListener) return false;
this.removeEventListener(original, $.event.special[fix].handler, true);
},
handler: function(e){
arguments[0] = $.event.fix(e);
arguments[0].type = fix;
return $.event.handle.apply(this, arguments);
}
};
});
}
(function(){
//IE has some trouble with focusout with select and keyboard navigation
var activeFocus = null;
$(document)
.bind('focusin', function(e){
var target = e.realTarget || e.target;
if (activeFocus && activeFocus !== target) {
e.type = 'focusout';
$(activeFocus).trigger(e);
e.type = 'focusin';
e.target = target;
}
activeFocus = target;
})
.bind('focusout', function(e){
activeFocus = null;
});
})();
})(jQuery);

View File

@ -0,0 +1,105 @@
== Firebug Lite Change Log ==
2009-03-09 Azer Koculu <azer@kodfabrik.com>
* firebug-lite.js: Removed getElementPosXY method.
Added toggle method to settings object.
2009-03-07 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Fixed issue 1554: Text nodes could not be accessed through the inspect button.
2009-03-07 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Issue 1566: Added firebug.env.targetWindow variable to hold target window
2009-03-06 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Changed default for firebug.env.detectFirebug to false
Fixed issue 1555: Ajax calls ... we should not assume that the same domain policy is applied, use try catch instead
Fixed Issue 1541: Add XHR tab to Firebug Lite popup window
progress.gif: Corrected mime type
2009-03-03 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js, firebug-lite.css, progress.gif: Fixed Issue 1544: Lite settings menu should have a "processing" gif
2009-02-21 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Fixed Issue 1542: FBLite F12 etc should dock the popup instead of hiding the panel
2009-02-21 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js, firebug-lite.css:
o Issue 1513 ... added:
- Settings dialog
- Settings storage cookie (stores settings, panel height, popup window positions etc.)
- firebug.env:
openInPopup
popupTop
popupLeft
popupWidth
popupHeight
o Various popup window bugfixes and performance improvements
2009-02-21 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js, firebug-lite.css, firebug_logo.png: Issue 1503: FBLite should have a visible sign it is running when starting hidden.
Added FBLite icon when hidden and firebug.env.showIconWhenHidden (default true).
2009-02-18 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Fixed issue 1086 - FBLite can now be used locally over HTTPS if firebug.env.css is set.
2009-02-17 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Fixed problem with issue 1481 - A DIV was created instead of an IFRAME so the shield was ineffective.
2009-02-14 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Issue 1487 - Added firebug.env.liteFilename ... this allows for the renaming of the FBLite script for use in popup windows.
2009-02-12 Azer Koculu <azer@kodfabrik.com>
* minifier.py, jsmin.py, minify: Added minifier library and compressing scripts.
2009-02-12 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Corrected 2 syntax errors
2009-02-11 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Issue 1400 - firebug.env.override is now false by default ... I have also added
two methods to make it easier for the user to control the console:
firebug.overrideConsole() & firebug.restoreConsole()
2009-02-09 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Issue 1400 - Re-implemented firebug.env.override. Default is now true ... it can only be manually changed in FBLite code.
The reason this is needed is because a number of browsers have a window.console object.
2009-02-08 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Issue 1406 - Added firebug.env.hideDOMFunctions to allow hiding of DOM functions in the DOM tab.
2009-02-06 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Removed firebug.env.override to prevent problems when override is set in FF3 where window.console
is a getter and allow more control over initialization.
* firebug-lite.js: Refactored code to allow firebug.env.detectFirebug to be set from user code
* firebug-lite.js: Moved console initialization code inside of the Firebug object to remove some global variables
* firebug-lite.js & firebug-lite.css: Fixed Issue 1348 - String, Date and Boolean objects should be displayed similar
to their primitive counterparts
* firebug-lite.js & firebug-lite.css: Fixed Issue 1023 - Elements such as Flash in iframes and dropdowns in IE bleed through FBLite,
I have put an iframe 'shield' behind FBLite to prevent this
* Updated mime-types for all FBLite SVN files
2009-02-05 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Added firebug.env.detectFirebug to prevent FBLite opening when Firebug is enabled (actually the Firebug Console).
To run FBLite and Firebug at the same time it is now necessary to change the value of detectFirebug directly in firebug-lite.js
* Fixed multiple console initialization and override issues
2009-02-05 Azer Koculu <azer@kodfabrik.com>
* firebug-lite.css: mime-type property has been set to "text/css".
2009-02-04 Azer Koculu <azer@kodfabrik.com>
* firebug-lite.js: Changed default css url getfirebug.com to fbug.google.com
* firebug-lite-compressed.js: It was unnecesseary, removed.
2009-02-02 Azer Koculu <azer@kodfabrik.com>
* Copied latest source to googlecode repository.
2009-01-31 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js: Added extra hotkeys to show / hide FBLite:
F12, (CTRL|CMD)+SHIFT+L, SHIFT+ENTER
* firebug-lite.js: Added console.firebug command to show FBLite version number
* firebug-lite.js: Fixed problem with Open in New Window in Safari
* firebug-lite.js: Added debug="true/false" to show or hide FBLite on startup
2009-01-01 Mike Ratcliffe <sabine.michael.ratcliffe@gmail.com>
* firebug-lite.js & firebug-lite.css: Added Open in New Window
* firebug-lite.js: Fixed: HTML Inspect only worked in 1 very specific situation ... it now works in all situations
* firebug-lite.js: Fixed: Permission was occasionally denied to inspect CSS or scripts when it should not have been
* firebug-lite.js: Fixed: Domain check now works with domain names containing - and _

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

View File

@ -0,0 +1,214 @@
/* */
#Firebug div, #Firebug h3, #Firebug span, #Firebug input, #Firebug textarea, #Firebug select, #Firebug option, #Firebug a, #Firebug strong, #Firebug em, #Firebug label, #Firebug center {
position:static; float:none; width:auto; height:auto; text-align:left; border:0; overflow:visible; background:transparent none repeat left top; color:#111; font-size:1em; font-weight:normal; font-style:normal; font-family:sans-serif; margin:0; padding:0; min-height:0;
}
#Firebug input, #Firebug select, #Firebug textarea { background:#fff; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; border:1px outset #aaa; padding:2px }
#Firebug center { text-align:center; }
#Firebug strong { font-weight:bold; }
#Firebug em { font-style:italic; }
/* */
#FirebugIFrame { position:fixed !important; position:absolute; z-index:99999998; left:0; bottom:0; width:100%; height:295px; background:#fff; border-width:0px; }
#Firebug { position:fixed !important; position:absolute; z-index:99999999; left:0; bottom:0; width:100%; height:295px; background:#fff; overflow:hidden; }
#Firebug .Resizer { position:absolute; top:0; width:100%; height:5px; background:transparent; *background:url(spacer.gif); cursor:n-resize; -moz-user-select:none; }
#Firebug .Header { height:38px; background:#ccc url(firebug.gif) 0 -85px; }
#Firebug .Left, #Firebug > .Right { background:#fff; height:257px;}
#Firebug .Left { float:left; width:100%; }
#Firebug .Right { float:left; display:none; width:30%; overflow:hidden; }
#Firebug .Right .Container { height:100%; border-left:1px solid #ccc; }
/*
* buttonset
*/
#Firebug .ButtonContainer { padding:8px 0 5px 10px; *margin-top:-3px; _margin-top:0; }
#Firebug .ButtonContainer .Button { cursor:pointer; font:12px "MS Sans Serif", Geneva, sans-serif; color:#000; margin-right:5px; padding:4px 4px 4px 4px; border:1px solid transparent !important; border-color:#ccc; text-decoration:none; }
#Firebug .ButtonContainer .Button:hover { border:1px outset #aaa !important; }
#Firebug .ButtonContainer .Button:active { border-style:inset !important; background-color:#ccc; }
#Firebug .ButtonContainer .Enabled { border:1px outset #aaa !important; }
#Firebug .ButtonContainer .Logo { text-decoration:none; padding-left:24px; background:url(firebug.gif) no-repeat 5px 3px; border:0 !important; opacity:0.7; filter:Alpha(Opacity=70); }
#Firebug .ButtonContainer .Logo:hover, #Firebug .ButtonContainer .Logo:active { border:0 !important; background-color:transparent; opacity:1; filter:Alpha(Opacity=100); }
#Firebug .ButtonContainer .Maximize, #Firebug .ButtonContainer .Minimize, #Firebug .ButtonContainer .NewWindow, #Firebug .ButtonContainer .Dock, #Firebug .ButtonContainer .Close { position:absolute; right:0; top:12px; padding:6px 8px 8px 8px; background:url(firebug.gif) no-repeat -3px -48px; border:0 !important; opacity:0.7; filter:Alpha(Opacity:80); *padding:0px 7px 1px 7px; }
#Firebug .ButtonContainer .Maximize:hover, #Firebug .ButtonContainer .Maximize:active, #Firebug .ButtonContainer .NewWindow:hover, #Firebug .ButtonContainer .NewWindow:active, #Firebug .ButtonContainer .Dock:hover, #Firebug .ButtonContainer .Dock:active, #Firebug .ButtonContainer .Minimize:hover, #Firebug .ButtonContainer .Minimize:active, #Firebug .ButtonContainer .Close:hover, #Firebug .ButtonContainer .Close:active { border:0 !important; background-color:transparent; opacity:1; filter:Alpha(Opacity:100); }
#Firebug .ButtonContainer .Minimize { background-position:-3px -20px; right:40px; }
#Firebug .ButtonContainer .Maximize { display:none; background-position:-3px -34px; right:40px; }
#Firebug .ButtonContainer .NewWindow { background-position:-3px -123px; right:20px; }
#Firebug .ButtonContainer .Dock { background-position:-3px -137px; }
#Firebug .ButtonContainer .ButtonSet { border-left:1px solid #ccc; padding:0 0 0 3px; display:inline; }
/*
* navigation
*/
#Firebug .Nav { height:23px; background:url(firebug.gif) repeat-x 0 -62px; padding-left:10px; }
#Firebug .Nav .Tab { display:block; width:auto; float:left; padding:3px 6px 2px 6px; cursor:pointer; border:1px outset transparent !important; border:0; font:bold 11px Arial, Helvetica, sans-serif; color:#666; }
#Firebug .Nav .Tab:hover, #Firebug .Nav .Selected { border:1px outset #ccc !important; border-top-color:transparent !important; }
#Firebug .Nav .Selected { color:#111; cursor:default; background-color:#f2f2f2; }
#Firebug .Nav .Settings { float:right; }
/*
* inspector
*/
#FirebugBorderInspector { display:none; z-index:99999998; position:absolute; top:0; left:0; border:2px solid #0000ff; background:transparent; *background:url(spacer.gif); opacity:0.5; filter:Alpha(Opacity=50); padding:0; margin:0; }
#FirebugBGInspector { display:none; z-index:99999998; position:absolute; top:0; left:0; background:rgb(0,150,255); opacity:0.5; filter:Alpha(Opacity=50); padding:0; margin:0; }
/*
* dom
*/
#Firebug .DOMRow { width:100% !important; width:90%; }
#Firebug .DOMRow .DOMRowLeft { float:left; width:30%; overflow:hidden; }
#Firebug .DOMRow .DOMRowLeft a { padding-left:15px; }
#Firebug .DOMRow .DOMRowRight { float:left; width:60%; *width:60%; _overflow:hidden; white-space:nowrap; }
#Firebug .DOMRow .DOMRowLeft .Object { background:url(tree_open.gif) no-repeat left center; cursor:pointer; }
#Firebug .DOMRow .DOMRowLeft .Object:hover { text-decoration:underline; color:#0000ff; }
#Firebug .DOMRow .DOMRowLeft .Opened { background-image:url(tree_close.gif) }
#Firebug .DOMRow .DOMRowSubContainer { display:none; padding-left:25px; }
/*
* console
*/
#Firebug .Left .Console { overflow:hidden; }
#Firebug .Left .Console .Monitor { height:210px; border-bottom:1px solid #ccc; overflow:scroll; overflow-x:hidden; }
#Firebug .Left .Console .Monitor .Error { background:#ffeff1; color:#ff0000; }
#Firebug .Left .Console .Monitor .Error strong { color:#ff0000; }
#Firebug .Left .Console .Monitor .Row { padding:2px 5px 2px 5px; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; overflow:hidden; border-bottom:1px solid #e5e5e5; }
#Firebug .Left .Console .Monitor .Arrow { color:rgb(0,0,255); white-space:nowrap; }
#Firebug .Left .Console .InputArrow { position:absolute; left:0px; width:20px; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; color:#0000ff; padding:2px 4px 0 4px; }
#Firebug .Left .Console .InputContainer { margin-left:35px; margin-right:5px; }
#Firebug .Left .Console .Input { width:100% !important; margin-top:2px; width:98%; overflow:hidden; border:0; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Console .MLButton { cursor:pointer; position:absolute; top:276px; right:-22px; width:20px; padding:4px 10px 10px 10px; font-size:1px; *padding:2px 10px 10px 10px; background:url(firebug.gif) no-repeat -3px -34px; opacity:0.8; filter:Alpha(Opacity:80); }
#Firebug .Console .MLButton:hover { opacity:1; filter:Alpha(Opacity:100); }
#Firebug .Console .CloseML { background-position:-3px -20px; }
#Firebug .Right .Console { background:#eee; }
#Firebug .Right .Console .Input { width:100%; _width:98%; height:228px; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; background:#fff; border:0; border-bottom:1px solid #ccc; margin-bottom:2px; *overflow:scroll; }
#Firebug .Right .Console .Button { cursor:pointer; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; padding:1px 4px 1px 4px; margin:0 4px 0 4px; background:#eee; border:1px solid #eee; text-decoration:none; *display:block; *width:auto; *float:left; *margin-top:-1px; }
#Firebug .Right .Console .Button:hover { border:1px outset #ccc; background:#fff; }
#Firebug .Right .Console .Button:active { border:1px inset #ccc; background:#ccc; }
#Firebug .Left .Console .Monitor .Warn { background:url(warningIcon.png) #00FFFF no-repeat 2px center; padding-left:20px; }
#Firebug .Left .Console .Monitor .Info { background:url(infoIcon.png) #fff no-repeat 2px center; padding-left:20px; }
#Firebug .Left .Console .Monitor .Error { background:url(errorIcon.png) #FFFFE0 no-repeat 2px center; padding-left:20px; }
/*
* html
*/
#Firebug .Left .HTML { display:none; height:250px; overflow:scroll; padding:0 10px 2px 10px; }
#Firebug .Left .HTML .Block { padding:0 0 0 0px; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Left .HTML .Block .Spacer { cursor:default; padding:2px 2px 2px 15px; }
#Firebug .Left .HTML .Block .Link { display:block; color:#333; padding:2px 2px 2px 0; }
#Firebug .Left .HTML .Block .Link .Content { padding:2px 2px 2px 2px; }
#Firebug .Left .HTML .Block .Link .TagName { cursor:pointer; }
#Firebug .Left .HTML .Block .Parent { font-weight:bold; color:#111; text-decoration:none; }
#Firebug .Left .HTML .Block .Parent .Spacer { background:#fff center no-repeat; }
#Firebug .Left .HTML .Block .Parent .Spacer { background-image:url(tree_open.gif); }
#Firebug .Left .HTML .Block .Open .Spacer { background-image:url(tree_close.gif) }
#Firebug .Left .HTML .Block .Selected { background-color:#4473c4; }
#Firebug .Left .HTML .Block .Selected * { color:#fff; }
#Firebug .Left .HTML .Block .SubContainer { padding-left:20px; height:0; font-size:0px; }
#Firebug .Left .HTML .Block .OpenSubContainer { height:auto; }
#Firebug .Left .HTML .Unvisible { opacity:0.5; filter:Alpha(Opacity=50); }
#Firebug .Right .HTML { display:none; }
#Firebug .Right .HTML .Content { overflow:scroll; height:235px; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Right .HTML .Content .CSSItem { padding:5px; color:#333; height:6px }
#Firebug .Right .HTML .Content .CSSProperty { width:45%; float:left; overflow:hidden; }
#Firebug .Right .HTML .Content .CSSValue { width:55%; float:left; white-space:nowrap; overflow:visible !important; overflow:hidden; }
#Firebug .Right .HTML .Content .CSSValue input { font:11px ArArial, Helvetica, sans-serif; color:#333; 3px 0 0 -3px; }
#Firebug .Right .HTML .Content .CSSItem center { color:rgb(0,150,0); padding:0 0 3px 0; font:bold 12px Arial, Helvetica, sans-serif; }
#Firebug .ButtonContainer .HTML { display:none; }
/*
* CSS
*/
#Firebug .Left .CSS { display:none; padding:5px; height:225px; overflow:scroll; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Right .CSS { display:none; }
#Firebug .Left .CSS .Selector { margin-top:10px }
#Firebug .Left .CSS .CSSText { padding-left:20px; }
#Firebug .Left .CSS .CSSProperty { color:#005500; margin-top:10px; }
#Firebug .Left .CSS .CSSValue { padding-left:5px; color:#000088; }
#Firebug .ButtonContainer .CSS { display:none; }
#Firebug .Right .CSS { background:#eee; }
#Firebug .Right .CSS .Input { width:100%; _width:98%; height:205px; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; background:#fff; border:0; border-bottom:1px solid #ccc; margin-bottom:2px; *overflow:scroll; }
#Firebug .Right .CSS .Button { cursor:pointer; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; padding:1px 4px 1px 4px; margin:0 4px 0 4px; background:#eee; border:1px solid #eee; text-decoration:none; *display:block; *width:auto; *float:left; *margin-top:-1px; }
#Firebug .Right .CSS .Button:hover { border:1px outset #ccc; background:#fff; }
#Firebug .Right .CSS .Button:active { border:1px inset #ccc; background:#ccc; }
/*
* Scripts
*/
#Firebug .Left .Scripts { display:none; height:225px; *height:235px; overflow:scroll; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Left .Scripts .LineNumber { float:left; width:30px; height:20px; padding:0 2px 0 0; background:#eee; color:#666; font-family:Courier; text-align:right; border-right:1px solid #ccc; }
#Firebug .Left .Scripts .CodeContainer { padding:5px; }
#Firebug .Left .Scripts .Code { height:20px; }
#Firebug .Left .Scripts .CodeContainer, #Firebug .Left .Scripts .Code { word-wrap:normal; white-space:pre; }
#Firebug .Right .Scripts { display:none; }
#Firebug .ButtonContainer .Scripts { display:none; }
/*
* DOM
*/
#Firebug .Left .DOM { display:none; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; padding:5px; overflow:scroll; height:225px; }
#Firebug .Left .DOM .Object { font-weight:bold; }
#Firebug .Right .DOM { display:none; overflow:scroll; }
#Firebug .ButtonContainer .DOM { display:none; }
#Firebug .ButtonContainer .DOM label { font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; }
/*
* XHR
*/
#Firebug .Left .XHR { display:none; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; padding:5px; overflow:scroll; height:225px; }
#Firebug .Left .XHR .Block { float:left; width:30%; }
#Firebug .Left .XHR .BlockContent { padding:3px; border-right:1px dotted #aaa; }
#Firebug .Left .XHR .Block strong { display:block; text-align:center; padding:3px; background:rgb(200,225,255); color:rgb(50,50,50); }
#Firebug .Left .XHR .Block span { display:block; text-align:center; overflow:hidden; }
#Firebug .Left .XHR .Block span a { cursor:pointer; color:rgb(0,0,200); }
#Firebug .Right .XHR { display:none; overflow:scroll; }
#Firebug .ButtonContainer .XHR { display:none; }
#Firebug .ButtonContainer .XHR label { font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Left .STR { display:none; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; padding:5px; overflow:scroll; height:225px; }
/*
* settings
*/
#Firebug .SettingsDiv {position:absolute;width:190px;top:60px;border:1px outset #000;-x-system-font:none;color:#000;font-family:"MS Sans Serif",Geneva,sans-serif;font-size:12px;font-size-adjust:none;font-stretch:normal;font-style:normal;font-variant:normal;font-weight:normal;line-height:normal;display:none;background-color:#FFF;}
#Firebug .SettingsDiv .Header {background:#CCC url(firebug.gif) repeat scroll 0 -85px;height:30px;border-bottom:1px inset #000;}
#Firebug .SettingsDiv .Header .Title {padding-top:10px;text-align:center;}
#Firebug .SettingsDiv .Content {padding:5px;}
#Firebug .SettingsDiv .Content .SettingsCBX{position:relative;top:2px;margin: 4px 5px 0 0; border:0 !important; padding:0 !important; height: 15px !important;}
#Firebug .SettingsDiv .Content .ButtonsLeft {float:left;padding:10px 0px 5px 0px;}
#Firebug .SettingsDiv .Content .ButtonsRight {float:right;padding:10px 0px 5px 0px;}
#Firebug .SettingsDiv .Content .ProgressDiv {background-color:#FFF;width:100%;height:100%;position:absolute;left:0;top:0;z-index:1;opacity:.8;filter:alpha(opacity=80);display:none;}
#Firebug .SettingsDiv .Content .Progress {background-image:url(progress.gif);width:66px;height:66px;position:absolute;top:50%;left:50%;margin-left:-33px;margin-top:-33px;}
/*
* element highlighting
*/
#Firebug .Italic { font-style:italic; }
#Firebug .Strong { font-weight:bold; }
#Firebug .NormalWeight { font-weight:normal; }
#Firebug .Green { color:#008800; }
#Firebug .Gray { color:#666; }
#Firebug .Blue { color:#0000ee; }
#Firebug .DarkBlue { color:#000099; }
#Firebug .Red { color:#ee0000; }
#Firebug .Maroon { color:#800000; font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; }
#Firebug .Null { font:11px Verdana, Geneva, Arial, Helvetica, sans-serif; color:#fff; padding:0 2px 0 2px; background:#999; border:1px solid #666; }
#Firebug .ObjectLink, #Firebug .ObjectLinkHover { cursor:pointer; }
#Firebug .ObjectLinkHover { border-bottom:1px solid #008800; }
#Firebug .Clear { clear:both; height:0px; font-size:0px; }
#firebugIconDiv {width:38px;height:38px;right:15pt;bottom:5pt;border:2px solid #000;display:none;position:fixed;background-image:url(firebug_logo.png);text-decoration:underline;cursor:pointer;filter:alpha(opacity:60);opacity:.6;z-index:99999998;}

View File

@ -0,0 +1,2518 @@
var firebug = {
version:[1.23,20090309],
el:{},
env:{
"cache":{},
"extConsole":null,
"css":"http://getfirebug.com/releases/lite/1.2/firebug-lite.css",
"debug":true,
"detectFirebug":false,
"dIndex":"console",
"height":295,
"hideDOMFunctions":false,
"init":false,
"isPopup":false,
"liteFilename":"firebug-lite.js",
"minimized":false,
"openInPopup": false,
"override":false,
"ml":false,
"popupWin":null,
"showIconWhenHidden":true,
"targetWindow":undefined,
"popupTop":1,
"popupLeft":1,
"popupWidth":undefined,
"popupHeight":undefined
},
initConsole:function(){
/*
* initialize the console - user defined values are not available within this method because FBLite is not yet initialized
*/
var command;
try{
if((!window.console || (window.console && !window.console.firebug)) || (firebug.env.override && !(/Firefox\/3/i.test(navigator.userAgent)))){
window.console = { "provider":"Firebug Lite" };
for(command in firebug.d.console.cmd){
window.console[command] = firebug.lib.util.Curry(firebug.d.console.run,window,command);
};
}
window.onerror = function(_message,_file,_line){
firebug.d.console.run('error',firebug.lib.util.String.format('{0} ({1},{2})',_message,firebug.getFileName(_file),_line));
};
} catch(e){}
},
overrideConsole:function(){
with (firebug){
env.override=true;
try{
env.extConsole=window.console;
} catch(e){}
initConsole();
}
},
restoreConsole:function(){
with(firebug){
if(env.extConsole){
env.override=false;
try{
window.console=env.extConsole;
} catch(e){}
env.extConsole=null;
}
}
},
init:function(_css){
var iconTitle = "Click here or press F12, (CTRL|CMD)+SHIFT+L or SHIFT+ENTER to show Firebug Lite. CTRL|CMD click this icon to hide it.";
with(firebug){
if(document.getElementsByTagName('html')[0].attributes.getNamedItem('debug')){
env.debug = document.getElementsByTagName('html')[0].attributes.getNamedItem('debug').nodeValue !== "false";
}
if(env.isPopup) {
env.openInPopup = false;
env.targetWindow = window.opener;
env.popupWidth = window.opener.firebug.env.popupWidth || window.opener.firebug.lib.util.GetViewport().width;
env.popupHeight = window.opener.firebug.env.popupHeight || window.opener.firebug.lib.util.GetViewport().height;
} else {
env.targetWindow = window;
env.popupWidth = env.popupWidth || lib.util.GetViewport().width;
env.popupHeight = env.popupHeight || lib.util.GetViewport().height;
}
settings.readCookie();
if(env.init || (env.detectFirebug && window.console && window.console.firebug)) {
return;
}
document.getElementsByTagName("head")[0].appendChild(
new lib.element("link").attribute.set("rel","stylesheet").attribute.set("type","text/css").attribute.set("href",env.css).element
);
if(env.override){
overrideConsole();
}
/*
* Firebug Icon
*/
el.firebugIcon = new lib.element("div").attribute.set("id","firebugIconDiv").attribute.set("title",iconTitle).attribute.set("alt",iconTitle).event.addListener("mousedown",win.iconClicked).insert(document.body);
/*
* main interface
*/
el.content = {};
el.mainiframe = new lib.element("IFRAME").attribute.set("id","FirebugIFrame").environment.addStyle({ "display":"none", "width":lib.util.GetViewport().width+"px" }).insert(document.body);
el.main = new lib.element("DIV").attribute.set("id","Firebug").environment.addStyle({ "display":"none", "width":lib.util.GetViewport().width+"px" }).insert(document.body);
if(!env.isPopup){
el.resizer = new lib.element("DIV").attribute.addClass("Resizer").event.addListener("mousedown",win.resizer.start).insert(el.main);
}
el.header = new lib.element("DIV").attribute.addClass("Header").insert(el.main);
el.left = {};
el.left.container = new lib.element("DIV").attribute.addClass("Left").insert(el.main);
el.right = {};
el.right.container = new lib.element("DIV").attribute.addClass("Right").insert(el.main);
el.main.child.add(new lib.element("DIV").attribute.addClass('Clear'));
/*
* buttons
*/
el.button = {};
el.button.container = new lib.element("DIV").attribute.addClass("ButtonContainer").insert(el.header);
el.button.logo = new lib.element("A").attribute.set("title","Firebug Lite").attribute.set("target","_blank").attribute.set("href","http://getfirebug.com/lite.html").update("&nbsp;").attribute.addClass("Button Logo").insert(el.button.container);
el.button.inspect = new lib.element("A").attribute.addClass("Button").event.addListener("click",env.targetWindow.firebug.d.inspector.toggle).update("Inspect").insert(el.button.container);
el.button.dock = new lib.element("A").attribute.addClass("Button Dock").event.addListener("click", win.dock).insert(el.button.container);
el.button.newWindow = new lib.element("A").attribute.addClass("Button NewWindow").event.addListener("click", win.newWindow).insert(el.button.container);
if(!env.isPopup){
el.button.maximize = new lib.element("A").attribute.addClass("Button Maximize").event.addListener("click",win.maximize).insert(el.button.container);
el.button.minimize = new lib.element("A").attribute.addClass("Button Minimize").event.addListener("click",win.minimize).insert(el.button.container);
el.button.close = new lib.element("A").attribute.addClass("Button Close").event.addListener("click",win.hide).insert(el.button.container);
}
if(lib.env.ie||lib.env.webkit){
el.button.container.environment.addStyle({ "paddingTop":"12px" });
}
/*
* navigation
*/
el.nav = {};
el.nav.container = new lib.element("DIV").attribute.addClass("Nav").insert(el.left.container);
el.nav.console = new lib.element("A").attribute.addClass("Tab Selected").event.addListener("click",lib.util.Curry(d.navigate,window,"console")).update("Console").insert(el.nav.container);
el.nav.html = new lib.element("A").attribute.addClass("Tab").update("HTML").event.addListener("click",lib.util.Curry(d.navigate,window,"html")).insert(el.nav.container);
el.nav.css = new lib.element("A").attribute.addClass("Tab").update("CSS").event.addListener("click",lib.util.Curry(d.navigate,window,"css")).insert(el.nav.container);
if(!env.isPopup){
el.nav.scripts = new lib.element("A").attribute.addClass("Tab").update("Script").event.addListener("click",lib.util.Curry(d.navigate,window,"scripts")).insert(el.nav.container);
}
el.nav.dom = new lib.element("A").attribute.addClass("Tab").update("DOM").event.addListener("click",lib.util.Curry(d.navigate,env.targetWindow,"dom")).insert(el.nav.container);
el.nav.xhr = new lib.element("A").attribute.addClass("Tab").update("XHR").event.addListener("click",lib.util.Curry(d.navigate,window,"xhr")).insert(el.nav.container);
el.nav.optionsdiv = new lib.element("DIV").attribute.addClass("Settings").insert(el.nav.container);
el.nav.options = new lib.element("A").attribute.addClass("Tab").update("Options&nbsp;&or;").event.addListener("click", settings.toggle).insert(el.nav.optionsdiv);
/*
* inspector
*/
el.borderInspector = new lib.element("DIV").attribute.set("id","FirebugBorderInspector").event.addListener("click",listen.inspector).insert(document.body);
el.bgInspector = new lib.element("DIV").attribute.set("id","FirebugBGInspector").insert(document.body);
/*
* console
*/
el.left.console = {};
el.left.console.container = new lib.element("DIV").attribute.addClass("Console").insert(el.left.container);
el.left.console.mlButton = new lib.element("A").attribute.addClass("MLButton").event.addListener("click",d.console.toggleML).insert(el.left.console.container);
el.left.console.monitor = new lib.element("DIV").insert(
new lib.element("DIV").attribute.addClass("Monitor").insert(el.left.console.container)
);
el.left.console.container.child.add(
new lib.element("DIV").attribute.addClass("InputArrow").update(">>>")
);
el.left.console.input = new lib.element("INPUT").attribute.set("type","text").attribute.addClass("Input").event.addListener("keydown",listen.consoleTextbox).insert(
new lib.element("DIV").attribute.addClass("InputContainer").insert(el.left.console.container)
);
el.right.console = {};
el.right.console.container = new lib.element("DIV").attribute.addClass("Console Container").insert(el.right.container);
el.right.console.mlButton = new lib.element("A").attribute.addClass("MLButton CloseML").event.addListener("click",d.console.toggleML).insert(el.right.console.container);
el.right.console.input = new lib.element("TEXTAREA").attribute.addClass("Input").insert(el.right.console.container);
el.right.console.input.event.addListener("keydown",lib.util.Curry(tab,window,el.right.console.input.element));
el.right.console.run = new lib.element("A").attribute.addClass("Button").event.addListener("click",listen.runMultiline).update("Run").insert(el.right.console.container);
el.right.console.clear = new lib.element("A").attribute.addClass("Button").event.addListener("click",lib.util.Curry(d.clean,window,el.right.console.input)).update("Clear").insert(el.right.console.container);
el.button.console = {};
el.button.console.container = new lib.element("DIV").attribute.addClass("ButtonSet").insert(el.button.container);
el.button.console.clear = new lib.element("A").attribute.addClass("Button").event.addListener("click",d.console.clear).update("Clear").insert(el.button.console.container);
/*
* html
*/
el.left.html = {};
el.left.html.container = new lib.element("DIV").attribute.addClass("HTML").insert(el.left.container);
el.right.html = {};
el.right.html.container = new lib.element("DIV").attribute.addClass("HTML Container").insert(el.right.container);
el.right.html.nav = {};
el.right.html.nav.container = new lib.element("DIV").attribute.addClass("Nav").insert(el.right.html.container);
el.right.html.nav.computedStyle = new lib.element("A").attribute.addClass("Tab Selected").event.addListener("click",lib.util.Curry(d.html.navigate,firebug,"computedStyle")).update("Computed Style").insert(el.right.html.nav.container);
el.right.html.nav.dom = new lib.element("A").attribute.addClass("Tab").event.addListener("click",lib.util.Curry(d.html.navigate,firebug,"dom")).update("DOM").insert(el.right.html.nav.container);
el.right.html.content = new lib.element("DIV").attribute.addClass("Content").insert(el.right.html.container);
el.button.html = {};
el.button.html.container = new lib.element("DIV").attribute.addClass("ButtonSet HTML").insert(el.button.container);
/*
* css
*/
el.left.css = {};
el.left.css.container = new lib.element("DIV").attribute.addClass("CSS").insert(el.left.container);
el.right.css = {};
el.right.css.container = new lib.element("DIV").attribute.addClass("CSS Container").insert(el.right.container);
el.right.css.nav = {};
el.right.css.nav.container = new lib.element("DIV").attribute.addClass("Nav").insert(el.right.css.container);
el.right.css.nav.runCSS = new lib.element("A").attribute.addClass("Tab Selected").update("Run CSS").insert(el.right.css.nav.container);
el.right.css.mlButton = new lib.element("A").attribute.addClass("MLButton CloseML").event.addListener("click",d.console.toggleML).insert(el.right.css.container);
el.right.css.input = new lib.element("TEXTAREA").attribute.addClass("Input").insert(el.right.css.container);
el.right.css.input.event.addListener("keydown",lib.util.Curry(firebug.tab,window,el.right.css.input.element));
el.right.css.run = new lib.element("A").attribute.addClass("Button").event.addListener("click",listen.runCSS).update("Run").insert(el.right.css.container);
el.right.css.clear = new lib.element("A").attribute.addClass("Button").event.addListener("click",lib.util.Curry(d.clean,window,el.right.css.input)).update("Clear").insert(el.right.css.container);
el.button.css = {};
el.button.css.container = new lib.element("DIV").attribute.addClass("ButtonSet CSS").insert(el.button.container);
el.button.css.selectbox = new lib.element("SELECT").event.addListener("change",listen.cssSelectbox).insert(el.button.css.container);
/*
* scripts
*/
el.left.scripts = {};
el.left.scripts.container = new lib.element("DIV").attribute.addClass("Scripts").insert(el.left.container);
el.right.scripts = {};
el.right.scripts.container = new lib.element("DIV").attribute.addClass("Scripts Container").insert(el.right.container);
el.button.scripts = {};
el.button.scripts.container = new lib.element("DIV").attribute.addClass("ButtonSet Scripts").insert(el.button.container);
el.button.scripts.selectbox = new lib.element("SELECT").event.addListener("change",listen.scriptsSelectbox).insert(el.button.scripts.container);
el.button.scripts.lineNumbers = new lib.element("A").attribute.addClass("Button").event.addListener("click",d.scripts.toggleLineNumbers).update("Show Line Numbers").insert(el.button.scripts.container);
/*
* dom
*/
el.left.dom = {};
el.left.dom.container = new lib.element("DIV").attribute.addClass("DOM").insert(el.left.container);
el.right.dom = {};
el.right.dom.container = new lib.element("DIV").attribute.addClass("DOM Container").insert(el.right.container);
el.button.dom = {};
el.button.dom.container = new lib.element("DIV").attribute.addClass("ButtonSet DOM").insert(el.button.container);
el.button.dom.label = new lib.element("LABEL").update("Object Path:").insert(el.button.dom.container);
el.button.dom.textbox = new lib.element("INPUT").event.addListener("keydown",listen.domTextbox).update(env.isPopup?"window.opener":"window").insert(el.button.dom.container);
/*
* str
*/
el.left.str = {};
el.left.str.container = new lib.element("DIV").attribute.addClass("STR").insert(el.left.container);
el.right.str = {};
el.right.str.container = new lib.element("DIV").attribute.addClass("STR").insert(el.left.container);
el.button.str = {};
el.button.str.container = new lib.element("DIV").attribute.addClass("ButtonSet XHR").insert(el.button.container);
el.button.str.watch = new lib.element("A").attribute.addClass("Button").event.addListener("click",lib.util.Curry(d.navigate,window,"xhr")).update("Back").insert(el.button.str.container);
/*
* xhr
*/
el.left.xhr = {};
el.left.xhr.container = new lib.element("DIV").attribute.addClass("XHR").insert(el.left.container);
el.right.xhr = {};
el.right.xhr.container = new lib.element("DIV").attribute.addClass("XHR").insert(el.left.container);
el.button.xhr = {};
el.button.xhr.container = new lib.element("DIV").attribute.addClass("ButtonSet XHR").insert(el.button.container);
el.button.xhr.label = new lib.element("LABEL").update("XHR Path:").insert(el.button.xhr.container);
el.button.xhr.textbox = new lib.element("INPUT").event.addListener("keydown",listen.xhrTextbox).insert(el.button.xhr.container);
el.button.xhr.watch = new lib.element("A").attribute.addClass("Button").event.addListener("click",listen.addXhrObject).update("Watch").insert(el.button.xhr.container);
/*
* settings
*/
el.settings = {};
el.settings.container = new lib.element("DIV").child.add(
new lib.element("DIV").attribute.addClass("Header").child.add(
new lib.element().attribute.addClass("Title").update('Firebug Lite Settings')
)
).attribute.addClass("SettingsDiv").insert(el.main);
el.settings.content = new lib.element("DIV").attribute.addClass("Content").insert(el.settings.container);
el.settings.progressDiv = new lib.element("DIV").attribute.addClass("ProgressDiv").insert(el.settings.content);
el.settings.progress = new lib.element("DIV").attribute.addClass("Progress").insert(el.settings.progressDiv);
el.settings.cbxDebug = new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);
el.settings.content.child.add(document.createTextNode("Start visible"));
new lib.element("BR").insert(el.settings.content);
el.settings.cbxDetectFirebug = new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);
el.settings.content.child.add(document.createTextNode("Hide when Firebug active"));
new lib.element("BR").insert(el.settings.content);
el.settings.cbxHideDOMFunctions = new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);
el.settings.content.child.add(document.createTextNode("Hide DOM functions"));
new lib.element("BR").insert(el.settings.content);
el.settings.cbxOverride = new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);
el.settings.content.child.add(document.createTextNode("Override window.console"));
new lib.element("BR").insert(el.settings.content);
el.settings.cbxShowIcon = new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);
el.settings.content.child.add(document.createTextNode("Show icon when hidden"));
new lib.element("BR").insert(el.settings.content);
el.settings.cbxOpenInPopup = new lib.element("INPUT").attribute.set("type","checkbox").attribute.addClass("SettingsCBX").insert(el.settings.content);
el.settings.content.child.add(document.createTextNode("Open in popup"));
el.settings.buttonDiv = new lib.element("DIV").insert(el.settings.content);
el.settings.buttonLeftDiv = new lib.element("DIV").attribute.addClass("ButtonsLeft").insert(el.settings.buttonDiv);
el.settings.resetButton = new lib.element("INPUT").attribute.set("type","button").update("Reset").event.addListener("click",settings.reset).insert(el.settings.buttonLeftDiv);
el.settings.buttonRightDiv = new lib.element("DIV").attribute.addClass("ButtonsRight").insert(el.settings.buttonDiv);
el.settings.cancelButton = new lib.element("INPUT").attribute.set("type","button").update("Cancel").event.addListener("click",settings.hide).insert(el.settings.buttonRightDiv);
el.settings.buttonRightDiv.child.add(document.createTextNode(" "));
el.settings.saveButton = new lib.element("INPUT").attribute.set("type","button").update("Save").event.addListener("click",settings.saveClicked).insert(el.settings.buttonRightDiv);
lib.util.AddEvent(document,"mousemove",listen.mouse)("mousemove",win.resizer.resize)("mouseup",win.resizer.stop)("keydown",listen.keyboard);
env.init = true;
for(var i=0, len=d.console.cache.length; i<len; i++){
var item = d.console.cache[i];
d.console.cmd[item.command].apply(window,item.arg);
};
if(lib.env.ie6){
window.onscroll = lib.util.Curry(win.setVerticalPosition,window,null);
var buttons = [
el.button.inspect,
el.button.close,
el.button.inspect,
el.button.console.clear,
el.right.console.run,
el.right.console.clear,
el.right.css.run,
el.right.css.clear
];
for(var i=0, len=buttons.length; i<len; i++)
buttons[i].attribute.set("href","#");
win.refreshSize();
}
if(env.showIconWhenHidden) {
if(!env.popupWin) {
el.firebugIcon.environment.addStyle({ "display": env.debug&&'none'||'block' });
}
}
lib.util.AddEvent(window, "unload", win.unload);
if (env.isPopup) {
env.height=lib.util.GetViewport().height;
lib.util.AddEvent(window, "resize", win.fitToPopup);
win.fitToPopup();
} else {
lib.util.AddEvent(window, "resize", win.refreshSize);
}
win.setHeight(env.height);
if(env.openInPopup&&!env.isPopup) {
win.newWindow();
} else {
el.main.environment.addStyle({ "display":env.debug&&'block'||'none' });
el.mainiframe.environment.addStyle({ "display":env.debug&&'block'||'none' });
}
}
},
inspect:function(){
return firebug.d.html.inspect.apply(window,arguments);
},
watchXHR:function(){
with(firebug){
d.xhr.addObject.apply(window,arguments);
if(env.dIndex!="xhr"){
d.navigate("xhr");
}
}
},
settings:{
isVisible:false,
show: function() {
with(firebug){
var posXY=lib.util.Element.getPosition(firebug.el.nav.options.element);
settings.refreshForm();
el.settings.container.environment.addStyle({
"display": "block",
"left": (posXY.offsetLeft-125)+"px"
});
el.settings.progressDiv.environment.addStyle({
"display": "none"
});
firebug.settings.isVisible = true;
}
},
hide: function() {
with(firebug){
firebug.el.settings.container.environment.addStyle({
"display": "none"
});
firebug.settings.isVisible = false;
}
},
toggle: function(){
with(firebug){
settings[!settings.isVisible && 'show' || 'hide']();
}
},
saveClicked: function() {
firebug.el.settings.progressDiv.environment.addStyle({
"display": "block"
});
setTimeout(firebug.settings.formToSettings,0);
},
formToSettings: function() {
var fe=firebug.env,
ofe,
elSet=firebug.el.settings,
exdate;
fe.debug=elSet.cbxDebug.element.checked;
fe.detectFirebug=elSet.cbxDetectFirebug.element.checked;
fe.hideDOMFunctions=elSet.cbxHideDOMFunctions.element.checked;
fe.override=elSet.cbxOverride.element.checked;
fe.showIconWhenHidden=elSet.cbxShowIcon.element.checked;
fe.openInPopup=elSet.cbxOpenInPopup.element.checked;
if(fe.isPopup) {
ofe=window.opener.firebug.env;
ofe.debug=fe.debug;
ofe.detectFirebug=fe.detectFirebug;
ofe.hideDOMFunctions=fe.hideDOMFunctions;
ofe.override=fe.override;
ofe.showIconWhenHidden=fe.showIconWhenHidden;
ofe.openInPopup=fe.openInPopup;
ofe.popupTop=fe.popupTop;
ofe.popupLeft=fe.popupLeft;
ofe.popupWidth=fe.popupWidth;
ofe.popupHeight=fe.popupHeight;
}
with(firebug) {
settings.writeCookie();
settings.hide();
win.refreshDOM();
}
},
reset: function() {
var exdate=new Date();
exdate.setTime(exdate.getTime()-1);
document.cookie='FBLiteSettings=;expires='+exdate.toGMTString();
location.reload(true);
},
readCookie: function() {
var i,cookieArr,valueArr,item,value;
with(firebug.env){
if(targetWindow.document.cookie.length>0) {
cookieArr=targetWindow.document.cookie.split('; ');
for(i=0;i<cookieArr.length;i++) {
if(cookieArr[i].split('=')[0]=='FBLiteSettings') {
valueArr=cookieArr[i].split('=')[1].split(',');
}
}
if(valueArr) {
for(i=0;i<valueArr.length;i++) {
item=valueArr[i].split(':')[0];
value=valueArr[i].split(':')[1];
switch(item) {
case 'debug':
debug=value=="true";
break;
case 'detectFirebug':
detectFirebug=value=="true";
break;
case 'hideDOMFunctions':
hideDOMFunctions=value=="true";
break;
case 'override':
override=value=="true";
break;
case 'showIconWhenHidden':
showIconWhenHidden=value=="true";
break;
case 'openInPopup':
openInPopup=value=="true";
break;
case 'popupTop':
popupTop=parseInt(value,10);
break;
case 'popupLeft':
popupLeft=parseInt(value,10);
break;
case 'popupWidth':
popupWidth=parseInt(value,10);
break;
case 'popupHeight':
popupHeight=parseInt(value,10);
break;
case 'height':
height=parseInt(value,10);
break;
}
}
}
}
}
},
writeCookie: function() {
var values;
with(firebug.env){
values='debug:'+debug+',';
values+='detectFirebug:'+detectFirebug+',';
values+='hideDOMFunctions:'+hideDOMFunctions+',';
values+='override:'+override+',';
values+='showIconWhenHidden:'+showIconWhenHidden+',';
values+='openInPopup:'+openInPopup+',';
if(isPopup) {
if(window.outerWidth===undefined) {
values+='popupTop:'+(window.screenTop-56)+',';
values+='popupLeft:'+(window.screenLeft-8)+',';
values+='popupWidth:'+document.body.clientWidth+',';
values+='popupHeight:'+document.body.clientHeight+',';
} else {
values+='popupTop:'+window.screenY+',';
values+='popupLeft:'+window.screenX+',';
values+='popupWidth:'+window.outerWidth+',';
values+='popupHeight:'+window.outerHeight+',';
}
} else {
values+='popupTop:'+popupTop+',';
values+='popupLeft:'+popupLeft+',';
values+='popupWidth:'+popupWidth+',';
values+='popupHeight:'+popupHeight+',';
}
values+='height:'+(parseInt(targetWindow.firebug.el.main.element.style.height.replace(/px/,''),10)-38);
exdate=new Date();
exdate.setDate(exdate.getDate()+365);
targetWindow.document.cookie='FBLiteSettings='+values+';expires='+exdate.toGMTString();
}
},
refreshForm: function() {
var fe=firebug.env,
elSet=firebug.el.settings;
elSet.cbxDebug.element.checked=fe.debug;
elSet.cbxDetectFirebug.element.checked=fe.detectFirebug;
elSet.cbxHideDOMFunctions.element.checked=fe.hideDOMFunctions;
elSet.cbxOverride.element.checked=fe.override;
elSet.cbxShowIcon.element.checked=fe.showIconWhenHidden;
elSet.cbxOpenInPopup.element.checked=fe.openInPopup;
}
},
win:{
hide:function(){
with(firebug){
el.main.environment.addStyle({
"display": "none"
});
el.mainiframe.environment.addStyle({
"display": "none"
});
if(env.showIconWhenHidden) {
el.firebugIcon.environment.addStyle({
"display": "block"
});
}
}
},
show:function(){
with(firebug){
el.main.environment.addStyle({
"display": "block"
});
el.mainiframe.environment.addStyle({
"display": "block"
});
if(env.showIconWhenHidden) {
el.firebugIcon.environment.addStyle({
"display": "none"
});
}
}
},
iconClicked:function(_event) {
with(firebug) {
if(_event.ctrlKey==true||_event.metaKey==true) {
el.firebugIcon.environment.addStyle({ "display": "none" });
env.showIconWhenHidden=false;
} else {
win.show();
}
}
},
minimize:function(){
with(firebug){
env.minimized=true;
el.main.environment.addStyle({ "height":"35px" });
el.mainiframe.environment.addStyle({ "height":"35px" });
el.button.maximize.environment.addStyle({ "display":"block" });
el.button.minimize.environment.addStyle({ "display":"none" });
win.refreshSize();
}
},
maximize:function(){
with(firebug){
env.minimized=false;
el.button.minimize.environment.addStyle({ "display":"block" });
el.button.maximize.environment.addStyle({ "display":"none" });
win.setHeight(env.height);
}
},
newWindow: function() {
var interval,scripts,script,scriptPath,
fe=firebug.env;
if (!fe.popupWin) {
scripts = document.getElementsByTagName('script');
fe.popupWin = window.open("", "_firebug",
"status=0,menubar=0,resizable=1,top="+fe.popupTop+",left="+fe.popupLeft+",width=" + fe.popupWidth +
",height=" + fe.popupHeight + ",scrollbars=0,addressbar=0,outerWidth="+fe.popupWidth+",outerHeight="+fe.popupHeight+
"toolbar=0,location=0,directories=0,dialog=0");
if(!fe.popupWin) {
alert("Firebug Lite could not open a pop-up window, most likely because of a popup blocker.\nPlease enable popups for this domain");
} else {
firebug.settings.hide();
for (i=0,len=scripts.length; i<len; i++) {
if (scripts[i].src.indexOf(fe.liteFilename) > -1) {
scriptPath = scripts[i].src;
break;
}
}
if (scriptPath) {
script = fe.popupWin.document.createElement('script'), done = false;
script.type = 'text/javascript';
script.src = scriptPath;
script[firebug.lib.env.ie?"onreadystatechange":"onload"] = function(){
if(!done && (!firebug.lib.env.ie || this.readyState == "complete" || this.readyState=="loaded")){
done = true;
if(fe.popupWin.firebug) {
with(fe.popupWin.firebug) {
env.isPopup = true;
env.css = fe.css;
init();
el.button.dock.environment.addStyle({ "display": "block"});
el.button.newWindow.environment.addStyle({ "display": "none"});
}
}
}
};
if (!done && firebug.lib.env.webkit) {
fe.popupWin.document.write('<html><head></head><body></body></html>');
interval = setInterval(function() {
if (fe.popupWin.firebug) {
clearInterval(interval);
done = true;
with(fe.popupWin.firebug) {
env.isPopup = true;
env.css = fe.css;
init();
el.button.dock.environment.addStyle({ "display": "block"});
el.button.newWindow.environment.addStyle({ "display": "none"});
}
}
}, 10);
};
if (!done) {
fe.popupWin.document.getElementsByTagName('head')[0].appendChild(script);
firebug.el.main.environment.addStyle({"display": "none"});
firebug.el.mainiframe.environment.addStyle({"display": "none"});
}
} else {
alert("Unable to detect the following script \"" + fe.liteFilename +
"\" ... if the script has been renamed then please set the value of firebug.env.liteFilename to reflect this change");
fe.popupWin.close();
fe.popupWin=null;
}
}
}
},
dock: function() {
with(opener.firebug) {
env.popupWin = null;
el.main.environment.addStyle({
"display": "block"
});
el.mainiframe.environment.addStyle({
"display": "block"
});
settings.readCookie();
window.close();
};
},
unload: function() {
with(firebug){
if(env.isPopup) {
win.dock();
} else if(env.popupWin) {
env.popupWin.close();
}
}
},
fitToPopup: function() {
with(firebug) {
var viewport = lib.util.GetViewport(window);
win.setHeight((window.innerHeight||viewport.height) - 38);
el.main.environment.addStyle({
"width": (viewport.width) + "px"
});
el.mainiframe.environment.addStyle({
"width": (viewport.width) + "px"
});
}
},
resizer:{
y:[], enabled:false,
start:function(_event){
with(firebug){
if(env.minimized)return;
win.resizer.y=[el.main.element.offsetHeight,_event.clientY];
if(lib.env.ie6){
win.resizer.y[3]=parseInt(el.main.environment.getPosition().top);
}
win.resizer.enabled=true;
}
},
resize:function(_event){
with(firebug){
if(!win.resizer.enabled)return;
win.resizer.y[2]=(win.resizer.y[0]+(win.resizer.y[1]-_event.clientY));
el.main.environment.addStyle({ "height":win.resizer.y[2]+"px" });
el.mainiframe.environment.addStyle({ "height":win.resizer.y[2]+"px" });
if(lib.env.ie6){
el.main.environment.addStyle({ "top":win.resizer.y[3]-(win.resizer.y[1]-_event.clientY)+"px" });
el.mainiframe.environment.addStyle({ "top":win.resizer.y[3]-(win.resizer.y[1]-_event.clientY)+"px" });
}
}
},
stop:function(_event){
with(firebug){
if(win.resizer.enabled){
win.resizer.enabled=false;
win.setHeight(win.resizer.y[2]-35);
}
}
}
},
setHeight:function(_height){
with(firebug){
env.height=_height;
el.left.container.environment.addStyle({ "height":_height+"px" });
el.right.container.environment.addStyle({ "height":_height+"px" });
el.main.environment.addStyle({ "height":_height+38+"px" });
el.mainiframe.environment.addStyle({ "height":_height+38+"px" });
win.refreshSize();
// console
el.left.console.monitor.element.parentNode.style.height=_height-47+"px";
el.left.console.mlButton.environment.addStyle({ "top":_height+19+"px" });
el.right.console.mlButton.environment.addStyle({ "top":_height+19+"px" });
el.right.console.input.environment.addStyle({ "height":_height-29+"px" });
// html
el.left.html.container.environment.addStyle({"height":_height-23+"px"});
el.right.html.content.environment.addStyle({"height":_height-23+"px"});
// css
el.left.css.container.environment.addStyle({"height":_height-33+"px"});
el.right.css.input.environment.addStyle({ "height":_height-55+"px" });
// script
el.left.scripts.container.environment.addStyle({"height":_height-23+"px"});
// dom
el.left.dom.container.environment.addStyle({"height":_height-31+"px"});
// xhr
el.left.xhr.container.environment.addStyle({"height":_height-32+"px"});
// string
el.left.str.container.environment.addStyle({"height":_height-32+"px"});
}
},
refreshDOM:function(){
with(firebug){
d.dom.open(eval(el.button.dom.textbox.environment.getElement().value),el.left.dom.container);
if(d.html.nIndex=="dom"){
firebug.d.html.navigate("dom")
}
}
},
refreshSize:function(){
with(firebug){
if(!env.init)
return;
var dim = lib.util.GetViewport();
el.main.environment.addStyle({ "width":dim.width+"px"});
el.mainiframe.environment.addStyle({ "width":dim.width+"px"});
if(lib.env.ie6)
win.setVerticalPosition(dim);
}
},
setVerticalPosition:function(_dim,_event){
with(firebug){
var dim = _dim||lib.util.GetViewport();
el.main.environment.addStyle({ "top":dim.height-el.main.environment.getSize().offsetHeight+Math.max(document.documentElement.scrollTop,document.body.scrollTop)+"px" });
el.mainiframe.environment.addStyle({ "top":dim.height-el.main.environment.getSize().offsetHeight+Math.max(document.documentElement.scrollTop,document.body.scrollTop)+"px" });
}
}
},
d: {
clean:function(_element){
with(firebug){
_element.update("");
}
},
console:{
addLine:function(){
with (firebug) {
return new lib.element("DIV").attribute.addClass("Row").insert(el.left.console.monitor);
}
},
cache:[],
clear:function(){
with(firebug){
d.clean(el.left.console.monitor);
d.console.cache = [];
}
},
formatArgs:function(){
with(firebug){
var content = [];
for(var i=0, len=arguments.length; i<len; i++){
content.push( d.highlight(arguments[i],false,false,true) );
}
return content.join(" ");
}
},
history:[], historyIndex:0,
openObject:function(_index){
with (firebug) {
d.dom.open(d.console.cache[_index], el.left.dom.container, lib.env.ie);
d.navigate("dom");
}
},
print: function(_cmd,_text){
with (firebug){
d.console.addLine().attribute.addClass("Arrow").update(">>> "+_cmd);
d.console.addLine().update(d.highlight(_text,false,false,true));
d.console.scroll();
}
},
printException: function(_exception){
with(firebug){
var message = _exception.description||_exception.message||_exception;
if(_exception.fileName){
message+=' ('+(_exception.name&&(_exception.name+', ')||'')+getFileName(_exception.fileName)+', '+_exception.lineNumber+')';
}
d.console.addLine().attribute.addClass("Error").update("<strong>Error: </strong>"+message,true);
}
},
eval:function(_cmd){
var result;
with(firebug){
if(_cmd.length==0)
return;
el.left.console.input.environment.getElement().value = "";
d.console.historyIndex = d.console.history.push(_cmd);
try {
if(_cmd==='console.firebug') {
d.console.addLine().attribute.addClass("Arrow").update(firebug.version);
} else {
result = eval.call(window,_cmd);
d.console.print(_cmd,result);
}
} catch(e){
d.console.addLine().attribute.addClass("Arrow").update(">>> "+_cmd);
d.console.printException(e);
}
d.console.scroll();
}
},
scroll:function(){
with(firebug){
el.left.console.monitor.environment.getElement().parentNode.scrollTop = Math.abs(el.left.console.monitor.environment.getSize().offsetHeight-(el.left.console.monitor.element.parentNode.offsetHeight-11));
}
},
run:function(_command){
with(firebug){
if(!env.init){
d.console.cache.push({ "command":_command, "arg":Array.prototype.slice.call(arguments,1) });
} else {
d.console.cmd[_command].apply(window,Array.prototype.slice.call(arguments,1));
}
}
},
toggleML:function(){
with(firebug){
var open = !env.ml;
env.ml = !env.ml;
d.navigateRightColumn("console",open);
el[open?"left":"right"].console.mlButton.environment.addStyle({ display:"none" });
el[!open?"left":"right"].console.mlButton.environment.addStyle({ display:"block" });
el.left.console.mlButton.attribute[(open?"add":"remove")+"Class"]("CloseML");
}
},
countMap:{}, timeMap: {},
cmd:{
log: function(_value){
with(firebug){
var args = d.console.formatArgs.apply(window,arguments);
d.console.addLine().attribute.addClass("Log").update(args);
d.console.scroll();
}
},
warn: function(_value){
with(firebug){
var args = d.console.formatArgs.apply(window,arguments);
d.console.addLine().attribute.addClass("Warn").update(args);
d.console.scroll();
}
},
info: function(_value){
with(firebug){
var args = d.console.formatArgs.apply(window,arguments);
d.console.addLine().attribute.addClass("Info").update(args);
d.console.scroll();
}
},
debug: function(_value){
with(firebug){
var args = d.console.formatArgs.apply(window,arguments);
d.console.addLine().attribute.addClass("Debug").update(args);
d.console.scroll();
}
},
error: function(_value){
with(firebug){
var args = d.console.formatArgs.apply(window,arguments);
d.console.addLine().attribute.addClass("Error").update(args);
d.console.scroll();
}
},
trace: function(_value){
with(firebug){
var stackAmt = 3, f = arguments.caller, isArray = lib.util.IsArray(f); //function that called trace
if((!isArray&&f)||(isArray&&f.length>0)){
d.console.addLine().attribute.addClass("Arrow").update(">>> console.trace(stack)");
for(var i=0;i<stackAmt;i++){
var func = f.toString(), args = f.arguments;
d.dom.open({"function":func, "arguments":args},d.console.addLine());
f = f.caller;
}
}
}
},
dir:function(_value){
with(firebug){
d.console.addLine().attribute.addClass("Arrow").update(">>> console.dir("+_value+")");
d.dom.open(_value,d.console.addLine());
}
},
dirxml: function(){
with(firebug){
d.console.cmd.log.apply(this, arguments);
}
},
time: function(_name){
with(firebug){
d.console.timeMap[_name] = new Date().getTime();
}
},
timeEnd: function(_name){
with(firebug){
if(_name in d.console.timeMap){
var delta = new Date().getTime() - d.console.timeMap[_name],
args = d.console.formatArgs.apply(window,[_name+":", delta+"ms"]);
d.console.addLine().attribute.addClass("log").update(args);
delete d.console.timeMap[_name];
}
}
},
count: function(_name){
with(firebug){
if(!d.console.countMap[_name])
d.console.countMap[_name] = 0;
d.console.countMap[_name]++;
d.console.cmd.log.apply(window, [_name, d.console.countMap[_name]]);
}
},
group:function(){
with(firebug){
d.console.cmd.log.apply(this, ["console.group is not supported"]);
}
},
groupEnd:function(){
with(firebug){
d.console.cmd.log.apply(this, ["console.groupEnd is not supported"]);
}
},
profile:function(){
with(firebug){
d.console.cmd.log.apply(this, ["console.profile is not supported"]);
}
},
profileEnd:function(){
with(firebug){
d.console.cmd.log.apply(this, ["console.profileEnd is not supported"]);
}
}
}
},
css:{
index:-1,
open:function(_index){
with (firebug) {
var item = env.targetWindow.document.styleSheets[_index],
uri = item.href;
try {
var rules = item[lib.env.ie ? "rules" : "cssRules"], str = "";
for (var i=0; i<rules.length; i++) {
var item = rules[i];
var selector = item.selectorText;
var cssText = lib.env.ie?item.style.cssText:item.cssText.match(/\{(.*)\}/)[1];
str+=d.css.printRule(selector, cssText.split(";"), el.left.css.container);
}
} catch(e) {
str="<em>Access to restricted URI denied</em>";
}
el.left.css.container.update(str);
}
},
printRule:function(_selector,_css,_layer){
with(firebug){
var str = "<div class='Selector'>"+_selector+" {</div>";
for(var i=0,len=_css.length; i<len; i++){
var item = _css[i];
str += "<div class='CSSText'>"+item.replace(/(.+\:)(.+)/,"<span class='CSSProperty'>$1</span><span class='CSSValue'>$2;</span>")+"</div>";
}
str+="<div class='Selector'>}</div>";
return str;
}
},
refresh:function(){
with(firebug){
el.button.css.selectbox.update("");
var collection = env.targetWindow.document.styleSheets;
for(var i=0,len=collection.length; i<len; i++){
var uri = getFileName(collection[i].href);
d.css.index=d.css.index<0?i:d.css.index;
el.button.css.selectbox.child.add(
new lib.element("OPTION").attribute.set("value",i).update(uri)
)
};
d.css.open(d.css.index);
}
}
},
dom: {
open: function(_object,_layer){
with (firebug) {
_layer.clean();
var container = new lib.element("DIV").attribute.addClass("DOMContent").insert(_layer);
d.dom.print(_object, container);
}
},
print:function(_object,_parent, _inTree){
with (firebug) {
var obj = _object || window, parentElement = _parent;
parentElement.update("");
if(parentElement.opened&&parentElement!=el.left.dom.container){
parentElement.environment.getParent().lib.child.get()[0].lib.child.get()[0].lib.attribute.removeClass("Opened");
parentElement.opened = false;
parentElement.environment.addStyle({ "display":"none" });
return;
}
if(_inTree)
parentElement.environment.getParent().lib.child.get()[0].lib.child.get()[0].lib.attribute.addClass("Opened");
parentElement.opened = true;
for (var key in obj) {
try {
if (env.hideDOMFunctions && typeof(obj[key]) == "function") continue;
var value = obj[key], property = key, container = new lib.element("DIV").attribute.addClass("DOMRow").insert(parentElement),
left = new lib.element("DIV").attribute.addClass("DOMRowLeft").insert(container), right = new lib.element("DIV").attribute.addClass("DOMRowRight").insert(container);
container.child.add(
new lib.element("DIV").attribute.addClass('Clear')
);
var link = new lib.element("A").attribute.addClass(
typeof value=="object"&&Boolean(value)?"Property Object":"Property"
).update(property).insert(left);
right.update(d.highlight(value,false,true));
var subContainer = new lib.element("DIV").attribute.addClass("DOMRowSubContainer").insert(container);
if(typeof value!="object"||Boolean(value)==false)
continue;
link.event.addListener("click",lib.util.Curry(d.dom.print,window,value, subContainer, true));
}catch(e){
}
}
parentElement.environment.addStyle({ "display":"block" });
}
}
},
highlight:function(_value,_inObject,_inArray,_link){
with(firebug){
var isArray = false, isHash, isElement = false, vtype=typeof _value, result=[];
if(vtype=="object"){
if(Object.prototype.toString.call(_value) === "[object Date]"){
vtype = "date";
} else if(Object.prototype.toString.call(_value) === "[object String]"){
vtype = "string";
} else if(Object.prototype.toString.call(_value) === "[object Boolean]"){
vtype = "boolean";
} else if(Object.prototype.toString.call(_value) === "[object RegExp]"){
vtype = "regexp";
}
}
try {
isArray = lib.util.IsArray(_value);
isHash = lib.util.IsHash(_value);
isElement = _value!=undefined&&Boolean(_value.nodeName)&&Boolean(_value.nodeType);
// number, string, boolean, null, function
if(_value==null||vtype=="number"||vtype=="string"||vtype=="boolean"||vtype=="function"||vtype=="regexp"||vtype=="date"){
if(_value==null){
result.push("<span class='Null'>null</span>");
}else if (vtype=="regexp") {
result.push("<span class='Maroon'>" + _value + "</span>");
}else if (vtype=="date") {
result.push("<span class='DarkBlue'>'" + _value + "'</span>");
} else if (vtype=="boolean"||vtype=="number") {
result.push("<span class='DarkBlue'>" + _value + "</span>");
} else if(vtype=="function"){
result.push("<span class='"+(_inObject?"Italic Gray":"Green")+"'>function()</span>");
} else {
result.push("<span class='Red'>\""+( !_inObject&&!_inArray?_value : _value.substring(0,35)+(_value.length>35?" ...":"") ).replace(/\n/g,"\\n").replace(/\s/g,"&nbsp;").replace(/>/g,"&#62;").replace(/</g,"&#60;")+"\"</span>");
}
}
// element
else if(isElement){
if(_value.nodeType==3)
result.push(d.highlight(_value.nodeValue));
else if(_inObject){
result.push("<span class='Gray Italic'>"+_value.nodeName.toLowerCase()+"</span>");
} else {
result.push("<span class='Blue"+ ( !_link?"'":" ObjectLink' onmouseover='this.className=this.className.replace(\"ObjectLink\",\"ObjectLinkHover\")' onmouseout='this.className=this.className.replace(\"ObjectLinkHover\",\"ObjectLink\")' onclick='firebug.d.html.inspect(firebug.d.console.cache[" +( d.console.cache.push( _value ) -1 )+"])'" ) + "'>");
if(_inArray){
result.push(_value.nodeName.toLowerCase());
if(_value.getAttribute){
if(_value.getAttribute&&_value.getAttribute("id"))
result.push("<span class='DarkBlue'>#"+_value.getAttribute("id")+"</span>");
var elClass = _value.getAttribute(lib.env.ie&&!lib.env.ie8?"className":"class")||"";
result.push(!elClass?"":"<span class='Red'>."+elClass.split(" ")[0]+"</span>");
}
result.push("</span>");
} else {
result.push("<span class='DarkBlue'>&#60;<span class='Blue TagName'>"+ _value.nodeName.toLowerCase());
if(_value.attributes){
for(var i=0,len=_value.attributes.length; i<len; i++){
var item = _value.attributes[i];
if(!lib.env.ie||item.nodeValue)
result.push(" <span class='DarkBlue'>"+item.nodeName+"=\"<span class='Red'>"+item.nodeValue+"</span>\"</span>");
}
}
result.push("</span>&#62;</span>");
}
}
}
// array, hash
else if(isArray||isHash){
if(isArray){
if(_inObject){
result.push("<span class='Gray Italic'>["+_value.length+"]</span>");
} else {
result.push("<span class='Strong'>[ ");
for(var i=0,len=_value.length; i<len; i++){
if((_inObject||_inArray)&&i>3){
result.push(", <span class='Strong Gray'>"+(len-4)+" More...</span>");
break;
}
result.push( (i > 0 ? ", " : "") + d.highlight(_value[i], false, true, true) );
}
result.push(" ]</span>");
}
} else if(_inObject){
result.push("<span class='Gray Italic'>Object</span>");
} else {
result.push("<span class='Strong Green"+ ( !_link?"'":" ObjectLink' onmouseover='this.className=this.className.replace(\"ObjectLink\",\"ObjectLinkHover\")' onmouseout='this.className=this.className.replace(\"ObjectLinkHover\",\"ObjectLink\")' onclick='firebug.d.console.openObject(" +( d.console.cache.push( _value ) -1 )+")'" ) + ">Object");
var i=0;
for(var key in _value){
var value = _value[key];
if((_inObject||_inArray)&&i>3){
result.push(" <span class='Strong Gray'>More...</span>");
break;
}
result.push(" "+key+"="+d.highlight(value,true));
i++;
}
result.push("</span>");
}
} else {
result.push(["<span class'Gray Italic'>"+_value+"</span>"]);
}
} catch(e){
result.push("..");
}
return result.join("");
}
},
html:{
nIndex:"computedStyle",
current:null,
highlight:function(_element,_clear,_event){
with(firebug){
if(_element.firebugElement){
return;
}
if(_clear){
env.targetWindow.firebug.el.bgInspector.environment.addStyle({ "display":"none" });
return;
}
d.inspector.inspect(_element,true);
}
},
inspect:function(_element){
var map = [],
parentLayer,
t,
tagName,
parent = _element;
while (parent) {
map.push(parent);
if (parent == firebug.env.targetWindow.document.body) break;
parent = parent.parentNode;
}
map = map.reverse();
with(firebug) {
if (env.dIndex != "html") {
env.targetWindow.firebug.d.navigate("html");
}
env.targetWindow.firebug.d.inspector.toggle(false);
for (t = 0; t < el.left.html.container.child.get().length; t++) {
if (el.left.html.container.child.get()[t].childNodes[0].childNodes[1].childNodes[0].childNodes[0]) {
if (el.left.html.container.child.get()[t].childNodes[0].childNodes[1].childNodes[0].childNodes[0].innerText) {
tagName = el.left.html.container.child.get()[t].childNodes[0].childNodes[1].childNodes[0].childNodes[0].innerText;
} else {
tagName = el.left.html.container.child.get()[t].childNodes[0].childNodes[1].childNodes[0].childNodes[0].textContent;
}
if (/<body/i.test(tagName)) {
parentLayer = el.left.html.container.child.get()[t].childNodes[1].lib;
break;
}
}
}
if (!parentLayer) {
parentLayer = el.left.html.container.child.get()[3].childNodes[1].lib;
}
for (t = 0, len = map.length; map[t]; t++) {
if (t == len - 1) {
var link = parentLayer.environment.getElement().previousSibling.lib;
link.attribute.addClass("Selected");
if (d.html.current) {
d.html.current[1].attribute.removeClass("Selected");
}
d.html.current = [_element, link];
return;
}
parentLayer = d.html.openHtmlTree(map[t], parentLayer, map[t + 1]);
}
}
},
navigate:function(_index,_element){
with(firebug){
el.right.html.nav[d.html.nIndex].attribute.removeClass("Selected");
el.right.html.nav[_index].attribute.addClass("Selected");
d.html.nIndex = _index;
d.html.openProperties();
}
},
openHtmlTree:function(_element,_parent,_returnParentElementByElement,_event){
with(firebug){
var element = _element || env.targetWindow.document.documentElement,
parent = _parent || el.left.html.container,
returnParentEl = _returnParentElementByElement || null,
returnParentVal = null,
len = element.childNodes.length,
nodeLink;
if(parent!=el.left.html.container){
nodeLink = parent.environment.getParent().lib.child.get()[0].lib;
if (d.html.current) {
d.html.current[1].attribute.removeClass("Selected");
}
nodeLink.attribute.addClass("Selected");
d.html.current = [_element,nodeLink];
d.html.openProperties();
};
if(element.childNodes&&(len==0||(len==1&&element.childNodes[0].nodeType==3)))return;
parent.clean();
if(parent.opened&&Boolean(_returnParentElementByElement)==false){
parent.opened = false;
parent.element.previousSibling.lib.attribute.removeClass("Open");
parent.element.lib.attribute.removeClass("OpenSubContainer");
return;
};
if (parent != el.left.html.container) {
parent.element.previousSibling.lib.attribute.addClass("Open");
parent.element.lib.attribute.addClass("OpenSubContainer");
parent.opened = true;
};
if(element==document.documentElement){
new lib.element("A").attribute.addClass("Block").update("<span class='DarkBlue'>&#60;<span class='Blue'>html</span>&#62;").insert(parent);
};
for(var i=0; i<=len; i++){
if(i==len){
new lib.element("A").attribute.addClass("Block").update("<span class='DarkBlue'>&#60;/<span class='Blue'>"+element.nodeName.toLowerCase()+"</span>&#62;").insert(container);
break;
}
var item = element.childNodes[i];
if (item.nodeType != 3){
var container = new lib.element().attribute.addClass("Block").insert(parent),
link = new lib.element("A").attribute.addClass("Link").insert(container),
spacer = new lib.element("SPAN").attribute.addClass("Spacer").update("&nbsp;").insert(link),
html = new lib.element("SPAN").attribute.addClass("Content").update(d.highlight(item)).insert(link),
subContainer = new lib.element("DIV").attribute.addClass("SubContainer").insert(container),
view = lib.util.Element.getView(item);
link.event.addListener("click", lib.util.Curry(d.html.openHtmlTree, window, item, subContainer, false));
link.event.addListener("mouseover", lib.util.Curry(d.html.highlight, window, item, false));
link.event.addListener("mouseout", lib.util.Curry(d.html.highlight, window, item, true));
returnParentVal = returnParentEl == item ? subContainer : returnParentVal;
if(d.html.current==null&&item==document.body){
link.attribute.addClass("Selected");
d.html.current = [item,link];
d.html.openHtmlTree(item,subContainer);
}
if(element.nodeName!="HEAD"&&element!=document.documentElement&&(view.visibility=="hidden"||view.display=="none")){
container.attribute.addClass("Unvisible");
};
if (item.childNodes){
var childLen = item.childNodes.length;
if (childLen == 1 && item.childNodes[0].nodeType == 3) {
html.child.add(document.createTextNode(item.childNodes[0].nodeValue.substring(0, 50)));
html.child.add(document.createTextNode("</"));
html.child.add(new lib.element("span").attribute.addClass("Blue").update(item.nodeName.toLowerCase()).environment.getElement());
html.child.add(document.createTextNode(">"));
continue;
}
else
if (childLen > 0) {
link.attribute.addClass("Parent");
}
}
}
};
return returnParentVal;
}
},
openProperties:function(){
with(firebug){
var index = d.html.nIndex;
var node = d.html.current[0];
d.clean(el.right.html.content);
var str = "";
switch(index){
case "computedStyle":
var property = ["opacity","filter","azimuth","background","backgroundAttachment","backgroundColor","backgroundImage","backgroundPosition","backgroundRepeat","border","borderCollapse","borderColor","borderSpacing","borderStyle","borderTop","borderRight","borderBottom","borderLeft","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","borderTopStyle","borderRightStyle","borderBottomStyle","borderLeftStyle","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderWidth","bottom","captionSide","clear","clip","color","content","counterIncrement","counterReset","cue","cueAfter","cueBefore","cursor","direction","display","elevation","emptyCells","cssFloat","font","fontFamily","fontSize","fontSizeAdjust","fontStretch","fontStyle","fontVariant","fontWeight","height","left","letterSpacing","lineHeight","listStyle","listStyleImage","listStylePosition","listStyleType","margin","marginTop","marginRight","marginBottom","marginLeft","markerOffset","marks","maxHeight","maxWidth","minHeight","minWidth","orphans","outline","outlineColor","outlineStyle","outlineWidth","overflow","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","page","pageBreakAfter","pageBreakBefore","pageBreakInside","pause","pauseAfter","pauseBefore","pitch","pitchRange","playDuring","position","quotes","richness","right","size","speak","speakHeader","speakNumeral","speakPunctuation","speechRate","stress","tableLayout","textAlign","textDecoration","textIndent","textShadow","textTransform","top","unicodeBidi","verticalAlign","visibility","voiceFamily","volume","whiteSpace","widows","width","wordSpacing","zIndex"].sort();
var view = document.defaultView?document.defaultView.getComputedStyle(node,null):node.currentStyle;
for(var i=0,len=property.length; i<len; i++){
var item = property[i];
if(!view[item])continue;
str+="<div class='CSSItem'><div class='CSSProperty'>"+item+"</div><div class='CSSValue'>"+d.highlight(view[item])+"</div></div>";
}
el.right.html.content.update(str);
break;
case "dom":
d.dom.open(node,el.right.html.content,lib.env.ie);
break;
}
}
}
},
inspector:{
enabled:false,
el:null,
inspect:function(_element,_bgInspector){
with(firebug){
var pos = env.targetWindow.firebug.lib.util.Element.getPosition(_element);
env.targetWindow.firebug.el[_bgInspector&&"bgInspector"||"borderInspector"].environment.addStyle({
"width":_element.offsetWidth+"px", "height":_element.offsetHeight+"px",
"top":pos.offsetTop-(_bgInspector?0:2)+"px", "left":pos.offsetLeft-(_bgInspector?0:2)+"px",
"display":"block"
});
9
if(!_bgInspector){
d.inspector.el = _element;
}
};
},
toggle:function(_absoluteValue,_event){
with (firebug) {
if(_absoluteValue==d.inspector.enabled)
return;
d.inspector.enabled = _absoluteValue!=undefined&&!_absoluteValue.clientX?_absoluteValue:!d.inspector.enabled;
el.button.inspect.attribute[(d.inspector.enabled ? "add" : "remove") + "Class"]("Enabled");
if(d.inspector.enabled==false){
el.borderInspector.environment.addStyle({ "display":"none" });
d.inspector.el = null;
} else if(lib.env.dIndex!="html") {
if (env.popupWin) {
env.popupWin.firebug.d.navigate("html");
} else {
d.navigate("html");
}
}
}
}
},
scripts:{
index:-1,
lineNumbers:false,
open:function(_index){
with(firebug){
d.scripts.index = _index;
el.left.scripts.container.update("");
var script = document.getElementsByTagName("script")[_index],uri = script.src||document.location.href,source;
try {
if(uri!=document.location.href){
source = env.cache[uri]||lib.xhr.get(uri).responseText;
env.cache[uri] = source;
} else {
source = script.innerHTML;
}
source = source.replace(/<|>/g,function(_ch){
return ({"<":"&#60;",">":"&#62;"})[_ch];
});
if(!d.scripts.lineNumbers)
el.left.scripts.container.child.add(
new lib.element("DIV").attribute.addClass("CodeContainer").update(source)
);
else {
source = source.split("<br />");
for (var i = 0; i < source.length; i++) {
el.left.scripts.container.child.add(new lib.element("DIV").child.add(new lib.element("DIV").attribute.addClass("LineNumber").update(i + 1), new lib.element("DIV").attribute.addClass("Code").update("&nbsp;" + source[i]), new lib.element("DIV").attribute.addClass('Clear')));
};
};
} catch(e){
el.left.scripts.container.child.add(
new lib.element("DIV").attribute.addClass("CodeContainer").update("<em>Access to restricted URI denied</em>")
);
}
}
},
toggleLineNumbers:function(){
with(firebug){
d.scripts.lineNumbers = !d.scripts.lineNumbers;
el.button.scripts.lineNumbers.attribute[(d.scripts.lineNumbers ? "add" : "remove") + "Class"]("Enabled");
d.scripts.open( d.scripts.index );
}
},
refresh:function(){
with(firebug){
el.button.scripts.selectbox.clean();
var collection = env.targetWindow.document.getElementsByTagName("script");
for(var i=0,len=collection.length; i<len; i++){
var item = collection[i],
fileName = getFileName(item.src||item.baseURI||"..");
d.scripts.index=d.scripts.index<0?i:d.scripts.index;
el.button.scripts.selectbox.child.add(
new lib.element("OPTION").attribute.set("value",i).update(fileName)
);
}
d.scripts.open( d.scripts.index );
}
}
},
str: {
open:function(_str){
with(firebug){
d.navigate("str");
el.left.str.container.update(_str.replace(/\n/g,"<br />"))
}
}
},
xhr:{
objects:[],
addObject:function(){
with(firebug){
for(var i=0,len=arguments.length; i<len; i++){
try {
var item = arguments[i],
val = env.targetWindow.eval(item);
d.xhr.objects.push([item, val]);
} catch(e){
continue;
}
}
}
},
open:function(){
with(firebug){
el.left.xhr.container.update("");
el.left.xhr.name = new lib.element("DIV").attribute.addClass("BlockContent").insert(new lib.element("DIV").attribute.addClass("Block").environment.addStyle({ "width":"20%" }).insert(el.left.xhr.container));
el.left.xhr.nameTitle = new lib.element("STRONG").update("Object Name:").insert(el.left.xhr.name);
el.left.xhr.nameContent = new lib.element("DIV").insert(el.left.xhr.name);
el.left.xhr.status = new lib.element("DIV").attribute.addClass("BlockContent").insert(new lib.element("DIV").attribute.addClass("Block").environment.addStyle({ "width":"10%" }).insert(el.left.xhr.container));
el.left.xhr.statusTitle = new lib.element("STRONG").update("Status:").insert(el.left.xhr.status);
el.left.xhr.statusContent = new lib.element("DIV").insert(el.left.xhr.status);
el.left.xhr.readystate = new lib.element("DIV").attribute.addClass("BlockContent").insert(new lib.element("DIV").environment.addStyle({ "width":"15%" }).attribute.addClass("Block").insert(el.left.xhr.container));
el.left.xhr.readystateTitle =el.left.xhr.nameTitle = new lib.element("STRONG").update("Ready State:").insert(el.left.xhr.readystate);
el.left.xhr.readystateContent = new lib.element("DIV").insert(el.left.xhr.readystate);
el.left.xhr.response = new lib.element("DIV").attribute.addClass("BlockContent").insert(new lib.element("DIV").environment.addStyle({ "width":(lib.env.ie?"50":"55")+"%" }).attribute.addClass("Block").insert(el.left.xhr.container));
el.left.xhr.responseTitle = new lib.element("STRONG").update("Response:").insert(el.left.xhr.response);
el.left.xhr.responseContent = new lib.element("DIV").insert(el.left.xhr.response);
setTimeout(d.xhr.refresh,500);
}
},
refresh:function(){
with(firebug){
el.left.xhr.nameContent.update("");
el.left.xhr.statusContent.update("");
el.left.xhr.readystateContent.update("");
el.left.xhr.responseContent.update("");
for(var i=0,len=d.xhr.objects.length; i<len; i++){
var item = d.xhr.objects[i],
response = item[1].responseText;
if(Boolean(item[1])==false)continue;
el.left.xhr.nameContent.child.add(new lib.element("span").update(item[0]));
try {
el.left.xhr.statusContent.child.add(new lib.element("span").update(item[1].status));
} catch(e){ el.left.xhr.statusContent.child.add(new lib.element("span").update("&nbsp;")); }
el.left.xhr.readystateContent.child.add(new lib.element("span").update(item[1].readyState));
el.left.xhr.responseContent.child.add(new lib.element("span").child.add(
new lib.element("A").event.addListener("click",lib.util.Curry(d.str.open,window,response)).update("&nbsp;"+response.substring(0,50))
));
};
if(env.dIndex=="xhr")
setTimeout(d.xhr.refresh,500);
}
}
},
navigateRightColumn:function(_index,_open){
with(firebug){
el.left.container.environment.addStyle({ "width":_open?"70%":"100%" });
el.right.container.environment.addStyle({ "display":_open?"block":"none" });
}
},
navigate:function(_index){
with(firebug){
var open = _index, close = env.dIndex;
env.dIndex = open;
settings.hide();
el.button[close].container.environment.addStyle({ "display":"none" });
el.left[close].container.environment.addStyle({ "display":"none" });
el.right[close].container.environment.addStyle({ "display":"none" });
el.button[open].container.environment.addStyle({ "display":"inline" });
el.left[open].container.environment.addStyle({ "display":"block" });
el.right[open].container.environment.addStyle({ "display":"block" });
if(el.nav[close])
el.nav[close].attribute.removeClass("Selected");
if(el.nav[open])
el.nav[open].attribute.addClass("Selected");
switch(open){
case "console":
d.navigateRightColumn(_index);
break;
case "html":
d.navigateRightColumn(_index,true);
if(!d.html.current){
var t=Number(new Date);
d.html.openHtmlTree();
}
break;
case "css":
d.navigateRightColumn(_index,true);
d.css.refresh();
break;
case "scripts":
d.navigateRightColumn(_index);
d.scripts.refresh();
break;
case "dom":
d.navigateRightColumn(_index);
if(el.left.dom.container.environment.getElement().innerHTML==""){
var t=Number(new Date);
d.dom.open(eval(el.button.dom.textbox.environment.getElement().value),el.left.dom.container);
}
break;
case "xhr":
d.navigateRightColumn(_index);
d.xhr.open();
break;
}
}
}
},
getFileName:function(_path){
var match = _path&&_path.match(/[\w\-\.\?\=\&]+$/);
return match&&match[0]||_path;
},
cancelEvent:function(_event){
if(_event.stopPropagation)
_event.stopPropagation();
if(_event.preventDefault)
_event.preventDefault();
},
getSelection:function(_el){
with(firebug){
if(lib.env.ie){
var range = document.selection.createRange(),stored = range.duplicate();
stored.moveToElementText(_el);
stored.setEndPoint('EndToEnd', range);
_el.selectionStart = stored.text.length - range.text.length;
_el.selectionEnd = _el.selectionStart + range.text.length;
}
return {
start:_el.selectionStart,
length:_el.selectionEnd-_el.selectionStart
}
}
},
tab:function(_el,_event){
with(firebug){
if(_event.keyCode==9){
if(_el.setSelectionRange){
var position = firebug.getSelection(_el);
_el.value = _el.value.substring(0,position.start) + String.fromCharCode(9) + _el.value.substring(position.start+position.length,_el.value.length);
_el.setSelectionRange(position.start+1,position.start+1);
} else if(document.selection) {
var range = document.selection.createRange(), isCollapsed = range.text == '';
range.text = String.fromCharCode(9);
range.moveStart('character', -1);
}
firebug.cancelEvent(_event);
if(lib.env.ie)
setTimeout(_el.focus,100);
};
}
},
listen: {
addXhrObject:function(){
with(firebug){
d.xhr.addObject.apply(env.targetWindow, el.button.xhr.textbox.environment.getElement().value.split(","));
}
},
consoleTextbox:function(_event){
with(firebug){
if(_event.keyCode==13&&(env.multilinemode==false||_event.shiftKey==false)){
d.console.historyIndex = d.console.history.length;
d.console.eval(el.left.console.input.environment.getElement().value);
return false;
}
switch(_event.keyCode){
case 40:
if(d.console.history[d.console.historyIndex+1]){
d.console.historyIndex+=1;
el.left.console.input.update( d.console.history[d.console.historyIndex] );
}
break;
case 38:
if(d.console.history[d.console.historyIndex-1]){
d.console.historyIndex-=1;
el.left.console.input.update( d.console.history[d.console.historyIndex] );
}
break;
}
}
},
cssSelectbox:function(){
with(firebug){
d.css.open(el.button.css.selectbox.environment.getElement().selectedIndex);
}
},
domTextbox:function(_event){
with(firebug){
if(_event.keyCode==13){
d.dom.open(eval(el.button.dom.textbox.environment.getElement().value),el.left.dom.container);
}
}
},
inspector:function(){
with(firebug){
if (env.popupWin) {
env.popupWin.firebug.d.html.inspect(firebug.d.inspector.el);
} else {
firebug.d.html.inspect(firebug.d.inspector.el);
}
}
},
keyboard:function(_event){
with(firebug){
if(_event.keyCode==27 && d.inspector.enabled){
d.inspector.toggle();
} else if(_event.keyCode === 123 && _event.ctrlKey || _event.metaKey) {
if(env.isPopup){
win.dock();
}else {
win.newWindow();
}
} else if(
(_event.keyCode === 123 && !_event.ctrlKey && !_event.metaKey) ||
(_event.keyCode === 76 && (_event.ctrlKey || _event.metaKey) && _event.shiftKey) ||
(_event.keyCode === 13 && _event.shiftKey)) {
if(env.isPopup){
win.dock();
} else if (el.main.environment.getStyle("display") === 'none') {
win.show();
} else {
win.hide();
}
}
}
},
mouse:function(_event){
with(firebug){
var target;
if(document.elementFromPoint) {
target = document.elementFromPoint(_event.clientX, _event.clientY);
} else {
if(lib.env.ie) {
target = _event.srcElement;
} else {
target = _event.explicitOriginalTarget || _event.target;
}
}
if( d.inspector.enabled&&
target!=document.body&&
target!=document.firstChild&&
target!=document.childNodes[1]&&
target!=el.borderInspector.environment.getElement()&&
target!=el.main.environment.getElement()&&
target.offsetParent!=el.main.environment.getElement() ) {
d.inspector.inspect(target);
}
}
},
runMultiline:function(){
with(firebug){
d.console.eval.call(window,el.right.console.input.environment.getElement().value);
}
},
runCSS:function(){
with(firebug){
var source = el.right.css.input.environment.getElement().value.replace(/\n|\t/g,"").split("}");
for(var i=0, len=source.length; i<len; i++){
var item = source[i]+"}", rule = !lib.env.ie?item:item.split(/{|}/),
styleSheet = document.styleSheets[0];
console.log(rule);
if(item.match(/.+\{.+\}/)){
if(lib.env.ie)
styleSheet.addRule(rule[0],rule[1]);
else
styleSheet.insertRule( rule, styleSheet.cssRules.length );
}
}
}
},
scriptsSelectbox:function(){
with(firebug){
d.scripts.open(parseInt(el.button.scripts.selectbox.environment.getElement().value));
}
},
xhrTextbox:function(_event){
with(firebug){
if(_event.keyCode==13){
d.xhr.addObject.apply(env.targetWindow, el.button.xhr.textbox.environment.getElement().value.split(","));
}
}
}
}
};
(function(_scope){
_scope.lib = {};
var pi = _scope.lib; pi.version = [1.1,2008091000];
pi.env = {
ie: /MSIE/i.test(navigator.userAgent),
ie6: /MSIE 6/i.test(navigator.userAgent),
ie7: /MSIE 7/i.test(navigator.userAgent),
ie8: /MSIE 8/i.test(navigator.userAgent),
firefox: /Firefox/i.test(navigator.userAgent),
opera: /Opera/i.test(navigator.userAgent),
webkit: /Webkit/i.test(navigator.userAgent),
camino: /Camino/i.test(navigator.userAgent)
};
pi.get = function(){
return document.getElementById(arguments[0]);
};
pi.get.byTag = function(){
return document.getElementsByTagName(arguments[0]);
};
pi.get.byClass = function(){ return document.getElementsByClassName.apply(document,arguments); };
pi.util = {
Array:{
clone:function(_array,_undeep){
var tmp = [];
Array.prototype.push.apply(tmp,_array);
pi.util.Array.forEach(tmp,function(_item,_index,_source){
if(_item instanceof Array&&!_undeep)
_source[_index] = pi.util.Array.clone(_source[_index]);
});
return tmp;
},
count:function(_array,_value){
var count = 0;
pi.util.Array.forEach(_array,function(){
count+=Number(arguments[0]==_value);
});
return count;
},
forEach:function(_array,_function){
if(_array.forEach)
return _array.forEach(_function);
for(var i=0,len=_array.length; i<len; i++)
_function.apply(_array,[_array[i],i,_array]);
},
getLatest:function(_array){
return _array[_array.length-1];
},
indexOf:function(_array,_value){
if(!pi.env.ie){
return _array.indexOf(_value);
};
var index = -1;
for(var i=0, len=_array.length; i<len; i++){
if(_array[i]==_value){
index = i;
break;
}
}
return index;
},
remove:function(_array,_index){
var result = _array.slice(0,_index);
_array = Array.prototype.push.apply(result,_array.slice(_index+1));
return result;
}
},
Curry:function(_fn,_scope){
var fn = _fn, scope = _scope||window, args = Array.prototype.slice.call(arguments,2);
return function(){
return fn.apply(scope,args.concat( Array.prototype.slice.call(arguments,0) ));
};
},
Extend:function(_superClass,_prototype,_skipClonning){
var object = new pi.base;
if(_prototype["$Init"]){
object.init = _prototype["$Init"];
delete _prototype["$Init"];
};
object.body = _superClass==pi.base?_prototype:pi.util.Hash.merge(_prototype,_superClass.prototype);
object.init=object.init||function(){
if(_superClass!=pi.base)
_superClass.apply(this,arguments);
};
return object.build(_skipClonning);
},
IsArray:function(_object){
if(_object===null){
return false;
}
if(window.NodeList&&window.NamedNodeMap&&!pi.env.ie8){
if(_object instanceof Array||_object instanceof NodeList||_object instanceof NamedNodeMap||(window.HTMLCollection&&_object instanceof HTMLCollection))
return true;
};
if(!_object||_object==window||typeof _object=="function"||typeof _object=="string"||typeof _object.length!="number"){
return false
};
var len = _object.length;
if(len>0&&_object[0]!=undefined&&_object[len-1]!=undefined){
return true;
} else {
for(var key in _object){
if(key!="item"&&key!="length"&&key!="setNamedItemNS"&&key!="setNamedItem"&&key!="getNamedItem"&&key!="removeNamedItem"&&key!="getNamedItemNS"&&key!="removeNamedItemNS"&&key!="tags"){
return false;
}
}
return true
};
},
IsHash:function(_object){
return _object && typeof _object=="object"&&(_object==window||_object instanceof Object)&&!_object.nodeName&&!pi.util.IsArray(_object)
},
Init:[],
AddEvent: function(_element,_eventName,_fn,_useCapture){
_element[pi.env.ie?"attachEvent":"addEventListener"]((pi.env.ie?"on":"")+_eventName,_fn,_useCapture||false);
return pi.util.Curry(pi.util.AddEvent,this,_element);
},
RemoveEvent: function(_element,_eventName,_fn,_useCapture){
_element[pi.env.ie?"detachEvent":"removeEventListener"]((pi.env.ie?"on":"")+_eventName,_fn,_useCapture||false);
return pi.util.Curry(pi.util.RemoveEvent,this,_element);
},
Element:{
addClass:function(_element,_class){
if( !pi.util.Element.hasClass(_element,_class) )
pi.util.Element.setClass(_element, pi.util.Element.getClass(_element) + " " + _class );
},
getClass:function(_element){
return _element.getAttribute(pi.env.ie&&!pi.env.ie8?"className":"class")||"";
},
hasClass:function(_element,_class){
return pi.util.Array.indexOf(pi.util.Element.getClass(_element).split(" "),_class)>-1;
},
removeClass:function(_element,_class){
if( pi.util.Element.hasClass(_element,_class) ){
var names = pi.util.Element.getClass(_element,_class).split(" ");
pi.util.Element.setClass(
_element,
pi.util.Array.remove(names,pi.util.Array.indexOf(names,_class)).join(" ")
);
}
},
setClass:function(_element,_value){
if(pi.env.ie8){
_element.setAttribute("className", _value );
_element.setAttribute("class", _value );
} else {
_element.setAttribute(pi.env.ie?"className":"class", _value );
}
},
toggleClass:function(){
if(pi.util.Element.hasClass.apply(this,arguments))
pi.util.Element.removeClass.apply(this,arguments);
else
pi.util.Element.addClass.apply(this,arguments);
},
getOpacity:function(_styleObject){
var styleObject = _styleObject;
if(!pi.env.ie)
return styleObject["opacity"];
var alpha = styleObject["filter"].match(/opacity\=(\d+)/i);
return alpha?alpha[1]/100:1;
},
setOpacity:function(_element,_value){
if(!pi.env.ie)
return pi.util.Element.addStyle(_element,{ "opacity":_value });
_value*=100;
pi.util.Element.addStyle(_element,{ "filter":"alpha(opacity="+_value+")" });
return this._parent_;
},
getPosition:function(_element){
var parent = _element,offsetLeft = document.body.offsetLeft, offsetTop = document.body.offsetTop, view = pi.util.Element.getView(_element);
while(parent&&parent!=document.body&&parent!=document.firstChild){
offsetLeft +=parseInt(parent.offsetLeft);
offsetTop += parseInt(parent.offsetTop);
parent = parent.offsetParent;
};
return {
"bottom":view["bottom"],
"clientLeft":_element.clientLeft,
"clientTop":_element.clientTop,
"left":view["left"],
"marginTop":view["marginTop"],
"marginLeft":view["marginLeft"],
"offsetLeft":offsetLeft,
"offsetTop":offsetTop,
"position":view["position"],
"right":view["right"],
"top":view["top"],
"zIndex":view["zIndex"]
};
},
getSize:function(_element){
var view = pi.util.Element.getView(_element);
return {
"height":view["height"],
"clientHeight":_element.clientHeight,
"clientWidth":_element.clientWidth,
"offsetHeight":_element.offsetHeight,
"offsetWidth":_element.offsetWidth,
"width":view["width"]
}
},
addStyle:function(_element,_style){
for(var key in _style){
key = key=="float"?pi.env.ie?"styleFloat":"cssFloat":key;
if (key == "opacity" && pi.env.ie) {
pi.util.Element.setOpacity(_element,_style[key]);
continue;
}
try {
_element.style[key] = _style[key];
}catch(e){}
}
},
getStyle:function(_element,_property){
_property = _property=="float"?pi.env.ie?"styleFloat":"cssFloat":_property;
if(_property=="opacity"&&pi.env.ie)
return pi.util.Element.getOpacity(_element.style);
return typeof _property=="string"?_element.style[_property]:_element.style;
},
getValue:function(_element){
switch(_element.nodeName.toLowerCase()){
case "input":
case "textarea":
return _element.value;
case "select":
return _element.options[_element.selectedIndex].value;
default:
return _element.innerHTML;
break;
}
},
getView:function(_element,_property){
var view = document.defaultView?document.defaultView.getComputedStyle(_element,null):_element.currentStyle;
_property = _property=="float"?pi.env.ie?"styleFloat":"cssFloat":_property;
if(_property=="opacity"&&pi.env.ie)
return pi.util.Element.getOpacity(_element,view);
return typeof _property=="string"?view[_property]:view;
}
},
Hash: {
clone:function(_hash,_undeep){
var tmp = {};
for(var key in _hash){
if( !_undeep&&pi.util.IsArray( _hash[key] ) ){
tmp[key] = pi.util.Array.clone( _hash[key] );
} else if( !_undeep&&pi.util.IsHash( _hash[key] ) ){
tmp[ key ] = pi.util.Hash.clone(_hash[key]);
} else {
tmp[key] = _hash[key];
}
}
return tmp;
},
merge:function(_hash,_source,_undeep){
for(var key in _source){
var value = _source[key];
if (!_undeep&&pi.util.IsArray(_source[key])) {
if(pi.util.IsArray( _hash[key] )){
Array.prototype.push.apply( _source[key], _hash[key] )
}
else
value = pi.util.Array.clone(_source[key]);
}
else if (!_undeep&&pi.util.IsHash(_source[key])) {
if (pi.util.IsHash(_hash[key])) {
value = pi.util.Hash.merge(_hash[key], _source[key]);
} else {
value = pi.util.Hash.clone( _source[key] );
}
} else if( _hash[key] )
value = _hash[ key ];
_hash[key] = value;
};
return _hash;
}
},
String:{
format:function(_str){
var values = Array.prototype.slice.call(arguments,1);
return _str.replace(/\{(\d)\}/g,function(){
return values[arguments[1]];
})
}
},
GetViewport:function(){
return {
height:document.documentElement.clientHeight||document.body.clientHeight,
width:document.documentElement.clientWidth||document.body.clientWidth
}
}
};
pi.base = function(){
this.body = {};
this.init = null;
this.build = function(_skipClonning){
var base = this, skipClonning = _skipClonning||false, _private = {},
fn = function(){
var _p = pi.util.Hash.clone(_private);
if(!skipClonning){
for(var key in this){
if(pi.util.IsArray( this[ key ] ) ){
this[key] = pi.util.Array.clone( this[key] );
} else
if( pi.util.IsHash(this[key]) ){
this[key] = pi.util.Hash.clone(
this[ key ],
function(_key,_object){
this[ _key ]._parent_ = this;
}
);
//this[key]._parent_ = this;
}
}
};
base.createAccessors( _p, this );
if(base.init)
return base.init.apply(this,arguments);
return this;
};
this.movePrivateMembers(this.body,_private);
if(this.init){
fn["$Init"] = this.init;
};
fn.prototype = this.body;
return fn;
};
this.createAccessors = function(_p, _branch){
var getter = function(_property){ return this[_property]; },
setter = function(_property,_value){ this[_property] = _value; return _branch._parent_||_branch; };
for (var name in _p) {
var isPrivate = name.substring(0, 1) == "_", title = name.substring(1, 2).toUpperCase() + name.substring(2);
if (isPrivate) {
_branch[(_branch["get" + title]?"_":"")+"get" + title] = pi.util.Curry(getter,_p,name);
_branch[(_branch["set" + title]?"_":"")+"set" + title] = pi.util.Curry(setter,_p,name);
}
else
if (pi.util.IsHash(_p[name])){
_branch[name]._parent_ = _branch;
if(!_branch[name])
_branch[name] = {};
this.createAccessors(_p[name], _branch[name]);
}
};
};
this.movePrivateMembers = function(_object, _branch){
for (var name in _object) {
var isPrivate = name.substring(0, 1) == "_";
if (isPrivate) {
_branch[name] = _object[name];
delete _object[name];
}
else
if (pi.util.IsHash(_object[name])){
_branch[name] = {};
this.movePrivateMembers(_object[name], _branch[name]);
}
};
};
};
pi.element = new pi.base;
pi.element.init = function(_val){
this.environment.setElement(
typeof _val=="string"||!_val?
document.createElement(_val||"DIV"):
_val
);
return this;
};
pi.element.body = {
"addStyle":function(){
return this.environment.addStyle.apply(this.environment,arguments);
},
"clean":function(){
var childs = this.child.get();
while(childs.length){
childs[0].parentNode.removeChild(childs[0]);
}
},
"clone":function(_deep){
return this.environment.getElement().cloneNode(_deep);
},
"insert":function(_element){
_element = _element.environment?_element.environment.getElement():_element;
_element.appendChild(this.environment.getElement());
return this;
},
"insertAfter":function(_referenceElement){
_referenceElement = _referenceElement.environment?_referenceElement.environment.getElement():_referenceElement;
_referenceElement.nextSibling?this.insertBefore(_referenceElement.nextSibling):this.insert(_referenceElement.parentNode);
return this;
},
"insertBefore":function(_referenceElement){
_referenceElement = _referenceElement.environment?_referenceElement.environment.getElement():_referenceElement;
_referenceElement.parentNode.insertBefore(this.environment.getElement(),_referenceElement);
return this;
},
"query":function(_expression,_resultType,namespaceResolver,_result){
return pi.xpath(_expression,_resultType||"ORDERED_NODE_SNAPSHOT_TYPE",this.environment.getElement(),_namespaceResolver,_result);
},
"remove":function(){
if (this.environment.getParent()) {
this.environment.getParent().removeChild(this.environment.getElement());
}
},
"update":function(_value){
this.element[this.element.nodeName.toLowerCase()=="textarea"||this.element.nodeName.toLowerCase()=="input"?"value":"innerHTML"]=_value;
return this;
},
"attribute":{
"getAll":function(){
return this._parent_.environment.getElement().attributes;
},
"clear":function(_name){
this.set(_name,"");
return this._parent_;
},
"get":function(_name){
return this._parent_.environment.getElement().getAttribute(_name);
},
"has":function(_name){
return pi.env.ie?(this.get(_name)!=null):this._parent_.environment.getElement().hasAttribute(_name);
},
"remove":function(_name){
this._parent_.environment.getElement().removeAttribute(_name);
return this._parent_;
},
"set":function(_name,_value){
this._parent_.environment.getElement().setAttribute(_name,_value);
return this._parent_;
},
"addClass":function(_classes){
for(var i=0,len=arguments.length; i<len; i++){
pi.util.Element.addClass(this._parent_.environment.getElement(),arguments[i]);
};
return this._parent_;
},
"clearClass":function(){
this.setClass("");
this._parent_;
},
"getClass":function(){
return pi.util.Element.getClass( this._parent_.environment.getElement() );
},
"hasClass":function(_class){
return pi.util.Element.hasClass( this._parent_.environment.getElement(), _class );
},
"setClass":function(_value){
return pi.util.Element.setClass( this._parent_.environment.getElement(), _value );
},
"removeClass":function(_class){
pi.util.Element.removeClass( this._parent_.environment.getElement(), _class );
return this._parent_;
},
"toggleClass":function(_class){
pi.util.Element.toggleClass( this._parent_.environment.getElement(), _class );
}
},
"child":{
"get":function(){
return this._parent_.environment.getElement().childNodes;
},
"add":function(_elements){
for (var i = 0; i < arguments.length; i++) {
var el = arguments[i];
this._parent_.environment.getElement().appendChild(
el.environment ? el.environment.getElement() : el
);
}
return this._parent_;
},
"addAfter":function(_element,_referenceElement){
this.addBefore(
_element.environment?_element.environment.getElement():_element,
(_referenceElement.environment?_referenceElement.environment.getElement():_referenceElement).nextSibling
);
return this._parent_;
},
"addBefore":function(_element,_referenceElement){
this._parent_.environment.getElement().insertBefore(
_element.environment?_element.environment.getElement():_element,
_referenceElement.environment?_referenceElement.environment.getElement():_referenceElement
);
return this._parent_;
},
"remove":function(_element){
this._parent_.environment.getElement().removeChild(_element.environment?_element.environment.getElement():_element);
}
},
"environment":{
"_element":null,
"setElement":function(_value){
this._parent_.element = _value;
this._parent_.element.lib = this._parent_;
this._parent_.element.firebugElement = true;
this._setElement(_value);
},
"getParent":function(){
return this.getElement().parentNode;
},
"getPosition":function(){
return pi.util.Element.getPosition(this.getElement());
},
"getSize":function(){
return pi.util.Element.getSize( this.getElement() );
},
"addStyle":function(_styleObject){
pi.util.Element.addStyle(this.getElement(),_styleObject);
return this._parent_;
},
"getStyle":function(_property){
return pi.util.Element.getStyle(this.getElement(),_property);
},
"getName":function(){
return this.getElement().nodeName;
},
"getType":function(){
return this.getElement().nodeType;
},
"getValue":function(){
return pi.util.Element.getValue(this.getElement());
},
"getView":function(_property){
return pi.util.Element.getView(this.getElement(),_property);
}
},
"event":{
"addListener":function(_event,_fn,_useCapture){
pi.util.AddEvent(this._parent_.environment.getElement(),_event,_fn,_useCapture);
return this._parent_;
},
"removeListener":function(_event,_fn,_useCapture){
pi.util.RemoveEvent(this._parent_.environment.getElement(),_event,_fn,_useCapture);
return this._parent_;
}
}
};
pi.element = pi.element.build();
pi.xhr = new pi.base;
pi.xhr.init = function(_url){
if(!window.XMLHttpRequest){
var names = ["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
for (var i = 0; i < names.length; i++) {
try {
this.environment.setApi(new ActiveXObject(names[i]));
break;
} catch (e) { continue; }
}
}
else {
this.environment.setApi(new XMLHttpRequest());
}
this.environment.getApi().onreadystatechange=pi.util.Curry(this.event.readystatechange,this);
this.environment.setUrl(_url);
this.environment.setCallback([]);
return this;
};
pi.xhr.body = {
"addCallback": function(){
return this.environment.addCallback.apply(this.environment,arguments);
},
"addData": function(){
return this.environment.addData.apply(this.environment,arguments);
},
"abort":function(){
this.environment.getApi().abort();
return this;
},
"send":function(){
var url = this.environment.getUrl(), data = this.environment.getData(),dataUrl = "";
if(!this.environment.getCache())
data["forceCache"] = Number(new Date);
for (var key in data)
dataUrl += pi.util.String.format("{0}={1}&",key, data[key]);
if (this.environment.getType()=="GET")
url += (url.search("\\?")==-1?"?":"&")+pi.util.String.format("{0}",dataUrl);
this.api.open(this.environment.getType(),url,this.environment.getAsync());
if(this.environment.getType()=="POST"){
this.api.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
};
this.api.send(this.environment.getType()=="GET"?"":dataUrl);
return this;
}
};
pi.xhr.body.environment = {
"_async":true, "_api":null, "_cache":true, "_callback":null, "_data":{}, "_type":"GET", "_url":"",
"setApi":function(_value){
this._parent_.api = _value;
this._setApi(_value);
},
"addCallback": function(_readyState,_fn){
this.getCallback().push({ "fn":_fn, "readyState":_readyState });
return this._parent_;
},
"addData": function(_key,_value){
this.getData()[_key] = _value;
return this._parent_;
},
"setType": function(_value){
this._setType(_value);
return this._parent_;
}
};
pi.xhr.body.event = {
"readystatechange":function(){
var readyState = this.environment.getApi().readyState, callback=this.environment.getCallback();
for (var i = 0, len=callback.length; i < len; i++) {
if(pi.util.Array.indexOf(callback[i].readyState,readyState)>-1){
callback[i].fn.apply(this);
}
}
}
};
pi.xhr = pi.xhr.build();
/*
* xml.xhr.get
*/
pi.xhr.get = function(_url,_returnPiObject){
var request = new pi.xhr();
request.environment.setAsync(false);
request.environment.setUrl(_url);
request.send();
return _returnPiObject?request:request.environment.getApi();
};
/*
* registering onload event for init functions
*/
pi.util.AddEvent(
pi.env.ie?window:document,
pi.env.ie?"load":"DOMContentLoaded",
function(){
for(var i=0,len=pi.util.Init.length; i<len; i++){
pi.util.Init[ i ]();
}
}
);
})(firebug);
with(firebug){
initConsole();
lib.util.Init.push(firebug.init);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

View File

@ -0,0 +1,4376 @@
/*!
* jQuery JavaScript Library v1.3.2
* http://jquery.com/
*
* Copyright (c) 2009 John Resig
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
* Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
* Revision: 6246
*/
(function(){
var
// Will speed up references to window, and allows munging its name.
window = this,
// Will speed up references to undefined, and allows munging its name.
undefined,
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$,
jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
// A simple way to check for HTML strings or ID strings
// (both of which we optimize for)
quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
// Is it a simple selector
isSimple = /^.[^:#\[\.,]*$/;
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
// Make sure that a selection was provided
selector = selector || document;
// Handle $(DOMElement)
if ( selector.nodeType ) {
this[0] = selector;
this.length = 1;
this.context = selector;
return this;
}
// Handle HTML strings
if ( typeof selector === "string" ) {
// Are we dealing with HTML string or an ID?
var match = quickExpr.exec( selector );
// Verify a match, and that no context was specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] )
selector = jQuery.clean( [ match[1] ], context );
// HANDLE: $("#id")
else {
var elem = document.getElementById( match[3] );
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem && elem.id != match[3] )
return jQuery().find( selector );
// Otherwise, we inject the element directly into the jQuery object
var ret = jQuery( elem || [] );
ret.context = document;
ret.selector = selector;
return ret;
}
// HANDLE: $(expr, [context])
// (which is just equivalent to: $(content).find(expr)
} else
return jQuery( context ).find( selector );
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) )
return jQuery( document ).ready( selector );
// Make sure that old selector state is passed along
if ( selector.selector && selector.context ) {
this.selector = selector.selector;
this.context = selector.context;
}
return this.setArray(jQuery.isArray( selector ) ?
selector :
jQuery.makeArray(selector));
},
// Start with an empty selector
selector: "",
// The current version of jQuery being used
jquery: "1.3.2",
// The number of elements contained in the matched element set
size: function() {
return this.length;
},
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num === undefined ?
// Return a 'clean' array
Array.prototype.slice.call( this ) :
// Return just the object
this[ num ];
},
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = jQuery( elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" )
ret.selector = this.selector + (this.selector ? " " : "") + selector;
else if ( name )
ret.selector = this.selector + "." + name + "(" + selector + ")";
// Return the newly-formed element set
return ret;
},
// Force the current matched set of elements to become
// the specified array of elements (destroying the stack in the process)
// You should use pushStack() in order to do this, but maintain the stack
setArray: function( elems ) {
// Resetting the length to 0, then using the native Array push
// is a super-fast way to populate an object with array-like properties
this.length = 0;
Array.prototype.push.apply( this, elems );
return this;
},
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},
// Determine the position of an element within
// the matched set of elements
index: function( elem ) {
// Locate the position of the desired element
return jQuery.inArray(
// If it receives a jQuery object, the first element is used
elem && elem.jquery ? elem[0] : elem
, this );
},
attr: function( name, value, type ) {
var options = name;
// Look for the case where we're accessing a style value
if ( typeof name === "string" )
if ( value === undefined )
return this[0] && jQuery[ type || "attr" ]( this[0], name );
else {
options = {};
options[ name ] = value;
}
// Check to see if we're setting style values
return this.each(function(i){
// Set all the styles
for ( name in options )
jQuery.attr(
type ?
this.style :
this,
name, jQuery.prop( this, options[ name ], type, i, name )
);
});
},
css: function( key, value ) {
// ignore negative width and height values
if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
value = undefined;
return this.attr( key, value, "curCSS" );
},
text: function( text ) {
if ( typeof text !== "object" && text != null )
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
var ret = "";
jQuery.each( text || this, function(){
jQuery.each( this.childNodes, function(){
if ( this.nodeType != 8 )
ret += this.nodeType != 1 ?
this.nodeValue :
jQuery.fn.text( [ this ] );
});
});
return ret;
},
wrapAll: function( html ) {
if ( this[0] ) {
// The elements to wrap the target around
var wrap = jQuery( html, this[0].ownerDocument ).clone();
if ( this[0].parentNode )
wrap.insertBefore( this[0] );
wrap.map(function(){
var elem = this;
while ( elem.firstChild )
elem = elem.firstChild;
return elem;
}).append(this);
}
return this;
},
wrapInner: function( html ) {
return this.each(function(){
jQuery( this ).contents().wrapAll( html );
});
},
wrap: function( html ) {
return this.each(function(){
jQuery( this ).wrapAll( html );
});
},
append: function() {
return this.domManip(arguments, true, function(elem){
if (this.nodeType == 1)
this.appendChild( elem );
});
},
prepend: function() {
return this.domManip(arguments, true, function(elem){
if (this.nodeType == 1)
this.insertBefore( elem, this.firstChild );
});
},
before: function() {
return this.domManip(arguments, false, function(elem){
this.parentNode.insertBefore( elem, this );
});
},
after: function() {
return this.domManip(arguments, false, function(elem){
this.parentNode.insertBefore( elem, this.nextSibling );
});
},
end: function() {
return this.prevObject || jQuery( [] );
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: [].push,
sort: [].sort,
splice: [].splice,
find: function( selector ) {
if ( this.length === 1 ) {
var ret = this.pushStack( [], "find", selector );
ret.length = 0;
jQuery.find( selector, this[0], ret );
return ret;
} else {
return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
return jQuery.find( selector, elem );
})), "find", selector );
}
},
clone: function( events ) {
// Do the clone
var ret = this.map(function(){
if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
// IE copies events bound via attachEvent when
// using cloneNode. Calling detachEvent on the
// clone will also remove the events from the orignal
// In order to get around this, we use innerHTML.
// Unfortunately, this means some modifications to
// attributes in IE that are actually only stored
// as properties will not be copied (such as the
// the name attribute on an input).
var html = this.outerHTML;
if ( !html ) {
var div = this.ownerDocument.createElement("div");
div.appendChild( this.cloneNode(true) );
html = div.innerHTML;
}
return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
} else
return this.cloneNode(true);
});
// Copy the events from the original to the clone
if ( events === true ) {
var orig = this.find("*").andSelf(), i = 0;
ret.find("*").andSelf().each(function(){
if ( this.nodeName !== orig[i].nodeName )
return;
var events = jQuery.data( orig[i], "events" );
for ( var type in events ) {
for ( var handler in events[ type ] ) {
jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
}
}
i++;
});
}
// Return the cloned set
return ret;
},
filter: function( selector ) {
return this.pushStack(
jQuery.isFunction( selector ) &&
jQuery.grep(this, function(elem, i){
return selector.call( elem, i );
}) ||
jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
return elem.nodeType === 1;
}) ), "filter", selector );
},
closest: function( selector ) {
var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
closer = 0;
return this.map(function(){
var cur = this;
while ( cur && cur.ownerDocument ) {
if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
jQuery.data(cur, "closest", closer);
return cur;
}
cur = cur.parentNode;
closer++;
}
});
},
not: function( selector ) {
if ( typeof selector === "string" )
// test special case where just one selector is passed in
if ( isSimple.test( selector ) )
return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
else
selector = jQuery.multiFilter( selector, this );
var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
return this.filter(function() {
return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
});
},
add: function( selector ) {
return this.pushStack( jQuery.unique( jQuery.merge(
this.get(),
typeof selector === "string" ?
jQuery( selector ) :
jQuery.makeArray( selector )
)));
},
is: function( selector ) {
return !!selector && jQuery.multiFilter( selector, this ).length > 0;
},
hasClass: function( selector ) {
return !!selector && this.is( "." + selector );
},
val: function( value ) {
if ( value === undefined ) {
var elem = this[0];
if ( elem ) {
if( jQuery.nodeName( elem, 'option' ) )
return (elem.attributes.value || {}).specified ? elem.value : elem.text;
// We need to handle select boxes special
if ( jQuery.nodeName( elem, "select" ) ) {
var index = elem.selectedIndex,
values = [],
options = elem.options,
one = elem.type == "select-one";
// Nothing was selected
if ( index < 0 )
return null;
// Loop through all the selected options
for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
var option = options[ i ];
if ( option.selected ) {
// Get the specifc value for the option
value = jQuery(option).val();
// We don't need an array for one selects
if ( one )
return value;
// Multi-Selects return an array
values.push( value );
}
}
return values;
}
// Everything else, we just grab the value
return (elem.value || "").replace(/\r/g, "");
}
return undefined;
}
if ( typeof value === "number" )
value += '';
return this.each(function(){
if ( this.nodeType != 1 )
return;
if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
this.checked = (jQuery.inArray(this.value, value) >= 0 ||
jQuery.inArray(this.name, value) >= 0);
else if ( jQuery.nodeName( this, "select" ) ) {
var values = jQuery.makeArray(value);
jQuery( "option", this ).each(function(){
this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
jQuery.inArray( this.text, values ) >= 0);
});
if ( !values.length )
this.selectedIndex = -1;
} else
this.value = value;
});
},
html: function( value ) {
return value === undefined ?
(this[0] ?
this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
null) :
this.empty().append( value );
},
replaceWith: function( value ) {
return this.after( value ).remove();
},
eq: function( i ) {
return this.slice( i, +i + 1 );
},
slice: function() {
return this.pushStack( Array.prototype.slice.apply( this, arguments ),
"slice", Array.prototype.slice.call(arguments).join(",") );
},
map: function( callback ) {
return this.pushStack( jQuery.map(this, function(elem, i){
return callback.call( elem, i, elem );
}));
},
andSelf: function() {
return this.add( this.prevObject );
},
domManip: function( args, table, callback ) {
if ( this[0] ) {
var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
first = fragment.firstChild;
if ( first )
for ( var i = 0, l = this.length; i < l; i++ )
callback.call( root(this[i], first), this.length > 1 || i > 0 ?
fragment.cloneNode(true) : fragment );
if ( scripts )
jQuery.each( scripts, evalScript );
}
return this;
function root( elem, cur ) {
return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
(elem.getElementsByTagName("tbody")[0] ||
elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
elem;
}
}
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
function evalScript( i, elem ) {
if ( elem.src )
jQuery.ajax({
url: elem.src,
async: false,
dataType: "script"
});
else
jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
if ( elem.parentNode )
elem.parentNode.removeChild( elem );
}
function now(){
return +new Date;
}
jQuery.extend = jQuery.fn.extend = function() {
// copy reference to target object
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) )
target = {};
// extend jQuery itself if only one argument is passed
if ( length == i ) {
target = this;
--i;
}
for ( ; i < length; i++ )
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null )
// Extend the base object
for ( var name in options ) {
var src = target[ name ], copy = options[ name ];
// Prevent never-ending loop
if ( target === copy )
continue;
// Recurse if we're merging object values
if ( deep && copy && typeof copy === "object" && !copy.nodeType )
target[ name ] = jQuery.extend( deep,
// Never move original objects, clone them
src || ( copy.length != null ? [ ] : { } )
, copy );
// Don't bring in undefined values
else if ( copy !== undefined )
target[ name ] = copy;
}
// Return the modified object
return target;
};
// exclude the following css properties to add px
var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
// cache defaultView
defaultView = document.defaultView || {},
toString = Object.prototype.toString;
jQuery.extend({
noConflict: function( deep ) {
window.$ = _$;
if ( deep )
window.jQuery = _jQuery;
return jQuery;
},
// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) {
return toString.call(obj) === "[object Function]";
},
isArray: function( obj ) {
return toString.call(obj) === "[object Array]";
},
// check if an element is in a (or is an) XML document
isXMLDoc: function( elem ) {
return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
},
// Evalulates a script in a global context
globalEval: function( data ) {
if ( data && /\S/.test(data) ) {
// Inspired by code by Andrea Giammarchi
// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
var head = document.getElementsByTagName("head")[0] || document.documentElement,
script = document.createElement("script");
script.type = "text/javascript";
if ( jQuery.support.scriptEval )
script.appendChild( document.createTextNode( data ) );
else
script.text = data;
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709).
head.insertBefore( script, head.firstChild );
head.removeChild( script );
}
},
nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
},
// args is for internal usage only
each: function( object, callback, args ) {
var name, i = 0, length = object.length;
if ( args ) {
if ( length === undefined ) {
for ( name in object )
if ( callback.apply( object[ name ], args ) === false )
break;
} else
for ( ; i < length; )
if ( callback.apply( object[ i++ ], args ) === false )
break;
// A special, fast, case for the most common use of each
} else {
if ( length === undefined ) {
for ( name in object )
if ( callback.call( object[ name ], name, object[ name ] ) === false )
break;
} else
for ( var value = object[0];
i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
}
return object;
},
prop: function( elem, value, type, i, name ) {
// Handle executable functions
if ( jQuery.isFunction( value ) )
value = value.call( elem, i );
// Handle passing in a number to a CSS property
return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
value + "px" :
value;
},
className: {
// internal only, use addClass("class")
add: function( elem, classNames ) {
jQuery.each((classNames || "").split(/\s+/), function(i, className){
if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
elem.className += (elem.className ? " " : "") + className;
});
},
// internal only, use removeClass("class")
remove: function( elem, classNames ) {
if (elem.nodeType == 1)
elem.className = classNames !== undefined ?
jQuery.grep(elem.className.split(/\s+/), function(className){
return !jQuery.className.has( classNames, className );
}).join(" ") :
"";
},
// internal only, use hasClass("class")
has: function( elem, className ) {
return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
}
},
// A method for quickly swapping in/out CSS properties to get correct calculations
swap: function( elem, options, callback ) {
var old = {};
// Remember the old values, and insert the new ones
for ( var name in options ) {
old[ name ] = elem.style[ name ];
elem.style[ name ] = options[ name ];
}
callback.call( elem );
// Revert the old values
for ( var name in options )
elem.style[ name ] = old[ name ];
},
css: function( elem, name, force, extra ) {
if ( name == "width" || name == "height" ) {
var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];
function getWH() {
val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
if ( extra === "border" )
return;
jQuery.each( which, function() {
if ( !extra )
val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
if ( extra === "margin" )
val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
else
val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
});
}
if ( elem.offsetWidth !== 0 )
getWH();
else
jQuery.swap( elem, props, getWH );
return Math.max(0, Math.round(val));
}
return jQuery.curCSS( elem, name, force );
},
curCSS: function( elem, name, force ) {
var ret, style = elem.style;
// We need to handle opacity special in IE
if ( name == "opacity" && !jQuery.support.opacity ) {
ret = jQuery.attr( style, "opacity" );
return ret == "" ?
"1" :
ret;
}
// Make sure we're using the right name for getting the float value
if ( name.match( /float/i ) )
name = styleFloat;
if ( !force && style && style[ name ] )
ret = style[ name ];
else if ( defaultView.getComputedStyle ) {
// Only "float" is needed here
if ( name.match( /float/i ) )
name = "float";
name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
var computedStyle = defaultView.getComputedStyle( elem, null );
if ( computedStyle )
ret = computedStyle.getPropertyValue( name );
// We should always get a number back from opacity
if ( name == "opacity" && ret == "" )
ret = "1";
} else if ( elem.currentStyle ) {
var camelCase = name.replace(/\-(\w)/g, function(all, letter){
return letter.toUpperCase();
});
ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
// Remember the original values
var left = style.left, rsLeft = elem.runtimeStyle.left;
// Put in the new values to get a computed value out
elem.runtimeStyle.left = elem.currentStyle.left;
style.left = ret || 0;
ret = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
elem.runtimeStyle.left = rsLeft;
}
}
return ret;
},
clean: function( elems, context, fragment ) {
context = context || document;
// !context.createElement fails in IE with an error but returns typeof 'object'
if ( typeof context.createElement === "undefined" )
context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
if ( match )
return [ context.createElement( match[1] ) ];
}
var ret = [], scripts = [], div = context.createElement("div");
jQuery.each(elems, function(i, elem){
if ( typeof elem === "number" )
elem += '';
if ( !elem )
return;
// Convert html string into DOM nodes
if ( typeof elem === "string" ) {
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
all :
front + "></" + tag + ">";
});
// Trim whitespace, otherwise indexOf won't work as expected
var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();
var wrap =
// option or optgroup
!tags.indexOf("<opt") &&
[ 1, "<select multiple='multiple'>", "</select>" ] ||
!tags.indexOf("<leg") &&
[ 1, "<fieldset>", "</fieldset>" ] ||
tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
[ 1, "<table>", "</table>" ] ||
!tags.indexOf("<tr") &&
[ 2, "<table><tbody>", "</tbody></table>" ] ||
// <thead> matched above
(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
!tags.indexOf("<col") &&
[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
// IE can't serialize <link> and <script> tags normally
!jQuery.support.htmlSerialize &&
[ 1, "div<div>", "</div>" ] ||
[ 0, "", "" ];
// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];
// Move to the right depth
while ( wrap[0]-- )
div = div.lastChild;
// Remove IE's autoinserted <tbody> from table fragments
if ( !jQuery.support.tbody ) {
// String was a <table>, *may* have spurious <tbody>
var hasBody = /<tbody/i.test(elem),
tbody = !tags.indexOf("<table") && !hasBody ?
div.firstChild && div.firstChild.childNodes :
// String was a bare <thead> or <tfoot>
wrap[1] == "<table>" && !hasBody ?
div.childNodes :
[];
for ( var j = tbody.length - 1; j >= 0 ; --j )
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
tbody[ j ].parentNode.removeChild( tbody[ j ] );
}
// IE completely kills leading whitespace when innerHTML is used
if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
elem = jQuery.makeArray( div.childNodes );
}
if ( elem.nodeType )
ret.push( elem );
else
ret = jQuery.merge( ret, elem );
});
if ( fragment ) {
for ( var i = 0; ret[i]; i++ ) {
if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
} else {
if ( ret[i].nodeType === 1 )
ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
fragment.appendChild( ret[i] );
}
}
return scripts;
}
return ret;
},
attr: function( elem, name, value ) {
// don't set attributes on text and comment nodes
if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
return undefined;
var notxml = !jQuery.isXMLDoc( elem ),
// Whether we are setting (or getting)
set = value !== undefined;
// Try to normalize/fix the name
name = notxml && jQuery.props[ name ] || name;
// Only do all the following if this is a node (faster for style)
// IE elem.getAttribute passes even for style
if ( elem.tagName ) {
// These attributes require special treatment
var special = /href|src|style/.test( name );
// Safari mis-reports the default selected property of a hidden option
// Accessing the parent's selectedIndex property fixes it
if ( name == "selected" && elem.parentNode )
elem.parentNode.selectedIndex;
// If applicable, access the attribute via the DOM 0 way
if ( name in elem && notxml && !special ) {
if ( set ){
// We can't allow the type property to be changed (since it causes problems in IE)
if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
throw "type property can't be changed";
elem[ name ] = value;
}
// browsers index elements by id/name on forms, give priority to attributes.
if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
return elem.getAttributeNode( name ).nodeValue;
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
if ( name == "tabIndex" ) {
var attributeNode = elem.getAttributeNode( "tabIndex" );
return attributeNode && attributeNode.specified
? attributeNode.value
: elem.nodeName.match(/(button|input|object|select|textarea)/i)
? 0
: elem.nodeName.match(/^(a|area)$/i) && elem.href
? 0
: undefined;
}
return elem[ name ];
}
if ( !jQuery.support.style && notxml && name == "style" )
return jQuery.attr( elem.style, "cssText", value );
if ( set )
// convert the value to a string (all browsers do this but IE) see #1070
elem.setAttribute( name, "" + value );
var attr = !jQuery.support.hrefNormalized && notxml && special
// Some attributes require a special call on IE
? elem.getAttribute( name, 2 )
: elem.getAttribute( name );
// Non-existent attributes return null, we normalize to undefined
return attr === null ? undefined : attr;
}
// elem is actually elem.style ... set the style
// IE uses filters for opacity
if ( !jQuery.support.opacity && name == "opacity" ) {
if ( set ) {
// IE has trouble with opacity if it does not have layout
// Force it by setting the zoom level
elem.zoom = 1;
// Set the alpha filter to set the opacity
elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
}
return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
"";
}
name = name.replace(/-([a-z])/ig, function(all, letter){
return letter.toUpperCase();
});
if ( set )
elem[ name ] = value;
return elem[ name ];
},
trim: function( text ) {
return (text || "").replace( /^\s+|\s+$/g, "" );
},
makeArray: function( array ) {
var ret = [];
if( array != null ){
var i = array.length;
// The window, strings (and functions) also have 'length'
if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
ret[0] = array;
else
while( i )
ret[--i] = array[i];
}
return ret;
},
inArray: function( elem, array ) {
for ( var i = 0, length = array.length; i < length; i++ )
// Use === because on IE, window == document
if ( array[ i ] === elem )
return i;
return -1;
},
merge: function( first, second ) {
// We have to loop this way because IE & Opera overwrite the length
// expando of getElementsByTagName
var i = 0, elem, pos = first.length;
// Also, we need to make sure that the correct elements are being returned
// (IE returns comment nodes in a '*' query)
if ( !jQuery.support.getAll ) {
while ( (elem = second[ i++ ]) != null )
if ( elem.nodeType != 8 )
first[ pos++ ] = elem;
} else
while ( (elem = second[ i++ ]) != null )
first[ pos++ ] = elem;
return first;
},
unique: function( array ) {
var ret = [], done = {};
try {
for ( var i = 0, length = array.length; i < length; i++ ) {
var id = jQuery.data( array[ i ] );
if ( !done[ id ] ) {
done[ id ] = true;
ret.push( array[ i ] );
}
}
} catch( e ) {
ret = array;
}
return ret;
},
grep: function( elems, callback, inv ) {
var ret = [];
// Go through the array, only saving the items
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ )
if ( !inv != !callback( elems[ i ], i ) )
ret.push( elems[ i ] );
return ret;
},
map: function( elems, callback ) {
var ret = [];
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, length = elems.length; i < length; i++ ) {
var value = callback( elems[ i ], i );
if ( value != null )
ret[ ret.length ] = value;
}
return ret.concat.apply( [], ret );
}
});
// Use of jQuery.browser is deprecated.
// It's included for backwards compatibility and plugins,
// although they should work to migrate away.
var userAgent = navigator.userAgent.toLowerCase();
// Figure out what browser is being used
jQuery.browser = {
version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
safari: /webkit/.test( userAgent ),
opera: /opera/.test( userAgent ),
msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
};
jQuery.each({
parent: function(elem){return elem.parentNode;},
parents: function(elem){return jQuery.dir(elem,"parentNode");},
next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
children: function(elem){return jQuery.sibling(elem.firstChild);},
contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
}, function(name, fn){
jQuery.fn[ name ] = function( selector ) {
var ret = jQuery.map( this, fn );
if ( selector && typeof selector == "string" )
ret = jQuery.multiFilter( selector, ret );
return this.pushStack( jQuery.unique( ret ), name, selector );
};
});
jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function(name, original){
jQuery.fn[ name ] = function( selector ) {
var ret = [], insert = jQuery( selector );
for ( var i = 0, l = insert.length; i < l; i++ ) {
var elems = (i > 0 ? this.clone(true) : this).get();
jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
ret = ret.concat( elems );
}
return this.pushStack( ret, name, selector );
};
});
jQuery.each({
removeAttr: function( name ) {
jQuery.attr( this, name, "" );
if (this.nodeType == 1)
this.removeAttribute( name );
},
addClass: function( classNames ) {
jQuery.className.add( this, classNames );
},
removeClass: function( classNames ) {
jQuery.className.remove( this, classNames );
},
toggleClass: function( classNames, state ) {
if( typeof state !== "boolean" )
state = !jQuery.className.has( this, classNames );
jQuery.className[ state ? "add" : "remove" ]( this, classNames );
},
remove: function( selector ) {
if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
// Prevent memory leaks
jQuery( "*", this ).add([this]).each(function(){
jQuery.event.remove(this);
jQuery.removeData(this);
});
if (this.parentNode)
this.parentNode.removeChild( this );
}
},
empty: function() {
// Remove element nodes and prevent memory leaks
jQuery(this).children().remove();
// Remove any remaining nodes
while ( this.firstChild )
this.removeChild( this.firstChild );
}
}, function(name, fn){
jQuery.fn[ name ] = function(){
return this.each( fn, arguments );
};
});
// Helper function used by the dimensions and offset modules
function num(elem, prop) {
return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
}
var expando = "jQuery" + now(), uuid = 0, windowData = {};
jQuery.extend({
cache: {},
data: function( elem, name, data ) {
elem = elem == window ?
windowData :
elem;
var id = elem[ expando ];
// Compute a unique ID for the element
if ( !id )
id = elem[ expando ] = ++uuid;
// Only generate the data cache if we're
// trying to access or manipulate it
if ( name && !jQuery.cache[ id ] )
jQuery.cache[ id ] = {};
// Prevent overriding the named cache with undefined values
if ( data !== undefined )
jQuery.cache[ id ][ name ] = data;
// Return the named cache data, or the ID for the element
return name ?
jQuery.cache[ id ][ name ] :
id;
},
removeData: function( elem, name ) {
elem = elem == window ?
windowData :
elem;
var id = elem[ expando ];
// If we want to remove a specific section of the element's data
if ( name ) {
if ( jQuery.cache[ id ] ) {
// Remove the section of cache data
delete jQuery.cache[ id ][ name ];
// If we've removed all the data, remove the element's cache
name = "";
for ( name in jQuery.cache[ id ] )
break;
if ( !name )
jQuery.removeData( elem );
}
// Otherwise, we want to remove all of the element's data
} else {
// Clean up the element expando
try {
delete elem[ expando ];
} catch(e){
// IE has trouble directly removing the expando
// but it's ok with using removeAttribute
if ( elem.removeAttribute )
elem.removeAttribute( expando );
}
// Completely remove the data cache
delete jQuery.cache[ id ];
}
},
queue: function( elem, type, data ) {
if ( elem ){
type = (type || "fx") + "queue";
var q = jQuery.data( elem, type );
if ( !q || jQuery.isArray(data) )
q = jQuery.data( elem, type, jQuery.makeArray(data) );
else if( data )
q.push( data );
}
return q;
},
dequeue: function( elem, type ){
var queue = jQuery.queue( elem, type ),
fn = queue.shift();
if( !type || type === "fx" )
fn = queue[0];
if( fn !== undefined )
fn.call(elem);
}
});
jQuery.fn.extend({
data: function( key, value ){
var parts = key.split(".");
parts[1] = parts[1] ? "." + parts[1] : "";
if ( value === undefined ) {
var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
if ( data === undefined && this.length )
data = jQuery.data( this[0], key );
return data === undefined && parts[1] ?
this.data( parts[0] ) :
data;
} else
return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
jQuery.data( this, key, value );
});
},
removeData: function( key ){
return this.each(function(){
jQuery.removeData( this, key );
});
},
queue: function(type, data){
if ( typeof type !== "string" ) {
data = type;
type = "fx";
}
if ( data === undefined )
return jQuery.queue( this[0], type );
return this.each(function(){
var queue = jQuery.queue( this, type, data );
if( type == "fx" && queue.length == 1 )
queue[0].call(this);
});
},
dequeue: function(type){
return this.each(function(){
jQuery.dequeue( this, type );
});
}
});/*!
* Sizzle CSS Selector Engine - v0.9.3
* Copyright 2009, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
done = 0,
toString = Object.prototype.toString;
var Sizzle = function(selector, context, results, seed) {
results = results || [];
context = context || document;
if ( context.nodeType !== 1 && context.nodeType !== 9 )
return [];
if ( !selector || typeof selector !== "string" ) {
return results;
}
var parts = [], m, set, checkSet, check, mode, extra, prune = true;
// Reset the position of the chunker regexp (start from head)
chunker.lastIndex = 0;
while ( (m = chunker.exec(selector)) !== null ) {
parts.push( m[1] );
if ( m[2] ) {
extra = RegExp.rightContext;
break;
}
}
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
Sizzle( parts.shift(), context );
while ( parts.length ) {
selector = parts.shift();
if ( Expr.relative[ selector ] )
selector += parts.shift();
set = posProcess( selector, set );
}
}
} else {
var ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
set = Sizzle.filter( ret.expr, ret.set );
if ( parts.length > 0 ) {
checkSet = makeArray(set);
} else {
prune = false;
}
while ( parts.length ) {
var cur = parts.pop(), pop = cur;
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
pop = parts.pop();
}
if ( pop == null ) {
pop = context;
}
Expr.relative[ cur ]( checkSet, pop, isXML(context) );
}
}
if ( !checkSet ) {
checkSet = set;
}
if ( !checkSet ) {
throw "Syntax error, unrecognized expression: " + (cur || selector);
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context.nodeType === 1 ) {
for ( var i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( var i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
results.push( set[i] );
}
}
}
} else {
makeArray( checkSet, results );
}
if ( extra ) {
Sizzle( extra, context, results, seed );
if ( sortOrder ) {
hasDuplicate = false;
results.sort(sortOrder);
if ( hasDuplicate ) {
for ( var i = 1; i < results.length; i++ ) {
if ( results[i] === results[i-1] ) {
results.splice(i--, 1);
}
}
}
}
}
return results;
};
Sizzle.matches = function(expr, set){
return Sizzle(expr, null, null, set);
};
Sizzle.find = function(expr, context, isXML){
var set, match;
if ( !expr ) {
return [];
}
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
if ( (match = Expr.match[ type ].exec( expr )) ) {
var left = RegExp.leftContext;
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
}
}
}
}
if ( !set ) {
set = context.getElementsByTagName("*");
}
return {set: set, expr: expr};
};
Sizzle.filter = function(expr, set, inplace, not){
var old = expr, result = [], curLoop = set, match, anyFound,
isXMLFilter = set && set[0] && isXML(set[0]);
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
if ( (match = Expr.match[ type ].exec( expr )) != null ) {
var filter = Expr.filter[ type ], found, item;
anyFound = false;
if ( curLoop == result ) {
result = [];
}
if ( Expr.preFilter[ type ] ) {
match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
if ( !match ) {
anyFound = found = true;
} else if ( match === true ) {
continue;
}
}
if ( match ) {
for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
found = filter( item, match, i, curLoop );
var pass = not ^ !!found;
if ( inplace && found != null ) {
if ( pass ) {
anyFound = true;
} else {
curLoop[i] = false;
}
} else if ( pass ) {
result.push( item );
anyFound = true;
}
}
}
}
if ( found !== undefined ) {
if ( !inplace ) {
curLoop = result;
}
expr = expr.replace( Expr.match[ type ], "" );
if ( !anyFound ) {
return [];
}
break;
}
}
}
// Improper expression
if ( expr == old ) {
if ( anyFound == null ) {
throw "Syntax error, unrecognized expression: " + expr;
} else {
break;
}
}
old = expr;
}
return curLoop;
};
var Expr = Sizzle.selectors = {
order: [ "ID", "NAME", "TAG" ],
match: {
ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
},
attrMap: {
"class": "className",
"for": "htmlFor"
},
attrHandle: {
href: function(elem){
return elem.getAttribute("href");
}
},
relative: {
"+": function(checkSet, part, isXML){
var isPartStr = typeof part === "string",
isTag = isPartStr && !/\W/.test(part),
isPartStrNotTag = isPartStr && !isTag;
if ( isTag && !isXML ) {
part = part.toUpperCase();
}
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
if ( (elem = checkSet[i]) ) {
while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
elem || false :
elem === part;
}
}
if ( isPartStrNotTag ) {
Sizzle.filter( part, checkSet, true );
}
},
">": function(checkSet, part, isXML){
var isPartStr = typeof part === "string";
if ( isPartStr && !/\W/.test(part) ) {
part = isXML ? part : part.toUpperCase();
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName === part ? parent : false;
}
}
} else {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
elem.parentNode === part;
}
}
if ( isPartStr ) {
Sizzle.filter( part, checkSet, true );
}
}
},
"": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck;
if ( !part.match(/\W/) ) {
var nodeCheck = part = isXML ? part : part.toUpperCase();
checkFn = dirNodeCheck;
}
checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
},
"~": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck;
if ( typeof part === "string" && !part.match(/\W/) ) {
var nodeCheck = part = isXML ? part : part.toUpperCase();
checkFn = dirNodeCheck;
}
checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
}
},
find: {
ID: function(match, context, isXML){
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? [m] : [];
}
},
NAME: function(match, context, isXML){
if ( typeof context.getElementsByName !== "undefined" ) {
var ret = [], results = context.getElementsByName(match[1]);
for ( var i = 0, l = results.length; i < l; i++ ) {
if ( results[i].getAttribute("name") === match[1] ) {
ret.push( results[i] );
}
}
return ret.length === 0 ? null : ret;
}
},
TAG: function(match, context){
return context.getElementsByTagName(match[1]);
}
},
preFilter: {
CLASS: function(match, curLoop, inplace, result, not, isXML){
match = " " + match[1].replace(/\\/g, "") + " ";
if ( isXML ) {
return match;
}
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
if ( !inplace )
result.push( elem );
} else if ( inplace ) {
curLoop[i] = false;
}
}
}
return false;
},
ID: function(match){
return match[1].replace(/\\/g, "");
},
TAG: function(match, curLoop){
for ( var i = 0; curLoop[i] === false; i++ ){}
return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
},
CHILD: function(match){
if ( match[1] == "nth" ) {
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
// calculate the numbers (first)n+(last) including if they are negative
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
// TODO: Move to normal caching system
match[0] = done++;
return match;
},
ATTR: function(match, curLoop, inplace, result, not, isXML){
var name = match[1].replace(/\\/g, "");
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
if ( match[2] === "~=" ) {
match[4] = " " + match[4] + " ";
}
return match;
},
PSEUDO: function(match, curLoop, inplace, result, not){
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
if ( !inplace ) {
result.push.apply( result, ret );
}
return false;
}
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
return true;
}
return match;
},
POS: function(match){
match.unshift( true );
return match;
}
},
filters: {
enabled: function(elem){
return elem.disabled === false && elem.type !== "hidden";
},
disabled: function(elem){
return elem.disabled === true;
},
checked: function(elem){
return elem.checked === true;
},
selected: function(elem){
// Accessing this property makes selected-by-default
// options in Safari work properly
elem.parentNode.selectedIndex;
return elem.selected === true;
},
parent: function(elem){
return !!elem.firstChild;
},
empty: function(elem){
return !elem.firstChild;
},
has: function(elem, i, match){
return !!Sizzle( match[3], elem ).length;
},
header: function(elem){
return /h\d/i.test( elem.nodeName );
},
text: function(elem){
return "text" === elem.type;
},
radio: function(elem){
return "radio" === elem.type;
},
checkbox: function(elem){
return "checkbox" === elem.type;
},
file: function(elem){
return "file" === elem.type;
},
password: function(elem){
return "password" === elem.type;
},
submit: function(elem){
return "submit" === elem.type;
},
image: function(elem){
return "image" === elem.type;
},
reset: function(elem){
return "reset" === elem.type;
},
button: function(elem){
return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
},
input: function(elem){
return /input|select|textarea|button/i.test(elem.nodeName);
}
},
setFilters: {
first: function(elem, i){
return i === 0;
},
last: function(elem, i, match, array){
return i === array.length - 1;
},
even: function(elem, i){
return i % 2 === 0;
},
odd: function(elem, i){
return i % 2 === 1;
},
lt: function(elem, i, match){
return i < match[3] - 0;
},
gt: function(elem, i, match){
return i > match[3] - 0;
},
nth: function(elem, i, match){
return match[3] - 0 == i;
},
eq: function(elem, i, match){
return match[3] - 0 == i;
}
},
filter: {
PSEUDO: function(elem, match, i, array){
var name = match[1], filter = Expr.filters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
for ( var i = 0, l = not.length; i < l; i++ ) {
if ( not[i] === elem ) {
return false;
}
}
return true;
}
},
CHILD: function(elem, match){
var type = match[1], node = elem;
switch (type) {
case 'only':
case 'first':
while (node = node.previousSibling) {
if ( node.nodeType === 1 ) return false;
}
if ( type == 'first') return true;
node = elem;
case 'last':
while (node = node.nextSibling) {
if ( node.nodeType === 1 ) return false;
}
return true;
case 'nth':
var first = match[2], last = match[3];
if ( first == 1 && last == 0 ) {
return true;
}
var doneName = match[0],
parent = elem.parentNode;
if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
var count = 0;
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.nodeIndex = ++count;
}
}
parent.sizcache = doneName;
}
var diff = elem.nodeIndex - last;
if ( first == 0 ) {
return diff == 0;
} else {
return ( diff % first == 0 && diff / first >= 0 );
}
}
},
ID: function(elem, match){
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
TAG: function(elem, match){
return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
},
CLASS: function(elem, match){
return (" " + (elem.className || elem.getAttribute("class")) + " ")
.indexOf( match ) > -1;
},
ATTR: function(elem, match){
var name = match[1],
result = Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
elem.getAttribute( name ),
value = result + "",
type = match[2],
check = match[4];
return result == null ?
type === "!=" :
type === "=" ?
value === check :
type === "*=" ?
value.indexOf(check) >= 0 :
type === "~=" ?
(" " + value + " ").indexOf(check) >= 0 :
!check ?
value && result !== false :
type === "!=" ?
value != check :
type === "^=" ?
value.indexOf(check) === 0 :
type === "$=" ?
value.substr(value.length - check.length) === check :
type === "|=" ?
value === check || value.substr(0, check.length + 1) === check + "-" :
false;
},
POS: function(elem, match, i, array){
var name = match[2], filter = Expr.setFilters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
}
}
}
};
var origPOS = Expr.match.POS;
for ( var type in Expr.match ) {
Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}
var makeArray = function(array, results) {
array = Array.prototype.slice.call( array );
if ( results ) {
results.push.apply( results, array );
return results;
}
return array;
};
// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
try {
Array.prototype.slice.call( document.documentElement.childNodes );
// Provide a fallback method if it does not work
} catch(e){
makeArray = function(array, results) {
var ret = results || [];
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
for ( var i = 0, l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
for ( var i = 0; array[i]; i++ ) {
ret.push( array[i] );
}
}
}
return ret;
};
}
var sortOrder;
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
};
} else if ( "sourceIndex" in document.documentElement ) {
sortOrder = function( a, b ) {
var ret = a.sourceIndex - b.sourceIndex;
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
};
} else if ( document.createRange ) {
sortOrder = function( a, b ) {
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.selectNode(a);
aRange.collapse(true);
bRange.selectNode(b);
bRange.collapse(true);
var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
};
}
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
// We're going to inject a fake input element with a specified name
var form = document.createElement("form"),
id = "script" + (new Date).getTime();
form.innerHTML = "<input name='" + id + "'/>";
// Inject it into the root element, check its status, and remove it quickly
var root = document.documentElement;
root.insertBefore( form, root.firstChild );
// The workaround has to do additional checks after a getElementById
// Which slows things down for other browsers (hence the branching)
if ( !!document.getElementById( id ) ) {
Expr.find.ID = function(match, context, isXML){
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
}
};
Expr.filter.ID = function(elem, match){
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return elem.nodeType === 1 && node && node.nodeValue === match;
};
}
root.removeChild( form );
})();
(function(){
// Check to see if the browser returns only elements
// when doing getElementsByTagName("*")
// Create a fake element
var div = document.createElement("div");
div.appendChild( document.createComment("") );
// Make sure no comments are found
if ( div.getElementsByTagName("*").length > 0 ) {
Expr.find.TAG = function(match, context){
var results = context.getElementsByTagName(match[1]);
// Filter out possible comments
if ( match[1] === "*" ) {
var tmp = [];
for ( var i = 0; results[i]; i++ ) {
if ( results[i].nodeType === 1 ) {
tmp.push( results[i] );
}
}
results = tmp;
}
return results;
};
}
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "<a href='#'></a>";
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
div.firstChild.getAttribute("href") !== "#" ) {
Expr.attrHandle.href = function(elem){
return elem.getAttribute("href", 2);
};
}
})();
if ( document.querySelectorAll ) (function(){
var oldSizzle = Sizzle, div = document.createElement("div");
div.innerHTML = "<p class='TEST'></p>";
// Safari can't handle uppercase or unicode characters when
// in quirks mode.
if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
return;
}
Sizzle = function(query, context, extra, seed){
context = context || document;
// Only use querySelectorAll on non-XML documents
// (ID selectors don't work in non-HTML documents)
if ( !seed && context.nodeType === 9 && !isXML(context) ) {
try {
return makeArray( context.querySelectorAll(query), extra );
} catch(e){}
}
return oldSizzle(query, context, extra, seed);
};
Sizzle.find = oldSizzle.find;
Sizzle.filter = oldSizzle.filter;
Sizzle.selectors = oldSizzle.selectors;
Sizzle.matches = oldSizzle.matches;
})();
if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
var div = document.createElement("div");
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
// Opera can't find a second classname (in 9.6)
if ( div.getElementsByClassName("e").length === 0 )
return;
// Safari caches class attributes, doesn't catch changes (in 3.2)
div.lastChild.className = "e";
if ( div.getElementsByClassName("e").length === 1 )
return;
Expr.order.splice(1, 0, "CLASS");
Expr.find.CLASS = function(match, context, isXML) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};
})();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
var sibDir = dir == "previousSibling" && !isXML;
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
if ( sibDir && elem.nodeType === 1 ){
elem.sizcache = doneName;
elem.sizset = i;
}
elem = elem[dir];
var match = false;
while ( elem ) {
if ( elem.sizcache === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 && !isXML ){
elem.sizcache = doneName;
elem.sizset = i;
}
if ( elem.nodeName === cur ) {
match = elem;
break;
}
elem = elem[dir];
}
checkSet[i] = match;
}
}
}
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
var sibDir = dir == "previousSibling" && !isXML;
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
if ( sibDir && elem.nodeType === 1 ) {
elem.sizcache = doneName;
elem.sizset = i;
}
elem = elem[dir];
var match = false;
while ( elem ) {
if ( elem.sizcache === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 ) {
if ( !isXML ) {
elem.sizcache = doneName;
elem.sizset = i;
}
if ( typeof cur !== "string" ) {
if ( elem === cur ) {
match = true;
break;
}
} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
match = elem;
break;
}
}
elem = elem[dir];
}
checkSet[i] = match;
}
}
}
var contains = document.compareDocumentPosition ? function(a, b){
return a.compareDocumentPosition(b) & 16;
} : function(a, b){
return a !== b && (a.contains ? a.contains(b) : true);
};
var isXML = function(elem){
return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
!!elem.ownerDocument && isXML( elem.ownerDocument );
};
var posProcess = function(selector, context){
var tmpSet = [], later = "", match,
root = context.nodeType ? [context] : context;
// Position selectors must be done after the filter
// And so must :not(positional) so we move all PSEUDOs to the end
while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
later += match[0];
selector = selector.replace( Expr.match.PSEUDO, "" );
}
selector = Expr.relative[selector] ? selector + "*" : selector;
for ( var i = 0, l = root.length; i < l; i++ ) {
Sizzle( selector, root[i], tmpSet );
}
return Sizzle.filter( later, tmpSet );
};
// EXPOSE
jQuery.find = Sizzle;
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
Sizzle.selectors.filters.hidden = function(elem){
return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};
Sizzle.selectors.filters.visible = function(elem){
return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};
Sizzle.selectors.filters.animated = function(elem){
return jQuery.grep(jQuery.timers, function(fn){
return elem === fn.elem;
}).length;
};
jQuery.multiFilter = function( expr, elems, not ) {
if ( not ) {
expr = ":not(" + expr + ")";
}
return Sizzle.matches(expr, elems);
};
jQuery.dir = function( elem, dir ){
var matched = [], cur = elem[dir];
while ( cur && cur != document ) {
if ( cur.nodeType == 1 )
matched.push( cur );
cur = cur[dir];
}
return matched;
};
jQuery.nth = function(cur, result, dir, elem){
result = result || 1;
var num = 0;
for ( ; cur; cur = cur[dir] )
if ( cur.nodeType == 1 && ++num == result )
break;
return cur;
};
jQuery.sibling = function(n, elem){
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType == 1 && n != elem )
r.push( n );
}
return r;
};
return;
window.Sizzle = Sizzle;
})();
/*
* A number of helper functions used for managing events.
* Many of the ideas behind this code originated from
* Dean Edwards' addEvent library.
*/
jQuery.event = {
// Bind an event to an element
// Original by Dean Edwards
add: function(elem, types, handler, data) {
if ( elem.nodeType == 3 || elem.nodeType == 8 )
return;
// For whatever reason, IE has trouble passing the window object
// around, causing it to be cloned in the process
if ( elem.setInterval && elem != window )
elem = window;
// Make sure that the function being executed has a unique ID
if ( !handler.guid )
handler.guid = this.guid++;
// if data is passed, bind to handler
if ( data !== undefined ) {
// Create temporary function pointer to original handler
var fn = handler;
// Create unique handler function, wrapped around original handler
handler = this.proxy( fn );
// Store data in unique handler
handler.data = data;
}
// Init the element's event structure
var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
// Handle the second event of a trigger and when
// an event is called after a page has unloaded
return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
jQuery.event.handle.apply(arguments.callee.elem, arguments) :
undefined;
});
// Add elem as a property of the handle function
// This is to prevent a memory leak with non-native
// event in IE.
handle.elem = elem;
// Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
jQuery.each(types.split(/\s+/), function(index, type) {
// Namespaced event handlers
var namespaces = type.split(".");
type = namespaces.shift();
handler.type = namespaces.slice().sort().join(".");
// Get the current list of functions bound to this event
var handlers = events[type];
if ( jQuery.event.specialAll[type] )
jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
// Init the event handler queue
if (!handlers) {
handlers = events[type] = {};
// Check for a special event handler
// Only use addEventListener/attachEvent if the special
// events handler returns false
if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
// Bind the global event handler to the element
if (elem.addEventListener)
elem.addEventListener(type, handle, false);
else if (elem.attachEvent)
elem.attachEvent("on" + type, handle);
}
}
// Add the function to the element's handler list
handlers[handler.guid] = handler;
// Keep track of which events have been used, for global triggering
jQuery.event.global[type] = true;
});
// Nullify elem to prevent memory leaks in IE
elem = null;
},
guid: 1,
global: {},
// Detach an event or set of events from an element
remove: function(elem, types, handler) {
// don't do events on text and comment nodes
if ( elem.nodeType == 3 || elem.nodeType == 8 )
return;
var events = jQuery.data(elem, "events"), ret, index;
if ( events ) {
// Unbind all events for the element
if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
for ( var type in events )
this.remove( elem, type + (types || "") );
else {
// types is actually an event object here
if ( types.type ) {
handler = types.handler;
types = types.type;
}
// Handle multiple events seperated by a space
// jQuery(...).unbind("mouseover mouseout", fn);
jQuery.each(types.split(/\s+/), function(index, type){
// Namespaced event handlers
var namespaces = type.split(".");
type = namespaces.shift();
var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
if ( events[type] ) {
// remove the given handler for the given type
if ( handler )
delete events[type][handler.guid];
// remove all handlers for the given type
else
for ( var handle in events[type] )
// Handle the removal of namespaced events
if ( namespace.test(events[type][handle].type) )
delete events[type][handle];
if ( jQuery.event.specialAll[type] )
jQuery.event.specialAll[type].teardown.call(elem, namespaces);
// remove generic event handler if no more handlers exist
for ( ret in events[type] ) break;
if ( !ret ) {
if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
if (elem.removeEventListener)
elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
else if (elem.detachEvent)
elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
}
ret = null;
delete events[type];
}
}
});
}
// Remove the expando if it's no longer used
for ( ret in events ) break;
if ( !ret ) {
var handle = jQuery.data( elem, "handle" );
if ( handle ) handle.elem = null;
jQuery.removeData( elem, "events" );
jQuery.removeData( elem, "handle" );
}
}
},
// bubbling is internal
trigger: function( event, data, elem, bubbling ) {
// Event object or event type
var type = event.type || event;
if( !bubbling ){
event = typeof event === "object" ?
// jQuery.Event object
event[expando] ? event :
// Object literal
jQuery.extend( jQuery.Event(type), event ) :
// Just the event type (string)
jQuery.Event(type);
if ( type.indexOf("!") >= 0 ) {
event.type = type = type.slice(0, -1);
event.exclusive = true;
}
// Handle a global trigger
if ( !elem ) {
// Don't bubble custom events when global (to avoid too much overhead)
event.stopPropagation();
// Only trigger if we've ever bound an event for it
if ( this.global[type] )
jQuery.each( jQuery.cache, function(){
if ( this.events && this.events[type] )
jQuery.event.trigger( event, data, this.handle.elem );
});
}
// Handle triggering a single element
// don't do events on text and comment nodes
if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
return undefined;
// Clean up in case it is reused
event.result = undefined;
event.target = elem;
// Clone the incoming data, if any
data = jQuery.makeArray(data);
data.unshift( event );
}
event.currentTarget = elem;
// Trigger the event, it is assumed that "handle" is a function
var handle = jQuery.data(elem, "handle");
if ( handle )
handle.apply( elem, data );
// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
event.result = false;
// Trigger the native events (except for clicks on links)
if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
this.triggered = true;
try {
elem[ type ]();
// prevent IE from throwing an error for some hidden elements
} catch (e) {}
}
this.triggered = false;
if ( !event.isPropagationStopped() ) {
var parent = elem.parentNode || elem.ownerDocument;
if ( parent )
jQuery.event.trigger(event, data, parent, true);
}
},
handle: function(event) {
// returned undefined or false
var all, handlers;
event = arguments[0] = jQuery.event.fix( event || window.event );
event.currentTarget = this;
// Namespaced event handlers
var namespaces = event.type.split(".");
event.type = namespaces.shift();
// Cache this now, all = true means, any handler
all = !namespaces.length && !event.exclusive;
var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
handlers = ( jQuery.data(this, "events") || {} )[event.type];
for ( var j in handlers ) {
var handler = handlers[j];
// Filter the functions by class
if ( all || namespace.test(handler.type) ) {
// Pass in a reference to the handler function itself
// So that we can later remove it
event.handler = handler;
event.data = handler.data;
var ret = handler.apply(this, arguments);
if( ret !== undefined ){
event.result = ret;
if ( ret === false ) {
event.preventDefault();
event.stopPropagation();
}
}
if( event.isImmediatePropagationStopped() )
break;
}
}
},
props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix: function(event) {
if ( event[expando] )
return event;
// store a copy of the original event object
// and "clone" to set read-only properties
var originalEvent = event;
event = jQuery.Event( originalEvent );
for ( var i = this.props.length, prop; i; ){
prop = this.props[ --i ];
event[ prop ] = originalEvent[ prop ];
}
// Fix target property, if necessary
if ( !event.target )
event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
// check if target is a textnode (safari)
if ( event.target.nodeType == 3 )
event.target = event.target.parentNode;
// Add relatedTarget, if necessary
if ( !event.relatedTarget && event.fromElement )
event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
// Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && event.clientX != null ) {
var doc = document.documentElement, body = document.body;
event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
}
// Add which for key events
if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
event.which = event.charCode || event.keyCode;
// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
if ( !event.metaKey && event.ctrlKey )
event.metaKey = event.ctrlKey;
// Add which for click: 1 == left; 2 == middle; 3 == right
// Note: button is not normalized, so don't use it
if ( !event.which && event.button )
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
return event;
},
proxy: function( fn, proxy ){
proxy = proxy || function(){ return fn.apply(this, arguments); };
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
// So proxy can be declared as an argument
return proxy;
},
special: {
ready: {
// Make sure the ready event is setup
setup: bindReady,
teardown: function() {}
}
},
specialAll: {
live: {
setup: function( selector, namespaces ){
jQuery.event.add( this, namespaces[0], liveHandler );
},
teardown: function( namespaces ){
if ( namespaces.length ) {
var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
jQuery.each( (jQuery.data(this, "events").live || {}), function(){
if ( name.test(this.type) )
remove++;
});
if ( remove < 1 )
jQuery.event.remove( this, namespaces[0], liveHandler );
}
}
}
}
};
jQuery.Event = function( src ){
// Allow instantiation without the 'new' keyword
if( !this.preventDefault )
return new jQuery.Event(src);
// Event object
if( src && src.type ){
this.originalEvent = src;
this.type = src.type;
// Event type
}else
this.type = src;
// timeStamp is buggy for some events on Firefox(#3843)
// So we won't rely on the native value
this.timeStamp = now();
// Mark it as fixed
this[expando] = true;
};
function returnFalse(){
return false;
}
function returnTrue(){
return true;
}
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
preventDefault: function() {
this.isDefaultPrevented = returnTrue;
var e = this.originalEvent;
if( !e )
return;
// if preventDefault exists run it on the original event
if (e.preventDefault)
e.preventDefault();
// otherwise set the returnValue property of the original event to false (IE)
e.returnValue = false;
},
stopPropagation: function() {
this.isPropagationStopped = returnTrue;
var e = this.originalEvent;
if( !e )
return;
// if stopPropagation exists run it on the original event
if (e.stopPropagation)
e.stopPropagation();
// otherwise set the cancelBubble property of the original event to true (IE)
e.cancelBubble = true;
},
stopImmediatePropagation:function(){
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
},
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse
};
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
var withinElement = function(event) {
// Check if mouse(over|out) are still within the same parent element
var parent = event.relatedTarget;
// Traverse up the tree
while ( parent && parent != this )
try { parent = parent.parentNode; }
catch(e) { parent = this; }
if( parent != this ){
// set the correct event type
event.type = event.data;
// handle event if we actually just moused on to a non sub-element
jQuery.event.handle.apply( this, arguments );
}
};
jQuery.each({
mouseover: 'mouseenter',
mouseout: 'mouseleave'
}, function( orig, fix ){
jQuery.event.special[ fix ] = {
setup: function(){
jQuery.event.add( this, orig, withinElement, fix );
},
teardown: function(){
jQuery.event.remove( this, orig, withinElement );
}
};
});
jQuery.fn.extend({
bind: function( type, data, fn ) {
return type == "unload" ? this.one(type, data, fn) : this.each(function(){
jQuery.event.add( this, type, fn || data, fn && data );
});
},
one: function( type, data, fn ) {
var one = jQuery.event.proxy( fn || data, function(event) {
jQuery(this).unbind(event, one);
return (fn || data).apply( this, arguments );
});
return this.each(function(){
jQuery.event.add( this, type, one, fn && data);
});
},
unbind: function( type, fn ) {
return this.each(function(){
jQuery.event.remove( this, type, fn );
});
},
trigger: function( type, data ) {
return this.each(function(){
jQuery.event.trigger( type, data, this );
});
},
triggerHandler: function( type, data ) {
if( this[0] ){
var event = jQuery.Event(type);
event.preventDefault();
event.stopPropagation();
jQuery.event.trigger( event, data, this[0] );
return event.result;
}
},
toggle: function( fn ) {
// Save reference to arguments for access in closure
var args = arguments, i = 1;
// link all the functions, so any of them can unbind this click handler
while( i < args.length )
jQuery.event.proxy( fn, args[i++] );
return this.click( jQuery.event.proxy( fn, function(event) {
// Figure out which function to execute
this.lastToggle = ( this.lastToggle || 0 ) % i;
// Make sure that clicks stop
event.preventDefault();
// and execute the function
return args[ this.lastToggle++ ].apply( this, arguments ) || false;
}));
},
hover: function(fnOver, fnOut) {
return this.mouseenter(fnOver).mouseleave(fnOut);
},
ready: function(fn) {
// Attach the listeners
bindReady();
// If the DOM is already ready
if ( jQuery.isReady )
// Execute the function immediately
fn.call( document, jQuery );
// Otherwise, remember the function for later
else
// Add the function to the wait list
jQuery.readyList.push( fn );
return this;
},
live: function( type, fn ){
var proxy = jQuery.event.proxy( fn );
proxy.guid += this.selector + type;
jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );
return this;
},
die: function( type, fn ){
jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
return this;
}
});
function liveHandler( event ){
var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
stop = true,
elems = [];
jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
if ( check.test(fn.type) ) {
var elem = jQuery(event.target).closest(fn.data)[0];
if ( elem )
elems.push({ elem: elem, fn: fn });
}
});
elems.sort(function(a,b) {
return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
});
jQuery.each(elems, function(){
if ( this.fn.call(this.elem, event, this.fn.data) === false )
return (stop = false);
});
return stop;
}
function liveConvert(type, selector){
return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
}
jQuery.extend({
isReady: false,
readyList: [],
// Handle when the DOM is ready
ready: function() {
// Make sure that the DOM is not already loaded
if ( !jQuery.isReady ) {
// Remember that the DOM is ready
jQuery.isReady = true;
// If there are functions bound, to execute
if ( jQuery.readyList ) {
// Execute all of them
jQuery.each( jQuery.readyList, function(){
this.call( document, jQuery );
});
// Reset the list of functions
jQuery.readyList = null;
}
// Trigger any bound ready events
jQuery(document).triggerHandler("ready");
}
}
});
var readyBound = false;
function bindReady(){
if ( readyBound ) return;
readyBound = true;
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", function(){
document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
jQuery.ready();
}, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", arguments.callee );
jQuery.ready();
}
});
// If IE and not an iframe
// continually check to see if the document is ready
if ( document.documentElement.doScroll && window == window.top ) (function(){
if ( jQuery.isReady ) return;
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( arguments.callee, 0 );
return;
}
// and execute any waiting functions
jQuery.ready();
})();
}
// A fallback to window.onload, that will always work
jQuery.event.add( window, "load", jQuery.ready );
}
jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
"mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
"change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){
// Handle event binding
jQuery.fn[name] = function(fn){
return fn ? this.bind(name, fn) : this.trigger(name);
};
});
// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover in other browsers
// Window isn't included so as not to unbind existing unload events
jQuery( window ).bind( 'unload', function(){
for ( var id in jQuery.cache )
// Skip the window
if ( id != 1 && jQuery.cache[ id ].handle )
jQuery.event.remove( jQuery.cache[ id ].handle.elem );
});
(function(){
jQuery.support = {};
var root = document.documentElement,
script = document.createElement("script"),
div = document.createElement("div"),
id = "script" + (new Date).getTime();
div.style.display = "none";
div.innerHTML = ' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
var all = div.getElementsByTagName("*"),
a = div.getElementsByTagName("a")[0];
// Can't get basic test support
if ( !all || !all.length || !a ) {
return;
}
jQuery.support = {
// IE strips leading whitespace when .innerHTML is used
leadingWhitespace: div.firstChild.nodeType == 3,
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
tbody: !div.getElementsByTagName("tbody").length,
// Make sure that you can get all elements in an <object> element
// IE 7 always returns no results
objectAll: !!div.getElementsByTagName("object")[0]
.getElementsByTagName("*").length,
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
htmlSerialize: !!div.getElementsByTagName("link").length,
// Get the style information from getAttribute
// (IE uses .cssText insted)
style: /red/.test( a.getAttribute("style") ),
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
hrefNormalized: a.getAttribute("href") === "/a",
// Make sure that element opacity exists
// (IE uses filter instead)
opacity: a.style.opacity === "0.5",
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
cssFloat: !!a.style.cssFloat,
// Will be defined later
scriptEval: false,
noCloneEvent: true,
boxModel: null
};
script.type = "text/javascript";
try {
script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
} catch(e){}
root.insertBefore( script, root.firstChild );
// Make sure that the execution of code works by injecting a script
// tag with appendChild/createTextNode
// (IE doesn't support this, fails, and uses .text instead)
if ( window[ id ] ) {
jQuery.support.scriptEval = true;
delete window[ id ];
}
root.removeChild( script );
if ( div.attachEvent && div.fireEvent ) {
div.attachEvent("onclick", function(){
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
jQuery.support.noCloneEvent = false;
div.detachEvent("onclick", arguments.callee);
});
div.cloneNode(true).fireEvent("onclick");
}
// Figure out if the W3C box model works as expected
// document.body must exist before we can do this
jQuery(function(){
var div = document.createElement("div");
div.style.width = div.style.paddingLeft = "1px";
document.body.appendChild( div );
jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
document.body.removeChild( div ).style.display = 'none';
});
})();
var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";
jQuery.props = {
"for": "htmlFor",
"class": "className",
"float": styleFloat,
cssFloat: styleFloat,
styleFloat: styleFloat,
readonly: "readOnly",
maxlength: "maxLength",
cellspacing: "cellSpacing",
rowspan: "rowSpan",
tabindex: "tabIndex"
};
jQuery.fn.extend({
// Keep a copy of the old load
_load: jQuery.fn.load,
load: function( url, params, callback ) {
if ( typeof url !== "string" )
return this._load( url );
var off = url.indexOf(" ");
if ( off >= 0 ) {
var selector = url.slice(off, url.length);
url = url.slice(0, off);
}
// Default to a GET request
var type = "GET";
// If the second parameter was provided
if ( params )
// If it's a function
if ( jQuery.isFunction( params ) ) {
// We assume that it's the callback
callback = params;
params = null;
// Otherwise, build a param string
} else if( typeof params === "object" ) {
params = jQuery.param( params );
type = "POST";
}
var self = this;
// Request the remote document
jQuery.ajax({
url: url,
type: type,
dataType: "html",
data: params,
complete: function(res, status){
// If successful, inject the HTML into all the matched elements
if ( status == "success" || status == "notmodified" )
// See if a selector was specified
self.html( selector ?
// Create a dummy div to hold the results
jQuery("<div/>")
// inject the contents of the document in, removing the scripts
// to avoid any 'Permission Denied' errors in IE
.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
// Locate the specified elements
.find(selector) :
// If not, just inject the full result
res.responseText );
if( callback )
self.each( callback, [res.responseText, status, res] );
}
});
return this;
},
serialize: function() {
return jQuery.param(this.serializeArray());
},
serializeArray: function() {
return this.map(function(){
return this.elements ? jQuery.makeArray(this.elements) : this;
})
.filter(function(){
return this.name && !this.disabled &&
(this.checked || /select|textarea/i.test(this.nodeName) ||
/text|hidden|password|search/i.test(this.type));
})
.map(function(i, elem){
var val = jQuery(this).val();
return val == null ? null :
jQuery.isArray(val) ?
jQuery.map( val, function(val, i){
return {name: elem.name, value: val};
}) :
{name: elem.name, value: val};
}).get();
}
});
// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
jQuery.fn[o] = function(f){
return this.bind(o, f);
};
});
var jsc = now();
jQuery.extend({
get: function( url, data, callback, type ) {
// shift arguments if data argument was ommited
if ( jQuery.isFunction( data ) ) {
callback = data;
data = null;
}
return jQuery.ajax({
type: "GET",
url: url,
data: data,
success: callback,
dataType: type
});
},
getScript: function( url, callback ) {
return jQuery.get(url, null, callback, "script");
},
getJSON: function( url, data, callback ) {
return jQuery.get(url, data, callback, "json");
},
post: function( url, data, callback, type ) {
if ( jQuery.isFunction( data ) ) {
callback = data;
data = {};
}
return jQuery.ajax({
type: "POST",
url: url,
data: data,
success: callback,
dataType: type
});
},
ajaxSetup: function( settings ) {
jQuery.extend( jQuery.ajaxSettings, settings );
},
ajaxSettings: {
url: location.href,
global: true,
type: "GET",
contentType: "application/x-www-form-urlencoded",
processData: true,
async: true,
/*
timeout: 0,
data: null,
username: null,
password: null,
*/
// Create the request object; Microsoft failed to properly
// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
// This function can be overriden by calling jQuery.ajaxSetup
xhr:function(){
return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
},
accepts: {
xml: "application/xml, text/xml",
html: "text/html",
script: "text/javascript, application/javascript",
json: "application/json, text/javascript",
text: "text/plain",
_default: "*/*"
}
},
// Last-Modified header cache for next request
lastModified: {},
ajax: function( s ) {
// Extend the settings, but re-extend 's' so that it can be
// checked again later (in the test suite, specifically)
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
var jsonp, jsre = /=\?(&|$)/g, status, data,
type = s.type.toUpperCase();
// convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" )
s.data = jQuery.param(s.data);
// Handle JSONP Parameter Callbacks
if ( s.dataType == "jsonp" ) {
if ( type == "GET" ) {
if ( !s.url.match(jsre) )
s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
} else if ( !s.data || !s.data.match(jsre) )
s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
s.dataType = "json";
}
// Build temporary JSONP function
if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
jsonp = "jsonp" + jsc++;
// Replace the =? sequence both in the query string and the data
if ( s.data )
s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
// We need to make sure
// that a JSONP style response is executed properly
s.dataType = "script";
// Handle JSONP-style loading
window[ jsonp ] = function(tmp){
data = tmp;
success();
complete();
// Garbage collect
window[ jsonp ] = undefined;
try{ delete window[ jsonp ]; } catch(e){}
if ( head )
head.removeChild( script );
};
}
if ( s.dataType == "script" && s.cache == null )
s.cache = false;
if ( s.cache === false && type == "GET" ) {
var ts = now();
// try replacing _= if it is there
var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
// if nothing was replaced, add timestamp to the end
s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
}
// If data is available, append data to url for get requests
if ( s.data && type == "GET" ) {
s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;
// IE likes to send both get and post data, prevent this
s.data = null;
}
// Watch for a new set of requests
if ( s.global && ! jQuery.active++ )
jQuery.event.trigger( "ajaxStart" );
// Matches an absolute URL, and saves the domain
var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );
// If we're requesting a remote document
// and trying to load JSON or Script with a GET
if ( s.dataType == "script" && type == "GET" && parts
&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = s.url;
if (s.scriptCharset)
script.charset = s.scriptCharset;
// Handle Script loading
if ( !jsonp ) {
var done = false;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
success();
complete();
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
head.removeChild( script );
}
};
}
head.appendChild(script);
// We handle everything using the script element injection
return undefined;
}
var requestDone = false;
// Create the request object
var xhr = s.xhr();
// Open the socket
// Passing null username, generates a login popup on Opera (#2865)
if( s.username )
xhr.open(type, s.url, s.async, s.username, s.password);
else
xhr.open(type, s.url, s.async);
// Need an extra try/catch for cross domain requests in Firefox 3
try {
// Set the correct header, if data is being sent
if ( s.data )
xhr.setRequestHeader("Content-Type", s.contentType);
// Set the If-Modified-Since header, if ifModified mode.
if ( s.ifModified )
xhr.setRequestHeader("If-Modified-Since",
jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
// Set header so the called script knows that it's an XMLHttpRequest
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// Set the Accepts header for the server, depending on the dataType
xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
s.accepts[ s.dataType ] + ", */*" :
s.accepts._default );
} catch(e){}
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
// close opended socket
xhr.abort();
return false;
}
if ( s.global )
jQuery.event.trigger("ajaxSend", [xhr, s]);
// Wait for a response to come back
var onreadystatechange = function(isTimeout){
// The request was aborted, clear the interval and decrement jQuery.active
if (xhr.readyState == 0) {
if (ival) {
// clear poll interval
clearInterval(ival);
ival = null;
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
}
// The transfer is complete and the data is available, or the request timed out
} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
requestDone = true;
// clear poll interval
if (ival) {
clearInterval(ival);
ival = null;
}
status = isTimeout == "timeout" ? "timeout" :
!jQuery.httpSuccess( xhr ) ? "error" :
s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
"success";
if ( status == "success" ) {
// Watch for, and catch, XML document parse errors
try {
// process the data (runs the xml through httpData regardless of callback)
data = jQuery.httpData( xhr, s.dataType, s );
} catch(e) {
status = "parsererror";
}
}
// Make sure that the request was successful or notmodified
if ( status == "success" ) {
// Cache Last-Modified header, if ifModified mode.
var modRes;
try {
modRes = xhr.getResponseHeader("Last-Modified");
} catch(e) {} // swallow exception thrown by FF if header is not available
if ( s.ifModified && modRes )
jQuery.lastModified[s.url] = modRes;
// JSONP handles its own success callback
if ( !jsonp )
success();
} else
jQuery.handleError(s, xhr, status);
// Fire the complete handlers
complete();
if ( isTimeout )
xhr.abort();
// Stop memory leaks
if ( s.async )
xhr = null;
}
};
if ( s.async ) {
// don't attach the handler to the request, just poll it instead
var ival = setInterval(onreadystatechange, 13);
// Timeout checker
if ( s.timeout > 0 )
setTimeout(function(){
// Check to see if the request is still happening
if ( xhr && !requestDone )
onreadystatechange( "timeout" );
}, s.timeout);
}
// Send the data
try {
xhr.send(s.data);
} catch(e) {
jQuery.handleError(s, xhr, null, e);
}
// firefox 1.5 doesn't fire statechange for sync requests
if ( !s.async )
onreadystatechange();
function success(){
// If a local callback was specified, fire it and pass it the data
if ( s.success )
s.success( data, status );
// Fire the global callback
if ( s.global )
jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
}
function complete(){
// Process result
if ( s.complete )
s.complete(xhr, status);
// The request was completed
if ( s.global )
jQuery.event.trigger( "ajaxComplete", [xhr, s] );
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
}
// return XMLHttpRequest to allow aborting the request etc.
return xhr;
},
handleError: function( s, xhr, status, e ) {
// If a local callback was specified, fire it
if ( s.error ) s.error( xhr, status, e );
// Fire the global callback
if ( s.global )
jQuery.event.trigger( "ajaxError", [xhr, s, e] );
},
// Counter for holding the number of active queries
active: 0,
// Determines if an XMLHttpRequest was successful or not
httpSuccess: function( xhr ) {
try {
// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
return !xhr.status && location.protocol == "file:" ||
( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
} catch(e){}
return false;
},
// Determines if an XMLHttpRequest returns NotModified
httpNotModified: function( xhr, url ) {
try {
var xhrRes = xhr.getResponseHeader("Last-Modified");
// Firefox always returns 200. check Last-Modified date
return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
} catch(e){}
return false;
},
httpData: function( xhr, type, s ) {
var ct = xhr.getResponseHeader("content-type"),
xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
data = xml ? xhr.responseXML : xhr.responseText;
if ( xml && data.documentElement.tagName == "parsererror" )
throw "parsererror";
// Allow a pre-filtering function to sanitize the response
// s != null is checked to keep backwards compatibility
if( s && s.dataFilter )
data = s.dataFilter( data, type );
// The filter can actually parse the response
if( typeof data === "string" ){
// If the type is "script", eval it in global context
if ( type == "script" )
jQuery.globalEval( data );
// Get the JavaScript object, if JSON is used.
if ( type == "json" )
data = window["eval"]("(" + data + ")");
}
return data;
},
// Serialize an array of form elements or a set of
// key/values into a query string
param: function( a ) {
var s = [ ];
function add( key, value ){
s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
};
// If an array was passed in, assume that it is an array
// of form elements
if ( jQuery.isArray(a) || a.jquery )
// Serialize the form elements
jQuery.each( a, function(){
add( this.name, this.value );
});
// Otherwise, assume that it's an object of key/value pairs
else
// Serialize the key/values
for ( var j in a )
// If the value is an array then the key names need to be repeated
if ( jQuery.isArray(a[j]) )
jQuery.each( a[j], function(){
add( j, this );
});
else
add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
// Return the resulting serialization
return s.join("&").replace(/%20/g, "+");
}
});
var elemdisplay = {},
timerId,
fxAttrs = [
// height animations
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
// width animations
[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
// opacity animations
[ "opacity" ]
];
function genFx( type, num ){
var obj = {};
jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
obj[ this ] = type;
});
return obj;
}
jQuery.fn.extend({
show: function(speed,callback){
if ( speed ) {
return this.animate( genFx("show", 3), speed, callback);
} else {
for ( var i = 0, l = this.length; i < l; i++ ){
var old = jQuery.data(this[i], "olddisplay");
this[i].style.display = old || "";
if ( jQuery.css(this[i], "display") === "none" ) {
var tagName = this[i].tagName, display;
if ( elemdisplay[ tagName ] ) {
display = elemdisplay[ tagName ];
} else {
var elem = jQuery("<" + tagName + " />").appendTo("body");
display = elem.css("display");
if ( display === "none" )
display = "block";
elem.remove();
elemdisplay[ tagName ] = display;
}
jQuery.data(this[i], "olddisplay", display);
}
}
// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( var i = 0, l = this.length; i < l; i++ ){
this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
}
return this;
}
},
hide: function(speed,callback){
if ( speed ) {
return this.animate( genFx("hide", 3), speed, callback);
} else {
for ( var i = 0, l = this.length; i < l; i++ ){
var old = jQuery.data(this[i], "olddisplay");
if ( !old && old !== "none" )
jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
}
// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( var i = 0, l = this.length; i < l; i++ ){
this[i].style.display = "none";
}
return this;
}
},
// Save the old toggle function
_toggle: jQuery.fn.toggle,
toggle: function( fn, fn2 ){
var bool = typeof fn === "boolean";
return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
this._toggle.apply( this, arguments ) :
fn == null || bool ?
this.each(function(){
var state = bool ? fn : jQuery(this).is(":hidden");
jQuery(this)[ state ? "show" : "hide" ]();
}) :
this.animate(genFx("toggle", 3), fn, fn2);
},
fadeTo: function(speed,to,callback){
return this.animate({opacity: to}, speed, callback);
},
animate: function( prop, speed, easing, callback ) {
var optall = jQuery.speed(speed, easing, callback);
return this[ optall.queue === false ? "each" : "queue" ](function(){
var opt = jQuery.extend({}, optall), p,
hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
self = this;
for ( p in prop ) {
if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
return opt.complete.call(this);
if ( ( p == "height" || p == "width" ) && this.style ) {
// Store display property
opt.display = jQuery.css(this, "display");
// Make sure that nothing sneaks out
opt.overflow = this.style.overflow;
}
}
if ( opt.overflow != null )
this.style.overflow = "hidden";
opt.curAnim = jQuery.extend({}, prop);
jQuery.each( prop, function(name, val){
var e = new jQuery.fx( self, opt, name );
if ( /toggle|show|hide/.test(val) )
e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
else {
var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
start = e.cur(true) || 0;
if ( parts ) {
var end = parseFloat(parts[2]),
unit = parts[3] || "px";
// We need to compute starting value
if ( unit != "px" ) {
self.style[ name ] = (end || 1) + unit;
start = ((end || 1) / e.cur(true)) * start;
self.style[ name ] = start + unit;
}
// If a +=/-= token was provided, we're doing a relative animation
if ( parts[1] )
end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
e.custom( start, end, unit );
} else
e.custom( start, val, "" );
}
});
// For JS strict compliance
return true;
});
},
stop: function(clearQueue, gotoEnd){
var timers = jQuery.timers;
if (clearQueue)
this.queue([]);
this.each(function(){
// go in reverse order so anything added to the queue during the loop is ignored
for ( var i = timers.length - 1; i >= 0; i-- )
if ( timers[i].elem == this ) {
if (gotoEnd)
// force the next step to be the last
timers[i](true);
timers.splice(i, 1);
}
});
// start the next in the queue if the last step wasn't forced
if (!gotoEnd)
this.dequeue();
return this;
}
});
// Generate shortcuts for custom animations
jQuery.each({
slideDown: genFx("show", 1),
slideUp: genFx("hide", 1),
slideToggle: genFx("toggle", 1),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" }
}, function( name, props ){
jQuery.fn[ name ] = function( speed, callback ){
return this.animate( props, speed, callback );
};
});
jQuery.extend({
speed: function(speed, easing, fn) {
var opt = typeof speed === "object" ? speed : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
};
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
// Queueing
opt.old = opt.complete;
opt.complete = function(){
if ( opt.queue !== false )
jQuery(this).dequeue();
if ( jQuery.isFunction( opt.old ) )
opt.old.call( this );
};
return opt;
},
easing: {
linear: function( p, n, firstNum, diff ) {
return firstNum + diff * p;
},
swing: function( p, n, firstNum, diff ) {
return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
}
},
timers: [],
fx: function( elem, options, prop ){
this.options = options;
this.elem = elem;
this.prop = prop;
if ( !options.orig )
options.orig = {};
}
});
jQuery.fx.prototype = {
// Simple function for setting a style value
update: function(){
if ( this.options.step )
this.options.step.call( this.elem, this.now, this );
(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
// Set display property to block for height/width animations
if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
this.elem.style.display = "block";
},
// Get the current size
cur: function(force){
if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
return this.elem[ this.prop ];
var r = parseFloat(jQuery.css(this.elem, this.prop, force));
return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
},
// Start an animation from one number to another
custom: function(from, to, unit){
this.startTime = now();
this.start = from;
this.end = to;
this.unit = unit || this.unit || "px";
this.now = this.start;
this.pos = this.state = 0;
var self = this;
function t(gotoEnd){
return self.step(gotoEnd);
}
t.elem = this.elem;
if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval(function(){
var timers = jQuery.timers;
for ( var i = 0; i < timers.length; i++ )
if ( !timers[i]() )
timers.splice(i--, 1);
if ( !timers.length ) {
clearInterval( timerId );
timerId = undefined;
}
}, 13);
}
},
// Simple 'show' function
show: function(){
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
this.options.show = true;
// Begin the animation
// Make sure that we start at a small width/height to avoid any
// flash of content
this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());
// Start by showing the element
jQuery(this.elem).show();
},
// Simple 'hide' function
hide: function(){
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
this.options.hide = true;
// Begin the animation
this.custom(this.cur(), 0);
},
// Each step of an animation
step: function(gotoEnd){
var t = now();
if ( gotoEnd || t >= this.options.duration + this.startTime ) {
this.now = this.end;
this.pos = this.state = 1;
this.update();
this.options.curAnim[ this.prop ] = true;
var done = true;
for ( var i in this.options.curAnim )
if ( this.options.curAnim[i] !== true )
done = false;
if ( done ) {
if ( this.options.display != null ) {
// Reset the overflow
this.elem.style.overflow = this.options.overflow;
// Reset the display
this.elem.style.display = this.options.display;
if ( jQuery.css(this.elem, "display") == "none" )
this.elem.style.display = "block";
}
// Hide the element if the "hide" operation was done
if ( this.options.hide )
jQuery(this.elem).hide();
// Reset the properties, if the item has been hidden or shown
if ( this.options.hide || this.options.show )
for ( var p in this.options.curAnim )
jQuery.attr(this.elem.style, p, this.options.orig[p]);
// Execute the complete function
this.options.complete.call( this.elem );
}
return false;
} else {
var n = t - this.startTime;
this.state = n / this.options.duration;
// Perform the easing function, defaults to swing
this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
this.now = this.start + ((this.end - this.start) * this.pos);
// Perform the next step of the animation
this.update();
}
return true;
}
};
jQuery.extend( jQuery.fx, {
speeds:{
slow: 600,
fast: 200,
// Default speed
_default: 400
},
step: {
opacity: function(fx){
jQuery.attr(fx.elem.style, "opacity", fx.now);
},
_default: function(fx){
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
fx.elem.style[ fx.prop ] = fx.now + fx.unit;
else
fx.elem[ fx.prop ] = fx.now;
}
}
});
if ( document.documentElement["getBoundingClientRect"] )
jQuery.fn.offset = function() {
if ( !this[0] ) return { top: 0, left: 0 };
if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
var box = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
top = box.top + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
return { top: top, left: left };
};
else
jQuery.fn.offset = function() {
if ( !this[0] ) return { top: 0, left: 0 };
if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
jQuery.offset.initialized || jQuery.offset.initialize();
var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
body = doc.body, defaultView = doc.defaultView,
prevComputedStyle = defaultView.getComputedStyle(elem, null),
top = elem.offsetTop, left = elem.offsetLeft;
while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
computedStyle = defaultView.getComputedStyle(elem, null);
top -= elem.scrollTop, left -= elem.scrollLeft;
if ( elem === offsetParent ) {
top += elem.offsetTop, left += elem.offsetLeft;
if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
top += parseInt( computedStyle.borderTopWidth, 10) || 0,
left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
}
if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
top += parseInt( computedStyle.borderTopWidth, 10) || 0,
left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
prevComputedStyle = computedStyle;
}
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
top += body.offsetTop,
left += body.offsetLeft;
if ( prevComputedStyle.position === "fixed" )
top += Math.max(docElem.scrollTop, body.scrollTop),
left += Math.max(docElem.scrollLeft, body.scrollLeft);
return { top: top, left: left };
};
jQuery.offset = {
initialize: function() {
if ( this.initialized ) return;
var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
for ( prop in rules ) container.style[prop] = rules[prop];
container.innerHTML = html;
body.insertBefore(container, body.firstChild);
innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;
this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
body.style.marginTop = '1px';
this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
body.style.marginTop = bodyMarginTop;
body.removeChild(container);
this.initialized = true;
},
bodyOffset: function(body) {
jQuery.offset.initialized || jQuery.offset.initialize();
var top = body.offsetTop, left = body.offsetLeft;
if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
top += parseInt( jQuery.curCSS(body, 'marginTop', true), 10 ) || 0,
left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
return { top: top, left: left };
}
};
jQuery.fn.extend({
position: function() {
var left = 0, top = 0, results;
if ( this[0] ) {
// Get *real* offsetParent
var offsetParent = this.offsetParent(),
// Get correct offsets
offset = this.offset(),
parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();
// Subtract element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
offset.top -= num( this, 'marginTop' );
offset.left -= num( this, 'marginLeft' );
// Add offsetParent borders
parentOffset.top += num( offsetParent, 'borderTopWidth' );
parentOffset.left += num( offsetParent, 'borderLeftWidth' );
// Subtract the two offsets
results = {
top: offset.top - parentOffset.top,
left: offset.left - parentOffset.left
};
}
return results;
},
offsetParent: function() {
var offsetParent = this[0].offsetParent || document.body;
while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
offsetParent = offsetParent.offsetParent;
return jQuery(offsetParent);
}
});
// Create scrollLeft and scrollTop methods
jQuery.each( ['Left', 'Top'], function(i, name) {
var method = 'scroll' + name;
jQuery.fn[ method ] = function(val) {
if (!this[0]) return null;
return val !== undefined ?
// Set the scroll offset
this.each(function() {
this == window || this == document ?
window.scrollTo(
!i ? val : jQuery(window).scrollLeft(),
i ? val : jQuery(window).scrollTop()
) :
this[ method ] = val;
}) :
// Return the scroll offset
this[0] == window || this[0] == document ?
self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
jQuery.boxModel && document.documentElement[ method ] ||
document.body[ method ] :
this[0][ method ];
};
});
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function(i, name){
var tl = i ? "Left" : "Top", // top or left
br = i ? "Right" : "Bottom", // bottom or right
lower = name.toLowerCase();
// innerHeight and innerWidth
jQuery.fn["inner" + name] = function(){
return this[0] ?
jQuery.css( this[0], lower, false, "padding" ) :
null;
};
// outerHeight and outerWidth
jQuery.fn["outer" + name] = function(margin) {
return this[0] ?
jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
null;
};
var type = name.toLowerCase();
jQuery.fn[ type ] = function( size ) {
// Get window width or height
return this[0] == window ?
// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
document.body[ "client" + name ] :
// Get document width or height
this[0] == document ?
// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
Math.max(
document.documentElement["client" + name],
document.body["scroll" + name], document.documentElement["scroll" + name],
document.body["offset" + name], document.documentElement["offset" + name]
) :
// Get or set width or height on the element
size === undefined ?
// Get width or height on the element
(this.length ? jQuery.css( this[0], type ) : null) :
// Set the width or height on the element (default to pixels if value is unitless)
this.css( type, typeof size === "string" ? size : size + "px" );
};
});
})();

View File

@ -0,0 +1,75 @@
/**
* Very basic Class utility. Based on base and jquery.class.
*
* Class definition: var Foo = Base.extend({ init: function(){ Constructor }; method_name: function(){ Method } });
*
* Inheritance: var Bar = Foo.extend({ method_name: function(){ this._super(); } });
*
* new-less Constructor: new Foo(arg) <-same as-> Foo(arg)
*/
var Base;
(function(){
var marker = {}, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Base = function(){};
Base.addMethod = function(name, func) {
var _super = this._super;
if (_super && fnTest.test(func)) {
this.prototype[name] = function(){
var tmp = this._super;
this._super = _super[name];
try {
var ret = func.apply(this, arguments);
}
finally {
this._super = tmp;
}
return ret;
}
}
else this.prototype[name] = func;
}
Base.addMethods = function(props) {
for (var name in props) {
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
else this.prototype[name] = props[name];
}
}
// Create a new Class that inherits from this class
Base.extend = function(props) {
// The dummy class constructor
var Kls = function() {
if (arguments[0] === marker) return;
if (this instanceof Kls) {
if (this.init) this.init.apply(this, arguments);
}
else {
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
}
}
// Add the common class variables and methods
Kls.constructor = Kls;
Kls.extend = Base.extend;
Kls.addMethod = Base.addMethod;
Kls.addMethods = Base.addMethods;
Kls._super = this.prototype;
// Attach the parent object to the inheritance chain
Kls.prototype = new this(marker);
// Copy the properties over onto the new prototype
Kls.addMethods(props);
return Kls;
};
})();

View File

@ -0,0 +1,188 @@
(function($){
var tokens = {
UNICODE: /\\[0-9a-f]{1,6}(?:\r\n|[ \n\r\t\f])?/,
ESCAPE: /(?:UNICODE)|\\[^\n\r\f0-9a-f]/,
NONASCII: /[^\x00-\x7F]/,
NMSTART: /[_a-z]|(?:NONASCII)|(?:ESCAPE)/,
NMCHAR: /[_a-z0-9-]|(?:NONASCII)|(?:ESCAPE)/,
IDENT: /-?(?:NMSTART)(?:NMCHAR)*/,
NL: /\n|\r\n|\r|\f/,
STRING: /(?:STRING1)|(?:STRING2)|(?:STRINGBARE)/,
STRING1: /"(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\"])*"/,
STRING2: /'(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\'])*'/,
STRINGBARE: /(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\]])*/,
FUNCTION: /(?:IDENT)\(\)/,
INTEGER: /[0-9]+/,
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
WITHOUTN: /([-+])?(INTEGER)/
}
var rx = {
not: /:not\(/,
not_end: /\)/,
tag: /((?:IDENT)|\*)/,
id: /#(IDENT)/,
cls: /\.(IDENT)/,
attr: /\[\s*(IDENT)\s*(?:([^=]?=)\s*(STRING)\s*)?\]/,
pseudo_el: /(?::(first-line|first-letter|before|after))|(?:::((?:FUNCTION)|(?:IDENT)))/,
pseudo_cls_nth: /:nth-child\(\s*(?:(?:WITHN)|(?:WITHOUTN)|(odd|even))\s*\)/,
pseudo_cls: /:(IDENT)/,
comb: /\s*(\+|~|>)\s*|\s+/,
comma: /\s*,\s*/,
important: /\s+!important\s*$/
}
/* Replace placeholders with actual regex, and mark all as case insensitive */
var token = /[A-Z][A-Z0-9]+/;
for (var k in rx) {
var src = rx[k].source;
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
rx[k] = new RegExp(src, 'gi');
}
/**
* A string that matches itself against regexii, and keeps track of how much of itself has been matched
*/
var ConsumableString = Base.extend({
init: function(str) {
this.str = str;
this.pos = 0;
},
match: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) {
this.pos = rx.lastIndex ? rx.lastIndex : this.str.length ;
return m;
}
return null;
},
peek: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) return m;
return null;
},
showpos: function() {
return this.str.slice(0,this.pos)+'<HERE>' + this.str.slice(this.pos);
},
done: function() {
return this.pos == this.str.length;
}
})
/* A base class that all Selectors inherit off */
var SelectorBase = Base.extend({});
/**
* A class representing a Simple Selector, as per the CSS3 selector spec
*/
var SimpleSelector = SelectorBase.extend({
init: function() {
this.tag = null;
this.id = null;
this.classes = [];
this.attrs = [];
this.nots = [];
this.pseudo_classes = [];
this.pseudo_els = [];
},
parse: function(selector) {
var m;
/* Pull out the initial tag first, if there is one */
if (m = selector.match(rx.tag)) this.tag = m[1];
/* Then for each selection type, try and find a match */
do {
if (m = selector.match(rx.not)) {
this.nots[this.nots.length] = SelectorsGroup().parse(selector)
if (!(m = selector.match(rx.not_end))) {
throw 'Invalid :not term in selector';
}
}
else if (m = selector.match(rx.id)) this.id = m[1];
else if (m = selector.match(rx.cls)) this.classes[this.classes.length] = m[1];
else if (m = selector.match(rx.attr)) this.attrs[this.attrs.length] = [ m[1], m[2], m[3] ];
else if (m = selector.match(rx.pseudo_el)) this.pseudo_els[this.pseudo_els.length] = m[1] || m[2];
else if (m = selector.match(rx.pseudo_cls_nth)) {
if (m[3]) {
var a = parseInt((m[1]||'')+(m[2]||'1'));
var b = parseInt((m[4]||'')+(m[5]||'0'));
}
else {
var a = m[8] ? 2 : 0;
var b = m[8] ? (4-m[8].length) : parseInt((m[6]||'')+m[7]);
}
this.pseudo_classes[this.pseudo_classes.length] = ['nth-child', [a, b]];
}
else if (m = selector.match(rx.pseudo_cls)) this.pseudo_classes[this.pseudo_classes.length] = [m[1]];
} while(m && !selector.done());
return this;
}
})
/**
* A class representing a Selector, as per the CSS3 selector spec
*/
var Selector = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = SimpleSelector().parse(cons);
while (!cons.done() && !cons.peek(rx.comma) && (m = cons.match(rx.comb))) {
this.parts[this.parts.length] = m[1] || ' ';
this.parts[this.parts.length] = SimpleSelector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
/**
* A class representing a sequence of selectors, as per the CSS3 selector spec
*/
var SelectorsGroup = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = Selector().parse(cons);
while (!cons.done() && (m = cons.match(rx.comma))) {
this.parts[this.parts.length] = Selector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
$.selector = function(s){
var cons = ConsumableString(s);
var res = SelectorsGroup().parse(cons);
res.selector = s;
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
else return res;
}
$.selector.SelectorBase = SelectorBase;
$.selector.SimpleSelector = SimpleSelector;
$.selector.Selector = Selector;
$.selector.SelectorsGroup = SelectorsGroup;
})(jQuery)

View File

@ -0,0 +1,353 @@
/*
This attempts to do the opposite of Sizzle.
Sizzle is good for finding elements for a selector, but not so good for telling if an individual element matches a selector
*/
(function($) {
/**** CAPABILITY TESTS ****/
var div = document.createElement('div');
div.innerHTML = '<form id="test"><input name="id" type="text"/></form>';
// In IE 6-7, getAttribute often does the wrong thing (returns similar to el.attr), so we need to use getAttributeNode on that browser
var getAttributeDodgy = div.firstChild.getAttribute('id') !== 'test';
// Does browser support Element.firstElementChild, Element.previousElementSibling, etc.
var hasElementTraversal = div.firstElementChild && div.firstElementChild.tagName == 'FORM';
// Does browser support Element.children
var hasChildren = div.children && div.children[0].tagName == 'FORM';
var FUNC_IN = /^\s*function\s*\([^)]*\)\s*{/;
var FUNC_OUT = /}\s*$/;
var funcToString = function(f) {
return (''+f).replace(FUNC_IN,'').replace(FUNC_OUT,'');
}
// Can we use Function#toString ?
try {
var testFunc = function(){ return 'good' };
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
}
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
/**** INTRO ****/
var GOOD = /GOOD/g;
var BAD = /BAD/g;
var STARTS_WITH_QUOTES = /^['"]/g;
var join = function(js) {
return js.join('\n');
}
var join_complex = function(js) {
code = new String(js.join('\n')); // String objects can have properties set. strings can't
code.complex = true;
return code;
}
/**** ATTRIBUTE ACCESSORS ****/
var getAttr;
// Good browsers
if (!getAttributeDodgy) {
getAttr = function(attr){ return 'var _'+attr+' = el.getAttribute("'+attr+'");' ; }
}
// IE 6, 7
else {
// On IE 6 + 7, getAttribute still has to be called with DOM property mirror name, not attribute name. Map attributes to those names
var getAttrIEMap = { 'class': 'className', 'for': 'htmlFor' };
getAttr = function(attr) {
var ieattr = getAttrIEMap[attr] || attr;
return 'var _'+attr+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
}
}
/**** ATTRIBUTE COMPARITORS ****/
var attrchecks = {
'-': '!_K',
'=': '_K != "V"',
'!=': '_K == "V"',
'~=': '__K.indexOf(" V ") == -1',
'^=': '!_K || _K.indexOf("V") != 0',
'*=': '!_K || _K.indexOf("V") == -1',
'$=': '!_K || _K.substr(_K.length-"V".length) != "V"'
}
/**** STATE TRACKER ****/
var State = $.selector.State = Base.extend({
init: function(){
this.reset();
},
reset: function() {
this.attrs = {}; this.wsattrs = {};
},
prev: function(){
this.reset();
if (hasElementTraversal) return 'el = el.previousElementSibling';
return 'while((el = el.previousSibling) && el.nodeType != 1) {}';
},
next: function() {
this.reset();
if (hasElementTraversal) return 'el = el.nextElementSibling';
return 'while((el = el.nextSibling) && el.nodeType != 1) {}';
},
prevLoop: function(body){
this.reset();
if (hasElementTraversal) return join([ 'while(el = el.previousElementSibling){', body]);
return join([
'while(el = el.previousSibling){',
'if (el.nodeType != 1) continue;',
body,
]);
},
parent: function() {
this.reset();
return 'el = el.parentNode;';
},
parentLoop: function(body) {
this.reset();
return join([
'while((el = el.parentNode) && el.nodeType == 1){',
body,
'}'
]);
},
uses_attr: function(attr) {
if (this.attrs[attr]) return;
this.attrs[attr] = true;
return getAttr(attr);
},
uses_wsattr: function(attr) {
if (this.wsattrs[attr]) return;
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var __'+attr+' = " "+_'+attr+'+" ";']);
},
save: function(lbl) {
return 'var el'+lbl+' = el;';
},
restore: function(lbl) {
this.reset();
return 'el = el'+lbl+';';
}
});
/**** PSEUDO-CLASS DETAILS ****/
var pseudoclschecks = {
'first-child': join([
'var cel = el;',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }',
]),
'last-child': join([
'var cel = el;',
'while(cel = cel.nextSibling){ if (cel.nodeType === 1) BAD; }'
]),
'nth-child': function(a,b) {
var get_i = join([
'var i = 1, cel = el;',
'while(cel = cel.previousSibling){',
'if (cel.nodeType === 1) i++;',
'}',
]);
if (a == 0) return join([
get_i,
'if (i- '+b+' != 0) BAD;'
]);
else if (b == 0 && a >= 0) return join([
get_i,
'if (i%'+a+' != 0 || i/'+a+' < 0) BAD;'
]);
else if (b == 0 && a < 0) return join([
'BAD;'
]);
else return join([
get_i,
'if ((i- '+b+')%'+a+' != 0 || (i- '+b+')/'+a+' < 0) BAD;'
]);
}
};
// Needs to refence contents of object, so must be injected after definition
pseudoclschecks['only-child'] = join([
pseudoclschecks['first-child'],
pseudoclschecks['last-child']
]);
/**** SimpleSelector ****/
$.selector.SimpleSelector.addMethod('compile', function(el) {
var js = [];
/* Check against element name */
if (this.tag && this.tag != '*') {
js[js.length] = 'if (el.tagName != "'+this.tag.toUpperCase()+'") BAD;';
}
/* Check against ID */
if (this.id) {
js[js.length] = el.uses_attr('id');
js[js.length] = 'if (_id !== "'+this.id+'") BAD;';
}
/* Build className checking variable */
if (this.classes.length) {
js[js.length] = el.uses_wsattr('class');
/* Check against class names */
$.each(this.classes, function(i, cls){
js[js.length] = 'if (__class.indexOf(" '+cls+' ") == -1) BAD;';
})
}
/* Check against attributes */
$.each(this.attrs, function(i, attr){
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
var check = attrchecks[ attr[1] || '-' ];
check = check.replace( /K/g, attr[0]).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
js[js.length] = 'if ('+check+') BAD;';
});
/* Check against nots */
$.each(this.nots, function(i, not){
var lbl = ++lbl_id;
var func = join([
'l'+lbl+':{',
not.compile(el).replace(BAD, 'break l'+lbl).replace(GOOD, 'BAD'),
'}'
]);
if (!(not instanceof $.selector.SimpleSelector)) func = join([
el.save(lbl),
func,
el.restore(lbl)
])
js[js.length] = func;
});
/* Check against pseudo-classes */
$.each(this.pseudo_classes, function(i, pscls){
var check = pseudoclschecks[pscls[0]];
if (check) {
js[js.length] = ( typeof check == 'function' ? check.apply(this, pscls[1]) : check );
}
else if (check = $.find.selectors.filters[pscls[0]]) {
if (funcToString) {
js[js.length] = funcToString(check).replace(/elem/g,'el').replace(/return([^;]+);/,'if (!($1)) BAD;');
}
else {
js[js.length] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;'
}
}
});
js[js.length] = 'GOOD';
/* Pass */
return join(js);
});
var lbl_id = 0;
/** Turns an compiled fragment into the first part of a combination */
function as_subexpr(f) {
if (f.complex)
return join([
'l'+(++lbl_id)+':{',
f.replace(GOOD, 'break l'+lbl_id),
'}',
]);
else
return f.replace(GOOD, '');
}
var combines = {
' ': function(el, f1, f2) {
return join_complex([
f2,
'while(true){',
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el).replace(BAD, 'continue'),
'}'
]);
},
'>': function(el, f1, f2) {
return join([
f2,
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el)
]);
},
'~': function(el, f1, f2) {
return join_complex([
f2,
el.prevLoop(),
f1.compile(el).replace(BAD, 'continue'),
'}',
'BAD;'
]);
},
'+': function(el, f1, f2) {
return join([
f2,
el.prev(),
'if (!el) BAD;',
f1.compile(el)
]);
}
};
$.selector.Selector.addMethod('compile', function(el) {
l = this.parts.length;
expr = this.parts[--l].compile(el);
while (l) {
combinator = this.parts[--l];
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
}
return expr;
});
$.selector.SelectorsGroup.addMethod('compile', function(el) {
var expr = [], lbl = ++lbl_id;
for (var i=0; i < this.parts.length; i++) {
expr[expr.length] = join([
i == 0 ? el.save(lbl) : el.restore(lbl),
'l'+lbl+'_'+i+':{',
this.parts[i].compile(el).replace(BAD, 'break l'+lbl+'_'+i),
'}'
]);
}
expr[expr.length] = 'BAD;';
return join(expr);
});
$.selector.SelectorBase.addMethod('matches', function(el){
this.matches = new Function('el', join([
'if (!el) return false;',
this.compile(new State()).replace(BAD, 'return false').replace(GOOD, 'return true')
]));
return this.matches(el);
});
})(jQuery);

View File

@ -0,0 +1,42 @@
(function($) {
$.selector.SimpleSelector.addMethod('specifity', function() {
if (this.spec) return this.spec;
var spec = [
this.id ? 1 : 0,
this.classes.length + this.attrs.length + this.pseudo_classes.length,
((this.tag && this.tag != '*') ? 1 : 0) + this.pseudo_els.length
];
$.each(this.nots, function(i,not){
var ns = not.specifity(); spec[0] += ns[0]; spec[1] += ns[1]; spec[2] += ns[2];
});
return this.spec = spec;
})
$.selector.Selector.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
if (i%2) return;
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
})
$.selector.SelectorsGroup.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
})
})(jQuery);

View File

@ -0,0 +1,8 @@
---
format: 1
lock: false
repository_class: Piston::Git::Repository
handler:
commit: 75336bc94061b818f90361ff163436c8eb61d4c3
branch: tags/2.8.4
repository_url: git://github.com/visionmedia/jspec.git

View File

@ -0,0 +1,473 @@
=== 2.8.4 / 2009-08-02
* Fixed error thrown when a module has no utilities
=== 2.8.3 / 2009-07-30
* Added JSpec.evalHook()
* Added JSpec.paramsFor()
* Refactored jspec.xhr.js
* Fixed mock xhr HEAD method
* Fixed node.js print() newline issue
* Fixed specs preventing spec/spec.node.js from running
=== 2.8.2 / 2009-07-29
* Added JSpec.tryLoading()
* Added JSpec.request used to reference the original XMLHttpRequest; used to fix [#149]
* Fixed Mock XHR issue messing up JSpec request related utilities such as fixture() [#149]
=== 2.8.1 / 2009-07-27
* Added Lawrence Pit as a contributor
* Fixed object hash equality [#146]
{ a : '1', b : '2' } is now the same as:
{ b : '2', a : '1' }
=== 2.8.0 / 2009-07-27
* Give readFile precendence over xhr so that fixtures work with mockRequest when using Rhino
* Give XMLHttpRequest precedence over microsoft crap
* Added Mock Ajax Support
* Added mockRequest(), unmockRequest() utilities
* Added jspec.xhr.js
* Fixed be_visible, and be_hidden. Now implement the jQuery <= 1.3.1 method
=== 2.7.2 / 2009-07-24
* Fixed "end" in spec bodies when using the grammar
* Fixed "it" in spec bodies when using the grammar [#142]
* Changed; HTML entities in descriptions are now escaped for DOM formatter [#141]
* Added enno84@gmx.net as a contributor (thanks for the bug reports)
=== 2.7.1 / 2009-07-17
* Changed; hash() now accepts null
* Fixed should_receive issue with validating boolean args or return values
* Fixed --server-only switch
* Fixed jQuery dependency error message when jQuery is not available
when using jspec.jquery.js
=== 2.7.0 / 2009-07-14
* Added fixture() utility function
* Templates initialize with example paths to lib
=== 2.6.0 / 2009-07-09
* Added Ruby on Rails support
* Added exporting of JSpec (node.js etc)
* Added be_undefined matcher [#134]
=== 2.5.1 / 2009-07-07
* Added intermediate node.js support
* Fixed; grammar now allows foo.bar.baz.stub() etc to convert to stub(foo.bar.baz)
=== 2.5.0 / 2009-07-03
* Added contrib in README (thanks to anyone who has helped)
* Added more shared behavior specs
* Added Module.DSLs support for extending / adding new DSLs (DSL exchange not yet fully implemented)
* Added spec to make sure methods like end() will not fail due to the grammar
* Changed; giving hook precedence to suite hooks (before_each, etc) over module hooks (beforeSuite, etc) ; (thanks mpd)
* Changed; calls to stub() without and_return() now simply stub an arbitrary method with no return value
* Changed JSpec.include(); now returns JSpec allowing chaining
* Fixed having "end" in descriptions which would be replaced with '});'
* Fixed negation of should.receive('foo') matcher
* Fixed shared behavior assertion count issue
=== 2.4.3 / 2009-07-02
* Fixed matcher semicolon matcher issue when using the JSpec grammar
* Added pass() util; Spec#pass() and Spec#fail() (thanks gisikw)
* Removing Object.prototype.stubby() after specs are finished to prevent pollution
=== 2.4.2 / 2009-06-30
* Fixed trailing comma (thanks Kevin)
=== 2.4.1 / 2009-06-30
* Moved beforeSpec and afterSpec hook into proper positions
allowing for additional assertions within afterSpec.
=== 2.4.0 / 2009-06-30
* Added hook() and hookImmutable()
* Added support for matcher lists ('be enabled disabled selected') == be_enabled, be_disabled etc
* Added JSpec.include()
* Added several hooks
* Added Module support
* Added grammar conversion of Foo.stub('method') to stub(Foo, 'method')
* Added grammar conversion of Foo.destub() to destub(Foo)
* Require bind gem
* Fixed `jspec` bin docs
=== 2.3.1 / 2009-06-25
* Fixed; all stubs generated with stub() are restored
to their original methods after each 'it' block. See
README for details.
=== 2.3.0 / 2009-06-25
* Added stub()
* Added destub()
* Changed; Server responses utilize Rack::Mime now for arbitrary requests
=== 2.2.1 / 2009-06-22
* Changed; reportToServer() now accepts url arg
* Fixed be_empty matcher; now considers {} empty, however { foo : bar } is not
* Fixed throw_error error messages for Opera
* Fixed throw_error in Rhino (Opera is broken now)
* Fixed stray console.log() call
* Fixed some tab issues.
When using the JSpec grammar option you should
use the 'soft tabs' feature of your IDE or text editor.
A patch for tabs is pending and should be available soon,
however be aware that a 'parse error' may occur otherwise.
=== 2.2.0 / 2009-06-18
* Added link to JSpec in JSMag June 2009
* Added Github gem source location to docs
* Changed throw_error matcher; now accepts two arguments
* Changed --server; serves from current working directory.
This allows files in ../lib/* to be served rather than ./spec/* only.
* Refactored argumentsToArray()
=== 2.1.0 / 2009-06-12
* Changed `jspec init` to utilize a single template
which allows for all three suite running capabilities
within a single template. Now after initializing a
project you may `jspec run --server`, `jspec run --rhino`
etc at any time without modifications.
=== 2.0.3 / 2009-05-15
* Table should span full width
(Very week release I know, but improperly styled things bug me :) )
=== 2.0.2 / 2009-05-11
* Added rails integration link http://github.com/bhauman/jspec-rails
* Changed; puts() now displays constructor name when available
* Fixed Terminal output which was not displaying due to a recent commit
* fixed IE bug : DOM elements don't have valueOf() method
=== 2.0.1 / 2009-05-01
* Added better failure messages for throw_error matcher
* Renamed print() to puts() [#108]
=== 2.0.0 / 2009-04-27
* Added DOM loading indicator [#105]
* Added wait() helper for additional async support
* Added shared behavior support using should_behave_like('Another Suite')
* Added CSS body toggling [#1]
* Added receive matcher for Proxy Assertins
* Added grammar-less support
* Added an_instance_of() helper
* Removed .this literal
* Removed deprecated be_a_TYPE_input matchers
* Added ProxyAssertion
* Added select() util
* Added does() util for report-less assertions
* Added find() util
* Added JSpec.contentsOf()
* Added matchers to body evaluation [#90]
=== 1.1.7 / 2009-04-22
* Removed trailing commas causing issues with IE (what a suprise ;) )
=== 1.1.6 / 2009-04-22
* Fixed typo in requires()
* Added expect()
=== 1.1.5 / 2009-04-17
* Strengthened specs for cascading hooks
* Fixed cascading hooks
=== 1.1.4 / 2009-04-17
* Added rhino and server template files
* Added JSpec.hasXhr()
* Added JSpec.xhr()
* Added Ruby javascript testing server
* Added support for options passed to run()
* Added failuresOnly for Terminal formatter
* Added terminal assertion graphs
* Addec color() utility
* Added main.puts() since we use print() as a utility
* Added rhino support
* Added fail() utility function
* Added JSpec.Assertion
* Added normalizeMatcherMessage()
* Added normalizeMatcherBody()
* Added have_classes [#19]
* Added extend() utility
* Added be_an_instance_of matcher
* Added constructor checking support for throw_error matcher [#72]
* Added file support for exception messages, making them much easier to debug
* Added catching of exceptions throw within specs [#46]
* Changed; executable aborts when template does not exist
* Changed; matchers now normalized upon creation, accepts function, hash, or string.
* Changed be() matcher to be strict [#57]
* Changed error() to conditionally show line number when available
* Renamed Jspec.addSuite to JSpec.describe and Suite#addSpec to Suite#it
* Refactored be_a_TYPE_input matchers so that the deprication warning is logged only when calling the method
* Fixed JSpec.requires() now works with latest version of JSpec.error()
* Fixed error() now displays exceptions throw that do not respond to .message
* Fixed commenting bug [#37]
* Removed JSpec.main, now just using local main
=== 1.1.3 / 2009-04-14
* Removed /test used for the executable, causing gem to fail building
=== 1.1.2 / 2009-04-14
* Added `jspec update` sub-command [#63]
=== 1.1.1 / 2009-04-12
* Added gemspec
=== 1.1.0 / 2009-04-12
* jspec executable fully functional
=== 1.0.4 / 2009-04-09
* Added `jspec bind`
* Added `jspec run`
* Added `jspec init`
* Added `jspec` executable
* Added gemspec and manifest
* Added command-line usage docs
* Added custom matchers documentation
* Removed double negation
=== 1.0.3 / 2009-04-08
* Added have_prop matcher, have_property is now strict [#56]
=== 1.0.2 / 2009-04-08
* Added be_selected, be_checked matchers
* Added string support to each() each('some foo bar', ...)
* Added have_ATTR matchers [#51]
* Deprected be_a_TYPE_input matchers [#50]
=== 1.0.1 / 2009-04-07
* Added have_property matcher [#53]
=== 1.0.0 / 2009-04-06
* Added option() which gives the query string option precedence [#39]
* Changed; Using JSpec.options.formatter not JSpec.formatter [#44]
* Fixed Console formatter, now displays nested suites
* Check out http://visionmedia.github.com/jspec for additional documentation!
=== 0.9.6 / 2009-04-03
* Added nesting support
* Added printing of nested specs
* Added assertion graphs to DOM formatter
* Refactored preprocess()
* Refactored most of the specs
* Renamed preProcess() to preprocess()
* Removed running of a single suite via ?suite= for now
=== 0.9.5 / 2009-04-02
* Added support for printing of function bodies and regexps [#27]
* Added support for strings / regexp with should_throw_error [#26]
* Added have_within matcher
* Added have_at_most matcher
* Added have_at_least matcher
* Added have matcher [#24]
=== 0.9.4 / 2009-04-02
* Added be_a_TYPE_input matchers (be_a_checkbox_input, be_a_text_input, etc)
* Added be_disabled matcher [#21]
* Added be_enabled matcher [#20]
* Refactored be_visible and be_hidden with $(elem).is()
=== 0.9.2 / 2009-04-02
* Added support for multi-arg failure messages [#2]
* Better printing of jQuery objects [#15]
=== 0.9.1 / 2009-04-02
* Added support for dotted negation of assertions (foo.should.not.equal bar)
* Added support for dot-style assertions [#17] (foo.should_not.equal bar)
=== 0.9.0 / 2009-04-01
* Added spec for strip()
* Added strip()
* Added any() util
* Added new improved include matcher
* Added have_many and have_one matchers
* Added have_attr matcher [#14]
* Added map() util
* Added inject() util
* Added escape() util
* Added recursive array and object printing
* Added DOM formatter option failuresOnly
* Added support for running of a single squite via ?suite=...
* Added query() util
* Added last() util
* Added be_within matcher, accepts a range literal
* Added inclusive range literal n..n
* Added row hover
* Refactored range()
* Refactored setMessage()
* Refactored hash()
* Changed; preprocessor passing array of args (multi-arg matcher support)
* Changed jQuery to $ for internal usage
=== 0.8.0 / 2009-02-27
* Added a new style for the DOM formatter (not finished yet)
=== 0.7.0 / 2009-02-27
* Added Console formatter (anything implementing the console object. Firebug, Safari 4, etc)
* Added JSpec.options.profile for optional profiling of specs
* Added this. literal alternative (view readme)
* Moved formatters into JSpec.formatters
* Added error() util
* Added savings raketask
* Fixed parse error bug in Safari 4
=== 0.6.3 / 2009-02-26
* Added minification for jspec.jquery.js when packaging before release
* Added compression of css when packaging before release
=== 0.6.2 / 2009-02-26
* Changed; using $ in jspec.jquery.js for JSpec, take that jQuery ;)
* Added addMatchers, print, hash, and each as 'utility functions' this allows
JSpec to do each(...) internally instead of JSpec.each(...), while still preventing
pollution of the global scope.
=== 0.6.1 / 2009-02-26
* Added closrue literal -{ (view README)
* Added option to DOM formatter, now allows you to specify which element id to output to
=== 0.6.0 / 2009-02-24
* Added JSpec.hash
* Added be_null matcher
* Allow recursive composite matching using should_eql and should_not_eql
For example [1, 2, [3]].should_eql([1, 2, [3]]) is true, works with object
'hashes' as well.
=== 0.5.1 / 2009-02-24
* Damn auto-release messed up
=== 0.5.0 / 2009-02-24
* Added async support for jQuery
* Added JSpec.requires for dependencies
* Added JSpec.throw
* Added JSpec.runSpec
* Refactored jspec.jquery.js
* Fixed evalBody exceptions, previously was not showing exception message
* Fixed bug of JSpec interpreting // in a string such as http:// to be a comment.
=== 0.4.1 / 2009-02-22
* Added elements() alias of element()
* Added support for string passed to runSuite; runSuite('Matchers') is the same as
runSuite(JSpec.suites['Matchers']).
* Fixed some documentation
=== 0.4.0 / 2009-02-20
* Added comment literal (//)
* Added pre-processor for convering matchers.
For example 'test'.should_be_true becomes JSpec.match('test', 'should_be', 'true'),
preventing pollution of core prototypes.
=== 0.3.2 / 2009-02-19
* Added TM bundle (go checkout my jspec.tmbundle repo on github)
* Renamed have_length_of to have_length
=== 0.3.1 / 2009-02-19
* Added jquery js to package
=== 0.3.0 / 2009-02-19
* Added JSpec.match
* Added options to report() which are passed to formatter
* Added sandbox helpers (reg / jquery)
* Added have_child and have_children
* Added have_tag and have_tags
* Changed exec to only load / eval file
* Fixed parser token issue, was previously matching things like end() as literal end
=== 0.2.3 / 2009-02-18
* Changed test dir to spec
* Changed test.js to core.spec.js
=== 0.2.2 / 2009-02-18
* Added contexts
=== 0.2.0 / 2009-02-18
* Added release rake task
* Added package with minified alternative
=== 0.1.0 / 2009-02-18
* Added new sexy syntax (warning: you will have to re-write your specs)
* Added pre-processor for optional matcher parens
* Added several new matchers
* Added matcher aliasing
* Added simple matcher declarations
* Added __END__
* Added yet-to-be-implemented specs
* Added loading of suites via JSpec.load
=== 0.0.4 / 2008-11-03
* Added ability to pass only a description to it(), meaning not yet implemented
=== 0.0.3 / 2008-10-28
* Added should_fail
* Added should_match
* Added should_not_match
* Added should_be and should_not_be
=== 0.0.2 / 2008-10-28
* Fixed typo in documentation for pointing to the master repo
=== 0.0.1 / 2008-10-28
* Initial release

View File

@ -0,0 +1,50 @@
bin/jspec
History.rdoc
jspec.gemspec
lib/images/bg.png
lib/images/hr.png
lib/images/loading.gif
lib/images/sprites.bg.png
lib/images/sprites.png
lib/images/vr.png
lib/jspec.css
lib/jspec.jquery.js
lib/jspec.js
lib/jspec.xhr.js
Manifest
Rakefile
README.rdoc
server/browsers.rb
server/server.rb
spec/async
spec/env.js
spec/fixtures/test.html
spec/fixtures/test.json
spec/fixtures/test.xml
spec/modules.js
spec/spec.dom.html
spec/spec.fixtures.js
spec/spec.grammar-less.js
spec/spec.grammar.js
spec/spec.jquery.js
spec/spec.jquery.xhr.js
spec/spec.js
spec/spec.matchers.js
spec/spec.modules.js
spec/spec.node.js
spec/spec.rhino.js
spec/spec.server.html
spec/spec.shared-behaviors.js
spec/spec.utils.js
spec/spec.xhr.js
templates/default/History.rdoc
templates/default/lib/yourlib.core.js
templates/default/README.rdoc
templates/default/spec/spec.core.js
templates/default/spec/spec.dom.html
templates/default/spec/spec.rhino.js
templates/default/spec/spec.server.html
templates/rails/spec.application.js
templates/rails/spec.dom.html
templates/rails/spec.rhino.js
templates/rails/spec.server.html

View File

@ -0,0 +1,724 @@
= JSpec
JSpec is a minimalistic JavaScript behavior driven development framework,
providing simple installation, extremely low learning curve, absolutely no pollution
to core prototypes, async request support, and incredibly sexy syntax, tons of matchers
and much more.
== Features
* Highly readable
* Framework / DOM independent
* Modular via JSpec Module's and hooks
* Mock Ajax Requests
* Rhino support
* Node.js support
* Async support
* Fixture support
* Ruby JavaScript testing server
* Nested describes
* Does not pollute core object prototypes
* Cascading before/after/before_each/after_each hooks
* Extremely simple and intuitive matcher declaration
* Over 45 core matchers
* Allows parens to be optional when using matchers to increase readability
* Several helpful formatters (DOM, Console, Terminal, ...)
* Assertion graphs displaying how many, and which assertions pass or failed
* Default / customizable evaluation contexts
* DOM sandbox support
* Great looking default DOM theme
* `jspec` command-line utility for auto-running specs, and initializing project templates
* Proxy or 'Spy' assertions
* Method Stubbing
* Shared behaviors
* Profiling
* Ruby on Rails Integration
* Tiny (15 kb compressed, 1300-ish LOC)
== Installation
Simply download JSpec and include JSpec.css and JSpec.js in your markup.
Head over to the downloads section on Github, clone this public repo, or
add JSpec as a git submodule with in your project. Alternatively JSpec is
also available as a Ruby Gem (though this is not required), which also
provides the `jspec` executable. To install execute:
$ gem sources -a http://gems.github.com (if you have not previously done so)
$ sudo gem install visionmedia-jspec
At which point you may:
$ jspec init myproject
JSpec scripts should NOT be referenced via the <script> tag, they should be
loaded using the exec method (unless you are using the grammar-less alternative).
Below is an example:
...
<script>
function runSuites() {
JSpec
.exec('spec.core.js')
.exec('spec.jquery.js')
.run({ failuresOnly : true })
.report()
}
</script>
<body onLoad="runSuites()">
...
You may optionally want to use sources in the /pkg directory
for your project, since it includes compressed alternatives generated
each release.
== Example
describe 'ShoppingCart'
before_each
cart = new ShoppingCart
end
describe 'addProducts'
it 'should add several products'
cart.addProduct('cookie')
cart.addProduct('icecream')
cart.should.have 2, 'products'
end
end
describe 'checkout'
it 'should throw an error when checking out with no products'
-{ cart.clear().checkout() }.should.throw_error EmptyCart
end
end
end
== Grammar-less Example
JSpec's grammar is optional, you may also use the equivalent grammar-less
alternative below using pure JavaScript (when using the JSpec grammar you
may also use grammar-less assertions):
JSpec.describe('ShoppingCart', function(){
before_each(function{
cart = new ShoppingCart
})
describe('addProducts', function(){
it ('should add several products', function(){
cart.addProducts('cookie')
cart.addProducts('icecream')
expect(cart).to(have, 2, 'products')
})
})
describe('checkout', function(){
it ('should throw an error when checking out with no products', function(){
expect(function(){ cart.clear().checkout() }).to(throw_error, EmptyCart)
})
})
})
== Options
You may alter the way JSpec operates by assigning options via the
JSpec.options hash, by passing string-based option values via the
query string, or passing a hash to run(). For example
JSpec.options.failuresOnly = true, and ?failuresOnly=1 will both work.
* profile {bool} when enabled, uses console.time() in order to display performance information in your console log as specs are completed. (DOM, Console)
* failuresOnly {bool} displays only failing specs, making them quick to discover and fix (DOM, Terminal)
* reportToId {string} an element id to report to when using the DOM formatter (DOM)
== Matchers
* Core
- equal, be ===
- be_a, be_an have constructor of x
- be_an_instance_of instanceof x
- be_at_least >=
- be_at_most <=
- be_null == null
- be_empty length < 0 or {}
- be_true == true
- be_false == false
- be_type be type of x
- be_greater_than >
- be_less_than <
- be_undefined check if variable passed is undefined
- throw_error should throw an error, optionally supply the error string or regexp for message comparison
- have object should have n of property (person.should.have(2, 'pets'))
- have_at_least object should have at least n of property
- have_at_most object should have a maximum n of property
- have_within object should have within n..n of property (person.should.have_within(1..3, 'pets')
- have_length length of n
- have_prop object should have property x, optionally supplying an expected value
- have_property strict version of have_prop
- be_within checks if n is within the range passed
- include include substring, array element, or hash key
- match string should match regexp x
- respond_to property x should be a function
- eql matches simple literals (strings, numbers) with ==
However composites like arrays or 'hashes' are recursively matched,
meaning that [1, 2, [3]].should_eql([1, 2, [3]]) will be true.
* jQuery
- have_tag, have_one have exactly one tag
- have_tags, have_many have more than one tag
- have_child have exactly one child
- have_children have more than one child
- have_text have plain text
- have_attr have an attribute, with optional value
- have_type
- have_id
- have_title
- have_alt
- have_href
- have_rel
- have_rev
- have_name
- have_target
- have_value
- have_class
- have_classes
- be_visible
- be_hidden
- be_enabled
- be_disabled
- be_selected
- be_checked
== Proxy Assertions
Proxy or 'Spy' assertions allow you to assert that a method is called n number
of times, with x arguments, returning x value. For example:
person = { getPets : function(species){ return ['izzy'] }}
person.should.receive('getPets', 'twice').with_args(an_instance_of(String))and_return(['izzy'])
person.getPets('dog') // This will pass
person.getPets() // This will fail because we asked an instance of String
This is a useful mechanism for testing the behavior of your object, as well as
how other methods may interact with it. Below is another example:
array = ['foo', 'bar']
array.should.receive('toString').and_return('foo,bar')
'array: ' + array // This line causes the spec to pass due to calling toString()
For more examples view spec/spec.matchers.js
== Method Stubbing
JSpec currently provides very simple stubbing support shown below:
person = { toString : function(){ return '<Person>' } }
stub(person, 'toString').and_return('Ive been stubbed!')
After each spec all stubs are restored to their original methods so
there is no reason to explicitly call destub(). To persist stubs,
use a before_each hook:
before_each
stub(someObject, 'method').and_return({ some : thing })
end
To destub a method simply call destub() at any time:
destub(person, 'toString')
If you would like to whipe an object clear of stubs simply pass it
to destub() without an additional method argument:
destub(person)
Alternatively both these utility functions may be called as methods
on any object when using the JSpec grammar:
someObject.stub('method').and_return('whatever')
// Converted to stub(someObject, 'method').and_return('whatever')
== Helpers
* Core
- an_instance_of used in conjunction with the 'receive' matcher
- mockRequest mock a request (requires jspec.xhr.js)
- unmockRequest unmock requests (requests jspec.xhr.js)
* jQuery
- sandbox used to generate new DOM sandbox, using jQuery object
- element same as invoking jQuery, just reads better and no need to worry about $ collisions
- elements alias of element
== Shared Behaviors
JSpec's support for shared behaviors allows multiple suites or describe blocks to share
common functionality. For example an Admin, would inherit all specs of User:
describe 'User'
before
User = function(name) { this.name = name }
user = new User('joe')
end
it 'should have a name'
user.should.have_property 'name'
end
describe 'Administrator'
should_behave_like('User')
before
Admin = function(name) { this.name = name }
Admin.prototype.may = function(perm){ return true }
user = new Admin('tj')
end
it 'should have access to all permissions'
user.may('edit pages').should.be_true
end
end
end
NOTE: both User and Administrator's before hooks implement the 'user' variable
== Mock Ajax Requests
JSpec supports generic Ajax mocking which is usable with any JavaScript framework via 'jspec.xhr.js'. The
API is comprised of two functions, mockRequest() and unmockRequest(). unmockRequest() is
automatically called after each specification to restore the default functionality of XMLHttpRequest,
so it is uncommon to call unmockRequest() directly. Below is a jQuery example:
it 'should mock requests'
mockRequest().and_return('{ foo : "bar" }', 'application/json')
$.getJSON('foo', function(response, statusText){
response.foo.should.eql 'bar'
})
end
The mockRequest().and_return signature is as follows:
mockRequest().and_return(<data>, [content-type], [response-status-code], [headers-hash])
At the moment mockRequest() itself does not accept any arguments, however in the future
this will be used to target specific uris for mocking.
NOTE: works with Rhino as well
== Hooks
Currently the following hooks are supported, and may be utilized any number of times as they
are simply pushed to a stack. So for instance you may have two before_each blocks within the same
scope, they will both run, but this can help keep your specs readable.
* before run once before the suite is executed
* after run once after the suite is executed
* before_each run before each specification
* after_each run after each specification
== Custom Contexts
Custom contexts can be applied to supply helper
methods or properties to all subsequent bodies (other hooks, or specs).
Keep in mind that when replacing the default context you will loose
functionality provided by it, unless you manually merge it with your
custom context.
To reset the context simply assign null to obtain the original context.
...
before
JSpec.context = { foo : 'bar' }
end
after
JSpec.context = null
end
it 'will work ;)'
foo.should_equal 'bar'
end
...
== Async Support
Currently only jspec.jquery.js supports async requests. JSpec uses jQuery.ajaxSetup and sets all
requests to sync, which preserves execution order, and reports correctly.
it 'should load mah cookies (textfile)'
$.post('async', function(text){
text.should_eql 'cookies!'
})
end
== Pre-processor
The pre-processing capability of JSpec is extremely powerful. Your JavaScript
code is not necessarily what it seems. For example when you seemingly invoke a
object's prototype like below:
'foobar'.should.include 'bar'
First parens are added:
'foobar'.should.include('bar')
Secondly the matcher invocation is converted to a non-polluting match() call:
JSpec.match('foobar', 'should', 'include', 'bar')
This also means instead of:
var object = { foo : 'bar' }
object.should.include 'foo'
We can do:
{ foo : 'bar' }.should.include 'foo'
=== Closure Literal
These are equivalent:
-{ throw 'test' }.should.throw_error
function() { throw 'test' }.should.throw_error
=== Inclusive Range Literal
The following expands to the array of [1,2,3,4,5]
n.should.be_within 1..5
== Formatters
To change a formatter simply alter the options hash like below, assigning
a new constructor, or pass it within the hash to run():
JSpec.options.formatter = JSpec.formatters.Console
OR
JSpec
.exec('...')
.run({ formatter : JSpec.formatters.Terminal })
.report()
== Fixtures
The fixture() utility function may be used in order to load arbitrary file contents
for use with your specifications. JSpec will resolve fixture('data') in the following
manor:
- 'data'
- 'spec/data'
- 'spec/fixtures/data'
- 'spec/fixtures/data.html'
So if the file 'spec/fixtures/data.html' exists, we can simply use fixture('data'),
where as 'spec/fixtures/xml/data.xml' must be specified with fixture('xml/data.xml').
If you prefer not to store fixtures in the 'fixtures' directory you must be more specific
with the path supplied.
== Testing DOM Elements
When using jQuery testing DOM elements is very easy. Many may think they require specific
sandbox divs in their html, however you do not. Using the fixture support mentioned above
you may simply load some HTML, and use the 'elements()' utility which is an alias of jQuery:
describe 'JSpec DOM testing'
describe 'is so easy'
before_each
list = elements(fixture('users-list'))
// or list = jQuery(fixture('users-list'))
// or list = $(fixture('users-list'))
end
it 'should have users'
list.should.have_tag 'ul'
end
end
end
You may also use simple strings, since jQuery's constructor will convert them to DOM elements:
describe 'Something'
before_each
html = elements('<p>Foo</p>')
// or html = $('<p>Foo</p>') ...
end
it 'should do something'
html.should.have_text 'Foo'
end
end
== Custom Matchers
First lets create a simple equality matcher. In the case below JSpec is smart enough to realize
this is simply a binary operator, and simply transforms this into 'actual === expected'
JSpec.addMatchers({
equal : '==='
})
To alias a method to keep your specs readable you may alias them like below:
JSpec.addMatchers({
be : 'alias equal'
})
'foo'.should.equal 'foo'
true.should.be true
Matchers with string bodies implicitly return the expression value.
The expanded version of the equal matcher would then be:
JSpec.addMatchers({
equal : 'actual === expected'
})
Large matchers or those which require several parameters may wish
to utilize the hash method:
JSpec.addMatchers({
equal : { match : function(actual, expected){
return actual === expected
}}
})
To keep JSpec tiny, JSpec will default to generating failure messages
for you, how ever this can be explicitly defined:
JSpec.addMatchers({
equal : {
match : function(actual, expected){
return actual === expected
},
message : function(actual, expected, negate) {
return 'a message here'
}
}
})
When defining matchers that are extremely similar in functionality, however
require different names, you may use a prefixed list of words like below which
defines be_disabled, be_selected, be_checked, and have_type, have_id, etc. Each
function must return the matcher body which will be used.
JSpec.addMatchers({
'be disabled selected checked' : function(attr) {
return 'jQuery(actual).attr("' + attr + '")'
},
'have type id title alt href src sel rev name target' : function(attr) {
return function(actual, value) {
return value ? jQuery(actual).attr(attr) == value:
jQuery(actual).attr(attr)
}
}
})
== Extending Or Hooking Into JSpec
JSpec provides a hook architecture for extending or analyzing various
points in its execution, through the use of 'Modules'. For a Module
example view lib/jspec.jquery.js.
The following methods or properties are utilized by JSpec:
- init : called to initialize a module
- utilities : hash of utility functions merged with JSpec.defaultContext
- matchers : hash of matchers merged with JSpec's core matchers via JSpec.addMatchers()
- DSLs : hash of DSL methods; for example DSLs.snake contains before_each, after_each, etc.
Where as DSLs.camel may contain beforeEach, afterEach, etc.
Below is a list of hooks, descriptions, and valid return values which
may simply be implemented as module methods. beforeSuite, afterSuite, beforeSpec, and afterSpec have lower
precedence than before_each, after_each etc within the specs themselves, allowing them to override or undo
anything that has been done by a Module.
- running(options) : started running JSpec with the options passed : returning 'stop' will halt running
- loading(file) : loading a file : returning 'stop' will prevent loading
- executing(file) : executing a file : returning 'stop' will prevent execution
- posting(data, url) : posting data to a url : returning 'stop' will prevent request
- reportingToServer(url) : reporting to server url : returning 'stop' will prevent reporting to server
- preprocessing(input) : before input string is preprocessed : return input string for next hook to preprocess
- stubbing(object, method, result) : called when stubbing an object's method, and return value (result). : (no return value)
- requiring(dependency, message) : requiring a dependency : (no return value)
- beforeAssertion(assertion) : before an assertion has been made : (no return value)
- afterAssertion(assertion) : after an assertion has been made : (no return value)
- addingMatcher(name, body) : unprocessed matcher name and body : (no return value)
- addingSuite(suite) : adding Suite instance to JSpec : (no return value)
- beforeSuite(suite) : before running of suite (describe block) : (no return value)
- afterSuite(suite) : after running of suite (describe block) : (no return value)
- beforeSpec(spec) : before running of spec (it block) : (no return value)
- afterSpec(spec) : after running of spec (it block) : (no return value)
- reporting(options) : called before reporting : (no return value)
- evaluatingBody(dsl, matchers, context, contents) : evaluating body contents, with the given context, matchers and dsl. : (no return value)
For example you may wish to proxy files which are being executed, simply implement the
executing method like below. This example will stop execution of any file matching /matchers/.
MyModule = {
executing : function(file) {
if (file.match(/matchers/))
return 'stop'
}
}
JSpec.include(MyModule)
Immutable values may also be passed to hooks using hookImmutable() internally. This allows
for simple numbers, strings, etc to be utilized or altered within a hook implementation. Below
is an example module which adds functionality to the JSpec grammar by converting SomeObject.stub('method')
to stub(SomeObject, 'method'):
JSpec.include({
preprocessing : function(input) {
return input.replace(/(\w+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)')
}
})
== JSpec Command-line Utility
When installed as a Ruby Gem, the `jspec` executable will become available,
allowing you to initialize project templates quickly, as well as auto-testing
specifications when a file is altered.
Initialize JSpec-driven project template in directory 'myproject':
$ jspec init myproject
Once within 'myproject' start testing by executing:
$ jspec
For additional usage execute:
$ jspec help
Or for specific usage:
$ jspec help run
== Rhino
JSpec provides transparent support for Rhino, while using the Terminal formatter.
Simply create a JavaScript file with contents similar to below, and then execute
the command following it:
load('lib/jspec.js')
JSpec
.exec('spec/spec.grammar.js')
.exec('spec/spec.core.js')
.run({ formatter : JSpec.formatters.Terminal, failuresOnly : true })
.report()
Initialize project with:
$ jspec init myproject
Run with:
$ jspec run --rhino
Or bind (automated testing):
$ jspec --rhino
== Server
The Ruby JavaScript testing server included with JSpec simply runs
the spec suites within each browser you specify, while reporting result
back to the terminal. It is essentially the same as using the DOM formatter
and auto-testing each browser, however results are centralized to the terminal,
removing the need to manually view each browser's output.
Initialize project with:
$ jspec init myproject
Run with:
$ jspec run --server
== Ruby on Rails
No additional gems are required for JSpec to work with rails, although
http://github.com/bhauman/jspec-rails has been created by 'bhauman'. JSpec
supports Rails out of the box, simply execute:
$ jspec init --rails
Then while still in the root directory of your Rails project, run the following
command which will bind to, and refresh your browsers automatically when any changes
are made to ./public/javascripts/*.js or ./jspec/*.js
$ jspec
Or just like regular JSpec applications, run once:
$ jspec run
Or run via the terminal using Rhino:
$ jspec run --rhino
== Known Issues
* Tabs may cause a parse error. To prevent this use 'soft tabs' (setting in your IDE/Editor)
or use JSpec's grammar-less alternative (mentioned above).
* The preprocessor is not (yet) capable of multiline conversions. For example the following is invalid
object.stub('getContentsOfURL').and_return(function(url){
return 'html'
})
In cases such as this, you may always revert to utilizing JSpec in a grammar-less form as follows:
stub(object, 'getContentsOfURL').and_return(function(url){
return 'html'
})
== More Information
* Featured article in JSMag: http://www.jsmag.com/main.issues.description/id=21/
* Syntax comparison with other frameworks http://gist.github.com/92283
* Get the TextMate bundle at https://github.com/visionmedia/jspec.tmbundle/tree
* For more information consult the JSpec source code documentation or visit http://visionmedia.github.com/jspec
* jQuery + HTML fixture example http://gist.github.com/147831
== Contributors
Many ideas and bug reports were contributed by
the following developers, thankyou for making
JSpec more enjoyable, and bug free ;)
* Lawrence Pit
* mpd@jesters-court.ne
* kevin.gisi@gmail.com
* enno84@gmx.net
== License
(The MIT License)
Copyright (c) 2008 - 2009 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,73 @@
require 'rubygems'
require 'rake'
require 'echoe'
def version
$1 if File.read('lib/jspec.js').match /version *: *'(.*?)'/
end
Echoe.new "jspec", version do |p|
p.author = "TJ Holowaychuk"
p.email = "tj@vision-media.ca"
p.summary = "JavaScript BDD Testing Framework"
p.url = "http://visionmedia.github.com/jspec"
p.runtime_dependencies << "visionmedia-commander >=3.2.9"
p.runtime_dependencies << "visionmedia-bind >=0.2.6"
end
namespace :pkg do
desc 'Build package'
task :build => ['pkg:clear'] do
begin
sh 'mkdir pkg'
sh 'cp -fr lib/* pkg'
minify 'lib/jspec.js', 'pkg/jspec.min.js'
minify 'lib/jspec.jquery.js', 'pkg/jspec.jquery.min.js'
compress 'lib/jspec.css', 'pkg/jspec.min.css'
sh 'git add pkg/.'
rescue Exception => e
puts "Failed to package: #{e}."
else
puts "Packaging of JSpec-#{version} completed."
end
end
desc 'Clear packaging'
task :clear do
if File.directory? 'pkg'
sh 'rm -fr pkg/*'
sh 'rmdir pkg'
end
end
desc 'Display compression savings of last release'
task :savings do
totals = Hash.new { |h, k| h[k] = 0 }
format = '%-20s : %0.3f kb'
totals = %w( pkg/jspec.min.js pkg/jspec.jquery.min.js pkg/jspec.min.css ).inject totals do |total, file|
uncompressed = File.size(file.sub('.min', '')).to_f / 1024
compressed = File.size(file).to_f / 1024
saved = uncompressed - compressed
puts format % [file.sub('pkg/', ''), saved]
totals[:saved] += saved
totals[:uncompressed] += uncompressed
totals[:compressed] += compressed
totals
end
puts
puts format % ['total uncompressed', totals[:uncompressed]]
puts format % ['total compressed', totals[:compressed]]
puts format % ['total saved', totals[:saved]]
end
end
def minify from, to
sh "jsmin < #{from} > #{to}"
end
def compress from, to
File.open(to, 'w+') do |file|
file.write File.read(from).gsub(/(^[\t ]*)|\n/, '')
end
end

View File

@ -0,0 +1,182 @@
#!/usr/bin/env ruby
JSPEC_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
$:.unshift JSPEC_ROOT
require 'rubygems'
require 'commander'
require 'bind'
require 'fileutils'
RHINO = 'java org.mozilla.javascript.tools.shell.Main'
program :name, 'JSpec'
program :version, '2.8.4'
program :description, 'JavaScript BDD Testing Framework'
default_command :bind
command :init do |c|
c.syntax = 'jspec init [dest]'
c.summary = 'Initialize a JSpec project template'
c.description = 'Initialize a JSpec project template. Defaults to the current directory
when [dest] is not specified. The template includes several files for
running via Rhino, DOM, and the JSpec Rack server.'
c.example 'Create a directory foo, initialized with a jspec template', 'jspec init foo'
c.option '-R', '--rails', 'Initialize rails template'
c.when_called do |args, options|
dest = args.shift || '.'
if options.rails
initialize_rails_to dest
else
initialize_to dest
end
say "Template initialized at '#{dest}'"
end
end
command :update do |c|
c.syntax = 'jspec update [path ...]'
c.summary = 'Update JSpec releases'
c.description = 'Update JSpec release in [paths], this will allow you to utilize
the latest JSpec features. Execute from JSpec project root without [paths] to
update the default template spec files.'
c.when_called do |args, options|
if args.empty?
if rails?
paths = 'jspec/spec.dom.html', 'jspec/spec.rhino.js'
else
paths = 'spec/spec.dom.html', 'spec/spec.rhino.js'
end
else
paths = args
end
update_version_in *paths
end
end
command :run do |c|
c.syntax = 'jspec run [path] [options]'
c.summary = 'Run specifications'
c.description = 'Run specifications, defaulting [path] to spec/spec.dom.html.
You will need to supply [path] if your specs do not reside
in this location. `run --bind` is the default sub-command of
jspec so you may simply execute `jspec` in order to bind execution
of your specs when a file is altered.
JSpec supports Rhino execution when installed. The [path] is assumed
to be spec/spec.rhino.js unless specified. See examples below for
using the --rhino switch.
JSpec\'s server is also available via --server, which defaults
the [path] to spec/server.html'
c.example 'Run once in Safari', 'jspec run'
c.example 'Run once in Safari and Firefox', 'jspec run --browsers Safari,Firefox'
c.example 'Run custom spec file', 'jspec run foo.html'
c.example 'Auto-run browsers when a file is altered', 'jspec run --bind --browsers Safari,Firefox'
c.example 'Shortcut for the previous example', 'jspec --browsers Safari,Firefox'
c.example 'Auto-run rhino when a file is altered', 'jspec --rhino'
c.example 'Run Rhino specs once', 'jspec run specs/something.js --rhino'
c.option '-b', '--browsers BROWSERS', Array, 'Specify browsers to test, defaults to Safari'
c.option '-p', '--paths PATHS', Array, 'Specify paths when binding, defaults to javascript within ./lib and ./spec'
c.option '-B', '--bind', 'Auto-run specs when source files or specs are altered'
c.option '-R', '--rhino', 'Run specs using Rhino'
c.option '-S', '--server', 'Run specs using the JSpec server'
c.option '-s', '--server-only', 'Start JSpec server without running browsers'
c.when_called do |args, options|
# Rails
if rails?
options.default :browsers => %w( Safari ), :paths => ['public/javascripts/**/*.js', 'jspec/**/*.js']
else
options.default :browsers => %w( Safari ), :paths => ['lib/**/*.js', 'spec/**/*.js']
end
# Actions
if options.rhino
spec = args.shift || path_to('spec.rhino.js')
action = lambda { rhino spec }
elsif options.server_only
start_server options, nil
elsif options.server
spec = args.shift || path_to('spec.server.html')
action = lambda { start_server options, spec }
else
spec = args.shift || path_to('spec.dom.html')
action = Bind::Actions::RefreshBrowsers.new spec, *options.browsers
end
# Binding
if options.bind
listener = Bind::Listener.new :paths => options.paths, :interval => 1, :actions => [action], :debug => $stdout
listener.run!
else
action.call File.new(spec)
end
end
end
alias_command :bind, :run, '--bind'
def initialize_to dest
unless Dir[dest + '/*'].empty?
abort unless agree "'#{dest}' is not empty; continue? "
end
copy_template_to 'default', dest
replace_root_in dest, 'spec/spec.dom.html', 'spec/spec.rhino.js'
end
def initialize_rails_to dest
unless looks_like_rails_root?(dest)
abort unless agree "'#{dest}' does not look like root of a rails project; continue? "
end
copy_template_to 'rails', "#{dest}/jspec"
replace_root_in "#{dest}/jspec", 'spec.dom.html', 'spec.rhino.js'
end
def copy_template_to name, dest
FileUtils.mkdir_p dest
FileUtils.cp_r path_to_template(name), dest
end
def path_to_template name
File.join JSPEC_ROOT, 'templates', name, '.'
end
def path_to file
rails? ? "jspec/#{file}" : "spec/#{file}"
end
def rhino file
abort "#{file} not found" unless File.exists? file
system "#{RHINO} #{file}"
end
def start_server options, spec
require 'server/server'
JSpec::Server.start options, spec
end
def rails?
File.directory? 'jspec'
end
def replace_root_in dest, *paths
paths.each do |path|
path = File.join dest, path
contents = File.read(path).gsub 'JSPEC_ROOT', JSPEC_ROOT
File.open(path, 'w') { |file| file.write contents }
end
end
def update_version_in *paths
paths.each do |path|
next unless File.exists? path
contents = File.read(path).gsub /visionmedia-jspec-(\d+\.\d+\.\d+)/, "visionmedia-jspec-#{program(:version)}"
File.open(path, 'r+'){ |file| file.write contents }
say "Updated #{path}; #{$1} -> #{program(:version)}"
end
say "Finished updating JSpec"
end
def looks_like_rails_root? path = '.'
File.directory? "#{path}/vendor"
end

View File

@ -0,0 +1,38 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{jspec}
s.version = "2.8.4"
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
s.authors = ["TJ Holowaychuk"]
s.date = %q{2009-08-02}
s.default_executable = %q{jspec}
s.description = %q{JavaScript BDD Testing Framework}
s.email = %q{tj@vision-media.ca}
s.executables = ["jspec"]
s.extra_rdoc_files = ["bin/jspec", "lib/images/bg.png", "lib/images/hr.png", "lib/images/loading.gif", "lib/images/sprites.bg.png", "lib/images/sprites.png", "lib/images/vr.png", "lib/jspec.css", "lib/jspec.jquery.js", "lib/jspec.js", "lib/jspec.xhr.js", "README.rdoc"]
s.files = ["bin/jspec", "History.rdoc", "jspec.gemspec", "lib/images/bg.png", "lib/images/hr.png", "lib/images/loading.gif", "lib/images/sprites.bg.png", "lib/images/sprites.png", "lib/images/vr.png", "lib/jspec.css", "lib/jspec.jquery.js", "lib/jspec.js", "lib/jspec.xhr.js", "Manifest", "Rakefile", "README.rdoc", "server/browsers.rb", "server/server.rb", "spec/async", "spec/env.js", "spec/fixtures/test.html", "spec/fixtures/test.json", "spec/fixtures/test.xml", "spec/modules.js", "spec/spec.dom.html", "spec/spec.fixtures.js", "spec/spec.grammar-less.js", "spec/spec.grammar.js", "spec/spec.jquery.js", "spec/spec.jquery.xhr.js", "spec/spec.js", "spec/spec.matchers.js", "spec/spec.modules.js", "spec/spec.node.js", "spec/spec.rhino.js", "spec/spec.server.html", "spec/spec.shared-behaviors.js", "spec/spec.utils.js", "spec/spec.xhr.js", "templates/default/History.rdoc", "templates/default/lib/yourlib.core.js", "templates/default/README.rdoc", "templates/default/spec/spec.core.js", "templates/default/spec/spec.dom.html", "templates/default/spec/spec.rhino.js", "templates/default/spec/spec.server.html", "templates/rails/spec.application.js", "templates/rails/spec.dom.html", "templates/rails/spec.rhino.js", "templates/rails/spec.server.html"]
s.homepage = %q{http://visionmedia.github.com/jspec}
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Jspec", "--main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{jspec}
s.rubygems_version = %q{1.3.5}
s.summary = %q{JavaScript BDD Testing Framework}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<visionmedia-commander>, [">= 3.2.9"])
s.add_runtime_dependency(%q<visionmedia-bind>, [">= 0.2.6"])
else
s.add_dependency(%q<visionmedia-commander>, [">= 3.2.9"])
s.add_dependency(%q<visionmedia-bind>, [">= 0.2.6"])
end
else
s.add_dependency(%q<visionmedia-commander>, [">= 3.2.9"])
s.add_dependency(%q<visionmedia-bind>, [">= 0.2.6"])
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

View File

@ -0,0 +1,145 @@
body.jspec {
margin: 45px 0;
font: 12px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
background: #efefef url(images/bg.png) top left repeat-x;
}
#jspec {
margin: 0 auto;
padding-top: 25px;
width: 1008px;
background: url(images/vr.png) top left repeat-y;
text-align: left;
}
#jspec-top {
position: relative;
margin: 0 auto;
width: 1008px;
height: 40px;
background: url(images/sprites.bg.png) top left no-repeat;
}
#jspec-bottom {
margin: 0 auto;
width: 1008px;
height: 15px;
background: url(images/sprites.bg.png) bottom left no-repeat;
}
#jspec .loading {
margin-top: -45px;
width: 1008px;
height: 80px;
background: url(images/loading.gif) 50% 50% no-repeat;
}
#jspec-title {
position: relative;
top: 35px;
left: 20px;
width: 160px;
font-size: 22px;
font-weight: normal;
background: url(images/sprites.png) 0 -126px no-repeat;
text-align: center;
}
#jspec-title em {
font-size: 10px;
font-style: normal;
color: #BCC8D1;
}
#jspec-report * {
margin: 0;
padding: 0;
background: none;
border: none;
}
#jspec-report {
padding: 15px 40px;
font: 11px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
color: #7B8D9B;
}
#jspec-report.has-failures {
padding-bottom: 30px;
}
#jspec-report .hidden {
display: none;
}
#jspec-report .heading {
margin-bottom: 15px;
}
#jspec-report .heading span {
padding-right: 10px;
}
#jspec-report .heading .passes em {
color: #0ea0eb;
}
#jspec-report .heading .failures em {
color: #FA1616;
}
#jspec-report table {
width: 100%;
font-size: 11px;
border-collapse: collapse;
}
#jspec-report td {
padding: 8px;
text-indent: 30px;
color: #7B8D9B;
}
#jspec-report tr.body {
display: none;
}
#jspec-report tr.body pre {
margin: 0;
padding: 0 0 5px 25px;
}
#jspec-report tr:not(.body):hover + tr.body {
display: block;
}
#jspec-report tr td:first-child em {
font-style: normal;
font-weight: normal;
color: #7B8D9B;
}
#jspec-report tr:not(.description):hover {
text-shadow: 1px 1px 1px #fff;
background: #F2F5F7;
}
#jspec-report td + td {
padding-right: 0;
width: 15px;
}
#jspec-report td.pass {
background: url(images/sprites.png) 3px -7px no-repeat;
}
#jspec-report td.fail {
background: url(images/sprites.png) 3px -47px no-repeat;
font-weight: bold;
color: #FC0D0D;
}
#jspec-report td.requires-implementation {
background: url(images/sprites.png) 3px -87px no-repeat;
}
#jspec-report tr.description td {
margin-top: 25px;
padding-top: 25px;
font-size: 12px;
font-weight: bold;
text-indent: 0;
color: #1a1a1a;
}
#jspec-report tr.description:first-child td {
border-top: none;
}
#jspec-report .assertion {
display: block;
float: left;
margin: 0 0 0 1px;
padding: 0;
width: 1px;
height: 5px;
background: #7B8D9B;
}
#jspec-report .assertion.failed {
background: red;
}
.jspec-sandbox {
display: none;
}

View File

@ -0,0 +1,70 @@
// JSpec - jQuery - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
JSpec
.requires('jQuery', 'when using jspec.jquery.js')
.include({
// --- Initialize
init : function() {
jQuery.ajaxSetup({ async : false })
},
// --- Utilities
utilities : {
element : jQuery,
elements : jQuery,
sandbox : function() {
return jQuery('<div class="sandbox"></div>')
}
},
// --- Matchers
matchers : {
have_tag : "jQuery(expected, actual).length == 1",
have_one : "alias have_tag",
have_tags : "jQuery(expected, actual).length > 1",
have_many : "alias have_tags",
have_child : "jQuery(actual).children(expected).length == 1",
have_children : "jQuery(actual).children(expected).length > 1",
have_text : "jQuery(actual).text() == expected",
have_value : "jQuery(actual).val() == expected",
be_enabled : "!jQuery(actual).attr('disabled')",
have_class : "jQuery(actual).hasClass(expected)",
be_visible : function(actual) {
return jQuery(actual).css('display') != 'none' &&
jQuery(actual).css('visibility') != 'hidden' &&
jQuery(actual).attr('type') != 'hidden'
},
be_hidden : function(actual) {
return !JSpec.does(actual, 'be_visible')
},
have_classes : function(actual) {
return !JSpec.any(JSpec.argumentsToArray(arguments, 1), function(arg){
return !JSpec.does(actual, 'have_class', arg)
})
},
have_attr : function(actual, attr, value) {
return value ? jQuery(actual).attr(attr) == value:
jQuery(actual).attr(attr)
},
'be disabled selected checked' : function(attr) {
return 'jQuery(actual).attr("' + attr + '")'
},
'have type id title alt href src sel rev name target' : function(attr) {
return function(actual, value) {
return JSpec.does(actual, 'have_attr', attr, value)
}
}
}
})

View File

@ -0,0 +1,1715 @@
// JSpec - Core - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
(function(){
JSpec = {
version : '2.8.4',
cache : {},
suites : [],
modules : [],
allSuites : [],
matchers : {},
stubbed : [],
request : 'XMLHttpRequest' in this ? XMLHttpRequest : null,
stats : { specs : 0, assertions : 0, failures : 0, passes : 0, specsFinished : 0, suitesFinished : 0 },
options : { profile : false },
/**
* Default context in which bodies are evaluated.
*
* Replace context simply by setting JSpec.context
* to your own like below:
*
* JSpec.context = { foo : 'bar' }
*
* Contexts can be changed within any body, this can be useful
* in order to provide specific helper methods to specific suites.
*
* To reset (usually in after hook) simply set to null like below:
*
* JSpec.context = null
*
*/
defaultContext : {
/**
* Return an object used for proxy assertions.
* This object is used to indicate that an object
* should be an instance of _object_, not the constructor
* itself.
*
* @param {function} constructor
* @return {hash}
* @api public
*/
an_instance_of : function(constructor) {
return { an_instance_of : constructor }
},
/**
* Load fixture at _path_. This utility function
* supplies the means to resolve, and cache fixture contents
* via the DOM or Rhino.
*
* Fixtures are resolved as:
*
* - <path>
* - fixtures/<path>
* - fixtures/<path>.html
*
* @param {string} path
* @return {string}
* @api public
*/
fixture : function(path) {
if (JSpec.cache[path]) return JSpec.cache[path]
return JSpec.cache[path] =
JSpec.tryLoading(path) ||
JSpec.tryLoading('fixtures/' + path) ||
JSpec.tryLoading('fixtures/' + path + '.html') ||
JSpec.tryLoading('spec/' + path) ||
JSpec.tryLoading('spec/fixtures/' + path) ||
JSpec.tryLoading('spec/fixtures/' + path + '.html')
}
},
// --- Objects
formatters : {
/**
* Default formatter, outputting to the DOM.
*
* Options:
* - reportToId id of element to output reports to, defaults to 'jspec'
* - failuresOnly displays only suites with failing specs
*
* @api public
*/
DOM : function(results, options) {
var id = option('reportToId') || 'jspec'
var report = document.getElementById(id)
var failuresOnly = option('failuresOnly')
var classes = results.stats.failures ? 'has-failures' : ''
if (!report) throw 'JSpec requires the element #' + id + ' to output its reports'
var markup =
'<div id="jspec-report" class="' + classes + '"><div class="heading"> \
<span class="passes">Passes: <em>' + results.stats.passes + '</em></span> \
<span class="failures">Failures: <em>' + results.stats.failures + '</em></span> \
</div><table class="suites">'
bodyContents = function(body) {
return JSpec.
escape(JSpec.contentsOf(body)).
replace(/^ */gm, function(a){ return (new Array(Math.round(a.length / 3))).join(' ') }).
replace("\n", '<br/>')
}
renderSuite = function(suite) {
var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
if (displaySuite && suite.hasSpecs()) {
markup += '<tr class="description"><td colspan="2">' + escape(suite.description) + '</td></tr>'
each(suite.specs, function(i, spec){
markup += '<tr class="' + (i % 2 ? 'odd' : 'even') + '">'
if (spec.requiresImplementation())
markup += '<td class="requires-implementation" colspan="2">' + escape(spec.description) + '</td>'
else if (spec.passed() && !failuresOnly)
markup += '<td class="pass">' + escape(spec.description)+ '</td><td>' + spec.assertionsGraph() + '</td>'
else if(!spec.passed())
markup += '<td class="fail">' + escape(spec.description) + ' <em>' + spec.failure().message + '</em>' + '</td><td>' + spec.assertionsGraph() + '</td>'
markup += '<tr class="body"><td colspan="2"><pre>' + bodyContents(spec.body) + '</pre></td></tr>'
})
markup += '</tr>'
}
}
renderSuites = function(suites) {
each(suites, function(suite){
renderSuite(suite)
if (suite.hasSuites()) renderSuites(suite.suites)
})
}
renderSuites(results.suites)
markup += '</table></div>'
report.innerHTML = markup
},
/**
* Terminal formatter.
*
* @api public
*/
Terminal : function(results, options) {
failuresOnly = option('failuresOnly')
print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') +
color(" Failures: ", 'bold') + color(results.stats.failures, 'red') + "\n")
indent = function(string) {
return string.replace(/^(.)/gm, ' $1')
}
renderSuite = function(suite) {
displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran
if (displaySuite && suite.hasSpecs()) {
print(color(' ' + suite.description, 'bold'))
each(suite.specs, function(spec){
var assertionsGraph = inject(spec.assertions, '', function(graph, assertion){
return graph + color('.', assertion.passed ? 'green' : 'red')
})
if (spec.requiresImplementation())
print(color(' ' + spec.description, 'blue') + assertionsGraph)
else if (spec.passed() && !failuresOnly)
print(color(' ' + spec.description, 'green') + assertionsGraph)
else if (!spec.passed())
print(color(' ' + spec.description, 'red') + assertionsGraph +
"\n" + indent(spec.failure().message) + "\n")
})
print("")
}
}
renderSuites = function(suites) {
each(suites, function(suite){
renderSuite(suite)
if (suite.hasSuites()) renderSuites(suite.suites)
})
}
renderSuites(results.suites)
},
/**
* Console formatter, tested with Firebug and Safari 4.
*
* @api public
*/
Console : function(results, options) {
console.log('')
console.log('Passes: ' + results.stats.passes + ' Failures: ' + results.stats.failures)
renderSuite = function(suite) {
if (suite.ran) {
console.group(suite.description)
each(suite.specs, function(spec){
var assertionCount = spec.assertions.length + ':'
if (spec.requiresImplementation())
console.warn(spec.description)
else if (spec.passed())
console.log(assertionCount + ' ' + spec.description)
else
console.error(assertionCount + ' ' + spec.description + ', ' + spec.failure().message)
})
console.groupEnd()
}
}
renderSuites = function(suites) {
each(suites, function(suite){
renderSuite(suite)
if (suite.hasSuites()) renderSuites(suite.suites)
})
}
renderSuites(results.suites)
}
},
Assertion : function(matcher, actual, expected, negate) {
extend(this, {
message : '',
passed : false,
actual : actual,
negate : negate,
matcher : matcher,
expected : expected,
// Report assertion results
report : function() {
this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++
return this
},
// Run the assertion
run : function() {
// TODO: remove unshifting
expected.unshift(actual)
this.result = matcher.match.apply(this, expected)
this.passed = negate ? !this.result : this.result
if (!this.passed) this.message = matcher.message.call(this, actual, expected, negate, matcher.name)
return this
}
})
},
ProxyAssertion : function(object, method, times, negate) {
var self = this
var old = object[method]
// Proxy
object[method] = function(){
args = argumentsToArray(arguments)
result = old.apply(object, args)
self.calls.push({ args : args, result : result })
return result
}
// Times
this.times = {
'once' : 1,
'twice' : 2
}[times] || times || 1
extend(this, {
calls : [],
message : '',
defer : true,
passed : false,
negate : negate,
object : object,
method : method,
// Proxy return value
and_return : function(result) {
this.expectedResult = result
return this
},
// Proxy arguments passed
with_args : function() {
this.expectedArgs = argumentsToArray(arguments)
return this
},
// Check if any calls have failing results
anyResultsFail : function() {
return any(this.calls, function(call){
return self.expectedResult.an_instance_of ?
call.result.constructor != self.expectedResult.an_instance_of:
hash(self.expectedResult) != hash(call.result)
})
},
// Check if any calls have passing results
anyResultsPass : function() {
return any(this.calls, function(call){
return self.expectedResult.an_instance_of ?
call.result.constructor == self.expectedResult.an_instance_of:
hash(self.expectedResult) == hash(call.result)
})
},
// Return the passing result
passingResult : function() {
return this.anyResultsPass().result
},
// Return the failing result
failingResult : function() {
return this.anyResultsFail().result
},
// Check if any arguments fail
anyArgsFail : function() {
return any(this.calls, function(call){
return any(self.expectedArgs, function(i, arg){
if (arg == null) return call.args[i] == null
return arg.an_instance_of ?
call.args[i].constructor != arg.an_instance_of:
hash(arg) != hash(call.args[i])
})
})
},
// Check if any arguments pass
anyArgsPass : function() {
return any(this.calls, function(call){
return any(self.expectedArgs, function(i, arg){
return arg.an_instance_of ?
call.args[i].constructor == arg.an_instance_of:
hash(arg) == hash(call.args[i])
})
})
},
// Return the passing args
passingArgs : function() {
return this.anyArgsPass().args
},
// Return the failing args
failingArgs : function() {
return this.anyArgsFail().args
},
// Report assertion results
report : function() {
this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++
return this
},
// Run the assertion
run : function() {
var methodString = 'expected ' + object.toString() + '.' + method + '()' + (negate ? ' not' : '' )
function times(n) {
return n > 2 ? n + ' times' : { 1 : 'once', 2 : 'twice' }[n]
}
if (this.expectedResult != null && (negate ? this.anyResultsPass() : this.anyResultsFail()))
this.message = methodString + ' to return ' + puts(this.expectedResult) +
' but ' + (negate ? 'it did' : 'got ' + puts(this.failingResult()))
if (this.expectedArgs && (negate ? !this.expectedResult && this.anyArgsPass() : this.anyArgsFail()))
this.message = methodString + ' to be called with ' + puts.apply(this, this.expectedArgs) +
' but was' + (negate ? '' : ' called with ' + puts.apply(this, this.failingArgs()))
if (negate ? !this.expectedResult && !this.expectedArgs && this.calls.length == this.times : this.calls.length != this.times)
this.message = methodString + ' to be called ' + times(this.times) +
', but ' + (this.calls.length == 0 ? ' was not called' : ' was called ' + times(this.calls.length))
if (!this.message.length)
this.passed = true
return this
}
})
},
/**
* Specification Suite block object.
*
* @param {string} description
* @param {function} body
* @api private
*/
Suite : function(description, body) {
var self = this
extend(this, {
body: body,
description: description,
suites: [],
specs: [],
ran: false,
hooks: { 'before' : [], 'after' : [], 'before_each' : [], 'after_each' : [] },
// Add a spec to the suite
addSpec : function(description, body) {
var spec = new JSpec.Spec(description, body)
this.specs.push(spec)
JSpec.stats.specs++ // TODO: abstract
spec.suite = this
},
// Add a hook to the suite
addHook : function(hook, body) {
this.hooks[hook].push(body)
},
// Add a nested suite
addSuite : function(description, body) {
var suite = new JSpec.Suite(description, body)
JSpec.allSuites.push(suite)
suite.name = suite.description
suite.description = this.description + ' ' + suite.description
this.suites.push(suite)
suite.suite = this
},
// Invoke a hook in context to this suite
hook : function(hook) {
if (this.suite) this.suite.hook(hook)
each(this.hooks[hook], function(body) {
JSpec.evalBody(body, "Error in hook '" + hook + "', suite '" + self.description + "': ")
})
},
// Check if nested suites are present
hasSuites : function() {
return this.suites.length
},
// Check if this suite has specs
hasSpecs : function() {
return this.specs.length
},
// Check if the entire suite passed
passed : function() {
return !any(this.specs, function(spec){
return !spec.passed()
})
}
})
},
/**
* Specification block object.
*
* @param {string} description
* @param {function} body
* @api private
*/
Spec : function(description, body) {
extend(this, {
body : body,
description : description,
assertions : [],
// Add passing assertion
pass : function(message) {
this.assertions.push({ passed : true, message : message })
++JSpec.stats.passes
},
// Add failing assertion
fail : function(message) {
this.assertions.push({ passed : false, message : message })
++JSpec.stats.failures
},
// Run deferred assertions
runDeferredAssertions : function() {
each(this.assertions, function(assertion){
if (assertion.defer) assertion.run().report(), hook('afterAssertion', assertion)
})
},
// Find first failing assertion
failure : function() {
return find(this.assertions, function(assertion){
return !assertion.passed
})
},
// Find all failing assertions
failures : function() {
return select(this.assertions, function(assertion){
return !assertion.passed
})
},
// Weither or not the spec passed
passed : function() {
return !this.failure()
},
// Weither or not the spec requires implementation (no assertions)
requiresImplementation : function() {
return this.assertions.length == 0
},
// Sprite based assertions graph
assertionsGraph : function() {
return map(this.assertions, function(assertion){
return '<span class="assertion ' + (assertion.passed ? 'passed' : 'failed') + '"></span>'
}).join('')
}
})
},
Module : function(methods) {
extend(this, methods)
},
// --- DSLs
DSLs : {
snake : {
expect : function(actual){
return JSpec.expect(actual)
},
describe : function(description, body) {
return JSpec.currentSuite.addSuite(description, body)
},
it : function(description, body) {
return JSpec.currentSuite.addSpec(description, body)
},
before : function(body) {
return JSpec.currentSuite.addHook('before', body)
},
after : function(body) {
return JSpec.currentSuite.addHook('after', body)
},
before_each : function(body) {
return JSpec.currentSuite.addHook('before_each', body)
},
after_each : function(body) {
return JSpec.currentSuite.addHook('after_each', body)
},
should_behave_like : function(description) {
return JSpec.shareBehaviorsOf(description)
}
}
},
// --- Methods
/**
* Check if _value_ is 'stop'. For use as a
* utility callback function.
*
* @param {mixed} value
* @return {bool}
* @api public
*/
haveStopped : function(value) {
return value === 'stop'
},
/**
* Include _object_ which may be a hash or Module instance.
*
* @param {has, Module} object
* @return {JSpec}
* @api public
*/
include : function(object) {
var module = object.constructor == JSpec.Module ? object : new JSpec.Module(object)
this.modules.push(module)
if ('init' in module) module.init()
if ('utilities' in module) extend(this.defaultContext, module.utilities)
if ('matchers' in module) this.addMatchers(module.matchers)
if ('DSLs' in module)
each(module.DSLs, function(name, methods){
JSpec.DSLs[name] = JSpec.DSLs[name] || {}
extend(JSpec.DSLs[name], methods)
})
return this
},
/**
* Add a module hook _name_, which is immediately
* called per module with the _args_ given. An array of
* hook return values is returned.
*
* @param {name} string
* @param {...} args
* @return {array}
* @api private
*/
hook : function(name, args) {
args = argumentsToArray(arguments, 1)
return inject(JSpec.modules, [], function(results, module){
if (typeof module[name] == 'function')
results.push(JSpec.evalHook(module, name, args))
})
},
/**
* Eval _module_ hook _name_ with _args_. Evaluates in context
* to the module itself, JSpec, and JSpec.context.
*
* @param {Module} module
* @param {string} name
* @param {array} args
* @return {mixed}
* @api private
*/
evalHook : function(module, name, args) {
var context = this.context || this.defaultContext
var contents = this.contentsOf(module[name])
var params = this.paramsFor(module[name])
module.utilities = module.utilities || {}
params.unshift('context'); args.unshift(context)
hook('evaluatingHookBody', module, name, context)
try { return new Function(params.join(), 'with (this.utilities) { with (context) { with (JSpec) { ' + contents + ' }}}').apply(module, args) }
catch(e) { error('Error in hook ' + module.name + "." + name + ': ', e) }
},
/**
* Same as hook() however accepts only one _arg_ which is
* considered immutable. This function passes the arg
* to the first module, then passes the return value of the last
* module called, to the following module.
*
* @param {string} name
* @param {mixed} arg
* @return {mixed}
* @api private
*/
hookImmutable : function(name, arg) {
return inject(JSpec.modules, arg, function(result, module){
if (typeof module[name] == 'function')
return JSpec.evalHook(module, name, [result])
})
},
/**
* Find a suite by its description or name.
*
* @param {string} description
* @return {Suite}
* @api private
*/
findSuite : function(description) {
return find(this.allSuites, function(suite){
return suite.name == description || suite.description == description
})
},
/**
* Share behaviors (specs) of the given suite with
* the current suite.
*
* @param {string} description
* @api public
*/
shareBehaviorsOf : function(description) {
if (suite = this.findSuite(description)) this.copySpecs(suite, this.currentSuite)
else throw 'failed to share behaviors. ' + puts(description) + ' is not a valid Suite name'
},
/**
* Copy specs from one suite to another.
*
* @param {Suite} fromSuite
* @param {Suite} toSuite
* @api public
*/
copySpecs : function(fromSuite, toSuite) {
each(fromSuite.specs, function(spec){
spec.assertions = []
toSuite.specs.push(spec)
})
},
/**
* Convert arguments to an array.
*
* @param {object} arguments
* @param {int} offset
* @return {array}
* @api public
*/
argumentsToArray : function(arguments, offset) {
return Array.prototype.slice.call(arguments, offset || 0)
},
/**
* Return ANSI-escaped colored string.
*
* @param {string} string
* @param {string} color
* @return {string}
* @api public
*/
color : function(string, color) {
return "\u001B[" + {
bold : 1,
black : 30,
red : 31,
green : 32,
yellow : 33,
blue : 34,
magenta : 35,
cyan : 36,
white : 37
}[color] + 'm' + string + "\u001B[0m"
},
/**
* Default matcher message callback.
*
* @api private
*/
defaultMatcherMessage : function(actual, expected, negate, name) {
return 'expected ' + puts(actual) + ' to ' +
(negate ? 'not ' : '') +
name.replace(/_/g, ' ') +
' ' + puts.apply(this, expected.slice(1))
},
/**
* Normalize a matcher message.
*
* When no messge callback is present the defaultMatcherMessage
* will be assigned, will suffice for most matchers.
*
* @param {hash} matcher
* @return {hash}
* @api public
*/
normalizeMatcherMessage : function(matcher) {
if (typeof matcher.message != 'function')
matcher.message = this.defaultMatcherMessage
return matcher
},
/**
* Normalize a matcher body
*
* This process allows the following conversions until
* the matcher is in its final normalized hash state.
*
* - '==' becomes 'actual == expected'
* - 'actual == expected' becomes 'return actual == expected'
* - function(actual, expected) { return actual == expected } becomes
* { match : function(actual, expected) { return actual == expected }}
*
* @param {mixed} body
* @return {hash}
* @api public
*/
normalizeMatcherBody : function(body) {
switch (body.constructor) {
case String:
if (captures = body.match(/^alias (\w+)/)) return JSpec.matchers[last(captures)]
if (body.length < 4) body = 'actual ' + body + ' expected'
return { match : function(actual, expected) { return eval(body) }}
case Function:
return { match : body }
default:
return body
}
},
/**
* Get option value. This method first checks if
* the option key has been set via the query string,
* otherwise returning the options hash value.
*
* @param {string} key
* @return {mixed}
* @api public
*/
option : function(key) {
return (value = query(key)) !== null ? value :
JSpec.options[key] || null
},
/**
* Generates a hash of the object passed.
*
* @param {object} object
* @return {string}
* @api private
*/
hash : function(object) {
if (object == null) return 'null'
if (object == undefined) return 'undefined'
serialize = function(prefix) {
return inject(object, prefix + ':', function(buffer, key, value){
return buffer += hash(value)
})
}
switch (object.constructor) {
case Array : return serialize('a')
case RegExp: return 'r:' + object.toString()
case Number: return 'n:' + object.toString()
case String: return 's:' + object.toString()
case Object: return 'o:' + inject(object, [], function(array, key, value){
array.push([key, hash(value)])
}).sort()
default: return object.toString()
}
},
/**
* Return last element of an array.
*
* @param {array} array
* @return {object}
* @api public
*/
last : function(array) {
return array[array.length - 1]
},
/**
* Convert object(s) to a print-friend string.
*
* @param {...} object
* @return {string}
* @api public
*/
puts : function(object) {
if (arguments.length > 1) {
return map(argumentsToArray(arguments), function(arg){
return puts(arg)
}).join(', ')
}
if (object === undefined) return ''
if (object === null) return 'null'
if (object === true) return 'true'
if (object === false) return 'false'
if (object.an_instance_of) return 'an instance of ' + object.an_instance_of.name
if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector) + ''
if (object.jquery) return escape(object.html())
if (object.nodeName) return escape(object.outerHTML)
switch (object.constructor) {
case String: return "'" + escape(object) + "'"
case Number: return object
case Function: return object.name || object
case Array :
return inject(object, '[', function(b, v){
return b + ', ' + puts(v)
}).replace('[,', '[') + ' ]'
case Object:
return inject(object, '{', function(b, k, v) {
return b + ', ' + puts(k) + ' : ' + puts(v)
}).replace('{,', '{') + ' }'
default:
return escape(object.toString())
}
},
/**
* Escape HTML.
*
* @param {string} html
* @return {string}
* @api public
*/
escape : function(html) {
return html.toString().
replace(/&/gmi, '&amp;').
replace(/"/gmi, '&quot;').
replace(/>/gmi, '&gt;').
replace(/</gmi, '&lt;')
},
/**
* Perform an assertion without reporting.
*
* This method is primarily used for internal
* matchers in order retain DRYness. May be invoked
* like below:
*
* does('foo', 'eql', 'foo')
* does([1,2], 'include', 1, 2)
*
* External hooks are not run for internal assertions
* performed by does().
*
* @param {mixed} actual
* @param {string} matcher
* @param {...} expected
* @return {mixed}
* @api private
*/
does : function(actual, matcher, expected) {
var assertion = new JSpec.Assertion(JSpec.matchers[matcher], actual, argumentsToArray(arguments, 2))
return assertion.run().result
},
/**
* Perform an assertion.
*
* expect(true).to('be', true)
* expect('foo').not_to('include', 'bar')
* expect([1, [2]]).to('include', 1, [2])
*
* @param {mixed} actual
* @return {hash}
* @api public
*/
expect : function(actual) {
assert = function(matcher, args, negate) {
var expected = argumentsToArray(args, 1)
matcher.negate = negate
assertion = new JSpec.Assertion(matcher, actual, expected, negate)
hook('beforeAssertion', assertion)
if (matcher.defer) assertion.run()
else JSpec.currentSpec.assertions.push(assertion.run().report()), hook('afterAssertion', assertion)
return assertion.result
}
to = function(matcher) {
return assert(matcher, arguments, false)
}
not_to = function(matcher) {
return assert(matcher, arguments, true)
}
return {
to : to,
should : to,
not_to: not_to,
should_not : not_to
}
},
/**
* Strim whitespace or chars.
*
* @param {string} string
* @param {string} chars
* @return {string}
* @api public
*/
strip : function(string, chars) {
return string.
replace(new RegExp('[' + (chars || '\\s') + ']*$'), '').
replace(new RegExp('^[' + (chars || '\\s') + ']*'), '')
},
/**
* Call an iterator callback with arguments a, or b
* depending on the arity of the callback.
*
* @param {function} callback
* @param {mixed} a
* @param {mixed} b
* @return {mixed}
* @api private
*/
callIterator : function(callback, a, b) {
return callback.length == 1 ? callback(b) : callback(a, b)
},
/**
* Extend an object with another.
*
* @param {object} object
* @param {object} other
* @api public
*/
extend : function(object, other) {
each(other, function(property, value){
object[property] = value
})
},
/**
* Iterate an object, invoking the given callback.
*
* @param {hash, array, string} object
* @param {function} callback
* @return {JSpec}
* @api public
*/
each : function(object, callback) {
if (typeof object == 'string') object = object.split(' ')
for (key in object)
if (object.hasOwnProperty(key))
callIterator(callback, key, object[key])
},
/**
* Iterate with memo.
*
* @param {hash, array} object
* @param {object} memo
* @param {function} callback
* @return {object}
* @api public
*/
inject : function(object, memo, callback) {
each(object, function(key, value){
memo = (callback.length == 2 ?
callback(memo, value):
callback(memo, key, value)) ||
memo
})
return memo
},
/**
* Destub _object_'s _method_. When no _method_ is passed
* all stubbed methods are destubbed. When no arguments
* are passed every object found in JSpec.stubbed will be
* destubbed.
*
* @param {mixed} object
* @param {string} method
* @api public
*/
destub : function(object, method) {
if (method) {
if (object['__prototype__' + method])
delete object[method]
else
object[method] = object['__original__' + method]
delete object['__prototype__' + method]
delete object['__original____' + method]
}
else if (object) {
for (var key in object)
if (captures = key.match(/^(?:__prototype__|__original__)(.*)/))
destub(object, captures[1])
}
else
while (JSpec.stubbed.length)
destub(JSpec.stubbed.shift())
},
/**
* Stub _object_'s _method_.
*
* stub(foo, 'toString').and_return('bar')
*
* @param {mixed} object
* @param {string} method
* @return {hash}
* @api public
*/
stub : function(object, method) {
hook('stubbing', object, method)
JSpec.stubbed.push(object)
var type = object.hasOwnProperty(method) ? '__original__' : '__prototype__'
object[type + method] = object[method]
object[method] = function(){}
return {
and_return : function(value) {
if (typeof value == 'function') object[method] = value
else object[method] = function(){ return value }
}
}
},
/**
* Map callback return values.
*
* @param {hash, array} object
* @param {function} callback
* @return {array}
* @api public
*/
map : function(object, callback) {
return inject(object, [], function(memo, key, value){
memo.push(callIterator(callback, key, value))
})
},
/**
* Returns the first matching expression or null.
*
* @param {hash, array} object
* @param {function} callback
* @return {mixed}
* @api public
*/
any : function(object, callback) {
return inject(object, null, function(state, key, value){
if (state == undefined)
return callIterator(callback, key, value) ? value : state
})
},
/**
* Returns an array of values collected when the callback
* given evaluates to true.
*
* @param {hash, array} object
* @return {function} callback
* @return {array}
* @api public
*/
select : function(object, callback) {
return inject(object, [], function(selected, key, value){
if (callIterator(callback, key, value))
selected.push(value)
})
},
/**
* Define matchers.
*
* @param {hash} matchers
* @api public
*/
addMatchers : function(matchers) {
each(matchers, function(name, body){
JSpec.addMatcher(name, body)
})
},
/**
* Define a matcher.
*
* @param {string} name
* @param {hash, function, string} body
* @api public
*/
addMatcher : function(name, body) {
hook('addingMatcher', name, body)
if (name.indexOf(' ') != -1) {
var matchers = name.split(/\s+/)
var prefix = matchers.shift()
each(matchers, function(name) {
JSpec.addMatcher(prefix + '_' + name, body(name))
})
}
this.matchers[name] = this.normalizeMatcherMessage(this.normalizeMatcherBody(body))
this.matchers[name].name = name
},
/**
* Add a root suite to JSpec.
*
* @param {string} description
* @param {body} function
* @api public
*/
describe : function(description, body) {
var suite = new JSpec.Suite(description, body)
hook('addingSuite', suite)
this.allSuites.push(suite)
this.suites.push(suite)
},
/**
* Return the contents of a function body.
*
* @param {function} body
* @return {string}
* @api public
*/
contentsOf : function(body) {
return body.toString().match(/^[^\{]*{((.*\n*)*)}/m)[1]
},
/**
* Return param names for a function body.
*
* @param {function} body
* @return {array}
* @api public
*/
paramsFor : function(body) {
return body.toString().match(/\((.*?)\)/)[1].match(/[\w]+/g) || []
},
/**
* Evaluate a JSpec capture body.
*
* @param {function} body
* @param {string} errorMessage (optional)
* @return {Type}
* @api private
*/
evalBody : function(body, errorMessage) {
var dsl = this.DSL || this.DSLs.snake
var matchers = this.matchers
var context = this.context || this.defaultContext
var contents = this.contentsOf(body)
hook('evaluatingBody', dsl, matchers, context, contents)
try { eval('with (dsl){ with (context) { with (matchers) { ' + contents + ' }}}') }
catch(e) { error(errorMessage, e) }
},
/**
* Pre-process a string of JSpec.
*
* @param {string} input
* @return {string}
* @api private
*/
preprocess : function(input) {
input = hookImmutable('preprocessing', input)
return input.
replace(/([\w\.]+)\.(stub|destub)\((.*?)\)$/gm, '$2($1, $3)').
replace(/describe\s+(.*?)$/gm, 'describe($1, function(){').
replace(/^\s+it\s+(.*?)$/gm, ' it($1, function(){').
replace(/^(?: *)(before_each|after_each|before|after)(?= |\n|$)/gm, 'JSpec.currentSuite.addHook("$1", function(){').
replace(/^\s*end(?=\s|$)/gm, '});').
replace(/-\{/g, 'function(){').
replace(/(\d+)\.\.(\d+)/g, function(_, a, b){ return range(a, b) }).
replace(/\.should([_\.]not)?[_\.](\w+)(?: |;|$)(.*)$/gm, '.should$1_$2($3)').
replace(/([\/\s]*)(.+?)\.(should(?:[_\.]not)?)[_\.](\w+)\((.*)\)\s*;?$/gm, '$1 expect($2).$3($4, $5)').
replace(/, \)/gm, ')').
replace(/should\.not/gm, 'should_not')
},
/**
* Create a range string which can be evaluated to a native array.
*
* @param {int} start
* @param {int} end
* @return {string}
* @api public
*/
range : function(start, end) {
var current = parseInt(start), end = parseInt(end), values = [current]
if (end > current) while (++current <= end) values.push(current)
else while (--current >= end) values.push(current)
return '[' + values + ']'
},
/**
* Report on the results.
*
* @api public
*/
report : function() {
hook('reporting', JSpec.options)
new (JSpec.options.formatter || JSpec.formatters.DOM)(JSpec, JSpec.options)
},
/**
* Run the spec suites. Options are merged
* with JSpec options when present.
*
* @param {hash} options
* @return {JSpec}
* @api public
*/
run : function(options) {
if (any(hook('running'), haveStopped)) return this
if (options) extend(this.options, options)
if (option('profile')) console.group('Profile')
each(this.suites, function(suite) { JSpec.runSuite(suite) })
if (option('profile')) console.groupEnd()
return this
},
/**
* Run a suite.
*
* @param {Suite} suite
* @api public
*/
runSuite : function(suite) {
this.currentSuite = suite
this.evalBody(suite.body)
suite.ran = true
hook('beforeSuite', suite), suite.hook('before')
each(suite.specs, function(spec) {
hook('beforeSpec', spec)
suite.hook('before_each')
JSpec.runSpec(spec)
hook('afterSpec', spec)
suite.hook('after_each')
})
if (suite.hasSuites()) {
each(suite.suites, function(suite) {
JSpec.runSuite(suite)
})
}
hook('afterSuite', suite), suite.hook('after')
this.stats.suitesFinished++
},
/**
* Report a failure for the current spec.
*
* @param {string} message
* @api public
*/
fail : function(message) {
JSpec.currentSpec.fail(message)
},
/**
* Report a passing assertion for the current spec.
*
* @param {string} message
* @api public
*/
pass : function(message) {
JSpec.currentSpec.pass(message)
},
/**
* Run a spec.
*
* @param {Spec} spec
* @api public
*/
runSpec : function(spec) {
this.currentSpec = spec
if (option('profile')) console.time(spec.description)
try { this.evalBody(spec.body) }
catch (e) { fail(e) }
if (option('profile')) console.timeEnd(spec.description)
spec.runDeferredAssertions()
destub()
this.stats.specsFinished++
this.stats.assertions += spec.assertions.length
},
/**
* Require a dependency, with optional message.
*
* @param {string} dependency
* @param {string} message (optional)
* @return {JSpec}
* @api public
*/
requires : function(dependency, message) {
hook('requiring', dependency, message)
try { eval(dependency) }
catch (e) { throw 'JSpec depends on ' + dependency + ' ' + message }
return this
},
/**
* Query against the current query strings keys
* or the queryString specified.
*
* @param {string} key
* @param {string} queryString
* @return {string, null}
* @api private
*/
query : function(key, queryString) {
var queryString = (queryString || (main.location ? main.location.search : null) || '').substring(1)
return inject(queryString.split('&'), null, function(value, pair){
parts = pair.split('=')
return parts[0] == key ? parts[1].replace(/%20|\+/gmi, ' ') : value
})
},
/**
* Throw a JSpec related error.
*
* @param {string} message
* @param {Exception} e
* @api public
*/
error : function(message, e) {
throw (message ? message : '') + e.toString() +
(e.line ? ' near line ' + e.line : '')
},
/**
* Ad-hoc POST request for JSpec server usage.
*
* @param {string} url
* @param {string} data
* @api private
*/
post : function(url, data) {
if (any(hook('posting', url, data), haveStopped)) return
var request = this.xhr()
request.open('POST', url, false)
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
request.send(data)
},
/**
* Report to server with statistics.
*
* @param {string} url
* @api private
*/
reportToServer : function(url) {
if (any(hook('reportingToServer', url), haveStopped)) return
JSpec.post(url || 'http://localhost:4444', 'passes=' + JSpec.stats.passes + '&failures=' + JSpec.stats.failures)
if ('close' in main) main.close()
},
/**
* Instantiate an XMLHttpRequest.
*
* @return {XMLHttpRequest, ActiveXObject}
* @api private
*/
xhr : function() {
return new (JSpec.request || ActiveXObject("Microsoft.XMLHTTP"))
},
/**
* Check for HTTP request support.
*
* @return {bool}
* @api private
*/
hasXhr : function() {
return JSpec.request || 'ActiveXObject' in main
},
/**
* Try loading _file_ returning the contents
* string or null. Chain to locate / read a file.
*
* @param {string} file
* @return {string}
* @api public
*/
tryLoading : function(file) {
try { return JSpec.load(file) }
catch (e) {}
},
/**
* Load a _file_'s contents.
*
* @param {string} file
* @param {function} callback
* @return {string}
* @api public
*/
load : function(file, callback) {
if (any(hook('loading', file), haveStopped)) return
if ('readFile' in main)
return callback ? readFile(file, callback) : readFile(file)
else if (this.hasXhr()) {
var request = this.xhr()
request.open('GET', file, false)
request.send(null)
if (request.readyState == 4) return request.responseText
}
else
error("failed to load `" + file + "'")
},
/**
* Load, pre-process, and evaluate a file.
*
* @param {string} file
* @param {JSpec}
* @api public
*/
exec : function(file) {
if (any(hook('executing', file), haveStopped)) return this
if ('node' in main)
this.load(file, function(contents){
eval('with (JSpec){ ' + JSpec.preprocess(contents) + ' }')
})
else
eval('with (JSpec){' + this.preprocess(this.load(file)) + '}')
return this
}
}
// --- Utility functions
var main = this
var find = JSpec.any
var utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \
error escape extend puts hash query strip color does addMatchers callIterator argumentsToArray'.split(/\s+/)
while (utils.length) util = utils.shift(), eval('var ' + util + ' = JSpec.' + util)
if (!main.setTimeout) main.setTimeout = function(callback){ callback() }
// --- Matchers
addMatchers({
equal : "===",
be : "alias equal",
be_greater_than : ">",
be_less_than : "<",
be_at_least : ">=",
be_at_most : "<=",
be_a : "actual.constructor == expected",
be_an : "alias be_a",
be_an_instance_of : "actual instanceof expected",
be_null : "actual == null",
be_true : "actual == true",
be_false : "actual == false",
be_undefined : "typeof actual == 'undefined'",
be_type : "typeof actual == expected",
match : "typeof actual == 'string' ? actual.match(expected) : false",
respond_to : "typeof actual[expected] == 'function'",
have_length : "actual.length == expected",
be_within : "actual >= expected[0] && actual <= last(expected)",
have_length_within : "actual.length >= expected[0] && actual.length <= last(expected)",
eql : function(actual, expected) {
return actual.constructor == Array ||
actual instanceof Object ?
hash(actual) == hash(expected):
actual == expected
},
receive : { defer : true, match : function(actual, method, times) {
proxy = new JSpec.ProxyAssertion(actual, method, times, this.negate)
JSpec.currentSpec.assertions.push(proxy)
return proxy
}},
be_empty : function(actual) {
if (actual.constructor == Object && actual.length == undefined)
for (var key in actual)
return false;
return !actual.length
},
include : function(actual) {
for (state = true, i = 1; i < arguments.length; i++) {
arg = arguments[i]
switch (actual.constructor) {
case String:
case Number:
case RegExp:
case Function:
state = actual.toString().match(arg.toString())
break
case Object:
state = arg in actual
break
case Array:
state = any(actual, function(value){ return hash(value) == hash(arg) })
break
}
if (!state) return false
}
return true
},
throw_error : { match : function(actual, expected, message) {
try { actual() }
catch (e) {
this.e = e
var assert = function(arg) {
switch (arg.constructor) {
case RegExp : return arg.test(e)
case String : return arg == (e.message || e.toString())
case Function : return (e.name || 'Error') == arg.name
}
}
return message ? assert(expected) && assert(message) :
expected ? assert(expected) :
true
}
}, message : function(actual, expected, negate) {
// TODO: refactor when actual is not in expected [0]
var message_for = function(i) {
if (expected[i] == undefined) return 'exception'
switch (expected[i].constructor) {
case RegExp : return 'exception matching ' + puts(expected[i])
case String : return 'exception of ' + puts(expected[i])
case Function : return expected[i].name || 'Error'
}
}
exception = message_for(1) + (expected[2] ? ' and ' + message_for(2) : '')
return 'expected ' + exception + (negate ? ' not ' : '' ) +
' to be thrown, but ' + (this.e ? 'got ' + puts(this.e) : 'nothing was')
}},
have : function(actual, length, property) {
return actual[property].length == length
},
have_at_least : function(actual, length, property) {
return actual[property].length >= length
},
have_at_most :function(actual, length, property) {
return actual[property].length <= length
},
have_within : function(actual, range, property) {
length = actual[property].length
return length >= range.shift() && length <= range.pop()
},
have_prop : function(actual, property, value) {
return actual[property] == null ||
actual[property] instanceof Function ? false:
value == null ? true:
does(actual[property], 'eql', value)
},
have_property : function(actual, property, value) {
return actual[property] == null ||
actual[property] instanceof Function ? false:
value == null ? true:
value === actual[property]
}
})
if ('exports' in main) exports.JSpec = JSpec
})()

View File

@ -0,0 +1,173 @@
// JSpec - XHR - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
(function(){
// --- Original XMLHttpRequest
var OriginalXMLHttpRequest = 'XMLHttpRequest' in this ?
XMLHttpRequest :
function(){}
// --- MockXMLHttpRequest
var MockXMLHttpRequest = function() {
this.requestHeaders = {}
}
MockXMLHttpRequest.prototype = {
status : 0,
async : true,
readyState : 0,
responseText : '',
abort : function(){},
onreadystatechange : function(){},
/**
* Return response headers hash.
*/
getAllResponseHeaders : function(){
return this.responseHeaders
},
/**
* Return case-insensitive value for header _name_.
*/
getResponseHeader : function(name) {
return this.responseHeaders[name.toLowerCase()]
},
/**
* Set case-insensitive _value_ for header _name_.
*/
setRequestHeader : function(name, value) {
this.requestHeaders[name.toLowerCase()] = value
},
/**
* Open mock request.
*/
open : function(method, url, async, user, password) {
this.user = user
this.password = password
this.url = url
this.readyState = 1
this.method = method.toUpperCase()
if (async != undefined) this.async = async
if (this.async) this.onreadystatechange()
},
/**
* Send request _data_.
*/
send : function(data) {
this.data = data
this.readyState = 4
if (this.method == 'HEAD') this.responseText = null
this.responseHeaders['content-length'] = (this.responseText || '').length
if(this.async) this.onreadystatechange()
}
}
// --- Response status codes
JSpec.statusCodes = {
100 : 'Continue',
101 : 'Switching Protocols',
200 : 'OK',
201 : 'Created',
202 : 'Accepted',
203 : 'Non-Authoritative Information',
204 : 'No Content',
205 : 'Reset Content',
206 : 'Partial Content',
300 : 'Multiple Choice',
301 : 'Moved Permanently',
302 : 'Found',
303 : 'See Other',
304 : 'Not Modified',
305 : 'Use Proxy',
307 : 'Temporary Redirect',
400 : 'Bad Request',
401 : 'Unauthorized',
402 : 'Payment Required',
403 : 'Forbidden',
404 : 'Not Found',
405 : 'Method Not Allowed',
406 : 'Not Acceptable',
407 : 'Proxy Authentication Required',
408 : 'Request Timeout',
409 : 'Conflict',
410 : 'Gone',
411 : 'Length Required',
412 : 'Precondition Failed',
413 : 'Request Entity Too Large',
414 : 'Request-URI Too Long',
415 : 'Unsupported Media Type',
416 : 'Requested Range Not Satisfiable',
417 : 'Expectation Failed',
422 : 'Unprocessable Entity',
500 : 'Internal Server Error',
501 : 'Not Implemented',
502 : 'Bad Gateway',
503 : 'Service Unavailable',
504 : 'Gateway Timeout',
505 : 'HTTP Version Not Supported'
}
/**
* Mock XMLHttpRequest requests.
*
* mockRequest().and_return('some data', 'text/plain', 200, { 'X-SomeHeader' : 'somevalue' })
*
* @return {hash}
* @api public
*/
function mockRequest() {
return { and_return : function(body, type, status, headers) {
XMLHttpRequest = MockXMLHttpRequest
status = status || 200
headers = headers || {}
headers['content-type'] = type
JSpec.extend(XMLHttpRequest.prototype, {
responseText : body,
responseHeaders : headers,
status : status,
statusText : JSpec.statusCodes[status]
})
}}
}
/**
* Unmock XMLHttpRequest requests.
*
* @api public
*/
function unmockRequest() {
XMLHttpRequest = OriginalXMLHttpRequest
}
JSpec.include({
// --- Utilities
utilities : {
mockRequest : mockRequest,
unmockRequest : unmockRequest
},
// --- Hooks
afterSpec : function() {
this.utilities.unmockRequest()
}
})
})()

View File

@ -0,0 +1,16 @@
module JSpec
class Browser
def open url
`open -g -a #{name} #{url}`
end
def name
self.class.to_s.split('::').last
end
class Firefox < self; end
class Safari < self; end
class Opera < self; end
end
end

View File

@ -0,0 +1,100 @@
require 'rubygems'
require 'rack'
require 'server/browsers'
module JSpec
class Server
attr_reader :responses, :browsers, :root
def initialize options = {}
@responses = []
@browsers = options.delete :browsers
@root = options.delete :root
end
def call env
request = Rack::Request.new env
path = request.path_info
body = case path
when '/'
agent = env['HTTP_USER_AGENT']
responses << browser(agent)
display_results browser(agent), request['failures'], request['passes']
type = 'text/plain'
'close'
when /jspec/
type = 'application/javascript'
File.read File.join(JSPEC_ROOT, 'lib', File.basename(path))
else
type = Rack::Mime.mime_type File.extname(path)
File.read File.join(root, path) rescue ''
end
[200, { 'Content-Type' => type, 'Content-Length' => body.length.to_s }, body]
end
def display_results browser, failures, passes
puts '%-14s - passes: %s failures: %s' % [bold(browser), green(passes), red(failures)]
end
def browser string
case string
when /Safari/ ; :Safari
when /Firefox/ ; :Firefox
when /MSIE/ ; :MSIE
when /Opera/ ; :Opera
end
end
def bold string
color string, 1
end
def red string
color string, 31
end
def green string
color string, 32
end
def color string, code
"\e[#{code}m#{string}\e[m"
end
def when_finished &block
Thread.new {
sleep 0.1 while responses.length < browsers.length
yield
}
end
def self.start options, spec
app = Rack::Builder.new do
server = JSpec::Server.new :browsers => options.browsers, :root => '.'
server.when_finished { exit }
run server
end
unless options.server_only
Thread.new {
sleep 2
puts "Running browsers: #{options.browsers.join(', ')}\n\n"
run_browsers options.browsers, spec
}
end
puts "JSpec server started\n"
Rack::Handler::Mongrel.run app, :Port => 4444
self
end
def self.run_browsers browsers, spec
browsers.each do |name|
browser(name).open "http://localhost:4444/#{spec}"
end
end
def self.browser name
eval("JSpec::Browser::#{name}").new
end
end
end

View File

@ -0,0 +1 @@
cookies!

View File

@ -0,0 +1,695 @@
/*
* Simulated browser environment for Rhino
* By John Resig <http://ejohn.org/>
* Copyright 2007 John Resig, under the MIT License
*/
// The window Object
var window = this;
(function(){
// Browser Navigator
window.navigator = {
get userAgent(){
return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3";
}
};
var curLocation = (new java.io.File("./")).toURL();
window.__defineSetter__("location", function(url){
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = function(){
curLocation = new java.net.URL( curLocation, url );
window.document = xhr.responseXML;
var event = document.createEvent();
event.initEvent("load");
window.dispatchEvent( event );
};
xhr.send();
});
window.__defineGetter__("location", function(url){
return {
get protocol(){
return curLocation.getProtocol() + ":";
},
get href(){
return curLocation.toString();
},
toString: function(){
return this.href;
}
};
});
// Timers
var timers = [];
window.setTimeout = function(fn, time){
var num;
return num = setInterval(function(){
fn();
clearInterval(num);
}, time);
};
window.setInterval = function(fn, time){
var num = timers.length;
timers[num] = new java.lang.Thread(new java.lang.Runnable({
run: function(){
while (true){
java.lang.Thread.currentThread().sleep(time);
fn();
}
}
}));
timers[num].start();
return num;
};
window.clearInterval = function(num){
if ( timers[num] ) {
timers[num].stop();
delete timers[num];
}
};
// Window Events
var events = [{}];
window.addEventListener = function(type, fn){
if ( !this.uuid || this == window ) {
this.uuid = events.length;
events[this.uuid] = {};
}
if ( !events[this.uuid][type] )
events[this.uuid][type] = [];
if ( events[this.uuid][type].indexOf( fn ) < 0 )
events[this.uuid][type].push( fn );
};
window.removeEventListener = function(type, fn){
if ( !this.uuid || this == window ) {
this.uuid = events.length;
events[this.uuid] = {};
}
if ( !events[this.uuid][type] )
events[this.uuid][type] = [];
events[this.uuid][type] =
events[this.uuid][type].filter(function(f){
return f != fn;
});
};
window.dispatchEvent = function(event){
if ( event.type ) {
if ( this.uuid && events[this.uuid][event.type] ) {
var self = this;
events[this.uuid][event.type].forEach(function(fn){
fn.call( self, event );
});
}
if ( this["on" + event.type] )
this["on" + event.type].call( self, event );
}
};
// DOM Document
window.DOMDocument = function(file){
this._file = file;
this._dom = Packages.javax.xml.parsers.
DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse(file);
if ( !obj_nodes.containsKey( this._dom ) )
obj_nodes.put( this._dom, this );
};
DOMDocument.prototype = {
createTextNode: function(text){
return makeNode( this._dom.createTextNode(
text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) );
},
createElement: function(name){
return makeNode( this._dom.createElement(name.toLowerCase()) );
},
getElementsByTagName: function(name){
return new DOMNodeList( this._dom.getElementsByTagName(
name.toLowerCase()) );
},
getElementById: function(id){
var elems = this._dom.getElementsByTagName("*");
for ( var i = 0; i < elems.length; i++ ) {
var elem = elems.item(i);
if ( elem.getAttribute("id") == id )
return makeNode(elem);
}
return null;
},
get body(){
return this.getElementsByTagName("body")[0];
},
get documentElement(){
return makeNode( this._dom.getDocumentElement() );
},
get ownerDocument(){
return null;
},
addEventListener: window.addEventListener,
removeEventListener: window.removeEventListener,
dispatchEvent: window.dispatchEvent,
get nodeName() {
return "#document";
},
importNode: function(node, deep){
return makeNode( this._dom.importNode(node._dom, deep) );
},
toString: function(){
return "Document" + (typeof this._file == "string" ?
": " + this._file : "");
},
get innerHTML(){
return this.documentElement.outerHTML;
},
get defaultView(){
return {
getComputedStyle: function(elem){
return {
getPropertyValue: function(prop){
prop = prop.replace(/\-(\w)/g,function(m,c){
return c.toUpperCase();
});
var val = elem.style[prop];
if ( prop == "opacity" && val == "" )
val = "1";
return val;
}
};
}
};
},
createEvent: function(){
return {
type: "",
initEvent: function(type){
this.type = type;
}
};
}
};
function getDocument(node){
return obj_nodes.get(node);
}
// DOM NodeList
window.DOMNodeList = function(list){
this._dom = list;
this.length = list.getLength();
for ( var i = 0; i < this.length; i++ ) {
var node = list.item(i);
this[i] = makeNode( node );
}
};
DOMNodeList.prototype = {
toString: function(){
return "[ " +
Array.prototype.join.call( this, ", " ) + " ]";
},
get outerHTML(){
return Array.prototype.map.call(
this, function(node){return node.outerHTML;}).join('');
}
};
// DOM Node
window.DOMNode = function(node){
this._dom = node;
};
DOMNode.prototype = {
get nodeType(){
return this._dom.getNodeType();
},
get nodeValue(){
return this._dom.getNodeValue();
},
get nodeName() {
return this._dom.getNodeName();
},
cloneNode: function(deep){
return makeNode( this._dom.cloneNode(deep) );
},
get ownerDocument(){
return getDocument( this._dom.ownerDocument );
},
get documentElement(){
return makeNode( this._dom.documentElement );
},
get parentNode() {
return makeNode( this._dom.getParentNode() );
},
get nextSibling() {
return makeNode( this._dom.getNextSibling() );
},
get previousSibling() {
return makeNode( this._dom.getPreviousSibling() );
},
toString: function(){
return '"' + this.nodeValue + '"';
},
get outerHTML(){
return this.nodeValue;
}
};
// DOM Element
window.DOMElement = function(elem){
this._dom = elem;
this.style = {
get opacity(){ return this._opacity; },
set opacity(val){ this._opacity = val + ""; }
};
// Load CSS info
var styles = (this.getAttribute("style") || "").split(/\s*;\s*/);
for ( var i = 0; i < styles.length; i++ ) {
var style = styles[i].split(/\s*:\s*/);
if ( style.length == 2 )
this.style[ style[0] ] = style[1];
}
};
DOMElement.prototype = extend( new DOMNode(), {
get nodeName(){
return this.tagName.toUpperCase();
},
get tagName(){
return this._dom.getTagName();
},
toString: function(){
return "<" + this.tagName + (this.id ? "#" + this.id : "" ) + ">";
},
get outerHTML(){
var ret = "<" + this.tagName, attr = this.attributes;
for ( var i in attr )
ret += " " + i + "='" + attr[i] + "'";
if ( this.childNodes.length || this.nodeName == "SCRIPT" )
ret += ">" + this.childNodes.outerHTML +
"</" + this.tagName + ">";
else
ret += "/>";
return ret;
},
get attributes(){
var attr = {}, attrs = this._dom.getAttributes();
for ( var i = 0; i < attrs.getLength(); i++ )
attr[ attrs.item(i).nodeName ] = attrs.item(i).nodeValue;
return attr;
},
get innerHTML(){
return this.childNodes.outerHTML;
},
set innerHTML(html){
html = html.replace(/<\/?([A-Z]+)/g, function(m){
return m.toLowerCase();
});
var nodes = this.ownerDocument.importNode(
new DOMDocument( new java.io.ByteArrayInputStream(
(new java.lang.String("<wrap>" + html + "</wrap>"))
.getBytes("UTF8"))).documentElement, true).childNodes;
while (this.firstChild)
this.removeChild( this.firstChild );
for ( var i = 0; i < nodes.length; i++ )
this.appendChild( nodes[i] );
},
get textContent(){
return nav(this.childNodes);
function nav(nodes){
var str = "";
for ( var i = 0; i < nodes.length; i++ )
if ( nodes[i].nodeType == 3 )
str += nodes[i].nodeValue;
else if ( nodes[i].nodeType == 1 )
str += nav(nodes[i].childNodes);
return str;
}
},
set textContent(text){
while (this.firstChild)
this.removeChild( this.firstChild );
this.appendChild( this.ownerDocument.createTextNode(text));
},
style: {},
clientHeight: 0,
clientWidth: 0,
offsetHeight: 0,
offsetWidth: 0,
get disabled() {
var val = this.getAttribute("disabled");
return val != "false" && !!val;
},
set disabled(val) { return this.setAttribute("disabled",val); },
get checked() {
var val = this.getAttribute("checked");
return val != "false" && !!val;
},
set checked(val) { return this.setAttribute("checked",val); },
get selected() {
if ( !this._selectDone ) {
this._selectDone = true;
if ( this.nodeName == "OPTION" && !this.parentNode.getAttribute("multiple") ) {
var opt = this.parentNode.getElementsByTagName("option");
if ( this == opt[0] ) {
var select = true;
for ( var i = 1; i < opt.length; i++ )
if ( opt[i].selected ) {
select = false;
break;
}
if ( select )
this.selected = true;
}
}
}
var val = this.getAttribute("selected");
return val != "false" && !!val;
},
set selected(val) { return this.setAttribute("selected",val); },
get className() { return this.getAttribute("class") || ""; },
set className(val) {
return this.setAttribute("class",
val.replace(/(^\s*|\s*$)/g,""));
},
get type() { return this.getAttribute("type") || ""; },
set type(val) { return this.setAttribute("type",val); },
get value() { return this.getAttribute("value") || ""; },
set value(val) { return this.setAttribute("value",val); },
get src() { return this.getAttribute("src") || ""; },
set src(val) { return this.setAttribute("src",val); },
get id() { return this.getAttribute("id") || ""; },
set id(val) { return this.setAttribute("id",val); },
getAttribute: function(name){
return this._dom.hasAttribute(name) ?
new String( this._dom.getAttribute(name) ) :
null;
},
setAttribute: function(name,value){
this._dom.setAttribute(name,value);
},
removeAttribute: function(name){
this._dom.removeAttribute(name);
},
get childNodes(){
return new DOMNodeList( this._dom.getChildNodes() );
},
get firstChild(){
return makeNode( this._dom.getFirstChild() );
},
get lastChild(){
return makeNode( this._dom.getLastChild() );
},
appendChild: function(node){
this._dom.appendChild( node._dom );
},
insertBefore: function(node,before){
this._dom.insertBefore( node._dom, before ? before._dom : before );
},
removeChild: function(node){
this._dom.removeChild( node._dom );
},
getElementsByTagName: DOMDocument.prototype.getElementsByTagName,
addEventListener: window.addEventListener,
removeEventListener: window.removeEventListener,
dispatchEvent: window.dispatchEvent,
click: function(){
var event = document.createEvent();
event.initEvent("click");
this.dispatchEvent(event);
},
submit: function(){
var event = document.createEvent();
event.initEvent("submit");
this.dispatchEvent(event);
},
focus: function(){
var event = document.createEvent();
event.initEvent("focus");
this.dispatchEvent(event);
},
blur: function(){
var event = document.createEvent();
event.initEvent("blur");
this.dispatchEvent(event);
},
get elements(){
return this.getElementsByTagName("*");
},
get contentWindow(){
return this.nodeName == "IFRAME" ? {
document: this.contentDocument
} : null;
},
get contentDocument(){
if ( this.nodeName == "IFRAME" ) {
if ( !this._doc )
this._doc = new DOMDocument(
new java.io.ByteArrayInputStream((new java.lang.String(
"<html><head><title></title></head><body></body></html>"))
.getBytes("UTF8")));
return this._doc;
} else
return null;
}
});
// Helper method for extending one object with another
function extend(a,b) {
for ( var i in b ) {
var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
if ( g || s ) {
if ( g )
a.__defineGetter__(i, g);
if ( s )
a.__defineSetter__(i, s);
} else
a[i] = b[i];
}
return a;
}
// Helper method for generating the right
// DOM objects based upon the type
var obj_nodes = new java.util.HashMap();
function makeNode(node){
if ( node ) {
if ( !obj_nodes.containsKey( node ) )
obj_nodes.put( node, node.getNodeType() ==
Packages.org.w3c.dom.Node.ELEMENT_NODE ?
new DOMElement( node ) : new DOMNode( node ) );
return obj_nodes.get(node);
} else
return null;
}
// XMLHttpRequest
// Originally implemented by Yehuda Katz
window.XMLHttpRequest = function(){
this.headers = {};
this.responseHeaders = {};
};
XMLHttpRequest.prototype = {
open: function(method, url, async, user, password){
this.readyState = 1;
if (async)
this.async = true;
this.method = method || "GET";
this.url = url;
this.onreadystatechange();
},
setRequestHeader: function(header, value){
this.headers[header] = value;
},
getResponseHeader: function(header){ },
send: function(data){
var self = this;
function makeRequest(){
var url = new java.net.URL(curLocation, self.url);
if ( url.getProtocol() == "file" ) {
if ( self.method == "PUT" ) {
var out = new java.io.FileWriter(
new java.io.File( new java.net.URI( url.toString() ) ) ),
text = new java.lang.String( data || "" );
out.write( text, 0, text.length() );
out.flush();
out.close();
} else if ( self.method == "DELETE" ) {
var file = new java.io.File( new java.net.URI( url.toString() ) );
file["delete"]();
} else {
var connection = url.openConnection();
connection.connect();
handleResponse();
}
} else {
var connection = url.openConnection();
connection.setRequestMethod( self.method );
// Add headers to Java connection
for (var header in self.headers)
connection.addRequestProperty(header, self.headers[header]);
connection.connect();
// Stick the response headers into responseHeaders
for (var i = 0; ; i++) {
var headerName = connection.getHeaderFieldKey(i);
var headerValue = connection.getHeaderField(i);
if (!headerName && !headerValue) break;
if (headerName)
self.responseHeaders[headerName] = headerValue;
}
handleResponse();
}
function handleResponse(){
self.readyState = 4;
self.status = parseInt(connection.responseCode) || undefined;
self.statusText = connection.responseMessage || "";
var stream = new java.io.InputStreamReader(connection.getInputStream()),
buffer = new java.io.BufferedReader(stream), line;
while ((line = buffer.readLine()) != null)
self.responseText += line;
self.responseXML = null;
if ( self.responseText.match(/^\s*</) ) {
try {
self.responseXML = new DOMDocument(
new java.io.ByteArrayInputStream(
(new java.lang.String(
self.responseText)).getBytes("UTF8")));
} catch(e) {}
}
}
self.onreadystatechange();
}
if (this.async)
(new java.lang.Thread(new java.lang.Runnable({
run: makeRequest
}))).start();
else
makeRequest();
},
abort: function(){},
onreadystatechange: function(){},
getResponseHeader: function(header){
if (this.readyState < 3)
throw new Error("INVALID_STATE_ERR");
else {
var returnedHeaders = [];
for (var rHeader in this.responseHeaders) {
if (rHeader.match(new Regexp(header, "i")))
returnedHeaders.push(this.responseHeaders[rHeader]);
}
if (returnedHeaders.length)
return returnedHeaders.join(", ");
}
return null;
},
getAllResponseHeaders: function(header){
if (this.readyState < 3)
throw new Error("INVALID_STATE_ERR");
else {
var returnedHeaders = [];
for (var header in this.responseHeaders)
returnedHeaders.push( header + ": " + this.responseHeaders[header] );
return returnedHeaders.join("\r\n");
}
},
async: true,
readyState: 0,
responseText: "",
status: 0
};
})();

View File

@ -0,0 +1 @@
<p>test</p>

View File

@ -0,0 +1 @@
{ users : { tj : { email : 'tj@vision-media.ca' }}}

View File

@ -0,0 +1,5 @@
<users>
<user name="tj">
<email>tj@vision-media.ca</email>
</user>
</users>

View File

@ -0,0 +1,42 @@
ExampleModule = {
name : 'ExampleModule',
utilities : {
doFoo : function(){ return 'foo' },
doBar : function(){ return 'bar' }
},
randomHook : function(a, b) {
return [a, b]
},
beforeSpec : function() { addedBeforeSpec = true; doFoo() },
afterSpec : function() { addedAfterSpec = true },
beforeSuite : function() { addedBeforeSuite = true },
afterSuite : function() { addedAfterSuite = true },
checkJSpecContext : function(){ return each },
checkContext : function() { return fixture('test') },
checkModuleContext : function() { return this.name },
checkUtilityContext : function() { return doFoo() },
matchers : {
be_foo_bar : function() {
return true
}
},
DSLs : {
snake : {
some_snake_case_stuff : function(){
return true
}
},
camel : {
someCamelCaseStuff : function() {
return true
}
}
}
}
JSpec.include(ExampleModule)
JSpec.include({
name : 'EmptyModule'
})

View File

@ -0,0 +1,33 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="../lib/jspec.css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script src="../lib/jspec.js"></script>
<script src="../lib/jspec.jquery.js"></script>
<script src="../lib/jspec.xhr.js"></script>
<script src="modules.js"></script>
<script src="spec.grammar-less.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.grammar.js')
.exec('spec.js')
.exec('spec.matchers.js')
.exec('spec.utils.js')
.exec('spec.fixtures.js')
.exec('spec.shared-behaviors.js')
.exec('spec.jquery.js')
.exec('spec.modules.js')
.exec('spec.xhr.js')
.exec('spec.jquery.xhr.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"><div class="loading"></div></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -0,0 +1,18 @@
describe 'Utility'
describe 'fixture()'
it 'should return a files contents'
fixture('fixtures/test.html').should.eql '<p>test</p>'
fixture('test.html').should.eql '<p>test</p>'
fixture('test').should.eql '<p>test</p>'
end
it 'should cache contents'
contents = fixture('test')
JSpec.cache['test'].should.eql contents
JSpec.cache['test'] = 'foo'
fixture('test').should.eql 'foo'
delete JSpec.cache['test']
end
end
end

View File

@ -0,0 +1,34 @@
JSpec.describe('Grammar-less', function(){
before(function(){
n = 1
})
it('should work', function(){
expect(true).to(be, true)
expect(n).to(equal, 1)
})
describe('with nested describes', function(){
before(function(){
hits = []
})
before_each(function(){
n++
hits.push('before_each')
})
it('should work', function(){
expect(true).not_to(be, false)
expect(n).to(eql, 2)
expect(hits).to(eql, ['before_each'])
})
it('should work again', function(){
expect(n).to(eql, 3)
expect(hits).to(eql, ['before_each', 'before_each'])
})
})
})

View File

@ -0,0 +1,218 @@
describe 'Grammar'
it 'should allow "it" spec literal'
true.should.be_true
end
n = 10
it 'should allow literal javascript outside of blocks'
n.should.eql 10
end
it 'should escape <html> in <p>descriptions</p> and body'
'<p></p>'.should.eql '<p></p>'
end
it 'should parse correctly when "it" is within the body'
text = 'Get it at Github'
text.should.include 'it'
end
it 'should parse correctly when "describe" is within the body'
text = 'It should work with describe'
text.should.include 'describe'
end
it 'should parse correctly when "end" is within the body'
text = 'This should not end the parsing :)'
text.should.include 'not'
end
it 'should parse correctly with "before" and "after" within the body'
text = 'This comes before that, which is after the rest'
text.should.include 'before'
end
it 'should allow parens to be optional when no args are passed'
true.should.be_true
true.should.be_true()
end
it 'should not mess up with words like it or append in descriptions'
-{ element.append().end() }.should.throw_error
end
it 'should not mess up "end" in strings'
'foo end bar'.should.not.eql 'foo }); bar'
end
it 'should allow semicolons'
true.should.be_true;
true.should.be_true();
true.should.be_true() ;
end
it 'should allow parens to be optional with args'
'foobar'.should.include 'foo'
'rawr'.should.not_include 'foo'
end
it 'should allow literals without defining variables variables'
{}.should.be_an Object
end
it 'should allow alternative closure literal'
-{ throw 'test' }.should.throw_error
end
it 'should allow grammar-less assertions'
expect(true).to(be, true)
expect([1,2,3]).to(include, 1, 2, 3)
expect(true).not_to(be, false)
end
it 'should allow multi-line expect() assertions'
expect(' \
foo \
bar \
').to(include, 'foo', 'bar')
end
it 'should allow commenting out of conversions'
// -{ throw 'foo' }.should.throw_error
// foo.should.not.eql 'bar'
end
it 'should allow inclusive range literal n..n'
1..5.should.eql [1,2,3,4,5]
3..4.should.eql [3,4]
1..1.should.eql [1]
3..1.should.eql [3,2,1]
end
it 'should allow snakecase style assertions'
'foo'.should_equal('foo')
'foo'.should_equal 'foo'
'bar'.should_not_equal('foo')
'bar'.should_not_equal 'foo'
end
it 'should allow dot style assertions'
'foo'.should.equal('foo')
'foo'.should.equal 'foo'
'bar'.should.not.equal('foo')
'bar'.should.not.equal 'foo'
end
describe 'with nested describe'
it 'should work'
true.should.be_true
end
describe 'nested again'
it 'should still work'
true.should.be_true
end
end
end
describe 'before / after blocks'
before
n = 1
hits = []
hits.push('before')
end
after
n = 0
hits.push('after')
end
it 'should work'
n.should.eql 1
hits.should.eql ['before']
n++
end
it 'should persist'
n.should.eql 2
hits.should.eql ['before']
end
describe 'with nested describe'
it 'should be accessable'
n.should.eql 1
hits.should.eql ['before']
end
end
end
describe 'before_each / after_each blocks'
hits = []
before_each
n = 1
hits.push('before_each')
end
after_each
o = 2
hits.push('after_each')
end
it 'should work'
n.should.eql 1
hits.should.eql ['before_each']
n = 2
end
it 'should not persist'
n.should.eql 1
o.should.eql 2
hits.should.eql ['before_each', 'after_each', 'before_each']
end
describe 'with nested describe'
it 'should be accessable'
n.should.eql 1
o.should.eql 2
hits.should.eql ['before_each', 'after_each', 'before_each', 'after_each', 'before_each']
end
it 'should continue hits'
hits.should.eql ['before_each', 'after_each', 'before_each', 'after_each', 'before_each', 'after_each', 'before_each']
end
describe 'with more hooks'
before_each
hits.push('before_each')
end
after_each
hits.push('after_each')
end
it 'should continue hits, while cascading properly'
hits.should.eql ['before_each', 'after_each', 'before_each', 'after_each', 'before_each', 'after_each', 'before_each', 'after_each', 'before_each', 'before_each']
end
end
describe 'with multiple hooks'
before_each
hits = []
end
before_each
hits.push('before_each')
end
it 'should work'
hits.should.eql ['before_each']
end
end
end
end
end

View File

@ -0,0 +1,173 @@
describe 'jQuery'
describe 'sandbox()'
before
dom = sandbox()
end
it 'should provide an empty DOM sandbox'
dom.prepend('<em>test</em>')
dom.should.have_text 'test'
end
end
describe 'element() / elements()'
it 'should alias jQuery'
element.should.be jQuery
elements.should.be jQuery
end
end
describe 'async'
it 'should load mah cookies (textfile)'
$.post('async', function(text){
text.should_eql 'cookies!'
})
end
it 'should load mah cookies twice (ensure multiple async requests work)'
$.post('async', function(text){
text.should.eql 'cookies!'
})
$.post('async', function(text){
text.should.not.eql 'rawr'
})
end
end
describe 'matchers'
before_each
html = '<p><label><em>Save?</em></label> \
<select class="save form-select" style="display: none;"> \
<option value="0">No</option> \
<option value="1">Yes</option> \
</select> \
<strong>test</strong> \
<strong>test</strong> \
</p>'
elem = $(html)
end
it 'should fail with pretty print of element'
elem.should.not.have_tag 'label'
end
describe 'have_tag / have_one'
it 'should check if a single child is present'
elem.should.have_tag 'label'
elem.should.have_tag 'em'
elem.should.have_one 'label'
elem.should.not.have_tag 'input'
end
end
describe 'have_tags / have_many'
it 'should check if more than one child is present'
elem.should.have_tags 'option'
elem.should.have_many 'option'
elem.should.not.have_many 'label'
end
end
describe 'have_child'
it 'should check if a direct child is present'
elem.should.have_child 'label'
elem.should.not.have_child 'em'
end
end
describe 'have_children'
it 'should check if more than one direct children are present'
elem.should.have_children 'strong'
elem.should.not.have_children 'select'
end
end
describe 'have_text'
it 'should check for plain text'
elem.children('label').should.have_text 'Save?'
end
end
describe 'have_value'
it 'should check if an element has the given value'
elem.find('option').get(1).should.have_value '1'
end
end
describe 'have_class'
it 'should check if an element has the given class'
elem.children('select').should.have_class 'save'
end
end
describe 'have_classes'
it 'should check if an element has the classes given'
elem.children('select').should.have_classes 'save', 'form-select'
elem.children('select').should.not.have_classes 'save', 'foo'
elem.children('select').should.not.have_classes 'foo', 'save'
end
end
describe 'be_visible'
it 'should check that an element is not hidden or set to display of none'
element('#jspec-report').should.be_visible
'#jspec-report'.should.be_visible
'<input style="visibility: hidden;"/>'.should.not.be_visible
'<input style="display: none;"/>'.should.not.be_visible
'<input />'.should.be_visible
end
end
describe 'be_enabled'
it 'should check that an element is currently enabled'
'<input type="button"/>'.should.be_enabled
'<input type="button" disabled="disabled" />'.should.not.be_enabled
end
end
describe 'be_BOOLATTR'
it 'should check that an element is currently selected, disabled, checked etc'
'<input type="button"/>'.should.not.be_disabled
'<input type="button" disabled="disabled" />'.should.be_disabled
'<option value="foo" selected="selected">Foo</option>'.should.be_selected
end
end
describe 'have_ATTR'
it 'should check if an attribute exists'
'<input type="checkbox"/>'.should.have_type
end
it 'should check if an attribute has a specific value'
'<input type="checkbox"/>'.should.have_type 'checkbox'
end
end
describe 'be_hidden'
it 'should check if an element is hidden'
'<input style="display: none;" />'.should.be_hidden
'<input style="visibility: hidden;" />'.should.be_hidden
'<input />'.should.not.be_hidden
end
end
describe 'have_attr'
before_each
elem = '<input type="button" title="some foo" value="Foo" />'
end
it 'should check that an element has the given attribute'
elem.should.have_attr 'title'
elem.should.not_have_attr 'rawr'
end
it 'should check that the given attribute has a specific value'
elem.should.have_attr 'title', 'some foo'
elem.should.not.have_attr 'some', 'rawr'
elem.should.not.have_attr 'title', 'bar'
end
end
end
end

View File

@ -0,0 +1,35 @@
describe 'jQuery'
describe '.getJSON()'
it 'should work with mockRequest'
mockRequest().and_return('{ foo : "bar" }')
$.getJSON('foo', function(response, statusText){
response.foo.should.eql 'bar'
statusText.should.eql 'success'
})
end
it 'should work with a json fixture'
mockRequest().and_return(fixture('test.json'))
$.getJSON('foo', function(response){
response.users.tj.email.should.eql 'tj@vision-media.ca'
})
end
it 'should not invoke callback when response status is 4xx'
mockRequest().and_return('foo', 'text/plain', 404)
$.getJSON('foo', function(){
fail('callback was invoked')
})
end
end
describe '.post()'
it 'should work with mockRequest'
mockRequest().and_return('<p></p>', 'text/html')
$.post('foo', function(response){
response.should.eql '<p></p>'
})
end
end
end

View File

@ -0,0 +1,127 @@
describe 'Negative specs'
it 'should fail'
'test'.should.not_eql 'test'
end
it 'should fail with one faulty assertion'
'test'.should.equal 'test'
'test'.should.equal 'foo'
end
it 'should fail and print array with square braces'
[1,2].should.equal [1,3]
end
it 'should fail and print nested array'
[1, ['foo']].should.equal [1, ['bar', ['whatever', 1.0, { foo : 'bar', bar : { 1 : 2 } }]]]
end
it 'should fail and print html elements'
elem = document.createElement('a')
elem.setAttribute('href', 'http://vision-media.ca')
elem.should.not.eql elem
end
it 'should fail with selector for jQuery objects'
elem = { jquery : '1.3.1', selector : '.foobar' }
elem.should.eql 'foo'
end
it 'should fail with negative message'
'1'.should.not.be_true
end
it 'should fail with positive message'
false.should.be_true
end
it 'should fail saying an error was throw'
-{ throw 'foo' }.should.not.throw_error
end
it 'should fail saying which error has been thrown'
-{ throw 'foo' }.should.throw_error 'bar'
end
it 'should fail saying no error was thrown'
-{ }.should.throw_error 'foo'
end
it 'should fail saying no error matching was thrown'
-{ throw 'bar' }.should.throw_error(/foo/)
end
it 'should fail saying no error matching foo should be thrown'
-{ throw 'foo' }.should.not.throw_error(/foo/)
end
it 'should fail saying constructors'
-{ throw new TypeError('oh no') }.should.throw_error(Error)
end
it 'should fail saying multiple arg messages'
-{ throw new TypeError('oh no') }.should.throw_error(TypeError, /foo/)
end
it 'should fail with constructor name'
function Foo(){}
function Bar(){}
Bar.prototype.toString = function(){ return 'Bar error: oh no' }
-{ throw new Bar }.should.throw_error Foo
end
it 'should fail with function body string'
-{ 'foo' }.should.not.include 'foo'
end
it 'should fail with constructor name'
function Foo(){ this.toString = function(){ return '<Foo>' }}
foo = new Foo
foo.should.not.be_an_instance_of Foo
end
it 'should fail with message of first failure'
true.should.be_true
'bar'.should.match(/foo/gm)
'bar'.should.include 'foo'
end
it 'should fail with list'
['foo', 'bar'].should.include 'foo', 'car'
end
it 'should catch exceptions throw within specs'
throw new Error('Oh noes!')
end
it 'should catch improper exceptions'
throw 'oh noes'
end
it 'should catch proper exceptions'
iDoNotExist.neitherDoI()
end
end
describe 'Contexts'
before
JSpec.context = { iLike : 'cookies' }
end
after
JSpec.context = null
end
it 'should be replaceable'
iLike.should.equal 'cookies'
end
end
describe 'Misc'
it 'requires implementation'
end
end

View File

@ -0,0 +1,492 @@
describe 'Matchers'
describe 'eql'
it 'should work with strings'
'test'.should.eql 'test'
'test'.should.not.eql 'foo'
end
it 'should work with numbers'
11.should.eql 11
10.should.not.eql 11
end
it 'should loosely compare numbers as strings'
'11'.should.eql 11
'10'.should.not.eql 11
end
it 'should hash compare arrays'
[1, 2].should.eql [1, 2]
[1, 2].should.not.eql [1, 3]
[1, 2, [3], { foo : 'bar' }].should.eql [1, 2, [3], { foo : 'bar' }]
end
it 'should hash compare objects'
{ foo : 'bar' }.should.eql { foo : 'bar' }
end
it 'should hash compare objects with different orders'
a = { one : 'two', three : 'four' }
b = { three : 'four', one : 'two' }
a.should.eql b
end
it 'should hash compare arbitrary objects'
Foo = function(){}, Bar = function(){}
Bar.prototype = { doSomething : function(){ }}
foo = new Foo, foo2 = new Foo, bar = new Bar
foo.should.eql foo2
foo.should.not.eql bar
end
it 'should work with constructors'
Array.should.eql Array
Array.should.not.eql Object
end
end
describe 'equal'
it 'should perform strict comparisons'
'test'.should.equal 'test'
'1'.should.not.equal 1
true.should.be true
'1'.should.not.be true
end
end
describe 'match'
it 'should match regular expressions'
'foobar'.should.match(/foo/)
'foobar'.should.not.match(/barfoo/)
end
end
describe 'be_empty'
it 'should consider any object with zero length to be empty'
''.should.be_empty
' '.should.not.be_empty
[].should.be_empty
{ length : 0 }.should.be_empty
{}.should.be_empty
'cookies'.should.not.be_empty
[0].should.not.be_empty
{ length : 1 }.should.not.be_empty
{ foo : 'bar' }.should.not.be_empty
end
end
describe 'be_null'
it 'should check if a value is null'
a = 0
b = null
null.should.be_null
0.should.not.be_null
a.should.not.be_null
b.should.be_null
end
end
describe 'be_undefined'
it 'should check if a var is defined'
var foo
foo.should.be_undefined
end
end
describe 'have_length'
it 'should compare the length of an object'
'foo'.should.have_length 3
[1, 2].should.have_length 2
end
end
describe 'have_length_within'
it 'should check if an object has a length within the specified range'
'foo'.should.have_length_within 2..4
'f'.should.not.have_length_within 2..4
end
end
describe 'have_prop'
it 'should check if a property exists'
'foo'.should.have_prop 'length'
end
it 'should check that a property has a specific value'
'foo'.should.have_prop 'length', 3
{ length : '3' }.should.have_prop 'length', 3
end
it 'should check object hashes'
{ foo : 1..3 }.should.have_prop 'foo', 1..3
end
it 'should fail when the property does not exist'
'foo'.should.not.have_prop 'foo'
'foo'.should.not.have_prop 'foo', 'bar'
end
it 'should fail when it is a function'
'foo'.should.not.have_prop 'toString'
end
end
describe 'have_property'
it 'should check if a property exists'
'foo'.should.have_property 'length'
end
it 'should check that a property has a specific value'
'foo'.should.have_property 'length', 3
{ length : '3' }.should.not.have_property 'length', 3
end
it 'should fail when the property does not exist'
'foo'.should.not.have_property 'foo'
'foo'.should.not.have_property 'foo', 'bar'
end
it 'should fail when it is a function'
'foo'.should.not.have_property 'toString'
end
end
describe 'respond_to'
it 'should check if an object contains a method'
'test'.should.respond_to('toString')
'test'.should.not.respond_to('rawr')
end
end
describe 'include'
it 'should check if an object includes a property'
{ hey : 'there' }.should.include 'hey'
{ hey : 'there' }.should.not.include 'foo'
end
it 'should check if a regular expression includes a string'
(/(foo)?bar/).should.include '(foo)'
end
it 'should check if a function body includes a string'
-{ return [foo, bar] }.should.include 'foo', 'bar'
end
it 'should check if an array contains element(s)'
[1,2,3].should.include 1
[1,2,3].should.include 1, 2, 3
[1].should.not.include 0
['foo', 'bar'].should.include 'foo', 'bar'
['foo', 'bar'].should.include 'bar', 'foo'
['foo', 'bar'].should.not.include 'foo', 'rawr'
['foo', 'bar'].should.not.include 'rawr', 'foo'
end
it 'should check hashes of array elements'
[1, [2]].should.include [2]
[1, [2]].should.include [2], 1
[1, { two : 'three' }].should.include { two : 'three' }
end
end
describe 'be_a'
it 'should compare the constructor of an object'
'test'.should.be_a String
[].should.be_an Array
end
end
describe 'throw_error'
it 'should check if an error is thrown'
-{ throw 'error' }.should.throw_error
-{ return 'test' }.should.not.throw_error
end
it 'should check if an error with a specific message is thrown'
-{ throw 'some foo bar' }.should.throw_error('some foo bar')
-{ throw 'some foo bar' }.should.throw_error(/foo bar/)
-{ throw 'some foo bar' }.should.not.throw_error(/rawr/)
-{ throw 'some foo bar' }.should.not.throw_error('rawr')
end
it 'should check if an error of a specific constructor is thrown'
-{ throw new Error('foo') }.should.throw_error(Error)
-{ throw new TypeError('foo') }.should.throw_error(TypeError)
-{ throw 'foo' }.should.throw_error Error
-{ throw 'foo' }.should.not.throw_error TypeError
end
it 'should check if an error with a specific constructor and message is thrown'
-{ throw new TypeError('oh no!') }.should.throw_error(TypeError, 'oh no!')
-{ throw new TypeError('oh no!') }.should.not.throw_error(TypeError, 'foo bar')
-{ throw new TypeError('oh no!') }.should.throw_error(TypeError, /oh no/)
-{ throw new TypeError('oh no!') }.should.not.throw_error(TypeError, /foo bar/)
-{ throw new TypeError('oh no!') }.should.not.throw_error(Error, 'oh no!')
-{ throw new TypeError('oh no!') }.should.not.throw_error(Error, 'foo bar')
end
end
describe 'be_an_instance_of'
it 'should check that an object is an instance of another'
MyObject = function(){}
myInstance = new MyObject()
{}.should.be_an_instance_of Object
[].should.be_an_instance_of Array
MyObject.should.be_an_instance_of Function
myInstance.should.be_an_instance_of MyObject
myInstance.should.be_an_instance_of Object
end
end
describe 'be_type'
it 'should compare the type of an object via typeof'
'hey'.should.be_type 'string'
{}.should.be_type 'object'
end
end
describe 'be_within'
it 'should check if a number is within a range'
5.should.be_within 1..10
15.should.not.be_within 10..5
end
end
describe 'have'
it 'should check the length of a property'
person = { pets : ['izzy', 'niko'] }
person.should.have 2, 'pets'
person.should.not.have 3, 'pets'
end
end
describe 'have_at_least'
it 'should check if a object has at least n of a property'
person = { pets : ['izzy', 'niko'] }
person.should.have_at_least 1, 'pets'
person.should.have_at_least 2, 'pets'
person.should.not.have_at_least 3, 'pets'
end
end
describe 'have_at_most'
it 'should check if an object has at most n of a property'
person = { pets : ['izzy', 'niko'] }
person.should.have_at_most 2, 'pets'
person.should.have_at_most 3, 'pets'
person.should.not.have_at_most 1, 'pets'
end
end
describe 'be_within'
it 'should check that an object has within n..n of a property'
person = { pets : ['izzy', 'niko'] }
person.should.have_within 1..2, 'pets'
person.should.have_within 2..5, 'pets'
person.should.not.have_within 5..10, 'pets'
end
end
describe 'receive'
before_each
person = { toString : function(){ return 'person' }}
personWithPets = {
toString : function(){ return 'personWithPets' },
getPets : function() { return ['izzy'] },
addPet : function(name) { return ['izzy', name] },
addPets : function(a, b) { return ['izzy', a, b] }
}
end
it 'should pass when the method is invoked'
personWithPets.should.receive('getPets')
personWithPets.getPets()
end
it 'should pass and original method should still return its result'
personWithPets.should.receive('getPets')
personWithPets.getPets().should.eql ['izzy']
end
it 'should pass when the proper value is returned'
personWithPets.should.receive('getPets').and_return(['izzy'])
personWithPets.getPets()
end
it 'should pass when invoked the expected number of times'
personWithPets.should.receive('getPets', 'twice').and_return(['izzy'])
personWithPets.getPets()
personWithPets.getPets()
end
it 'should pass when a method is invoked with specific arguments'
personWithPets.should.receive('addPet', 'once').with_args('suki')
personWithPets.addPet('suki')
end
it 'should pass with multiple arguments'
personWithPets.should.receive('addPets').with_args('suki', 'max')
personWithPets.addPets('suki', 'max')
end
it 'should pass with arguments and return value'
personWithPets.should.receive('addPet').with_args('suki').and_return(['izzy', 'suki'])
personWithPets.addPet('suki')
end
it 'should pass when argument is the correct type'
personWithPets.should.receive('addPet').with_args(an_instance_of(String))
personWithPets.addPet('suki')
end
it 'should pass when return type is correct'
personWithPets.should.receive('addPet').and_return(an_instance_of(Array))
personWithPets.addPet('suki')
end
it 'should pass when checking the type of multiple args and return types'
personWithPets.should.receive('addPets').with_args(an_instance_of(String), an_instance_of(String)).and_return(an_instance_of(Array))
personWithPets.addPets('suki', 'max')
end
it 'should pass with negation when a method is not called'
personWithPets.should.not.receive('addPets')
end
it 'should pass with negation with args'
personWithPets.should.not.receive('addPets').with_args('izzy')
personWithPets.addPets('max')
end
it 'should pass with negation with return values'
personWithPets.should.not.receive('addPets').with_args('izzy').and_return('test')
personWithPets.addPets('izzy')
end
it 'should pass with negation with times'
personWithPets.should.not.receive('addPets', 'twice')
personWithPets.addPets('izzy')
end
it 'should pass with boolean args'
foo = { bar : function(arg){ return arg }}
foo.should.receive('bar', 'twice').with_args(true)
foo.bar(true)
foo.bar(true)
end
it 'should pass with null args'
foo = { bar : function(arg){ return arg }}
foo.should.receive('bar').with_args(null)
foo.bar(null)
end
it 'should pass with boolean return value true'
foo = { bar : function(){ return true }}
foo.should.receive('bar').and_return(true)
foo.bar()
end
it 'should pass with boolean return value false'
foo = { bar : function(){ return false }}
foo.should.receive('bar').and_return(false)
foo.bar()
end
it 'should pass with null return value'
foo = { bar : function(){ return null }}
foo.should.receive('bar').and_return(null)
foo.bar()
end
it 'should fail when the method does not exist'
person.should.receive('getPets')
end
it 'should fail when the method is never invoked'
personWithPets.should.receive('getPets')
end
it 'should fail when improper value is returned'
personWithPets.should.receive('getPets').and_return(['niko'])
personWithPets.getPets()
end
it 'should fail when checking the type of multiple args and return types'
personWithPets.should.receive('addPets').with_args(an_instance_of(String), an_instance_of(Array)).and_return(an_instance_of(Array))
personWithPets.addPets('suki', 'max')
end
it 'should fail when not invoked the expected number of times'
personWithPets.should.receive('getPets', 'twice').and_return(['izzy'])
personWithPets.getPets()
end
it 'should fail when not invoked many times'
personWithPets.should.receive('getPets', 3).and_return(['izzy'])
personWithPets.getPets()
personWithPets.getPets()
end
it 'should fail when not invoked with specific arguments'
personWithPets.should.receive('addPet', 'once').with_args('suki')
personWithPets.addPet('niko')
end
it 'should fail when expecting multiple arguments'
personWithPets.should.receive('addPets').with_args('suki', 'max')
personWithPets.addPets('suki')
end
it 'should fail when argument is of the wrong type'
personWithPets.should.receive('addPet').with_args(an_instance_of(String))
personWithPets.addPet(['suki'])
end
it 'should fail when return type is incorrect'
personWithPets.should.receive('addPet').and_return(an_instance_of(String))
personWithPets.addPet('suki')
end
it 'should fail with negation when a method is called'
personWithPets.should.not.receive('addPets')
personWithPets.addPets('izzy')
end
it 'should fail with negation with args'
personWithPets.should.not.receive('addPets').with_args('izzy')
personWithPets.addPets('izzy')
end
it 'should fail with negation with return values'
personWithPets.should.not.receive('addPets').with_args('izzy').and_return(an_instance_of(Array))
personWithPets.addPets('izzy')
end
it 'should fail with negation with times'
personWithPets.should.not.receive('addPets', 'twice')
personWithPets.addPets('izzy')
personWithPets.addPets('max')
end
it 'should fail with boolean args'
foo = { bar : function(arg){ return arg }}
foo.should.receive('bar').with_args(true)
foo.bar(false)
end
it 'should fail with boolean return value true'
foo = { bar : function(){ return true }}
foo.should.receive('bar').and_return(false)
foo.bar()
end
it 'should fail with boolean return value false'
foo = { bar : function(){ return false }}
foo.should.receive('bar').and_return(true)
foo.bar()
end
end
end

View File

@ -0,0 +1,67 @@
describe 'JSpec'
describe 'module'
describe 'hooks'
it 'should run beforeSpec'
addedBeforeSpec.should.be_true
end
it 'should run afterSpec'
addedAfterSpec.should.be_true
end
describe 'with suites'
it 'should run beforeSuite'
addedBeforeSuite.should.be_true
end
end
it 'should run afterSuite'
addedAfterSuite.should.be_true
end
it 'should run in context with JSpec'
hook('checkJSpecContext')[0].should.equal JSpec.each
end
it 'should run in context to JSpecs default context'
hook('checkContext')[0].should.eql fixture('test')
end
it 'should run in context to the module itself'
hook('checkModuleContext')[0].should.eql 'ExampleModule'
end
it 'should run in context to the modules utilities'
hook('checkUtilityContext')[0].should.eql 'foo'
end
end
describe '.hook()'
it 'should invoke hooks, returning an array of results'
results = hook('randomHook', 'foo', 'bar')
results.should.eql [['foo', 'bar']]
end
end
describe '.utilities'
it 'should be merged with the default utilities'
doFoo().should.eql 'foo'
doBar().should.eql 'bar'
end
end
describe '.matchers'
it 'should be merged with default matchers'
'test'.should.be_foo_bar
end
end
describe '.DSLs'
it 'should be merged with default DSLs'
JSpec.DSLs.snake.some_snake_case_stuff().should.be_true
JSpec.DSLs.camel.someCamelCaseStuff().should.be_true
end
end
end
end

View File

@ -0,0 +1,46 @@
__loading__ = []
__loadDelay__ = 1000
originalPrint = print
print = puts
readFile = function(path, callback) {
__loading__.push(path)
var promise = node.fs.cat(path, "utf8")
promise.addErrback(function(){ throw "failed to read file `" + path + "'" })
promise.addCallback(function(contents){
setTimeout(function(){
if (__loading__[0] == path)
__loading__.shift(), callback(contents)
else
setTimeout(arguments.callee, 50)
}, 50)
})
}
load = function(path) {
readFile(path, function(contents){
eval(contents)
})
}
load('lib/jspec.js')
load('spec/modules.js')
load('spec/spec.grammar-less.js')
setTimeout(function(){
JSpec
.exec('spec/spec.grammar.js')
.exec('spec/spec.js')
.exec('spec/spec.matchers.js')
.exec('spec/spec.utils.js')
.exec('spec/spec.shared-behaviors.js')
setTimeout(function(){
JSpec.run({ formatter : JSpec.formatters.Terminal, failuresOnly : false })
setTimeout(function() {
JSpec.report()
}, __loadDelay__ / 3)
}, __loadDelay__ / 3)
}, __loadDelay__ / 3)

View File

@ -0,0 +1,17 @@
load('lib/jspec.js')
load('lib/jspec.xhr.js')
load('spec/modules.js')
load('spec/spec.grammar-less.js')
JSpec
.exec('spec/spec.grammar.js')
.exec('spec/spec.js')
.exec('spec/spec.matchers.js')
.exec('spec/spec.utils.js')
.exec('spec/spec.fixtures.js')
.exec('spec/spec.shared-behaviors.js')
.exec('spec/spec.modules.js')
.exec('spec/spec.xhr.js')
.run({ formatter : JSpec.formatters.Terminal, failuresOnly : false })
.report()

View File

@ -0,0 +1,29 @@
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>
<script src="jspec.js"></script>
<script src="jspec.jquery.js"></script>
<script src="jspec.xhr.js"></script>
<script src="modules.js"></script>
<script src="spec.grammar-less.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.grammar.js')
.exec('spec.js')
.exec('spec.matchers.js')
.exec('spec.utils.js')
.exec('spec.fixtures.js')
.exec('spec.shared-behaviors.js')
.exec('spec.jquery.js')
.exec('spec.modules.js')
.exec('spec.xhr.js')
.exec('spec.jquery.xhr.js')
.run()
.reportToServer()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
</body>
</html>

View File

@ -0,0 +1,80 @@
describe 'Shared Behaviors'
describe 'User'
before
User = function(name) { this.name = name }
user = new User('joe')
end
it 'should have a name'
user.should.have_property 'name'
end
describe 'Administrator'
should_behave_like('User')
before
Admin = function(name) { this.name = name }
Admin.prototype.may = function(perm){ return true }
user = new Admin('tj')
end
it 'should have access to all permissions'
user.may('edit pages').should.be_true
user.may('delete users').should.be_true
end
describe 'Super Administrator'
should_behave_like('Administrator')
before
SuperAdmin = function(name) { this.name = name }
SuperAdmin.prototype.may = function(perm){ return true }
user = new SuperAdmin('tj')
end
end
end
end
describe 'User with toString()'
before
user = { toString : function() { return '<User tj>' }}
end
it 'should return &lt;User NAME&gt;'
user.toString().should.match(/\<User/)
end
end
describe 'Manager'
should_behave_like('User')
should_behave_like('User with toString()')
before
Manager = function(name) { this.name = name }
Manager.prototype.may = function(perm){ return perm == 'hire' || perm == 'fire' }
Manager.prototype.toString = function(){ return '<User ' + this.name + '>' }
user = new Manager('tj')
end
it 'should have access to hire or fire employees'
user.may('hire').should.be_true
user.may('fire').should.be_true
user.may('do anything else').should.be_false
end
end
describe 'findSuite'
it 'should find a suite by full description'
JSpec.findSuite('Shared Behaviors User Administrator').should.be_a JSpec.Suite
end
it 'should find a suite by name'
JSpec.findSuite('User').should.be_a JSpec.Suite
end
it 'should return null when not found'
JSpec.findSuite('Rawr').should.be_null
end
end
end

View File

@ -0,0 +1,276 @@
describe 'Utility'
describe 'fail()'
it 'should fail the current spec'
fail('I failed!')
end
end
describe 'pass()'
it 'should pass the current spec'
pass('yay')
pass('wahoo')
end
end
describe 'stubbing'
before_each
Object.prototype.stubby = function() { return 'Not stubbed' }
object = { toString : function() { return '<Im an object>' }}
stub(object, 'stubby').and_return('Im stubbed')
stub(object, 'toString').and_return('<No im not>')
end
after_each
delete Object.prototype.stubby
end
describe 'stub()'
it 'should stub :)'
object.stubby().should.eql 'Im stubbed'
object.toString().should.eql '<No im not>'
end
it 'should allow being called as a core prototype method'
foo = { bar : function(){ return 'baz' }}
foo.stub('bar').and_return('something else')
foo.bar().should.eql 'something else'
foo.destub()
foo.bar().should.eql 'baz'
end
it 'should stub methods starting with an underscore'
object._foo = function(){ return 'bar' }
object.stub('_foo').and_return('something else')
object._foo().should.eql 'something else'
object.destub()
object._foo().should.eql 'bar'
end
it 'should stub methods with whitespace'
object['foo bar'] = function(){ return 'rawr' }
object.stub('foo bar').and_return('baz')
object['foo bar']().should.eql 'baz'
object.destub()
object['foo bar']().should.eql 'rawr'
end
it 'should stub with arbitrary method when no return value is set'
object.stub(' super cool ')
object[' super cool '].should.be_a Function
destub(object)
object[' super cool '].should.be_null
end
it 'should stub sub properties using the JSpec grammar'
object = { foo : { bar : {}}}
object.foo.bar.stub('kitten').and_return('meow')
object.foo.bar.kitten().should.eql 'meow'
object.foo.bar.destub()
object.foo.bar.should.not.respond_to('kitten')
end
it 'should allow functions to be passed as a method'
stub(object, 'post').and_return(function(url, callback){
if (url == 'http://jspec.info')
callback('is awesome')
})
object.post('http://jspec.info', function(text){
text.should_eql 'is awesome'
})
end
end
describe 'destub()'
it 'should restore old methods'
destub(object, 'toString')
destub(object, 'stubby')
object.toString().should.eql '<Im an object>'
object.stubby().should.eql 'Not stubbed'
end
it 'should restore prototypal methods'
Object.prototype.stubby = function() { return 'Oh no im new' }
destub(object, 'stubby')
object.stubby().should.eql 'Oh no im new'
end
it 'should destub all methods stubbed related to the object passed when no method is given'
destub(object)
object.toString().should.eql '<Im an object>'
object.stubby().should.eql 'Not stubbed'
end
describe 'should restore after each spec'
before
a = { toString : function(){ return 'Wahoo' }}
b = { toString : function(){ return 'Wahhhhhooo' }}
end
it 'should stub'
stub(a, 'toString').and_return('Oh no')
stub(b, 'toString').and_return('Oh noooo')
a.toString().should.eql 'Oh no'
b.toString().should.eql 'Oh noooo'
end
it 'should restore'
a.toString().should.eql 'Wahoo'
b.toString().should.eql 'Wahhhhhooo'
end
end
end
end
describe 'query()'
it 'should return a pairs value'
query('suite', '?suite=Positive%20specs').should.equal 'Positive specs'
end
it 'should return null when key is not present'
query('foo', '?suite=Positive%20specs').should.be_null
end
end
describe 'strip()'
it 'should strip whitespace by default'
strip(" foo \n\n").should.equal 'foo'
end
it 'should strip the characters passed'
strip('[foo]', '\\[\\]').should.equal 'foo'
end
end
describe 'each()'
it 'should iterate an array'
result = []
each([1,2,3], function(value){
result.push(value)
})
result.should.eql [1,2,3]
end
it 'should iterate words in a string'
result = []
each('some foo bar', function(value){
result.push(value)
})
result.should.eql ['some', 'foo', 'bar']
end
end
describe 'map()'
it 'should return an array of mapped values'
result = map([1,2,3], function(value){
return value * 2
})
result.should.eql [2,4,6]
end
it 'should inherit the ability to iterate words in a string'
result = map('some foo bar', function(i, value){
return i + '-' + value
})
result.should.eql ['0-some', '1-foo', '2-bar']
end
end
describe 'inject()'
it 'should provide a memo object while iterating, not expecting returning of memo for composits'
result = inject([1,2,3], [], function(memo, value){
memo.push(value)
})
result.should.eql [1,2,3]
end
it 'should require returning of memo for scalar variables'
result = inject([1,2,3], false, function(memo, value){
return memo ? memo : value == 2
})
result.should.be_true
end
end
describe 'any()'
it 'should return null when no matches are found'
result = any('some foo bar', function(value){
return value.length > 5
})
result.should.be_null
end
it 'should return the value of the first matching expression'
result = any('foo some bar', function(value){
return value.length > 3
})
result.should.eql 'some'
end
describe 'haveStopped'
it 'should check if "stop" has been returned by a callback hook'
any([true, 'stop'], haveStopped).should.eql 'stop'
any([true, true], haveStopped).should.be_null
any([true, null], haveStopped).should.be_null
end
end
end
describe 'select()'
it 'should return an array of values when the callback evaluates to true'
result = select('some foo bar baz stuff', function(value){
return value.length > 3
})
result.should.eql ['some', 'stuff']
end
end
describe 'last()'
it 'should return the last element in an array'
last(['foo', 'bar']).should.eql 'bar'
end
end
describe 'argumentsToArray()'
it 'should return an array of arguments'
func = function(){ return argumentsToArray(arguments) }
func('foo', 'bar').should.eql ['foo', 'bar']
end
it 'should return the offset of an arguments array'
func = function(){ return argumentsToArray(arguments, 2) }
func('foo', 'bar', 'baz').should.eql ['baz']
end
end
describe 'does()'
it 'should assert without reporting'
does('foo', 'eql', 'foo')
JSpec.currentSpec.assertions.should.have_length 0
end
end
describe 'contentsOf()'
it 'should return a function body'
JSpec.contentsOf(-{ return 'foo' }).should.include 'return', 'foo'
end
end
describe 'paramsFor()'
it 'should return an array of function parameter names'
JSpec.paramsFor(function(foo, bar){}).should.eql ['foo', 'bar']
end
it 'should return only the params for the root function'
foo = function(bar){
function baz(test) {}
var something = function(foo, bar){}
}
JSpec.paramsFor(foo).should.eql ['bar']
end
it 'should return empty array when no params are present'
JSpec.paramsFor(function(){}).should.eql []
end
end
end

View File

@ -0,0 +1,151 @@
describe 'JSpec'
describe '.mockRequest'
before
responseFrom = function(path) {
request = new XMLHttpRequest
request.open('POST', path, false)
request.send(null)
return request.responseText
}
end
it 'should mock XMLHttpRequests if unmockRequest() is called or the spec block has finished'
original = XMLHttpRequest
mockRequest().and_return('test')
XMLHttpRequest.should.not.equal original
unmockRequest()
XMLHttpRequest.should.equal original
end
it 'should restore original XMLHttpRequest constructor after each spec'
XMLHttpRequest.should.not.eql JSpec.XMLHttpRequest
end
describe 'mock response'
before_each
mockRequest().and_return('bar', 'text/plain', 200, { 'x-foo' : 'bar' })
request = new XMLHttpRequest
request.open('GET', 'path', false, 'foo', 'bar')
request.send('foo=bar')
end
it 'should allow setting response status'
mockRequest().and_return('bar', 'text/plain', 404)
request = new XMLHttpRequest
request.open('GET', 'path', false)
request.send(null)
request.status.should.eql 404
request.statusText.should.eql 'Not Found'
end
it 'should default readyState to 0'
request = new XMLHttpRequest
request.readyState.should.eql 0
end
it 'should populate user'
request.user.should.eql 'foo'
end
it 'should populate password'
request.password.should.eql 'bar'
end
it 'should populate method'
request.method.should.eql 'GET'
end
it 'should populate readyState'
request.readyState.should.eql 4
end
it 'should populate url'
request.url.should.eql 'path'
end
it 'should populate status'
request.status.should.eql 200
end
it 'should populate statusText'
request.statusText.should.eql 'OK'
end
it 'should populate content type response header'
request.getResponseHeader('Content-Type').should.eql 'text/plain'
end
it 'should populate Content-Length response header'
request.getResponseHeader('Content-Length').should.eql 3
end
it 'should populate data'
request.data.should.eql 'foo=bar'
end
it 'should populate responseText'
request.responseText.should.eql 'bar'
end
it 'should populate headers'
request.getResponseHeader('X-Foo').should.eql 'bar'
end
it 'should not interrupt JSpec request related functionality'
mockRequest().and_return('fail')
(JSpec.tryLoading('async') || JSpec.tryLoading('spec/async')).should.eql 'cookies!'
fixture('test').should.eql '<p>test</p>'
fixture('test.json').should.include '{ user'
end
describe '.onreadystatechange()'
before_each
mockRequest().and_return('bar', 'text/plain', 200)
request = new XMLHttpRequest
end
it 'should be called when opening request in context to the request'
request.onreadystatechange = function(){
this.readyState.should.eql 1
}
request.open('GET', 'path')
end
it 'should be called when sending request'
request.open('GET', 'path')
request.onreadystatechange = function(){
this.readyState.should.eql 4
}
request.send(null)
end
end
describe '.setRequestHeader()'
it 'should set request headers'
mockRequest().and_return('bar', 'text/plain', 200)
request.open('GET', 'path', false, 'foo', 'bar')
request.setRequestHeader('Accept', 'foo')
request.send(null)
request.requestHeaders['accept'].should.eql 'foo'
end
end
describe 'HEAD'
it 'should respond with headers only'
mockRequest().and_return('bar', 'text/plain', 200)
request.open('HEAD', 'path', false)
request.send(null)
request.responseText.should.be_null
end
end
describe 'with uri'
it 'should mock only the uri specified'
mockRequest('ilike').and_return('cookies')
responseFrom('async').should.eql 'cookies'
end
end
end
end
end

View File

@ -0,0 +1,4 @@
=== 0.0.1 / YYYY-MM-DD
* Initial release

View File

@ -0,0 +1,29 @@
= YourLib
Description
== License
(The MIT License)
Copyright (c) 2009 Your Name <Your Email>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,2 @@
// Your library here

View File

@ -0,0 +1,8 @@
describe 'YourLib'
describe '.someMethod()'
it 'should do something'
true.should.be true
end
end
end

View File

@ -0,0 +1,20 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="JSPEC_ROOT/lib/jspec.css" />
<script src="JSPEC_ROOT/lib/jspec.js"></script>
<script src="../lib/yourlib.core.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.core.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -0,0 +1,8 @@
load('JSPEC_ROOT/lib/jspec.js')
load('lib/yourlib.core.js')
JSpec
.exec('spec/spec.core.js')
.run({ formatter : JSpec.formatters.Terminal })
.report()

View File

@ -0,0 +1,16 @@
<html>
<head>
<script src="jspec.js"></script>
<script src="../lib/yourlib.core.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.core.js')
.run()
.reportToServer()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
</body>
</html>

View File

@ -0,0 +1,8 @@
describe 'YourLib'
describe '.someMethod()'
it 'should do something'
true.should.be true
end
end
end

View File

@ -0,0 +1,20 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="JSPEC_ROOT/lib/jspec.css" />
<script src="JSPEC_ROOT/lib/jspec.js"></script>
<script src="../public/javascripts/application.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.application.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -0,0 +1,8 @@
load('JSPEC_ROOT/lib/jspec.js')
load('public/javascripts/application.js')
JSpec
.exec('spec/spec.application.js')
.run({ formatter : JSpec.formatters.Terminal })
.report()

View File

@ -0,0 +1,16 @@
<html>
<head>
<script src="jspec.js"></script>
<script src="../public/javascripts/application.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.application.js')
.run()
.reportToServer()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
</body>
</html>