├── SlickSpec ├── moobugger │ ├── images │ │ ├── icon.png │ │ ├── info.png │ │ ├── lick.png │ │ ├── max.png │ │ ├── min.png │ │ ├── time.png │ │ ├── close.png │ │ ├── error.png │ │ ├── warning.png │ │ ├── mootools.png │ │ ├── group-close.png │ │ └── group-open.png │ ├── debugger.html │ ├── bookmarklet.html │ ├── debugger.css │ ├── test.html │ ├── debugger.js │ └── debugger-iframe.js ├── simple_request.js ├── Loader.js ├── ie.css ├── JSSpec.css ├── slickspec.css ├── screen.css ├── JSSpecHelpers.js └── JSSpec.js ├── 1.2private └── Core │ ├── Browser.js │ └── Core.js ├── 1.2public ├── Utilities │ └── Cookie.js ├── Core │ ├── Browser.js │ └── Core.js ├── Element │ ├── Element.Style.js │ └── Element.Dimensions.js ├── Native │ ├── Number.js │ ├── Function.js │ ├── Array.js │ ├── String.js │ └── Hash.js └── Class │ ├── Class.js │ └── Class.Extras.js ├── Sets.js ├── index.html ├── runner1.3.html └── runner1.2.html /SlickSpec/moobugger/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/icon.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/info.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/lick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/lick.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/max.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/min.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/time.png -------------------------------------------------------------------------------- /1.2private/Core/Browser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Browser.js 3 | Private Specs for Browser.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/close.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/error.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/warning.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/mootools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/mootools.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/group-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/group-close.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/group-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/group-open.png -------------------------------------------------------------------------------- /1.2public/Utilities/Cookie.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Core.js 3 | Public Specs for Core.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe('Cookie', { 10 | 11 | "should set a cookie": function(){ 12 | Cookie.write('test', 1); 13 | 14 | 15 | } 16 | 17 | }); -------------------------------------------------------------------------------- /Sets.js: -------------------------------------------------------------------------------- 1 | var Sets = { 2 | 3 | '1.2public': [ 4 | 'Core/Core.js', 'Core/Browser.js', 5 | 'Native/Array.js', 'Native/String.js', 'Native/Function.js', 'Native/Number.js', 'Native/Hash.js', 6 | 'Class/Class.js', 'Class/Class.Extras.js', 7 | 'Element/Element.js', 'Element/Element.Style.js', 'Element/Element.Dimensions.js', 8 | ], 9 | 10 | '1.2private': [ 11 | 'Core/Core.js', 'Core/Browser.js', 12 | 13 | ] 14 | 15 | }; -------------------------------------------------------------------------------- /1.2public/Core/Browser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Browser.js 3 | Public Specs for Browser.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe('$exec', { 10 | 11 | 'should evaluate on global scope': function(){ 12 | $exec.call($exec, 'var execSpec = 42'); 13 | value_of(window.execSpec).should_be(42); 14 | }, 15 | 16 | 'should return the evaluated script': function(){ 17 | value_of($exec('$empty();')).should_be('$empty();'); 18 | } 19 | 20 | }); 21 | 22 | describe('Document', { 23 | 24 | 'should hold the parent window': function(){ 25 | value_of(document.window).should_be(window); 26 | }, 27 | 28 | 'should hold the head element': function(){ 29 | value_of(document.head.tagName.toLowerCase()).should_be('head'); 30 | } 31 | 32 | }); 33 | 34 | describe('Window', { 35 | 36 | 'should set the Element prototype': function(){ 37 | value_of($defined(window.Element.prototype)).should_be_true(); 38 | } 39 | 40 | }); -------------------------------------------------------------------------------- /SlickSpec/moobugger/debugger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | debugger 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 | 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /SlickSpec/simple_request.js: -------------------------------------------------------------------------------- 1 | var SimpleRequest = (function(){ 2 | 3 | function SimpleRequest(){ 4 | this.initialize(); 5 | }; 6 | SimpleRequest.prototype = { 7 | 8 | initialize: function(){ 9 | this.xhr = this.createXHR(); 10 | }, 11 | 12 | createXHR: function(){ 13 | // return ('XMLHttpRequest' in window)? new XMLHttpRequest(): new ActiveXObject('MSXML2.XMLHTTP'); 14 | return ('XMLHttpRequest' in window)? new XMLHttpRequest(): new ActiveXObject('Microsoft.XMLHTTP'); 15 | }, 16 | 17 | stateChange: function(fn){ 18 | if(this.xhr.readyState == 4 && this.xhr.status == 200){ 19 | fn.apply(this, [this.xhr.responseText, this.getXML()]); 20 | } 21 | }, 22 | 23 | getXML: function(){ 24 | if (this.xhr.responseXML && this.xhr.responseXML.documentElement) 25 | return this.xhr.responseXML; 26 | return parseXML(this.xhr.responseText); 27 | }, 28 | 29 | send: function(url, fn){ 30 | var self = this; 31 | this.xhr.onreadystatechange = function(){ self.stateChange(fn); }; 32 | this.xhr.open('get', url + '?n=' + (new Date()).getTime(), true); 33 | this.xhr.send(null); 34 | } 35 | 36 | }; 37 | 38 | return SimpleRequest; 39 | })(); 40 | -------------------------------------------------------------------------------- /SlickSpec/moobugger/bookmarklet.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | bugger 9 | 10 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |

The MooBugger.

41 |

moobugger - bookmark it!

42 |
43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SlickSpec for MooTools 5 | 6 | 7 | 47 | 48 | 49 |
50 | 51 |

SlickSpec alpha for MooTools-Core

52 | 53 | 57 | 58 |
59 | 60 | -------------------------------------------------------------------------------- /1.2private/Core/Core.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Core.js 3 | Private Specs for Core.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | (function(){ 10 | 11 | var Instrument = new Native({ 12 | 13 | name: 'instrument', 14 | 15 | initialize: function(name){ 16 | this.name = name; 17 | } 18 | 19 | }); 20 | 21 | Instrument.implement({ 22 | 23 | method: function(){ 24 | return this.property + ' ' + this.name; 25 | }, 26 | 27 | property: 'stuff' 28 | 29 | }); 30 | 31 | var Car = new Native({ 32 | 33 | name: 'car', 34 | 35 | protect: true, 36 | 37 | initialize: function(name){ 38 | this.name = name; 39 | } 40 | 41 | }); 42 | 43 | Car.implement({ 44 | 45 | property: 'stuff', 46 | 47 | method: function(){ 48 | return this.name + '_' + this.property; 49 | } 50 | 51 | }); 52 | 53 | describe('Native (private)', { 54 | 55 | 'should allow implementation over existing methods when browser option is not set': function(){ 56 | Instrument.implement({ property: 'staff' }); 57 | var myInstrument = new Instrument('xeelophone'); 58 | value_of(myInstrument.method()).should_be('staff xeelophone'); 59 | }, 60 | 61 | 'should not override existing methods when browser option is set': function(){ 62 | Car.implement({ property: 'staff' }); 63 | var myCar = new Car('smart'); 64 | value_of(myCar.method()).should_be('smart_stuff'); 65 | }, 66 | 67 | 'should allow generic calls': function(){ 68 | value_of(Car.method({name: 'ciccio', property: 'bello'})).should_be('ciccio_bello'); 69 | }, 70 | 71 | "should have a 'native' type": function(){ 72 | value_of(Native.type(Car)).should_be_true(); 73 | } 74 | 75 | }); 76 | 77 | })(); -------------------------------------------------------------------------------- /SlickSpec/Loader.js: -------------------------------------------------------------------------------- 1 | var loadSpecs = (function(Sets){ 2 | 3 | // Uses String.parseQueryString from MooTools-More 4 | // TODO Needs compat for other browsers 5 | var parseQueryString = function(string){ 6 | var vars = string.split(/[&;]/), res = {}; 7 | if (vars.length) vars.forEach(function(val){ 8 | var index = val.indexOf('='), 9 | keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g), 10 | value = decodeURIComponent(val.substr(index + 1)), 11 | obj = res; 12 | keys.forEach(function(key, i){ 13 | var current = obj[key]; 14 | if(i < keys.length - 1) 15 | obj = obj[key] = current || {}; 16 | else if(current && current._type == 'Array') // JSSpec prototyping 17 | current.push(value); 18 | else 19 | obj[key] = current != undefined ? [current, value] : value; 20 | }); 21 | }); 22 | return res; 23 | }; 24 | 25 | var getSpecs = function(queryString){ 26 | queryString = parseQueryString(queryString); 27 | 28 | var requestedSpecs = [], 29 | specs = queryString.specs; 30 | 31 | (specs && specs._type == 'Array' ? specs : [specs]).forEach(function(spec){ 32 | if (Sets[spec] && requestedSpecs.indexOf(spec) == -1) requestedSpecs.push(spec); 33 | }); 34 | 35 | return requestedSpecs; 36 | }; 37 | 38 | var loadSpecs = function(obj){ 39 | for (var i = 0; i < obj.length; i++){ 40 | SpecNames.push(obj[i]); 41 | 42 | var specs = Sets[obj[i]]; 43 | for (var j = 0; j < specs.length; j++){ 44 | document.write('<\/script>'); 45 | } 46 | } 47 | }; 48 | 49 | var requestedSpecs = getSpecs(document.location.search.substr(1)); 50 | loadSpecs(requestedSpecs); 51 | 52 | }); -------------------------------------------------------------------------------- /SlickSpec/ie.css: -------------------------------------------------------------------------------- 1 | /* 2 | curl -s http://tripledoubleyou.subtlegradient.com/c/blueprint/ie.css #*/ 3 | /* ----------------------------------------------------------------------- 4 | 5 | 6 | Blueprint CSS Framework 0.9 7 | http://blueprintcss.org 8 | 9 | * Copyright (c) 2007-Present. See LICENSE for more info. 10 | * See README for instructions on how to use Blueprint. 11 | * For credits and origins, see AUTHORS. 12 | * This is a compressed file. See the sources in the 'src' directory. 13 | 14 | ----------------------------------------------------------------------- */ 15 | 16 | /* ie.css */ 17 | body {text-align:center;} 18 | .container {text-align:left;} 19 | * html .column, * html div.span-1, * html div.span-2, * html div.span-3, * html div.span-4, * html div.span-5, * html div.span-6, * html div.span-7, * html div.span-8, * html div.span-9, * html div.span-10, * html div.span-11, * html div.span-12, * html div.span-13, * html div.span-14, * html div.span-15, * html div.span-16, * html div.span-17, * html div.span-18, * html div.span-19, * html div.span-20, * html div.span-21, * html div.span-22, * html div.span-23, * html div.span-24 {display:inline;overflow-x:hidden;} 20 | * html legend {margin:0px -8px 16px 0;padding:0;} 21 | sup {vertical-align:text-top;} 22 | sub {vertical-align:text-bottom;} 23 | html>body p code {*white-space:normal;} 24 | hr {margin:-8px auto 11px;} 25 | img {-ms-interpolation-mode:bicubic;} 26 | .clearfix, .container {display:inline-block;} 27 | * html .clearfix, * html .container {height:1%;} 28 | fieldset {padding-top:0;} 29 | textarea {overflow:auto;} 30 | input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;} 31 | input.text:focus, input.title:focus {border-color:#666;} 32 | input.text, input.title, textarea, select {margin:0.5em 0;} 33 | input.checkbox, input.radio {position:relative;top:.25em;} 34 | form.inline div, form.inline p {vertical-align:middle;} 35 | form.inline label {position:relative;top:-0.25em;} 36 | form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;} 37 | button, input.button {position:relative;top:0.25em;} 38 | -------------------------------------------------------------------------------- /1.2public/Element/Element.Style.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Element.Style.js 3 | Specification Examples of Element.Style.js. 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe('Element.set `opacity`', { 10 | 11 | 'should set the opacity of an Element': function() { 12 | var el = new Element('div').set('opacity', 0.4); 13 | if (Browser.Engine.trident) value_of(el.style.filter).should_be('alpha(opacity=40)'); 14 | value_of(el.style.opacity).should_be(0.4); 15 | }, 16 | 17 | 'should return the opacity of an Element': function() { 18 | value_of(new Element('div').set('opacity', 0.4).get('opacity')).should_be(0.4); 19 | } 20 | 21 | }); 22 | 23 | describe('Element.getStyle', { 24 | 25 | 'should get a six digit hex code from a three digit hex code': function() { 26 | var el = new Element('div').set('html', '
'); 27 | value_of(el.getElement('div').getStyle('color')).should_be('#00ff00'); 28 | }, 29 | 30 | 'should getStyle a six digit hex code from an RGB value': function() { 31 | var el = new Element('div').set('html', '
'); 32 | value_of(el.getElement('div').getStyle('color')).should_be('#00ff00'); 33 | }, 34 | 35 | 'should `getStyle` with a dash in it': function() { 36 | var el = new Element('div').set('html', '
'); 37 | value_of(el.getElement('div').getStyle('list-style-type')).should_be('square'); 38 | } 39 | 40 | }); 41 | 42 | describe('Element.setStyle', { 43 | 44 | 'should set the `styles` property on an Element using the Element constructor': function() { 45 | value_of(new Element('div', {styles:{'color':'#00ff00'}}).getStyle('color')).should_be('#00ff00'); 46 | }, 47 | 48 | 'should `setStyle` on an Element': function() { 49 | value_of(new Element('div').setStyle('color','#00ff00').getStyle('color')).should_be('#00ff00'); 50 | }, 51 | 52 | 'should properly `setStyle` for a property with a dash in it': function() { 53 | value_of(new Element('div').setStyle('list-style-type', 'square').getStyle('list-style-type')).should_be('square'); 54 | } 55 | 56 | }); 57 | 58 | describe('Element.getStyles', { 59 | 60 | 'should return multiple styles': function() { 61 | var el = new Element('div').set('html', '
'); 62 | value_of(el.getElement('div').getStyles('color', 'list-style-type')).should_be({color:'#00ff00', 'list-style-type':'square'}); 63 | } 64 | 65 | }); 66 | 67 | describe('Element.setStyles', { 68 | 69 | 'should set multiple styles': function() { 70 | value_of(new Element('div').setStyles({'list-style-type':'square', 'color':'#00ff00'}).getStyles('list-style-type', 'color')).should_be({'list-style-type':'square', color:'#00ff00'}); 71 | } 72 | 73 | }); -------------------------------------------------------------------------------- /1.2public/Element/Element.Dimensions.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Element.Dimensions.js 3 | Specs for Element.Dimensions.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | (function(){ 10 | var div, relDiv, absDiv, scrollDiv, tallDiv; 11 | window.addEvent('domready', function(){ 12 | div = new Element('div', { 13 | id: 'ElementDimensionsTest', 14 | styles: { 15 | width: 100, 16 | height: 100, 17 | margin: 2, 18 | padding: 3, 19 | border: '1px solid black', 20 | visibility: 'hidden', 21 | display: 'block', 22 | position: 'absolute', 23 | top: 100, 24 | left: 100, 25 | overflow: 'hidden', 26 | zIndex: 1 27 | } 28 | }).inject($(document.body)); 29 | 30 | relDiv = new Element('div', { 31 | styles: { 32 | width: 50, 33 | height: 50, 34 | margin: 5, 35 | padding: 5, 36 | border: '1px solid green', 37 | visibility: 'hidden', 38 | position: 'relative', 39 | overflow: 'hidden' 40 | } 41 | }).inject(div); 42 | 43 | absDiv = new Element('div', { 44 | styles: { 45 | width: 10, 46 | height: 10, 47 | margin: 5, 48 | padding: 5, 49 | border: '1px solid red', 50 | visibility: 'hidden', 51 | position: 'absolute', 52 | top: 10, 53 | left: 10, 54 | overflow: 'hidden' 55 | } 56 | }).inject(relDiv); 57 | 58 | scrollDiv = new Element('div', { 59 | styles: { 60 | width: 100, 61 | height: 100, 62 | overflow: 'scroll', 63 | visibility: 'hidden', 64 | position: 'absolute', 65 | top: 0, 66 | left: 0 67 | } 68 | }).inject($(document.body)); 69 | 70 | tallDiv = new Element('div', { 71 | styles: { 72 | width: 200, 73 | height: 200, 74 | visibility: 'hidden' 75 | } 76 | }).inject(scrollDiv); 77 | 78 | }); 79 | 80 | describe('Element.getSize', { 81 | 82 | 'should measure the width and height of the element': function(){ 83 | value_of(div.getSize()).should_be({x: 108, y: 108}); 84 | } 85 | 86 | }); 87 | 88 | describe('Element.getPosition', { 89 | 90 | 'should measure the x and y position of the element': function(){ 91 | value_of(div.getPosition()).should_be({x: 102, y: 102}); 92 | } 93 | 94 | }); 95 | 96 | describe('Element.getCoordinates', { 97 | 98 | 'should return the coordinates relative to parent': function(){ 99 | value_of(absDiv.getCoordinates(relDiv)).should_be({left:16, top:16, width:22, height:22, right:38, bottom:38}); 100 | } 101 | 102 | }); 103 | 104 | describe('Element.getScrollSize', { 105 | 106 | 'should return the scrollSize': function(){ 107 | value_of(scrollDiv.getScrollSize()).should_be({x:200, y:200}); 108 | } 109 | 110 | }); 111 | 112 | describe('Element.scrollTo', { 113 | 114 | 'should scroll the element': function(){ 115 | value_of(scrollDiv.scrollTo(20,20).getScroll()).should_be({x:20, y:20}); 116 | } 117 | 118 | }); 119 | 120 | })(); 121 | -------------------------------------------------------------------------------- /runner1.3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | SlickSpec Select 63 |

SlickSpec

64 | 65 |

66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /runner1.2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | SlickSpec Select 63 |

SlickSpec

64 | 65 |

66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /1.2public/Native/Number.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Number.js 3 | Specs for Number.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe("Number Methods", { 10 | 11 | // Number.toInt 12 | 13 | 'should convert a number to an integer': function(){ 14 | value_of((111).toInt()).should_be(111); 15 | }, 16 | 17 | 'should convert a number depending on the radix provided': function(){ 18 | value_of((111).toInt(2)).should_be(7); 19 | value_of((0x16).toInt(10)).should_be(22); //ECMA standard, radix is optional so if starts with 0x then parsed as hexadecimal 20 | value_of((016).toInt(10)).should_be(14); //ECMA standard, radix is optional so if starts with 0 then parsed as octal 21 | }, 22 | 23 | // Number.toFloat 24 | 25 | 'should convert a number to a float': function(){ 26 | value_of((1.00).toFloat()).should_be(1); 27 | value_of((1.12 - 0.12).toFloat()).should_be(1); 28 | value_of((0.0010).toFloat()).should_be(0.001); 29 | value_of((Number.MIN_VALUE).toFloat()).should_be(Number.MIN_VALUE); 30 | }, 31 | 32 | // Number.limit 33 | 34 | 'should limit a number within a range': function(){ 35 | value_of((-1).limit(0, 1)).should_be(0); 36 | value_of((3).limit(1, 2)).should_be(2); 37 | }, 38 | 39 | 'should not limit a number if within the range': function(){ 40 | value_of((2).limit(0,4)).should_be(2); 41 | }, 42 | 43 | // Number.round 44 | 45 | 'should round a number to the nearest whole number if units place is not specified': function(){ 46 | value_of((0.01).round()).should_be(0); 47 | }, 48 | 49 | 'should round a number according the units place specified': function(){ 50 | value_of((0.01).round(2)).should_be(0.01); 51 | value_of((1).round(3)).should_be(1); 52 | value_of((-1.01).round()).should_be(-1); 53 | value_of((-1.01).round(2)).should_be(-1.01); 54 | value_of((111).round(-1)).should_be(110); 55 | value_of((-111).round(-2)).should_be(-100); 56 | value_of((100).round(-5)).should_be(0); 57 | }, 58 | 59 | // Number.times 60 | 61 | 'should call the function for the specified number of times': function(){ 62 | var found = 0; 63 | (3).times(function(i){ 64 | found = i; 65 | }); 66 | 67 | var found2 = -1; 68 | (0).times(function(i){ 69 | found2 = i; 70 | }); 71 | 72 | value_of(found).should_be(2); 73 | value_of(found2).should_be(-1); 74 | }, 75 | 76 | 'should bind and call the function for the specified number of times': function(){ 77 | var aTest = 'hi'; 78 | var found3 = false; 79 | (1).times(function(i){ 80 | found3 = (this == aTest); 81 | }, aTest); 82 | value_of(found3).should_be_true(); 83 | } 84 | 85 | }); 86 | 87 | (function(math){ 88 | var examples = {}; 89 | new Hash(math).each(function(value, key){ 90 | var example = {}; 91 | var b = value.test[1]; 92 | examples['should return the ' + value.title + ' value of the number' + ((b) ? ' and the passed number' : '')] = function(){ 93 | value_of(value.test[0][key](b)).should_be(Math[key].apply(null, value.test)); 94 | }; 95 | }); 96 | describe("Number Math Methods", examples); 97 | })({ 98 | abs: { test: [-1], title: 'absolute' }, 99 | acos: { test: [0], title: 'arc cosine' }, 100 | asin: { test: [0.5], title: 'arc sine' }, 101 | atan: { test: [0.5], title: 'arc tangent' }, 102 | atan2: { test: [0.1, 0.5], title: 'arc tangent' }, 103 | ceil: { test: [0.6], title: 'number closest to and not less than the' }, 104 | cos: { test: [30], title: 'cosine' }, 105 | exp: { test: [2], title: 'exponent' }, 106 | floor: { test: [2.4], title: 'integer closet to and not greater than' }, 107 | log: { test: [2], title: 'log' }, 108 | max: { test: [5, 3], title: 'maximum' }, 109 | min: { test: [-4, 2], title: 'minimum' }, 110 | pow: { test: [2, 2], title: 'power' }, 111 | sin: { test: [0.5], title: 'sine' }, 112 | sqrt: { test: [4], title: 'square root' }, 113 | tan: { test: [0.3], title: 'tangent' } 114 | }); -------------------------------------------------------------------------------- /SlickSpec/moobugger/debugger.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | #debug { 7 | display: none; 8 | font-family: arial, helvetica; 9 | width: 100%; 10 | border-top: 1px solid #B9ACAC; 11 | z-index: 9999; 12 | } 13 | 14 | #debug-header { 15 | height: 16px; 16 | border-top: 1px solid #F3F1F1; 17 | border-bottom: 1px solid #B9ACAC; 18 | background: #DBD6D6; 19 | text-align: right; 20 | padding: 3px 7px 0; 21 | font-size: 10px; 22 | color: #B9ACAC; 23 | } 24 | 25 | #debug-mootools-net:link, #debug-mootools-net:visited { 26 | display: block; 27 | float: left; 28 | width: 14px; 29 | height: 14px; 30 | margin-top: -1px; 31 | background: url(images/icon.png) no-repeat top left; 32 | } 33 | 34 | #debug-mootools-net:hover, #debug-mootools-net:active { 35 | background-position: bottom left; 36 | } 37 | 38 | #debug-header span { 39 | cursor: pointer; 40 | width: 13px; 41 | height: 13px; 42 | display: block; 43 | float: right; 44 | margin-left: 5px; 45 | } 46 | 47 | #debug-header b { 48 | display: block; 49 | clear: both; 50 | } 51 | 52 | #debug-button-close { 53 | background: url(images/close.png); 54 | } 55 | 56 | #debug-button-max { 57 | background: url(images/max.png); 58 | } 59 | 60 | #debug-button-min { 61 | background: url(images/min.png); 62 | } 63 | 64 | #debug-messages { 65 | height: 133px; 66 | border-top: 1px solid #EEE; 67 | overflow: auto; 68 | background: #FFF url(images/mootools.png) no-repeat center center; 69 | } 70 | 71 | #debug-messages pre { 72 | white-space: normal; 73 | padding: 3px 5px; 74 | margin: 0; 75 | font: 11px "Andale Mono", Monaco, "Courier New"; 76 | border-bottom: 1px solid #ccc; 77 | color: #444; 78 | background-color: #FFF; 79 | } 80 | 81 | #debug-messages pre.error { 82 | background: #F0F0F0 url(images/error.png) no-repeat 5px 50%; 83 | color: #da5d0b; 84 | padding-left: 25px; 85 | } 86 | 87 | #debug-messages pre.error span.string { 88 | color: #da5d0b; 89 | } 90 | 91 | #debug-messages pre.warning { 92 | background: #F0F0F0 url(images/warning.png) no-repeat 5px 50%; 93 | color: #ce7a2a; 94 | padding-left: 25px; 95 | } 96 | 97 | #debug-messages pre.warning span.string { 98 | color: #ce7a2a; 99 | } 100 | 101 | #debug-messages pre.info { 102 | background: #F0F0F0 url(images/info.png) no-repeat 5px 50%; 103 | color: #528CE0; 104 | padding-left: 25px; 105 | } 106 | 107 | #debug-messages pre.info span.string { 108 | color: #528CE0; 109 | } 110 | 111 | #debug-messages pre.time { 112 | background: #F0F0F0 url(images/time.png) no-repeat 5px 50%; 113 | color: #111; 114 | padding-left: 25px; 115 | } 116 | 117 | #debug-messages pre.group { 118 | background: #f9f9f9 url(images/group-close.png) no-repeat 6px 50%; 119 | color: #222; 120 | padding-left: 25px; 121 | cursor: pointer; 122 | } 123 | 124 | #debug-messages pre.group-closed { 125 | background-image: url(images/group-open.png); 126 | } 127 | 128 | #debug-messages div.group { 129 | padding: 2px 0 2px 10px; 130 | background: #ccc; 131 | } 132 | 133 | #debug-messages pre.logger { 134 | color: #4373B8; 135 | } 136 | 137 | #debug-input-area { 138 | overflow: hidden; 139 | border-top: 1px solid #B9ACAC; 140 | } 141 | 142 | #debug-input { 143 | width: 100% !important; 144 | width: 90%; 145 | color: #222; 146 | font: 12px Andale Mono, Monaco, "Courier New"; 147 | margin: 0; 148 | border: 0; 149 | padding: 4px 5px 3px 30px; 150 | background: #fff url(images/lick.png) no-repeat 7px 63%; 151 | } 152 | 153 | #debug-messages a:link, #debug-messages a:visited { 154 | color: #333; 155 | text-decoration: none; 156 | } 157 | 158 | #debug-messages a:hover, #debug-messages a:active { 159 | background-color: #757E8A; 160 | color: #fff; 161 | } 162 | 163 | #debug-messages a:hover span, #debug-messages a:active span { 164 | color: #EECFCF; 165 | } 166 | 167 | #debug-messages span.number { 168 | color: #B33F3F; 169 | } 170 | 171 | #debug-messages span.boolean { 172 | color: #A652E0; 173 | } 174 | 175 | #debug-messages span.undefined, #debug-messages span.false { 176 | color: #888; 177 | background-color: #eee; 178 | } 179 | 180 | #debug-messages span.key { 181 | color: #888; 182 | } 183 | 184 | #debug-messages span.tag { 185 | color: #528CE0; 186 | } 187 | 188 | #debug-messages span.string { 189 | color: #8B9E41; 190 | } -------------------------------------------------------------------------------- /SlickSpec/JSSpec.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | /* -------------------- 4 | * @Layout 5 | */ 6 | 7 | html { 8 | overflow: hidden; 9 | } 10 | 11 | body, #jsspec_container { 12 | overflow: hidden; 13 | padding: 0; 14 | margin: 0; 15 | width: 100%; 16 | height: 100%; 17 | background-color: white; 18 | } 19 | 20 | #title { 21 | padding: 0; 22 | margin: 0; 23 | position: absolute; 24 | top: 0px; 25 | left: 0px; 26 | width: 100%; 27 | height: 40px; 28 | overflow: hidden; 29 | } 30 | 31 | #list { 32 | padding: 0; 33 | margin: 0; 34 | position: absolute; 35 | top: 40px; 36 | left: 0px; 37 | bottom: 0px; 38 | overflow: auto; 39 | width: 250px; 40 | _height:expression(document.body.clientHeight-40); 41 | } 42 | 43 | #log { 44 | padding: 0; 45 | margin: 0; 46 | position: absolute; 47 | top: 40px; 48 | left: 250px; 49 | right: 0px; 50 | bottom: 0px; 51 | overflow: auto; 52 | _height:expression(document.body.clientHeight-40); 53 | _width:expression(document.body.clientWidth-250); 54 | } 55 | 56 | /*iPhone*/ 57 | @media only screen and (max-device-width: 480px) { 58 | #list { 59 | overflow: visible; 60 | } 61 | 62 | #log { 63 | overflow: visible; 64 | } 65 | } 66 | 67 | 68 | /* -------------------- 69 | * @Decorations and colors 70 | */ 71 | * { 72 | padding: 0; 73 | margin: 0; 74 | font-family: "Lucida Grande", Helvetica, sans-serif; 75 | } 76 | 77 | li { 78 | list-style: none; 79 | } 80 | 81 | /* hiding subtitles */ 82 | h2 { 83 | display: none; 84 | } 85 | 86 | /* title section */ 87 | div#title { 88 | padding: 0em 0.5em; 89 | } 90 | 91 | div#title h1 { 92 | font-size: 1.5em; 93 | float: left; 94 | } 95 | 96 | div#title ul li { 97 | float: left; 98 | padding: 0.5em 0em 0.5em 0.75em; 99 | } 100 | 101 | div#title p { 102 | float:right; 103 | margin-right:1em; 104 | font-size: 0.75em; 105 | } 106 | 107 | /* spec container */ 108 | ul.specs { 109 | margin: 0.5em; 110 | } 111 | ul.specs li { 112 | margin-bottom: 0.1em; 113 | } 114 | 115 | /* spec title */ 116 | ul.specs li h3 { 117 | font-weight: bold; 118 | font-size: 0.75em; 119 | padding: 0.2em 1em; 120 | cursor: pointer; 121 | _cursor: hand; 122 | } 123 | 124 | /* example container */ 125 | ul.examples li { 126 | border-style: solid; 127 | border-width: 0px 0px 1px 5px; 128 | margin: 0.2em 0em 0.2em 1em; 129 | } 130 | 131 | /* example title */ 132 | ul.examples li h4 { 133 | font-weight: normal; 134 | font-size: 0.75em; 135 | margin-left: 1em; 136 | } 137 | 138 | pre.examples-code { 139 | margin: 0.5em 2em; 140 | padding: 0.5em; 141 | background: white; 142 | border: solid 1px #CCC; 143 | font-size: 10px; 144 | font-family: "Panic Sans", "Monaco", monospace !important; 145 | } 146 | 147 | /* example explaination */ 148 | ul.examples li div { 149 | padding: 1em 2em; 150 | font-size: 0.75em; 151 | } 152 | 153 | /* styles for ongoing, success, failure, error */ 154 | div.success, div.success a { 155 | color: #FFFFFF; 156 | background-color: #65C400; 157 | } 158 | 159 | li.ongoing li.success pre.examples-code, 160 | li pre.examples-code { 161 | display:none !important; 162 | } 163 | li.exception pre.examples-code, 164 | li.ongoing pre.examples-code { 165 | display:block !important; 166 | } 167 | 168 | ul.specs li.success h3, ul.specs li.success h3 a { 169 | color: #FFFFFF; 170 | background-color: #65C400; 171 | } 172 | 173 | ul.examples li.success, ul.examples li.success a { 174 | color: #3D7700; 175 | background-color: #DBFFB4; 176 | border-color: #65C400; 177 | } 178 | 179 | div.exception, div.exception a { 180 | color: #FFFFFF; 181 | background-color: #C20000; 182 | } 183 | 184 | ul.specs li.exception h3, ul.specs li.exception h3 a { 185 | color: #FFFFFF; 186 | background-color: #C20000; 187 | } 188 | 189 | ul.examples li.exception, ul.examples li.exception a { 190 | color: #C20000; 191 | background-color: #FFFBD3; 192 | border-color: #C20000; 193 | } 194 | 195 | div.ongoing, div.ongoing a { 196 | color: #000000; 197 | background-color: #FFFF80; 198 | } 199 | 200 | ul.specs li.ongoing h3, ul.specs li.ongoing h3 a { 201 | color: #000000; 202 | background-color: #FFFF80; 203 | } 204 | 205 | ul.examples li.ongoing, ul.examples li.ongoing a { 206 | color: #000000; 207 | background-color: #FFFF80; 208 | border-color: #DDDD00; 209 | } 210 | 211 | 212 | 213 | /* -------------------- 214 | * values 215 | */ 216 | .number_value, .string_value, .regexp_value, .boolean_value, .dom_value { 217 | font-family: monospace; 218 | color: blue; 219 | } 220 | .object_value, .array_value { 221 | line-height: 2em; 222 | padding: 0.1em 0.2em; 223 | margin: 0.1em 0; 224 | } 225 | .date_value { 226 | font-family: monospace; 227 | color: olive; 228 | } 229 | .undefined_value, .null_value { 230 | font-style: italic; 231 | color: blue; 232 | } 233 | .dom_attr_name { 234 | } 235 | .dom_attr_value { 236 | color: red; 237 | } 238 | .dom_path { 239 | font-size: 0.75em; 240 | color: gray; 241 | } 242 | strong { 243 | font-weight: normal; 244 | background-color: #FFC6C6; 245 | } 246 | -------------------------------------------------------------------------------- /1.2public/Native/Function.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Function.js 3 | Specs for Function.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | (function(){ 10 | 11 | var fn = function(){ 12 | return $A(arguments); 13 | }; 14 | 15 | var Rules = function(){ 16 | return this + ' rules'; 17 | }; 18 | 19 | var Args = function(){ 20 | return [this].concat($A(arguments)); 21 | }; 22 | 23 | describe("Function Methods", { 24 | 25 | // Function.create 26 | 27 | 'should return a new function': function(){ 28 | var fnc = $empty.create(); 29 | value_of($empty === fnc).should_be_false(); 30 | }, 31 | 32 | 'should return a new function with specified argument': function(){ 33 | var fnc = fn.create({'arguments': 'rocks'}); 34 | value_of(fnc()).should_be(['rocks']); 35 | }, 36 | 37 | 'should return a new function with multiple arguments': function(){ 38 | var fnc = fn.create({'arguments': ['MooTools', 'rocks']}); 39 | value_of(fnc()).should_be(['MooTools', 'rocks']); 40 | }, 41 | 42 | 'should return a new function bound to an object': function(){ 43 | var fnc = Rules.create({'bind': 'MooTools'}); 44 | value_of(fnc()).should_be('MooTools rules'); 45 | }, 46 | 47 | 'should return a new function as an event': function(){ 48 | var fnc = fn.create({'arguments': [0, 1], 'event': true}); 49 | value_of(fnc('an Event occurred')).should_be(['an Event occurred', 0, 1]); 50 | }, 51 | 52 | // Function.bind 53 | 54 | 'should return the function bound to an object': function(){ 55 | var fnc = Rules.bind('MooTools'); 56 | value_of(fnc()).should_be('MooTools rules'); 57 | }, 58 | 59 | 'should return the function bound to an object with specified argument': function(){ 60 | var fnc = Args.bind('MooTools', 'rocks'); 61 | value_of(fnc()).should_be(['MooTools', 'rocks']); 62 | }, 63 | 64 | 'should return the function bound to an object with multiple arguments': function(){ 65 | var fnc = Args.bind('MooTools', ['rocks', 'da house']); 66 | value_of(fnc()).should_be(['MooTools', 'rocks', 'da house']); 67 | }, 68 | 69 | 'should return the function bound to an object and make the function an event listener': function(){ 70 | var fnc = Args.bindWithEvent('MooTools'); 71 | value_of(fnc('an Event ocurred')).should_be(['MooTools', 'an Event ocurred']); 72 | }, 73 | 74 | 'should return the function bound to an object and make the function event listener with multiple arguments': function(){ 75 | var fnc = Args.bindWithEvent('MooTools', ['rocks', 'da house']); 76 | value_of(fnc('an Event ocurred')).should_be(['MooTools', 'an Event ocurred', 'rocks', 'da house']); 77 | }, 78 | 79 | // Function.pass 80 | 81 | 'should return a function that when called passes the specified arguments to the original function': function(){ 82 | var fnc = fn.pass('MooTools is beautiful and elegant'); 83 | value_of(fnc()).should_be(['MooTools is beautiful and elegant']); 84 | }, 85 | 86 | 'should pass multiple arguments and bind the function to a specific object when it is called': function(){ 87 | var fnc = Args.pass(['rocks', 'da house'], 'MooTools'); 88 | value_of(fnc()).should_be(['MooTools', 'rocks', 'da house']); 89 | }, 90 | 91 | // Function.run 92 | 93 | 'should run the function': function(){ 94 | var result = fn.run(); 95 | value_of(result).should_be([]); 96 | }, 97 | 98 | 'should run the function with multiple arguments': function(){ 99 | var result = fn.run(['MooTools', 'beautiful', 'elegant']); 100 | value_of(result).should_be(['MooTools', 'beautiful', 'elegant']); 101 | }, 102 | 103 | 'should run the function with multiple arguments and bind the function to an object': function(){ 104 | var result = Args.run(['beautiful', 'elegant'], 'MooTools'); 105 | value_of(result).should_be(['MooTools', 'beautiful', 'elegant']); 106 | }, 107 | 108 | // Function.extend 109 | 110 | "should extend the function's properties": function(){ 111 | var fnc = (function(){}).extend({a: 1, b: 'c'}); 112 | value_of(fnc.a).should_be(1); 113 | value_of(fnc.b).should_be('c'); 114 | }, 115 | 116 | // Function.attempt 117 | 118 | 'should call the function without raising an exception': function(){ 119 | var fnc = function(){ 120 | this_should_not_work(); 121 | }; 122 | fnc.attempt(); 123 | }, 124 | 125 | "should return the function's return value": function(){ 126 | var fnc = $lambda('hello world!'); 127 | value_of(fnc.attempt()).should_be('hello world!'); 128 | }, 129 | 130 | 'should return null if the function raises an exception': function(){ 131 | var fnc = function(){ 132 | this_should_not_work(); 133 | }; 134 | value_of(fnc.attempt()).should_be_null(); 135 | }, 136 | 137 | // Function.delay 138 | 139 | 'delay should return a timer pointer': function(){ 140 | var timer = $empty.delay(10000); 141 | value_of(Number.type(timer)).should_be_true(); 142 | $clear(timer); 143 | }, 144 | 145 | // Function.periodical 146 | 147 | 'periodical should return a timer pointer': function(){ 148 | var timer = $empty.periodical(10000); 149 | value_of(Number.type(timer)).should_be_true(); 150 | $clear(timer); 151 | } 152 | 153 | }); 154 | 155 | })(); -------------------------------------------------------------------------------- /SlickSpec/moobugger/test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | the moobugger 9 | 10 | 11 | 12 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 |

The MooBugger.

43 | 44 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

45 | 46 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

47 | 48 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

49 | 50 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

51 | 52 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

53 | 54 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

55 | 56 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

57 | 58 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

59 |
60 | 61 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /1.2public/Native/Array.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Array.js 3 | Specs for Array.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe("Array Methods", { 10 | 11 | // Array.flatten 12 | 13 | 'should flatten a multidimensional array': function(){ 14 | var arr = [1,2,3,[4,5,[6,7,[8]]], [[[[[9]]]]]]; 15 | value_of(arr.flatten()).should_be([1,2,3,4,5,6,7,8,9]); 16 | }, 17 | 18 | 'should flatten arguments': function(){ 19 | var test = function(){ 20 | return Array.flatten(arguments); 21 | }; 22 | value_of(test(1,2,3)).should_be([1,2,3]); 23 | value_of(test([1,2,3])).should_be([1,2,3]); 24 | value_of(test(1,2,[3])).should_be([1,2,3]); 25 | }, 26 | 27 | // Array.filter 28 | 29 | 'should filter an array': function(){ 30 | var array = [1,2,3,0,0,0]; 31 | var arr = array.concat([false, null, 4]).filter(Number.type); 32 | value_of(arr).should_be(array.concat(4)); 33 | }, 34 | 35 | // Array.clean 36 | 37 | 'should clean an array from undefined and null values': function(){ 38 | var array = [null, 1, 0, true, false, "foo", undefined]; 39 | var arr = array.clean(); 40 | value_of(arr).should_be([1, 0, true, false, "foo"]); 41 | }, 42 | 43 | // Array.map 44 | 45 | 'should return a mapping of an array': function(){ 46 | var arr = [1,2,3,0,0,0].map(function(item){ 47 | return (item + 1); 48 | }); 49 | 50 | value_of(arr).should_be([2,3,4,1,1,1]); 51 | }, 52 | 53 | // Array.every 54 | 55 | 'should return true if every item matches the comparator, otherwise false': function(){ 56 | value_of([1,2,3,0,0,0].every(Number.type)).should_be_true(); 57 | 58 | value_of(['1',2,3,0].every(Number.type)).should_be_false(); 59 | }, 60 | 61 | // Array.some 62 | 63 | 'should return true if some of the items in the array match the comparator, otherwise false': function(){ 64 | value_of(['1',2,3,0].some(Number.type)).should_be_true(); 65 | 66 | value_of([1,2,3,0,0,0].map(String).some(Number.type)).should_be_false(); 67 | }, 68 | 69 | // Array.indexOf 70 | 71 | 'should return the index of the item': function(){ 72 | value_of([1,2,3,0,0,0].indexOf(0)).should_be(3); 73 | }, 74 | 75 | 'should return -1 if the item is not found in the array': function(){ 76 | value_of([1,2,3,0,0,0].indexOf('not found')).should_be(-1); 77 | }, 78 | 79 | // Array.erase 80 | 81 | 'should remove all items in the array that match the specified item': function(){ 82 | var arr = [1,2,3,0,0,0].erase(0); 83 | value_of(arr).should_be([1,2,3]); 84 | }, 85 | 86 | // Array.contains 87 | 88 | 'should return true if the array contains the specified item': function(){ 89 | value_of([1,2,3,0,0,0].contains(0)).should_be_true(); 90 | }, 91 | 92 | 'should return false if the array does not contain the specified item': function(){ 93 | value_of([0,1,2].contains('not found')).should_be_false(); 94 | }, 95 | 96 | // Array.associate 97 | 98 | 'should associate an array with a specified array': function(){ 99 | var obj = [1,2,3,0,0,0].associate(['a', 'b', 'c', 'd']); 100 | value_of(obj).should_be({a:1, b:2, c:3, d:0}); 101 | }, 102 | 103 | // Array.link 104 | 105 | 'should link an array items to a new object according to the specified matchers': function(){ 106 | var el = document.createElement('div'); 107 | var assoc2 = [100, 'Hello', {foo: 'bar'}, el, false].link({ 108 | myNumber: Number.type, 109 | myElement: Element.type, 110 | myObject: Object.type, 111 | myString: String.type, 112 | myBoolean: $defined 113 | }); 114 | 115 | value_of(assoc2).should_be({ 116 | myNumber: 100, 117 | myElement: el, 118 | myObject: {foo: 'bar'}, 119 | myString: 'Hello', 120 | myBoolean: false 121 | }); 122 | }, 123 | 124 | // Array.extend 125 | 126 | 'should extend an array': function(){ 127 | var a = [1,2,4]; 128 | var b = [2,3,4,5]; 129 | a.extend(b); 130 | value_of(a).should_be([1,2,4,2,3,4,5]); 131 | value_of(b).should_be([2,3,4,5]); 132 | }, 133 | 134 | // Array.combine 135 | 136 | 'should combine an array': function(){ 137 | var arr = [1,2,3,4].combine([3,1,4,5,6,7]); 138 | value_of(arr).should_be([1,2,3,4,5,6,7]); 139 | }, 140 | 141 | // Array.include 142 | 143 | 'should include only new items': function(){ 144 | var arr = [1,2,3,4].include(1).include(5); 145 | value_of(arr).should_be([1,2,3,4,5]); 146 | }, 147 | 148 | // Array.getLast 149 | 150 | 'should return the last item in the array': function(){ 151 | value_of([1,2,3,0,0,0].getLast()).should_be(0); 152 | value_of([3].getLast()).should_be(3); 153 | }, 154 | 155 | 'should return null if there are no items': function(){ 156 | value_of([].getLast()).should_be(null); 157 | }, 158 | 159 | // Array.empty 160 | 161 | 'should empty the array': function(){ 162 | var arr = [1,2,3,4].empty(); 163 | value_of(arr).should_be([]); 164 | } 165 | 166 | }); 167 | 168 | describe("Array Color Methods", { 169 | 170 | // Array.hexToRgb 171 | 172 | 'should return null if the length of the array is not 3': function(){ 173 | value_of([].hexToRgb()).should_be_null(); 174 | }, 175 | 176 | 'should return a CSS rgb string': function(){ 177 | value_of(['0','0','0'].hexToRgb()).should_be('rgb(0,0,0)'); 178 | }, 179 | 180 | 'should support shorthand hex': function(){ 181 | value_of(['c','c','c'].hexToRgb()).should_be('rgb(204,204,204)'); 182 | }, 183 | 184 | 'should return an array with 16-based numbers when passed true': function(){ 185 | value_of(['ff','ff','ff'].hexToRgb(true)).should_be([255,255,255]); 186 | }, 187 | 188 | // Array.rgbToHex 189 | 190 | 'should return null if the array does not have at least 3 times': function(){ 191 | value_of([0,1].rgbToHex()).should_be_null(); 192 | }, 193 | 194 | 'should return a css hexadecimal string': function(){ 195 | value_of(['255', '0', '0'].rgbToHex()).should_be('#ff0000'); 196 | value_of([0,0,255].rgbToHex()).should_be('#0000ff'); 197 | }, 198 | 199 | 'should return an array with hexadecimal string items': function(){ 200 | value_of([0,255,0].rgbToHex(true)).should_be(['00', 'ff', '00']); 201 | }, 202 | 203 | 'should return `transparent` if the fourth item is 0 and first param is not true': function(){ 204 | value_of([0,0,0,0].rgbToHex()).should_be('transparent'); 205 | } 206 | 207 | }); -------------------------------------------------------------------------------- /1.2public/Native/String.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: String.js 3 | Specs for String.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe("String Methods", { 10 | 11 | // String.capitalize 12 | 13 | 'should capitalize each word': function(){ 14 | value_of('i like cookies'.capitalize()).should_be('I Like Cookies'); 15 | value_of('I Like cOOKIES'.capitalize()).should_be('I Like COOKIES'); 16 | }, 17 | 18 | // String.camelCase 19 | 20 | 'should convert a hyphenated string into a camel cased string': function(){ 21 | value_of('i-like-cookies'.camelCase()).should_be('iLikeCookies'); 22 | value_of('I-Like-Cookies'.camelCase()).should_be('ILikeCookies'); 23 | }, 24 | 25 | // String.hyphenate 26 | 27 | 'should convert a camel cased string into a hyphenated string': function(){ 28 | value_of('iLikeCookies'.hyphenate()).should_be('i-like-cookies'); 29 | value_of('ILikeCookies'.hyphenate()).should_be('-i-like-cookies'); 30 | }, 31 | 32 | // String.clean 33 | 34 | 'should clean all extraneous whitespace from the string': function(){ 35 | value_of(' i like cookies '.clean()).should_be("i like cookies"); 36 | value_of(' i\nlike \n cookies \n\t '.clean()).should_be("i like cookies"); 37 | }, 38 | 39 | // String.trim 40 | 41 | 'should trim left and right whitespace from the string': function(){ 42 | value_of(' i like cookies '.trim()).should_be('i like cookies'); 43 | value_of(' i \tlike cookies '.trim()).should_be('i \tlike cookies'); 44 | }, 45 | 46 | // String.contains 47 | 48 | 'should return true if the string contains a string otherwise false': function(){ 49 | value_of('i like cookies'.contains('cookies')).should_be_true(); 50 | value_of('i,like,cookies'.contains('cookies')).should_be_true(); 51 | value_of('mootools'.contains('inefficient javascript')).should_be_false(); 52 | }, 53 | 54 | 'should return true if the string constains the string and separator otherwise false': function(){ 55 | value_of('i like cookies'.contains('cookies', ' ')).should_be_true(); 56 | value_of('i like cookies'.contains('cookies', ',')).should_be_false(); 57 | 58 | value_of('i,like,cookies'.contains('cookies', ' ')).should_be_false(); 59 | value_of('i,like,cookies'.contains('cookies', ',')).should_be_true(); 60 | }, 61 | 62 | // String.test 63 | 64 | 'should return true if the test matches the string otherwise false': function(){ 65 | value_of('i like teh cookies'.test('cookies')).should_be_true(); 66 | value_of('i like cookies'.test('ke coo')).should_be_true(); 67 | value_of('I LIKE COOKIES'.test('cookie', 'i')).should_be_true(); 68 | value_of('i like cookies'.test('cookiez')).should_be_false(); 69 | }, 70 | 71 | 'should return true if the regular expression test matches the string otherwise false': function(){ 72 | value_of('i like cookies'.test(/like/)).should_be_true(); 73 | value_of('i like cookies'.test(/^l/)).should_be_false(); 74 | }, 75 | 76 | // String.toInt 77 | 78 | 'should convert the string into an integer': function(){ 79 | value_of('10'.toInt()).should_be(10); 80 | value_of('10px'.toInt()).should_be(10); 81 | value_of('10.10em'.toInt()).should_be(10); 82 | }, 83 | 84 | 'should convert the string into an integer with a specific base': function(){ 85 | value_of('10'.toInt(5)).should_be(5); 86 | }, 87 | 88 | // String.toFloat 89 | 90 | 'should convert the string into a float': function(){ 91 | value_of('10.11'.toFloat()).should_be(10.11); 92 | value_of('10.55px'.toFloat()).should_be(10.55); 93 | }, 94 | 95 | // String.rgbToHex 96 | 97 | 'should convert the string into a CSS hex string': function(){ 98 | value_of('rgb(255,255,255)'.rgbToHex()).should_be('#ffffff'); 99 | value_of('rgb(255,255,255,0)'.rgbToHex()).should_be('transparent'); 100 | }, 101 | 102 | // String.hexToRgb 103 | 104 | 'should convert the CSS hex string into a CSS rgb string': function(){ 105 | value_of('#fff'.hexToRgb()).should_be('rgb(255,255,255)'); 106 | value_of('ff00'.hexToRgb()).should_be('rgb(255,0,0)'); 107 | value_of('#000000'.hexToRgb()).should_be('rgb(0,0,0)'); 108 | }, 109 | 110 | // String.stripScripts 111 | 112 | 'should strip all script tags from a string': function(){ 113 | value_of('
'.stripScripts()).should_be('
'); 114 | }, 115 | 116 | 'should execute the stripped tags from the string': function(){ 117 | value_of('
'.stripScripts(true)).should_be('
'); 118 | value_of(window.stripScriptsSpec).should_be(42); 119 | value_of('
'.stripScripts(true)).should_be('
'); 120 | value_of(window.stripScriptsSpec).should_be(24); 121 | value_of('
'.stripScripts(true)).should_be('
'); 122 | value_of(window.stripScriptsSpec).should_be(4242); 123 | }, 124 | 125 | // String.substitute 126 | 127 | 'should substitute values from objects': function(){ 128 | value_of('This is {color}.'.substitute({'color': 'blue'})).should_be('This is blue.'); 129 | value_of('This is {color} and {size}.'.substitute({'color': 'blue', 'size': 'small'})).should_be('This is blue and small.'); 130 | }, 131 | 132 | 'should substitute values from arrays': function(){ 133 | value_of('This is {0}.'.substitute(['blue'])).should_be('This is blue.'); 134 | value_of('This is {0} and {1}.'.substitute(['blue', 'small'])).should_be('This is blue and small.'); 135 | }, 136 | 137 | 'should remove undefined values': function(){ 138 | value_of('Checking {0}, {1}, {2}, {3} and {4}.'.substitute([1, 0, undefined, null])).should_be('Checking 1, 0, , and .'); 139 | value_of('This is {not-set}.'.substitute({})).should_be('This is .'); 140 | }, 141 | 142 | 'should ignore escaped placeholders': function(){ 143 | value_of('Ignore \\{this} but not {that}.'.substitute({'that': 'the others'})).should_be('Ignore {this} but not the others.'); 144 | }, 145 | 146 | 'should substitute with a custom regex': function(){ 147 | var php = (/\$([\w-]+)/g); 148 | value_of('I feel so $language.'.substitute({'language': 'PHP'}, php)).should_be('I feel so PHP.'); 149 | var ror = (/#\{([^}]+)\}/g); 150 | value_of('I feel so #{language}.'.substitute({'language': 'RoR'}, ror)).should_be('I feel so RoR.'); 151 | }, 152 | 153 | 'should substitute without goofing up nested curly braces': function(){ 154 | value_of("fred {is {not} very} cool".substitute({ 'is {not':'BROKEN' })).should_not_be("fred BROKEN very} cool"); 155 | value_of('this {should {break} mo} betta'.substitute({ 'break':'work' })).should_be('this {should work mo} betta'); 156 | } 157 | 158 | }); 159 | -------------------------------------------------------------------------------- /1.2public/Native/Hash.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Hash.js 3 | Specs for Hash.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | (function(){ 10 | 11 | var hash2 = new Hash({ a: 'string', b: 233, c: {} }); 12 | 13 | describe("Hash Methods", { 14 | 15 | // Hash.constructor 16 | 17 | 'should return a new hash': function(){ 18 | value_of(Hash.type(new Hash())).should_be_true(); 19 | }, 20 | 21 | 'should return a copy of a hash': function(){ 22 | var hash = new Hash({a: 1, b: 2, c: 3}); 23 | var copy = new Hash(hash); 24 | value_of(copy !== hash).should_be_true(); 25 | value_of(copy).should_be(hash); 26 | }, 27 | 28 | // Hash.erase 29 | 30 | 'should remove a key and its value from the hash': function(){ 31 | var hash = new Hash({a: 1, b: 2, c: 3}); 32 | value_of(hash.erase('a')).should_be(new Hash({b:2,c:3})); 33 | value_of(hash.erase('d')).should_be(new Hash({b:2,c:3})); 34 | 35 | hash = new Hash({a: 1, b: 2, c: 3}); 36 | value_of(hash.erase('a')).should_be(new Hash({b:2,c:3})); 37 | value_of(hash.erase('d')).should_be(new Hash({b:2,c:3})); 38 | }, 39 | 40 | // Hash.get 41 | 42 | 'should return the value corresponding to the specified key otherwise null': function(){ 43 | var hash = new Hash({a: 1, b: 2, c: 3}); 44 | value_of(hash.get('c')).should_be(3); 45 | value_of(hash.get('d')).should_be_null(); 46 | }, 47 | 48 | // Hash.set 49 | 50 | 'should set the key with the corresponding value': function(){ 51 | var myHash = new Hash({a: 1, b: 2, c: 3}).set('c', 7).set('d', 8); 52 | value_of(myHash).should_be(new Hash({a:1,b:2,c:7,d:8})); 53 | }, 54 | 55 | // Hash.empty 56 | 57 | 'should empty the hash': function(){ 58 | var hash = new Hash({a: 1, b: 2, c: 3}); 59 | value_of(hash.empty()).should_be(new Hash()); 60 | }, 61 | 62 | // Hash.include 63 | 64 | 'should include a key value if the hash does not have the key otherwise ignore': function(){ 65 | var hash = new Hash({a: 1, b: 2, c: 3}); 66 | value_of(hash.include('e', 7)).should_be(new Hash({a:1,b:2,c:3,e:7})); 67 | value_of(hash.include('a', 7)).should_be(new Hash({a:1,b:2,c:3,e:7})); 68 | }, 69 | 70 | // Hash.keyOf | Hash.indexOf 71 | 72 | 'should return the key of the value or null if not found': function(){ 73 | var hash = new Hash({a: 1, b: 2, c: 3, d: 1}); 74 | value_of(hash.keyOf(1)).should_be('a'); 75 | value_of(hash.keyOf('not found')).should_be_null(); 76 | 77 | value_of(hash.indexOf(1)).should_be('a'); 78 | value_of(hash.indexOf('not found')).should_be_null(); 79 | }, 80 | 81 | // Hash.has 82 | 83 | 'should return true if the hash has the key otherwise false': function(){ 84 | var hash = new Hash({a: 1, b: 2, c: 3}); 85 | value_of(hash.has('a')).should_be_true(); 86 | value_of(hash.has('d')).should_be_false(); 87 | }, 88 | 89 | // Hash.hasValue | Hash.contains 90 | 91 | 'should return true if the hash hasValue otherwise false': function(){ 92 | var hash = new Hash({a: 1, b: 2, c: 3}); 93 | value_of(hash.hasValue(1)).should_be_true(); 94 | value_of(hash.hasValue('not found')).should_be_false(); 95 | 96 | value_of(hash.contains(1)).should_be_true(); 97 | value_of(hash.contains('not found')).should_be_false(); 98 | }, 99 | 100 | // Hash.getClean 101 | 102 | 'should getClean JavaScript object': function(){ 103 | var hash = new Hash({a: 1, b: 2, c: 3}); 104 | value_of(hash.getClean()).should_be({a:1,b:2,c:3}); 105 | }, 106 | 107 | // Hash.extend 108 | 109 | 'should extend a Hash with an object': function(){ 110 | var hash = new Hash({a: 1, b: 2, c: 3}); 111 | value_of(hash.extend({a:4,d:7,e:8})).should_be(new Hash({a:4,b:2,c:3,d:7,e:8})); 112 | }, 113 | 114 | 'should extend a Hash with another Hash': function(){ 115 | var hash = new Hash({a: 1, b: 2, c: 3}); 116 | value_of(hash.extend(new Hash({a:4,d:7,e:8}))).should_be(new Hash({a:4,b:2,c:3,d:7,e:8})); 117 | }, 118 | 119 | // Hash.combine 120 | 121 | 'should merge a Hash with an object': function(){ 122 | var hash = new Hash({a: 1, b: 2, c: 3}); 123 | value_of(hash.combine({a:4,d:7,e:8})).should_be(new Hash({a:1,b:2,c:3,d:7,e:8})); 124 | }, 125 | 126 | 'should merge a Hash with another Hash': function(){ 127 | var hash = new Hash({a: 1, b: 2, c: 3}); 128 | value_of(hash.combine(new Hash({a:4,d:7,e:8}))).should_be(new Hash({a:1,b:2,c:3,d:7,e:8})); 129 | }, 130 | 131 | // Hash.each 132 | 133 | 'should iterate through each property': function(){ 134 | var newHash = new Hash(); 135 | var hash = new Hash({a: 1, b: 2, c: 3}); 136 | hash.each(function(value, key){ 137 | newHash.set(key, value); 138 | }); 139 | value_of(newHash).should_be(hash); 140 | }, 141 | 142 | // Hash.map 143 | 144 | 'should map a new Hash according to the comparator': function(){ 145 | value_of(hash2.map(Number.type)).should_be(new Hash({a:false,b:true,c:false})); 146 | }, 147 | 148 | // Hash.filter 149 | 150 | 'should filter the Hash according to the comparator': function(){ 151 | value_of(hash2.filter(Number.type)).should_be(new Hash({b:233})); 152 | }, 153 | 154 | // Hash.every 155 | 156 | 'should return true if every value matches the comparator, otherwise false': function(){ 157 | value_of(hash2.every($defined)).should_be_true(); 158 | value_of(hash2.every(Number.type)).should_be_false(); 159 | }, 160 | 161 | // Hash.some 162 | 163 | 'should return true if some of the values match the comparator, otherwise false': function(){ 164 | value_of(hash2.some(Number.type)).should_be_true(); 165 | value_of(hash2.some(Array.type)).should_be_false(); 166 | }, 167 | 168 | // Hash.getKeys 169 | 170 | 'getKeys should return an empty array': function(){ 171 | value_of(new Hash().getKeys()).should_be([]); 172 | }, 173 | 174 | 'should return an array containing the keys of the hash': function(){ 175 | value_of(hash2.getKeys()).should_be(['a', 'b', 'c']); 176 | }, 177 | 178 | // Hash.getValues 179 | 180 | 'getValues should return an empty array': function(){ 181 | value_of(new Hash().getValues()).should_be([]); 182 | }, 183 | 184 | 'should return an array with the values of the hash': function(){ 185 | value_of(hash2.getValues()).should_be(['string', 233, {}]); 186 | }, 187 | 188 | // Hash.toQueryString 189 | 190 | 'should return a query string': function(){ 191 | var myHash = new Hash({apple: "red", lemon: "yellow"}); 192 | value_of(myHash.toQueryString()).should_be('apple=red&lemon=yellow'); 193 | 194 | var myHash2 = new Hash({apple: ['red', 'yellow'], lemon: ['green', 'yellow']}); 195 | value_of(myHash2.toQueryString()).should_be('apple[0]=red&apple[1]=yellow&lemon[0]=green&lemon[1]=yellow'); 196 | 197 | var myHash3 = new Hash({fruits: {apple: ['red', 'yellow'], lemon: ['green', 'yellow']}}); 198 | value_of(myHash3.toQueryString()).should_be('fruits[apple][0]=red&fruits[apple][1]=yellow&fruits[lemon][0]=green&fruits[lemon][1]=yellow'); 199 | } 200 | 201 | }); 202 | 203 | })(); -------------------------------------------------------------------------------- /1.2public/Class/Class.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Class.js 3 | Specs for Class.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | (function(){ 10 | 11 | var Animal = new Class({ 12 | 13 | initialized: false, 14 | 15 | initialize: function(name, sound){ 16 | this.name = name; 17 | this.sound = sound || ''; 18 | this.initialized = true; 19 | }, 20 | 21 | eat: function(){ 22 | return 'animal:eat:' + this.name; 23 | }, 24 | 25 | say: function(){ 26 | return 'animal:say:' + this.name; 27 | } 28 | 29 | }); 30 | 31 | var Cat = new Class({ 32 | 33 | Extends: Animal, 34 | 35 | ferocious: false, 36 | 37 | initialize: function(name, sound){ 38 | this.parent(name, sound || 'miao'); 39 | }, 40 | 41 | eat: function(){ 42 | return 'cat:eat:' + this.name; 43 | }, 44 | 45 | play: function(){ 46 | return 'cat:play:' + this.name; 47 | } 48 | 49 | }); 50 | 51 | var Lion = new Class({ 52 | 53 | Extends: Cat, 54 | 55 | ferocious: true, 56 | 57 | initialize: function(name){ 58 | this.parent(name, 'rarr'); 59 | }, 60 | 61 | eat: function(){ 62 | return 'lion:eat:' + this.name; 63 | } 64 | 65 | }); 66 | 67 | var Actions = new Class({ 68 | 69 | jump: function(){ 70 | return 'actions:jump:' + this.name; 71 | }, 72 | 73 | sleep: function(){ 74 | return 'actions:sleep:' + this.name; 75 | } 76 | 77 | }); 78 | 79 | var Attributes = new Class({ 80 | 81 | color: function(){ 82 | return 'attributes:color:' + this.name; 83 | }, 84 | 85 | size: function(){ 86 | return 'attributes:size:' + this.name; 87 | } 88 | 89 | }); 90 | 91 | 92 | describe('Class creation', { 93 | 94 | "Classes should be of type 'class'": function(){ 95 | value_of($type(Animal)).should_be('class'); 96 | value_of(Class.type(Animal)).should_be_true(); 97 | }, 98 | 99 | "should call initialize upon instantiation": function(){ 100 | var animal = new Animal('lamina'); 101 | value_of(animal.name).should_be('lamina'); 102 | value_of(animal.initialized).should_be_true(); 103 | value_of(animal.say()).should_be('animal:say:lamina'); 104 | }, 105 | 106 | "should use 'Extend' property to extend another class": function(){ 107 | var cat = new Cat('fluffy'); 108 | value_of(cat.name).should_be('fluffy'); 109 | value_of(cat.sound).should_be('miao'); 110 | value_of(cat.ferocious).should_be_false(); 111 | value_of(cat.say()).should_be('animal:say:fluffy'); 112 | value_of(cat.eat()).should_be('cat:eat:fluffy'); 113 | value_of(cat.play()).should_be('cat:play:fluffy'); 114 | }, 115 | 116 | "should use 'Extend' property to extend an extended class": function(){ 117 | var leo = new Lion('leo'); 118 | value_of(leo.name).should_be('leo'); 119 | value_of(leo.sound).should_be('rarr'); 120 | value_of(leo.ferocious).should_be_true(); 121 | value_of(leo.say()).should_be('animal:say:leo'); 122 | value_of(leo.eat()).should_be('lion:eat:leo'); 123 | value_of(leo.play()).should_be('cat:play:leo'); 124 | }, 125 | 126 | "should use 'Implements' property to implement another class": function(){ 127 | var Dog = new Class({ 128 | Implements: Animal 129 | }); 130 | 131 | var rover = new Dog('rover'); 132 | value_of(rover.name).should_be('rover'); 133 | value_of(rover.initialized).should_be_true(); 134 | value_of(rover.eat()).should_be('animal:eat:rover'); 135 | }, 136 | 137 | "should use 'Implements' property to implement any number of classes": function(){ 138 | var Dog = new Class({ 139 | Extends: Animal, 140 | Implements: [Actions, Attributes] 141 | }); 142 | 143 | var rover = new Dog('rover'); 144 | value_of(rover.initialized).should_be_true(); 145 | value_of(rover.eat()).should_be('animal:eat:rover'); 146 | value_of(rover.say()).should_be('animal:say:rover'); 147 | value_of(rover.jump()).should_be('actions:jump:rover'); 148 | value_of(rover.sleep()).should_be('actions:sleep:rover'); 149 | value_of(rover.size()).should_be('attributes:size:rover'); 150 | value_of(rover.color()).should_be('attributes:color:rover'); 151 | }, 152 | 153 | "should alter the Class's prototype when implementing new methods": function(){ 154 | var Dog = new Class({ 155 | Extends: Animal 156 | }); 157 | 158 | var rover = new Dog('rover'); 159 | 160 | Dog.implement({ 161 | jump: function(){ 162 | return 'dog:jump:' + this.name; 163 | } 164 | }); 165 | 166 | var spot = new Dog('spot'); 167 | 168 | value_of(spot.jump()).should_be('dog:jump:spot'); 169 | value_of(rover.jump()).should_be('dog:jump:rover'); 170 | }, 171 | 172 | "should alter the Class's prototype when implementing new methods into the super class": function(){ 173 | var Dog = new Class({ 174 | Extends: Animal 175 | }); 176 | 177 | var rover = new Dog('rover'); 178 | 179 | Animal.implement({ 180 | jump: function(){ 181 | return 'animal:jump:' + this.name; 182 | } 183 | }); 184 | 185 | var spot = new Dog('spot'); 186 | 187 | value_of(spot.jump()).should_be('animal:jump:spot'); 188 | value_of(rover.jump()).should_be('animal:jump:rover'); 189 | }, 190 | 191 | "should alter the Class's prototype when overwriting methods in the super class": function(){ 192 | var Dog = new Class({ 193 | Extends: Animal 194 | }); 195 | 196 | var rover = new Dog('rover'); 197 | value_of(rover.say()).should_be('animal:say:rover'); 198 | 199 | Animal.implement({ 200 | say: function(){ 201 | return 'NEW:animal:say:' + this.name; 202 | } 203 | }); 204 | 205 | var spot = new Dog('spot'); 206 | 207 | value_of(spot.say()).should_be('NEW:animal:say:spot'); 208 | value_of(rover.say()).should_be('NEW:animal:say:rover'); 209 | } 210 | 211 | /* 212 | * TODO: 2.0 213 | "should access the proper parent when it is overwritten after instantiation": function(){ 214 | var Dog = new Class({ 215 | Extends: Animal, 216 | say: function(){ 217 | return this.parent(); 218 | } 219 | }); 220 | 221 | var rover = new Dog('rover'); 222 | value_of(rover.say()).should_be('animal:say:rover'); 223 | 224 | Animal.implement({ 225 | say: function(){ 226 | return 'NEW:animal:say:' + this.name; 227 | } 228 | }); 229 | 230 | var spot = new Dog('spot'); 231 | 232 | value_of(spot.say()).should_be('NEW:animal:say:spot'); 233 | value_of(rover.say()).should_be('NEW:animal:say:rover'); 234 | }*/ 235 | 236 | }); 237 | 238 | describe('Class::implement', { 239 | 240 | 'should implement an object': function(){ 241 | var Dog = new Class({ 242 | Extends: Animal 243 | }); 244 | 245 | Dog.implement(new Actions); 246 | 247 | var rover = new Dog('rover'); 248 | 249 | value_of(rover.name).should_be('rover'); 250 | value_of(rover.jump()).should_be('actions:jump:rover'); 251 | value_of(rover.sleep()).should_be('actions:sleep:rover'); 252 | }, 253 | 254 | 'should implement any number of objects': function(){ 255 | var Dog = new Class({ 256 | Extends: Animal 257 | }); 258 | 259 | Dog.implement(new Actions).implement(new Attributes); 260 | 261 | var rover = new Dog('rover'); 262 | 263 | value_of(rover.name).should_be('rover'); 264 | value_of(rover.jump()).should_be('actions:jump:rover'); 265 | value_of(rover.sleep()).should_be('actions:sleep:rover'); 266 | value_of(rover.size()).should_be('attributes:size:rover'); 267 | value_of(rover.color()).should_be('attributes:color:rover'); 268 | } 269 | 270 | }); 271 | 272 | })(); -------------------------------------------------------------------------------- /SlickSpec/moobugger/debugger.js: -------------------------------------------------------------------------------- 1 | var Moo = { 2 | 3 | defined: function(obj){ 4 | return (obj != undefined); 5 | }, 6 | 7 | type: function(obj){ 8 | if (obj == null) return false; 9 | if (!Moo.defined(obj)) return false; 10 | var type = typeof obj; 11 | if (type == 'object' && obj.nodeName){ 12 | switch(obj.nodeType){ 13 | case 1: return 'element'; 14 | case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace'; 15 | } 16 | } 17 | if (type == 'object' || type == 'function'){ 18 | switch(obj.constructor){ 19 | case Array: return 'array'; 20 | case RegExp: return 'regexp'; 21 | } 22 | if (typeof obj.length == 'number'){ 23 | if (obj.item) return 'collection'; 24 | if (obj.callee) return 'arguments'; 25 | } 26 | } 27 | return type; 28 | } 29 | 30 | }; 31 | 32 | Moo.Client = { 33 | Engine: {'name': 'unknown', 'version': ''}, 34 | Platform: {}, 35 | Features: {} 36 | }; 37 | Moo.Client.Features.xhr = !!(window.XMLHttpRequest); 38 | Moo.Client.Features.xpath = !!(document.evaluate); 39 | 40 | if (window.opera) Moo.Client.Engine.name = 'opera'; 41 | else if (window.ActiveXObject) Moo.Client.Engine = {'name': 'ie', 'version': (Moo.Client.Features.xhr) ? 7 : 6}; 42 | else if (!navigator.taintEnabled) Moo.Client.Engine = {'name': 'webkit', 'version': (Moo.Client.Features.xpath) ? 420 : 419}; 43 | else if (document.getBoxObjectFor != null) Moo.Client.Engine.name = 'gecko'; 44 | Moo.Client.Engine[Moo.Client.Engine.name] = Moo.Client.Engine[Moo.Client.Engine.name + Moo.Client.Engine.version] = true; 45 | 46 | Moo.Client.Platform.name = navigator.platform.match(/(mac)|(win)|(linux)|(nix)/i) || ['Other']; 47 | Moo.Client.Platform.name = Moo.Client.Platform.name[0].toLowerCase(); 48 | Moo.Client.Platform[Moo.Client.Platform.name] = true; 49 | 50 | Moo.ViewPort = { 51 | 52 | getWidth: function(){ 53 | if (Moo.Client.Engine.webkit419) return window.innerWidth; 54 | if (Moo.Client.Engine.opera) return document.body.clientWidth; 55 | return document.documentElement.clientWidth; 56 | }, 57 | 58 | getHeight: function(){ 59 | if (Moo.Client.Engine.webkit419) return window.innerHeight; 60 | if (Moo.Client.Engine.opera) return document.body.clientHeight; 61 | return document.documentElement.clientHeight; 62 | }, 63 | 64 | getScrollWidth: function(){ 65 | if (Moo.Client.Engine.ie) return Math.max(document.documentElement.offsetWidth, document.documentElement.scrollWidth); 66 | if (Moo.Client.Engine.webkit) return document.body.scrollWidth; 67 | return document.documentElement.scrollWidth; 68 | }, 69 | 70 | getScrollHeight: function(){ 71 | if (Moo.Client.Engine.ie) return Math.max(document.documentElement.offsetHeight, document.documentElement.scrollHeight); 72 | if (Moo.Client.Engine.webkit) return document.body.scrollHeight; 73 | return document.documentElement.scrollHeight; 74 | }, 75 | 76 | getScrollLeft: function(){ 77 | return window.pageXOffset || document.documentElement.scrollLeft; 78 | }, 79 | 80 | getScrollTop: function(){ 81 | return window.pageYOffset || document.documentElement.scrollTop; 82 | } 83 | 84 | }; 85 | 86 | Moo.Element = { 87 | 88 | addEvent: function(element, type, fn){ 89 | if (element.addEventListener) element.addEventListener(type, fn, false); 90 | else element.attachEvent('on' + type, fn); 91 | }, 92 | 93 | removeEvent: function(item, type, fn){ 94 | if (item.removeEventListener) item.removeEventListener(type, fn, false); 95 | else item.detachEvent('on' + type, fn); 96 | }, 97 | 98 | remove: function(item){ 99 | if (!item || !item.parentNode) return; 100 | item.parentNode.removeChild(item); 101 | } 102 | 103 | }; 104 | 105 | Moo.Array = { 106 | 107 | forEach: function(items, fn, bind){ 108 | for (var i = 0, j = items.length; i < j; i++) fn.call(bind, items[i], i, items); 109 | } 110 | 111 | }; 112 | 113 | Moo.String = { 114 | 115 | contains: function(item, string, s){ 116 | return (s) ? (s + item + s).indexOf(s + string + s) > -1 : item.indexOf(string) > -1; 117 | } 118 | 119 | }; 120 | 121 | Moo.Object = { 122 | 123 | add: function(item, properties){ 124 | var i = 0; 125 | for (var property in properties){ 126 | item[property] = properties[property]; 127 | if (i++ > 10) break; 128 | } 129 | } 130 | 131 | }; 132 | 133 | var debug = debug || {}; 134 | debug.queue = []; 135 | debug.methods = ['log', 'time', 'timeEnd', 'group', 'groupEnd', 'warn', 'info', 'error']; 136 | 137 | Moo.Array.forEach(debug.methods, function(name){ 138 | debug[name] = function(){ 139 | debug.queue.push({'name': name, 'arguments': arguments}); 140 | }; 141 | }); 142 | 143 | Moo.Debugger = { 144 | 145 | load: function(){ 146 | 147 | document.documentElement.className = document.documentElement.className + ' moobugger'; 148 | 149 | debug.spacer = document.createElement('div'); 150 | debug.spacer.className = 'debug-spacer'; 151 | document.body.appendChild(debug.spacer); 152 | 153 | debug.iFrame = document.createElement('iframe'); 154 | 155 | debug.iFrame.frameBorder = 0; 156 | 157 | Moo.Object.add(debug.iFrame.style, { 158 | 'border': 'none', 159 | 'padding': 0, 160 | 'margin': 0, 161 | 'width': '100%', 162 | 'position': 'fixed', 163 | 'bottom': 0, 164 | 'left': 0, 165 | 'zIndex': 999999 166 | }); 167 | 168 | if (Moo.Client.Engine.ie) debug.iFrame.style.position = 'absolute'; 169 | 170 | debug.iFrame.id = debug.iFrame.name = 'debugger'; 171 | debug.iFrame.src = (debug.local) ? debug.path + 'debugger.html' : 'javascript:parent.debug.htmlString'; 172 | 173 | 174 | document.body.appendChild(debug.iFrame); 175 | 176 | Moo.Element.addEvent(debug.iFrame, 'load', Moo.Debugger.onFrameLoaded); 177 | }, 178 | 179 | getPath: function(){ 180 | var path = ''; 181 | Moo.Array.forEach(document.getElementsByTagName('script'), function(script){ 182 | if (!path && Moo.String.contains(script.src, '/') && Moo.String.contains(script.src, 'debugger.js')) path = script.src.substr(0, script.src.lastIndexOf('/')); 183 | }); 184 | return path + '/'; 185 | }, 186 | 187 | onFrameLoaded: function(){ 188 | debug.frame = window.frames['debugger']; 189 | 190 | Moo.Array.forEach(debug.methods, function(name){ 191 | debug[name] = debug.frame.debug[name]; 192 | }); 193 | 194 | Moo.Element.addEvent(window, 'resize', Moo.Debugger.reposition); 195 | Moo.Element.addEvent(window, 'scroll', Moo.Debugger.reposition); 196 | 197 | Moo.Debugger.reposition(); 198 | }, 199 | 200 | reposition: function(){ 201 | debug.spacer.style.height = debug.iFrame.offsetHeight + 'px'; 202 | var top = Moo.ViewPort.getHeight() - debug.iFrame.offsetHeight; 203 | if (top < 0) return; 204 | if (Moo.Client.Engine.ie6){ 205 | top = Moo.ViewPort.getScrollTop() + top; 206 | debug.iFrame.style.top = top + 'px'; 207 | } 208 | }, 209 | 210 | unload: function(){ 211 | debug.queue = []; 212 | document.documentElement.className = document.documentElement.className.replace(/ ?moobugger ?/,' '); 213 | Moo.Element.remove(debug.iFrame); 214 | Moo.Element.remove(debug.spacer); 215 | Moo.Element.remove(document.getElementById('debug-bookmarklet')); 216 | }, 217 | 218 | evaluate: function(value){ 219 | try { 220 | var evaluation = value; 221 | if (typeof value == 'string') 222 | evaluation = eval(value); 223 | if (evaluation !== debug.frame.debug.$nil){ 224 | if (evaluation == window) evaluation = {'window': '[native code]'}; 225 | if (evaluation.nodeType === 9) evaluation = {'document': '[native code]'}; 226 | debug.frame.debug.$parse([evaluation]); 227 | } 228 | } catch(err){ 229 | debug.frame.debug.error(err); 230 | } 231 | } 232 | 233 | }; 234 | 235 | window.$ = window.$ || function(id){ 236 | return document.getElementById(id); 237 | }; 238 | 239 | window.$$ = window.$$ || function(tag){ 240 | return document.getElementsByTagName(tag); 241 | }; 242 | 243 | if (!debug.path) debug.local = true; 244 | 245 | debug.path = debug.path || Moo.Debugger.getPath(); 246 | 247 | debug.htmlString = ' \ 248 | \ 249 | \ 250 | \ 251 | debugger \ 252 | \ 253 | \ 254 | \ 255 | \ 256 | \ 257 |
\ 258 |
\ 259 | \ 260 | \ 261 | \ 262 | \ 263 | \ 264 |
\ 265 |
\ 266 |
\ 267 | \ 268 |
\ 269 |
\ 270 | \ 271 | '; 272 | 273 | if (!window.console || !console.group){ 274 | window.console = debug; 275 | if (!debug.local) Moo.Debugger.load(); 276 | else Moo.Element.addEvent(window, 'load', Moo.Debugger.load); 277 | } 278 | 279 | if (!window.onerror) 280 | window.onerror = function(error, url, line){ 281 | console.error({error:error, url:url, line:line}); 282 | return true; 283 | } 284 | 285 | -------------------------------------------------------------------------------- /SlickSpec/slickspec.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | /* -------------------- 4 | * @Layout 5 | */ 6 | 7 | 8 | #slickspec_logo { 9 | position: absolute; 10 | top: 0px; 11 | left: 0px; 12 | z-index: 9999; 13 | padding:0.5em 0.75em; 14 | color: #fff; 15 | 16 | font-size: 20px; 17 | line-height: 20px; 18 | 19 | font-weight: normal; 20 | } 21 | #slickspec_logo a, 22 | #slickspec_logo a:link, 23 | #slickspec_logo a:visited { 24 | color: #fff; 25 | text-decoration: none; 26 | } 27 | #slickspec_logo a:hover { 28 | color: #000; 29 | } 30 | 31 | #jsspec_container{padding-top:3em !important;} 32 | #framework_name { 33 | position: absolute; 34 | top: 42px; 35 | right: 0px; 36 | z-index: 1; 37 | padding:0.5em 0.75em; 38 | color: #000; 39 | 40 | font-size: 20px; 41 | line-height: 20px; 42 | 43 | font-weight: normal; 44 | } 45 | #framework_name a, 46 | #framework_name a:link, 47 | #framework_name a:visited { 48 | color: #fff; 49 | text-decoration: none; 50 | } 51 | #framework_name a:hover { 52 | color: #000; 53 | } 54 | 55 | 56 | #copyright{ 57 | position: fixed; 58 | bottom: 0px; 59 | right: 0px; 60 | z-index: 1000; 61 | font-size: 10px; 62 | padding:0.5em 0.75em; 63 | background-color: #fff; 64 | background-color: rgba(255,255,255,0.9); 65 | 66 | -webkit-border-top-left-radius: 6px; 67 | } 68 | * html #copyright { 69 | position: absolute; 70 | bottom: auto; 71 | top: expression(eval(document.documentElement.scrollTop+document.documentElement.clientHeight-this.clientHeight)); 72 | } 73 | html.moobugger #copyright{ 74 | bottom: 180px; 75 | } 76 | 77 | 78 | html { 79 | overflow-x: hidden; 80 | overflow-y: scroll; 81 | } 82 | 83 | iframe {display:none;} 84 | iframe#debugger {display:block !important;} 85 | iframe#debugger{ position: fixed !important; bottom: 0; top: auto; } 86 | * html iframe#debugger{position:absolute !important;bottom:auto;top:expression(eval(document.documentElement.scrollTop+document.documentElement.clientHeight-this.clientHeight));} 87 | 88 | .debug-spacer {display:none;} 89 | 90 | /*Make position:fixed work in IE6!*/ 91 | 92 | * html,* html body /* IE6 Fixed Position Jitter Fix */ {background-image:url(about:blank);background-attachment:fixed;} 93 | 94 | #slickspec_logo, 95 | .fixed-top /* position fixed Top */ {position: fixed ; bottom: auto ; top: 0px ; } 96 | * html #slickspec_logo, 97 | * html .fixed-top /* IE6 position fixed Top */ {position: absolute ; bottom: auto ; top: expression(eval(document.documentElement.scrollTop)) ; } 98 | .fixed-bottom /* position fixed Bottom */ {position: fixed ; bottom: 0px ; top: auto ; } 99 | * html .fixed-bottom /* IE6 position fixed Bottom */ {position: absolute ; bottom: auto ; top: expression(eval(document.documentElement.scrollTop+document.documentElement.clientHeight-this.clientHeight)) ; } 100 | .fixed-left /* position fixed Left */ {position: fixed ; right: auto ; left: 0px ; } 101 | * html .fixed-left /* IE6 position fixed Left */ {position: absolute ; right: auto ; left: expression(eval(document.documentElement.scrollLeft)) ; } 102 | .fixed-right /* position fixed right */ {position: fixed ; right: 0px ; left: auto ; } 103 | * html .fixed-right /* IE6 position fixed right */ {position: absolute ; right: auto ; left: expression(eval(document.documentElement.scrollWidth-this.clientWidth)) ; } 104 | 105 | 106 | 107 | body, #jsspec_container { 108 | padding: 0; 109 | margin: 0; 110 | background-color: #FFFFFF; 111 | } 112 | 113 | #jsspec_container { 114 | padding-bottom:180px; 115 | } 116 | 117 | #title { 118 | padding: 0 !important; 119 | margin: 0 !important; 120 | height: 40px !important; 121 | overflow: hidden !important; 122 | 123 | position: fixed; 124 | _position: absolute; 125 | top: 0px; 126 | _top: expression(eval(document.documentElement.scrollTop)); 127 | left: 0px; 128 | _left: expression(eval(document.documentElement.scrollLeft)); 129 | width: 100%; 130 | _width: expression(eval(document.documentElement.clientWidth)); 131 | 132 | z-index: 1000; 133 | } 134 | 135 | #title { 136 | border-bottom:2px solid #fff; 137 | } 138 | 139 | #list { 140 | overflow: auto; 141 | 142 | position: fixed; 143 | top: 0px; 144 | left: 0px; 145 | bottom: 0px; 146 | background: #eee; 147 | 148 | margin-top:42px; 149 | 150 | width: 250px; 151 | } 152 | * html #list{ 153 | position: absolute; 154 | bottom: auto; 155 | top: expression(eval(document.documentElement.scrollTop)); 156 | height: expression(eval(document.documentElement.clientHeight - 42)); 157 | } 158 | html.moobugger #list{ 159 | bottom: 180px; 160 | } 161 | * html.moobugger #list{ 162 | bottom: auto; 163 | height: expression(eval(document.documentElement.clientHeight - 42 - 180)); 164 | } 165 | 166 | #log { 167 | position:relative; 168 | overflow: hidden; 169 | 170 | padding: 0; 171 | margin: 0; 172 | margin-top: 42px; 173 | margin-left: 250px; 174 | } 175 | 176 | /*iPhone*/ 177 | @media only screen and (max-device-width: 480px) { 178 | #list { 179 | overflow: visible; 180 | } 181 | 182 | #log { 183 | overflow: visible; 184 | } 185 | } 186 | 187 | 188 | /* -------------------- 189 | * @Decorations and colors 190 | */ 191 | * { 192 | padding: 0; 193 | margin: 0; 194 | font-family: "Lucida Grande", Helvetica, sans-serif; 195 | } 196 | 197 | li { 198 | list-style: none; 199 | } 200 | 201 | /* hiding subtitles */ 202 | h2 { 203 | display: none; 204 | } 205 | 206 | /* title section */ 207 | div#title { 208 | padding: 0em 0.5em; 209 | } 210 | 211 | div#title h1 { 212 | float: right; 213 | padding: 0.25em 0.75em; 214 | font-size: 16px; 215 | } 216 | div#title h1:before{ 217 | content:"powered by "; 218 | font-size: 9px; 219 | font-style: italic; 220 | color:#fff; 221 | } 222 | 223 | div#title ul { 224 | position: absolute; 225 | top: 0px; 226 | left: 0px; 227 | margin-left:120px; 228 | } 229 | div#title ul li { 230 | font-size: 12px; 231 | float: left; 232 | padding: 0.75em 0em 0.5em 0.75em; 233 | max-width: 14em; 234 | } 235 | 236 | div#title p { 237 | float:right; 238 | padding: 0.5em 0em 0.5em 0.75em; 239 | font-size: 10px; 240 | } 241 | 242 | /* spec container */ 243 | ul.specs { 244 | margin: 0.5em; 245 | } 246 | ul.specs li { 247 | margin-bottom: 0.1em; 248 | position:relative; 249 | } 250 | 251 | /* spec title */ 252 | ul.specs li h3 { 253 | font-weight: bold; 254 | font-size: 11px; 255 | line-height: 13px; 256 | padding: 0.5em 0.75em; 257 | cursor: pointer; 258 | _cursor: hand; 259 | } 260 | ul.specs li h3 a { 261 | text-decoration: none; 262 | } 263 | ul.specs li h3 a:hover { 264 | text-decoration: underline; 265 | } 266 | 267 | /* example container */ 268 | ul.examples li { 269 | background-color: #eee; 270 | color: #999; 271 | 272 | border-style: solid; 273 | border-width: 0px 0px 1px 5px; 274 | margin: 0.2em 0em 0.2em 1em; 275 | } 276 | 277 | /* example title */ 278 | ul.examples li h4 { 279 | font-weight: normal; 280 | font-size: 12px; 281 | margin-left: 1em; 282 | } 283 | 284 | pre.examples-code { 285 | background: #FFFFFF; 286 | border: solid 1px #CCC; 287 | 288 | margin: 0.5em 2em; 289 | } 290 | pre.examples-code code { 291 | display:block; 292 | 293 | padding: 0.5em; 294 | _padding-right: 0; 295 | _padding-left: 0; 296 | 297 | white-space: pre; 298 | _width:100%; 299 | 300 | font-size: 10px; 301 | font-family: "Panic Sans", "Monaco", monospace !important; 302 | overflow-x: auto; 303 | 304 | scrollbar-base-color: #FFF; 305 | scrollbar-arrow-color: #FFF; 306 | scrollbar-track-color: #FFF; 307 | scrollbar-3dlight-color: #eee; 308 | scrollbar-highlight-color: #eee; 309 | scrollbar-face-color: #eee; 310 | scrollbar-shadow-color: #eee; 311 | scrollbar-darkshadow-color: #FFF; 312 | } 313 | 314 | /* example explaination */ 315 | ul.examples li div { 316 | padding: 1em 2em; 317 | font-size: 11px; 318 | } 319 | 320 | /* styles for ongoing, success, failure, error */ 321 | ul.specs li h3, ul.specs li h3 a { color: #666; background-color: #fff; } 322 | 323 | div.success, div.success a { color: #FFFFFF; background-color: #6C6; } 324 | div.exception, div.exception a { color: #FFFFFF; background-color: #C30; } 325 | 326 | ul.specs li.success h3, ul.specs li.success h3 a { color: #FFFFFF; background-color: #6C6; } 327 | ul.examples li.success, ul.examples li.success a { color: #363; background-color: #CFC; border-color: #6C6; } 328 | 329 | ul.specs li.exception h3, ul.specs li.exception h3 a { color: #FFFFFF; background-color: #C30; } 330 | ul.examples li.exception, ul.examples li.exception a { color: #C30; background-color: #FFC; border-color: #C30; } 331 | div.ongoing, div.ongoing a { color: #330; background-color: #FF9; } 332 | 333 | ul.specs li.ongoing h3, ul.specs li.ongoing h3 a { color: #330; background-color: #FF9; } 334 | ul.examples li.ongoing, ul.examples li.ongoing a { color: #330; background-color: #FF9; border-color: #CC0; } 335 | 336 | 337 | 338 | /* -------------------- 339 | * values 340 | */ 341 | .number_value, .string_value, .regexp_value, .boolean_value, .dom_value { 342 | font-family: monospace; 343 | color: blue; 344 | } 345 | .object_value, .array_value { 346 | line-height: 2em; 347 | padding: 0.1em 0.2em; 348 | margin: 0.1em 0; 349 | } 350 | .date_value { 351 | font-family: monospace; 352 | color: olive; 353 | } 354 | .undefined_value, .null_value { 355 | font-style: italic; 356 | color: blue; 357 | } 358 | .dom_attr_name { 359 | } 360 | .dom_attr_value { 361 | color: red; 362 | } 363 | .dom_path { 364 | font-size: 11px; 365 | color: gray; 366 | } 367 | -------------------------------------------------------------------------------- /1.2public/Class/Class.Extras.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Class.Extras.js 3 | Public specs for Class.Extras.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | var Local = Local || {}; 10 | 11 | describe("Chain Class", { 12 | 13 | "before all": function(){ 14 | Local.Chain = new Class({ 15 | 16 | Implements: Chain 17 | 18 | }); 19 | }, 20 | 21 | "callChain should not fail when nothing was added to the chain": function(){ 22 | var chain = new Local.Chain(); 23 | chain.callChain(); 24 | }, 25 | 26 | "should pass arguments to the function and return values": function(){ 27 | var chain = new Local.Chain(); 28 | var arr = []; 29 | chain.chain(function(a, b){ 30 | var str = "0" + b + a; 31 | arr.push(str); 32 | return str; 33 | }); 34 | chain.chain(function(a, b){ 35 | var str = "1" + b + a; 36 | arr.push(str); 37 | return str; 38 | }); 39 | var ret; 40 | value_of(arr).should_be([]); 41 | ret = chain.callChain("a", "A"); 42 | value_of(ret).should_be("0Aa"); 43 | value_of(arr).should_be(["0Aa"]); 44 | 45 | ret = chain.callChain("b", "B"); 46 | value_of(ret).should_be("1Bb"); 47 | value_of(arr).should_be(["0Aa", "1Bb"]); 48 | 49 | ret = chain.callChain(); 50 | value_of(ret).should_be(false); 51 | value_of(arr).should_be(["0Aa", "1Bb"]); 52 | }, 53 | 54 | "should chain any number of functions": function(){ 55 | var chain = new Local.Chain(); 56 | var arr = []; 57 | 58 | chain.chain(function(){ 59 | arr.push(0); 60 | }, function(){ 61 | arr.push(1); 62 | }); 63 | 64 | value_of(arr).should_be([]); 65 | chain.callChain(); 66 | value_of(arr).should_be([0]); 67 | chain.chain(function(){ 68 | arr.push(2); 69 | }); 70 | chain.callChain(); 71 | value_of(arr).should_be([0, 1]); 72 | chain.callChain(); 73 | value_of(arr).should_be([0, 1, 2]); 74 | chain.callChain(); 75 | value_of(arr).should_be([0, 1, 2]); 76 | }, 77 | 78 | "should allow an array of functions": function(){ 79 | var chain = new Local.Chain(); 80 | var arr = []; 81 | 82 | chain.chain([function(){ 83 | arr.push(0); 84 | }, function(){ 85 | arr.push(1); 86 | }, function(){ 87 | arr.push(2); 88 | }]); 89 | 90 | value_of(arr).should_be([]); 91 | chain.callChain(); 92 | value_of(arr).should_be([0]); 93 | chain.callChain(); 94 | value_of(arr).should_be([0, 1]); 95 | chain.callChain(); 96 | value_of(arr).should_be([0, 1, 2]); 97 | chain.callChain(); 98 | value_of(arr).should_be([0, 1, 2]); 99 | }, 100 | 101 | "each instance should have its own chain": function(){ 102 | var foo = new Local.Chain(); 103 | var bar = new Local.Chain(); 104 | foo.val = "F"; 105 | bar.val = "B"; 106 | foo.chain(function(){ 107 | this.val += 'OO'; 108 | }); 109 | bar.chain(function(){ 110 | this.val += 'AR'; 111 | }); 112 | value_of(foo.val).should_be('F'); 113 | value_of(bar.val).should_be('B'); 114 | foo.callChain(); 115 | bar.callChain(); 116 | value_of(foo.val).should_be('FOO'); 117 | value_of(bar.val).should_be('BAR'); 118 | }, 119 | 120 | "should be able to clear the chain": function(){ 121 | var called; 122 | var fn = function(){ 123 | called = true; 124 | }; 125 | 126 | var chain = new Local.Chain(); 127 | chain.chain(fn, fn, fn, fn); 128 | 129 | chain.callChain(); 130 | value_of(called).should_be_true(); 131 | called = false; 132 | 133 | chain.clearChain(); 134 | 135 | chain.callChain(); 136 | value_of(called).should_be_false(); 137 | called = false; 138 | }, 139 | 140 | "should be able to clear the chain from within": function(){ 141 | var foo = new Local.Chain(); 142 | 143 | var test = 0; 144 | foo.chain(function(){ 145 | test++; 146 | foo.clearChain(); 147 | }).chain(function(){ 148 | test++; 149 | }).callChain(); 150 | 151 | value_of(test).should_be(1); 152 | } 153 | 154 | }); 155 | 156 | Hash.each({ 157 | 158 | element: function(){ 159 | return new Element('div'); 160 | }, 161 | 162 | mixin: function(){ 163 | return new Events(); 164 | } 165 | 166 | }, function(createObject, type){ 167 | describe('Events API: ' + type.capitalize(), { 168 | 169 | 'before each': function(){ 170 | Local.called = 0; 171 | Local.fn = function(){ 172 | return Local.called++; 173 | }; 174 | }, 175 | 176 | 'should add an Event to the Class': function(){ 177 | var object = createObject(); 178 | 179 | object.addEvent('event', Local.fn).fireEvent('event'); 180 | 181 | value_of(Local.called).should_be(1); 182 | }, 183 | 184 | 'should add multiple Events to the Class': function(){ 185 | createObject().addEvents({ 186 | event1: Local.fn, 187 | event2: Local.fn 188 | }).fireEvent('event1').fireEvent('event2'); 189 | 190 | value_of(Local.called).should_be(2); 191 | }, 192 | 193 | // TODO 2.0only 194 | /*'should be able to remove event during firing': function(){ 195 | createObject().addEvent('event', Local.fn).addEvent('event', function(){ 196 | Local.fn(); 197 | this.removeEvent('event', arguments.callee); 198 | }).addEvent('event', function(){ Local.fn(); }).fireEvent('event').fireEvent('event'); 199 | 200 | value_of(Local.called).should_be(5); 201 | },*/ 202 | 203 | 'should add a protected event': function(){ 204 | var object = createObject(); 205 | 206 | //TODO 2.0; 1.2 intentionally has a different API 207 | if (type == 'element'){ 208 | value_of(1).should_be(1); 209 | return; 210 | } 211 | 212 | var protectedFn = (function(){ Local.fn(); }); 213 | 214 | object.addEvent('protected', protectedFn, true).removeEvent('protected', protectedFn).fireEvent('protected'); 215 | 216 | value_of(Local.called).should_be(1); 217 | }, 218 | 219 | 'should remove a specific method for an event': function(){ 220 | var object = createObject(); 221 | var x = 0, fn = function(){ x++; }; 222 | 223 | object.addEvent('event', Local.fn).addEvent('event', fn).removeEvent('event', Local.fn).fireEvent('event'); 224 | 225 | value_of(x).should_be(1); 226 | value_of(Local.called).should_be(0); 227 | }, 228 | 229 | 'should remove an event and its methods': function(){ 230 | var object = createObject(); 231 | var x = 0, fn = function(){ x++; }; 232 | 233 | object.addEvent('event', Local.fn).addEvent('event', fn).removeEvents('event').fireEvent('event'); 234 | 235 | value_of(x).should_be(0); 236 | value_of(Local.called).should_be(0); 237 | }, 238 | 239 | 'should remove all events': function(){ 240 | var object = createObject(); 241 | var x = 0, fn = function(){ x++; }; 242 | 243 | object.addEvent('event1', Local.fn).addEvent('event2', fn).removeEvents(); 244 | object.fireEvent('event1').fireEvent('event2'); 245 | 246 | value_of(x).should_be(0); 247 | value_of(Local.called).should_be(0); 248 | }, 249 | 250 | 'should remove events with an object': function(){ 251 | var object = createObject(); 252 | var events = { 253 | event1: Local.fn, 254 | event2: Local.fn 255 | }; 256 | 257 | object.addEvent('event1', function(){ Local.fn(); }).addEvents(events).fireEvent('event1'); 258 | value_of(Local.called).should_be(2); 259 | 260 | object.removeEvents(events); 261 | object.fireEvent('event1'); 262 | value_of(Local.called).should_be(3); 263 | 264 | object.fireEvent('event2'); 265 | value_of(Local.called).should_be(3); 266 | }, 267 | 268 | 'should remove an event immediately': function(){ 269 | var object = createObject(); 270 | 271 | var methods = []; 272 | 273 | var three = function(){ 274 | methods.push(3); 275 | }; 276 | 277 | object.addEvent('event', function(){ 278 | methods.push(1); 279 | this.removeEvent('event', three); 280 | }).addEvent('event', function(){ 281 | methods.push(2); 282 | }).addEvent('event', three); 283 | 284 | object.fireEvent('event'); 285 | value_of(methods).should_be([1, 2]); 286 | 287 | object.fireEvent('event'); 288 | value_of(methods).should_be([1, 2, 1, 2]); 289 | } 290 | }); 291 | }); 292 | 293 | describe("Options Class", { 294 | 295 | "before all": function(){ 296 | Local.OptionsTest = new Class({ 297 | Implements: [Options, Events], 298 | 299 | options: { 300 | a: 1, 301 | b: 2 302 | }, 303 | 304 | initialize: function(options){ 305 | this.setOptions(options); 306 | } 307 | }); 308 | }, 309 | 310 | "should set options": function(){ 311 | var myTest = new Local.OptionsTest({a: 1, b: 3}); 312 | value_of(myTest.options).should_not_be(undefined); 313 | }, 314 | 315 | "should override default options": function(){ 316 | var myTest = new Local.OptionsTest({a: 3, b: 4}); 317 | value_of(myTest.options.a).should_be(3); 318 | value_of(myTest.options.b).should_be(4); 319 | } 320 | 321 | }); 322 | 323 | describe("Options Class with Events", { 324 | 325 | "before all": function(){ 326 | Local.OptionsTest = new Class({ 327 | Implements: [Options, Events], 328 | 329 | options: { 330 | onEvent1: function(){ 331 | return true; 332 | }, 333 | onEvent2: function(){ 334 | return false; 335 | } 336 | }, 337 | 338 | initialize: function(options){ 339 | this.setOptions(options); 340 | } 341 | }); 342 | }, 343 | 344 | "should add events in the options object if class has implemented the Events class": function(){ 345 | var myTest = new Local.OptionsTest({ 346 | onEvent2: function(){ 347 | return true; 348 | }, 349 | 350 | onEvent3: function(){ 351 | return true; 352 | } 353 | }); 354 | 355 | value_of(myTest.$events.event1.length).should_be(1); 356 | value_of(myTest.$events.event2.length).should_be(1); 357 | value_of(myTest.$events.event3.length).should_be(1); 358 | } 359 | 360 | }); 361 | 362 | describe("Options Class", { 363 | 364 | "before all": function(){ 365 | Local.OptionsTest = new Class({ 366 | Implements: Options, 367 | 368 | initialize: function(options){ 369 | this.setOptions(options); 370 | } 371 | }); 372 | }, 373 | 374 | "should set options": function(){ 375 | var myTest = new Local.OptionsTest({ a: 1, b: 2}); 376 | value_of(myTest.options).should_not_be(undefined); 377 | }, 378 | 379 | "should override default options": function(){ 380 | Local.OptionsTest.implement({ 381 | options: { 382 | a: 1, 383 | b: 2 384 | } 385 | }); 386 | var myTest = new Local.OptionsTest({a: 3, b: 4}); 387 | value_of(myTest.options.a).should_be(3); 388 | value_of(myTest.options.b).should_be(4); 389 | }, 390 | 391 | "should add events in the options object if class has implemented the Events class": function(){ 392 | Local.OptionsTest.implement(new Events).implement({ 393 | options: { 394 | onEvent1: function(){ 395 | return true; 396 | }, 397 | onEvent2: function(){ 398 | return false; 399 | } 400 | } 401 | }); 402 | var myTest = new Local.OptionsTest({ 403 | onEvent3: function(){ 404 | return true; 405 | } 406 | }); 407 | var events = myTest.$events; 408 | value_of(events).should_not_be(undefined); 409 | value_of(events.event1.length).should_be(1); 410 | value_of(events.event1.length).should_be(1); 411 | value_of(events.event1.length).should_be(1); 412 | } 413 | 414 | }); -------------------------------------------------------------------------------- /1.2public/Core/Core.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Core.js 3 | Public Specs for Core.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe('$A', { 10 | 11 | 'should return a copy for an array': function(){ 12 | var arr1 = [1,2,3]; 13 | var arr2 = $A(arr1); 14 | value_of(arr1 !== arr2).should_be_true(); 15 | }, 16 | 17 | 'should return an array for an Elements collection': function(){ 18 | var div1 = document.createElement('div'); 19 | var div2 = document.createElement('div'); 20 | var div3 = document.createElement('div'); 21 | 22 | div1.appendChild(div2); 23 | div1.appendChild(div3); 24 | 25 | var array = $A(div1.getElementsByTagName('*')); 26 | value_of(Array.type(array)).should_be_true(); 27 | }, 28 | 29 | 'should return an array for arguments': function(){ 30 | var fnTest = function(){ 31 | return $A(arguments); 32 | }; 33 | var arr = fnTest(1,2,3); 34 | value_of(Array.type(arr)).should_be_true(); 35 | value_of(arr).should_have(3, 'items'); 36 | } 37 | 38 | }); 39 | 40 | describe('$arguments', { 41 | 42 | 'should return the argument passed according to the index': function(){ 43 | value_of($arguments(0)('a','b','c','d')).should_be('a'); 44 | value_of($arguments(1)('a','b','c','d')).should_be('b'); 45 | value_of($arguments(2)('a','b','c','d')).should_be('c'); 46 | value_of($arguments(3)('a','b','c','d')).should_be('d'); 47 | } 48 | 49 | }); 50 | 51 | describe('$chk', { 52 | 53 | 'should return false on false': function(){ 54 | value_of($chk(false)).should_be_false(); 55 | }, 56 | 57 | 'should return false on null': function(){ 58 | value_of($chk(null)).should_be_false(); 59 | }, 60 | 61 | 'should return false on undefined': function(){ 62 | value_of($chk(undefined)).should_be_false(); 63 | }, 64 | 65 | 'should return true on 0': function(){ 66 | value_of($chk(0)).should_be_true(); 67 | }, 68 | 69 | 'should return true for any truthsie': function(){ 70 | value_of($chk(1)).should_be_true(); 71 | value_of($chk({})).should_be_true(); 72 | value_of($chk(true)).should_be_true(); 73 | } 74 | 75 | }); 76 | 77 | describe('$clear', { 78 | 79 | 'should clear timeouts': function(){ 80 | var timeout = setTimeout(function(){}, 100); 81 | value_of($clear(timeout)).should_be_null(); 82 | }, 83 | 84 | 'should clear intervals': function(){ 85 | var interval = setInterval(function(){}, 100); 86 | value_of($clear(interval)).should_be_null(); 87 | } 88 | 89 | }); 90 | 91 | describe('$defined', { 92 | 93 | 'should return true on 0': function(){ 94 | value_of($defined(0)).should_be_true(); 95 | }, 96 | 97 | 'should return true on false': function(){ 98 | value_of($defined(false)).should_be_true(); 99 | }, 100 | 101 | 'should return false on null': function(){ 102 | value_of($defined(null)).should_be_false(); 103 | }, 104 | 105 | 'should return false on undefined': function(){ 106 | value_of($defined(undefined)).should_be_false(); 107 | } 108 | 109 | }); 110 | 111 | describe('$each', { 112 | 113 | 'should call the function for each item in Function arguments': function(){ 114 | var daysArr = []; 115 | (function(){ 116 | $each(arguments, function(value, key){ 117 | daysArr[key] = value; 118 | }); 119 | })('Sun','Mon','Tue'); 120 | 121 | value_of(daysArr).should_be(['Sun','Mon','Tue']); 122 | }, 123 | 124 | 'should call the function for each item in the array': function(){ 125 | var daysArr = []; 126 | $each(['Sun','Mon','Tue'], function(value, key){ 127 | daysArr[key] = value; 128 | }); 129 | 130 | value_of(daysArr).should_be(['Sun','Mon','Tue']); 131 | }, 132 | 133 | 'should call the function for each item in the object': function(){ 134 | var daysObj = {}; 135 | $each({first: "Sunday", second: "Monday", third: "Tuesday"}, function(value, key){ 136 | daysObj[key] = value; 137 | }); 138 | 139 | value_of(daysObj).should_be({first: 'Sunday', second: 'Monday', third: 'Tuesday'}); 140 | } 141 | 142 | }); 143 | 144 | describe('$extend', { 145 | 146 | 'should extend two objects': function(){ 147 | var obj1 = {a: 1, b: 2}; 148 | var obj2 = {b: 3, c: 4}; 149 | $extend(obj1, obj2); 150 | value_of(obj1).should_be({a: 1, b: 3, c: 4}); 151 | }, 152 | 153 | 'should overwrite properties': function(){ 154 | var obj1 = {a: 1, b: 2}; 155 | var obj2 = {b: 3, c: 4, a: 5}; 156 | $extend(obj1, obj2); 157 | value_of(obj1).should_be({a: 5, b: 3, c: 4}); 158 | }, 159 | 160 | 'should not extend with null argument': function(){ 161 | var obj1 = {a: 1, b: 2}; 162 | $extend(obj1); 163 | value_of(obj1).should_be({a: 1, b: 2}); 164 | } 165 | 166 | }); 167 | 168 | describe('$lambda', { 169 | 170 | 'if a function is passed in that function should be returned': function(){ 171 | var fn = function(a,b){ return a; }; 172 | value_of($lambda(fn)).should_be(fn); 173 | }, 174 | 175 | 'should return a function that returns the value passed when called': function(){ 176 | value_of($lambda('hello world!')()).should_be('hello world!'); 177 | } 178 | 179 | }); 180 | 181 | describe('$merge', { 182 | 183 | 'should dereference objects': function(){ 184 | var obj1 = {a: 1, b: 2}; 185 | var obj2 = $merge(obj1); 186 | value_of(obj1 === obj2).should_be_false(); 187 | }, 188 | 189 | 'should merge any arbitrary number of nested objects': function(){ 190 | var obj1 = {a: {a: 1, b: 2, c: 3}, b: 2}; 191 | var obj2 = {a: {a: 2, b: 8, c: 3, d: 8}, b: 3, c: 4}; 192 | var obj3 = {a: {a: 3}, b: 3, c: false}; 193 | value_of($merge(obj1, obj2, obj3)).should_be({a: {a: 3, b: 8, c: 3, d: 8}, b: 3, c: false}); 194 | } 195 | 196 | }); 197 | 198 | describe('$pick', { 199 | 200 | 'should return the first false argument': function(){ 201 | var picked1 = $pick(null, undefined, false, [1,2,3], {}); 202 | value_of(picked1).should_be_false(); 203 | }, 204 | 205 | 'should return the first defined argument': function(){ 206 | var picked1 = $pick(null, undefined, null, [1,2,3], {}); 207 | value_of(picked1).should_be([1,2,3]); 208 | } 209 | 210 | }); 211 | 212 | describe('$random', { 213 | 214 | 'should return a number between two numbers specified': function(){ 215 | var rand = $random(1, 3); 216 | value_of((rand <= 3 && rand >= 1)).should_be_true(); 217 | } 218 | 219 | }); 220 | 221 | describe('$splat', { 222 | 223 | 'should transform a non array into an array': function(){ 224 | value_of($splat(1)).should_be([1]); 225 | }, 226 | 227 | 'should transforum an undefined or null into an empty array': function(){ 228 | value_of($splat(null)).should_be([]); 229 | value_of($splat(undefined)).should_be([]); 230 | }, 231 | 232 | 'should ignore and return an array': function(){ 233 | value_of($splat([1,2,3])).should_be([1,2,3]); 234 | } 235 | 236 | }); 237 | 238 | describe('$time', { 239 | 240 | 'should return a timestamp': function(){ 241 | value_of(Number.type($time())).should_be_true(); 242 | }, 243 | 244 | 'should be within a reasonable range': function(){ 245 | value_of($time() < 1e13 && $time() > 1e12).should_be_true(); 246 | } 247 | 248 | }); 249 | 250 | describe('$try', { 251 | 252 | 'should return the result of the first successful function without executing successive functions': function(){ 253 | var calls = 0; 254 | var attempt = $try(function(){ 255 | calls++; 256 | throw new Exception(); 257 | }, function(){ 258 | calls++; 259 | return 'success'; 260 | }, function(){ 261 | calls++; 262 | return 'moo'; 263 | }); 264 | value_of(calls).should_be(2); 265 | value_of(attempt).should_be('success'); 266 | }, 267 | 268 | 'should return null when no function succeeded': function(){ 269 | var calls = 0; 270 | var attempt = $try(function(){ 271 | calls++; 272 | return I_invented_this(); 273 | }, function(){ 274 | calls++; 275 | return uninstall_ie(); 276 | }); 277 | value_of(calls).should_be(2); 278 | value_of(attempt).should_be_null(); 279 | } 280 | 281 | }); 282 | 283 | describe('$type', { 284 | 285 | "should return 'array' for Array objects": function(){ 286 | value_of($type([1,2])).should_be('array'); 287 | }, 288 | 289 | "should return 'string' for String objects": function(){ 290 | value_of($type('ciao')).should_be('string'); 291 | }, 292 | 293 | "should return 'regexp' for RegExp objects": function(){ 294 | value_of($type(/_/)).should_be('regexp'); 295 | }, 296 | 297 | "should return 'function' for Function objects": function(){ 298 | value_of($type(function(){})).should_be('function'); 299 | }, 300 | 301 | "should return 'number' for Number objects": function(){ 302 | value_of($type(10)).should_be('number'); 303 | value_of($type(NaN)).should_not_be('number'); 304 | }, 305 | 306 | "should return 'boolean' for Boolean objects": function(){ 307 | value_of($type(true)).should_be('boolean'); 308 | value_of($type(false)).should_be('boolean'); 309 | }, 310 | 311 | "should return 'object' for Object objects": function(){ 312 | value_of($type({a:2})).should_be('object'); 313 | }, 314 | 315 | "should return 'arguments' for Function arguments": function(){ 316 | value_of($type(arguments)).should_be((window.opera) ? 'array' : 'arguments'); //opera's arguments behave like arrays--which is actually better. 317 | }, 318 | 319 | "should return false for null objects": function(){ 320 | value_of($type(null)).should_be_false(); 321 | }, 322 | 323 | "should return false for undefined objects": function(){ 324 | value_of($type(undefined)).should_be_false(); 325 | }, 326 | 327 | "should return 'collection' for HTMLElements collections": function(){ 328 | value_of($type(document.getElementsByTagName('*'))).should_be('collection'); 329 | }, 330 | 331 | "should return 'element' for an Element": function(){ 332 | var div = document.createElement('div'); 333 | value_of($type(div)).should_be('element'); 334 | }, 335 | 336 | "should return 'window' for the window object": function(){ 337 | value_of($type(window)).should_be('window'); 338 | }, 339 | 340 | "should return 'document' for the document object": function(){ 341 | value_of($type(document)).should_be('document'); 342 | } 343 | 344 | }); 345 | 346 | describe('$unlink', { 347 | 348 | "should unlink an object recursivly": function(){ 349 | var inner = {b: 2}; 350 | var obj = {a: 1, inner: inner}; 351 | var copy = $unlink(obj); 352 | obj.a = 10; 353 | inner.b = 20; 354 | 355 | value_of(obj.a).should_be(10); 356 | value_of(obj.inner.b).should_be(20); 357 | value_of($type(obj)).should_be('object'); 358 | 359 | value_of(copy.a).should_be(1); 360 | value_of(copy.inner.b).should_be(2); 361 | value_of($type(copy)).should_be('object'); 362 | }, 363 | 364 | "should unlink an Hash": function(){ 365 | var hash = new Hash({a: 'one'}); 366 | var copy = $unlink(hash); 367 | 368 | value_of($type(hash)).should_be('hash'); 369 | value_of($type(copy)).should_be('hash'); 370 | 371 | copy.set('a', 'two'); 372 | 373 | value_of(hash.get('a')).should_be('one'); 374 | value_of(copy.get('a')).should_be('two'); 375 | } 376 | 377 | }); 378 | 379 | describe('Hash.getLength', { 380 | 381 | "should return the number of items in it": function(){ 382 | var hash = new Hash({}); 383 | value_of(hash.getLength()).should_be(0); 384 | hash.set('mootools', 'awesome'); 385 | hash.milk = 'yummy'; 386 | value_of(hash.getLength()).should_be(2); 387 | }, 388 | 389 | "should not fail when length is set": function(){ 390 | var hash = new Hash({'length': 10}); 391 | value_of(hash.getLength()).should_be(1); 392 | }, 393 | 394 | "should work as a generic on objects": function(){ 395 | value_of(Hash.getLength({})).should_be(0); 396 | value_of(Hash.getLength({'': '', '0': '0', 'length': 99})).should_be(3); 397 | } 398 | 399 | }); 400 | 401 | describe('$H', { 402 | 403 | "should create a new hash": function(){ 404 | var hash = $H({}); 405 | value_of($type(hash)).should_be('hash'); 406 | } 407 | 408 | }); 409 | -------------------------------------------------------------------------------- /SlickSpec/moobugger/debugger-iframe.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Debugger.js 3 | Creates Firebug style debugger for browsers without Firebug. 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | var FOR_IN_MAX = 20; 10 | 11 | var debug = { 12 | 13 | $nil: Function.empty, 14 | 15 | $init: function(){ 16 | debug.loaded = false; 17 | debug.$groups = {'keys': [], 'values': []}; 18 | debug.$timers = {}; 19 | }, 20 | 21 | $register: function(text){ 22 | debug.$messages.remove(text); 23 | debug.$messages.push(text); 24 | debug.$midx = debug.$messages.length; 25 | var toCookie = debug.$messages.join('|||').replace(/;/g, '%%%'); 26 | if (toCookie.length >= 4096) toCookie = toCookie.substring(2048, 4049); 27 | Cookie.set('mootools-debugger-history', toCookie, {duration: 10}); 28 | }, 29 | 30 | $parse: function(args, separator, to_e, klass){ 31 | separator = $pick(separator, ' '); 32 | var chunks = []; 33 | for (var i = 0, l = args.length; i < l; i++){ 34 | if (args[i] === undefined){ 35 | chunks.push({'type': 'undefined', 'value': 'undefined'}); 36 | continue; 37 | } 38 | var argument = args[i]; 39 | var type = parent.Moo.type(argument); 40 | if (['boolean', false].contains(type)) argument = String(argument); 41 | else if (type == 'collection') type = 'array'; 42 | chunks.push({'type': type.toString(), 'value': argument}); 43 | } 44 | var holder = new Element('span'); 45 | chunks.each(function(chunk, i){ 46 | switch(chunk.type){ 47 | case 'element': holder.adopt(debug.$element(chunk.value)); break; 48 | case 'array': holder.adopt(new Element('span').setHTML('['), debug.$parse(chunk.value, ', ', true), new Element('span').setHTML(']')); break; 49 | case 'object': 50 | try{ 51 | holder.adopt(new Element('span').setHTML('{')); 52 | var x = 0; 53 | var length = 0; 54 | 55 | for (var len in chunk.value) length++; 56 | for (var key in chunk.value){ 57 | x++; 58 | holder.adopt(new Element('span').setHTML('', key, '', ': '), debug.$parse([chunk.value[key]], '', true)); 59 | if (x != length) holder.adopt(new Element('span').setHTML(', ')); 60 | if (x > FOR_IN_MAX){ 61 | holder.adopt(new Element('span').setHTML('and '+ (length - x) +' more properties…')); 62 | break; 63 | } 64 | } 65 | holder.adopt(new Element('span').setHTML('}')); 66 | }catch(e){ 67 | new Element('span').addClass(chunk.type).appendText(chunk.value).inject(holder); 68 | } 69 | break; 70 | default: new Element('span').addClass(chunk.type).appendText(chunk.value).inject(holder); 71 | } 72 | if (i != (chunks.length - 1)) holder.adopt(new Element('span').setHTML(separator)); 73 | }); 74 | if (to_e){ 75 | return holder; 76 | } else { 77 | debug.$pre(holder, klass); 78 | return debug.$nil; 79 | } 80 | }, 81 | 82 | $element: function(el){ 83 | var el_style = el.style; 84 | if (!el_style) el_style = {}; 85 | 86 | var oldbg = el_style.backgroundColor; 87 | var oldfg = el_style.color; 88 | 89 | var link = new Element('a', {'href': '#'}).addEvents({ 90 | 91 | mouseenter: function(){ 92 | el_style.backgroundColor = '#DBEAF0'; 93 | el_style.color = '#757E8A'; 94 | }, 95 | 96 | mouseleave: function(){ 97 | el_style.backgroundColor = oldbg; 98 | el_style.color = oldfg; 99 | }, 100 | 101 | click: function(){ 102 | return false; 103 | } 104 | 105 | }); 106 | var htm = ['<' + '' + Element.getTag(el) + '']; 107 | ['id', 'className', 'name', 'href', 'title', 'rel', 'type'].each(function(attr){ 108 | if (el[attr]) htm.push(attr + '="' + '' + el[attr] + '' + '"'); 109 | }); 110 | return link.setHTML(htm.join(' '), '>'); 111 | }, 112 | 113 | $pre: function(content, klass){ 114 | var pre = new Element('pre', {'class': klass || 'message'}); 115 | if ($type(content) == "string") pre.appendText(content); 116 | else pre.adopt(content); 117 | pre.inject(debug.$groups.values.getLast()); 118 | if (debug.loaded) debug._scroll.toBottom(); 119 | return pre; 120 | }, 121 | 122 | $log: function(args, separator, klass){ 123 | separator = $pick(separator, ', '); 124 | var sRegExp = /%[sdifo]/gi; 125 | if ($type(args[0]) == 'string' && args[0].test(sRegExp)){ 126 | separator = ''; 127 | var logCollection = [], lastIndex = 0; 128 | sRegExp.lastIndex = 0; 129 | var token; 130 | for (var i = 1; (i < args.length) && (token = sRegExp.exec(args[0])); i++){ 131 | logCollection.push(args[0].substring(lastIndex, token.index), args[i]); 132 | lastIndex = sRegExp.lastIndex; 133 | } 134 | sRegExp.lastIndex = 0; 135 | if (!lastIndex) return debug.$parse(args); 136 | logCollection.push(args[0].substring(lastIndex)); 137 | args = logCollection; 138 | } 139 | debug.$parse(args, separator, false, klass); 140 | return debug.$nil; 141 | }, 142 | 143 | $special: function(obj, klass){ 144 | if (obj.length == 1){ 145 | var one = obj[0]; 146 | var type = $type(one); 147 | if ((type == 'object' && one.name && one.message) || (type == 'string')){ 148 | var name, message; 149 | 150 | if (type == 'object'){ 151 | name = one.name; 152 | message = one.message; 153 | } else if (type == 'string'){ 154 | name = klass.capitalize(); 155 | message = one; 156 | } 157 | 158 | return debug.$pre(name + ': ' + message, klass); 159 | } 160 | } 161 | return debug.$log([klass.capitalize() + ':'].concat(obj), ' ', klass); 162 | }, 163 | 164 | $load: function(){ 165 | debug.loaded = true; 166 | debug.$messages = Cookie.get('mootools-debugger-history') || []; 167 | debug.$messages = debug.$messages.length ? debug.$messages.replace(/%%%/g, ';').split('|||') : []; 168 | debug.$midx = debug.$messages.length; 169 | 170 | debug._body = $('debug').setStyle('display', 'block'); 171 | debug._messages = $('debug-messages'); 172 | 173 | debug.$groups.keys.push('$main$'); 174 | debug.$groups.values.push(debug._messages); 175 | 176 | debug._input = $('debug-input'); 177 | 178 | debug._scroll = new Fx.Scroll(debug._messages, {duration: 300, wait: false}); 179 | 180 | debug._input.addEvent('keydown', debug.$key); 181 | 182 | debug._max = $('debug-button-max').addEvent('click', debug.$max); 183 | 184 | debug._min = $('debug-button-min').addEvent('click', debug.$min); 185 | 186 | debug._close = $('debug-button-close').addEvent('click', debug.$unload); 187 | 188 | debug._maxValue = 132; 189 | debug._minValue = 18; 190 | 191 | var state = Cookie.get('mootools-debugger-state'); 192 | if (state) debug[state](); 193 | else debug.$max(); 194 | 195 | for (var i = 0, l = parent.debug.queue.length; i < l; i++){ 196 | var kue = parent.debug.queue[i]; 197 | debug[kue.name].apply(debug, kue.arguments); 198 | } 199 | 200 | debug._scroll.toBottom(); 201 | }, 202 | 203 | $max: function(){ 204 | Cookie.set('mootools-debugger-state', '$max', {duration: 10}); 205 | debug._messages.setStyles({ 206 | 'height': debug._maxValue, 207 | 'overflow': 'auto' 208 | }); 209 | debug._max.setStyle('display', 'none'); 210 | debug._min.setStyle('display', 'block'); 211 | debug.$pad(); 212 | }, 213 | 214 | $min: function(){ 215 | Cookie.set('mootools-debugger-state', '$min', {duration: 10}); 216 | debug._messages.setStyles({ 217 | 'height': debug._minValue, 218 | 'overflow': 'hidden' 219 | }); 220 | debug._max.setStyle('display', 'block'); 221 | debug._min.setStyle('display', 'none'); 222 | debug.$pad(); 223 | }, 224 | 225 | $pad: function(){ 226 | parent.debug.iFrame.style.height = debug._body.offsetHeight + 'px'; 227 | debug._messages.scrollTop = debug._messages.scrollHeight - debug._messages.offsetHeight; 228 | parent.Moo.Debugger.reposition(); 229 | }, 230 | 231 | $unload: function(){ 232 | if (!debug.loaded) return; 233 | debug.$init(); 234 | parent.Moo.Debugger.unload(); 235 | }, 236 | 237 | $focus: function(){ 238 | debug._input.focus(); 239 | }, 240 | 241 | $key: function(e){ 242 | var value = debug._input.value; 243 | 244 | switch(e.key){ 245 | case 'enter': 246 | if (!value){ 247 | return false; 248 | } 249 | debug._input.value = ''; 250 | switch(value){ 251 | case 'exit': debug.$unload(); return false; 252 | case 'clear': case 'clr': debug._messages.empty(); return false; 253 | } 254 | 255 | debug.$pre('>>> ' + value, 'logger'); 256 | debug.$register(value); 257 | if (value.indexOf('var ') == 0) value = value.substring(4, value.length); 258 | if (value.charAt(value.length - 1) == ';') value = value.substring(0, value.length - 1); 259 | if (value.indexOf('{') == 0) value = '(' + value + ')'; 260 | 261 | parent.Moo.Debugger.evaluate(value); 262 | break; 263 | 264 | case 'up': 265 | e.stop(); 266 | var i = debug.$midx - 1; 267 | if (debug.$messages[i]){ 268 | debug._input.value = debug.$messages[i]; 269 | debug.$midx = i; 270 | } 271 | break; 272 | 273 | case 'down': 274 | e.stop(); 275 | var j = debug.$midx + 1; 276 | if (debug.$messages[j]){ 277 | debug._input.value = debug.$messages[j]; 278 | debug.$midx = j; 279 | } else { 280 | debug._input.value = ''; 281 | debug.$midx = debug.$messages.length; 282 | } 283 | } 284 | 285 | return debug.$focus.delay(50); 286 | }, 287 | 288 | /* 289 | Property: log 290 | sends a message to the debugger. 291 | Arguments: 292 | messages - any number of strings, objects, etc. to print out 293 | Note: 294 | The debugger will allow firebug style log messages: 295 | %s - String 296 | %d, %i - Integer (numeric formatting is not yet supported) 297 | %f - Floating point number (numeric formatting is not yet supported) 298 | %o - Object hyperlink 299 | Example: 300 | >console.log("the value of x is %s and this paragraph is %o", x, $('id')); 301 | > the value of x is and this paragraph is

302 | */ 303 | 304 | log: function(){ 305 | return debug.$log($A(arguments)); 306 | }, 307 | 308 | /* 309 | Property: time 310 | Starts a timer. 311 | Argument: 312 | name - the name of the timer 313 | */ 314 | 315 | time: function(name){ 316 | if (debug.$timers[name]){ 317 | debug.error("a timer called " + name + ' already exists'); 318 | } else { 319 | debug.$pre(name + ' started', 'time'); 320 | debug.$timers[name] = new Date().getTime(); 321 | } 322 | return debug.$nil; 323 | }, 324 | 325 | /* 326 | Property: timeEnd 327 | Ends a timer and logs that value to the console. 328 | Argument: 329 | name - the name of the timer 330 | */ 331 | 332 | timeEnd: function(name){ 333 | if (debug.$timers[name]) debug.$pre(name + ' ended: ' + (new Date().getTime() - debug.$timers[name]) + ' ms', 'time'); 334 | else debug.error("no such timer called " + name); 335 | return debug.$nil; 336 | }, 337 | 338 | group: function(name){ 339 | if (debug.$groups.keys.contains(name)){ 340 | debug.error('a group called ' + name + ' already exists'); 341 | } else { 342 | var pre = debug.$pre('Group: ' + name, 'group'); 343 | var grp = new Element('div', {'class': 'group'}).inject(debug.$groups.values.getLast()); 344 | pre.addEvent('click', function(){ 345 | var none = (grp.getStyle('display') == 'none'); 346 | var name = none ? 'block' : 'none'; 347 | grp.setStyle('display', name); 348 | this.toggleClass('group-closed'); 349 | }); 350 | debug.$groups.keys.push(name); 351 | debug.$groups.values.push(grp); 352 | } 353 | return debug.$nil; 354 | }, 355 | 356 | groupEnd: function(name){ 357 | var idx = debug.$groups.keys.indexOf(name); 358 | if (idx >= 0){ 359 | debug.$groups.values.remove(debug.$groups.values[idx]); 360 | debug.$groups.keys.remove(name); 361 | } else { 362 | debug.error('no such group called ' + name); 363 | } 364 | return debug.$nil; 365 | }, 366 | 367 | error: function(){ 368 | debug.$special($A(arguments), 'error'); 369 | return debug.$nil; 370 | }, 371 | 372 | warn: function(warning){ 373 | debug.$special($A(arguments), 'warning'); 374 | return debug.$nil; 375 | }, 376 | 377 | info: function(){ 378 | debug.$special($A(arguments), 'info'); 379 | return debug.$nil; 380 | } 381 | 382 | }; 383 | 384 | debug.$init(); 385 | 386 | window.addEvent('load', debug.$load); -------------------------------------------------------------------------------- /SlickSpec/screen.css: -------------------------------------------------------------------------------- 1 | /* 2 | curl -s http://tripledoubleyou.subtlegradient.com/c/blueprint/screen.css #*/ 3 | /* ----------------------------------------------------------------------- 4 | 5 | 6 | Blueprint CSS Framework 0.9 7 | http://blueprintcss.org 8 | 9 | * Copyright (c) 2007-Present. See LICENSE for more info. 10 | * See README for instructions on how to use Blueprint. 11 | * For credits and origins, see AUTHORS. 12 | * This is a compressed file. See the sources in the 'src' directory. 13 | 14 | ----------------------------------------------------------------------- */ 15 | 16 | /* reset.css */ 17 | html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} 18 | body {line-height:1.5;} 19 | table {border-collapse:separate;border-spacing:0;} 20 | caption, th, td {text-align:left;font-weight:normal;} 21 | table, td, th {vertical-align:middle;} 22 | blockquote:before, blockquote:after, q:before, q:after {content:"";} 23 | blockquote, q {quotes:"" "";} 24 | a img {border:none;} 25 | 26 | /* typography.css */ 27 | html {font-size:100.01%;} 28 | body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} 29 | h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} 30 | h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} 31 | h2 {font-size:2em;margin-bottom:0.75em;} 32 | h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} 33 | h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} 34 | h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} 35 | h6 {font-size:1em;font-weight:bold;} 36 | h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} 37 | p {margin:0 0 1.5em;} 38 | p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} 39 | p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} 40 | a:focus, a:hover {color:#000;} 41 | a {color:#009;text-decoration:underline;} 42 | blockquote {margin:1.5em;color:#666;font-style:italic;} 43 | strong {font-weight:bold;} 44 | em, dfn {font-style:italic;} 45 | dfn {font-weight:bold;} 46 | sup, sub {line-height:0;} 47 | abbr, acronym {border-bottom:1px dotted #666;} 48 | address {margin:0 0 1.5em;font-style:italic;} 49 | del {color:#666;} 50 | pre {margin:1.5em 0;white-space:pre;} 51 | pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} 52 | li ul, li ol {margin:0;} 53 | ul, ol {margin:0 1.5em 1.5em 0;padding-left:3.333em;} 54 | ul {list-style-type:disc;} 55 | ol {list-style-type:decimal;} 56 | dl {margin:0 0 1.5em 0;} 57 | dl dt {font-weight:bold;} 58 | dd {margin-left:1.5em;} 59 | table {margin-bottom:1.4em;width:100%;} 60 | th {font-weight:bold;} 61 | thead th {background:#c3d9ff;} 62 | th, td, caption {padding:4px 10px 4px 5px;} 63 | tr.even td {background:#e5ecf9;} 64 | tfoot {font-style:italic;} 65 | caption {background:#eee;} 66 | .small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} 67 | .large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} 68 | .hide {display:none;} 69 | .quiet {color:#666;} 70 | .loud {color:#000;} 71 | .highlight {background:#ff0;} 72 | .added {background:#060;color:#fff;} 73 | .removed {background:#900;color:#fff;} 74 | .first {margin-left:0;padding-left:0;} 75 | .last {margin-right:0;padding-right:0;} 76 | .top {margin-top:0;padding-top:0;} 77 | .bottom {margin-bottom:0;padding-bottom:0;} 78 | 79 | /* forms.css */ 80 | label {font-weight:bold;} 81 | fieldset {padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} 82 | legend {font-weight:bold;font-size:1.2em;} 83 | input[type=text], input[type=password], input.text, input.title, textarea, select {background-color:#fff;border:1px solid #bbb;} 84 | input[type=text]:focus, input[type=password]:focus, input.text:focus, input.title:focus, textarea:focus, select:focus {border-color:#666;} 85 | input[type=text], input[type=password], input.text, input.title, textarea, select {margin:0.5em 0;} 86 | input.text, input.title {width:300px;padding:5px;} 87 | input.title {font-size:1.5em;} 88 | textarea {width:390px;height:250px;padding:5px;} 89 | input[type=checkbox], input[type=radio], input.checkbox, input.radio {position:relative;top:.25em;} 90 | form.inline {line-height:3;} 91 | form.inline p {margin-bottom:0;} 92 | .error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;} 93 | .error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;} 94 | .notice {background:#FFF6BF;color:#514721;border-color:#FFD324;} 95 | .success {background:#E6EFC2;color:#264409;border-color:#C6D880;} 96 | .error a {color:#8a1f11;} 97 | .notice a {color:#514721;} 98 | .success a {color:#264409;} 99 | 100 | /* grid.css */ 101 | .container {width:950px;margin:0 auto;} 102 | .showgrid {background:url(src/grid.png);} 103 | .column, div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;} 104 | .last, div.last {margin-right:0;} 105 | .span-1 {width:30px;} 106 | .span-2 {width:70px;} 107 | .span-3 {width:110px;} 108 | .span-4 {width:150px;} 109 | .span-5 {width:190px;} 110 | .span-6 {width:230px;} 111 | .span-7 {width:270px;} 112 | .span-8 {width:310px;} 113 | .span-9 {width:350px;} 114 | .span-10 {width:390px;} 115 | .span-11 {width:430px;} 116 | .span-12 {width:470px;} 117 | .span-13 {width:510px;} 118 | .span-14 {width:550px;} 119 | .span-15 {width:590px;} 120 | .span-16 {width:630px;} 121 | .span-17 {width:670px;} 122 | .span-18 {width:710px;} 123 | .span-19 {width:750px;} 124 | .span-20 {width:790px;} 125 | .span-21 {width:830px;} 126 | .span-22 {width:870px;} 127 | .span-23 {width:910px;} 128 | .span-24, div.span-24 {width:950px;margin-right:0;} 129 | input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px!important;border-right-width:1px!important;padding-left:5px!important;padding-right:5px!important;} 130 | input.span-1, textarea.span-1 {width:18px!important;} 131 | input.span-2, textarea.span-2 {width:58px!important;} 132 | input.span-3, textarea.span-3 {width:98px!important;} 133 | input.span-4, textarea.span-4 {width:138px!important;} 134 | input.span-5, textarea.span-5 {width:178px!important;} 135 | input.span-6, textarea.span-6 {width:218px!important;} 136 | input.span-7, textarea.span-7 {width:258px!important;} 137 | input.span-8, textarea.span-8 {width:298px!important;} 138 | input.span-9, textarea.span-9 {width:338px!important;} 139 | input.span-10, textarea.span-10 {width:378px!important;} 140 | input.span-11, textarea.span-11 {width:418px!important;} 141 | input.span-12, textarea.span-12 {width:458px!important;} 142 | input.span-13, textarea.span-13 {width:498px!important;} 143 | input.span-14, textarea.span-14 {width:538px!important;} 144 | input.span-15, textarea.span-15 {width:578px!important;} 145 | input.span-16, textarea.span-16 {width:618px!important;} 146 | input.span-17, textarea.span-17 {width:658px!important;} 147 | input.span-18, textarea.span-18 {width:698px!important;} 148 | input.span-19, textarea.span-19 {width:738px!important;} 149 | input.span-20, textarea.span-20 {width:778px!important;} 150 | input.span-21, textarea.span-21 {width:818px!important;} 151 | input.span-22, textarea.span-22 {width:858px!important;} 152 | input.span-23, textarea.span-23 {width:898px!important;} 153 | input.span-24, textarea.span-24 {width:938px!important;} 154 | .append-1 {padding-right:40px;} 155 | .append-2 {padding-right:80px;} 156 | .append-3 {padding-right:120px;} 157 | .append-4 {padding-right:160px;} 158 | .append-5 {padding-right:200px;} 159 | .append-6 {padding-right:240px;} 160 | .append-7 {padding-right:280px;} 161 | .append-8 {padding-right:320px;} 162 | .append-9 {padding-right:360px;} 163 | .append-10 {padding-right:400px;} 164 | .append-11 {padding-right:440px;} 165 | .append-12 {padding-right:480px;} 166 | .append-13 {padding-right:520px;} 167 | .append-14 {padding-right:560px;} 168 | .append-15 {padding-right:600px;} 169 | .append-16 {padding-right:640px;} 170 | .append-17 {padding-right:680px;} 171 | .append-18 {padding-right:720px;} 172 | .append-19 {padding-right:760px;} 173 | .append-20 {padding-right:800px;} 174 | .append-21 {padding-right:840px;} 175 | .append-22 {padding-right:880px;} 176 | .append-23 {padding-right:920px;} 177 | .prepend-1 {padding-left:40px;} 178 | .prepend-2 {padding-left:80px;} 179 | .prepend-3 {padding-left:120px;} 180 | .prepend-4 {padding-left:160px;} 181 | .prepend-5 {padding-left:200px;} 182 | .prepend-6 {padding-left:240px;} 183 | .prepend-7 {padding-left:280px;} 184 | .prepend-8 {padding-left:320px;} 185 | .prepend-9 {padding-left:360px;} 186 | .prepend-10 {padding-left:400px;} 187 | .prepend-11 {padding-left:440px;} 188 | .prepend-12 {padding-left:480px;} 189 | .prepend-13 {padding-left:520px;} 190 | .prepend-14 {padding-left:560px;} 191 | .prepend-15 {padding-left:600px;} 192 | .prepend-16 {padding-left:640px;} 193 | .prepend-17 {padding-left:680px;} 194 | .prepend-18 {padding-left:720px;} 195 | .prepend-19 {padding-left:760px;} 196 | .prepend-20 {padding-left:800px;} 197 | .prepend-21 {padding-left:840px;} 198 | .prepend-22 {padding-left:880px;} 199 | .prepend-23 {padding-left:920px;} 200 | div.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;} 201 | div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;} 202 | .pull-1 {margin-left:-40px;} 203 | .pull-2 {margin-left:-80px;} 204 | .pull-3 {margin-left:-120px;} 205 | .pull-4 {margin-left:-160px;} 206 | .pull-5 {margin-left:-200px;} 207 | .pull-6 {margin-left:-240px;} 208 | .pull-7 {margin-left:-280px;} 209 | .pull-8 {margin-left:-320px;} 210 | .pull-9 {margin-left:-360px;} 211 | .pull-10 {margin-left:-400px;} 212 | .pull-11 {margin-left:-440px;} 213 | .pull-12 {margin-left:-480px;} 214 | .pull-13 {margin-left:-520px;} 215 | .pull-14 {margin-left:-560px;} 216 | .pull-15 {margin-left:-600px;} 217 | .pull-16 {margin-left:-640px;} 218 | .pull-17 {margin-left:-680px;} 219 | .pull-18 {margin-left:-720px;} 220 | .pull-19 {margin-left:-760px;} 221 | .pull-20 {margin-left:-800px;} 222 | .pull-21 {margin-left:-840px;} 223 | .pull-22 {margin-left:-880px;} 224 | .pull-23 {margin-left:-920px;} 225 | .pull-24 {margin-left:-960px;} 226 | .pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} 227 | .push-1 {margin:0 -40px 1.5em 40px;} 228 | .push-2 {margin:0 -80px 1.5em 80px;} 229 | .push-3 {margin:0 -120px 1.5em 120px;} 230 | .push-4 {margin:0 -160px 1.5em 160px;} 231 | .push-5 {margin:0 -200px 1.5em 200px;} 232 | .push-6 {margin:0 -240px 1.5em 240px;} 233 | .push-7 {margin:0 -280px 1.5em 280px;} 234 | .push-8 {margin:0 -320px 1.5em 320px;} 235 | .push-9 {margin:0 -360px 1.5em 360px;} 236 | .push-10 {margin:0 -400px 1.5em 400px;} 237 | .push-11 {margin:0 -440px 1.5em 440px;} 238 | .push-12 {margin:0 -480px 1.5em 480px;} 239 | .push-13 {margin:0 -520px 1.5em 520px;} 240 | .push-14 {margin:0 -560px 1.5em 560px;} 241 | .push-15 {margin:0 -600px 1.5em 600px;} 242 | .push-16 {margin:0 -640px 1.5em 640px;} 243 | .push-17 {margin:0 -680px 1.5em 680px;} 244 | .push-18 {margin:0 -720px 1.5em 720px;} 245 | .push-19 {margin:0 -760px 1.5em 760px;} 246 | .push-20 {margin:0 -800px 1.5em 800px;} 247 | .push-21 {margin:0 -840px 1.5em 840px;} 248 | .push-22 {margin:0 -880px 1.5em 880px;} 249 | .push-23 {margin:0 -920px 1.5em 920px;} 250 | .push-24 {margin:0 -960px 1.5em 960px;} 251 | .push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:right;position:relative;} 252 | .prepend-top {margin-top:1.5em;} 253 | .append-bottom {margin-bottom:1.5em;} 254 | .box {padding:1.5em;margin-bottom:1.5em;background:#E5ECF9;} 255 | hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:none;} 256 | hr.space {background:#fff;color:#fff;visibility:hidden;} 257 | .clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;} 258 | .clearfix, .container {display:block;} 259 | .clear {clear:both;} 260 | -------------------------------------------------------------------------------- /SlickSpec/JSSpecHelpers.js: -------------------------------------------------------------------------------- 1 | function $try(){ 2 | for (var i = 0, l = arguments.length; i < l; i++){ 3 | try { 4 | return arguments[i](); 5 | } catch(e){} 6 | } 7 | return null; 8 | }; 9 | 10 | Function.prototype._type = "Function"; 11 | 12 | String.escapeSingle = String.escapeSingle || function escapeSingle(string){ 13 | return (''+string).replace(/(?=[\\\n'])/g,'\\'); 14 | }; 15 | 16 | 17 | var global = this; 18 | global.context = this; 19 | var specs, spec, it, its; 20 | var descriptionParent = ''; 21 | var uniquespecs = {}; 22 | 23 | function Describe(description,specBuilder){ 24 | // Backup existing object so we don't override it 25 | var old_specs = specs; 26 | specs = spec = it = its = {}; 27 | 28 | // Inherit the before and afters of your forefathers 29 | if (old_specs) { 30 | if (old_specs.before ) specs.before = old_specs.before; 31 | if (old_specs.before_all ) specs.before_all = old_specs.before_all; 32 | if (old_specs.before_each ) specs.before_each = old_specs.before_each; 33 | if (old_specs.after ) specs.after = old_specs.after; 34 | if (old_specs.after_all ) specs.after_all = old_specs.after_all; 35 | if (old_specs.after_each ) specs.after_each = old_specs.after_each; 36 | } 37 | 38 | // Inherit the description of your forefathers 39 | description = descriptionParent + (descriptionParent ? ': ' : '') + String(description); 40 | var old_descriptionParent = descriptionParent; 41 | descriptionParent = description; 42 | 43 | // Build the spec object 44 | specBuilder(specs,global.context); 45 | 46 | // Create the tests and go! 47 | var spec_count = 0; 48 | var specnames = []; 49 | for (var specname in specs){ 50 | if (/^(before|after)[_ ](all|each)$/.test(specname)) continue; 51 | if (!specs[specname]) continue; 52 | spec_count++; 53 | specnames.push(description+specname); 54 | } 55 | if (spec_count && !uniquespecs[specnames]){ 56 | describe(description, specs); 57 | uniquespecs[specnames] = true; 58 | } 59 | 60 | // Reset 61 | descriptionParent = old_descriptionParent; 62 | specs = spec = it = its = old_specs; 63 | }; 64 | 65 | 66 | global.mocks = {}; 67 | var Mock = (function(){ 68 | 69 | function Mock(mockName, testBuilder){ 70 | if (mockName && !testBuilder) throw new Error("Invalid mockName, Mock syntax: `new Mock(/mockName/, function(specs, window){})`"); 71 | 72 | if (Object.prototype.toString.call(mockName) != '[object RegExp]') 73 | mockName = new RegExp(mockName); 74 | 75 | this.mockName = mockName; 76 | this.testBuilder = testBuilder; 77 | Mock.mocks.push(this); 78 | }; 79 | 80 | Mock.mocks = []; 81 | 82 | Mock.prototype.run = function(){ 83 | var globalContextOld = global.context; 84 | for (var mockName in global.mocks) if (this.mockName.test(mockName)) { 85 | 86 | global.context = global.mocks[mockName]; 87 | for (var i = 0, l = global.willDefineEverywhere.length; i < l; i++) { 88 | try { 89 | global.willDefineEverywhere[i](global.context); 90 | } finally { 91 | continue; 92 | } 93 | } 94 | Describe(mockName,this.testBuilder); 95 | 96 | } 97 | global.context = globalContextOld; 98 | }; 99 | 100 | Mock.register = function(name, window){ 101 | clearTimeout(Mock.register.delay); 102 | global.mocks[name] = window; 103 | Mock.register.delay = setTimeout(Mock.register.done, 1000); 104 | }; 105 | 106 | Mock.register.done = function(){ 107 | for (var i=0; i < Mock.mocks.length; i++){ 108 | try { 109 | Mock.mocks[i].run(); 110 | } finally { 111 | continue; 112 | } 113 | } 114 | 115 | global.runSpecs(); 116 | }; 117 | 118 | 119 | return Mock; 120 | })(); 121 | 122 | Mock.Request = function(mockName, url){ 123 | if (!this instanceof Mock.Request) throw new Error('Mock.Request is not callable directly. Must use `new Mock.Request`'); 124 | 125 | this.mockName = mockName; 126 | this.url = url; 127 | 128 | var self = this; 129 | this.callback = function(html, xml){ 130 | Mock.register(self.mockName +': '+ String(self.url).replace(/^.*\//,''), newFakeWinFromDoc(xml)); 131 | }; 132 | this.rq = new SimpleRequest(); 133 | this.rq.send(this.url, this.callback); 134 | }; 135 | 136 | global.willDefineEverywhere = []; 137 | Mock.defineEverywhere = function(definer){ 138 | global.willDefineEverywhere.push(definer); 139 | } 140 | 141 | var TODO = function(){ throw "TODO: This test has not be written yet"; }; 142 | 143 | if(typeof JSSpec == 'undefined') var JSSpec = {}; 144 | if(!JSSpec.Browser) JSSpec.Browser = {}; 145 | JSSpec.Browser.NativeConsole = !!(('console' in this) && ('log' in console) && ('toString' in console.log) && console.log.toString().match(/\[native code\]/)); 146 | JSSpec.Browser.Trident = (JSSpec.Browser.Trident && !JSSpec.Browser.NativeConsole); 147 | 148 | // Stop the normal JSSpec onload from firing yet 149 | var runSpecs_actually = window.onload; 150 | // console.time('runSpecs'); 151 | var runSpecs = function(){ 152 | // console.timeEnd('runSpecs'); 153 | // console.time('runSpecs'); 154 | // console.log('runSpecs'); 155 | clearTimeout(global.runSpecs_timer); 156 | global.runSpecs_timer = setTimeout(runSpecs_actually, 1000); 157 | }; 158 | window.onload = function(){ 159 | window.loaded = true; 160 | // setTimeout(runSpecs, 100); 161 | }; 162 | 163 | 164 | // XML 165 | // from http://www.webreference.com/programming/javascript/definitive2/ 166 | /** 167 | * Create a new Document object. If no arguments are specified, 168 | * the document will be empty. If a root tag is specified, the document 169 | * will contain that single root tag. If the root tag has a namespace 170 | * prefix, the second argument must specify the URL that identifies the 171 | * namespace. 172 | */ 173 | var newXMLDocument = (document.implementation && document.implementation.createDocument) 174 | ? function(rootTagName, namespaceURL){ 175 | return document.implementation.createDocument(namespaceURL||'', rootTagName||'', null); 176 | } 177 | : function(rootTagName, namespaceURL){ 178 | if (!rootTagName) rootTagName = ""; 179 | if (!namespaceURL) namespaceURL = ""; 180 | // This is the IE way to do it 181 | // Create an empty document as an ActiveX object 182 | // If there is no root element, this is all we have to do 183 | var doc = new ActiveXObject("MSXML2.DOMDocument"); 184 | // If there is a root tag, initialize the document 185 | if (rootTagName) { 186 | // Look for a namespace prefix 187 | var prefix = ""; 188 | var tagname = rootTagName; 189 | var p = rootTagName.indexOf(':'); 190 | if (p != -1) { 191 | prefix = rootTagName.substring(0, p); 192 | tagname = rootTagName.substring(p+1); 193 | } 194 | // If we have a namespace, we must have a namespace prefix 195 | // If we don't have a namespace, we discard any prefix 196 | if (namespaceURL) { 197 | if (!prefix) prefix = "a0"; // What Firefox uses 198 | } 199 | else prefix = ""; 200 | // Create the root element (with optional namespace) as a 201 | // string of text 202 | var text = "<" + (prefix?(prefix+":"):"") + tagname + 203 | (namespaceURL 204 | ?(" xmlns:" + prefix + '="' + namespaceURL +'"') 205 | :"") + 206 | "/>"; 207 | // And parse that text into the empty document 208 | doc.loadXML(text); 209 | } 210 | return doc; 211 | }; 212 | 213 | /** 214 | * Synchronously load the XML document at the specified URL and 215 | * return it as a Document object 216 | */ 217 | var loadXML = function(url) { 218 | // Create a new document with the previously defined function 219 | var xmldoc = newXMLDocument(); 220 | xmldoc.async = false; // We want to load synchronously 221 | xmldoc.load(url); // Load and parse 222 | return xmldoc; // Return the document 223 | }; 224 | 225 | /** 226 | * Parse the XML document contained in the string argument and return 227 | * a Document object that represents it. 228 | */ 229 | var parseXML = (function(){ 230 | 231 | // Mozilla, Firefox, and related browsers 232 | if (typeof DOMParser != "undefined") 233 | return function(rawXML){ 234 | return (new DOMParser()).parseFromString(rawXML, "application/xml"); 235 | }; 236 | 237 | // Internet Explorer. 238 | if (typeof ActiveXObject != "undefined") 239 | return function(rawXML){ 240 | var xmlDocument = new ActiveXObject("Microsoft.XMLDOM"); 241 | xmlDocument.async = false; 242 | xmlDocument.loadXML(rawXML); 243 | 244 | if (xmlDocument.parseError && xmlDocument.parseError.errorCode) 245 | xmlDocument.loadXML(rawXML.replace(/]*?>/i,'')); 246 | 247 | if (xmlDocument.parseError && xmlDocument.parseError.errorCode) 248 | throw new Error(['' 249 | ,("Error code: " + xmlDocument.parseError.errorCode) 250 | ,("Error reason: " + xmlDocument.parseError.reason) 251 | ,("Error line: " + xmlDocument.parseError.line) 252 | ,rawXML 253 | ].join('\n')); 254 | 255 | return xmlDocument; 256 | }; 257 | 258 | // As a last resort, try loading the document from a data: URL 259 | // This is supposed to work in Safari. Thanks to Manos Batsis and 260 | // his Sarissa library (sarissa.sourceforge.net) for this technique. 261 | return function(rawXML){ 262 | var url = "data:text/xml;charset=utf-8," + encodeURIComponent(rawXML); 263 | var request = new XMLHttpRequest(); 264 | request.open("GET", url, false); 265 | request.send(null); 266 | return request.responseXML; 267 | }; 268 | 269 | })(); 270 | 271 | 272 | function getXML(url,mime){ 273 | if (!mime && url && /\.(svg|xml|xhtml)/i.test(url)) 274 | mime = 'text/xml'; 275 | 276 | var request; 277 | if (XMLHttpRequest != undefined) 278 | request = new XMLHttpRequest(); 279 | else 280 | request = new ActiveXObject("Microsoft.XMLHTTP"); 281 | 282 | request.open("GET", url, false); 283 | if (mime && request.overrideMimeType) request.overrideMimeType(mime); 284 | request.send(null); 285 | return request; 286 | return request.responseXML || parseXML(request.responseText); 287 | 288 | }; 289 | 290 | 291 | function newFakeWinFromDoc(document){ 292 | var fakeWin = { fake:true }; 293 | fakeWin.document = document; 294 | // fakeWin.document = getXML('/test/tools/MooTools_Logo.svg'); 295 | // fakeWin.document = loadXML('tools/MooTools_Logo.svg'); 296 | // fakeWin.document = document.getElementById('svg_logo_data_island').document; 297 | // fakeWin.document = parseXML(''); 298 | 299 | fakeWin.SELECT = function(context, expression){ 300 | return global.SELECT.call(fakeWin, context, expression); 301 | }; 302 | // if (fakeWin.document) fakeWin.document.search = function(expression){ 303 | // return SELECT(fakeWin.document, expression); 304 | // }; 305 | 306 | return fakeWin; 307 | }; 308 | 309 | -------------------------------------------------------------------------------- /SlickSpec/JSSpec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSSpec 3 | * 4 | * Copyright 2007 Alan Kang 5 | * - mailto:jania902@gmail.com 6 | * - http://jania.pe.kr 7 | * 8 | * http://jania.pe.kr/aw/moin.cgi/JSSpec 9 | * 10 | * Dependencies: 11 | * - diff_match_patch.js ( http://code.google.com/p/google-diff-match-patch ) 12 | * 13 | * This library is free software; you can redistribute it and/or 14 | * modify it under the terms of the GNU Lesser General Public 15 | * License as published by the Free Software Foundation; either 16 | * version 2.1 of the License, or (at your option) any later version. 17 | * 18 | * This library is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 | * Lesser General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Lesser General Public 24 | * License along with this library; if not, write to the Free Software 25 | * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 26 | */ 27 | 28 | /** 29 | * Namespace 30 | */ 31 | 32 | var baseQueryString = window.location.search.replace(/&?rerun=.*?(&|$)/ig,'').replace(/&$/,''); 33 | if (!baseQueryString) baseQueryString = '?'; else baseQueryString += '&'; 34 | 35 | var JSSpec = { 36 | specs: [], 37 | 38 | EMPTY_FUNCTION: function() {}, 39 | 40 | Browser: { 41 | // By Rendering Engines 42 | Trident: navigator.appName === "Microsoft Internet Explorer", 43 | Webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1, 44 | Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') === -1, 45 | KHTML: navigator.userAgent.indexOf('KHTML') !== -1, 46 | Presto: navigator.appName === "Opera", 47 | 48 | // By Platforms 49 | Mac: navigator.userAgent.indexOf("Macintosh") !== -1, 50 | Ubuntu: navigator.userAgent.indexOf('Ubuntu') !== -1, 51 | Win: navigator.userAgent.indexOf('Windows') !== -1, 52 | 53 | // By Browsers 54 | IE: navigator.appName === "Microsoft Internet Explorer", 55 | IE6: navigator.userAgent.indexOf('MSIE 6') !== -1, 56 | IE7: navigator.userAgent.indexOf('MSIE 7') !== -1, 57 | IE8: navigator.userAgent.indexOf('MSIE 8') !== -1, 58 | 59 | FF: navigator.userAgent.indexOf('Firefox') !== -1, 60 | FF2: navigator.userAgent.indexOf('Firefox/2') !== -1, 61 | FF3: navigator.userAgent.indexOf('Firefox/3') !== -1, 62 | Safari: navigator.userAgent.indexOf('Safari') !== -1 63 | } 64 | }; 65 | 66 | 67 | 68 | /** 69 | * Executor 70 | */ 71 | JSSpec.Executor = function(target, onSuccess, onException) { 72 | this.target = target; 73 | this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION; 74 | this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION; 75 | 76 | if(JSSpec.Browser.Trident) { 77 | // Exception handler for Trident. It helps to collect exact line number where exception occured. 78 | window.onerror = function(message, fileName, lineNumber) { 79 | var self = window._curExecutor; 80 | var ex = {message:message, fileName:fileName, lineNumber:lineNumber}; 81 | 82 | if(JSSpec._secondPass) { 83 | ex = self.mergeExceptions(JSSpec._assertionFailure, ex); 84 | delete JSSpec._secondPass; 85 | delete JSSpec._assertionFailure; 86 | 87 | ex.type = "failure"; 88 | self.onException(self, ex); 89 | } else if(JSSpec._assertionFailure) { 90 | JSSpec._secondPass = true; 91 | self.run(); 92 | } else { 93 | self.onException(self, ex); 94 | } 95 | 96 | return true; 97 | }; 98 | } 99 | }; 100 | JSSpec.Executor.prototype.mergeExceptions = function(assertionFailure, normalException) { 101 | var merged = { 102 | message:assertionFailure.message, 103 | fileName:normalException.fileName, 104 | lineNumber:normalException.lineNumber 105 | }; 106 | 107 | return merged; 108 | }; 109 | 110 | JSSpec.Executor.prototype.run = function() { 111 | var self = this; 112 | var target = this.target; 113 | var onSuccess = this.onSuccess; 114 | var onException = this.onException; 115 | 116 | window.setTimeout( 117 | function() { 118 | var result; 119 | if(JSSpec.Browser.Trident) { 120 | window._curExecutor = self; 121 | 122 | result = self.target(); 123 | self.onSuccess(self, result); 124 | } else { 125 | try { 126 | result = self.target(); 127 | self.onSuccess(self, result); 128 | } catch(ex) { 129 | if(JSSpec.Browser.Webkit) ex = {message:ex.message, fileName:ex.sourceURL, lineNumber:ex.line}; 130 | 131 | if(JSSpec._secondPass) { 132 | ex = self.mergeExceptions(JSSpec._assertionFailure, ex); 133 | delete JSSpec._secondPass; 134 | delete JSSpec._assertionFailure; 135 | 136 | ex.type = "failure"; 137 | self.onException(self, ex); 138 | } else if(JSSpec._assertionFailure) { 139 | JSSpec._secondPass = true; 140 | self.run(); 141 | } else { 142 | self.onException(self, ex); 143 | } 144 | } 145 | } 146 | }, 147 | 0 148 | ); 149 | }; 150 | 151 | 152 | 153 | /** 154 | * CompositeExecutor composites one or more executors and execute them sequencially. 155 | */ 156 | JSSpec.CompositeExecutor = function(onSuccess, onException, continueOnException) { 157 | this.queue = []; 158 | this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION; 159 | this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION; 160 | this.continueOnException = !!continueOnException; 161 | }; 162 | 163 | JSSpec.CompositeExecutor.prototype.addFunction = function(func) { 164 | this.addExecutor(new JSSpec.Executor(func)); 165 | }; 166 | 167 | JSSpec.CompositeExecutor.prototype.addExecutor = function(executor) { 168 | var last = this.queue.length == 0 ? null : this.queue[this.queue.length - 1]; 169 | if(last) { 170 | last.next = executor; 171 | } 172 | 173 | executor.parent = this; 174 | executor.onSuccessBackup = executor.onSuccess; 175 | executor.onSuccess = function(result) { 176 | this.onSuccessBackup(result); 177 | if(this.next) { 178 | this.next.run(); 179 | } else { 180 | this.parent.onSuccess(); 181 | } 182 | }; 183 | executor.onExceptionBackup = executor.onException; 184 | executor.onException = function(executor, ex) { 185 | this.onExceptionBackup(executor, ex); 186 | 187 | if(this.parent.continueOnException) { 188 | if(this.next) { 189 | this.next.run(); 190 | } else { 191 | this.parent.onSuccess(); 192 | } 193 | } else { 194 | this.parent.onException(executor, ex); 195 | } 196 | }; 197 | 198 | this.queue.push(executor); 199 | }; 200 | 201 | JSSpec.CompositeExecutor.prototype.run = function() { 202 | if(this.queue.length > 0) { 203 | this.queue[0].run(); 204 | } 205 | }; 206 | 207 | /** 208 | * Spec is a set of Examples in a specific context 209 | */ 210 | JSSpec.Spec = function(context, entries) { 211 | this.id = JSSpec.Spec.id++; 212 | this.context = context; 213 | this.url = location.href; 214 | 215 | this.filterEntriesByEmbeddedExpressions(entries); 216 | this.extractOutSpecialEntries(entries); 217 | this.examples = this.makeExamplesFromEntries(entries); 218 | this.examplesMap = this.makeMapFromExamples(this.examples); 219 | }; 220 | 221 | JSSpec.Spec.id = 0; 222 | JSSpec.Spec.prototype.getExamples = function() { 223 | return this.examples; 224 | }; 225 | 226 | JSSpec.Spec.prototype.hasException = function() { 227 | return this.getTotalFailures() > 0 || this.getTotalErrors() > 0; 228 | }; 229 | 230 | JSSpec.Spec.prototype.getTotalFailures = function() { 231 | var examples = this.examples; 232 | var failures = 0; 233 | for(var i = 0; i < examples.length; i++) { 234 | if(examples[i].isFailure()) failures++; 235 | } 236 | return failures; 237 | }; 238 | 239 | JSSpec.Spec.prototype.getTotalErrors = function() { 240 | var examples = this.examples; 241 | var errors = 0; 242 | for(var i = 0; i < examples.length; i++) { 243 | if(examples[i].isError()) errors++; 244 | } 245 | return errors; 246 | }; 247 | 248 | JSSpec.Spec.prototype.filterEntriesByEmbeddedExpressions = function(entries) { 249 | var isTrue; 250 | for(name in entries) if(entries.hasOwnProperty(name)) { 251 | var m = name.match(/\[\[(.+)\]\]/); 252 | if(m && m[1]) { 253 | eval("isTrue = (" + m[1] + ")"); 254 | if(!isTrue) delete entries[name]; 255 | } 256 | } 257 | }; 258 | 259 | JSSpec.Spec.prototype.extractOutSpecialEntries = function(entries) { 260 | this.beforeEach = JSSpec.EMPTY_FUNCTION; 261 | this.beforeAll = JSSpec.EMPTY_FUNCTION; 262 | this.afterEach = JSSpec.EMPTY_FUNCTION; 263 | this.afterAll = JSSpec.EMPTY_FUNCTION; 264 | 265 | for(name in entries) if(entries.hasOwnProperty(name)) { 266 | if(name == 'before' || name == 'before each' || name == 'before_each') { 267 | this.beforeEach = entries[name]; 268 | } else if(name == 'before all' || name == 'before_all') { 269 | this.beforeAll = entries[name]; 270 | } else if(name == 'after' || name == 'after each' || name == 'after_each') { 271 | this.afterEach = entries[name]; 272 | } else if(name == 'after all' || name == 'after_all') { 273 | this.afterAll = entries[name]; 274 | } 275 | } 276 | 277 | delete entries['before']; 278 | delete entries['before each']; 279 | delete entries['before_each']; 280 | delete entries['before all']; 281 | delete entries['before_all']; 282 | delete entries['after']; 283 | delete entries['after each']; 284 | delete entries['after_each']; 285 | delete entries['after all']; 286 | delete entries['after_all']; 287 | }; 288 | 289 | JSSpec.Spec.prototype.makeExamplesFromEntries = function(entries) { 290 | var examples = []; 291 | for(name in entries) if(entries.hasOwnProperty(name)) { 292 | examples.push(new JSSpec.Example(name, entries[name], this.beforeEach, this.afterEach)); 293 | } 294 | return examples; 295 | }; 296 | 297 | JSSpec.Spec.prototype.makeMapFromExamples = function(examples) { 298 | var map = {}; 299 | for(var i = 0; i < examples.length; i++) { 300 | var example = examples[i]; 301 | map[example.id] = examples[i]; 302 | } 303 | return map; 304 | }; 305 | 306 | JSSpec.Spec.prototype.getExampleById = function(id) { 307 | return this.examplesMap[id]; 308 | }; 309 | 310 | JSSpec.Spec.prototype.getExecutor = function() { 311 | var self = this; 312 | var onException = function(executor, ex) { 313 | self.exception = ex; 314 | }; 315 | 316 | var composite = new JSSpec.CompositeExecutor(); 317 | composite.addFunction(function() {JSSpec.log.onSpecStart(self);}); 318 | composite.addExecutor(new JSSpec.Executor(this.beforeAll, null, function(exec, ex) { 319 | self.exception = ex; 320 | JSSpec.log.onSpecEnd(self); 321 | })); 322 | 323 | var exampleAndAfter = new JSSpec.CompositeExecutor(null,null,true); 324 | for(var i = 0; i < this.examples.length; i++) { 325 | exampleAndAfter.addExecutor(this.examples[i].getExecutor()); 326 | } 327 | exampleAndAfter.addExecutor(new JSSpec.Executor(this.afterAll, null, onException)); 328 | exampleAndAfter.addFunction(function() {JSSpec.log.onSpecEnd(self);}); 329 | composite.addExecutor(exampleAndAfter); 330 | 331 | return composite; 332 | }; 333 | 334 | /** 335 | * Example 336 | */ 337 | JSSpec.Example = function(name, target, before, after) { 338 | this.id = JSSpec.Example.id++; 339 | this.name = name; 340 | this.target = target; 341 | this.before = before; 342 | this.after = after; 343 | }; 344 | 345 | JSSpec.Example.id = 0; 346 | JSSpec.Example.prototype.isFailure = function() { 347 | return this.exception && this.exception.type == "failure"; 348 | }; 349 | 350 | JSSpec.Example.prototype.isError = function() { 351 | return this.exception && !this.exception.type; 352 | }; 353 | 354 | JSSpec.Example.prototype.getExecutor = function() { 355 | var self = this; 356 | var onException = function(executor, ex) { 357 | self.exception = ex; 358 | }; 359 | 360 | var composite = new JSSpec.CompositeExecutor(); 361 | composite.addFunction(function() {JSSpec.log.onExampleStart(self);}); 362 | composite.addExecutor(new JSSpec.Executor(this.before, null, function(exec, ex) { 363 | self.exception = ex; 364 | JSSpec.log.onExampleEnd(self); 365 | })); 366 | 367 | var targetAndAfter = new JSSpec.CompositeExecutor(null,null,true); 368 | 369 | targetAndAfter.addExecutor(new JSSpec.Executor(this.target, null, onException)); 370 | targetAndAfter.addExecutor(new JSSpec.Executor(this.after, null, onException)); 371 | targetAndAfter.addFunction(function() {JSSpec.log.onExampleEnd(self);}); 372 | 373 | composite.addExecutor(targetAndAfter); 374 | 375 | return composite; 376 | }; 377 | 378 | /** 379 | * Runner 380 | */ 381 | JSSpec.Runner = function(specs, logger) { 382 | JSSpec.log = logger; 383 | 384 | this.totalExamples = 0; 385 | this.specs = []; 386 | this.specsMap = {}; 387 | this.addAllSpecs(specs); 388 | }; 389 | 390 | JSSpec.Runner.prototype.addAllSpecs = function(specs) { 391 | for(var i = 0; i < specs.length; i++) { 392 | this.addSpec(specs[i]); 393 | } 394 | }; 395 | 396 | JSSpec.Runner.prototype.addSpec = function(spec) { 397 | this.specs.push(spec); 398 | this.specsMap[spec.id] = spec; 399 | this.totalExamples += spec.getExamples().length; 400 | }; 401 | 402 | JSSpec.Runner.prototype.getSpecById = function(id) { 403 | return this.specsMap[id]; 404 | }; 405 | 406 | JSSpec.Runner.prototype.getSpecByContext = function(context) { 407 | for(var i = 0; i < this.specs.length; i++) { 408 | if(this.specs[i].context == context) return this.specs[i]; 409 | } 410 | return null; 411 | }; 412 | 413 | JSSpec.Runner.prototype.getSpecs = function() { 414 | return this.specs; 415 | }; 416 | 417 | JSSpec.Runner.prototype.hasException = function() { 418 | return this.getTotalFailures() > 0 || this.getTotalErrors() > 0; 419 | }; 420 | 421 | JSSpec.Runner.prototype.getTotalFailures = function() { 422 | var specs = this.specs; 423 | var failures = 0; 424 | for(var i = 0; i < specs.length; i++) { 425 | failures += specs[i].getTotalFailures(); 426 | } 427 | return failures; 428 | }; 429 | 430 | JSSpec.Runner.prototype.getTotalErrors = function() { 431 | var specs = this.specs; 432 | var errors = 0; 433 | for(var i = 0; i < specs.length; i++) { 434 | errors += specs[i].getTotalErrors(); 435 | } 436 | return errors; 437 | }; 438 | 439 | 440 | JSSpec.Runner.prototype.run = function() { 441 | JSSpec.log.onRunnerStart(); 442 | var executor = new JSSpec.CompositeExecutor(function() {JSSpec.log.onRunnerEnd()},null,true); 443 | for(var i = 0; i < this.specs.length; i++) { 444 | executor.addExecutor(this.specs[i].getExecutor()); 445 | } 446 | executor.run(); 447 | }; 448 | 449 | 450 | JSSpec.Runner.prototype.rerun = function(context) { 451 | JSSpec.runner = new JSSpec.Runner([this.getSpecByContext(context)], JSSpec.log); 452 | JSSpec.runner.run(); 453 | }; 454 | 455 | /** 456 | * Logger 457 | */ 458 | JSSpec.Logger = function() { 459 | this.finishedExamples = 0; 460 | this.startedAt = null; 461 | }; 462 | 463 | JSSpec.Logger.prototype.onRunnerStart = function() { 464 | this._title = document.title; 465 | 466 | this.startedAt = new Date(); 467 | var container = document.getElementById('jsspec_container'); 468 | if(container) { 469 | container.innerHTML = ""; 470 | } else { 471 | container = document.createElement("DIV"); 472 | container.id = "jsspec_container"; 473 | document.body.appendChild(container); 474 | } 475 | 476 | var title = document.createElement("DIV"); 477 | title.id = "title"; 478 | title.innerHTML = [ 479 | '

JSSpec

', 480 | '
    ', 481 | JSSpec.options.rerun ? '
  • [X] ' + JSSpec.util.escapeTags(decodeURIComponent(JSSpec.options.rerun)) + '
  • ' : '', 482 | '
  • ' + JSSpec.runner.totalExamples + ' examples
  • ', 483 | '
  • 0 failures
  • ', 484 | '
  • 0 errors
  • ', 485 | '
  • 0% done
  • ', 486 | '
  • 0 secs
  • ', 487 | '
', 488 | '

JSSpec homepage

', 489 | ].join(""); 490 | container.appendChild(title); 491 | 492 | var list = document.createElement("DIV"); 493 | list.id = "list"; 494 | list.innerHTML = [ 495 | '

List

', 496 | '
    ', 497 | function() { 498 | var specs = JSSpec.runner.getSpecs(); 499 | var sb = []; 500 | for(var i = 0; i < specs.length; i++) { 501 | var spec = specs[i]; 502 | sb.push('
  • ' + JSSpec.util.escapeTags(specs[i].context) + ' [rerun]

  • '); 503 | } 504 | return sb.join(""); 505 | }(), 506 | '
' 507 | ].join(""); 508 | container.appendChild(list); 509 | 510 | var log = document.createElement("DIV"); 511 | log.id = "log"; 512 | log.innerHTML = [ 513 | '

Log

', 514 | '
    ', 515 | function() { 516 | var specs = JSSpec.runner.getSpecs(); 517 | var sb = []; 518 | for(var i = 0; i < specs.length; i++) { 519 | var spec = specs[i]; 520 | sb.push('
  • '); 521 | sb.push('

    ' + JSSpec.util.escapeTags(specs[i].context) + ' [rerun]

    '); 522 | sb.push('
      '); 523 | for(var j = 0; j < spec.examples.length; j++) { 524 | var example = spec.examples[j]; 525 | sb.push('
    • '); 526 | sb.push('

      ' + JSSpec.util.escapeTags(example.name) + '

      '); 527 | sb.push('
      '+JSSpec.util.escapeTags(example.target.toString())+'
      '); 528 | sb.push('
    • '); 529 | } 530 | sb.push('
    '); 531 | sb.push('
  • '); 532 | } 533 | return sb.join(""); 534 | }(), 535 | '
' 536 | ].join(""); 537 | 538 | container.appendChild(log); 539 | 540 | // add event handler for toggling 541 | var specs = JSSpec.runner.getSpecs(); 542 | var sb = []; 543 | for(var i = 0; i < specs.length; i++) { 544 | var spec = document.getElementById("spec_" + specs[i].id); 545 | var title = spec.getElementsByTagName("H3")[0]; 546 | title.onclick = function(e) { 547 | var target = document.getElementById(this.parentNode.id + "_examples"); 548 | target.style.display = target.style.display == "none" ? "block" : "none"; 549 | return true; 550 | } 551 | } 552 | }; 553 | 554 | JSSpec.Logger.prototype.onRunnerEnd = function() { 555 | if(JSSpec.runner.hasException()) { 556 | var times = 4; 557 | var title1 = "*" + this._title; 558 | var title2 = "*F" + JSSpec.runner.getTotalFailures() + " E" + JSSpec.runner.getTotalErrors() + "* " + this._title; 559 | } else { 560 | var times = 2; 561 | var title1 = this._title; 562 | var title2 = "Success"; 563 | } 564 | this.blinkTitle(times,title1,title2); 565 | }; 566 | 567 | JSSpec.Logger.prototype.blinkTitle = function(times, title1, title2) { 568 | var times = times * 2; 569 | var mode = true; 570 | 571 | var f = function() { 572 | if(times > 0) { 573 | document.title = mode ? title1 : title2; 574 | mode = !mode; 575 | times--; 576 | window.setTimeout(f, 500); 577 | } else { 578 | document.title = title1; 579 | } 580 | }; 581 | 582 | f(); 583 | }; 584 | 585 | JSSpec.Logger.prototype.onSpecStart = function(spec) { 586 | var spec_list = document.getElementById("spec_" + spec.id + "_list"); 587 | var spec_log = document.getElementById("spec_" + spec.id); 588 | 589 | spec_list.className = "ongoing"; 590 | spec_log.className = "ongoing"; 591 | }; 592 | 593 | JSSpec.Logger.prototype.onSpecEnd = function(spec) { 594 | var spec_list = document.getElementById("spec_" + spec.id + "_list"); 595 | var spec_log = document.getElementById("spec_" + spec.id); 596 | var examples = document.getElementById("spec_" + spec.id + "_examples"); 597 | var className = spec.hasException() ? "exception" : "success"; 598 | 599 | spec_list.className = className; 600 | spec_log.className = className; 601 | 602 | if(JSSpec.options.autocollapse && !spec.hasException()) examples.style.display = "none"; 603 | 604 | if(spec.exception) { 605 | spec_log.appendChild(document.createTextNode(" - " + spec.exception.message)); 606 | } 607 | }; 608 | 609 | JSSpec.Logger.prototype.onExampleStart = function(example) { 610 | var li = document.getElementById("example_" + example.id); 611 | li.className = "ongoing"; 612 | }; 613 | 614 | JSSpec.Logger.prototype.onExampleEnd = function(example) { 615 | var li = document.getElementById("example_" + example.id); 616 | li.className = example.exception ? "exception" : "success"; 617 | 618 | if(example.exception) { 619 | var div = document.createElement("DIV"); 620 | div.innerHTML = example.exception.message + "


" + " at " + example.exception.fileName + ", line " + example.exception.lineNumber + "

"; 621 | li.appendChild(div); 622 | } 623 | 624 | var title = document.getElementById("title"); 625 | var runner = JSSpec.runner; 626 | 627 | title.className = runner.hasException() ? "exception" : "success"; 628 | 629 | this.finishedExamples++; 630 | document.getElementById("total_failures").innerHTML = runner.getTotalFailures(); 631 | document.getElementById("total_errors").innerHTML = runner.getTotalErrors(); 632 | var progress = parseInt(this.finishedExamples / runner.totalExamples * 100); 633 | document.getElementById("progress").innerHTML = progress; 634 | document.getElementById("total_elapsed").innerHTML = (new Date().getTime() - this.startedAt.getTime()) / 1000; 635 | 636 | document.title = progress + "%: " + this._title; 637 | }; 638 | 639 | /** 640 | * IncludeMatcher 641 | */ 642 | JSSpec.IncludeMatcher = function(actual, expected, condition) { 643 | this.actual = actual; 644 | this.expected = expected; 645 | this.condition = condition; 646 | this.match = false; 647 | this.explaination = this.makeExplain(); 648 | }; 649 | 650 | JSSpec.IncludeMatcher.createInstance = function(actual, expected, condition) { 651 | return new JSSpec.IncludeMatcher(actual, expected, condition); 652 | }; 653 | 654 | JSSpec.IncludeMatcher.prototype.matches = function() { 655 | return this.match; 656 | }; 657 | 658 | JSSpec.IncludeMatcher.prototype.explain = function() { 659 | return this.explaination; 660 | }; 661 | 662 | JSSpec.IncludeMatcher.prototype.makeExplain = function() { 663 | if(typeof this.actual.length == 'undefined') { 664 | return this.makeExplainForNotArray(); 665 | } else { 666 | return this.makeExplainForArray(); 667 | } 668 | }; 669 | 670 | JSSpec.IncludeMatcher.prototype.makeExplainForNotArray = function() { 671 | if(this.condition) { 672 | this.match = !!this.actual[this.expected]; 673 | } else { 674 | this.match = !this.actual[this.expected]; 675 | } 676 | 677 | var sb = []; 678 | sb.push('

actual value:

'); 679 | sb.push('

' + JSSpec.util.inspect(this.actual, false, this.expected) + '

'); 680 | sb.push('

should ' + (this.condition ? '' : 'not') + ' include:

'); 681 | sb.push('

' + JSSpec.util.inspect(this.expected) + '

'); 682 | return sb.join(""); 683 | }; 684 | 685 | JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() { 686 | var matches; 687 | if(this.condition) { 688 | for(var i = 0; i < this.actual.length; i++) { 689 | matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches(); 690 | if(matches) { 691 | this.match = true; 692 | break; 693 | } 694 | } 695 | } else { 696 | for(var i = 0; i < this.actual.length; i++) { 697 | matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches(); 698 | if(matches) { 699 | this.match = false; 700 | break; 701 | } 702 | } 703 | } 704 | 705 | if(this.match) return ""; 706 | 707 | var sb = []; 708 | sb.push('

actual value:

'); 709 | sb.push('

' + JSSpec.util.inspect(this.actual, false, this.condition ? null : i) + '

'); 710 | sb.push('

should ' + (this.condition ? '' : 'not') + ' include:

'); 711 | sb.push('

' + JSSpec.util.inspect(this.expected) + '

'); 712 | return sb.join(""); 713 | }; 714 | 715 | /** 716 | * PropertyLengthMatcher 717 | */ 718 | JSSpec.PropertyLengthMatcher = function(num, property, o, condition) { 719 | this.num = num; 720 | this.o = o; 721 | this.property = property; 722 | if((property == 'characters' || property == 'items') && typeof o.length != 'undefined') { 723 | this.property = 'length'; 724 | } 725 | 726 | this.condition = condition; 727 | this.conditionMet = function(x) { 728 | if(condition == 'exactly') return x.length == num; 729 | if(condition == 'at least') return x.length >= num; 730 | if(condition == 'at most') return x.length <= num; 731 | 732 | throw "Unknown condition '" + condition + "'"; 733 | }; 734 | this.match = false; 735 | this.explaination = this.makeExplain(); 736 | }; 737 | 738 | JSSpec.PropertyLengthMatcher.prototype.makeExplain = function() { 739 | if(this.o._type == 'String' && this.property == 'length') { 740 | this.match = this.conditionMet(this.o); 741 | return this.match ? '' : this.makeExplainForString(); 742 | } else if(typeof this.o.length != 'undefined' && this.property == "length") { 743 | this.match = this.conditionMet(this.o); 744 | return this.match ? '' : this.makeExplainForArray(); 745 | } else if(typeof this.o[this.property] != 'undefined' && this.o[this.property] != null) { 746 | this.match = this.conditionMet(this.o[this.property]); 747 | return this.match ? '' : this.makeExplainForObject(); 748 | } else if(typeof this.o[this.property] == 'undefined' || this.o[this.property] == null) { 749 | this.match = false; 750 | return this.makeExplainForNoProperty(); 751 | } 752 | 753 | this.match = true; 754 | }; 755 | 756 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForString = function() { 757 | var sb = []; 758 | 759 | var exp = this.num == 0 ? 760 | 'be an empty string' : 761 | 'have ' + this.condition + ' ' + this.num + ' characters'; 762 | 763 | sb.push('

actual value has ' + this.o.length + ' characters:

'); 764 | sb.push('

' + JSSpec.util.inspect(this.o) + '

'); 765 | sb.push('

but it should ' + exp + '.

'); 766 | 767 | return sb.join(""); 768 | }; 769 | 770 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForArray = function() { 771 | var sb = []; 772 | 773 | var exp = this.num == 0 ? 774 | 'be an empty array' : 775 | 'have ' + this.condition + ' ' + this.num + ' items'; 776 | 777 | sb.push('

actual value has ' + this.o.length + ' items:

'); 778 | sb.push('

' + JSSpec.util.inspect(this.o) + '

'); 779 | sb.push('

but it should ' + exp + '.

'); 780 | 781 | return sb.join(""); 782 | }; 783 | 784 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForObject = function() { 785 | var sb = []; 786 | 787 | var exp = this.num == 0 ? 788 | 'be empty' : 789 | 'have ' + this.condition + ' ' + this.num + ' ' + this.property + '.'; 790 | 791 | sb.push('

actual value has ' + this.o[this.property].length + ' ' + this.property + ':

'); 792 | sb.push('

' + JSSpec.util.inspect(this.o, false, this.property) + '

'); 793 | sb.push('

but it should ' + exp + '.

'); 794 | 795 | return sb.join(""); 796 | }; 797 | 798 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForNoProperty = function() { 799 | var sb = []; 800 | 801 | sb.push('

actual value:

'); 802 | sb.push('

' + JSSpec.util.inspect(this.o) + '

'); 803 | sb.push('

should have ' + this.condition + ' ' + this.num + ' ' + this.property + ' but there\'s no such property.

'); 804 | 805 | return sb.join(""); 806 | }; 807 | 808 | JSSpec.PropertyLengthMatcher.prototype.matches = function() { 809 | return this.match; 810 | }; 811 | 812 | JSSpec.PropertyLengthMatcher.prototype.explain = function() { 813 | return this.explaination; 814 | }; 815 | 816 | JSSpec.PropertyLengthMatcher.createInstance = function(num, property, o, condition) { 817 | return new JSSpec.PropertyLengthMatcher(num, property, o, condition); 818 | }; 819 | 820 | /** 821 | * EqualityMatcher 822 | */ 823 | JSSpec.EqualityMatcher = {}; 824 | 825 | JSSpec.EqualityMatcher.createInstance = function(expected, actual) { 826 | if(expected == null || actual == null) { 827 | return new JSSpec.NullEqualityMatcher(expected, actual); 828 | } else if(expected._type && expected._type == actual._type) { 829 | if(expected._type == "String") { 830 | return new JSSpec.StringEqualityMatcher(expected, actual); 831 | } else if(expected._type == "Date") { 832 | return new JSSpec.DateEqualityMatcher(expected, actual); 833 | } else if(expected._type == "Number") { 834 | return new JSSpec.NumberEqualityMatcher(expected, actual); 835 | } else if(expected._type == "Array") { 836 | return new JSSpec.ArrayEqualityMatcher(expected, actual); 837 | } else if(expected._type == "Boolean") { 838 | return new JSSpec.BooleanEqualityMatcher(expected, actual); 839 | } 840 | } 841 | 842 | return new JSSpec.ObjectEqualityMatcher(expected, actual); 843 | }; 844 | 845 | JSSpec.EqualityMatcher.basicExplain = function(expected, actual, expectedDesc, actualDesc) { 846 | var sb = []; 847 | 848 | sb.push(actualDesc || '

actual value:

'); 849 | sb.push('

' + JSSpec.util.inspect(actual) + '

'); 850 | sb.push(expectedDesc || '

should be:

'); 851 | sb.push('

' + JSSpec.util.inspect(expected) + '

'); 852 | 853 | return sb.join(""); 854 | }; 855 | 856 | JSSpec.EqualityMatcher.diffExplain = function(expected, actual) { 857 | var sb = []; 858 | 859 | sb.push('

diff:

'); 860 | sb.push('

'); 861 | 862 | var dmp = new diff_match_patch(); 863 | var diff = dmp.diff_main(expected, actual); 864 | dmp.diff_cleanupEfficiency(diff); 865 | 866 | sb.push(JSSpec.util.inspect(dmp.diff_prettyHtml(diff), true)); 867 | 868 | sb.push('

'); 869 | 870 | return sb.join(""); 871 | }; 872 | 873 | /** 874 | * BooleanEqualityMatcher 875 | */ 876 | JSSpec.BooleanEqualityMatcher = function(expected, actual) { 877 | this.expected = expected; 878 | this.actual = actual; 879 | }; 880 | 881 | JSSpec.BooleanEqualityMatcher.prototype.explain = function() { 882 | var sb = []; 883 | 884 | sb.push('

actual value:

'); 885 | sb.push('

' + JSSpec.util.inspect(this.actual) + '

'); 886 | sb.push('

should be:

'); 887 | sb.push('

' + JSSpec.util.inspect(this.expected) + '

'); 888 | 889 | return sb.join(""); 890 | }; 891 | 892 | JSSpec.BooleanEqualityMatcher.prototype.matches = function() { 893 | return this.expected == this.actual; 894 | }; 895 | 896 | /** 897 | * NullEqualityMatcher 898 | */ 899 | JSSpec.NullEqualityMatcher = function(expected, actual) { 900 | this.expected = expected; 901 | this.actual = actual; 902 | }; 903 | 904 | JSSpec.NullEqualityMatcher.prototype.matches = function() { 905 | return this.expected == this.actual && typeof this.expected == typeof this.actual; 906 | }; 907 | 908 | JSSpec.NullEqualityMatcher.prototype.explain = function() { 909 | return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual); 910 | }; 911 | 912 | JSSpec.DateEqualityMatcher = function(expected, actual) { 913 | this.expected = expected; 914 | this.actual = actual; 915 | }; 916 | 917 | JSSpec.DateEqualityMatcher.prototype.matches = function() { 918 | return this.expected.getTime() == this.actual.getTime(); 919 | }; 920 | 921 | JSSpec.DateEqualityMatcher.prototype.explain = function() { 922 | var sb = []; 923 | 924 | sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); 925 | sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected.toString(), this.actual.toString())); 926 | 927 | return sb.join(""); 928 | }; 929 | 930 | /** 931 | * ObjectEqualityMatcher 932 | */ 933 | JSSpec.ObjectEqualityMatcher = function(expected, actual) { 934 | this.expected = expected; 935 | this.actual = actual; 936 | this.match = this.expected == this.actual; 937 | this.explaination = this.makeExplain(); 938 | }; 939 | 940 | JSSpec.ObjectEqualityMatcher.prototype.matches = function() {return this.match}; 941 | 942 | JSSpec.ObjectEqualityMatcher.prototype.explain = function() {return this.explaination}; 943 | 944 | JSSpec.ObjectEqualityMatcher.prototype.makeExplain = function() { 945 | if(this.expected == this.actual) { 946 | this.match = true; 947 | return ""; 948 | } 949 | 950 | if(JSSpec.util.isDomNode(this.expected)) { 951 | return this.makeExplainForDomNode(); 952 | } 953 | 954 | var key, expectedHasItem, actualHasItem; 955 | 956 | for(key in this.expected) { 957 | expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined'; 958 | actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined'; 959 | if(expectedHasItem && !actualHasItem) return this.makeExplainForMissingItem(key); 960 | } 961 | for(key in this.actual) { 962 | expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined'; 963 | actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined'; 964 | if(actualHasItem && !expectedHasItem) return this.makeExplainForUnknownItem(key); 965 | } 966 | 967 | for(key in this.expected) { 968 | var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[key], this.actual[key]); 969 | if(!matcher.matches()) return this.makeExplainForItemMismatch(key); 970 | } 971 | 972 | this.match = true; 973 | }; 974 | 975 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForDomNode = function(key) { 976 | var sb = []; 977 | 978 | sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); 979 | 980 | return sb.join(""); 981 | }; 982 | 983 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForMissingItem = function(key) { 984 | var sb = []; 985 | 986 | sb.push('

actual value has no item named ' + JSSpec.util.inspect(key) + '

'); 987 | sb.push('

' + JSSpec.util.inspect(this.actual, false, key) + '

'); 988 | sb.push('

but it should have the item whose value is ' + JSSpec.util.inspect(this.expected[key]) + '

'); 989 | sb.push('

' + JSSpec.util.inspect(this.expected, false, key) + '

'); 990 | 991 | return sb.join(""); 992 | }; 993 | 994 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForUnknownItem = function(key) { 995 | var sb = []; 996 | 997 | sb.push('

actual value has item named ' + JSSpec.util.inspect(key) + '

'); 998 | sb.push('

' + JSSpec.util.inspect(this.actual, false, key) + '

'); 999 | sb.push('

but there should be no such item

'); 1000 | sb.push('

' + JSSpec.util.inspect(this.expected, false, key) + '

'); 1001 | 1002 | return sb.join(""); 1003 | }; 1004 | 1005 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForItemMismatch = function(key) { 1006 | var sb = []; 1007 | 1008 | sb.push('

actual value has an item named ' + JSSpec.util.inspect(key) + ' whose value is ' + JSSpec.util.inspect(this.actual[key]) + '

'); 1009 | sb.push('

' + JSSpec.util.inspect(this.actual, false, key) + '

'); 1010 | sb.push('

but it\'s value should be ' + JSSpec.util.inspect(this.expected[key]) + '

'); 1011 | sb.push('

' + JSSpec.util.inspect(this.expected, false, key) + '

'); 1012 | 1013 | return sb.join(""); 1014 | }; 1015 | 1016 | 1017 | 1018 | 1019 | /** 1020 | * ArrayEqualityMatcher 1021 | */ 1022 | JSSpec.ArrayEqualityMatcher = function(expected, actual) { 1023 | this.expected = expected; 1024 | this.actual = actual; 1025 | this.match = this.expected == this.actual; 1026 | this.explaination = this.makeExplain(); 1027 | }; 1028 | 1029 | JSSpec.ArrayEqualityMatcher.prototype.matches = function() {return this.match}; 1030 | 1031 | JSSpec.ArrayEqualityMatcher.prototype.explain = function() {return this.explaination}; 1032 | 1033 | JSSpec.ArrayEqualityMatcher.prototype.makeExplain = function() { 1034 | if(this.expected.length != this.actual.length) return this.makeExplainForLengthMismatch(); 1035 | 1036 | for(var i = 0; i < this.expected.length; i++) { 1037 | var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[i], this.actual[i]); 1038 | if(!matcher.matches()) return this.makeExplainForItemMismatch(i); 1039 | } 1040 | 1041 | this.match = true; 1042 | }; 1043 | 1044 | JSSpec.ArrayEqualityMatcher.prototype.makeExplainForLengthMismatch = function() { 1045 | return JSSpec.EqualityMatcher.basicExplain( 1046 | this.expected, 1047 | this.actual, 1048 | '

but it should be ' + this.expected.length + '

', 1049 | '

actual value has ' + this.actual.length + ' items

' 1050 | ); 1051 | }; 1052 | 1053 | JSSpec.ArrayEqualityMatcher.prototype.makeExplainForItemMismatch = function(index) { 1054 | var postfix = ["th", "st", "nd", "rd", "th"][Math.min((index + 1) % 10,4)]; 1055 | 1056 | var sb = []; 1057 | 1058 | sb.push('

' + (index + 1) + postfix + ' item (index ' + index + ') of actual value is ' + JSSpec.util.inspect(this.actual[index]) + ':

'); 1059 | sb.push('

' + JSSpec.util.inspect(this.actual, false, index) + '

'); 1060 | sb.push('

but it should be ' + JSSpec.util.inspect(this.expected[index]) + ':

'); 1061 | sb.push('

' + JSSpec.util.inspect(this.expected, false, index) + '

'); 1062 | 1063 | return sb.join(""); 1064 | }; 1065 | 1066 | /** 1067 | * NumberEqualityMatcher 1068 | */ 1069 | JSSpec.NumberEqualityMatcher = function(expected, actual) { 1070 | this.expected = expected; 1071 | this.actual = actual; 1072 | }; 1073 | 1074 | JSSpec.NumberEqualityMatcher.prototype.matches = function() { 1075 | if(this.expected == this.actual) return true; 1076 | }; 1077 | 1078 | JSSpec.NumberEqualityMatcher.prototype.explain = function() { 1079 | return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual); 1080 | }; 1081 | 1082 | /** 1083 | * StringEqualityMatcher 1084 | */ 1085 | JSSpec.StringEqualityMatcher = function(expected, actual) { 1086 | this.expected = expected; 1087 | this.actual = actual; 1088 | }; 1089 | 1090 | JSSpec.StringEqualityMatcher.prototype.matches = function() { 1091 | return this.expected == this.actual; 1092 | }; 1093 | 1094 | JSSpec.StringEqualityMatcher.prototype.explain = function() { 1095 | var sb = []; 1096 | 1097 | sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); 1098 | sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected, this.actual)); 1099 | return sb.join(""); 1100 | }; 1101 | 1102 | /** 1103 | * PatternMatcher 1104 | */ 1105 | JSSpec.PatternMatcher = function(actual, pattern, condition) { 1106 | this.actual = actual; 1107 | this.pattern = pattern; 1108 | this.condition = condition; 1109 | this.match = false; 1110 | this.explaination = this.makeExplain(); 1111 | }; 1112 | 1113 | JSSpec.PatternMatcher.createInstance = function(actual, pattern, condition) { 1114 | return new JSSpec.PatternMatcher(actual, pattern, condition); 1115 | }; 1116 | 1117 | JSSpec.PatternMatcher.prototype.makeExplain = function() { 1118 | var sb; 1119 | if(this.actual == null || this.actual._type != 'String') { 1120 | sb = []; 1121 | sb.push('

actual value:

'); 1122 | sb.push('

' + JSSpec.util.inspect(this.actual) + '

'); 1123 | sb.push('

should ' + (this.condition ? '' : 'not') + ' match with pattern:

'); 1124 | sb.push('

' + JSSpec.util.inspect(this.pattern) + '

'); 1125 | sb.push('

but pattern matching cannot be performed.

'); 1126 | return sb.join(""); 1127 | } else { 1128 | this.match = this.condition == !!this.actual.match(this.pattern); 1129 | if(this.match) return ""; 1130 | 1131 | sb = []; 1132 | sb.push('

actual value:

'); 1133 | sb.push('

' + JSSpec.util.inspect(this.actual) + '

'); 1134 | sb.push('

should ' + (this.condition ? '' : 'not') + ' match with pattern:

'); 1135 | sb.push('

' + JSSpec.util.inspect(this.pattern) + '

'); 1136 | return sb.join(""); 1137 | } 1138 | }; 1139 | 1140 | JSSpec.PatternMatcher.prototype.matches = function() { 1141 | return this.match; 1142 | }; 1143 | 1144 | JSSpec.PatternMatcher.prototype.explain = function() { 1145 | return this.explaination; 1146 | }; 1147 | 1148 | /** 1149 | * Domain Specific Languages 1150 | */ 1151 | JSSpec.DSL = {}; 1152 | 1153 | JSSpec.DSL.forString = { 1154 | normalizeHtml: function() { 1155 | var html = this; 1156 | 1157 | // Uniformize quotation, turn tag names and attribute names into lower case 1158 | html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str, closingMark, tagName, attrs) { 1159 | var sortedAttrs = JSSpec.util.sortHtmlAttrs(JSSpec.util.correctHtmlAttrQuotation(attrs).toLowerCase()) 1160 | return "<" + closingMark + tagName.toLowerCase() + sortedAttrs + ">" 1161 | }); 1162 | 1163 | // validation self-closing tags 1164 | html = html.replace(/<(br|hr|img)([^>]*?)>/mg, function(str, tag, attrs) { 1165 | return "<" + tag + attrs + " />"; 1166 | }); 1167 | 1168 | // append semi-colon at the end of style value 1169 | html = html.replace(/style="(.*?)"/mg, function(str, styleStr) { 1170 | styleStr = JSSpec.util.sortStyleEntries(styleStr.strip()); // for Safari 1171 | if(styleStr.charAt(styleStr.length - 1) != ';') styleStr += ";" 1172 | 1173 | return 'style="' + styleStr + '"' 1174 | }); 1175 | 1176 | // sort style entries 1177 | 1178 | // remove empty style attributes 1179 | html = html.replace(/ style=";"/mg, ""); 1180 | 1181 | // remove new-lines 1182 | html = html.replace(/\r/mg, ''); 1183 | html = html.replace(/\n/mg, ''); 1184 | 1185 | return html; 1186 | } 1187 | }; 1188 | 1189 | 1190 | JSSpec.DSL.describe = function(context, entries, base) { 1191 | if(base) { 1192 | for(var i = 0; i < JSSpec.specs.length; i++) { 1193 | if(JSSpec.specs[i].context === base) { 1194 | base = JSSpec.specs[i]; 1195 | break; 1196 | } 1197 | } 1198 | 1199 | for(var i = 0; i < base.examples.length; i++) { 1200 | var example = base.examples[i]; 1201 | 1202 | if(!entries[example.name]) entries[example.name] = example.target; 1203 | } 1204 | } 1205 | 1206 | JSSpec.specs.push(new JSSpec.Spec(context, entries)); 1207 | }; 1208 | 1209 | JSSpec.DSL.value_of = function(target) { 1210 | if(JSSpec._secondPass) return {}; 1211 | 1212 | var subject = new JSSpec.DSL.Subject(target); 1213 | return subject; 1214 | }; 1215 | 1216 | JSSpec.DSL.Subject = function(target) { 1217 | this.target = target; 1218 | }; 1219 | 1220 | JSSpec.DSL.Subject.prototype._type = 'Subject'; 1221 | 1222 | JSSpec.DSL.Subject.prototype.should_fail = function(message) { 1223 | JSSpec._assertionFailure = {message:message}; 1224 | throw JSSpec._assertionFailure; 1225 | }; 1226 | 1227 | JSSpec.DSL.Subject.prototype.should_be = function(expected) { 1228 | var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target); 1229 | if(!matcher.matches()) { 1230 | JSSpec._assertionFailure = {message:matcher.explain()}; 1231 | throw JSSpec._assertionFailure; 1232 | } 1233 | }; 1234 | 1235 | JSSpec.DSL.Subject.prototype.should_not_be = function(expected) { 1236 | // TODO JSSpec.EqualityMatcher should support 'condition' 1237 | var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target); 1238 | if(matcher.matches()) { 1239 | JSSpec._assertionFailure = {message:"'" + this.target + "' should not be '" + expected + "'"}; 1240 | throw JSSpec._assertionFailure; 1241 | } 1242 | }; 1243 | 1244 | JSSpec.DSL.Subject.prototype.should_be_empty = function() { 1245 | this.should_have(0, this.getType() == 'String' ? 'characters' : 'items'); 1246 | }; 1247 | 1248 | JSSpec.DSL.Subject.prototype.should_not_be_empty = function() { 1249 | this.should_have_at_least(1, this.getType() == 'String' ? 'characters' : 'items'); 1250 | }; 1251 | 1252 | JSSpec.DSL.Subject.prototype.should_be_true = function() { 1253 | this.should_be(true); 1254 | }; 1255 | 1256 | JSSpec.DSL.Subject.prototype.should_be_false = function() { 1257 | this.should_be(false); 1258 | }; 1259 | 1260 | JSSpec.DSL.Subject.prototype.should_be_null = function() { 1261 | this.should_be(null); 1262 | }; 1263 | 1264 | JSSpec.DSL.Subject.prototype.should_be_undefined = function() { 1265 | this.should_be(undefined); 1266 | }; 1267 | 1268 | JSSpec.DSL.Subject.prototype.should_not_be_null = function() { 1269 | this.should_not_be(null); 1270 | }; 1271 | 1272 | JSSpec.DSL.Subject.prototype.should_not_be_undefined = function() { 1273 | this.should_not_be(undefined); 1274 | }; 1275 | 1276 | JSSpec.DSL.Subject.prototype._should_have = function(num, property, condition) { 1277 | var matcher = JSSpec.PropertyLengthMatcher.createInstance(num, property, this.target, condition); 1278 | if(!matcher.matches()) { 1279 | JSSpec._assertionFailure = {message:matcher.explain()}; 1280 | throw JSSpec._assertionFailure; 1281 | } 1282 | }; 1283 | 1284 | JSSpec.DSL.Subject.prototype.should_have = function(num, property) { 1285 | this._should_have(num, property, "exactly"); 1286 | }; 1287 | 1288 | JSSpec.DSL.Subject.prototype.should_have_exactly = function(num, property) { 1289 | this._should_have(num, property, "exactly"); 1290 | }; 1291 | 1292 | JSSpec.DSL.Subject.prototype.should_have_at_least = function(num, property) { 1293 | this._should_have(num, property, "at least"); 1294 | }; 1295 | 1296 | JSSpec.DSL.Subject.prototype.should_have_at_most = function(num, property) { 1297 | this._should_have(num, property, "at most"); 1298 | }; 1299 | 1300 | JSSpec.DSL.Subject.prototype.should_include = function(expected) { 1301 | var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, true); 1302 | if(!matcher.matches()) { 1303 | JSSpec._assertionFailure = {message:matcher.explain()}; 1304 | throw JSSpec._assertionFailure; 1305 | } 1306 | }; 1307 | 1308 | JSSpec.DSL.Subject.prototype.should_not_include = function(expected) { 1309 | var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, false); 1310 | if(!matcher.matches()) { 1311 | JSSpec._assertionFailure = {message:matcher.explain()}; 1312 | throw JSSpec._assertionFailure; 1313 | } 1314 | }; 1315 | 1316 | JSSpec.DSL.Subject.prototype.should_match = function(pattern) { 1317 | var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, true); 1318 | if(!matcher.matches()) { 1319 | JSSpec._assertionFailure = {message:matcher.explain()}; 1320 | throw JSSpec._assertionFailure; 1321 | } 1322 | } 1323 | JSSpec.DSL.Subject.prototype.should_not_match = function(pattern) { 1324 | var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, false); 1325 | if(!matcher.matches()) { 1326 | JSSpec._assertionFailure = {message:matcher.explain()}; 1327 | throw JSSpec._assertionFailure; 1328 | } 1329 | }; 1330 | 1331 | JSSpec.DSL.Subject.prototype.getType = function() { 1332 | if(typeof this.target == 'undefined') { 1333 | return 'undefined'; 1334 | } else if(this.target == null) { 1335 | return 'null'; 1336 | } else if(this.target._type) { 1337 | return this.target._type; 1338 | } else if(JSSpec.util.isDomNode(this.target)) { 1339 | return 'DomNode'; 1340 | } else { 1341 | return 'object'; 1342 | } 1343 | }; 1344 | 1345 | /** 1346 | * Utilities 1347 | */ 1348 | JSSpec.util = { 1349 | escapeTags: function(string) { 1350 | return string.replace(//img, '>'); 1351 | }, 1352 | escapeMetastring: function(string) { 1353 | return string.replace(/\r/img, '\\r').replace(/\n/img, '\\n').replace(/\¶\;\/img, '\\n').replace(/\t/img, '\\t'); 1354 | }, 1355 | parseOptions: function(defaults) { 1356 | var options = defaults; 1357 | 1358 | var url = location.href; 1359 | var queryIndex = url.indexOf('?'); 1360 | if(queryIndex == -1) return options; 1361 | 1362 | var query = url.substring(queryIndex + 1).split('#')[0]; 1363 | var pairs = query.split('&'); 1364 | for(var i = 0; i < pairs.length; i++) { 1365 | var tokens = pairs[i].split('='); 1366 | options[tokens[0]] = tokens[1]; 1367 | } 1368 | 1369 | return options; 1370 | }, 1371 | correctHtmlAttrQuotation: function(html) { 1372 | html = html.replace(/(\w+)=['"]([^'"]+)['"]/mg,function (str, name, value) {return name + '=' + '"' + value + '"';}); 1373 | html = html.replace(/(\w+)=([^ '"]+)/mg,function (str, name, value) {return name + '=' + '"' + value + '"';}); 1374 | html = html.replace(/'/mg, '"'); 1375 | 1376 | return html; 1377 | }, 1378 | sortHtmlAttrs: function(html) { 1379 | var attrs = []; 1380 | html.replace(/((\w+)="[^"]+")/mg, function(str, matched) { 1381 | attrs.push(matched); 1382 | }); 1383 | return attrs.length == 0 ? "" : " " + attrs.sort().join(" "); 1384 | }, 1385 | sortStyleEntries: function(styleText) { 1386 | var entries = styleText.split(/; /); 1387 | return entries.sort().join("; "); 1388 | }, 1389 | escapeHtml: function(str) { 1390 | if(!this._div) { 1391 | this._div = document.createElement("DIV"); 1392 | this._text = document.createTextNode(''); 1393 | this._div.appendChild(this._text); 1394 | } 1395 | this._text.data = str; 1396 | return this._div.innerHTML; 1397 | }, 1398 | isDomNode: function(o) { 1399 | // TODO: make it more stricter 1400 | return (typeof o.nodeName == 'string') && (typeof o.nodeType == 'number'); 1401 | }, 1402 | inspectDomPath: function(o) { 1403 | var sb = []; 1404 | while(o && o.nodeName != '#document' && o.parent) { 1405 | var siblings = o.parentNode.childNodes; 1406 | for(var i = 0; i < siblings.length; i++) { 1407 | if(siblings[i] == o) { 1408 | sb.push(o.nodeName + (i == 0 ? '' : '[' + i + ']')); 1409 | break; 1410 | } 1411 | } 1412 | o = o.parentNode; 1413 | } 1414 | return sb.join(" > "); 1415 | }, 1416 | inspectDomNode: function(o) { 1417 | if(o.nodeType == 1) { 1418 | var nodeName = o.nodeName.toLowerCase(); 1419 | var sb = []; 1420 | sb.push(''); 1421 | sb.push("<"); 1422 | sb.push(nodeName); 1423 | 1424 | var attrs = o.attributes; 1425 | for(var i = 0; i < attrs.length; i++) { 1426 | if( 1427 | attrs[i].nodeValue && 1428 | attrs[i].nodeName != 'contentEditable' && 1429 | attrs[i].nodeName != 'style' && 1430 | typeof attrs[i].nodeValue != 'function' 1431 | ) sb.push(' ' + attrs[i].nodeName.toLowerCase() + '="' + attrs[i].nodeValue + '"'); 1432 | } 1433 | if(o.style && o.style.cssText) { 1434 | sb.push(' style="' + o.style.cssText + '"'); 1435 | } 1436 | sb.push('>'); 1437 | sb.push(JSSpec.util.escapeHtml(o.innerHTML)); 1438 | sb.push('</' + nodeName + '>'); 1439 | sb.push(' (' + JSSpec.util.inspectDomPath(o) + ')' ); 1440 | sb.push(''); 1441 | return sb.join(""); 1442 | } else if(o.nodeType == 3) { 1443 | return '#text ' + o.nodeValue + ''; 1444 | } else { 1445 | return 'UnknownDomNode'; 1446 | } 1447 | }, 1448 | inspect: function(o, dontEscape, emphasisKey) { 1449 | var sb, inspected; 1450 | 1451 | if(typeof o == 'undefined') return 'undefined'; 1452 | if(o == null) return 'null'; 1453 | if(o._type == 'String') return '"' + (dontEscape ? JSSpec.util.escapeMetastring(o) : JSSpec.util.escapeHtml(JSSpec.util.escapeMetastring(o))) + '"'; 1454 | 1455 | if(o._type == 'Date') { 1456 | return '"' + o.toString() + '"'; 1457 | } 1458 | 1459 | if(o._type == 'Number') return '' + (dontEscape ? o : JSSpec.util.escapeHtml(o)) + ''; 1460 | 1461 | if(o._type == 'Boolean') return '' + o + ''; 1462 | 1463 | if(o._type == 'RegExp') return '' + JSSpec.util.escapeHtml(o.toString()) + ''; 1464 | 1465 | if(JSSpec.util.isDomNode(o)) return JSSpec.util.inspectDomNode(o); 1466 | 1467 | if(o._type == 'Array' || typeof o.length != 'undefined') { 1468 | sb = []; 1469 | for(var i = 0; i < o.length; i++) { 1470 | inspected = JSSpec.util.inspect(o[i]); 1471 | sb.push(i == emphasisKey ? ('' + inspected + '') : inspected); 1472 | } 1473 | return '[' + sb.join(', ') + ']'; 1474 | } 1475 | 1476 | // object 1477 | sb = []; 1478 | for(var key in o) { 1479 | if(key == 'should') continue; 1480 | 1481 | inspected = JSSpec.util.inspect(key) + ":" + JSSpec.util.inspect(o[key]); 1482 | sb.push(key == emphasisKey ? ('' + inspected + '') : inspected); 1483 | } 1484 | return '{' + sb.join(', ') + '}'; 1485 | } 1486 | }; 1487 | 1488 | describe = JSSpec.DSL.describe; 1489 | behavior_of = JSSpec.DSL.describe; 1490 | value_of = JSSpec.DSL.value_of; 1491 | expect = JSSpec.DSL.value_of; // @deprecated 1492 | 1493 | String.prototype._type = "String"; 1494 | Number.prototype._type = "Number"; 1495 | Date.prototype._type = "Date"; 1496 | Array.prototype._type = "Array"; 1497 | Boolean.prototype._type = "Boolean"; 1498 | RegExp.prototype._type = "RegExp"; 1499 | 1500 | var targets = [Array.prototype, Date.prototype, Number.prototype, String.prototype, Boolean.prototype, RegExp.prototype]; 1501 | 1502 | String.prototype.normalizeHtml = JSSpec.DSL.forString.normalizeHtml; 1503 | String.prototype.asHtml = String.prototype.normalizeHtml; //@deprecated 1504 | String.prototype.strip = function() {return this.replace(/^\s+/, '').replace(/\s+$/, '');} 1505 | 1506 | 1507 | /** 1508 | * Main 1509 | */ 1510 | JSSpec.defaultOptions = { 1511 | autorun: 1, 1512 | specIdBeginsWith: 0, 1513 | exampleIdBeginsWith: 0, 1514 | autocollapse: 1 1515 | }; 1516 | JSSpec.options = JSSpec.util.parseOptions(JSSpec.defaultOptions); 1517 | 1518 | JSSpec.Spec.id = JSSpec.options.specIdBeginsWith; 1519 | JSSpec.Example.id = JSSpec.options.exampleIdBeginsWith; 1520 | 1521 | 1522 | 1523 | window.onload = function() { 1524 | if(JSSpec.specs.length > 0) { 1525 | if(!JSSpec.options.inSuite) { 1526 | JSSpec.runner = new JSSpec.Runner(JSSpec.specs, new JSSpec.Logger()); 1527 | if(JSSpec.options.rerun) { 1528 | JSSpec.runner.rerun(decodeURIComponent(JSSpec.options.rerun)); 1529 | } else { 1530 | JSSpec.runner.run(); 1531 | } 1532 | } else { 1533 | // in suite, send all specs to parent 1534 | var parentWindow = window.frames.parent.window; 1535 | for(var i = 0; i < JSSpec.specs.length; i++) { 1536 | parentWindow.JSSpec.specs.push(JSSpec.specs[i]); 1537 | } 1538 | } 1539 | } else { 1540 | var links = document.getElementById('list'); 1541 | if (links) links = links.getElementsByTagName('A'); 1542 | else return; 1543 | var frameContainer = document.createElement('DIV'); 1544 | frameContainer.style.display = 'none'; 1545 | document.body.appendChild(frameContainer); 1546 | 1547 | for(var i = 0; i < links.length; i++) { 1548 | var frame = document.createElement('IFRAME'); 1549 | frame.src = links[i].href + '?inSuite=0&specIdBeginsWith=' + (i * 10000) + '&exampleIdBeginsWith=' + (i * 10000); 1550 | frameContainer.appendChild(frame); 1551 | } 1552 | } 1553 | } --------------------------------------------------------------------------------