├── .gitignore ├── changelog.txt ├── index.html ├── lib ├── cookie.polypage.jquery.js ├── events.polypage.jquery.js ├── gui.polypage.jquery.js ├── jquery.js ├── keyboard.polypage.jquery.js └── polypage.jquery.js ├── readme.markdown ├── skins ├── default.css └── plain.css └── spec ├── index.html ├── matchers.js ├── polypage-base-spec.js ├── polypage-cookie-spec.js ├── polypage-events-spec.js ├── polypage-gui-spec.js └── screw-unit ├── jquery-1.2.3.js ├── jquery.fn.js ├── jquery.print.js ├── screw.assets.js ├── screw.behaviors.js ├── screw.builder.js ├── screw.css ├── screw.events.js ├── screw.matchers.js └── screw.server.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | PolyPage Change Log 2 | 3 | 4 | 0.9.1 5 | 6 | - Fix default states. 7 | 8 | 9 | 0.9 10 | 11 | - Some performance improvements. 12 | - Provide hooks for custom show/hide animations via base:{ show:func, hide:func } 13 | - options for ppBase now go on the root object... {base:{opts}} becomes {opts} 14 | 15 | 16 | 0.8.3 17 | 18 | - Split extensions out into separate files to make mix'n'match easier. 19 | - Extensions are now auto loaded into the $(el).polypage() function when they are available. 20 | 21 | 22 | 0.8.2 23 | 24 | - Fixed a regression which broke alphabetical sorting. 25 | - Added a new extension to handle keyboard shortcuts. (ALT + first_char_in_state_name toggles the state) 26 | - Added a friendly error message when you have syntax errors in class names. 27 | 28 | 29 | 0.8.1 30 | 31 | - Readme updates. 32 | - extensions now take a 'polypage' option to assist with custom bindings. 33 | - API Change, ppbase options are now passed in via $(el).polypage({ base: {} }); so it is inline with other extensions. 34 | 35 | 36 | 0.8 37 | 38 | - This release is a big refactoring to split the PolyPage logic into a Base library and series of smaller modules. 39 | - A public event API for triggering page state changes and being notified of them. 40 | - The plugin now is a real JS object so theoretically you could have several PolyPage's co-existing on one html page. 41 | - PolyPage now follows the jQuery standard and is fully chainable, and scoped, in most cases $(document).polypage() is fine. 42 | - The GUI, Event bindings and Cookie support is now 100% independent of the polypage Base functionality. You can now run polypage with no GUI or DOM injection at all. 43 | - A decent amount of high level test coverage on the key components. 44 | 45 | 46 | 0.7 47 | 48 | * Officially note the MooTools branch. 49 | * Added a test suite around the public API, this was long overdue, sorry. 50 | * Pulled in some of Yoan's changes to allow #state tiggering on 'a' and 'form' elements. 51 | * Started a skins folder based on Rich's suggestion, if you've got any good ones I would love to have them in the repo. 52 | 53 | 54 | 0.6 55 | 56 | * PolyPage can now be intialized on a jQuery collection using the $(selector).polypage() syntax. the matched elements are used as the container for the navigation controls. This is now the recommended way to initialize jQuery although the $.polypage.init() method still works for backwards compatibility at the moment. (Thanks to Phil Oye) 57 | * Some refactoring of cookie support and setState(); 58 | * Fixes a bug where setting a cookie and a hash in the url to both be on would cause an infinite loop of toggling. 59 | * Added a customizable label to make it clear the toggles are page states, use the 'label' option to change. (Thanks to Phil Oye) 60 | * Increased the specificity of the CSS selectors so they are unlikely to be affected by other page styling. 61 | * If no PolyPage elements exist on a page then we now avoid adding an empty toggle box to the page. (thanks to Phil Oye for the suggestion) 62 | * The 'pp' classname prefix can now be changed via the 'prefix' option. 63 | * The classname separator can now be changed via the 'separator' option. 64 | 65 | 66 | 0.5.1 67 | 68 | * Upgraded to work with jQuery 1.3.x 69 | * Fixed a CSS typo 70 | 71 | 72 | 0.5 73 | 74 | * Regex improvements 75 | * Support for numbers in state names. 76 | * Safari 3 support. 77 | * Alphabetically sort states in the toggle list to avoid confusing users with changing orders across different pages. 78 | * IE6 support. 79 | * Anchor the toggles to the top of the html rather than the viewport. 80 | 81 | 82 | 0.4 83 | 84 | * Fixed a major bug with cookies, all cookies are now scoped to the root of the domain. (thanks Nat) 85 | 86 | 87 | 0.3 88 | 89 | * Added cookie support. 90 | * Wrapped the plugin in a closure. 91 | * Added MIT & GPL Licenses. 92 | 93 | 94 | 0.2 95 | 96 | * Upgraded jQuery. 97 | * Fixed some false negative results. 98 | * Made the syntax slightly easier. 99 | 100 | 101 | 0.1 102 | 103 | * Initial release. 104 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | PolyPage 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | 60 |

Polypage

61 | 62 |

What's this all about?

63 |

Polypage was designed to ease the process of showing multiple page states in html mock-ups. By simply adding class names to a document you can imply state and conditional view logic.

64 | 65 |

Where can I see a demo?

66 |

This page is using PolyPage! You may have noticed the options in the top right corner of this page. Try toggling them to see the text below change to reflect your choices.

67 | 75 | 76 |

How does it work?

77 |

Any class names that you would like Polypage to process should begin with pp_ Polypage will ignore all other classes in the document.

78 |

The pp_ prefix is then followed by a state, this can be any page state that you wish to use, some examples; logged_in, administrator, group_owner, blank_state. A complete class name might look something like pp_logged_in

79 |

When the page loads states are collected automatically from your used class names, because of this it is not necessary to tell Polypage what states you are using in a particular page.

80 |

States can be joined together and negated to add some basic logic to pages. The syntax for this follows natural language. Some examples; pp_logged_in_and_admin, pp_not_logged_in, pp_admin_or_group_owner.

81 |

The state of a page can be easily changed by toggling the state options on and off in the grey box in the top left corner of the window.

82 |

Polypage has support for defaults and persistence through several different means:

83 |
84 |
URL anchors
85 |
These work the same as class names so building an anchor like #logged_in_and_admin would turn these two states on by default on page load.
86 |
Init options
87 |
Passing an array of states to the polypage.init() function will set default on states for the page.
88 |
Cookies
89 |
Cookie support relies on the jQuery cookie plugin, when this is present it is used to make choices persistent. 90 | You can set a cookie from the server by setting 'polypage_option'='yes'
91 |
92 | 93 |

Where can I get hold of it?

94 |

The source code is available on GitHub

95 |

This version is still an early release but is in regular use by several companies. 96 | Check-out the changelog for more details on what's new.

97 | 98 |

What's next?

99 |

Who knows? I'm open to suggestions / patches

100 | 101 |
102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /lib/cookie.polypage.jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cookie plugin 3 | * 4 | * Copyright (c) 2006 Klaus Hartl (stilbuero.de) 5 | * Dual licensed under the MIT and GPL licenses: 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * http://www.gnu.org/licenses/gpl.html 8 | * 9 | */ 10 | 11 | /* 12 | * Altered by Natalie Downe to make $cookie() with no arguments return an array of the set cookies 13 | * eg [["cookiename1", "cookievalue1"], ["cookiename2", "cookievalue2"]] 14 | */ 15 | jQuery.cookie = function(name, value, options) { 16 | if (typeof value != 'undefined') { // name and value given, set cookie 17 | options = options || {}; 18 | if (value === null) { 19 | value = ''; 20 | options.expires = -1; 21 | } 22 | var expires = ''; 23 | if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { 24 | var date; 25 | if (typeof options.expires == 'number') { 26 | date = new Date(); 27 | date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); 28 | } else { 29 | date = options.expires; 30 | } 31 | expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE 32 | } 33 | var path = options.path ? '; path=' + options.path : ''; 34 | var domain = options.domain ? '; domain=' + options.domain : ''; 35 | var secure = options.secure ? '; secure' : ''; 36 | document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); 37 | } else if(typeof name != 'undefined') { // only name given, get cookie 38 | var cookieValue = null; 39 | if (document.cookie && document.cookie != '') { 40 | var cookies = document.cookie.split(';'); 41 | for (var i = 0; i < cookies.length; i++) { 42 | var cookie = jQuery.trim(cookies[i]); 43 | // Does this cookie string begin with the name we want? 44 | if (cookie.substring(0, name.length + 1) == (name + '=')) { 45 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 46 | break; 47 | } 48 | } 49 | } 50 | return cookieValue; 51 | } else { 52 | // not given anything 53 | var cookies = document.cookie.split(';'); 54 | for (var i = 0; i < cookies.length; i++) { 55 | cookies[i] = jQuery.trim(cookies[i]).split('=')[0]; 56 | } 57 | return cookies; 58 | } 59 | }; 60 | 61 | 62 | // ============================= 63 | // = PolyPage Cookie Extension = 64 | // ============================= 65 | 66 | (function($) { 67 | 68 | $.polypage.extension('Cookie', 69 | { 70 | expires: null, // can be a Date object or a integer number of days 71 | namespace:'pp_' 72 | }, 73 | { 74 | init: function(){ 75 | this.checkForCookieSupport(); 76 | this.setInitialStateFromCookie(); 77 | this.bindStateChangeListener(); 78 | }, 79 | 80 | checkForCookieSupport: function(){ 81 | if($.cookie===undefined) throw "The jQuery Cookie plugin is required to use the ppCookie() extension but was not found at $.cookie()"; 82 | }, 83 | 84 | setInitialStateFromCookie: function(){ 85 | var states = this.polypage.statesList(); 86 | for(var i in states) { 87 | var stateName = states[i]; 88 | if(this.getCookie(stateName)) { 89 | var obj = {} 90 | obj[stateName]=true 91 | this.scope.trigger('pp_setState', obj); 92 | 93 | } 94 | } 95 | }, 96 | 97 | setCookie: function(state,val) { 98 | if(!state.length) return false; 99 | $.cookie(this.prefixed(state), val ? 'yes' : null, {"path":"/", "expires":this.options.expires}); 100 | return true; 101 | }, 102 | 103 | getCookie: function(state) { 104 | if(!state.length) return false; 105 | return $.cookie(this.prefixed(state))!=null; 106 | }, 107 | 108 | prefixed: function(state) { 109 | return this.options.namespace+state; 110 | }, 111 | 112 | bindStateChangeListener: function(){ 113 | var c = this; 114 | this.polypageScope.bind('pp_stateChange', function(e, state) { 115 | c.setCookie(state.name, state.value); 116 | }); 117 | } 118 | }); 119 | 120 | })(jQuery); -------------------------------------------------------------------------------- /lib/events.polypage.jquery.js: -------------------------------------------------------------------------------- 1 | // ============================= 2 | // = PolyPage Events Extension = 3 | // ============================= 4 | 5 | (function($) { 6 | 7 | $.polypage.extension('Events', 8 | { 9 | enableClickEvents: true, 10 | enableSubmitEvents: true 11 | }, 12 | { 13 | init: function(){ 14 | this.bindEvents(); 15 | }, 16 | 17 | bindEvents: function() { 18 | var pp = this; 19 | if(this.options.enableClickEvents) 20 | $('body').bind('click.pp', function(e) { pp.clickHandler(e); }); 21 | if(this.options.enableSubmitEvents) 22 | $('form').bind('submit.pp', function(e) { pp.submitHandler(e); }); // FIXME try to bind this to body not just form elements 23 | }, 24 | 25 | clickHandler: function(e) { 26 | var a = $(e.target).closest('a'); 27 | if(a.length==0) return; 28 | var href = a.attr('href'); 29 | if(!href) return; 30 | if(this.triggerEventFromHash(href)) e.preventDefault(); 31 | }, 32 | 33 | submitHandler: function(e){ 34 | var form = $(e.target).closest('form') 35 | if(form.length==0) return; 36 | var action = form.attr('action'); 37 | if(!action) return; 38 | if(this.triggerEventFromHash(action)) e.preventDefault(); 39 | }, 40 | 41 | triggerEventFromHash: function(hash){ 42 | if(hash.search(/^#pp_/)==-1) return false; 43 | var hash = hash.replace(/^#pp_/,''); 44 | if(hash.search(/^toggle_/)>=0) { 45 | this.scope.trigger('pp_toggleState', hash.replace(/^toggle_/, '')); 46 | return true 47 | } 48 | if(hash.search(/^set_/)>=0) { 49 | var val = hash.search(/_false$/)==-1 50 | hash = hash.replace(/^set_/, '') 51 | hash = hash.replace(/_true$|_false$/, '') 52 | var obj = {} 53 | obj[hash]=val 54 | this.scope.trigger('pp_setState', obj); 55 | return true 56 | } 57 | return false 58 | } 59 | }); 60 | 61 | })(jQuery); -------------------------------------------------------------------------------- /lib/gui.polypage.jquery.js: -------------------------------------------------------------------------------- 1 | // ========================== 2 | // = PolyPage GUI Extension = 3 | // ========================== 4 | 5 | (function($) { 6 | 7 | $.polypage.extension('GUI', 8 | { 9 | containerID: 'pp_options', 10 | label: "Page States:" 11 | }, 12 | { 13 | init: function(){ 14 | this.bindEvents(); 15 | this.redraw(); 16 | }, 17 | 18 | bindEvents: function() { 19 | var nb = this; 20 | this.scope.bind('pp_gui_forceRedraw', function() { nb.redraw() }); 21 | this.polypageScope.bind('pp_stateChange', function() { nb.redraw()}); 22 | }, 23 | 24 | redraw: function(){ 25 | this.scope.trigger('pp_gui_redrawBegin', this); 26 | this.undraw(); 27 | if(!this.needsOptionBar()) return; 28 | this.draw(); 29 | this.scope.trigger('pp_gui_redrawComplete', this); 30 | }, 31 | 32 | draw: function(){ 33 | this.scope.append('

'+this.options.label+'

'); 34 | var states = this.states(); 35 | for(var index in states) { 36 | var state = states[index]; 37 | var humanStateName = state.name.replace(this.separator(),' '); 38 | var className = state.value ? 'on' : 'off' 39 | $('#pp_options ul').append('
  • '+humanStateName+'
  • '); 40 | } 41 | }, 42 | 43 | undraw: function(){ 44 | this.scope.find('#'+this.options.containerID).remove(); 45 | }, 46 | 47 | needsOptionBar: function(){ 48 | return this.polypage.hasElements(); 49 | }, 50 | 51 | states: function(){ 52 | return this.polypage.alphabeticalStates(); 53 | }, 54 | 55 | separator: function(){ 56 | return this.polypage.options.separator; 57 | } 58 | }); 59 | 60 | })(jQuery); -------------------------------------------------------------------------------- /lib/jquery.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery JavaScript Library v1.3.2 3 | * http://jquery.com/ 4 | * 5 | * Copyright (c) 2009 John Resig 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://docs.jquery.com/License 8 | * 9 | * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) 10 | * Revision: 6246 11 | */ 12 | (function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); 13 | /* 14 | * Sizzle CSS Selector Engine - v0.9.3 15 | * Copyright 2009, The Dojo Foundation 16 | * Released under the MIT, BSD, and GPL Licenses. 17 | * More information: http://sizzlejs.com/ 18 | */ 19 | (function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

    ";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
    ";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
    ").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
    ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); -------------------------------------------------------------------------------- /lib/keyboard.polypage.jquery.js: -------------------------------------------------------------------------------- 1 | // =============================== 2 | // = PolyPage Keyboard Extension = 3 | // =============================== 4 | 5 | (function($) { 6 | 7 | $.polypage.extension('Keyboard', 8 | { 9 | modifier: 18, 10 | mapping: null 11 | }, 12 | { 13 | init: function(){ 14 | if(!this.options.mapping) this.options.mapping = this.autoMapping(); 15 | this.captureKeyPresses(); 16 | }, 17 | 18 | autoMapping: function(){ 19 | var states = this.polypage.statesList(); 20 | var mapping = {}; 21 | for(var i in states) { 22 | var s = states[i]; 23 | var keycodeFor = function(str,attempt) { 24 | if(str.length==attempt) return null; 25 | var code = str.charCodeAt(attempt); 26 | if(mapping[code]==undefined) return code; 27 | return keycodeFor(str,attempt+1); 28 | }; 29 | mapping[ keycodeFor(s.toUpperCase(), 0) ] = s; 30 | }; 31 | return mapping; 32 | }, 33 | 34 | captureKeyPresses: function(){ 35 | var keyboard = this; 36 | $(document).keydown(function(e) { 37 | var k = e.which || e.keyCode; 38 | if(k==keyboard.options.modifier) keyboard.activated = true; 39 | }); 40 | $(document).keyup(function(e) { 41 | var k = e.which || e.keyCode; 42 | if(k==keyboard.options.modifier) keyboard.activated = false; 43 | if(keyboard.activated && k>=65 && k<=90) { 44 | e.preventDefault(); 45 | keyboard.toggleByKeyCode(k); 46 | } 47 | }); 48 | }, 49 | 50 | toggleByKeyCode: function(keyCode){ 51 | this.scope.trigger('pp_toggleState', this.options.mapping[keyCode]); 52 | } 53 | }); 54 | 55 | })(jQuery); -------------------------------------------------------------------------------- /lib/polypage.jquery.js: -------------------------------------------------------------------------------- 1 | /* 2 | * PolyPage 0.9.1 3 | * 4 | * Copyright (c) 2009 Andy Kent 5 | * Dual licensed under the MIT and GPL licenses: 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * http://www.gnu.org/licenses/gpl.html 8 | * 9 | * For cookie support you will also need the modified version of the jQuery cookie plugin that comes bundled with this plugin. 10 | * 11 | * For help please open the index.html file in your web browser. 12 | * 13 | * Developed by Andy Kent 14 | */ 15 | 16 | (function($) { 17 | 18 | $.polypage = { 19 | extensionList: [] 20 | }; 21 | 22 | 23 | // ============================ 24 | // = PolyPage Agregate helper = 25 | // ============================ 26 | 27 | $.fn.polypage = function(opts) { 28 | var opts = opts || {}; 29 | this.ppBase(opts); 30 | for(var i in $.polypage.extensionList) { 31 | var extension = $.polypage.extensionList[i]; 32 | if(this['pp'+extension]) this['pp'+extension](opts[extension.toLowerCase()] || {}); 33 | } 34 | return this 35 | }; 36 | 37 | 38 | // ================= 39 | // = PolyPage Base = 40 | // ================= 41 | 42 | $.fn.ppBase = function(opts) { 43 | return this.each(function() { 44 | if($(this).data('polypage')===undefined) 45 | $(this).data('polypage', new $.polypage.Base(this, opts)); 46 | }); 47 | }; 48 | 49 | $.polypage.Base = function(scope, options) { 50 | this.scope = $(scope); 51 | this.options = $.extend($.polypage.Base.DEFAULTS, options||{}); 52 | this.init(); 53 | }; 54 | 55 | $.polypage.Base.DEFAULTS = { 56 | prefix: 'pp', 57 | separator: '_', 58 | show: null, 59 | hide: null 60 | }; 61 | 62 | $.polypage.Base.prototype = { 63 | init: function(){ 64 | this.findStates(); 65 | this.bindEvents(); 66 | this.setStartStates(); 67 | this.refresh(); 68 | }, 69 | 70 | 71 | setState: function(stateName,val){ 72 | var newState = !!val; 73 | if(this.getState(stateName)==newState) return newState; 74 | this.states[stateName] = newState; 75 | this.refresh(); 76 | this.scope.trigger('pp_stateChange', { name:stateName, value:newState }); 77 | return newState; 78 | }, 79 | 80 | getState: function(stateName){ 81 | return this.states[stateName]; 82 | }, 83 | 84 | setStates: function(states) { 85 | for(var stateName in states) { this.setState(stateName, states[stateName]) }; 86 | }, 87 | 88 | toggleState: function(stateName) { return this.setState(stateName, !this.getState(stateName)); }, 89 | 90 | toggleStates: function(stateNames) { for(var i in stateNames) this.toggleState(stateNames[i]); }, 91 | 92 | prefixed: function(str){ 93 | return this.options.prefix+this.options.separator+str; 94 | }, 95 | 96 | bindEvents: function(){ 97 | var pp = this; 98 | var doc = $(document.body); 99 | this.scope.bind('pp_setState pp_setStates', function(e, opts) { pp.setStates(opts); }); 100 | this.scope.bind('pp_toggleState', function(e, stateName) { pp.toggleState(stateName); }); 101 | this.scope.bind('pp_toggleStates', function(e, stateNames) { pp.toggleStates($.makeArray(arguments).slice(1)); }); 102 | }, 103 | 104 | separated: function(val) { 105 | return this.options.separator+val+this.options.separator 106 | }, 107 | 108 | setStartStates: function(){ 109 | if(this.options.defaultStates) 110 | for(var i in this.options.defaultStates) { this.setState(this.options.defaultStates[i], true); } 111 | this.setValuesFromHash(window.location.hash); 112 | }, 113 | 114 | setValuesFromHash: function(hash) { 115 | var hashValues = hash.replace(/^#/,'').split(new RegExp(this.separated('and'))); 116 | for(var i in hashValues) { this.setState(hashValues[i], true); } 117 | return this; 118 | }, 119 | 120 | 121 | findStates: function(){ 122 | var el = this.findAll(); 123 | var self = this; 124 | this.states = {}; 125 | el.each(function() { 126 | var s = self.extractDataFromClassName($(this).attr('class')); 127 | var states = s.split(new RegExp(self.separated('?not')+'|'+self.separated('or')+'|'+self.separated('and'))); 128 | for(var state in states) { 129 | if(self.states[states[state]]==undefined && states[state]!='') 130 | self.states[states[state]]=false; 131 | } 132 | }); 133 | return this.states; 134 | }, 135 | 136 | extractDataFromClassName: function(className){ 137 | var classes = className.split(' '); 138 | for(var i in classes) { 139 | var matcher = new RegExp('^'+this.options.prefix+this.options.separator); 140 | if(matcher.test(classes[i])) return classes[i].replace(matcher,''); 141 | } 142 | return ''; 143 | }, 144 | 145 | findAll: function(){ 146 | return $("*[class*='"+this.options.prefix+this.options.separator+"']", this.scope); 147 | }, 148 | 149 | hasElements: function() { 150 | return this.findAll().length>0 151 | }, 152 | 153 | refresh: function(){ 154 | var self = this; 155 | this.findAll().each(function(){ self.evaluateNode(this); }); 156 | return this; 157 | }, 158 | 159 | evaluateNode: function(node){ 160 | var $node = $(node); 161 | var oldVis = $node.is(':visible'); 162 | var newVis = this.evaluate(this.extractDataFromClassName($node.attr('class'))); 163 | if(oldVis == newVis) return newVis; 164 | newVis ? this.show($node) : this.hide($node); 165 | return newVis; 166 | }, 167 | 168 | show: function($node){ 169 | $.isFunction(this.options.show) ? this.options.show($node) : $node.show(); 170 | }, 171 | 172 | hide: function($node){ 173 | $.isFunction(this.options.hide) ? this.options.hide($node) : $node.hide(); 174 | }, 175 | evaluate: function(input){ 176 | var str = input 177 | .replace(new RegExp(this.separated('and'),'gi'),' && ') 178 | .replace(new RegExp(this.separated('or'),'gi'),' || ') 179 | .replace(new RegExp(this.separated('?not'),'gi'),' !') 180 | .replace(/([a-z_0-9\-]+)/gi,"this.states['$1']"); 181 | try { var ret = eval(str) } 182 | catch(e) { throw "Ooops! there seems to have been an error in your use of polypage classname ("+input+")" } 183 | return ret; 184 | }, 185 | 186 | statesList: function(){ 187 | var list = []; 188 | for(var state in this.states) { list.push(state) }; 189 | return list.sort(); 190 | }, 191 | 192 | alphabeticalStates: function(){ 193 | var alphaStates = [], ret = []; 194 | for(var state in this.states) { alphaStates.push(state) } 195 | alphaStates = this.statesList(); 196 | for(var state in alphaStates) { ret.push({name:alphaStates[state], value:this.states[alphaStates[state]]}); }; 197 | return ret 198 | } 199 | }; 200 | 201 | 202 | // ==================== 203 | // = Extension Helper = 204 | // ==================== 205 | 206 | $.polypage.extension = function(__extensionName__, __defaults__, __prototype__) { 207 | $.polypage.extensionList.push(__extensionName__); 208 | // create a jQuery chainable helper 209 | $.fn['pp'+__extensionName__] = function(opts) { 210 | return this.each(function() { 211 | (opts = opts || {}).bindTo = opts.bindTo || this; 212 | $(this).data('pp'+__extensionName__, new $.polypage[__extensionName__](this, opts)); 213 | }); 214 | }; 215 | 216 | // object initializer 217 | $.polypage[__extensionName__] = function(scope, options) { 218 | this.scope = $(scope); 219 | this.options = $.extend($.polypage[__extensionName__].DEFAULTS, options||{}); 220 | // polypage binding 221 | this.polypageScope = $(this.options.bindTo); 222 | this.polypage = this.options.polypage || this.polypageScope.data('polypage'); 223 | if(this.polypage===undefined) 224 | throw "A polypage "+__extensionName__+" object must be bound to a polypage enabled element, try setting the 'bindTo' option."; 225 | // initialization 226 | this.init(); 227 | }; 228 | 229 | // empty defaults 230 | $.polypage[__extensionName__].DEFAULTS = __defaults__ || {}; 231 | 232 | // blank init to stop errors 233 | $.polypage[__extensionName__].prototype = $.extend({ init: function(){} }, __prototype__); 234 | }; 235 | 236 | })(jQuery); -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | PolyPage 2 | ======== 3 | Polypage was designed to ease the process of showing 4 | multiple page states in html mock-ups. 5 | By simply adding class names to a document you can 6 | imply state and conditional view logic. 7 | 8 | 9 | BASIC USAGE 10 | ----------- 11 | 12 | Initialise PolyPage... 13 | 14 | $(document).ready(function() { 15 | $('body').polypage(); 16 | }); 17 | 18 | Use stateful class names in your html... 19 | 20 |
  • pp_admin
  • 21 |
  • pp_not_logged_in
  • 22 |
  • pp_logged_in_or_admin
  • 23 |
  • pp_logged_in_and_admin
  • 24 |
  • pp_not_logged_in_and_not_admin
  • 25 | 26 | Use special href attributes to trigger state changes if required... 27 | 28 | Log In Toggle 29 | Log In 30 | Log Out 31 | 32 | 33 | Use special form actions to trigger state changes if required... 34 | 35 |
    36 | 37 |
    38 | 39 | 40 | API 41 | --- 42 | 43 | PolyPage makes extensive use of custom events and triggering/binding to these is the recommended way of interacting with page states programatically. Here are some examples (which assume you have bound polypage to the 'body')... 44 | 45 | Toggle the logged in state... 46 | 47 | $('body').trigger('pp_toggleState', 'logged_in'); 48 | 49 | Set the logged in state... 50 | 51 | $('body').trigger('pp_setState', { logged_in:true }); 52 | 53 | Set the logged in state... 54 | 55 | $('body').trigger('pp_setState', { logged_in:true }); 56 | 57 | Listen for state changes... 58 | 59 | $('body').bind('pp_stateChange', function(e, state) { 60 | alert("state "+ state.name + " changed to " + state.value); 61 | }); 62 | 63 | See the specs for some more examples. 64 | 65 | ADVANCED USAGE 66 | -------------- 67 | 68 | Under the hood PolyPage is logically separated into a core library and several extensions. The currently implemented extensions are... 69 | 70 | - Base: the core event system and bare essentials to get polypage working 71 | - GUI: the nav bar for pre-built GUI interactions 72 | - Events: some default event handlers for taking care of polypage formatted click and submit events automagically 73 | - Cookies: cookie support for maintaining state across pages 74 | - Keyboard: Keyboard shortcuts for toggling states via "ALT + first_letter_of_state_name" 75 | 76 | Full documentation for all of the extensions will be coming soon but for now you can see the source code for available options. 77 | 78 | Some usage examples 79 | ---------------------- 80 | 81 | Starting PolyPage without cookie support or Event helpers... 82 | 83 | $('body').polypage({ cookie:false, events:false }); 84 | 85 | 86 | Starting PolyPage with a 2 day cookie expiry a 'ux' prefix and no Event helpers... 87 | 88 | $('body').polypage({ prefix:'ux', cookie:{ expires:2 }, events:false }); 89 | 90 | Roll your own PolyPage! if you want to completely customise PolyPage then you can break away from using the polypage() function and initialize the individual components yourself... 91 | 92 | $(el).ppBase(opts); 93 | $(el).ppEvents(opts); 94 | $(el).ppGUI(opts); 95 | $(el).ppCookie(opts); 96 | $(el).ppKeyboard(opts); 97 | 98 | 99 | MORE INFO 100 | --------- 101 | For an example open the index.html file in a web browser. 102 | 103 | If you would prefer be using MooTools instead of jQuery then for the moment you really should checkout cheeaun's fork http://github.com/cheeaun/polypage which contains lots of MooTools goodness. 104 | 105 | CONTRIBUTERS 106 | ------------ 107 | - Andy Kent (http://adkent.com) 108 | - Natalie Downe (http://notes.natbat.net) 109 | - Phil Oye (http://philoye.com) 110 | - Lim Chee Aun (http://cheeaun.com) 111 | - Yoan Blanc (http://yoan.dosimple.ch/) 112 | -------------------------------------------------------------------------------- /skins/default.css: -------------------------------------------------------------------------------- 1 | body div#pp_options { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | padding: 4px; 6 | background: #f0f0f0; 7 | border-left: 1px solid #ddd; 8 | border-bottom: 1px solid #ddd; 9 | opacity: 0.85; 10 | font-size: 11px; 11 | line-height: 1em; 12 | } 13 | body div#pp_options p { 14 | display: inline; 15 | color: #aaa; 16 | } 17 | body div#pp_options ul { 18 | display: inline; 19 | margin: 0; 20 | padding: 0; 21 | } 22 | body div#pp_options li { 23 | display: inline; 24 | padding: 0 9px; 25 | } 26 | body div#pp_options li a { 27 | color: #999; 28 | } 29 | body div#pp_options li a.on { 30 | color: #000; 31 | background: #d4fb78; 32 | } -------------------------------------------------------------------------------- /skins/plain.css: -------------------------------------------------------------------------------- 1 | body div#pp_options { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | padding: 5px; 6 | font-size: 12px; 7 | line-height: 1; 8 | background: #f0f0f0; 9 | border-left: 1px solid #ddd; 10 | border-bottom: 1px solid #ddd; 11 | overflow:hidden; 12 | } 13 | body div#pp_options .closer { 14 | color: #999; 15 | padding:5px; 16 | margin-left:-4px; 17 | text-decoration:none; 18 | font-weight:bold; 19 | } 20 | body div#pp_options p { 21 | display: inline; 22 | color: #bbb; 23 | } 24 | body div#pp_options ul { 25 | display: inline; 26 | margin: 0; 27 | padding: 0; 28 | } 29 | body div#pp_options li { 30 | display: inline; 31 | padding: 0 .3em; 32 | } 33 | body div#pp_options li a { 34 | color: #999; 35 | } 36 | body div#pp_options li a.on { 37 | color: #000; 38 | } 39 | -------------------------------------------------------------------------------- /spec/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | Polypage Test Suite 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 37 | 38 | 39 | 40 |
    41 | 42 | Log In Toggle 43 | Log In 44 | Log Out 45 | 46 |
    47 |

    48 |
    49 | 50 |
      51 |
    • No Class
    • 52 |
    • pp_logged_in
    • 53 |
    • pp_admin
    • 54 |
    • pp_not_logged_in
    • 55 |
    • pp_logged_in_or_admin
    • 56 |
    • pp_logged_in_and_admin
    • 57 |
    • pp_not_logged_in_and_not_admin
    • 58 |
    59 | 60 |
      61 |
    • No Class
    • 62 |
    • pp-logged-in
    • 63 |
    • pp-not-logged-in
    • 64 |
    • pp-logged-in-or-admin
    • 65 |
    • pp-logged-in-and-admin
    • 66 |
    • pp-not-logged-in-and-not-admin
    • 67 |
    68 |
    69 | 70 | 71 | 72 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /spec/matchers.js: -------------------------------------------------------------------------------- 1 | Screw.Matchers["be_visible"] = { 2 | match: function(expected, actual) { 3 | if (!(actual instanceof jQuery)) actual = jQuery(actual) 4 | if(actual.length==0) throw "Unable to determine visibility as no elements matched "+actual.toString(); 5 | return actual.is(':visible'); 6 | }, 7 | failure_message: function(expected, actual, not) { 8 | return 'expected ' + $.print(actual) + (not ? ' not' : '') + ' to be visible'; 9 | } 10 | }; 11 | 12 | Screw.Matchers["be_hidden"] = { 13 | match: function(expected, actual) { 14 | if (!(actual instanceof jQuery)) actual = jQuery(actual) 15 | if(actual.length==0) throw "Unable to determine visibility as no elements matched "+actual.toString(); 16 | return actual.is(':hidden'); 17 | }, 18 | failure_message: function(expected, actual, not) { 19 | return 'expected ' + $.print(actual) + (not ? ' not' : '') + ' to be hidden'; 20 | } 21 | } -------------------------------------------------------------------------------- /spec/polypage-base-spec.js: -------------------------------------------------------------------------------- 1 | Screw.Unit(function() { 2 | describe("PolyPage Base", function() { 3 | describe("triggering pp_setState events", function() { 4 | it("shows and hides matching elements", function() { 5 | $('#dom').trigger('pp_setState', { logged_in:false }); 6 | expect('.pp_logged_in').to(be_hidden); 7 | $('#dom').trigger('pp_setState', { logged_in:true }); 8 | expect('.pp_logged_in').to(be_visible); 9 | }); 10 | 11 | it("shows and hides inverted matching elements", function() { 12 | $('#dom').trigger('pp_setState', { logged_in:false }); 13 | expect('.pp_not_logged_in').to(be_visible); 14 | $('#dom').trigger('pp_setState', { logged_in:true }); 15 | expect('.pp_not_logged_in').to(be_hidden); 16 | }); 17 | 18 | it("shows and hides AND'ed matching elements", function() { 19 | $('#dom').trigger('pp_setStates', { logged_in:false, admin:false }); 20 | expect('.pp_logged_in_and_admin').to(be_hidden); 21 | $('#dom').trigger('pp_setState', { logged_in:true }); 22 | expect('.pp_logged_in_and_admin').to(be_hidden); 23 | $('#dom').trigger('pp_setState', { admin:true }); 24 | expect('.pp_logged_in_and_admin').to(be_visible); 25 | }); 26 | 27 | it("shows and hides OR'ed matching elements", function() { 28 | $('#dom').trigger('pp_setStates', { logged_in:false, admin:false }); 29 | expect('.pp_logged_in_or_admin').to(be_hidden); 30 | $('#dom').trigger('pp_setState', { logged_in:true }); 31 | expect('.pp_logged_in_or_admin').to(be_visible); 32 | $('#dom').trigger('pp_setState', { admin:true }); 33 | expect('.pp_logged_in_or_admin').to(be_visible); 34 | }); 35 | }); 36 | 37 | describe("triggering pp_toggleState events", function() { 38 | it("toggles a single state", function() { 39 | $('#dom').trigger('pp_setState', { logged_in:false }); 40 | $('#dom').trigger('pp_toggleState', 'logged_in'); 41 | expect('.pp_logged_in').to(be_visible); 42 | $('#dom').trigger('pp_toggleState', 'logged_in'); 43 | expect('.pp_logged_in').to(be_hidden); 44 | }); 45 | }); 46 | 47 | describe("triggering pp_toggleStates events", function() { 48 | it("toggles multiple states", function() { 49 | $('#dom').trigger('pp_setState', { logged_in:false, admin:true }); 50 | $('#dom').trigger('pp_toggleStates', ['logged_in', 'admin']); 51 | expect('.pp_logged_in').to(be_visible); 52 | expect('.pp_admin').to(be_hidden); 53 | $('#dom').trigger('pp_toggleStates', ['logged_in', 'admin']); 54 | expect('.pp_logged_in').to(be_hidden); 55 | expect('.pp_admin').to(be_visible); 56 | }); 57 | }); 58 | 59 | describe("fired events", function() { 60 | it("fires 'pp_stateChanged' everytime a page state changes (via toggling)", function() { 61 | $('#dom').trigger('pp_setState', {logged_in:false}); 62 | var data = false; 63 | $('#dom').bind('pp_stateChange', function(e, obj) { data = obj }); 64 | $('#dom').trigger('pp_toggleStates', 'logged_in'); 65 | expect(data).to(equal, {name:'logged_in', value:true}); 66 | }); 67 | 68 | it("fires 'pp_stateChanged' everytime a page state changes (via switching)", function() { 69 | $('#dom').trigger('pp_setState', {logged_in:true}); 70 | var data = false; 71 | $('#dom').bind('pp_stateChange', function(e, obj) { data = obj }); 72 | $('#dom').trigger('pp_setState', {logged_in:false}); 73 | expect(data).to(equal, {name:'logged_in', value:false}); 74 | }); 75 | 76 | it("doesn't fire 'pp_stateChanged' if the state hasn't changed", function() { 77 | $('#dom').trigger('pp_setState', {logged_in:true}); 78 | var data = false; 79 | $('#dom').bind('pp_stateChange', function(e, obj) { data = true }); 80 | $('#dom').trigger('pp_setState', {logged_in:true}); 81 | expect(data).to(be_false); 82 | }); 83 | }); 84 | }); 85 | }); -------------------------------------------------------------------------------- /spec/polypage-cookie-spec.js: -------------------------------------------------------------------------------- 1 | Screw.Unit(function() { 2 | describe("PolyPage Cookie", function() { 3 | it("stores state in a cookie when switched on", function() { 4 | $('#dom').trigger('pp_setState', { logged_in:false }); 5 | $('#dom').trigger('pp_setState', { logged_in:true }); 6 | expect($.cookie('pp_logged_in')).to(equal, 'yes'); 7 | }); 8 | 9 | it("stores forgets state in a cookie when switched off", function() { 10 | $('#dom').trigger('pp_setState', { logged_in:true }); 11 | $('#dom').trigger('pp_setState', { logged_in:false }); 12 | expect($.cookie('pp_logged_in')).to(equal, null); 13 | }); 14 | 15 | it("stores state in a cookie when toggled", function() { 16 | $('#dom').trigger('pp_toggleState', 'logged_in'); 17 | expect($.cookie('pp_logged_in')).to(equal, 'yes'); 18 | $('#dom').trigger('pp_toggleState', 'logged_in'); 19 | expect($.cookie('pp_logged_in')).to(equal, null); 20 | }); 21 | }); 22 | }); -------------------------------------------------------------------------------- /spec/polypage-events-spec.js: -------------------------------------------------------------------------------- 1 | Screw.Unit(function() { 2 | describe("PolyPage Events", function() { 3 | describe("triggering pp_toggleState events via link hash attributes", function() { 4 | it("binds to clicks on links and toggles with pp_toggle_logged_in", function() { 5 | $('#dom').trigger('pp_setState', { logged_in:false }); 6 | $('a#toggle_logged_in_link').click(); 7 | expect('.pp_logged_in').to(be_visible); 8 | $('a#toggle_logged_in_link').click(); 9 | expect('.pp_logged_in').to(be_hidden); 10 | }); 11 | 12 | it("works when the click event occurs on a nested attribute", function() { 13 | $('#dom').trigger('pp_setState', { logged_in:false }); 14 | $('a#toggle_logged_in_link strong').click(); 15 | expect('.pp_logged_in').to(be_visible); 16 | $('a#toggle_logged_in_link strong').click(); 17 | expect('.pp_logged_in').to(be_hidden); 18 | }); 19 | }); 20 | 21 | describe("triggering pp_setState events via link hash attributes", function() { 22 | it("binds to clicks on links and switches on with pp_set_logged_in_true", function() { 23 | $('#dom').trigger('pp_setState', { logged_in:false }); 24 | $('a#set_logged_in_true_link').click(); 25 | expect('.pp_logged_in').to(be_visible); 26 | $('a#set_logged_in_true_link').click(); 27 | expect('.pp_logged_in').to(be_visible); 28 | }); 29 | 30 | it("binds to clicks on links and switches off with pp_set_logged_in_false", function() { 31 | $('#dom').trigger('pp_setState', { logged_in:true }); 32 | $('a#set_logged_in_false_link').click(); 33 | expect('.pp_logged_in').to(be_hidden); 34 | $('a#set_logged_in_false_link').click(); 35 | expect('.pp_logged_in').to(be_hidden); 36 | }); 37 | }); 38 | 39 | describe("triggering pp_toggleState events via form action attributes", function() { 40 | it("binds to submit events on forms and toggles with pp_toggle_logged_in", function() { 41 | $('#dom').trigger('pp_setState', { logged_in:false }); 42 | $('form#toggle_logged_in_form').submit(); 43 | expect('.pp_logged_in').to(be_visible); 44 | $('form#toggle_logged_in_form').submit(); 45 | expect('.pp_logged_in').to(be_hidden); 46 | }); 47 | }); 48 | }); 49 | }); -------------------------------------------------------------------------------- /spec/polypage-gui-spec.js: -------------------------------------------------------------------------------- 1 | Screw.Unit(function() { 2 | describe("PolyPage GUI", function() { 3 | describe("redrawing", function() { 4 | it("fires a pp_gui_redrawBegin event when redrawing begins", function() { 5 | var redraw = false; 6 | $('#dom').bind('pp_gui_redrawBegin', function(e) { redraw = true }); 7 | $('#dom').data('ppGUI').redraw(); 8 | expect(redraw).to(be_true); 9 | }); 10 | 11 | it("fires a pp_gui_redrawComplete event when redrawing finishes", function() { 12 | var redraw = false; 13 | $('#dom').bind('pp_gui_redrawComplete', function(e) { redraw = true }); 14 | $('#dom').data('ppGUI').redraw(); 15 | expect(redraw).to(be_true); 16 | }); 17 | 18 | it("redraws when a page state changes", function() { 19 | var redraw = false; 20 | $('#dom').bind('pp_gui_redrawBegin', function(e) { redraw = true }); 21 | $('#dom').trigger('pp_toggleState', 'logged_in'); 22 | expect(redraw).to(be_true); 23 | }); 24 | }); 25 | 26 | describe("toggling", function() { 27 | it("allows state toggling by clicking on the links", function() { 28 | $('#dom').trigger('pp_setState', { logged_in:false }); 29 | $('#pp_state_toggle_logged_in').click(); 30 | expect('.pp_logged_in').to(be_visible); 31 | $('#pp_state_toggle_logged_in').click(); 32 | expect('.pp_logged_in').to(be_hidden); 33 | }); 34 | }); 35 | }); 36 | }); -------------------------------------------------------------------------------- /spec/screw-unit/jquery.fn.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.fn.fn = function() { 3 | var self = this; 4 | var extension = arguments[0], name = arguments[0]; 5 | if (typeof name == "string") { 6 | return apply(self, name, $.makeArray(arguments).slice(1, arguments.length)); 7 | } else { 8 | $.each(extension, function(key, value) { 9 | define(self, key, value); 10 | }); 11 | return self; 12 | } 13 | } 14 | function define(self, name, fn) { 15 | self.data(namespacedName(name), fn); 16 | }; 17 | function apply(self, name, args) { 18 | var result; 19 | self.each(function(i, item) { 20 | var fn = $(item).data(namespacedName(name)); 21 | if (fn) result = fn.apply(item, args) 22 | else throw(name + " is not defined"); 23 | }); 24 | return result; 25 | }; 26 | function namespacedName(name) { 27 | return 'fn.' + name; 28 | } 29 | })(jQuery); -------------------------------------------------------------------------------- /spec/screw-unit/jquery.print.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | function print_array(obj, opts) { 4 | var result = []; 5 | for (var i = 0; i < Math.min(opts.max_array, obj.length); i++) 6 | result.push($.print(obj[i], $.extend({}, opts, { max_array: 3, max_string: 40 }))); 7 | 8 | if (obj.length > opts.max_array) 9 | result.push((obj.length - opts.max_array) + ' more...'); 10 | if (result.length == 0) return "[]" 11 | return "[ " + result.join(", ") + " ]"; 12 | } 13 | 14 | function print_element(obj) { 15 | if (obj.nodeType == 1) { 16 | var result = []; 17 | var properties = [ 'className', 'id' ]; 18 | var extra = { 19 | 'input': ['type', 'name', 'value'], 20 | 'a': ['href', 'target'], 21 | 'form': ['method', 'action'] 22 | }; 23 | $.each(properties.concat(extra[obj.tagName.toLowerCase()] || []), function() { 24 | if (obj[this]) 25 | result.push(' ' + this.replace('className', 'class') + "=" + $.print(obj[this])) 26 | }); 27 | return "<" + obj.tagName.toLowerCase() 28 | + result.join('') + ">"; 29 | } 30 | } 31 | 32 | function print_object(obj, opts) { 33 | var seen = opts.seen || []; 34 | 35 | var result = [], key, value; 36 | for (var k in obj) { 37 | if (obj.hasOwnProperty(k) && $.inArray(obj[k], seen) < 0) { 38 | seen.push(obj[k]); 39 | value = $.print(obj[k], $.extend({}, opts, { max_array: 6, max_string: 40, seen: seen })); 40 | } else 41 | value = "..."; 42 | result.push(k + ": " + value); 43 | } 44 | if (result.length == 0) return "{}"; 45 | return "{ " + result.join(", ") + " }"; 46 | } 47 | 48 | function print_jquery(obj) { 49 | } 50 | 51 | function print_string(value, opts) { 52 | var character_substitutions = { 53 | '\b': '\\b', 54 | '\t': '\\t', 55 | '\n': '\\n', 56 | '\f': '\\f', 57 | '\r': '\\r', 58 | '"' : '\\"', 59 | '\\': '\\\\' 60 | }; 61 | var r = /["\\\x00-\x1f\x7f-\x9f]/g; 62 | 63 | var str = r.test(value) 64 | ? '"' + value.replace(r, function (a) { 65 | var c = character_substitutions[a]; 66 | if (c) return c; 67 | c = a.charCodeAt(); 68 | return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); 69 | }) + '"' 70 | : '"' + value + '"'; 71 | if (str.length > opts.max_string) 72 | return str.slice(0, opts.max_string + 1) + '..."'; 73 | else 74 | return str; 75 | } 76 | 77 | $.print = function(obj, options) { 78 | var opts = $.extend({}, { max_array: 10, max_string: 100 }, options); 79 | 80 | if (typeof obj == 'undefined') 81 | return "undefined"; 82 | else if (typeof obj == 'boolean') 83 | return obj.toString(); 84 | else if (!obj && typeof obj == 'number') 85 | return 'NaN'; 86 | else if (!obj) 87 | return "null"; 88 | else if (typeof obj == 'string') 89 | return print_string(obj, opts); 90 | else if (obj instanceof RegExp) 91 | return obj.toString(); 92 | else if (typeof obj == 'function' || obj instanceof Function) 93 | return obj.toString().match(/^([^)]*\))/)[1]; 94 | else if (obj instanceof Array) 95 | return print_array(obj, opts); 96 | else if (obj.nodeType) 97 | return print_element(obj); 98 | else if (obj instanceof jQuery) 99 | return "$(" + $.print(obj.get()) + ")"; 100 | else if (obj instanceof Error) 101 | return print_object(obj, $.extend({}, options, { max_string: 200 })); 102 | else if (obj instanceof Object) 103 | return print_object(obj, opts); 104 | else 105 | return obj.toString().replace(/\n\s*/g, ''); 106 | } 107 | 108 | })(jQuery); -------------------------------------------------------------------------------- /spec/screw-unit/screw.assets.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | Screw.Assets = {}; 3 | Screw.Assets.use_cache_buster = false; // TODO: NS/CTI - make this configurable from the UI. 4 | var required_paths = []; 5 | var included_stylesheets = {}; 6 | var cache_buster = parseInt(new Date().getTime()/(1*1000)); 7 | 8 | Screw.Assets.require = function(javascript_path, onload) { 9 | if(!required_paths[javascript_path]) { 10 | var full_path = javascript_path + ".js"; 11 | if (Screw.Assets.use_cache_buster) { 12 | full_path += '?' + cache_buster; 13 | } 14 | document.write(""); 15 | if(onload) { 16 | var scripts = document.getElementsByTagName('script'); 17 | scripts[scripts.length-1].onload = onload; 18 | } 19 | required_paths[javascript_path] = true; 20 | } 21 | }; 22 | 23 | Screw.Assets.stylesheet = function(stylesheet_path) { 24 | if(!included_stylesheets[stylesheet_path]) { 25 | var full_path = stylesheet_path + ".css"; 26 | if(Screw.Assets.use_cache_buster) { 27 | full_path += '?' + cache_buster; 28 | } 29 | document.write(""); 30 | included_stylesheets[stylesheet_path] = true; 31 | } 32 | }; 33 | 34 | window.require = Screw.Assets.require; 35 | window.stylesheet = Screw.Assets.stylesheet; 36 | })(); 37 | -------------------------------------------------------------------------------- /spec/screw-unit/screw.behaviors.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(Screw).bind('loaded', function() { 3 | $('.status').fn({ 4 | display: function() { 5 | $(this).text( 6 | $('.passed').length + $('.failed').length + ' test(s), ' + $('.failed').length + ' failure(s)' 7 | ); 8 | } 9 | }); 10 | 11 | $('.describe').fn({ 12 | parent: function() { 13 | return $(this).parent('.describes').parent('.describe'); 14 | }, 15 | 16 | run_befores: function() { 17 | $(this).fn('parent').fn('run_befores'); 18 | $(this).children('.befores').children('.before').fn('run'); 19 | }, 20 | 21 | run_afters: function() { 22 | $(this).fn('parent').fn('run_afters'); 23 | $(this).children('.afters').children('.after').fn('run'); 24 | }, 25 | 26 | enqueue: function() { 27 | $(this).children('.its').children('.it').fn('enqueue'); 28 | $(this).children('.describes').children('.describe').fn('enqueue'); 29 | }, 30 | 31 | selector: function() { 32 | return $(this).fn('parent').fn('selector') 33 | + ' > .describes > .describe:eq(' + $(this).parent('.describes').children('.describe').index(this) + ')'; 34 | } 35 | }); 36 | 37 | $('body > .describe').fn({ 38 | selector: function() { return 'body > .describe' } 39 | }); 40 | 41 | $('.it').fn({ 42 | parent: function() { 43 | return $(this).parent('.its').parent('.describe'); 44 | }, 45 | 46 | run: function() { 47 | try { 48 | try { 49 | $(this).fn('parent').fn('run_befores'); 50 | $(this).data('screwunit.run')(); 51 | } finally { 52 | $(this).fn('parent').fn('run_afters'); 53 | } 54 | $(this).trigger('passed'); 55 | } catch(e) { 56 | $(this).trigger('failed', [e]); 57 | } 58 | }, 59 | 60 | enqueue: function() { 61 | var self = $(this).trigger('enqueued'); 62 | $(Screw) 63 | .queue(function() { 64 | self.fn('run'); 65 | setTimeout(function() { $(Screw).dequeue() }, 0); 66 | }); 67 | }, 68 | 69 | selector: function() { 70 | return $(this).fn('parent').fn('selector') 71 | + ' > .its > .it:eq(' + $(this).parent('.its').children('.it').index(this) + ')'; 72 | } 73 | }); 74 | 75 | $('.before').fn({ 76 | run: function() { $(this).data('screwunit.run')() } 77 | }); 78 | 79 | $('.after').fn({ 80 | run: function() { $(this).data('screwunit.run')() } 81 | }); 82 | 83 | $(Screw).trigger('before'); 84 | var to_run = unescape(location.search.slice(1)) || 'body > .describe > .describes > .describe'; 85 | $(to_run) 86 | .focus() 87 | .eq(0).trigger('scroll').end() 88 | .fn('enqueue'); 89 | $(Screw).queue(function() { $(Screw).trigger('after') }); 90 | }) 91 | })(jQuery); 92 | -------------------------------------------------------------------------------- /spec/screw-unit/screw.builder.js: -------------------------------------------------------------------------------- 1 | var Screw = (function($) { 2 | var screw = { 3 | Unit: function(fn) { 4 | var contents = fn.toString().match(/^[^\{]*{((.*\n*)*)}/m)[1]; 5 | var fn = new Function("matchers", "specifications", 6 | "with (specifications) { with (matchers) { " + contents + " } }" 7 | ); 8 | 9 | $(Screw).queue(function() { 10 | Screw.Specifications.context.push($('body > .describe')); 11 | fn.call(this, Screw.Matchers, Screw.Specifications); 12 | Screw.Specifications.context.pop(); 13 | $(this).dequeue(); 14 | }); 15 | }, 16 | 17 | Specifications: { 18 | context: [], 19 | 20 | describe: function(name, fn) { 21 | var describe = $('
  • ') 22 | .append($('

    ').text(name)) 23 | .append('
      ') 24 | .append('
        ') 25 | .append('
          ') 26 | .append('
            '); 27 | 28 | this.context.push(describe); 29 | fn.call(); 30 | this.context.pop(); 31 | 32 | this.context[this.context.length-1] 33 | .children('.describes') 34 | .append(describe); 35 | }, 36 | 37 | it: function(name, fn) { 38 | var it = $('
          1. ') 39 | .append($('

            ').text(name)) 40 | .data('screwunit.run', fn); 41 | 42 | this.context[this.context.length-1] 43 | .children('.its') 44 | .append(it); 45 | }, 46 | 47 | before: function(fn) { 48 | var before = $('
          2. ') 49 | .data('screwunit.run', fn); 50 | 51 | this.context[this.context.length-1] 52 | .children('.befores') 53 | .append(before); 54 | }, 55 | 56 | after: function(fn) { 57 | var after = $('
          3. ') 58 | .data('screwunit.run', fn); 59 | 60 | this.context[this.context.length-1] 61 | .children('.afters') 62 | .append(after); 63 | } 64 | } 65 | }; 66 | 67 | $(screw).queue(function() { $(screw).trigger('loading') }); 68 | $(function() { 69 | $('
            ') 70 | .append('

            ') 71 | .append('
              ') 72 | .append('
                ') 73 | .append('
                  ') 74 | .appendTo('body'); 75 | 76 | $(screw).dequeue(); 77 | $(screw).trigger('loaded'); 78 | }); 79 | return screw; 80 | })(jQuery); -------------------------------------------------------------------------------- /spec/screw-unit/screw.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | font-weight: normal; 5 | list-style: none; 6 | font-size: 1em; 7 | } 8 | 9 | body { 10 | background: #fff; 11 | color: #333; 12 | font-family: "Helvetica Neue", Helvetica, Arial, "Sans-Serif"; 13 | font-size: 78.5%; 14 | padding-top: 3em; 15 | } 16 | 17 | h3.status { 18 | position: fixed; 19 | width: 100%; 20 | top: 0; 21 | left: 0; 22 | margin: 0 0 1em 0; 23 | padding: .75em; 24 | font-size: 1.1em; 25 | color: #fff; 26 | background-color: #3875d7; 27 | font-weight: normal; 28 | -webkit-box-shadow: 0px 1px 5px #666; 29 | -moz-box-shadow: 0px 2px 3px #999; 30 | } 31 | 32 | ul { 33 | margin: .5em 0; 34 | padding: 0 1em; 35 | } 36 | 37 | h1 { 38 | margin: 0 1em; 39 | padding: .25em 0; 40 | border-bottom: 1px dotted #ccc; 41 | font-weight: bold; 42 | } 43 | 44 | h2 { 45 | padding: .2em .5em; 46 | color: #ccc; 47 | } 48 | 49 | .enqueued h2 { 50 | color: #999; 51 | } 52 | 53 | .passed h2 { 54 | color: #333; 55 | background: #abffa5; 56 | border-top: 2px solid #ccffcc; 57 | border-bottom: 2px solid #66ff66; 58 | } 59 | 60 | .failed h2 { 61 | color: #333; 62 | background: #ffaaaa; 63 | border-top: 2px solid #ffcccc; 64 | border-bottom: 2px solid #ff9977; 65 | } 66 | 67 | .error { 68 | background-color: #f9f9f9; 69 | margin-bottom: .5em; 70 | padding: 1em; 71 | border-left: 1px solid #ddd; 72 | border-right: 1px solid #ddd; 73 | border-bottom: 1px solid #ddd; 74 | } -------------------------------------------------------------------------------- /spec/screw-unit/screw.events.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(Screw) 3 | .bind('loaded', function() { 4 | $('.describe, .it') 5 | .click(function() { 6 | document.location = location.href.split('?')[0] + '?' + $(this).fn('selector'); 7 | return false; 8 | }) 9 | .focus(function() { 10 | return $(this).addClass('focused'); 11 | }) 12 | .bind('scroll', function() { 13 | document.body.scrollTop = $(this).offset().top; 14 | }); 15 | 16 | $('.it') 17 | .bind('enqueued', function() { 18 | $(this).addClass('enqueued'); 19 | }) 20 | .bind('running', function() { 21 | $(this).addClass('running'); 22 | }) 23 | .bind('passed', function() { 24 | $(this).addClass('passed'); 25 | }) 26 | .bind('failed', function(e, reason) { 27 | $(this) 28 | .addClass('failed') 29 | .append($('

                  ').text(reason.toString())); 30 | if (reason.fileName || reason.lineNumber) { 31 | $(this) 32 | .append($('

                  ').text(reason.fileName + " : " + reason.lineNumber)); 33 | } 34 | }) 35 | }) 36 | .bind('before', function() { 37 | $('.status').text('Running...'); 38 | }) 39 | .bind('after', function() { 40 | $('.status').fn('display') 41 | }) 42 | })(jQuery); -------------------------------------------------------------------------------- /spec/screw-unit/screw.matchers.js: -------------------------------------------------------------------------------- 1 | Screw.Matchers = (function($) { 2 | return matchers = { 3 | expect: function(actual) { 4 | return { 5 | to: function(matcher, expected, not) { 6 | var matched = matcher.match(expected, actual); 7 | if (not ? matched : !matched) { 8 | throw(matcher.failure_message(expected, actual, not)); 9 | } 10 | }, 11 | 12 | to_not: function(matcher, expected) { 13 | this.to(matcher, expected, true); 14 | } 15 | } 16 | }, 17 | 18 | equal: { 19 | match: function(expected, actual) { 20 | if (expected instanceof Array) { 21 | for (var i = 0; i < actual.length; i++) 22 | if (!Screw.Matchers.equal.match(expected[i], actual[i])) return false; 23 | return actual.length == expected.length; 24 | } else if (expected instanceof Object) { 25 | for (var key in expected) 26 | if (expected[key] != actual[key]) return false; 27 | for (var key in actual) 28 | if (actual[key] != expected[key]) return false; 29 | return true; 30 | } else { 31 | return expected == actual; 32 | } 33 | }, 34 | 35 | failure_message: function(expected, actual, not) { 36 | return 'expected ' + $.print(actual) + (not ? ' to not equal ' : ' to equal ') + $.print(expected); 37 | } 38 | }, 39 | 40 | match: { 41 | match: function(expected, actual) { 42 | if (expected.constructor == RegExp) 43 | return expected.exec(actual.toString()); 44 | else 45 | return actual.indexOf(expected) > -1; 46 | }, 47 | 48 | failure_message: function(expected, actual, not) { 49 | return 'expected ' + $.print(actual) + (not ? ' to not match ' : ' to match ') + $.print(expected); 50 | } 51 | }, 52 | 53 | be_empty: { 54 | match: function(expected, actual) { 55 | if (actual.length == undefined) throw(actual.toString() + " does not respond to length"); 56 | 57 | return actual.length == 0; 58 | }, 59 | 60 | failure_message: function(expected, actual, not) { 61 | return 'expected ' + $.print(actual) + (not ? ' to not be empty' : ' to be empty'); 62 | } 63 | }, 64 | 65 | have_length: { 66 | match: function(expected, actual) { 67 | if (actual.length == undefined) throw(actual.toString() + " does not respond to length"); 68 | 69 | return actual.length == expected; 70 | }, 71 | 72 | failure_message: function(expected, actual, not) { 73 | return 'expected ' + $.print(actual) + (not ? ' to not' : ' to') + ' have length ' + expected; 74 | } 75 | }, 76 | 77 | be_null: { 78 | match: function(expected, actual) { 79 | return actual == null; 80 | }, 81 | 82 | failure_message: function(expected, actual, not) { 83 | return 'expected ' + $.print(actual) + (not ? ' to not be null' : ' to be null'); 84 | } 85 | }, 86 | 87 | be_undefined: { 88 | match: function(expected, actual) { 89 | return actual == undefined; 90 | }, 91 | 92 | failure_message: function(expected, actual, not) { 93 | return 'expected ' + $.print(actual) + (not ? ' to not be undefined' : ' to be undefined'); 94 | } 95 | }, 96 | 97 | be_true: { 98 | match: function(expected, actual) { 99 | return actual; 100 | }, 101 | 102 | failure_message: function(expected, actual, not) { 103 | return 'expected ' + $.print(actual) + (not ? ' to not be true' : ' to be true'); 104 | } 105 | }, 106 | 107 | be_false: { 108 | match: function(expected, actual) { 109 | return !actual; 110 | }, 111 | 112 | failure_message: function(expected, actual, not) { 113 | return 'expected ' + $.print(actual) + (not ? ' to not be false' : ' to be false'); 114 | } 115 | }, 116 | 117 | match_selector: { 118 | match: function(expected, actual) { 119 | if (!(actual instanceof jQuery)) { 120 | throw expected.toString() + " must be an instance of jQuery to match against a selector" 121 | } 122 | 123 | return actual.is(expected); 124 | }, 125 | 126 | failure_message: function(expected, actual, not) { 127 | return 'expected ' + $.print(actual) + (not ? ' to not match selector ' : ' to match selector ') + expected; 128 | } 129 | }, 130 | 131 | contain_selector: { 132 | match: function(expected, actual) { 133 | if (!(actual instanceof jQuery)) { 134 | throw expected.toString() + " must be an instance of jQuery to match against a selector" 135 | } 136 | 137 | return actual.find(expected).length > 0; 138 | }, 139 | 140 | failure_message: function(expected, actual, not) { 141 | return 'expected ' + $.print(actual) + (not ? ' to not contain selector ' : ' to contain selector ') + expected; 142 | } 143 | } 144 | } 145 | })(jQuery); -------------------------------------------------------------------------------- /spec/screw-unit/screw.server.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var ajax = $.ajax; 3 | $(Screw).bind('after', function() { 4 | var error_text = $(".error").map(function(i, element) { 5 | return element.innerHTML; 6 | }).get().join("\n"); 7 | 8 | var suite_id; 9 | if(top.runOptions) { 10 | suite_id = top.runOptions.getSessionId(); 11 | } else { 12 | suite_id = 'user'; 13 | } 14 | 15 | ajax({ 16 | type: "POST", 17 | url: '/suites/' + suite_id + '/finish', 18 | data: {"text": error_text} 19 | }); 20 | }); 21 | })(jQuery); 22 | --------------------------------------------------------------------------------