MINOR Moved jquery-fitheighttoparent, jquery-layout, swfupload from sapphire into cms

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92614 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-11-21 02:36:54 +00:00
parent e17b09b2cc
commit 538ff9b321
43 changed files with 21030 additions and 4 deletions

View File

@ -86,7 +86,7 @@ class AssetAdmin extends LeftAndMain {
Requirements::javascript(CMS_DIR . "/javascript/CMSMain_upload.js");
Requirements::javascript(CMS_DIR . "/javascript/Upload.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/swfupload/swfupload.js");
Requirements::javascript(CMS_DIR . "/thirdparty/swfupload/swfupload.js");
Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js");
Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js");

View File

@ -186,8 +186,8 @@ class LeftAndMain extends Controller {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.slide.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.drop.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/effects.scale.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-layout/jquery.layout.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-fitheighttoparent/jquery.fitheighttoparent.js');
Requirements::javascript(CMS_DIR . '/thirdparty/jquery-layout/jquery.layout.js');
Requirements::javascript(CMS_DIR . '/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js');
Requirements::javascript(CMS_DIR . '/javascript/ssui.core.js');
// @todo Load separately so the CSS files can be inlined
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.all.css');
@ -276,7 +276,7 @@ class LeftAndMain extends Controller {
'cms/javascript/LeftAndMain_right.js',
'jsparty/tree/tree.js',
'cms/javascript/TinyMCEImageEnhancement.js',
'sapphire/thirdparty/swfupload/swfupload.js',
'cms/thirdparty/swfupload/swfupload.js',
'cms/javascript/Upload.js',
'cms/javascript/TinyMCEImageEnhancement.js',
'sapphire/javascript/TreeSelectorField.js',

View File

@ -0,0 +1,53 @@
/**
* Fits an element's height to its parent by substracting
* all (visible) siblings heights from the element.
* Caution: This will set overflow: hidden on the parent
*
* Copyright 2009 Ingo Schommer, SilverStripe Ltd.
* Licensed under MIT License: http://www.opensource.org/licenses/mit-license.php
*
* @todo Implement selectors to ignore certain elements
*
* @author Ingo Schommer, SilverStripe Ltd.
* @version 0.1
*/
jQuery.fn.extend({
fitHeightToParent: function() {
return jQuery(this).each(function() {
var $this = jQuery(this);
var boxmodel = ['marginTop','marginBottom','paddingTop','paddingBottom','borderBottomWidth','borderTopWidth'];
// don't bother if element or parent arent visible,
// we won't get height readings
if($this.is(':visible') && $this.parent().is(':visible')) {
// we set overflow = hidden so that large children don't muck things up in IE6 box model
$this.parent().css('overflow', 'hidden');
// get height from parent without any margins as a starting point,
// and reduce any top/bottom paddings
var height = $this.parent().innerHeight()
- parseFloat($this.parent().css('paddingTop'))
- parseFloat($this.parent().css('paddingBottom'));
// substract height of any siblings of the current element
// including their margins/paddings/borders
$this.siblings(':visible').filter(function() {
// remove all absolutely positioned elements
return (jQuery(this).css('position') != 'absolute');
}).each(function() {
height -= jQuery(this).outerHeight(true);
});
// remove margins/paddings/borders on inner element
jQuery.each(boxmodel, function(i, name) {
height -= parseFloat($this.css(name)) || 0;
});
// set new height
$this.height(height);
}
});
}
});

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="jquery.fitheighttoparent.js"></script>
</head>
<body>
test
</body>
</html>

View File

@ -0,0 +1,150 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
<script type="text/javascript" src="../jquery.fitheighttoparent.js"></script>
<link rel="stylesheet" href="http://dev.jquery.com/view/trunk/qunit/testsuite.css" type="text/css" media="screen" />
<style type="text/css">
.outer {background: #333;}
.inner {background: #f0f;}
.sibling {background: #bbb;}
#test1-parent {height: 200px; margin: 10px 0; padding: 15px 10px; border: 5px solid #000;}
#test1-sibling-before {height: 20px; margin: 3px 0; padding: 2px 0;}
#test1-sibling-after {height: 30px; margin: 3px 0; padding: 2px 0;}
#test2-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
#test2-sibling-absolute {height: 20px; position: absolute;}
#test3-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
#test3-sibling {height: 20px;}
#test3-sibling-hidden {height: 30px; display: none;}
#test4-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
#test4-sibling {height: 20px;}
#test4 {margin: 15px 0; padding: 10px 0;border: 5px solid #000;}
#test5-parent {height: 200px; margin: 10px 0; padding: 15px 10px; }
#test5-sibling {height: 20px;}
#test5 {margin: 15px 0; padding: 10px 0;border: 5px solid #000;}
#test6-parent {height: 200px; margin: 10px 0; padding: 15px 10px; position: relative; width: 100%;}
#test6-sibling {height: 20px;}
#test6 {overflow: auto; }
</style>
<script>
$(document).ready(function(){
test("with inline siblings, margins/paddings/borders on parent", function() {
equals(
jQuery('#test1').fitHeightToParent().height(),
130
);
});
test("with absolute siblings", function() {
equals(
jQuery('#test2').fitHeightToParent().height(),
200
);
});
test("with hidden siblings", function() {
equals(
jQuery('#test3').fitHeightToParent().height(),
180
);
});
test("with margins/paddings/borders on resized element", function() {
equals(
jQuery('#test4').fitHeightToParent().height(),
120
);
});
test("form with fieldset", function() {
equals(
jQuery('#test5').fitHeightToParent().height(),
120
);
});
test("overflow auto with long inner element", function() {
equals(
jQuery('#test6').fitHeightToParent().height(),
180
);
});
});
</script>
</head>
<body>
<script type="text/javascript" src="http://jqueryjs.googlecode.com/svn/trunk/qunit/testrunner.js"></script>
<h1>jquery.fitheighttoparent unit test</h1>
<h2 id="banner"></h2>
<h2 id="userAgent"></h2>
<ol id="tests"></ol>
<div id="main"></div>
<div id="test1-parent" class="outer">
<div id="test1-sibling-before" class="sibling"></div>
<div id="test1" class="inner">
<p>test 1</p>
</div>
<div id="test1-sibling-after" class="sibling"></div>
</div>
<div id="test2-parent" class="outer">
<div id="test2-sibling-absolute" class="sibling"></div>
<div id="test2" class="inner">
<p>test 2</p>
</div>
</div>
<div id="test3-parent" class="outer">
<div id="test3-sibling" class="sibling"></div>
<div id="test3" class="inner">
<p>test 3</p>
</div>
<div id="test3-sibling-hidden" class="sibling"></div>
</div>
<div id="test4-parent" class="outer">
<div id="test4-sibling" class="sibling"></div>
<div id="test4" class="inner">
<p>test 4</p>
</div>
</div>
<form action="#" method="POST" id="test5-parent" class="outer">
<fieldset id="test5" class="inner">
<p>test 5</p>
<input type="text">
</fieldset>
<div id="test5-sibling" class="sibling"></div>
</form>
<div id="test6-parent" class="outer">
<div id="test6-sibling" class="sibling"></div>
<div id="test6" class="inner">
<p>test 6</p>
<p>Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.</p>
<p>
Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.</p>
<p>
Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.</p>
</div>
</div>
</body>
</html>

1
thirdparty/jquery-layout/README.txt vendored Normal file
View File

@ -0,0 +1 @@
See http://layout.jquery-dev.net/downloads.html

60
thirdparty/jquery-layout/changelog.txt vendored Executable file
View File

@ -0,0 +1,60 @@
1.2.0
* ADDED maskIframesOnResize option: true=ALL -OR- a selector string
* ADDED options to set different animations on open and close
* ADDED new callback events, ie: onshow, onhide
* ADDED start/end callbacks, eg: onopen_start, onopen_end, etc.
* ADDED ability to cancel events using callbacks, eg: onopen_start
* CHANGED Layout.config.fxDefaults to Layout.effects (internal use)
* FIXED missing semi-colon so minified version works in IE
1.1.3
* FIXED typo in cursor-hotkeys code
* ADDED scrollToBookmarkOnLoad options - enables use of URL hash:
o www.site.com/page.html#myBookmark
o AFTER layout is created, attempts to scroll to bookmark
o default = true - otherwise bookmarks are non-functional
1.1.2
* UPDATED paneSelector rules to handle FORMS and pane-nesting
o automatically looks for panes inside 'first form' in container
o if using an ID as paneSelector, pane can be 'deeply nested'
* ADDED auto-CSS for 'containers' other than BODY
o overflow: hidden - ensures no scrollbars on container
o position: relative - IF NOT: fixed, absolute or relative
o height: 100% - IF NOT specified or is 'auto'
* ADDED noAnimation param to open() and close() - not used internally
1.1.1
* CHANGED toggler element from a SPAN to a DIV
* CHANGED auto-generated custom-buttons classes for better consistency
o [buttonClass]-[pane]-[buttonType] ==> [buttonClass]-[buttonType]-[pane]
o ui-layout-button-west-open ==> ui-layout-button-open-west
o ui-layout-button-west-pin-up ==> ui-layout-button-pin-west-up
* CHANGED default for hideTogglerOnSlide to false
* CHANGED internal 'cDims' hash to alias for state.container
* CHANGED internal aliases: s = state[pane] and o = options[pane]
* UPDATED toggler-text to auto-show correct spans (content-open/closed)
* FIXED toggler-text - now centers text span correctly
* FIXED bug affecting IE6 when layout has no north or south pane
* ADDED new layout property 'state' - eg: myLayout.state.west.size
* REMOVED layout.containerDimensions property - USE: layout.state.container
* CHANGED data returned to callbacks - added pane-state as 3rd param
1.1.0
* RENAMED raisePaneZindexOnHover ==> showOverflowOnHover
* REMOVED "overflow: auto" from base-styles. Overflow must now be set by
CSS - unless applyDefaultStyles==true. No longer need "!important" to
set pane overflow in your stylesheet.
* CHANGED minSize default from 50 to 0 (still auto-limited to 'css size')
* FIXED bug in allowOverflow - now works with 'custom paneClass'
* EXPOSED two CSS utility methods
o myLayout.cssWidth( elem )
o myLayout.cssHeight( elem )
* NEW auto-resize for ALL layouts on windows.resize
* UPDATED auto-resizing of panes after a container-resize
* NEW flow-code to prevent simultaneous pane animations
* NEW options to add text inside toggler-buttons
* NEW options for hotkeys - standard (cursors) and user-defined
1.0
* Initial release

23
thirdparty/jquery-layout/example.html vendored Executable file
View File

@ -0,0 +1,23 @@
<HTML>
<HEAD>
<TITLE>Layout Example</TITLE>
<SCRIPT type="text/javascript" src="jquery.js"></SCRIPT>
<SCRIPT type="text/javascript" src="jquery.layout.js"></SCRIPT>
<SCRIPT type="text/javascript">
$(document).ready(function () {
$('body').layout({ applyDefaultStyles: true });
});
</SCRIPT>
</HEAD>
<BODY>
<DIV class="ui-layout-center">Center
<P><A href="http://layout.jquery-dev.net/demos.html">Go to the Demos page</A></P>
<P>* Pane-resizing is disabled because ui.draggable.js is not linked</P>
<P>* Pane-animation is disabled because ui.effects.js is not linked</P>
</DIV>
<DIV class="ui-layout-north">North</DIV>
<DIV class="ui-layout-south">South</DIV>
<DIV class="ui-layout-east">East</DIV>
<DIV class="ui-layout-west">West</DIV>
</BODY>
</HTML>

4376
thirdparty/jquery-layout/jquery.js vendored Executable file
View File

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

2507
thirdparty/jquery-layout/jquery.layout.js vendored Executable file
View File

@ -0,0 +1,2507 @@
/*
* jquery.layout 1.2.0
*
* Copyright (c) 2008
* Fabrizio Balliano (http://www.fabrizioballiano.net)
* Kevin Dalman (http://allpro.net)
*
* Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
* and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
*
* $Date: 2008-12-27 02:17:22 +0100 (sab, 27 dic 2008) $
* $Rev: 203 $
*
* NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars
*/
(function($) {
$.fn.layout = function (opts) {
/*
* ###########################
* WIDGET CONFIG & OPTIONS
* ###########################
*/
// DEFAULTS for options
var
prefix = "ui-layout-" // prefix for ALL selectors and classNames
, defaults = { // misc default values
paneClass: prefix+"pane" // ui-layout-pane
, resizerClass: prefix+"resizer" // ui-layout-resizer
, togglerClass: prefix+"toggler" // ui-layout-toggler
, togglerInnerClass: prefix+"" // ui-layout-open / ui-layout-closed
, buttonClass: prefix+"button" // ui-layout-button
, contentSelector: "."+prefix+"content"// ui-layout-content
, contentIgnoreSelector: "."+prefix+"ignore" // ui-layout-mask
}
;
// DEFAULT PANEL OPTIONS - CHANGE IF DESIRED
var options = {
name: "" // FUTURE REFERENCE - not used right now
, scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
, defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
applyDefaultStyles: false // apply basic styles directly to resizers & buttons? If not, then stylesheet must handle it
, closable: true // pane can open & close
, resizable: true // when open, pane can be resized
, slidable: true // when closed, pane can 'slide' open over other panes - closes on mouse-out
//, paneSelector: [ ] // MUST be pane-specific!
, contentSelector: defaults.contentSelector // INNER div/element to auto-size so only it scrolls, not the entire pane!
, contentIgnoreSelector: defaults.contentIgnoreSelector // elem(s) to 'ignore' when measuring 'content'
, paneClass: defaults.paneClass // border-Pane - default: 'ui-layout-pane'
, resizerClass: defaults.resizerClass // Resizer Bar - default: 'ui-layout-resizer'
, togglerClass: defaults.togglerClass // Toggler Button - default: 'ui-layout-toggler'
, buttonClass: defaults.buttonClass // CUSTOM Buttons - default: 'ui-layout-button-toggle/-open/-close/-pin'
, resizerDragOpacity: 1 // option for ui.draggable
//, resizerCursor: "" // MUST be pane-specific - cursor when over resizer-bar
, maskIframesOnResize: true // true = all iframes OR = iframe-selector(s) - adds masking-div during resizing/dragging
//, size: 100 // inital size of pane - defaults are set 'per pane'
, minSize: 0 // when manually resizing a pane
, maxSize: 0 // ditto, 0 = no limit
, spacing_open: 6 // space between pane and adjacent panes - when pane is 'open'
, spacing_closed: 6 // ditto - when pane is 'closed'
, togglerLength_open: 50 // Length = WIDTH of toggler button on north/south edges - HEIGHT on east/west edges
, togglerLength_closed: 50 // 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
, togglerAlign_open: "center" // top/left, bottom/right, center, OR...
, togglerAlign_closed: "center" // 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
, togglerTip_open: "Close" // Toggler tool-tip (title)
, togglerTip_closed: "Open" // ditto
, resizerTip: "Resize" // Resizer tool-tip (title)
, sliderTip: "Slide Open" // resizer-bar triggers 'sliding' when pane is closed
, sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
, slideTrigger_open: "click" // click, dblclick, mouseover
, slideTrigger_close: "mouseout" // click, mouseout
, hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
, togglerContent_open: "" // text or HTML to put INSIDE the toggler
, togglerContent_closed: "" // ditto
, showOverflowOnHover: false // will bind allowOverflow() utility to pane.onMouseOver
, enableCursorHotkey: true // enabled 'cursor' hotkeys
//, customHotkey: "" // MUST be pane-specific - EITHER a charCode OR a character
, customHotkeyModifier: "SHIFT" // either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
// NOTE: fxSss_open & fxSss_close options (eg: fxName_open) are auto-generated if not passed
, fxName: "slide" // ('none' or blank), slide, drop, scale
, fxSpeed: null // slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
, fxSettings: {} // can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
, initClosed: false // true = init pane as 'closed'
, initHidden: false // true = init pane as 'hidden' - no resizer or spacing
/* callback options do not have to be set - listed here for reference only
, onshow_start: "" // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
, onshow_end: "" // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
, onhide_start: "" // CALLBACK when pane STARTS to Close - BEFORE onclose_start
, onhide_end: "" // CALLBACK when pane ENDS being Closed - AFTER onclose_end
, onopen_start: "" // CALLBACK when pane STARTS to Open
, onopen_end: "" // CALLBACK when pane ENDS being Opened
, onclose_start: "" // CALLBACK when pane STARTS to Close
, onclose_end: "" // CALLBACK when pane ENDS being Closed
, onresize_start: "" // CALLBACK when pane STARTS to be ***MANUALLY*** Resized
, onresize_end: "" // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
*/
}
, north: {
paneSelector: "."+prefix+"north" // default = .ui-layout-north
, size: "auto"
, resizerCursor: "n-resize"
}
, south: {
paneSelector: "."+prefix+"south" // default = .ui-layout-south
, size: "auto"
, resizerCursor: "s-resize"
}
, east: {
paneSelector: "."+prefix+"east" // default = .ui-layout-east
, size: 200
, resizerCursor: "e-resize"
}
, west: {
paneSelector: "."+prefix+"west" // default = .ui-layout-west
, size: 200
, resizerCursor: "w-resize"
}
, center: {
paneSelector: "."+prefix+"center" // default = .ui-layout-center
}
};
var effects = { // LIST *PREDEFINED EFFECTS* HERE, even if effect has no settings
slide: {
all: { duration: "fast" } // eg: duration: 1000, easing: "easeOutBounce"
, north: { direction: "up" }
, south: { direction: "down" }
, east: { direction: "right"}
, west: { direction: "left" }
}
, drop: {
all: { duration: "slow" } // eg: duration: 1000, easing: "easeOutQuint"
, north: { direction: "up" }
, south: { direction: "down" }
, east: { direction: "right"}
, west: { direction: "left" }
}
, scale: {
all: { duration: "fast" }
}
};
// STATIC, INTERNAL CONFIG - DO NOT CHANGE THIS!
var config = {
allPanes: "north,south,east,west,center"
, borderPanes: "north,south,east,west"
, zIndex: { // set z-index values here
resizer_normal: 1 // normal z-index for resizer-bars
, pane_normal: 2 // normal z-index for panes
, mask: 4 // overlay div used to mask pane(s) during resizing
, sliding: 100 // applied to both the pane and its resizer when a pane is 'slid open'
, resizing: 10000 // applied to the CLONED resizer-bar when being 'dragged'
, animation: 10000 // applied to the pane when being animated - not applied to the resizer
}
, resizers: {
cssReq: {
position: "absolute"
, padding: 0
, margin: 0
, fontSize: "1px"
, textAlign: "left" // to counter-act "center" alignment!
, overflow: "hidden" // keep toggler button from overflowing
, zIndex: 1
}
, cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
background: "#DDD"
, border: "none"
}
}
, togglers: {
cssReq: {
position: "absolute"
, display: "block"
, padding: 0
, margin: 0
, overflow: "hidden"
, textAlign: "center"
, fontSize: "1px"
, cursor: "pointer"
, zIndex: 1
}
, cssDef: { // DEFAULT CSS - applied if: options.PANE.applyDefaultStyles=true
background: "#AAA"
}
}
, content: {
cssReq: {
overflow: "auto"
}
, cssDef: {}
}
, defaults: { // defaults for ALL panes - overridden by 'per-pane settings' below
cssReq: {
position: "absolute"
, margin: 0
, zIndex: 2
}
, cssDef: {
padding: "10px"
, background: "#FFF"
, border: "1px solid #BBB"
, overflow: "auto"
}
}
, north: {
edge: "top"
, sizeType: "height"
, dir: "horz"
, cssReq: {
top: 0
, bottom: "auto"
, left: 0
, right: 0
, width: "auto"
// height: DYNAMIC
}
}
, south: {
edge: "bottom"
, sizeType: "height"
, dir: "horz"
, cssReq: {
top: "auto"
, bottom: 0
, left: 0
, right: 0
, width: "auto"
// height: DYNAMIC
}
}
, east: {
edge: "right"
, sizeType: "width"
, dir: "vert"
, cssReq: {
left: "auto"
, right: 0
, top: "auto" // DYNAMIC
, bottom: "auto" // DYNAMIC
, height: "auto"
// width: DYNAMIC
}
}
, west: {
edge: "left"
, sizeType: "width"
, dir: "vert"
, cssReq: {
left: 0
, right: "auto"
, top: "auto" // DYNAMIC
, bottom: "auto" // DYNAMIC
, height: "auto"
// width: DYNAMIC
}
}
, center: {
dir: "center"
, cssReq: {
left: "auto" // DYNAMIC
, right: "auto" // DYNAMIC
, top: "auto" // DYNAMIC
, bottom: "auto" // DYNAMIC
, height: "auto"
, width: "auto"
}
}
};
// DYNAMIC DATA
var state = {
// generate random 'ID#' to identify layout - used to create global namespace for timers
id: Math.floor(Math.random() * 10000)
, container: {}
, north: {}
, south: {}
, east: {}
, west: {}
, center: {}
};
var
altEdge = {
top: "bottom"
, bottom: "top"
, left: "right"
, right: "left"
}
, altSide = {
north: "south"
, south: "north"
, east: "west"
, west: "east"
}
;
/*
* ###########################
* INTERNAL HELPER FUNCTIONS
* ###########################
*/
/**
* isStr
*
* Returns true if passed param is EITHER a simple string OR a 'string object' - otherwise returns false
*/
var isStr = function (o) {
if (typeof o == "string")
return true;
else if (typeof o == "object") {
try {
var match = o.constructor.toString().match(/string/i);
return (match !== null);
} catch (e) {}
}
return false;
};
/**
* str
*
* Returns a simple string if the passed param is EITHER a simple string OR a 'string object',
* else returns the original object
*/
var str = function (o) {
if (typeof o == "string" || isStr(o)) return $.trim(o); // trim converts 'String object' to a simple string
else return o;
};
/**
* min / max
*
* Alias for Math.min/.max to simplify coding
*/
var min = function (x,y) { return Math.min(x,y); };
var max = function (x,y) { return Math.max(x,y); };
/**
* transformData
*
* Processes the options passed in and transforms them into the format used by layout()
* Missing keys are added, and converts the data if passed in 'flat-format' (no sub-keys)
* In flat-format, pane-specific-settings are prefixed like: north__optName (2-underscores)
* To update effects, options MUST use nested-keys format, with an effects key
*
* @callers initOptions()
* @params JSON d Data/options passed by user - may be a single level or nested levels
* @returns JSON Creates a data struture that perfectly matches 'options', ready to be imported
*/
var transformData = function (d) {
var json = { defaults:{fxSettings:{}}, north:{fxSettings:{}}, south:{fxSettings:{}}, east:{fxSettings:{}}, west:{fxSettings:{}}, center:{fxSettings:{}} };
d = d || {};
if (d.effects || d.defaults || d.north || d.south || d.west || d.east || d.center)
json = $.extend( json, d ); // already in json format - add to base keys
else
// convert 'flat' to 'nest-keys' format - also handles 'empty' user-options
$.each( d, function (key,val) {
a = key.split("__");
json[ a[1] ? a[0] : "defaults" ][ a[1] ? a[1] : a[0] ] = val;
});
return json;
};
/**
* setFlowCallback
*
* Set an INTERNAL callback to avoid simultaneous animation
* Runs only if needed and only if all callbacks are not 'already set'!
*
* @param String action Either 'open' or 'close'
* @pane String pane A valid border-pane name, eg 'west'
* @pane Boolean param Extra param for callback (optional)
*/
var setFlowCallback = function (action, pane, param) {
var
cb = action +","+ pane +","+ (param ? 1 : 0)
, cP, cbPane
;
$.each(c.borderPanes.split(","), function (i,p) {
if (c[p].isMoving) {
bindCallback(p); // TRY to bind a callback
return false; // BREAK
}
});
function bindCallback (p, test) {
cP = c[p];
if (!cP.doCallback) {
cP.doCallback = true;
cP.callback = cb;
}
else { // try to 'chain' this callback
cpPane = cP.callback.split(",")[1]; // 2nd param is 'pane'
if (cpPane != p && cpPane != pane) // callback target NOT 'itself' and NOT 'this pane'
bindCallback (cpPane, true); // RECURSE
}
}
};
/**
* execFlowCallback
*
* RUN the INTERNAL callback for this pane - if one exists
*
* @param String action Either 'open' or 'close'
* @pane String pane A valid border-pane name, eg 'west'
* @pane Boolean param Extra param for callback (optional)
*/
var execFlowCallback = function (pane) {
var cP = c[pane];
// RESET flow-control flaGs
c.isLayoutBusy = false;
delete cP.isMoving;
if (!cP.doCallback || !cP.callback) return;
cP.doCallback = false; // RESET logic flag
// EXECUTE the callback
var
cb = cP.callback.split(",")
, param = (cb[2] > 0 ? true : false)
;
if (cb[0] == "open")
open( cb[1], param );
else if (cb[0] == "close")
close( cb[1], param );
if (!cP.doCallback) cP.callback = null; // RESET - unless callback above enabled it again!
};
/**
* execUserCallback
*
* Executes a Callback function after a trigger event, like resize, open or close
*
* @param String pane This is passed only so we can pass the 'pane object' to the callback
* @param String v_fn Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
*/
var execUserCallback = function (pane, v_fn) {
if (!v_fn) return;
var fn;
try {
if (typeof v_fn == "function")
fn = v_fn;
else if (typeof v_fn != "string")
return;
else if (v_fn.indexOf(",") > 0) {
// function name cannot contain a comma, so must be a function name AND a 'name' parameter
var
args = v_fn.split(",")
, fn = eval(args[0])
;
if (typeof fn=="function" && args.length > 1)
return fn(args[1]); // pass the argument parsed from 'list'
}
else // just the name of an external function?
fn = eval(v_fn);
if (typeof fn=="function")
// pass data: pane-name, pane-element, pane-state, pane-options, and layout-name
return fn( pane, $Ps[pane], $.extend({},state[pane]), $.extend({},options[pane]), options.name );
}
catch (ex) {}
};
/**
* cssNum
*
* Returns the 'current CSS value' for an element - returns 0 if property does not exist
*
* @callers Called by many methods
* @param jQuery $Elem Must pass a jQuery object - first element is processed
* @param String property The name of the CSS property, eg: top, width, etc.
* @returns Variant Usually is used to get an integer value for position (top, left) or size (height, width)
*/
var cssNum = function ($E, prop) {
var
val = 0
, hidden = false
, visibility = ""
;
if (!$.browser.msie) { // IE CAN read dimensions of 'hidden' elements - FF CANNOT
if ($.curCSS($E[0], "display", true) == "none") {
hidden = true;
visibility = $.curCSS($E[0], "visibility", true); // SAVE current setting
$E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so we can measure it
}
}
val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
if (hidden) { // WAS hidden, so put back the way it was
$E.css({ display: "none" });
if (visibility && visibility != "hidden")
$E.css({ visibility: visibility }); // reset 'visibility'
}
return val;
};
/**
* cssW / cssH / cssSize
*
* Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
*
* @callers initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
* @param Variant elem Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
* @param Integer outerWidth/outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
* @returns Integer Returns the innerHeight of the elem by subtracting padding and borders
*
* @TODO May need to add additional logic to handle more browser/doctype variations?
*/
var cssW = function (e, outerWidth) {
var $E;
if (isStr(e)) {
e = str(e);
$E = $Ps[e];
}
else
$E = $(e);
// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
if (outerWidth <= 0)
return 0;
else if (!(outerWidth>0))
outerWidth = isStr(e) ? getPaneSize(e) : $E.outerWidth();
if (!$.boxModel)
return outerWidth;
else // strip border and padding size from outerWidth to get CSS Width
return outerWidth
- cssNum($E, "paddingLeft")
- cssNum($E, "paddingRight")
- ($.curCSS($E[0], "borderLeftStyle", true) == "none" ? 0 : cssNum($E, "borderLeftWidth"))
- ($.curCSS($E[0], "borderRightStyle", true) == "none" ? 0 : cssNum($E, "borderRightWidth"))
;
};
var cssH = function (e, outerHeight) {
var $E;
if (isStr(e)) {
e = str(e);
$E = $Ps[e];
}
else
$E = $(e);
// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
if (outerHeight <= 0)
return 0;
else if (!(outerHeight>0))
outerHeight = (isStr(e)) ? getPaneSize(e) : $E.outerHeight();
if (!$.boxModel)
return outerHeight;
else // strip border and padding size from outerHeight to get CSS Height
return outerHeight
- cssNum($E, "paddingTop")
- cssNum($E, "paddingBottom")
- ($.curCSS($E[0], "borderTopStyle", true) == "none" ? 0 : cssNum($E, "borderTopWidth"))
- ($.curCSS($E[0], "borderBottomStyle", true) == "none" ? 0 : cssNum($E, "borderBottomWidth"))
;
};
var cssSize = function (pane, outerSize) {
if (c[pane].dir=="horz") // pane = north or south
return cssH(pane, outerSize);
else // pane = east or west
return cssW(pane, outerSize);
};
/**
* getPaneSize
*
* Calculates the current 'size' (width or height) of a border-pane - optionally with 'pane spacing' added
*
* @returns Integer Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
*/
var getPaneSize = function (pane, inclSpace) {
var
$P = $Ps[pane]
, o = options[pane]
, s = state[pane]
, oSp = (inclSpace ? o.spacing_open : 0)
, cSp = (inclSpace ? o.spacing_closed : 0)
;
if (!$P || s.isHidden)
return 0;
else if (s.isClosed || (s.isSliding && inclSpace))
return cSp;
else if (c[pane].dir == "horz")
return $P.outerHeight() + oSp;
else // dir == "vert"
return $P.outerWidth() + oSp;
};
var setPaneMinMaxSizes = function (pane) {
var
d = cDims
, edge = c[pane].edge
, dir = c[pane].dir
, o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $altPane = $Ps[ altSide[pane] ]
, paneSpacing = o.spacing_open
, altPaneSpacing = options[ altSide[pane] ].spacing_open
, altPaneSize = (!$altPane ? 0 : (dir=="horz" ? $altPane.outerHeight() : $altPane.outerWidth()))
, containerSize = (dir=="horz" ? d.innerHeight : d.innerWidth)
// limitSize prevents this pane from 'overlapping' opposite pane - even if opposite pane is currently closed
, limitSize = containerSize - paneSpacing - altPaneSize - altPaneSpacing
, minSize = s.minSize || 0
, maxSize = Math.min(s.maxSize || 9999, limitSize)
, minPos, maxPos // used to set resizing limits
;
switch (pane) {
case "north": minPos = d.offsetTop + minSize;
maxPos = d.offsetTop + maxSize;
break;
case "west": minPos = d.offsetLeft + minSize;
maxPos = d.offsetLeft + maxSize;
break;
case "south": minPos = d.offsetTop + d.innerHeight - maxSize;
maxPos = d.offsetTop + d.innerHeight - minSize;
break;
case "east": minPos = d.offsetLeft + d.innerWidth - maxSize;
maxPos = d.offsetLeft + d.innerWidth - minSize;
break;
}
// save data to pane-state
$.extend(s, { minSize: minSize, maxSize: maxSize, minPosition: minPos, maxPosition: maxPos });
};
/**
* getPaneDims
*
* Returns data for setting the size/position of center pane. Date is also used to set Height for east/west panes
*
* @returns JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
*/
var getPaneDims = function () {
var d = {
top: getPaneSize("north", true) // true = include 'spacing' value for p
, bottom: getPaneSize("south", true)
, left: getPaneSize("west", true)
, right: getPaneSize("east", true)
, width: 0
, height: 0
};
with (d) {
width = cDims.innerWidth - left - right;
height = cDims.innerHeight - bottom - top;
// now add the 'container border/padding' to get final positions - relative to the container
top += cDims.top;
bottom += cDims.bottom;
left += cDims.left;
right += cDims.right;
}
return d;
};
/**
* getElemDims
*
* Returns data for setting size of an element (container or a pane).
*
* @callers create(), onWindowResize() for container, plus others for pane
* @returns JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
*/
var getElemDims = function ($E) {
var
d = {} // dimensions hash
, e, b, p // edge, border, padding
;
$.each("Left,Right,Top,Bottom".split(","), function () {
e = str(this);
b = d["border" +e] = cssNum($E, "border"+e+"Width");
p = d["padding"+e] = cssNum($E, "padding"+e);
d["offset" +e] = b + p; // total offset of content from outer edge
// if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
if ($E == $Container)
d[e.toLowerCase()] = ($.boxModel ? p : 0);
});
d.innerWidth = d.outerWidth = $E.outerWidth();
d.innerHeight = d.outerHeight = $E.outerHeight();
if ($.boxModel) {
d.innerWidth -= (d.offsetLeft + d.offsetRight);
d.innerHeight -= (d.offsetTop + d.offsetBottom);
}
return d;
};
var setTimer = function (pane, action, fn, ms) {
var
Layout = window.layout = window.layout || {}
, Timers = Layout.timers = Layout.timers || {}
, name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
;
if (Timers[name]) return; // timer already set!
else Timers[name] = setTimeout(fn, ms);
};
var clearTimer = function (pane, action) {
var
Layout = window.layout = window.layout || {}
, Timers = Layout.timers = Layout.timers || {}
, name = "layout_"+ state.id +"_"+ pane +"_"+ action // UNIQUE NAME for every layout-pane-action
;
if (Timers[name]) {
clearTimeout( Timers[name] );
delete Timers[name];
return true;
}
else
return false;
};
/*
* ###########################
* INITIALIZATION METHODS
* ###########################
*/
/**
* create
*
* Initialize the layout - called automatically whenever an instance of layout is created
*
* @callers NEVER explicity called
* @returns An object pointer to the instance created
*/
var create = function () {
// initialize config/options
initOptions();
// initialize all objects
initContainer(); // set CSS as needed and init state.container dimensions
initPanes(); // size & position all panes
initHandles(); // create and position all resize bars & togglers buttons
initResizable(); // activate resizing on all panes where resizable=true
sizeContent("all"); // AFTER panes & handles have been initialized, size 'content' divs
if (options.scrollToBookmarkOnLoad)
with (self.location) if (hash) replace( hash ); // scrollTo Bookmark
// bind hotkey function - keyDown - if required
initHotkeys();
// bind resizeAll() for 'this layout instance' to window.resize event
$(window).resize(function () {
var timerID = "timerLayout_"+state.id;
if (window[timerID]) clearTimeout(window[timerID]);
window[timerID] = null;
if (true || $.browser.msie) // use a delay for IE because the resize event fires repeatly
window[timerID] = setTimeout(resizeAll, 100);
else // most other browsers have a built-in delay before firing the resize event
resizeAll(); // resize all layout elements NOW!
});
};
/**
* initContainer
*
* Validate and initialize container CSS and events
*
* @callers create()
*/
var initContainer = function () {
try { // format html/body if this is a full page layout
if ($Container[0].tagName == "BODY") {
$("html").css({
height: "100%"
, overflow: "hidden"
});
$("body").css({
position: "relative"
, height: "100%"
, overflow: "hidden"
, margin: 0
, padding: 0 // TODO: test whether body-padding could be handled?
, border: "none" // a body-border creates problems because it cannot be measured!
});
}
else { // set required CSS - overflow and position
var
CSS = { overflow: "hidden" } // make sure container will not 'scroll'
, p = $Container.css("position")
, h = $Container.css("height")
;
// if this is a NESTED layout, then outer-pane ALREADY has position and height
if (!$Container.hasClass("ui-layout-pane")) {
if (!p || "fixed,absolute,relative".indexOf(p) < 0)
CSS.position = "relative"; // container MUST have a 'position'
if (!h || h=="auto")
CSS.height = "100%"; // container MUST have a 'height'
}
$Container.css( CSS );
}
} catch (ex) {}
// get layout-container dimensions (updated when necessary)
cDims = state.container = getElemDims( $Container ); // update data-pointer too
};
/**
* initHotkeys
*
* Bind layout hotkeys - if options enabled
*
* @callers create()
*/
var initHotkeys = function () {
// bind keyDown to capture hotkeys, if option enabled for ANY pane
$.each(c.borderPanes.split(","), function (i,pane) {
var o = options[pane];
if (o.enableCursorHotkey || o.customHotkey) {
$(document).keydown( keyDown ); // only need to bind this ONCE
return false; // BREAK - binding was done
}
});
};
/**
* initOptions
*
* Build final CONFIG and OPTIONS data
*
* @callers create()
*/
var initOptions = function () {
// simplify logic by making sure passed 'opts' var has basic keys
opts = transformData( opts );
// update default effects, if case user passed key
if (opts.effects) {
$.extend( effects, opts.effects );
delete opts.effects;
}
// see if any 'global options' were specified
$.each("name,scrollToBookmarkOnLoad".split(","), function (idx,key) {
if (opts[key] !== undefined)
options[key] = opts[key];
else if (opts.defaults[key] !== undefined) {
options[key] = opts.defaults[key];
delete opts.defaults[key];
}
});
// remove any 'defaults' that MUST be set 'per-pane'
$.each("paneSelector,resizerCursor,customHotkey".split(","),
function (idx,key) { delete opts.defaults[key]; } // is OK if key does not exist
);
// now update options.defaults
$.extend( options.defaults, opts.defaults );
// make sure required sub-keys exist
//if (typeof options.defaults.fxSettings != "object") options.defaults.fxSettings = {};
// merge all config & options for the 'center' pane
c.center = $.extend( true, {}, c.defaults, c.center );
$.extend( options.center, opts.center );
// Most 'default options' do not apply to 'center', so add only those that DO
var o_Center = $.extend( true, {}, options.defaults, opts.defaults, options.center ); // TEMP data
$.each("paneClass,contentSelector,contentIgnoreSelector,applyDefaultStyles,showOverflowOnHover".split(","),
function (idx,key) { options.center[key] = o_Center[key]; }
);
var defs = options.defaults;
// create a COMPLETE set of options for EACH border-pane
$.each(c.borderPanes.split(","), function(i,pane) {
// apply 'pane-defaults' to CONFIG.PANE
c[pane] = $.extend( true, {}, c.defaults, c[pane] );
// apply 'pane-defaults' + user-options to OPTIONS.PANE
o = options[pane] = $.extend( true, {}, options.defaults, options[pane], opts.defaults, opts[pane] );
// make sure we have base-classes
if (!o.paneClass) o.paneClass = defaults.paneClass;
if (!o.resizerClass) o.resizerClass = defaults.resizerClass;
if (!o.togglerClass) o.togglerClass = defaults.togglerClass;
// create FINAL fx options for each pane, ie: options.PANE.fxName/fxSpeed/fxSettings[_open|_close]
$.each(["_open","_close",""], function (i,n) {
var
sName = "fxName"+n
, sSpeed = "fxSpeed"+n
, sSettings = "fxSettings"+n
;
// recalculate fxName according to specificity rules
o[sName] =
opts[pane][sName] // opts.west.fxName_open
|| opts[pane].fxName // opts.west.fxName
|| opts.defaults[sName] // opts.defaults.fxName_open
|| opts.defaults.fxName // opts.defaults.fxName
|| o[sName] // options.west.fxName_open
|| o.fxName // options.west.fxName
|| defs[sName] // options.defaults.fxName_open
|| defs.fxName // options.defaults.fxName
|| "none"
;
// validate fxName to be sure is a valid effect
var fxName = o[sName];
if (fxName == "none" || !$.effects || !$.effects[fxName] || (!effects[fxName] && !o[sSettings] && !o.fxSettings))
fxName = o[sName] = "none"; // effect not loaded, OR undefined FX AND fxSettings not passed
// set vars for effects subkeys to simplify logic
var
fx = effects[fxName] || {} // effects.slide
, fx_all = fx.all || {} // effects.slide.all
, fx_pane = fx[pane] || {} // effects.slide.west
;
// RECREATE the fxSettings[_open|_close] keys using specificity rules
o[sSettings] = $.extend(
{}
, fx_all // effects.slide.all
, fx_pane // effects.slide.west
, defs.fxSettings || {} // options.defaults.fxSettings
, defs[sSettings] || {} // options.defaults.fxSettings_open
, o.fxSettings // options.west.fxSettings
, o[sSettings] // options.west.fxSettings_open
, opts.defaults.fxSettings // opts.defaults.fxSettings
, opts.defaults[sSettings] || {} // opts.defaults.fxSettings_open
, opts[pane].fxSettings // opts.west.fxSettings
, opts[pane][sSettings] || {} // opts.west.fxSettings_open
);
// recalculate fxSpeed according to specificity rules
o[sSpeed] =
opts[pane][sSpeed] // opts.west.fxSpeed_open
|| opts[pane].fxSpeed // opts.west.fxSpeed (pane-default)
|| opts.defaults[sSpeed] // opts.defaults.fxSpeed_open
|| opts.defaults.fxSpeed // opts.defaults.fxSpeed
|| o[sSpeed] // options.west.fxSpeed_open
|| o[sSettings].duration // options.west.fxSettings_open.duration
|| o.fxSpeed // options.west.fxSpeed
|| o.fxSettings.duration // options.west.fxSettings.duration
|| defs.fxSpeed // options.defaults.fxSpeed
|| defs.fxSettings.duration// options.defaults.fxSettings.duration
|| fx_pane.duration // effects.slide.west.duration
|| fx_all.duration // effects.slide.all.duration
|| "normal" // DEFAULT
;
// DEBUG: if (pane=="east") debugData( $.extend({}, {speed: o[sSpeed], fxSettings_duration: o[sSettings].duration}, o[sSettings]), pane+"."+sName+" = "+fxName );
});
});
};
/**
* initPanes
*
* Initialize module objects, styling, size and position for all panes
*
* @callers create()
*/
var initPanes = function () {
// NOTE: do north & south FIRST so we can measure their height - do center LAST
$.each(c.allPanes.split(","), function() {
var
pane = str(this)
, o = options[pane]
, s = state[pane]
, fx = s.fx
, dir = c[pane].dir
// if o.size is not > 0, then we will use MEASURE the pane and use that as it's 'size'
, size = o.size=="auto" || isNaN(o.size) ? 0 : o.size
, minSize = o.minSize || 1
, maxSize = o.maxSize || 9999
, spacing = o.spacing_open || 0
, sel = o.paneSelector
, isIE6 = ($.browser.msie && $.browser.version < 7)
, CSS = {}
, $P, $C
;
$Cs[pane] = false; // init
if (sel.substr(0,1)==="#") // ID selector
// NOTE: elements selected 'by ID' DO NOT have to be 'children'
$P = $Ps[pane] = $Container.find(sel+":first");
else { // class or other selector
$P = $Ps[pane] = $Container.children(sel+":first");
// look for the pane nested inside a 'form' element
if (!$P.length) $P = $Ps[pane] = $Container.children("form:first").children(sel+":first");
}
if (!$P.length) {
$Ps[pane] = false; // logic
return true; // SKIP to next
}
// add basic classes & attributes
$P
.attr("pane", pane) // add pane-identifier
.addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
;
// init pane-logic vars, etc.
if (pane != "center") {
s.isClosed = false; // true = pane is closed
s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
s.isResizing= false; // true = pane is in process of being resized
s.isHidden = false; // true = pane is hidden - no spacing, resizer or toggler is visible!
s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
// create special keys for internal use
c[pane].pins = []; // used to track and sync 'pin-buttons' for border-panes
}
CSS = $.extend({ visibility: "visible", display: "block" }, c.defaults.cssReq, c[pane].cssReq );
if (o.applyDefaultStyles) $.extend( CSS, c.defaults.cssDef, c[pane].cssDef ); // cosmetic defaults
$P.css(CSS); // add base-css BEFORE 'measuring' to calc size & position
CSS = {}; // reset var
// set css-position to account for container borders & padding
switch (pane) {
case "north": CSS.top = cDims.top;
CSS.left = cDims.left;
CSS.right = cDims.right;
break;
case "south": CSS.bottom = cDims.bottom;
CSS.left = cDims.left;
CSS.right = cDims.right;
break;
case "west": CSS.left = cDims.left; // top, bottom & height set by sizeMidPanes()
break;
case "east": CSS.right = cDims.right; // ditto
break;
case "center": // top, left, width & height set by sizeMidPanes()
}
if (dir == "horz") { // north or south pane
if (size === 0 || size == "auto") {
$P.css({ height: "auto" });
size = $P.outerHeight();
}
size = max(size, minSize);
size = min(size, maxSize);
size = min(size, cDims.innerHeight - spacing);
CSS.height = max(1, cssH(pane, size));
s.size = size; // update state
// make sure minSize is sufficient to avoid errors
s.maxSize = maxSize; // init value
s.minSize = max(minSize, size - CSS.height + 1); // = pane.outerHeight when css.height = 1px
// handle IE6
//if (isIE6) CSS.width = cssW($P, cDims.innerWidth);
$P.css(CSS); // apply size & position
}
else if (dir == "vert") { // east or west pane
if (size === 0 || size == "auto") {
$P.css({ width: "auto", float: "left" }); // float = FORCE pane to auto-size
size = $P.outerWidth();
$P.css({ float: "none" }); // RESET
}
size = max(size, minSize);
size = min(size, maxSize);
size = min(size, cDims.innerWidth - spacing);
CSS.width = max(1, cssW(pane, size));
s.size = size; // update state
s.maxSize = maxSize; // init value
// make sure minSize is sufficient to avoid errors
s.minSize = max(minSize, size - CSS.width + 1); // = pane.outerWidth when css.width = 1px
$P.css(CSS); // apply size - top, bottom & height set by sizeMidPanes
sizeMidPanes(pane, null, true); // true = onInit
}
else if (pane == "center") {
$P.css(CSS); // top, left, width & height set by sizeMidPanes...
sizeMidPanes("center", null, true); // true = onInit
}
// close or hide the pane if specified in settings
if (o.initClosed && o.closable) {
$P.hide().addClass("closed");
s.isClosed = true;
}
else if (o.initHidden || o.initClosed) {
hide(pane, true); // will be completely invisible - no resizer or spacing
s.isHidden = true;
}
else
$P.addClass("open");
// check option for auto-handling of pop-ups & drop-downs
if (o.showOverflowOnHover)
$P.hover( allowOverflow, resetOverflow );
/*
* see if this pane has a 'content element' that we need to auto-size
*/
if (o.contentSelector) {
$C = $Cs[pane] = $P.children(o.contentSelector+":first"); // match 1-element only
if (!$C.length) {
$Cs[pane] = false;
return true; // SKIP to next
}
$C.css( c.content.cssReq );
if (o.applyDefaultStyles) $C.css( c.content.cssDef ); // cosmetic defaults
// NO PANE-SCROLLING when there is a content-div
$P.css({ overflow: "hidden" });
}
});
};
/**
* initHandles
*
* Initialize module objects, styling, size and position for all resize bars and toggler buttons
*
* @callers create()
*/
var initHandles = function () {
// create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
$.each(c.borderPanes.split(","), function() {
var
pane = str(this)
, o = options[pane]
, s = state[pane]
, rClass = o.resizerClass
, tClass = o.togglerClass
, $P = $Ps[pane]
;
$Rs[pane] = false; // INIT
$Ts[pane] = false;
if (!$P || (!o.closable && !o.resizable)) return; // pane does not exist - skip
var
edge = c[pane].edge
, isOpen = $P.is(":visible")
, spacing = (isOpen ? o.spacing_open : o.spacing_closed)
, _pane = "-"+ pane // used for classNames
, _state = (isOpen ? "-open" : "-closed") // used for classNames
, $R, $T
;
// INIT RESIZER BAR
$R = $Rs[pane] = $("<span></span>");
if (isOpen && o.resizable)
; // this is handled by initResizable
else if (!isOpen && o.slidable)
$R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);
$R
// if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
.attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
.attr("resizer", pane) // so we can read this from the resizer
.css(c.resizers.cssReq) // add base/required styles
// POSITION of resizer bar - allow for container border & padding
.css(edge, cDims[edge] + getPaneSize(pane))
// ADD CLASSNAMES - eg: class="resizer resizer-west resizer-open"
.addClass( rClass +" "+ rClass+_pane +" "+ rClass+_state +" "+ rClass+_pane+_state )
.appendTo($Container) // append DIV to container
;
// ADD VISUAL STYLES
if (o.applyDefaultStyles)
$R.css(c.resizers.cssDef);
if (o.closable) {
// INIT COLLAPSER BUTTON
$T = $Ts[pane] = $("<div></div>");
$T
// if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-toggler"
.attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
.css(c.togglers.cssReq) // add base/required styles
.attr("title", (isOpen ? o.togglerTip_open : o.togglerTip_closed))
.click(function(evt){ toggle(pane); evt.stopPropagation(); })
.mouseover(function(evt){ evt.stopPropagation(); }) // prevent resizer event
// ADD CLASSNAMES - eg: class="toggler toggler-west toggler-west-open"
.addClass( tClass +" "+ tClass+_pane +" "+ tClass+_state +" "+ tClass+_pane+_state )
.appendTo($R) // append SPAN to resizer DIV
;
// ADD INNER-SPANS TO TOGGLER
if (o.togglerContent_open) // ui-layout-open
$("<span>"+ o.togglerContent_open +"</span>")
.addClass("content content-open")
.css("display", s.isClosed ? "none" : "block")
.appendTo( $T )
;
if (o.togglerContent_closed) // ui-layout-closed
$("<span>"+ o.togglerContent_closed +"</span>")
.addClass("content content-closed")
.css("display", s.isClosed ? "block" : "none")
.appendTo( $T )
;
// ADD BASIC VISUAL STYLES
if (o.applyDefaultStyles)
$T.css(c.togglers.cssDef);
if (!isOpen) bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
}
});
// SET ALL HANDLE SIZES & LENGTHS
sizeHandles("all", true); // true = onInit
};
/**
* initResizable
*
* Add resize-bars to all panes that specify it in options
*
* @dependancies $.fn.resizable - will abort if not found
* @callers create()
*/
var initResizable = function () {
var
draggingAvailable = (typeof $.fn.draggable == "function")
, minPosition, maxPosition, edge // set in start()
;
$.each(c.borderPanes.split(","), function() {
var
pane = str(this)
, o = options[pane]
, s = state[pane]
;
if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
o.resizable = false;
return true; // skip to next
}
var
rClass = o.resizerClass
// 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
, dragClass = rClass+"-drag" // resizer-drag
, dragPaneClass = rClass+"-"+pane+"-drag" // resizer-north-drag
// 'dragging' class is applied to the CLONED resizer-bar while it is being dragged
, draggingClass = rClass+"-dragging" // resizer-dragging
, draggingPaneClass = rClass+"-"+pane+"-dragging" // resizer-north-dragging
, draggingClassSet = false // logic var
, $P = $Ps[pane]
, $R = $Rs[pane]
;
if (!s.isClosed)
$R
.attr("title", o.resizerTip)
.css("cursor", o.resizerCursor) // n-resize, s-resize, etc
;
$R.draggable({
containment: $Container[0] // limit resizing to layout container
, axis: (c[pane].dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
, delay: 200
, distance: 1
// basic format for helper - style it using class: .ui-draggable-dragging
, helper: "clone"
, opacity: o.resizerDragOpacity
//, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
, zIndex: c.zIndex.resizing
, start: function (e, ui) {
// onresize_start callback - will CANCEL hide if returns false
// TODO: CONFIRM that dragging can be cancelled like this???
if (false === execUserCallback(pane, o.onresize_start)) return false;
s.isResizing = true; // prevent pane from closing while resizing
clearTimer(pane, "closeSlider"); // just in case already triggered
$R.addClass( dragClass +" "+ dragPaneClass ); // add drag classes
draggingClassSet = false; // reset logic var - see drag()
// SET RESIZING LIMITS - used in drag()
var resizerWidth = (pane=="east" || pane=="south" ? o.spacing_open : 0);
setPaneMinMaxSizes(pane); // update pane-state
s.minPosition -= resizerWidth;
s.maxPosition -= resizerWidth;
edge = (c[pane].dir=="horz" ? "top" : "left");
// MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
$(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).each(function() {
$('<div class="ui-layout-mask"/>')
.css({
background: "#fff"
, opacity: "0.001"
, zIndex: 9
, position: "absolute"
, width: this.offsetWidth+"px"
, height: this.offsetHeight+"px"
})
.css($(this).offset()) // top & left
.appendTo(this.parentNode) // put div INSIDE pane to avoid zIndex issues
;
});
}
, drag: function (e, ui) {
if (!draggingClassSet) { // can only add classes after clone has been added to the DOM
$(".ui-draggable-dragging")
.addClass( draggingClass +" "+ draggingPaneClass ) // add dragging classes
.children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
;
draggingClassSet = true;
// draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
if (s.isSliding) $Ps[pane].css("zIndex", c.zIndex.sliding);
}
// CONTAIN RESIZER-BAR TO RESIZING LIMITS
if (ui.position[edge] < s.minPosition) ui.position[edge] = s.minPosition;
else if (ui.position[edge] > s.maxPosition) ui.position[edge] = s.maxPosition;
}
, stop: function (e, ui) {
var
dragPos = ui.position
, resizerPos
, newSize
;
$R.removeClass( dragClass +" "+ dragPaneClass ); // remove drag classes
switch (pane) {
case "north": resizerPos = dragPos.top; break;
case "west": resizerPos = dragPos.left; break;
case "south": resizerPos = cDims.outerHeight - dragPos.top - $R.outerHeight(); break;
case "east": resizerPos = cDims.outerWidth - dragPos.left - $R.outerWidth(); break;
}
// remove container margin from resizer position to get the pane size
newSize = resizerPos - cDims[ c[pane].edge ];
sizePane(pane, newSize);
// UN-MASK PANES MASKED IN drag.start
$("div.ui-layout-mask").remove(); // Remove iframe masks
s.isResizing = false;
}
});
});
};
/*
* ###########################
* ACTION METHODS
* ###########################
*/
/**
* hide / show
*
* Completely 'hides' a pane, including its spacing - as if it does not exist
* The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
*
* @param String pane The pane being hidden, ie: north, south, east, or west
*/
var hide = function (pane, onInit) {
var
o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
;
if (!$P || s.isHidden) return; // pane does not exist OR is already hidden
// onhide_start callback - will CANCEL hide if returns false
if (false === execUserCallback(pane, o.onhide_start)) return;
s.isSliding = false; // just in case
// now hide the elements
if ($R) $R.hide(); // hide resizer-bar
if (onInit || s.isClosed) {
s.isClosed = true; // to trigger open-animation on show()
s.isHidden = true;
$P.hide(); // no animation when loading page
sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
execUserCallback(pane, o.onhide_end || o.onhide);
}
else {
s.isHiding = true; // used by onclose
close(pane, false); // adjust all panes to fit
//s.isHidden = true; - will be set by close - if not cancelled
}
};
var show = function (pane, openPane) {
var
o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
;
if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden
// onhide_start callback - will CANCEL hide if returns false
if (false === execUserCallback(pane, o.onshow_start)) return;
s.isSliding = false; // just in case
s.isShowing = true; // used by onopen/onclose
//s.isHidden = false; - will be set by open/close - if not cancelled
// now show the elements
if ($R && o.spacing_open > 0) $R.show();
if (openPane === false)
close(pane, true); // true = force
else
open(pane); // adjust all panes to fit
};
/**
* toggle
*
* Toggles a pane open/closed by calling either open or close
*
* @param String pane The pane being toggled, ie: north, south, east, or west
*/
var toggle = function (pane) {
var s = state[pane];
if (s.isHidden)
show(pane); // will call 'open' after unhiding it
else if (s.isClosed)
open(pane);
else
close(pane);
};
/**
* close
*
* Close the specified pane (animation optional), and resize all other panes as needed
*
* @param String pane The pane being closed, ie: north, south, east, or west
*/
var close = function (pane, force, noAnimation) {
var
$P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, o = options[pane]
, s = state[pane]
, doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none")
, edge = c[pane].edge
, rClass = o.resizerClass
, tClass = o.togglerClass
, _pane = "-"+ pane // used for classNames
, _open = "-open"
, _sliding= "-sliding"
, _closed = "-closed"
// transfer logic vars to temp vars
, isShowing = s.isShowing
, isHiding = s.isHiding
;
// now clear the logic vars
delete s.isShowing;
delete s.isHiding;
if (!$P || (!o.resizable && !o.closable)) return; // invalid request
else if (!force && s.isClosed && !isShowing) return; // already closed
if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
setFlowCallback("close", pane, force); // set a callback for this action, if possible
return; // ABORT
}
// onclose_start callback - will CANCEL hide if returns false
// SKIP if just 'showing' a hidden pane as 'closed'
if (!isShowing && false === execUserCallback(pane, o.onclose_start)) return;
// SET flow-control flags
c[pane].isMoving = true;
c.isLayoutBusy = true;
s.isClosed = true;
// update isHidden BEFORE sizing panes
if (isHiding) s.isHidden = true;
else if (isShowing) s.isHidden = false;
// sync any 'pin buttons'
syncPinBtns(pane, false);
// resize panes adjacent to this one
if (!s.isSliding) sizeMidPanes(c[pane].dir == "horz" ? "all" : "center");
// if this pane has a resizer bar, move it now
if ($R) {
$R
.css(edge, cDims[edge]) // move the resizer bar
.removeClass( rClass+_open +" "+ rClass+_pane+_open )
.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
.addClass( rClass+_closed +" "+ rClass+_pane+_closed )
;
// DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent
if (o.resizable)
$R
.draggable("disable")
.css("cursor", "default")
.attr("title","")
;
// if pane has a toggler button, adjust that too
if ($T) {
$T
.removeClass( tClass+_open +" "+ tClass+_pane+_open )
.addClass( tClass+_closed +" "+ tClass+_pane+_closed )
.attr("title", o.togglerTip_closed) // may be blank
;
}
sizeHandles(); // resize 'length' and position togglers for adjacent panes
}
// ANIMATE 'CLOSE' - if no animation, then was ALREADY shown above
if (doFX) {
lockPaneForFX(pane, true); // need to set left/top so animation will work
$P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
lockPaneForFX(pane, false); // undo
if (!s.isClosed) return; // pane was opened before animation finished!
close_2();
});
}
else {
$P.hide(); // just hide pane NOW
close_2();
}
// SUBROUTINE
function close_2 () {
bindStartSlidingEvent(pane, true); // will enable if state.PANE.isSliding = true
// onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
if (!isShowing) execUserCallback(pane, o.onclose_end || o.onclose);
// onhide OR onshow callback
if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
if (isHiding) execUserCallback(pane, o.onhide_end || o.onhide);
// internal flow-control callback
execFlowCallback(pane);
}
};
/**
* open
*
* Open the specified pane (animation optional), and resize all other panes as needed
*
* @param String pane The pane being opened, ie: north, south, east, or west
*/
var open = function (pane, slide, noAnimation) {
var
$P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, o = options[pane]
, s = state[pane]
, doFX = !noAnimation && s.isClosed && (o.fxName_open != "none")
, edge = c[pane].edge
, rClass = o.resizerClass
, tClass = o.togglerClass
, _pane = "-"+ pane // used for classNames
, _open = "-open"
, _closed = "-closed"
, _sliding= "-sliding"
// transfer logic var to temp var
, isShowing = s.isShowing
;
// now clear the logic var
delete s.isShowing;
if (!$P || (!o.resizable && !o.closable)) return; // invalid request
else if (!s.isClosed && !s.isSliding) return; // already open
// pane can ALSO be unhidden by just calling show(), so handle this scenario
if (s.isHidden && !isShowing) {
show(pane, true);
return;
}
if (c.isLayoutBusy) { // layout is 'busy' - probably with an animation
setFlowCallback("open", pane, slide); // set a callback for this action, if possible
return; // ABORT
}
// onopen_start callback - will CANCEL hide if returns false
if (false === execUserCallback(pane, o.onopen_start)) return;
// SET flow-control flags
c[pane].isMoving = true;
c.isLayoutBusy = true;
// 'PIN PANE' - stop sliding
if (s.isSliding && !slide) // !slide = 'open pane normally' - NOT sliding
bindStopSlidingEvents(pane, false); // will set isSliding=false
s.isClosed = false;
// update isHidden BEFORE sizing panes
if (isShowing) s.isHidden = false;
// Container size may have changed - shrink the pane if now 'too big'
setPaneMinMaxSizes(pane); // update pane-state
if (s.size > s.maxSize) // pane is too big! resize it before opening
$P.css( c[pane].sizeType, max(1, cssSize(pane, s.maxSize)) );
bindStartSlidingEvent(pane, false); // remove trigger event from resizer-bar
if (doFX) { // ANIMATE
lockPaneForFX(pane, true); // need to set left/top so animation will work
$P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
lockPaneForFX(pane, false); // undo
if (s.isClosed) return; // pane was closed before animation finished!
open_2(); // continue
});
}
else {// no animation
$P.show(); // just show pane and...
open_2(); // continue
}
// SUBROUTINE
function open_2 () {
// NOTE: if isSliding, then other panes are NOT 'resized'
if (!s.isSliding) // resize all panes adjacent to this one
sizeMidPanes(c[pane].dir=="vert" ? "center" : "all");
// if this pane has a toggler, move it now
if ($R) {
$R
.css(edge, cDims[edge] + getPaneSize(pane)) // move the toggler
.removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
.addClass( rClass+_open +" "+ rClass+_pane+_open )
.addClass( !s.isSliding ? "" : rClass+_sliding +" "+ rClass+_pane+_sliding )
;
if (o.resizable)
$R
.draggable("enable")
.css("cursor", o.resizerCursor)
.attr("title", o.resizerTip)
;
else
$R.css("cursor", "default"); // n-resize, s-resize, etc
// if pane also has a toggler button, adjust that too
if ($T) {
$T
.removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
.addClass( tClass+_open +" "+ tClass+_pane+_open )
.attr("title", o.togglerTip_open) // may be blank
;
}
sizeHandles("all"); // resize resizer & toggler sizes for all panes
}
// resize content every time pane opens - to be sure
sizeContent(pane);
// sync any 'pin buttons'
syncPinBtns(pane, !s.isSliding);
// onopen callback
execUserCallback(pane, o.onopen_end || o.onopen);
// onshow callback
if (isShowing) execUserCallback(pane, o.onshow_end || o.onshow);
// internal flow-control callback
execFlowCallback(pane);
}
};
/**
* lockPaneForFX
*
* Must set left/top on East/South panes so animation will work properly
*
* @param String pane The pane to lock, 'east' or 'south' - any other is ignored!
* @param Boolean doLock true = set left/top, false = remove
*/
var lockPaneForFX = function (pane, doLock) {
var $P = $Ps[pane];
if (doLock) {
$P.css({ zIndex: c.zIndex.animation }); // overlay all elements during animation
if (pane=="south")
$P.css({ top: cDims.top + cDims.innerHeight - $P.outerHeight() });
else if (pane=="east")
$P.css({ left: cDims.left + cDims.innerWidth - $P.outerWidth() });
}
else {
if (!state[pane].isSliding) $P.css({ zIndex: c.zIndex.pane_normal });
if (pane=="south")
$P.css({ top: "auto" });
else if (pane=="east")
$P.css({ left: "auto" });
}
};
/**
* bindStartSlidingEvent
*
* Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
*
* @callers open(), close()
* @param String pane The pane to enable/disable, 'north', 'south', etc.
* @param Boolean enable Enable or Disable sliding?
*/
var bindStartSlidingEvent = function (pane, enable) {
var
o = options[pane]
, $R = $Rs[pane]
, trigger = o.slideTrigger_open
;
if (!$R || !o.slidable) return;
// make sure we have a valid event
if (trigger != "click" && trigger != "dblclick" && trigger != "mouseover") trigger = "click";
$R
// add or remove trigger event
[enable ? "bind" : "unbind"](trigger, slideOpen)
// set the appropriate cursor & title/tip
.css("cursor", (enable ? o.sliderCursor: "default"))
.attr("title", (enable ? o.sliderTip : ""))
;
};
/**
* bindStopSlidingEvents
*
* Add or remove 'mouseout' events to 'slide close' when pane is 'sliding' open or closed
* Also increases zIndex when pane is sliding open
* See bindStartSlidingEvent for code to control 'slide open'
*
* @callers slideOpen(), slideClosed()
* @param String pane The pane to process, 'north', 'south', etc.
* @param Boolean isOpen Is pane open or closed?
*/
var bindStopSlidingEvents = function (pane, enable) {
var
o = options[pane]
, s = state[pane]
, trigger = o.slideTrigger_close
, action = (enable ? "bind" : "unbind") // can't make 'unbind' work! - see disabled code below
, $P = $Ps[pane]
, $R = $Rs[pane]
;
s.isSliding = enable; // logic
clearTimer(pane, "closeSlider"); // just in case
// raise z-index when sliding
$P.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.pane_normal) });
$R.css({ zIndex: (enable ? c.zIndex.sliding : c.zIndex.resizer_normal) });
// make sure we have a valid event
if (trigger != "click" && trigger != "mouseout") trigger = "mouseout";
// when trigger is 'mouseout', must cancel timer when mouse moves between 'pane' and 'resizer'
if (enable) { // BIND trigger events
$P.bind(trigger, slideClosed );
$R.bind(trigger, slideClosed );
if (trigger = "mouseout") {
$P.bind("mouseover", cancelMouseOut );
$R.bind("mouseover", cancelMouseOut );
}
}
else { // UNBIND trigger events
// TODO: why does unbind of a 'single function' not work reliably?
//$P[action](trigger, slideClosed );
$P.unbind(trigger);
$R.unbind(trigger);
if (trigger = "mouseout") {
//$P[action]("mouseover", cancelMouseOut );
$P.unbind("mouseover");
$R.unbind("mouseover");
clearTimer(pane, "closeSlider");
}
}
// SUBROUTINE for mouseout timer clearing
function cancelMouseOut (evt) {
clearTimer(pane, "closeSlider");
evt.stopPropagation();
}
};
var slideOpen = function () {
var pane = $(this).attr("resizer"); // attr added by initHandles
if (state[pane].isClosed) { // skip if already open!
bindStopSlidingEvents(pane, true); // pane is opening, so BIND trigger events to close it
open(pane, true); // true = slide - ie, called from here!
}
};
var slideClosed = function () {
var
$E = $(this)
, pane = $E.attr("pane") || $E.attr("resizer")
, o = options[pane]
, s = state[pane]
;
if (s.isClosed || s.isResizing)
return; // skip if already closed OR in process of resizing
else if (o.slideTrigger_close == "click")
close_NOW(); // close immediately onClick
else // trigger = mouseout - use a delay
setTimer(pane, "closeSlider", close_NOW, 300); // .3 sec delay
// SUBROUTINE for timed close
function close_NOW () {
bindStopSlidingEvents(pane, false); // pane is being closed, so UNBIND trigger events
if (!s.isClosed) close(pane); // skip if already closed!
}
};
/**
* sizePane
*
* @callers initResizable.stop()
* @param String pane The pane being resized - usually west or east, but potentially north or south
* @param Integer newSize The new size for this pane - will be validated
*/
var sizePane = function (pane, size) {
// TODO: accept "auto" as size, and size-to-fit pane content
var
edge = c[pane].edge
, dir = c[pane].dir
, o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
;
// calculate 'current' min/max sizes
setPaneMinMaxSizes(pane); // update pane-state
// compare/update calculated min/max to user-options
s.minSize = max(s.minSize, o.minSize);
if (o.maxSize > 0) s.maxSize = min(s.maxSize, o.maxSize);
// validate passed size
size = max(size, s.minSize);
size = min(size, s.maxSize);
s.size = size; // update state
// move the resizer bar and resize the pane
$R.css( edge, size + cDims[edge] );
$P.css( c[pane].sizeType, max(1, cssSize(pane, size)) );
// resize all the adjacent panes, and adjust their toggler buttons
if (!s.isSliding) sizeMidPanes(dir=="horz" ? "all" : "center");
sizeHandles();
sizeContent(pane);
execUserCallback(pane, o.onresize_end || o.onresize);
};
/**
* sizeMidPanes
*
* @callers create(), open(), close(), onWindowResize()
*/
var sizeMidPanes = function (panes, overrideDims, onInit) {
if (!panes || panes == "all") panes = "east,west,center";
var d = getPaneDims();
if (overrideDims) $.extend( d, overrideDims );
$.each(panes.split(","), function() {
if (!$Ps[this]) return; // NO PANE - skip
var
pane = str(this)
, o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
, hasRoom = true
, CSS = {}
;
if (pane == "center") {
d = getPaneDims(); // REFRESH Dims because may have just 'unhidden' East or West pane after a 'resize'
CSS = $.extend( {}, d ); // COPY ALL of the paneDims
CSS.width = max(1, cssW(pane, CSS.width));
CSS.height = max(1, cssH(pane, CSS.height));
hasRoom = (CSS.width > 1 && CSS.height > 1);
/*
* Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
* Normally these panes have only 'left' & 'right' positions so pane auto-sizes
*/
if ($.browser.msie && (!$.boxModel || $.browser.version < 7)) {
if ($Ps.north) $Ps.north.css({ width: cssW($Ps.north, cDims.innerWidth) });
if ($Ps.south) $Ps.south.css({ width: cssW($Ps.south, cDims.innerWidth) });
}
}
else { // for east and west, set only the height
CSS.top = d.top;
CSS.bottom = d.bottom;
CSS.height = max(1, cssH(pane, d.height));
hasRoom = (CSS.height > 1);
}
if (hasRoom) {
$P.css(CSS);
if (s.noRoom) {
s.noRoom = false;
if (s.isHidden) return;
else show(pane, !s.isClosed);
/* OLD CODE - keep until sure line above works right!
if (!s.isClosed) $P.show(); // in case was previously hidden due to NOT hasRoom
if ($R) $R.show();
*/
}
if (!onInit) {
sizeContent(pane);
execUserCallback(pane, o.onresize_end || o.onresize);
}
}
else if (!s.noRoom) { // no room for pane, so just hide it (if not already)
s.noRoom = true; // update state
if (s.isHidden) return;
if (onInit) { // skip onhide callback and other logic onLoad
$P.hide();
if ($R) $R.hide();
}
else hide(pane);
}
});
};
var sizeContent = function (panes) {
if (!panes || panes == "all") panes = c.allPanes;
$.each(panes.split(","), function() {
if (!$Cs[this]) return; // NO CONTENT - skip
var
pane = str(this)
, ignore = options[pane].contentIgnoreSelector
, $P = $Ps[pane]
, $C = $Cs[pane]
, e_C = $C[0] // DOM element
, height = cssH($P); // init to pane.innerHeight
;
$P.children().each(function() {
if (this == e_C) return; // Content elem - skip
var $E = $(this);
if (!ignore || !$E.is(ignore))
height -= $E.outerHeight();
});
if (height > 0)
height = cssH($C, height);
if (height < 1)
$C.hide(); // no room for content!
else
$C.css({ height: height }).show();
});
};
/**
* sizeHandles
*
* Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
*
* @callers initHandles(), open(), close(), resizeAll()
*/
var sizeHandles = function (panes, onInit) {
if (!panes || panes == "all") panes = c.borderPanes;
$.each(panes.split(","), function() {
var
pane = str(this)
, o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
;
if (!$P || !$R || (!o.resizable && !o.closable)) return; // skip
var
dir = c[pane].dir
, _state = (s.isClosed ? "_closed" : "_open")
, spacing = o["spacing"+ _state]
, togAlign = o["togglerAlign"+ _state]
, togLen = o["togglerLength"+ _state]
, paneLen
, offset
, CSS = {}
;
if (spacing == 0) {
$R.hide();
return;
}
else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
$R.show(); // in case was previously hidden
// Resizer Bar is ALWAYS same width/height of pane it is attached to
if (dir == "horz") { // north/south
paneLen = $P.outerWidth();
$R.css({
width: max(1, cssW($R, paneLen)) // account for borders & padding
, height: max(1, cssH($R, spacing)) // ditto
, left: cssNum($P, "left")
});
}
else { // east/west
paneLen = $P.outerHeight();
$R.css({
height: max(1, cssH($R, paneLen)) // account for borders & padding
, width: max(1, cssW($R, spacing)) // ditto
, top: cDims.top + getPaneSize("north", true)
//, top: cssNum($Ps["center"], "top")
});
}
if ($T) {
if (togLen == 0 || (s.isSliding && o.hideTogglerOnSlide)) {
$T.hide(); // always HIDE the toggler when 'sliding'
return;
}
else
$T.show(); // in case was previously hidden
if (!(togLen > 0) || togLen == "100%" || togLen > paneLen) {
togLen = paneLen;
offset = 0;
}
else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
if (typeof togAlign == "string") {
switch (togAlign) {
case "top":
case "left": offset = 0;
break;
case "bottom":
case "right": offset = paneLen - togLen;
break;
case "middle":
case "center":
default: offset = Math.floor((paneLen - togLen) / 2); // 'default' catches typos
}
}
else { // togAlign = number
var x = parseInt(togAlign); //
if (togAlign >= 0) offset = x;
else offset = paneLen - togLen + x; // NOTE: x is negative!
}
}
var
$TC_o = (o.togglerContent_open ? $T.children(".content-open") : false)
, $TC_c = (o.togglerContent_closed ? $T.children(".content-closed") : false)
, $TC = (s.isClosed ? $TC_c : $TC_o)
;
if ($TC_o) $TC_o.css("display", s.isClosed ? "none" : "block");
if ($TC_c) $TC_c.css("display", s.isClosed ? "block" : "none");
if (dir == "horz") { // north/south
var width = cssW($T, togLen);
$T.css({
width: max(0, width) // account for borders & padding
, height: max(1, cssH($T, spacing)) // ditto
, left: offset // TODO: VERIFY that toggler positions correctly for ALL values
});
if ($TC) // CENTER the toggler content SPAN
$TC.css("marginLeft", Math.floor((width-$TC.outerWidth())/2)); // could be negative
}
else { // east/west
var height = cssH($T, togLen);
$T.css({
height: max(0, height) // account for borders & padding
, width: max(1, cssW($T, spacing)) // ditto
, top: offset // POSITION the toggler
});
if ($TC) // CENTER the toggler content SPAN
$TC.css("marginTop", Math.floor((height-$TC.outerHeight())/2)); // could be negative
}
}
// DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
if (onInit && o.initHidden) {
$R.hide();
if ($T) $T.hide();
}
});
};
/**
* resizeAll
*
* @callers window.onresize(), callbacks or custom code
*/
var resizeAll = function () {
var
oldW = cDims.innerWidth
, oldH = cDims.innerHeight
;
cDims = state.container = getElemDims($Container); // UPDATE container dimensions
var
checkH = (cDims.innerHeight < oldH)
, checkW = (cDims.innerWidth < oldW)
, s, dir
;
if (checkH || checkW)
// NOTE special order for sizing: S-N-E-W
$.each(["south","north","east","west"], function(i,pane) {
s = state[pane];
dir = c[pane].dir;
if (!s.isClosed && ((checkH && dir=="horz") || (checkW && dir=="vert"))) {
setPaneMinMaxSizes(pane); // update pane-state
// shrink pane if 'too big' to fit
if (s.size > s.maxSize)
sizePane(pane, s.maxSize);
}
});
sizeMidPanes("all");
sizeHandles("all"); // reposition the toggler elements
};
/**
* keyDown
*
* Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
*
* @callers document.keydown()
*/
function keyDown (evt) {
if (!evt) return true;
var code = evt.keyCode;
if (code < 33) return true; // ignore special keys: ENTER, TAB, etc
var
PANE = {
38: "north" // Up Cursor
, 40: "south" // Down Cursor
, 37: "west" // Left Cursor
, 39: "east" // Right Cursor
}
, isCursorKey = (code >= 37 && code <= 40)
, ALT = evt.altKey // no worky!
, SHIFT = evt.shiftKey
, CTRL = evt.ctrlKey
, pane = false
, s, o, k, m, el
;
if (!CTRL && !SHIFT)
return true; // no modifier key - abort
else if (isCursorKey && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
pane = PANE[code];
else // check to see if this matches a custom-hotkey
$.each(c.borderPanes.split(","), function(i,p) { // loop each pane to check its hotkey
o = options[p];
k = o.customHotkey;
m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
if (k && code == (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
pane = p;
return false; // BREAK
}
}
});
if (!pane) return true; // no hotkey - abort
// validate pane
o = options[pane]; // get pane options
s = state[pane]; // get pane options
if (!o.enableCursorHotkey || s.isHidden || !$Ps[pane]) return true;
// see if user is in a 'form field' because may be 'selecting text'!
el = evt.target || evt.srcElement;
if (el && SHIFT && isCursorKey && (el.tagName=="TEXTAREA" || (el.tagName=="INPUT" && (code==37 || code==39))))
return true; // allow text-selection
// SYNTAX NOTES
// use "returnValue=false" to abort keystroke but NOT abort function - can run another command afterwards
// use "return false" to abort keystroke AND abort function
toggle(pane);
evt.stopPropagation();
evt.returnValue = false; // CANCEL key
return false;
};
/*
* ###########################
* UTILITY METHODS
* called externally only
* ###########################
*/
function allowOverflow (elem) {
if (this && this.tagName) elem = this; // BOUND to element
var $P;
if (typeof elem=="string")
$P = $Ps[elem];
else {
if ($(elem).attr("pane")) $P = $(elem);
else $P = $(elem).parents("div[pane]:first");
}
if (!$P.length) return; // INVALID
var
pane = $P.attr("pane")
, s = state[pane]
;
// if pane is already raised, then reset it before doing it again!
// this would happen if allowOverflow is attached to BOTH the pane and an element
if (s.cssSaved)
resetOverflow(pane); // reset previous CSS before continuing
// if pane is raised by sliding or resizing, or it's closed, then abort
if (s.isSliding || s.isResizing || s.isClosed) {
s.cssSaved = false;
return;
}
var
newCSS = { zIndex: (c.zIndex.pane_normal + 1) }
, curCSS = {}
, of = $P.css("overflow")
, ofX = $P.css("overflowX")
, ofY = $P.css("overflowY")
;
// determine which, if any, overflow settings need to be changed
if (of != "visible") {
curCSS.overflow = of;
newCSS.overflow = "visible";
}
if (ofX && ofX != "visible" && ofX != "auto") {
curCSS.overflowX = ofX;
newCSS.overflowX = "visible";
}
if (ofY && ofY != "visible" && ofY != "auto") {
curCSS.overflowY = ofX;
newCSS.overflowY = "visible";
}
// save the current overflow settings - even if blank!
s.cssSaved = curCSS;
// apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
$P.css( newCSS );
// make sure the zIndex of all other panes is normal
$.each(c.allPanes.split(","), function(i, p) {
if (p != pane) resetOverflow(p);
});
};
function resetOverflow (elem) {
if (this && this.tagName) elem = this; // BOUND to element
var $P;
if (typeof elem=="string")
$P = $Ps[elem];
else {
if ($(elem).hasClass("ui-layout-pane")) $P = $(elem);
else $P = $(elem).parents("div[pane]:first");
}
if (!$P.length) return; // INVALID
var
pane = $P.attr("pane")
, s = state[pane]
, CSS = s.cssSaved || {}
;
// reset the zIndex
if (!s.isSliding && !s.isResizing)
$P.css("zIndex", c.zIndex.pane_normal);
// reset Overflow - if necessary
$P.css( CSS );
// clear var
s.cssSaved = false;
};
/**
* getBtn
*
* Helper function to validate params received by addButton utilities
*
* @param String selector jQuery selector for button, eg: ".ui-layout-north .toggle-button"
* @param String pane Name of the pane the button is for: 'north', 'south', etc.
* @returns If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise 'false'
*/
function getBtn(selector, pane, action) {
var
$E = $(selector)
, err = "Error Adding Button \n\nInvalid "
;
if (!$E.length) // element not found
alert(err+"selector: "+ selector);
else if (c.borderPanes.indexOf(pane) == -1) // invalid 'pane' sepecified
alert(err+"pane: "+ pane);
else { // VALID
var btn = options[pane].buttonClass +"-"+ action;
$E.addClass( btn +" "+ btn +"-"+ pane );
return $E;
}
return false; // INVALID
};
/**
* addToggleBtn
*
* Add a custom Toggler button for a pane
*
* @param String selector jQuery selector for button, eg: ".ui-layout-north .toggle-button"
* @param String pane Name of the pane the button is for: 'north', 'south', etc.
*/
function addToggleBtn (selector, pane) {
var $E = getBtn(selector, pane, "toggle");
if ($E)
$E
.attr("title", state[pane].isClosed ? "Open" : "Close")
.click(function (evt) {
toggle(pane);
evt.stopPropagation();
})
;
};
/**
* addOpenBtn
*
* Add a custom Open button for a pane
*
* @param String selector jQuery selector for button, eg: ".ui-layout-north .open-button"
* @param String pane Name of the pane the button is for: 'north', 'south', etc.
*/
function addOpenBtn (selector, pane) {
var $E = getBtn(selector, pane, "open");
if ($E)
$E
.attr("title", "Open")
.click(function (evt) {
open(pane);
evt.stopPropagation();
})
;
};
/**
* addCloseBtn
*
* Add a custom Close button for a pane
*
* @param String selector jQuery selector for button, eg: ".ui-layout-north .close-button"
* @param String pane Name of the pane the button is for: 'north', 'south', etc.
*/
function addCloseBtn (selector, pane) {
var $E = getBtn(selector, pane, "close");
if ($E)
$E
.attr("title", "Close")
.click(function (evt) {
close(pane);
evt.stopPropagation();
})
;
};
/**
* addPinBtn
*
* Add a custom Pin button for a pane
*
* Four classes are added to the element, based on the paneClass for the associated pane...
* Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
* - ui-layout-pane-pin
* - ui-layout-pane-west-pin
* - ui-layout-pane-pin-up
* - ui-layout-pane-west-pin-up
*
* @param String selector jQuery selector for button, eg: ".ui-layout-north .ui-layout-pin"
* @param String pane Name of the pane the pin is for: 'north', 'south', etc.
*/
function addPinBtn (selector, pane) {
var $E = getBtn(selector, pane, "pin");
if ($E) {
var s = state[pane];
$E.click(function (evt) {
setPinState($(this), pane, (s.isSliding || s.isClosed));
if (s.isSliding || s.isClosed) open( pane ); // change from sliding to open
else close( pane ); // slide-closed
evt.stopPropagation();
});
// add up/down pin attributes and classes
setPinState ($E, pane, (!s.isClosed && !s.isSliding));
// add this pin to the pane data so we can 'sync it' automatically
// PANE.pins key is an array so we can store multiple pins for each pane
c[pane].pins.push( selector ); // just save the selector string
}
};
/**
* syncPinBtns
*
* INTERNAL function to sync 'pin buttons' when pane is opened or closed
* Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
*
* @callers open(), close()
* @params pane These are the params returned to callbacks by layout()
* @params doPin True means set the pin 'down', False means 'up'
*/
function syncPinBtns (pane, doPin) {
$.each(c[pane].pins, function (i, selector) {
setPinState($(selector), pane, doPin);
});
};
/**
* setPinState
*
* Change the class of the pin button to make it look 'up' or 'down'
*
* @callers addPinBtn(), syncPinBtns()
* @param Element $Pin The pin-span element in a jQuery wrapper
* @param Boolean doPin True = set the pin 'down', False = set it 'up'
* @param String pinClass The root classname for pins - will add '-up' or '-down' suffix
*/
function setPinState ($Pin, pane, doPin) {
var updown = $Pin.attr("pin");
if (updown && doPin == (updown=="down")) return; // already in correct state
var
root = options[pane].buttonClass
, class1 = root +"-pin"
, class2 = class1 +"-"+ pane
, UP1 = class1 + "-up"
, UP2 = class2 + "-up"
, DN1 = class1 + "-down"
, DN2 = class2 + "-down"
;
$Pin
.attr("pin", doPin ? "down" : "up") // logic
.attr("title", doPin ? "Un-Pin" : "Pin")
.removeClass( doPin ? UP1 : DN1 )
.removeClass( doPin ? UP2 : DN2 )
.addClass( doPin ? DN1 : UP1 )
.addClass( doPin ? DN2 : UP2 )
;
};
/*
* ###########################
* CREATE/RETURN BORDER-LAYOUT
* ###########################
*/
// init global vars
var
$Container = $(this).css({ overflow: "hidden" }) // Container elem
, $Ps = {} // Panes x4 - set in initPanes()
, $Cs = {} // Content x4 - set in initPanes()
, $Rs = {} // Resizers x4 - set in initHandles()
, $Ts = {} // Togglers x4 - set in initHandles()
// object aliases
, c = config // alias for config hash
, cDims = state.container // alias for easy access to 'container dimensions'
;
// create the border layout NOW
create();
// return object pointers to expose data & option Properties, and primary action Methods
return {
options: options // property - options hash
, state: state // property - dimensions hash
, panes: $Ps // property - object pointers for ALL panes: panes.north, panes.center
, toggle: toggle // method - pass a 'pane' ("north", "west", etc)
, open: open // method - ditto
, close: close // method - ditto
, hide: hide // method - ditto
, show: show // method - ditto
, resizeContent: sizeContent // method - ditto
, sizePane: sizePane // method - pass a 'pane' AND a 'size' in pixels
, resizeAll: resizeAll // method - no parameters
, addToggleBtn: addToggleBtn // utility - pass element selector and 'pane'
, addOpenBtn: addOpenBtn // utility - ditto
, addCloseBtn: addCloseBtn // utility - ditto
, addPinBtn: addPinBtn // utility - ditto
, allowOverflow: allowOverflow // utility - pass calling element
, resetOverflow: resetOverflow // utility - ditto
, cssWidth: cssW
, cssHeight: cssH
};
}
})( jQuery );

80
thirdparty/jquery-layout/jquery.layout.min.js vendored Executable file
View File

@ -0,0 +1,80 @@
/*
* jquery.layout 1.2.0
*
* Copyright (c) 2008
* Fabrizio Balliano (http://www.fabrizioballiano.net)
* Kevin Dalman (http://allpro.net)
*
* Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
* and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
*
* $Date: 2008-12-27 02:17:22 +0100 (sab, 27 dic 2008) $
* $Rev: 203 $
*
* NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars
*/
(function($){$.fn.layout=function(opts){var
prefix="ui-layout-",defaults={paneClass:prefix+"pane",resizerClass:prefix+"resizer",togglerClass:prefix+"toggler",togglerInnerClass:prefix+"",buttonClass:prefix+"button",contentSelector:"."+prefix+"content",contentIgnoreSelector:"."+prefix+"ignore"};var options={name:"",scrollToBookmarkOnLoad:true,defaults:{applyDefaultStyles:false,closable:true,resizable:true,slidable:true,contentSelector:defaults.contentSelector,contentIgnoreSelector:defaults.contentIgnoreSelector,paneClass:defaults.paneClass,resizerClass:defaults.resizerClass,togglerClass:defaults.togglerClass,buttonClass:defaults.buttonClass,resizerDragOpacity:1,maskIframesOnResize:true,minSize:0,maxSize:0,spacing_open:6,spacing_closed:6,togglerLength_open:50,togglerLength_closed:50,togglerAlign_open:"center",togglerAlign_closed:"center",togglerTip_open:"Close",togglerTip_closed:"Open",resizerTip:"Resize",sliderTip:"Slide Open",sliderCursor:"pointer",slideTrigger_open:"click",slideTrigger_close:"mouseout",hideTogglerOnSlide:false,togglerContent_open:"",togglerContent_closed:"",showOverflowOnHover:false,enableCursorHotkey:true,customHotkeyModifier:"SHIFT",fxName:"slide",fxSpeed:null,fxSettings:{},initClosed:false,initHidden:false},north:{paneSelector:"."+prefix+"north",size:"auto",resizerCursor:"n-resize"},south:{paneSelector:"."+prefix+"south",size:"auto",resizerCursor:"s-resize"},east:{paneSelector:"."+prefix+"east",size:200,resizerCursor:"e-resize"},west:{paneSelector:"."+prefix+"west",size:200,resizerCursor:"w-resize"},center:{paneSelector:"."+prefix+"center"}};var effects={slide:{all:{duration:"fast"},north:{direction:"up"},south:{direction:"down"},east:{direction:"right"},west:{direction:"left"}},drop:{all:{duration:"slow"},north:{direction:"up"},south:{direction:"down"},east:{direction:"right"},west:{direction:"left"}},scale:{all:{duration:"fast"}}};var config={allPanes:"north,south,east,west,center",borderPanes:"north,south,east,west",zIndex:{resizer_normal:1,pane_normal:2,mask:4,sliding:100,resizing:10000,animation:10000},resizers:{cssReq:{position:"absolute",padding:0,margin:0,fontSize:"1px",textAlign:"left",overflow:"hidden",zIndex:1},cssDef:{background:"#DDD",border:"none"}},togglers:{cssReq:{position:"absolute",display:"block",padding:0,margin:0,overflow:"hidden",textAlign:"center",fontSize:"1px",cursor:"pointer",zIndex:1},cssDef:{background:"#AAA"}},content:{cssReq:{overflow:"auto"},cssDef:{}},defaults:{cssReq:{position:"absolute",margin:0,zIndex:2},cssDef:{padding:"10px",background:"#FFF",border:"1px solid #BBB",overflow:"auto"}},north:{edge:"top",sizeType:"height",dir:"horz",cssReq:{top:0,bottom:"auto",left:0,right:0,width:"auto"}},south:{edge:"bottom",sizeType:"height",dir:"horz",cssReq:{top:"auto",bottom:0,left:0,right:0,width:"auto"}},east:{edge:"right",sizeType:"width",dir:"vert",cssReq:{left:"auto",right:0,top:"auto",bottom:"auto",height:"auto"}},west:{edge:"left",sizeType:"width",dir:"vert",cssReq:{left:0,right:"auto",top:"auto",bottom:"auto",height:"auto"}},center:{dir:"center",cssReq:{left:"auto",right:"auto",top:"auto",bottom:"auto",height:"auto",width:"auto"}}};var state={id:Math.floor(Math.random()*10000),container:{},north:{},south:{},east:{},west:{},center:{}};var
altEdge={top:"bottom",bottom:"top",left:"right",right:"left"},altSide={north:"south",south:"north",east:"west",west:"east"};var isStr=function(o){if(typeof o=="string")return true;else if(typeof o=="object"){try{var match=o.constructor.toString().match(/string/i);return(match!==null);}catch(e){}}return false;};var str=function(o){if(typeof o=="string"||isStr(o))return $.trim(o);else return o;};var min=function(x,y){return Math.min(x,y);};var max=function(x,y){return Math.max(x,y);};var transformData=function(d){var json={defaults:{fxSettings:{}},north:{fxSettings:{}},south:{fxSettings:{}},east:{fxSettings:{}},west:{fxSettings:{}},center:{fxSettings:{}}};d=d||{};if(d.effects||d.defaults||d.north||d.south||d.west||d.east||d.center)json=$.extend(json,d);else
$.each(d,function(key,val){a=key.split("__");json[a[1]?a[0]:"defaults"][a[1]?a[1]:a[0]]=val;});return json;};var setFlowCallback=function(action,pane,param){var
cb=action+","+pane+","+(param?1:0),cP,cbPane;$.each(c.borderPanes.split(","),function(i,p){if(c[p].isMoving){bindCallback(p);return false;}});function bindCallback(p,test){cP=c[p];if(!cP.doCallback){cP.doCallback=true;cP.callback=cb;}else{cpPane=cP.callback.split(",")[1];if(cpPane!=p&&cpPane!=pane)bindCallback(cpPane,true);}}};var execFlowCallback=function(pane){var cP=c[pane];c.isLayoutBusy=false;delete cP.isMoving;if(!cP.doCallback||!cP.callback)return;cP.doCallback=false;var
cb=cP.callback.split(","),param=(cb[2]>0?true:false);if(cb[0]=="open")open(cb[1],param);else if(cb[0]=="close")close(cb[1],param);if(!cP.doCallback)cP.callback=null;};var execUserCallback=function(pane,v_fn){if(!v_fn)return;var fn;try{if(typeof v_fn=="function")fn=v_fn;else if(typeof v_fn!="string")return;else if(v_fn.indexOf(",")>0){var
args=v_fn.split(","),fn=eval(args[0]);if(typeof fn=="function"&&args.length>1)return fn(args[1]);}else
fn=eval(v_fn);if(typeof fn=="function")return fn(pane,$Ps[pane],$.extend({},state[pane]),$.extend({},options[pane]),options.name);}catch(ex){}};var cssNum=function($E,prop){var
val=0,hidden=false,visibility="";if(!$.browser.msie){if($.curCSS($E[0],"display",true)=="none"){hidden=true;visibility=$.curCSS($E[0],"visibility",true);$E.css({display:"block",visibility:"hidden"});}}val=parseInt($.curCSS($E[0],prop,true),10)||0;if(hidden){$E.css({display:"none"});if(visibility&&visibility!="hidden")$E.css({visibility:visibility});}return val;};var cssW=function(e,outerWidth){var $E;if(isStr(e)){e=str(e);$E=$Ps[e];}else
$E=$(e);if(outerWidth<=0)return 0;else if(!(outerWidth>0))outerWidth=isStr(e)?getPaneSize(e):$E.outerWidth();if(!$.boxModel)return outerWidth;else
return outerWidth
-cssNum($E,"paddingLeft")-cssNum($E,"paddingRight")-($.curCSS($E[0],"borderLeftStyle",true)=="none"?0:cssNum($E,"borderLeftWidth"))-($.curCSS($E[0],"borderRightStyle",true)=="none"?0:cssNum($E,"borderRightWidth"));};var cssH=function(e,outerHeight){var $E;if(isStr(e)){e=str(e);$E=$Ps[e];}else
$E=$(e);if(outerHeight<=0)return 0;else if(!(outerHeight>0))outerHeight=(isStr(e))?getPaneSize(e):$E.outerHeight();if(!$.boxModel)return outerHeight;else
return outerHeight
-cssNum($E,"paddingTop")-cssNum($E,"paddingBottom")-($.curCSS($E[0],"borderTopStyle",true)=="none"?0:cssNum($E,"borderTopWidth"))-($.curCSS($E[0],"borderBottomStyle",true)=="none"?0:cssNum($E,"borderBottomWidth"));};var cssSize=function(pane,outerSize){if(c[pane].dir=="horz")return cssH(pane,outerSize);else
return cssW(pane,outerSize);};var getPaneSize=function(pane,inclSpace){var
$P=$Ps[pane],o=options[pane],s=state[pane],oSp=(inclSpace?o.spacing_open:0),cSp=(inclSpace?o.spacing_closed:0);if(!$P||s.isHidden)return 0;else if(s.isClosed||(s.isSliding&&inclSpace))return cSp;else if(c[pane].dir=="horz")return $P.outerHeight()+oSp;else
return $P.outerWidth()+oSp;};var setPaneMinMaxSizes=function(pane){var
d=cDims,edge=c[pane].edge,dir=c[pane].dir,o=options[pane],s=state[pane],$P=$Ps[pane],$altPane=$Ps[altSide[pane]],paneSpacing=o.spacing_open,altPaneSpacing=options[altSide[pane]].spacing_open,altPaneSize=(!$altPane?0:(dir=="horz"?$altPane.outerHeight():$altPane.outerWidth())),containerSize=(dir=="horz"?d.innerHeight:d.innerWidth),limitSize=containerSize-paneSpacing-altPaneSize-altPaneSpacing,minSize=s.minSize||0,maxSize=Math.min(s.maxSize||9999,limitSize),minPos,maxPos;switch(pane){case"north":minPos=d.offsetTop+minSize;maxPos=d.offsetTop+maxSize;break;case"west":minPos=d.offsetLeft+minSize;maxPos=d.offsetLeft+maxSize;break;case"south":minPos=d.offsetTop+d.innerHeight-maxSize;maxPos=d.offsetTop+d.innerHeight-minSize;break;case"east":minPos=d.offsetLeft+d.innerWidth-maxSize;maxPos=d.offsetLeft+d.innerWidth-minSize;break;}$.extend(s,{minSize:minSize,maxSize:maxSize,minPosition:minPos,maxPosition:maxPos});};var getPaneDims=function(){var d={top:getPaneSize("north",true),bottom:getPaneSize("south",true),left:getPaneSize("west",true),right:getPaneSize("east",true),width:0,height:0};with(d){width=cDims.innerWidth-left-right;height=cDims.innerHeight-bottom-top;top+=cDims.top;bottom+=cDims.bottom;left+=cDims.left;right+=cDims.right;}return d;};var getElemDims=function($E){var
d={},e,b,p;$.each("Left,Right,Top,Bottom".split(","),function(){e=str(this);b=d["border"+e]=cssNum($E,"border"+e+"Width");p=d["padding"+e]=cssNum($E,"padding"+e);d["offset"+e]=b+p;if($E==$Container)d[e.toLowerCase()]=($.boxModel?p:0);});d.innerWidth=d.outerWidth=$E.outerWidth();d.innerHeight=d.outerHeight=$E.outerHeight();if($.boxModel){d.innerWidth-=(d.offsetLeft+d.offsetRight);d.innerHeight-=(d.offsetTop+d.offsetBottom);}return d;};var setTimer=function(pane,action,fn,ms){var
Layout=window.layout=window.layout||{},Timers=Layout.timers=Layout.timers||{},name="layout_"+state.id+"_"+pane+"_"+action;if(Timers[name])return;else Timers[name]=setTimeout(fn,ms);};var clearTimer=function(pane,action){var
Layout=window.layout=window.layout||{},Timers=Layout.timers=Layout.timers||{},name="layout_"+state.id+"_"+pane+"_"+action;if(Timers[name]){clearTimeout(Timers[name]);delete Timers[name];return true;}else
return false;};var create=function(){initOptions();initContainer();initPanes();initHandles();initResizable();sizeContent("all");if(options.scrollToBookmarkOnLoad)with(self.location)if(hash)replace(hash);initHotkeys();$(window).resize(function(){var timerID="timerLayout_"+state.id;if(window[timerID])clearTimeout(window[timerID]);window[timerID]=null;if(true||$.browser.msie)window[timerID]=setTimeout(resizeAll,100);else
resizeAll();});};var initContainer=function(){try{if($Container[0].tagName=="BODY"){$("html").css({height:"100%",overflow:"hidden"});$("body").css({position:"relative",height:"100%",overflow:"hidden",margin:0,padding:0,border:"none"});}else{var
CSS={overflow:"hidden"},p=$Container.css("position"),h=$Container.css("height");if(!$Container.hasClass("ui-layout-pane")){if(!p||"fixed,absolute,relative".indexOf(p)<0)CSS.position="relative";if(!h||h=="auto")CSS.height="100%";}$Container.css(CSS);}}catch(ex){}cDims=state.container=getElemDims($Container);};var initHotkeys=function(){$.each(c.borderPanes.split(","),function(i,pane){var o=options[pane];if(o.enableCursorHotkey||o.customHotkey){$(document).keydown(keyDown);return false;}});};var initOptions=function(){opts=transformData(opts);if(opts.effects){$.extend(effects,opts.effects);delete opts.effects;}$.each("name,scrollToBookmarkOnLoad".split(","),function(idx,key){if(opts[key]!==undefined)options[key]=opts[key];else if(opts.defaults[key]!==undefined){options[key]=opts.defaults[key];delete opts.defaults[key];}});$.each("paneSelector,resizerCursor,customHotkey".split(","),function(idx,key){delete opts.defaults[key];});$.extend(options.defaults,opts.defaults);c.center=$.extend(true,{},c.defaults,c.center);$.extend(options.center,opts.center);var o_Center=$.extend(true,{},options.defaults,opts.defaults,options.center);$.each("paneClass,contentSelector,contentIgnoreSelector,applyDefaultStyles,showOverflowOnHover".split(","),function(idx,key){options.center[key]=o_Center[key];});var defs=options.defaults;$.each(c.borderPanes.split(","),function(i,pane){c[pane]=$.extend(true,{},c.defaults,c[pane]);o=options[pane]=$.extend(true,{},options.defaults,options[pane],opts.defaults,opts[pane]);if(!o.paneClass)o.paneClass=defaults.paneClass;if(!o.resizerClass)o.resizerClass=defaults.resizerClass;if(!o.togglerClass)o.togglerClass=defaults.togglerClass;$.each(["_open","_close",""],function(i,n){var
sName="fxName"+n,sSpeed="fxSpeed"+n,sSettings="fxSettings"+n;o[sName]=opts[pane][sName]||opts[pane].fxName||opts.defaults[sName]||opts.defaults.fxName||o[sName]||o.fxName||defs[sName]||defs.fxName||"none";var fxName=o[sName];if(fxName=="none"||!$.effects||!$.effects[fxName]||(!effects[fxName]&&!o[sSettings]&&!o.fxSettings))fxName=o[sName]="none";var
fx=effects[fxName]||{},fx_all=fx.all||{},fx_pane=fx[pane]||{};o[sSettings]=$.extend({},fx_all,fx_pane,defs.fxSettings||{},defs[sSettings]||{},o.fxSettings,o[sSettings],opts.defaults.fxSettings,opts.defaults[sSettings]||{},opts[pane].fxSettings,opts[pane][sSettings]||{});o[sSpeed]=opts[pane][sSpeed]||opts[pane].fxSpeed||opts.defaults[sSpeed]||opts.defaults.fxSpeed||o[sSpeed]||o[sSettings].duration||o.fxSpeed||o.fxSettings.duration||defs.fxSpeed||defs.fxSettings.duration||fx_pane.duration||fx_all.duration||"normal";});});};var initPanes=function(){$.each(c.allPanes.split(","),function(){var
pane=str(this),o=options[pane],s=state[pane],fx=s.fx,dir=c[pane].dir,size=o.size=="auto"||isNaN(o.size)?0:o.size,minSize=o.minSize||1,maxSize=o.maxSize||9999,spacing=o.spacing_open||0,sel=o.paneSelector,isIE6=($.browser.msie&&$.browser.version<7),CSS={},$P,$C;$Cs[pane]=false;if(sel.substr(0,1)==="#")$P=$Ps[pane]=$Container.find(sel+":first");else{$P=$Ps[pane]=$Container.children(sel+":first");if(!$P.length)$P=$Ps[pane]=$Container.children("form:first").children(sel+":first");}if(!$P.length){$Ps[pane]=false;return true;}$P.attr("pane",pane).addClass(o.paneClass+" "+o.paneClass+"-"+pane);if(pane!="center"){s.isClosed=false;s.isSliding=false;s.isResizing=false;s.isHidden=false;s.noRoom=false;c[pane].pins=[];}CSS=$.extend({visibility:"visible",display:"block"},c.defaults.cssReq,c[pane].cssReq);if(o.applyDefaultStyles)$.extend(CSS,c.defaults.cssDef,c[pane].cssDef);$P.css(CSS);CSS={};switch(pane){case"north":CSS.top=cDims.top;CSS.left=cDims.left;CSS.right=cDims.right;break;case"south":CSS.bottom=cDims.bottom;CSS.left=cDims.left;CSS.right=cDims.right;break;case"west":CSS.left=cDims.left;break;case"east":CSS.right=cDims.right;break;case"center":}if(dir=="horz"){if(size===0||size=="auto"){$P.css({height:"auto"});size=$P.outerHeight();}size=max(size,minSize);size=min(size,maxSize);size=min(size,cDims.innerHeight-spacing);CSS.height=max(1,cssH(pane,size));s.size=size;s.maxSize=maxSize;s.minSize=max(minSize,size-CSS.height+1);$P.css(CSS);}else if(dir=="vert"){if(size===0||size=="auto"){$P.css({width:"auto",float:"left"});size=$P.outerWidth();$P.css({float:"none"});}size=max(size,minSize);size=min(size,maxSize);size=min(size,cDims.innerWidth-spacing);CSS.width=max(1,cssW(pane,size));s.size=size;s.maxSize=maxSize;s.minSize=max(minSize,size-CSS.width+1);$P.css(CSS);sizeMidPanes(pane,null,true);}else if(pane=="center"){$P.css(CSS);sizeMidPanes("center",null,true);}if(o.initClosed&&o.closable){$P.hide().addClass("closed");s.isClosed=true;}else if(o.initHidden||o.initClosed){hide(pane,true);s.isHidden=true;}else
$P.addClass("open");if(o.showOverflowOnHover)$P.hover(allowOverflow,resetOverflow);if(o.contentSelector){$C=$Cs[pane]=$P.children(o.contentSelector+":first");if(!$C.length){$Cs[pane]=false;return true;}$C.css(c.content.cssReq);if(o.applyDefaultStyles)$C.css(c.content.cssDef);$P.css({overflow:"hidden"});}});};var initHandles=function(){$.each(c.borderPanes.split(","),function(){var
pane=str(this),o=options[pane],s=state[pane],rClass=o.resizerClass,tClass=o.togglerClass,$P=$Ps[pane];$Rs[pane]=false;$Ts[pane]=false;if(!$P||(!o.closable&&!o.resizable))return;var
edge=c[pane].edge,isOpen=$P.is(":visible"),spacing=(isOpen?o.spacing_open:o.spacing_closed),_pane="-"+pane,_state=(isOpen?"-open":"-closed"),$R,$T;$R=$Rs[pane]=$("<span></span>");if(isOpen&&o.resizable);else if(!isOpen&&o.slidable)$R.attr("title",o.sliderTip).css("cursor",o.sliderCursor);$R.attr("id",(o.paneSelector.substr(0,1)=="#"?o.paneSelector.substr(1)+"-resizer":"")).attr("resizer",pane).css(c.resizers.cssReq).css(edge,cDims[edge]+getPaneSize(pane)).addClass(rClass+" "+rClass+_pane+" "+rClass+_state+" "+rClass+_pane+_state).appendTo($Container);if(o.applyDefaultStyles)$R.css(c.resizers.cssDef);if(o.closable){$T=$Ts[pane]=$("<div></div>");$T.attr("id",(o.paneSelector.substr(0,1)=="#"?o.paneSelector.substr(1)+"-toggler":"")).css(c.togglers.cssReq).attr("title",(isOpen?o.togglerTip_open:o.togglerTip_closed)).click(function(evt){toggle(pane);evt.stopPropagation();}).mouseover(function(evt){evt.stopPropagation();}).addClass(tClass+" "+tClass+_pane+" "+tClass+_state+" "+tClass+_pane+_state).appendTo($R);if(o.togglerContent_open)$("<span>"+o.togglerContent_open+"</span>").addClass("content content-open").css("display",s.isClosed?"none":"block").appendTo($T);if(o.togglerContent_closed)$("<span>"+o.togglerContent_closed+"</span>").addClass("content content-closed").css("display",s.isClosed?"block":"none").appendTo($T);if(o.applyDefaultStyles)$T.css(c.togglers.cssDef);if(!isOpen)bindStartSlidingEvent(pane,true);}});sizeHandles("all",true);};var initResizable=function(){var
draggingAvailable=(typeof $.fn.draggable=="function"),minPosition,maxPosition,edge;$.each(c.borderPanes.split(","),function(){var
pane=str(this),o=options[pane],s=state[pane];if(!draggingAvailable||!$Ps[pane]||!o.resizable){o.resizable=false;return true;}var
rClass=o.resizerClass,dragClass=rClass+"-drag",dragPaneClass=rClass+"-"+pane+"-drag",draggingClass=rClass+"-dragging",draggingPaneClass=rClass+"-"+pane+"-dragging",draggingClassSet=false,$P=$Ps[pane],$R=$Rs[pane];if(!s.isClosed)$R.attr("title",o.resizerTip).css("cursor",o.resizerCursor);$R.draggable({containment:$Container[0],axis:(c[pane].dir=="horz"?"y":"x"),delay:200,distance:1,helper:"clone",opacity:o.resizerDragOpacity,zIndex:c.zIndex.resizing,start:function(e,ui){if(false===execUserCallback(pane,o.onresize_start))return false;s.isResizing=true;clearTimer(pane,"closeSlider");$R.addClass(dragClass+" "+dragPaneClass);draggingClassSet=false;var resizerWidth=(pane=="east"||pane=="south"?o.spacing_open:0);setPaneMinMaxSizes(pane);s.minPosition-=resizerWidth;s.maxPosition-=resizerWidth;edge=(c[pane].dir=="horz"?"top":"left");$(o.maskIframesOnResize===true?"iframe":o.maskIframesOnResize).each(function(){$('<div class="ui-layout-mask"/>').css({background:"#fff",opacity:"0.001",zIndex:9,position:"absolute",width:this.offsetWidth+"px",height:this.offsetHeight+"px"}).css($(this).offset()).appendTo(this.parentNode);});},drag:function(e,ui){if(!draggingClassSet){$(".ui-draggable-dragging").addClass(draggingClass+" "+draggingPaneClass).children().css("visibility","hidden");draggingClassSet=true;if(s.isSliding)$Ps[pane].css("zIndex",c.zIndex.sliding);}if(ui.position[edge]<s.minPosition)ui.position[edge]=s.minPosition;else if(ui.position[edge]>s.maxPosition)ui.position[edge]=s.maxPosition;},stop:function(e,ui){var
dragPos=ui.position,resizerPos,newSize;$R.removeClass(dragClass+" "+dragPaneClass);switch(pane){case"north":resizerPos=dragPos.top;break;case"west":resizerPos=dragPos.left;break;case"south":resizerPos=cDims.outerHeight-dragPos.top-$R.outerHeight();break;case"east":resizerPos=cDims.outerWidth-dragPos.left-$R.outerWidth();break;}newSize=resizerPos-cDims[c[pane].edge];sizePane(pane,newSize);$("div.ui-layout-mask").remove();s.isResizing=false;}});});};var hide=function(pane,onInit){var
o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane];if(!$P||s.isHidden)return;if(false===execUserCallback(pane,o.onhide_start))return;s.isSliding=false;if($R)$R.hide();if(onInit||s.isClosed){s.isClosed=true;s.isHidden=true;$P.hide();sizeMidPanes(c[pane].dir=="horz"?"all":"center");execUserCallback(pane,o.onhide_end||o.onhide);}else{s.isHiding=true;close(pane,false);}};var show=function(pane,openPane){var
o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane];if(!$P||!s.isHidden)return;if(false===execUserCallback(pane,o.onshow_start))return;s.isSliding=false;s.isShowing=true;if($R&&o.spacing_open>0)$R.show();if(openPane===false)close(pane,true);else
open(pane);};var toggle=function(pane){var s=state[pane];if(s.isHidden)show(pane);else if(s.isClosed)open(pane);else
close(pane);};var close=function(pane,force,noAnimation){var
$P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],o=options[pane],s=state[pane],doFX=!noAnimation&&!s.isClosed&&(o.fxName_close!="none"),edge=c[pane].edge,rClass=o.resizerClass,tClass=o.togglerClass,_pane="-"+pane,_open="-open",_sliding="-sliding",_closed="-closed",isShowing=s.isShowing,isHiding=s.isHiding;delete s.isShowing;delete s.isHiding;if(!$P||(!o.resizable&&!o.closable))return;else if(!force&&s.isClosed&&!isShowing)return;if(c.isLayoutBusy){setFlowCallback("close",pane,force);return;}if(!isShowing&&false===execUserCallback(pane,o.onclose_start))return;c[pane].isMoving=true;c.isLayoutBusy=true;s.isClosed=true;if(isHiding)s.isHidden=true;else if(isShowing)s.isHidden=false;syncPinBtns(pane,false);if(!s.isSliding)sizeMidPanes(c[pane].dir=="horz"?"all":"center");if($R){$R.css(edge,cDims[edge]).removeClass(rClass+_open+" "+rClass+_pane+_open).removeClass(rClass+_sliding+" "+rClass+_pane+_sliding).addClass(rClass+_closed+" "+rClass+_pane+_closed);if(o.resizable)$R.draggable("disable").css("cursor","default").attr("title","");if($T){$T.removeClass(tClass+_open+" "+tClass+_pane+_open).addClass(tClass+_closed+" "+tClass+_pane+_closed).attr("title",o.togglerTip_closed);}sizeHandles();}if(doFX){lockPaneForFX(pane,true);$P.hide(o.fxName_close,o.fxSettings_close,o.fxSpeed_close,function(){lockPaneForFX(pane,false);if(!s.isClosed)return;close_2();});}else{$P.hide();close_2();}function close_2(){bindStartSlidingEvent(pane,true);if(!isShowing)execUserCallback(pane,o.onclose_end||o.onclose);if(isShowing)execUserCallback(pane,o.onshow_end||o.onshow);if(isHiding)execUserCallback(pane,o.onhide_end||o.onhide);execFlowCallback(pane);}};var open=function(pane,slide,noAnimation){var
$P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],o=options[pane],s=state[pane],doFX=!noAnimation&&s.isClosed&&(o.fxName_open!="none"),edge=c[pane].edge,rClass=o.resizerClass,tClass=o.togglerClass,_pane="-"+pane,_open="-open",_closed="-closed",_sliding="-sliding",isShowing=s.isShowing;delete s.isShowing;if(!$P||(!o.resizable&&!o.closable))return;else if(!s.isClosed&&!s.isSliding)return;if(s.isHidden&&!isShowing){show(pane,true);return;}if(c.isLayoutBusy){setFlowCallback("open",pane,slide);return;}if(false===execUserCallback(pane,o.onopen_start))return;c[pane].isMoving=true;c.isLayoutBusy=true;if(s.isSliding&&!slide)bindStopSlidingEvents(pane,false);s.isClosed=false;if(isShowing)s.isHidden=false;setPaneMinMaxSizes(pane);if(s.size>s.maxSize)$P.css(c[pane].sizeType,max(1,cssSize(pane,s.maxSize)));bindStartSlidingEvent(pane,false);if(doFX){lockPaneForFX(pane,true);$P.show(o.fxName_open,o.fxSettings_open,o.fxSpeed_open,function(){lockPaneForFX(pane,false);if(s.isClosed)return;open_2();});}else{$P.show();open_2();}function open_2(){if(!s.isSliding)sizeMidPanes(c[pane].dir=="vert"?"center":"all");if($R){$R.css(edge,cDims[edge]+getPaneSize(pane)).removeClass(rClass+_closed+" "+rClass+_pane+_closed).addClass(rClass+_open+" "+rClass+_pane+_open).addClass(!s.isSliding?"":rClass+_sliding+" "+rClass+_pane+_sliding);if(o.resizable)$R.draggable("enable").css("cursor",o.resizerCursor).attr("title",o.resizerTip);else
$R.css("cursor","default");if($T){$T.removeClass(tClass+_closed+" "+tClass+_pane+_closed).addClass(tClass+_open+" "+tClass+_pane+_open).attr("title",o.togglerTip_open);}sizeHandles("all");}sizeContent(pane);syncPinBtns(pane,!s.isSliding);execUserCallback(pane,o.onopen_end||o.onopen);if(isShowing)execUserCallback(pane,o.onshow_end||o.onshow);execFlowCallback(pane);}};var lockPaneForFX=function(pane,doLock){var $P=$Ps[pane];if(doLock){$P.css({zIndex:c.zIndex.animation});if(pane=="south")$P.css({top:cDims.top+cDims.innerHeight-$P.outerHeight()});else if(pane=="east")$P.css({left:cDims.left+cDims.innerWidth-$P.outerWidth()});}else{if(!state[pane].isSliding)$P.css({zIndex:c.zIndex.pane_normal});if(pane=="south")$P.css({top:"auto"});else if(pane=="east")$P.css({left:"auto"});}};var bindStartSlidingEvent=function(pane,enable){var
o=options[pane],$R=$Rs[pane],trigger=o.slideTrigger_open;if(!$R||!o.slidable)return;if(trigger!="click"&&trigger!="dblclick"&&trigger!="mouseover")trigger="click";$R
[enable?"bind":"unbind"](trigger,slideOpen).css("cursor",(enable?o.sliderCursor:"default")).attr("title",(enable?o.sliderTip:""));};var bindStopSlidingEvents=function(pane,enable){var
o=options[pane],s=state[pane],trigger=o.slideTrigger_close,action=(enable?"bind":"unbind"),$P=$Ps[pane],$R=$Rs[pane];s.isSliding=enable;clearTimer(pane,"closeSlider");$P.css({zIndex:(enable?c.zIndex.sliding:c.zIndex.pane_normal)});$R.css({zIndex:(enable?c.zIndex.sliding:c.zIndex.resizer_normal)});if(trigger!="click"&&trigger!="mouseout")trigger="mouseout";if(enable){$P.bind(trigger,slideClosed);$R.bind(trigger,slideClosed);if(trigger="mouseout"){$P.bind("mouseover",cancelMouseOut);$R.bind("mouseover",cancelMouseOut);}}else{$P.unbind(trigger);$R.unbind(trigger);if(trigger="mouseout"){$P.unbind("mouseover");$R.unbind("mouseover");clearTimer(pane,"closeSlider");}}function cancelMouseOut(evt){clearTimer(pane,"closeSlider");evt.stopPropagation();}};var slideOpen=function(){var pane=$(this).attr("resizer");if(state[pane].isClosed){bindStopSlidingEvents(pane,true);open(pane,true);}};var slideClosed=function(){var
$E=$(this),pane=$E.attr("pane")||$E.attr("resizer"),o=options[pane],s=state[pane];if(s.isClosed||s.isResizing)return;else if(o.slideTrigger_close=="click")close_NOW();else
setTimer(pane,"closeSlider",close_NOW,300);function close_NOW(){bindStopSlidingEvents(pane,false);if(!s.isClosed)close(pane);}};var sizePane=function(pane,size){var
edge=c[pane].edge,dir=c[pane].dir,o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane];setPaneMinMaxSizes(pane);s.minSize=max(s.minSize,o.minSize);if(o.maxSize>0)s.maxSize=min(s.maxSize,o.maxSize);size=max(size,s.minSize);size=min(size,s.maxSize);s.size=size;$R.css(edge,size+cDims[edge]);$P.css(c[pane].sizeType,max(1,cssSize(pane,size)));if(!s.isSliding)sizeMidPanes(dir=="horz"?"all":"center");sizeHandles();sizeContent(pane);execUserCallback(pane,o.onresize_end||o.onresize);};var sizeMidPanes=function(panes,overrideDims,onInit){if(!panes||panes=="all")panes="east,west,center";var d=getPaneDims();if(overrideDims)$.extend(d,overrideDims);$.each(panes.split(","),function(){if(!$Ps[this])return;var
pane=str(this),o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane],hasRoom=true,CSS={};if(pane=="center"){d=getPaneDims();CSS=$.extend({},d);CSS.width=max(1,cssW(pane,CSS.width));CSS.height=max(1,cssH(pane,CSS.height));hasRoom=(CSS.width>1&&CSS.height>1);if($.browser.msie&&(!$.boxModel||$.browser.version<7)){if($Ps.north)$Ps.north.css({width:cssW($Ps.north,cDims.innerWidth)});if($Ps.south)$Ps.south.css({width:cssW($Ps.south,cDims.innerWidth)});}}else{CSS.top=d.top;CSS.bottom=d.bottom;CSS.height=max(1,cssH(pane,d.height));hasRoom=(CSS.height>1);}if(hasRoom){$P.css(CSS);if(s.noRoom){s.noRoom=false;if(s.isHidden)return;else show(pane,!s.isClosed);}if(!onInit){sizeContent(pane);execUserCallback(pane,o.onresize_end||o.onresize);}}else if(!s.noRoom){s.noRoom=true;if(s.isHidden)return;if(onInit){$P.hide();if($R)$R.hide();}else hide(pane);}});};var sizeContent=function(panes){if(!panes||panes=="all")panes=c.allPanes;$.each(panes.split(","),function(){if(!$Cs[this])return;var
pane=str(this),ignore=options[pane].contentIgnoreSelector,$P=$Ps[pane],$C=$Cs[pane],e_C=$C[0],height=cssH($P);;$P.children().each(function(){if(this==e_C)return;var $E=$(this);if(!ignore||!$E.is(ignore))height-=$E.outerHeight();});if(height>0)height=cssH($C,height);if(height<1)$C.hide();else
$C.css({height:height}).show();});};var sizeHandles=function(panes,onInit){if(!panes||panes=="all")panes=c.borderPanes;$.each(panes.split(","),function(){var
pane=str(this),o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane];if(!$P||!$R||(!o.resizable&&!o.closable))return;var
dir=c[pane].dir,_state=(s.isClosed?"_closed":"_open"),spacing=o["spacing"+_state],togAlign=o["togglerAlign"+_state],togLen=o["togglerLength"+_state],paneLen,offset,CSS={};if(spacing==0){$R.hide();return;}else if(!s.noRoom&&!s.isHidden)$R.show();if(dir=="horz"){paneLen=$P.outerWidth();$R.css({width:max(1,cssW($R,paneLen)),height:max(1,cssH($R,spacing)),left:cssNum($P,"left")});}else{paneLen=$P.outerHeight();$R.css({height:max(1,cssH($R,paneLen)),width:max(1,cssW($R,spacing)),top:cDims.top+getPaneSize("north",true)});}if($T){if(togLen==0||(s.isSliding&&o.hideTogglerOnSlide)){$T.hide();return;}else
$T.show();if(!(togLen>0)||togLen=="100%"||togLen>paneLen){togLen=paneLen;offset=0;}else{if(typeof togAlign=="string"){switch(togAlign){case"top":case"left":offset=0;break;case"bottom":case"right":offset=paneLen-togLen;break;case"middle":case"center":default:offset=Math.floor((paneLen-togLen)/2);}}else{var x=parseInt(togAlign);if(togAlign>=0)offset=x;else offset=paneLen-togLen+x;}}var
$TC_o=(o.togglerContent_open?$T.children(".content-open"):false),$TC_c=(o.togglerContent_closed?$T.children(".content-closed"):false),$TC=(s.isClosed?$TC_c:$TC_o);if($TC_o)$TC_o.css("display",s.isClosed?"none":"block");if($TC_c)$TC_c.css("display",s.isClosed?"block":"none");if(dir=="horz"){var width=cssW($T,togLen);$T.css({width:max(0,width),height:max(1,cssH($T,spacing)),left:offset});if($TC)$TC.css("marginLeft",Math.floor((width-$TC.outerWidth())/2));}else{var height=cssH($T,togLen);$T.css({height:max(0,height),width:max(1,cssW($T,spacing)),top:offset});if($TC)$TC.css("marginTop",Math.floor((height-$TC.outerHeight())/2));}}if(onInit&&o.initHidden){$R.hide();if($T)$T.hide();}});};var resizeAll=function(){var
oldW=cDims.innerWidth,oldH=cDims.innerHeight;cDims=state.container=getElemDims($Container);var
checkH=(cDims.innerHeight<oldH),checkW=(cDims.innerWidth<oldW),s,dir;if(checkH||checkW)$.each(["south","north","east","west"],function(i,pane){s=state[pane];dir=c[pane].dir;if(!s.isClosed&&((checkH&&dir=="horz")||(checkW&&dir=="vert"))){setPaneMinMaxSizes(pane);if(s.size>s.maxSize)sizePane(pane,s.maxSize);}});sizeMidPanes("all");sizeHandles("all");};function keyDown(evt){if(!evt)return true;var code=evt.keyCode;if(code<33)return true;var
PANE={38:"north",40:"south",37:"west",39:"east"},isCursorKey=(code>=37&&code<=40),ALT=evt.altKey,SHIFT=evt.shiftKey,CTRL=evt.ctrlKey,pane=false,s,o,k,m,el;if(!CTRL&&!SHIFT)return true;else if(isCursorKey&&options[PANE[code]].enableCursorHotkey)pane=PANE[code];else
$.each(c.borderPanes.split(","),function(i,p){o=options[p];k=o.customHotkey;m=o.customHotkeyModifier;if((SHIFT&&m=="SHIFT")||(CTRL&&m=="CTRL")||(CTRL&&SHIFT)){if(k&&code==(isNaN(k)||k<=9?k.toUpperCase().charCodeAt(0):k)){pane=p;return false;}}});if(!pane)return true;o=options[pane];s=state[pane];if(!o.enableCursorHotkey||s.isHidden||!$Ps[pane])return true;el=evt.target||evt.srcElement;if(el&&SHIFT&&isCursorKey&&(el.tagName=="TEXTAREA"||(el.tagName=="INPUT"&&(code==37||code==39))))return true;toggle(pane);evt.stopPropagation();evt.returnValue=false;return false;};function allowOverflow(elem){if(this&&this.tagName)elem=this;var $P;if(typeof elem=="string")$P=$Ps[elem];else{if($(elem).attr("pane"))$P=$(elem);else $P=$(elem).parents("div[pane]:first");}if(!$P.length)return;var
pane=$P.attr("pane"),s=state[pane];if(s.cssSaved)resetOverflow(pane);if(s.isSliding||s.isResizing||s.isClosed){s.cssSaved=false;return;}var
newCSS={zIndex:(c.zIndex.pane_normal+1)},curCSS={},of=$P.css("overflow"),ofX=$P.css("overflowX"),ofY=$P.css("overflowY");if(of!="visible"){curCSS.overflow=of;newCSS.overflow="visible";}if(ofX&&ofX!="visible"&&ofX!="auto"){curCSS.overflowX=ofX;newCSS.overflowX="visible";}if(ofY&&ofY!="visible"&&ofY!="auto"){curCSS.overflowY=ofX;newCSS.overflowY="visible";}s.cssSaved=curCSS;$P.css(newCSS);$.each(c.allPanes.split(","),function(i,p){if(p!=pane)resetOverflow(p);});};function resetOverflow(elem){if(this&&this.tagName)elem=this;var $P;if(typeof elem=="string")$P=$Ps[elem];else{if($(elem).hasClass("ui-layout-pane"))$P=$(elem);else $P=$(elem).parents("div[pane]:first");}if(!$P.length)return;var
pane=$P.attr("pane"),s=state[pane],CSS=s.cssSaved||{};if(!s.isSliding&&!s.isResizing)$P.css("zIndex",c.zIndex.pane_normal);$P.css(CSS);s.cssSaved=false;};function getBtn(selector,pane,action){var
$E=$(selector),err="Error Adding Button \n\nInvalid ";if(!$E.length)alert(err+"selector: "+selector);else if(c.borderPanes.indexOf(pane)==-1)alert(err+"pane: "+pane);else{var btn=options[pane].buttonClass+"-"+action;$E.addClass(btn+" "+btn+"-"+pane);return $E;}return false;};function addToggleBtn(selector,pane){var $E=getBtn(selector,pane,"toggle");if($E)$E.attr("title",state[pane].isClosed?"Open":"Close").click(function(evt){toggle(pane);evt.stopPropagation();});};function addOpenBtn(selector,pane){var $E=getBtn(selector,pane,"open");if($E)$E.attr("title","Open").click(function(evt){open(pane);evt.stopPropagation();});};function addCloseBtn(selector,pane){var $E=getBtn(selector,pane,"close");if($E)$E.attr("title","Close").click(function(evt){close(pane);evt.stopPropagation();});};function addPinBtn(selector,pane){var $E=getBtn(selector,pane,"pin");if($E){var s=state[pane];$E.click(function(evt){setPinState($(this),pane,(s.isSliding||s.isClosed));if(s.isSliding||s.isClosed)open(pane);else close(pane);evt.stopPropagation();});setPinState($E,pane,(!s.isClosed&&!s.isSliding));c[pane].pins.push(selector);}};function syncPinBtns(pane,doPin){$.each(c[pane].pins,function(i,selector){setPinState($(selector),pane,doPin);});};function setPinState($Pin,pane,doPin){var updown=$Pin.attr("pin");if(updown&&doPin==(updown=="down"))return;var
root=options[pane].buttonClass,class1=root+"-pin",class2=class1+"-"+pane,UP1=class1+"-up",UP2=class2+"-up",DN1=class1+"-down",DN2=class2+"-down";$Pin.attr("pin",doPin?"down":"up").attr("title",doPin?"Un-Pin":"Pin").removeClass(doPin?UP1:DN1).removeClass(doPin?UP2:DN2).addClass(doPin?DN1:UP1).addClass(doPin?DN2:UP2);};var
$Container=$(this).css({overflow:"hidden"}),$Ps={},$Cs={},$Rs={},$Ts={},c=config,cDims=state.container;create();return{options:options,state:state,panes:$Ps,toggle:toggle,open:open,close:close,hide:hide,show:show,resizeContent:sizeContent,sizePane:sizePane,resizeAll:resizeAll,addToggleBtn:addToggleBtn,addOpenBtn:addOpenBtn,addCloseBtn:addCloseBtn,addPinBtn:addPinBtn,allowOverflow:allowOverflow,resetOverflow:resetOverflow,cssWidth:cssW,cssHeight:cssH};}})(jQuery);

9074
thirdparty/jquery-layout/jquery.ui.all.js vendored Executable file
View File

@ -0,0 +1,9074 @@
/*
* jQuery UI 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI
*/
;jQuery.ui || (function($) {
var _remove = $.fn.remove,
isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9);
//Helper functions and ui object
$.ui = {
version: "1.7.1",
// $.ui.plugin is deprecated. Use the proxy pattern instead.
plugin: {
add: function(module, option, set) {
var proto = $.ui[module].prototype;
for(var i in set) {
proto.plugins[i] = proto.plugins[i] || [];
proto.plugins[i].push([option, set[i]]);
}
},
call: function(instance, name, args) {
var set = instance.plugins[name];
if(!set || !instance.element[0].parentNode) { return; }
for (var i = 0; i < set.length; i++) {
if (instance.options[set[i][0]]) {
set[i][1].apply(instance.element, args);
}
}
}
},
contains: function(a, b) {
return document.compareDocumentPosition
? a.compareDocumentPosition(b) & 16
: a !== b && a.contains(b);
},
hasScroll: function(el, a) {
//If overflow is hidden, the element might have extra content, but the user wants to hide it
if ($(el).css('overflow') == 'hidden') { return false; }
var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
has = false;
if (el[scroll] > 0) { return true; }
// TODO: determine which cases actually cause this to happen
// if the element doesn't have the scroll set, see if it's possible to
// set the scroll
el[scroll] = 1;
has = (el[scroll] > 0);
el[scroll] = 0;
return has;
},
isOverAxis: function(x, reference, size) {
//Determines when x coordinate is over "b" element axis
return (x > reference) && (x < (reference + size));
},
isOver: function(y, x, top, left, height, width) {
//Determines when x, y coordinates is over "b" element
return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
},
keyCode: {
BACKSPACE: 8,
CAPS_LOCK: 20,
COMMA: 188,
CONTROL: 17,
DELETE: 46,
DOWN: 40,
END: 35,
ENTER: 13,
ESCAPE: 27,
HOME: 36,
INSERT: 45,
LEFT: 37,
NUMPAD_ADD: 107,
NUMPAD_DECIMAL: 110,
NUMPAD_DIVIDE: 111,
NUMPAD_ENTER: 108,
NUMPAD_MULTIPLY: 106,
NUMPAD_SUBTRACT: 109,
PAGE_DOWN: 34,
PAGE_UP: 33,
PERIOD: 190,
RIGHT: 39,
SHIFT: 16,
SPACE: 32,
TAB: 9,
UP: 38
}
};
// WAI-ARIA normalization
if (isFF2) {
var attr = $.attr,
removeAttr = $.fn.removeAttr,
ariaNS = "http://www.w3.org/2005/07/aaa",
ariaState = /^aria-/,
ariaRole = /^wairole:/;
$.attr = function(elem, name, value) {
var set = value !== undefined;
return (name == 'role'
? (set
? attr.call(this, elem, name, "wairole:" + value)
: (attr.apply(this, arguments) || "").replace(ariaRole, ""))
: (ariaState.test(name)
? (set
? elem.setAttributeNS(ariaNS,
name.replace(ariaState, "aaa:"), value)
: attr.call(this, elem, name.replace(ariaState, "aaa:")))
: attr.apply(this, arguments)));
};
$.fn.removeAttr = function(name) {
return (ariaState.test(name)
? this.each(function() {
this.removeAttributeNS(ariaNS, name.replace(ariaState, ""));
}) : removeAttr.call(this, name));
};
}
//jQuery plugins
$.fn.extend({
remove: function() {
// Safari has a native remove event which actually removes DOM elements,
// so we have to use triggerHandler instead of trigger (#3037).
$("*", this).add(this).each(function() {
$(this).triggerHandler("remove");
});
return _remove.apply(this, arguments );
},
enableSelection: function() {
return this
.attr('unselectable', 'off')
.css('MozUserSelect', '')
.unbind('selectstart.ui');
},
disableSelection: function() {
return this
.attr('unselectable', 'on')
.css('MozUserSelect', 'none')
.bind('selectstart.ui', function() { return false; });
},
scrollParent: function() {
var scrollParent;
if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
scrollParent = this.parents().filter(function() {
return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
}).eq(0);
} else {
scrollParent = this.parents().filter(function() {
return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
}).eq(0);
}
return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
}
});
//Additional selectors
$.extend($.expr[':'], {
data: function(elem, i, match) {
return !!$.data(elem, match[3]);
},
focusable: function(element) {
var nodeName = element.nodeName.toLowerCase(),
tabIndex = $.attr(element, 'tabindex');
return (/input|select|textarea|button|object/.test(nodeName)
? !element.disabled
: 'a' == nodeName || 'area' == nodeName
? element.href || !isNaN(tabIndex)
: !isNaN(tabIndex))
// the element and all of its ancestors must be visible
// the browser may report that the area is hidden
&& !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
},
tabbable: function(element) {
var tabIndex = $.attr(element, 'tabindex');
return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
}
});
// $.widget is a factory to create jQuery plugins
// taking some boilerplate code out of the plugin code
function getter(namespace, plugin, method, args) {
function getMethods(type) {
var methods = $[namespace][plugin][type] || [];
return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
}
var methods = getMethods('getter');
if (args.length == 1 && typeof args[0] == 'string') {
methods = methods.concat(getMethods('getterSetter'));
}
return ($.inArray(method, methods) != -1);
}
$.widget = function(name, prototype) {
var namespace = name.split(".")[0];
name = name.split(".")[1];
// create plugin method
$.fn[name] = function(options) {
var isMethodCall = (typeof options == 'string'),
args = Array.prototype.slice.call(arguments, 1);
// prevent calls to internal methods
if (isMethodCall && options.substring(0, 1) == '_') {
return this;
}
// handle getter methods
if (isMethodCall && getter(namespace, name, options, args)) {
var instance = $.data(this[0], name);
return (instance ? instance[options].apply(instance, args)
: undefined);
}
// handle initialization and non-getter methods
return this.each(function() {
var instance = $.data(this, name);
// constructor
(!instance && !isMethodCall &&
$.data(this, name, new $[namespace][name](this, options))._init());
// method call
(instance && isMethodCall && $.isFunction(instance[options]) &&
instance[options].apply(instance, args));
});
};
// create widget constructor
$[namespace] = $[namespace] || {};
$[namespace][name] = function(element, options) {
var self = this;
this.namespace = namespace;
this.widgetName = name;
this.widgetEventPrefix = $[namespace][name].eventPrefix || name;
this.widgetBaseClass = namespace + '-' + name;
this.options = $.extend({},
$.widget.defaults,
$[namespace][name].defaults,
$.metadata && $.metadata.get(element)[name],
options);
this.element = $(element)
.bind('setData.' + name, function(event, key, value) {
if (event.target == element) {
return self._setData(key, value);
}
})
.bind('getData.' + name, function(event, key) {
if (event.target == element) {
return self._getData(key);
}
})
.bind('remove', function() {
return self.destroy();
});
};
// add widget prototype
$[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
// TODO: merge getter and getterSetter properties from widget prototype
// and plugin prototype
$[namespace][name].getterSetter = 'option';
};
$.widget.prototype = {
_init: function() {},
destroy: function() {
this.element.removeData(this.widgetName)
.removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled')
.removeAttr('aria-disabled');
},
option: function(key, value) {
var options = key,
self = this;
if (typeof key == "string") {
if (value === undefined) {
return this._getData(key);
}
options = {};
options[key] = value;
}
$.each(options, function(key, value) {
self._setData(key, value);
});
},
_getData: function(key) {
return this.options[key];
},
_setData: function(key, value) {
this.options[key] = value;
if (key == 'disabled') {
this.element
[value ? 'addClass' : 'removeClass'](
this.widgetBaseClass + '-disabled' + ' ' +
this.namespace + '-state-disabled')
.attr("aria-disabled", value);
}
},
enable: function() {
this._setData('disabled', false);
},
disable: function() {
this._setData('disabled', true);
},
_trigger: function(type, event, data) {
var callback = this.options[type],
eventName = (type == this.widgetEventPrefix
? type : this.widgetEventPrefix + type);
event = $.Event(event);
event.type = eventName;
// copy original event properties over to the new event
// this would happen if we could call $.event.fix instead of $.Event
// but we don't have a way to force an event to be fixed multiple times
if (event.originalEvent) {
for (var i = $.event.props.length, prop; i;) {
prop = $.event.props[--i];
event[prop] = event.originalEvent[prop];
}
}
this.element.trigger(event, data);
return !($.isFunction(callback) && callback.call(this.element[0], event, data) === false
|| event.isDefaultPrevented());
}
};
$.widget.defaults = {
disabled: false
};
/** Mouse Interaction Plugin **/
$.ui.mouse = {
_mouseInit: function() {
var self = this;
this.element
.bind('mousedown.'+this.widgetName, function(event) {
return self._mouseDown(event);
})
.bind('click.'+this.widgetName, function(event) {
if(self._preventClickEvent) {
self._preventClickEvent = false;
event.stopImmediatePropagation();
return false;
}
});
// Prevent text selection in IE
if ($.browser.msie) {
this._mouseUnselectable = this.element.attr('unselectable');
this.element.attr('unselectable', 'on');
}
this.started = false;
},
// TODO: make sure destroying one instance of mouse doesn't mess with
// other instances of mouse
_mouseDestroy: function() {
this.element.unbind('.'+this.widgetName);
// Restore text selection in IE
($.browser.msie
&& this.element.attr('unselectable', this._mouseUnselectable));
},
_mouseDown: function(event) {
// don't let more than one widget handle mouseStart
// TODO: figure out why we have to use originalEvent
event.originalEvent = event.originalEvent || {};
if (event.originalEvent.mouseHandled) { return; }
// we may have missed mouseup (out of window)
(this._mouseStarted && this._mouseUp(event));
this._mouseDownEvent = event;
var self = this,
btnIsLeft = (event.which == 1),
elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
return true;
}
this.mouseDelayMet = !this.options.delay;
if (!this.mouseDelayMet) {
this._mouseDelayTimer = setTimeout(function() {
self.mouseDelayMet = true;
}, this.options.delay);
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted = (this._mouseStart(event) !== false);
if (!this._mouseStarted) {
event.preventDefault();
return true;
}
}
// these delegates are required to keep context
this._mouseMoveDelegate = function(event) {
return self._mouseMove(event);
};
this._mouseUpDelegate = function(event) {
return self._mouseUp(event);
};
$(document)
.bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
.bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
// preventDefault() is used to prevent the selection of text here -
// however, in Safari, this causes select boxes not to be selectable
// anymore, so this fix is needed
($.browser.safari || event.preventDefault());
event.originalEvent.mouseHandled = true;
return true;
},
_mouseMove: function(event) {
// IE mouseup check - mouseup happened when mouse was out of window
if ($.browser.msie && !event.button) {
return this._mouseUp(event);
}
if (this._mouseStarted) {
this._mouseDrag(event);
return event.preventDefault();
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted =
(this._mouseStart(this._mouseDownEvent, event) !== false);
(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
}
return !this._mouseStarted;
},
_mouseUp: function(event) {
$(document)
.unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
.unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
if (this._mouseStarted) {
this._mouseStarted = false;
this._preventClickEvent = (event.target == this._mouseDownEvent.target);
this._mouseStop(event);
}
return false;
},
_mouseDistanceMet: function(event) {
return (Math.max(
Math.abs(this._mouseDownEvent.pageX - event.pageX),
Math.abs(this._mouseDownEvent.pageY - event.pageY)
) >= this.options.distance
);
},
_mouseDelayMet: function(event) {
return this.mouseDelayMet;
},
// These are placeholder methods, to be overriden by extending plugin
_mouseStart: function(event) {},
_mouseDrag: function(event) {},
_mouseStop: function(event) {},
_mouseCapture: function(event) { return true; }
};
$.ui.mouse.defaults = {
cancel: null,
distance: 1,
delay: 0
};
})(jQuery);
/*
* jQuery UI Draggable 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Draggables
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.draggable", $.extend({}, $.ui.mouse, {
_init: function() {
if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
this.element[0].style.position = 'relative';
(this.options.addClasses && this.element.addClass("ui-draggable"));
(this.options.disabled && this.element.addClass("ui-draggable-disabled"));
this._mouseInit();
},
destroy: function() {
if(!this.element.data('draggable')) return;
this.element
.removeData("draggable")
.unbind(".draggable")
.removeClass("ui-draggable"
+ " ui-draggable-dragging"
+ " ui-draggable-disabled");
this._mouseDestroy();
},
_mouseCapture: function(event) {
var o = this.options;
if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
return false;
//Quit if we're not on a valid handle
this.handle = this._getHandle(event);
if (!this.handle)
return false;
return true;
},
_mouseStart: function(event) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper(event);
//Cache the helper size
this._cacheHelperProportions();
//If ddmanager is used for droppables, set the global draggable
if($.ui.ddmanager)
$.ui.ddmanager.current = this;
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Store the helper's css position
this.cssPosition = this.helper.css("position");
this.scrollParent = this.helper.scrollParent();
//The element's absolute position on the page minus margins
this.offset = this.element.offset();
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
$.extend(this.offset, {
click: { //Where the click happened, relative to the element
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
},
parent: this._getParentOffset(),
relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
});
//Generate the original position
this.originalPosition = this._generatePosition(event);
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
//Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
if(o.cursorAt)
this._adjustOffsetFromHelper(o.cursorAt);
//Set a containment if given in the options
if(o.containment)
this._setContainment();
//Call plugins and callbacks
this._trigger("start", event);
//Recache the helper size
this._cacheHelperProportions();
//Prepare the droppable offsets
if ($.ui.ddmanager && !o.dropBehaviour)
$.ui.ddmanager.prepareOffsets(this, event);
this.helper.addClass("ui-draggable-dragging");
this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
return true;
},
_mouseDrag: function(event, noPropagation) {
//Compute the helpers position
this.position = this._generatePosition(event);
this.positionAbs = this._convertPositionTo("absolute");
//Call plugins and callbacks and use the resulting position if something is returned
if (!noPropagation) {
var ui = this._uiHash();
this._trigger('drag', event, ui);
this.position = ui.position;
}
if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
return false;
},
_mouseStop: function(event) {
//If we are using droppables, inform the manager about the drop
var dropped = false;
if ($.ui.ddmanager && !this.options.dropBehaviour)
dropped = $.ui.ddmanager.drop(this, event);
//if a drop comes from outside (a sortable)
if(this.dropped) {
dropped = this.dropped;
this.dropped = false;
}
if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
var self = this;
$(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
self._trigger("stop", event);
self._clear();
});
} else {
this._trigger("stop", event);
this._clear();
}
return false;
},
_getHandle: function(event) {
var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
$(this.options.handle, this.element)
.find("*")
.andSelf()
.each(function() {
if(this == event.target) handle = true;
});
return handle;
},
_createHelper: function(event) {
var o = this.options;
var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
if(!helper.parents('body').length)
helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
helper.css("position", "absolute");
return helper;
},
_adjustOffsetFromHelper: function(obj) {
if(obj.left != undefined) this.offset.click.left = obj.left + this.margins.left;
if(obj.right != undefined) this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
if(obj.top != undefined) this.offset.click.top = obj.top + this.margins.top;
if(obj.bottom != undefined) this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
},
_getParentOffset: function() {
//Get the offsetParent and cache its position
this.offsetParent = this.helper.offsetParent();
var po = this.offsetParent.offset();
// This is a special case where we need to modify a offset calculated on start, since the following happened:
// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
// the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
po.left += this.scrollParent.scrollLeft();
po.top += this.scrollParent.scrollTop();
}
if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
|| (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
po = { top: 0, left: 0 };
return {
top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
};
},
_getRelativeOffset: function() {
if(this.cssPosition == "relative") {
var p = this.element.position();
return {
top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
};
} else {
return { top: 0, left: 0 };
}
},
_cacheMargins: function() {
this.margins = {
left: (parseInt(this.element.css("marginLeft"),10) || 0),
top: (parseInt(this.element.css("marginTop"),10) || 0)
};
},
_cacheHelperProportions: function() {
this.helperProportions = {
width: this.helper.outerWidth(),
height: this.helper.outerHeight()
};
},
_setContainment: function() {
var o = this.options;
if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
if(o.containment == 'document' || o.containment == 'window') this.containment = [
0 - this.offset.relative.left - this.offset.parent.left,
0 - this.offset.relative.top - this.offset.parent.top,
$(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
];
if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
var ce = $(o.containment)[0]; if(!ce) return;
var co = $(o.containment).offset();
var over = ($(ce).css("overflow") != 'hidden');
this.containment = [
co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
];
} else if(o.containment.constructor == Array) {
this.containment = o.containment;
}
},
_convertPositionTo: function(d, pos) {
if(!pos) pos = this.position;
var mod = d == "absolute" ? 1 : -1;
var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
return {
top: (
pos.top // The absolute mouse position
+ this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
- ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
),
left: (
pos.left // The absolute mouse position
+ this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
- ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
)
};
},
_generatePosition: function(event) {
var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
// This is another very weird special case that only happens for relative elements:
// 1. If the css position is relative
// 2. and the scroll parent is the document or similar to the offset parent
// we have to refresh the relative offset during the scroll so there are no jumps
if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
this.offset.relative = this._getRelativeOffset();
}
var pageX = event.pageX;
var pageY = event.pageY;
/*
* - Position constraining -
* Constrain the position to a mix of grid, containment.
*/
if(this.originalPosition) { //If we are not dragging yet, we won't check for options
if(this.containment) {
if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
}
if(o.grid) {
var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
}
}
return {
top: (
pageY // The absolute mouse position
- this.offset.click.top // Click offset (relative to the element)
- this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.top // The offsetParent's offset without borders (offset + border)
+ ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
),
left: (
pageX // The absolute mouse position
- this.offset.click.left // Click offset (relative to the element)
- this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.left // The offsetParent's offset without borders (offset + border)
+ ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
)
};
},
_clear: function() {
this.helper.removeClass("ui-draggable-dragging");
if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
//if($.ui.ddmanager) $.ui.ddmanager.current = null;
this.helper = null;
this.cancelHelperRemoval = false;
},
// From now on bulk stuff - mainly helpers
_trigger: function(type, event, ui) {
ui = ui || this._uiHash();
$.ui.plugin.call(this, type, [event, ui]);
if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
return $.widget.prototype._trigger.call(this, type, event, ui);
},
plugins: {},
_uiHash: function(event) {
return {
helper: this.helper,
position: this.position,
absolutePosition: this.positionAbs, //deprecated
offset: this.positionAbs
};
}
}));
$.extend($.ui.draggable, {
version: "1.7.1",
eventPrefix: "drag",
defaults: {
addClasses: true,
appendTo: "parent",
axis: false,
cancel: ":input,option",
connectToSortable: false,
containment: false,
cursor: "auto",
cursorAt: false,
delay: 0,
distance: 1,
grid: false,
handle: false,
helper: "original",
iframeFix: false,
opacity: false,
refreshPositions: false,
revert: false,
revertDuration: 500,
scope: "default",
scroll: true,
scrollSensitivity: 20,
scrollSpeed: 20,
snap: false,
snapMode: "both",
snapTolerance: 20,
stack: false,
zIndex: false
}
});
$.ui.plugin.add("draggable", "connectToSortable", {
start: function(event, ui) {
var inst = $(this).data("draggable"), o = inst.options,
uiSortable = $.extend({}, ui, { item: inst.element });
inst.sortables = [];
$(o.connectToSortable).each(function() {
var sortable = $.data(this, 'sortable');
if (sortable && !sortable.options.disabled) {
inst.sortables.push({
instance: sortable,
shouldRevert: sortable.options.revert
});
sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
sortable._trigger("activate", event, uiSortable);
}
});
},
stop: function(event, ui) {
//If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
var inst = $(this).data("draggable"),
uiSortable = $.extend({}, ui, { item: inst.element });
$.each(inst.sortables, function() {
if(this.instance.isOver) {
this.instance.isOver = 0;
inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
//The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
if(this.shouldRevert) this.instance.options.revert = true;
//Trigger the stop of the sortable
this.instance._mouseStop(event);
this.instance.options.helper = this.instance.options._helper;
//If the helper has been the original item, restore properties in the sortable
if(inst.options.helper == 'original')
this.instance.currentItem.css({ top: 'auto', left: 'auto' });
} else {
this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
this.instance._trigger("deactivate", event, uiSortable);
}
});
},
drag: function(event, ui) {
var inst = $(this).data("draggable"), self = this;
var checkPos = function(o) {
var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
var itemHeight = o.height, itemWidth = o.width;
var itemTop = o.top, itemLeft = o.left;
return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
};
$.each(inst.sortables, function(i) {
//Copy over some variables to allow calling the sortable's native _intersectsWith
this.instance.positionAbs = inst.positionAbs;
this.instance.helperProportions = inst.helperProportions;
this.instance.offset.click = inst.offset.click;
if(this.instance._intersectsWith(this.instance.containerCache)) {
//If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
if(!this.instance.isOver) {
this.instance.isOver = 1;
//Now we fake the start of dragging for the sortable instance,
//by cloning the list group item, appending it to the sortable and using it as inst.currentItem
//We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
this.instance.options.helper = function() { return ui.helper[0]; };
event.target = this.instance.currentItem[0];
this.instance._mouseCapture(event, true);
this.instance._mouseStart(event, true, true);
//Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
this.instance.offset.click.top = inst.offset.click.top;
this.instance.offset.click.left = inst.offset.click.left;
this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
inst._trigger("toSortable", event);
inst.dropped = this.instance.element; //draggable revert needs that
//hack so receive/update callbacks work (mostly)
inst.currentItem = inst.element;
this.instance.fromOutside = inst;
}
//Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
if(this.instance.currentItem) this.instance._mouseDrag(event);
} else {
//If it doesn't intersect with the sortable, and it intersected before,
//we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
if(this.instance.isOver) {
this.instance.isOver = 0;
this.instance.cancelHelperRemoval = true;
//Prevent reverting on this forced stop
this.instance.options.revert = false;
// The out event needs to be triggered independently
this.instance._trigger('out', event, this.instance._uiHash(this.instance));
this.instance._mouseStop(event, true);
this.instance.options.helper = this.instance.options._helper;
//Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
this.instance.currentItem.remove();
if(this.instance.placeholder) this.instance.placeholder.remove();
inst._trigger("fromSortable", event);
inst.dropped = false; //draggable revert needs that
}
};
});
}
});
$.ui.plugin.add("draggable", "cursor", {
start: function(event, ui) {
var t = $('body'), o = $(this).data('draggable').options;
if (t.css("cursor")) o._cursor = t.css("cursor");
t.css("cursor", o.cursor);
},
stop: function(event, ui) {
var o = $(this).data('draggable').options;
if (o._cursor) $('body').css("cursor", o._cursor);
}
});
$.ui.plugin.add("draggable", "iframeFix", {
start: function(event, ui) {
var o = $(this).data('draggable').options;
$(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
$('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
.css({
width: this.offsetWidth+"px", height: this.offsetHeight+"px",
position: "absolute", opacity: "0.001", zIndex: 1000
})
.css($(this).offset())
.appendTo("body");
});
},
stop: function(event, ui) {
$("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
}
});
$.ui.plugin.add("draggable", "opacity", {
start: function(event, ui) {
var t = $(ui.helper), o = $(this).data('draggable').options;
if(t.css("opacity")) o._opacity = t.css("opacity");
t.css('opacity', o.opacity);
},
stop: function(event, ui) {
var o = $(this).data('draggable').options;
if(o._opacity) $(ui.helper).css('opacity', o._opacity);
}
});
$.ui.plugin.add("draggable", "scroll", {
start: function(event, ui) {
var i = $(this).data("draggable");
if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
},
drag: function(event, ui) {
var i = $(this).data("draggable"), o = i.options, scrolled = false;
if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
if(!o.axis || o.axis != 'x') {
if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
}
if(!o.axis || o.axis != 'y') {
if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
}
} else {
if(!o.axis || o.axis != 'x') {
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
}
if(!o.axis || o.axis != 'y') {
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
}
}
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
$.ui.ddmanager.prepareOffsets(i, event);
}
});
$.ui.plugin.add("draggable", "snap", {
start: function(event, ui) {
var i = $(this).data("draggable"), o = i.options;
i.snapElements = [];
$(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
var $t = $(this); var $o = $t.offset();
if(this != i.element[0]) i.snapElements.push({
item: this,
width: $t.outerWidth(), height: $t.outerHeight(),
top: $o.top, left: $o.left
});
});
},
drag: function(event, ui) {
var inst = $(this).data("draggable"), o = inst.options;
var d = o.snapTolerance;
var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
for (var i = inst.snapElements.length - 1; i >= 0; i--){
var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
//Yes, I know, this is insane ;)
if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
inst.snapElements[i].snapping = false;
continue;
}
if(o.snapMode != 'inner') {
var ts = Math.abs(t - y2) <= d;
var bs = Math.abs(b - y1) <= d;
var ls = Math.abs(l - x2) <= d;
var rs = Math.abs(r - x1) <= d;
if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
}
var first = (ts || bs || ls || rs);
if(o.snapMode != 'outer') {
var ts = Math.abs(t - y1) <= d;
var bs = Math.abs(b - y2) <= d;
var ls = Math.abs(l - x1) <= d;
var rs = Math.abs(r - x2) <= d;
if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
}
if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
(inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
};
}
});
$.ui.plugin.add("draggable", "stack", {
start: function(event, ui) {
var o = $(this).data("draggable").options;
var group = $.makeArray($(o.stack.group)).sort(function(a,b) {
return (parseInt($(a).css("zIndex"),10) || o.stack.min) - (parseInt($(b).css("zIndex"),10) || o.stack.min);
});
$(group).each(function(i) {
this.style.zIndex = o.stack.min + i;
});
this[0].style.zIndex = o.stack.min + group.length;
}
});
$.ui.plugin.add("draggable", "zIndex", {
start: function(event, ui) {
var t = $(ui.helper), o = $(this).data("draggable").options;
if(t.css("zIndex")) o._zIndex = t.css("zIndex");
t.css('zIndex', o.zIndex);
},
stop: function(event, ui) {
var o = $(this).data("draggable").options;
if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
}
});
})(jQuery);
/*
* jQuery UI Droppable 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Droppables
*
* Depends:
* ui.core.js
* ui.draggable.js
*/
(function($) {
$.widget("ui.droppable", {
_init: function() {
var o = this.options, accept = o.accept;
this.isover = 0; this.isout = 1;
this.options.accept = this.options.accept && $.isFunction(this.options.accept) ? this.options.accept : function(d) {
return d.is(accept);
};
//Store the droppable's proportions
this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
// Add the reference and positions to the manager
$.ui.ddmanager.droppables[this.options.scope] = $.ui.ddmanager.droppables[this.options.scope] || [];
$.ui.ddmanager.droppables[this.options.scope].push(this);
(this.options.addClasses && this.element.addClass("ui-droppable"));
},
destroy: function() {
var drop = $.ui.ddmanager.droppables[this.options.scope];
for ( var i = 0; i < drop.length; i++ )
if ( drop[i] == this )
drop.splice(i, 1);
this.element
.removeClass("ui-droppable ui-droppable-disabled")
.removeData("droppable")
.unbind(".droppable");
},
_setData: function(key, value) {
if(key == 'accept') {
this.options.accept = value && $.isFunction(value) ? value : function(d) {
return d.is(value);
};
} else {
$.widget.prototype._setData.apply(this, arguments);
}
},
_activate: function(event) {
var draggable = $.ui.ddmanager.current;
if(this.options.activeClass) this.element.addClass(this.options.activeClass);
(draggable && this._trigger('activate', event, this.ui(draggable)));
},
_deactivate: function(event) {
var draggable = $.ui.ddmanager.current;
if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
(draggable && this._trigger('deactivate', event, this.ui(draggable)));
},
_over: function(event) {
var draggable = $.ui.ddmanager.current;
if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
if (this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
this._trigger('over', event, this.ui(draggable));
}
},
_out: function(event) {
var draggable = $.ui.ddmanager.current;
if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
if (this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
this._trigger('out', event, this.ui(draggable));
}
},
_drop: function(event,custom) {
var draggable = custom || $.ui.ddmanager.current;
if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
var childrenIntersection = false;
this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
var inst = $.data(this, 'droppable');
if(inst.options.greedy && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)) {
childrenIntersection = true; return false;
}
});
if(childrenIntersection) return false;
if(this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
this._trigger('drop', event, this.ui(draggable));
return this.element;
}
return false;
},
ui: function(c) {
return {
draggable: (c.currentItem || c.element),
helper: c.helper,
position: c.position,
absolutePosition: c.positionAbs, //deprecated
offset: c.positionAbs
};
}
});
$.extend($.ui.droppable, {
version: "1.7.1",
eventPrefix: 'drop',
defaults: {
accept: '*',
activeClass: false,
addClasses: true,
greedy: false,
hoverClass: false,
scope: 'default',
tolerance: 'intersect'
}
});
$.ui.intersect = function(draggable, droppable, toleranceMode) {
if (!droppable.offset) return false;
var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
var l = droppable.offset.left, r = l + droppable.proportions.width,
t = droppable.offset.top, b = t + droppable.proportions.height;
switch (toleranceMode) {
case 'fit':
return (l < x1 && x2 < r
&& t < y1 && y2 < b);
break;
case 'intersect':
return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
&& x2 - (draggable.helperProportions.width / 2) < r // Left Half
&& t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
&& y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
break;
case 'pointer':
var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
return isOver;
break;
case 'touch':
return (
(y1 >= t && y1 <= b) || // Top edge touching
(y2 >= t && y2 <= b) || // Bottom edge touching
(y1 < t && y2 > b) // Surrounded vertically
) && (
(x1 >= l && x1 <= r) || // Left edge touching
(x2 >= l && x2 <= r) || // Right edge touching
(x1 < l && x2 > r) // Surrounded horizontally
);
break;
default:
return false;
break;
}
};
/*
This manager tracks offsets of draggables and droppables
*/
$.ui.ddmanager = {
current: null,
droppables: { 'default': [] },
prepareOffsets: function(t, event) {
var m = $.ui.ddmanager.droppables[t.options.scope];
var type = event ? event.type : null; // workaround for #2317
var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
droppablesLoop: for (var i = 0; i < m.length; i++) {
if(m[i].options.disabled || (t && !m[i].options.accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
m[i].offset = m[i].element.offset();
m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
}
},
drop: function(draggable, event) {
var dropped = false;
$.each($.ui.ddmanager.droppables[draggable.options.scope], function() {
if(!this.options) return;
if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
dropped = this._drop.call(this, event);
if (!this.options.disabled && this.visible && this.options.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
this.isout = 1; this.isover = 0;
this._deactivate.call(this, event);
}
});
return dropped;
},
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
//Run through all droppables and check their positions based on specific tolerance options
$.each($.ui.ddmanager.droppables[draggable.options.scope], function() {
if(this.options.disabled || this.greedyChild || !this.visible) return;
var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
if(!c) return;
var parentInstance;
if (this.options.greedy) {
var parent = this.element.parents(':data(droppable):eq(0)');
if (parent.length) {
parentInstance = $.data(parent[0], 'droppable');
parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
}
}
// we just moved into a greedy child
if (parentInstance && c == 'isover') {
parentInstance['isover'] = 0;
parentInstance['isout'] = 1;
parentInstance._out.call(parentInstance, event);
}
this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
this[c == "isover" ? "_over" : "_out"].call(this, event);
// we just moved out of a greedy child
if (parentInstance && c == 'isout') {
parentInstance['isout'] = 0;
parentInstance['isover'] = 1;
parentInstance._over.call(parentInstance, event);
}
});
}
};
})(jQuery);
/*
* jQuery UI Resizable 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Resizables
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.resizable", $.extend({}, $.ui.mouse, {
_init: function() {
var self = this, o = this.options;
this.element.addClass("ui-resizable");
$.extend(this, {
_aspectRatio: !!(o.aspectRatio),
aspectRatio: o.aspectRatio,
originalElement: this.element,
_proportionallyResizeElements: [],
_helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
});
//Wrap the element if it cannot hold child nodes
if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
//Opera fix for relative positioning
if (/relative/.test(this.element.css('position')) && $.browser.opera)
this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
//Create a wrapper element and set the wrapper to the new current internal element
this.element.wrap(
$('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
position: this.element.css('position'),
width: this.element.outerWidth(),
height: this.element.outerHeight(),
top: this.element.css('top'),
left: this.element.css('left')
})
);
//Overwrite the original this.element
this.element = this.element.parent().data(
"resizable", this.element.data('resizable')
);
this.elementIsWrapper = true;
//Move margins to the wrapper
this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
//Prevent Safari textarea resize
this.originalResizeStyle = this.originalElement.css('resize');
this.originalElement.css('resize', 'none');
//Push the actual element to our proportionallyResize internal array
this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
// avoid IE jump (hard set the margin)
this.originalElement.css({ margin: this.originalElement.css('margin') });
// fix handlers offset
this._proportionallyResize();
}
this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
if(this.handles.constructor == String) {
if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
var n = this.handles.split(","); this.handles = {};
for(var i = 0; i < n.length; i++) {
var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
// increase zIndex of sw, se, ne, nw axis
//TODO : this modifies original option
if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
//TODO : What's going on here?
if ('se' == handle) {
axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
};
//Insert into internal handles object and append to element
this.handles[handle] = '.ui-resizable-'+handle;
this.element.append(axis);
}
}
this._renderAxis = function(target) {
target = target || this.element;
for(var i in this.handles) {
if(this.handles[i].constructor == String)
this.handles[i] = $(this.handles[i], this.element).show();
//Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
var axis = $(this.handles[i], this.element), padWrapper = 0;
//Checking the correct pad and border
padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
//The padding type i have to apply...
var padPos = [ 'padding',
/ne|nw|n/.test(i) ? 'Top' :
/se|sw|s/.test(i) ? 'Bottom' :
/^e$/.test(i) ? 'Right' : 'Left' ].join("");
target.css(padPos, padWrapper);
this._proportionallyResize();
}
//TODO: What's that good for? There's not anything to be executed left
if(!$(this.handles[i]).length)
continue;
}
};
//TODO: make renderAxis a prototype function
this._renderAxis(this.element);
this._handles = $('.ui-resizable-handle', this.element)
.disableSelection();
//Matching axis name
this._handles.mouseover(function() {
if (!self.resizing) {
if (this.className)
var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
//Axis, default = se
self.axis = axis && axis[1] ? axis[1] : 'se';
}
});
//If we want to auto hide the elements
if (o.autoHide) {
this._handles.hide();
$(this.element)
.addClass("ui-resizable-autohide")
.hover(function() {
$(this).removeClass("ui-resizable-autohide");
self._handles.show();
},
function(){
if (!self.resizing) {
$(this).addClass("ui-resizable-autohide");
self._handles.hide();
}
});
}
//Initialize the mouse interaction
this._mouseInit();
},
destroy: function() {
this._mouseDestroy();
var _destroy = function(exp) {
$(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
.removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
};
//TODO: Unwrap at same DOM position
if (this.elementIsWrapper) {
_destroy(this.element);
var wrapper = this.element;
wrapper.parent().append(
this.originalElement.css({
position: wrapper.css('position'),
width: wrapper.outerWidth(),
height: wrapper.outerHeight(),
top: wrapper.css('top'),
left: wrapper.css('left')
})
).end().remove();
}
this.originalElement.css('resize', this.originalResizeStyle);
_destroy(this.originalElement);
},
_mouseCapture: function(event) {
var handle = false;
for(var i in this.handles) {
if($(this.handles[i])[0] == event.target) handle = true;
}
return this.options.disabled || !!handle;
},
_mouseStart: function(event) {
var o = this.options, iniPos = this.element.position(), el = this.element;
this.resizing = true;
this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
// bugfix for http://dev.jquery.com/ticket/1749
if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
}
//Opera fixing relative position
if ($.browser.opera && (/relative/).test(el.css('position')))
el.css({ position: 'relative', top: 'auto', left: 'auto' });
this._renderProxy();
var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
if (o.containment) {
curleft += $(o.containment).scrollLeft() || 0;
curtop += $(o.containment).scrollTop() || 0;
}
//Store needed variables
this.offset = this.helper.offset();
this.position = { left: curleft, top: curtop };
this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
this.originalPosition = { left: curleft, top: curtop };
this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
this.originalMousePosition = { left: event.pageX, top: event.pageY };
//Aspect Ratio
this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
var cursor = $('.ui-resizable-' + this.axis).css('cursor');
$('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
el.addClass("ui-resizable-resizing");
this._propagate("start", event);
return true;
},
_mouseDrag: function(event) {
//Increase performance, avoid regex
var el = this.helper, o = this.options, props = {},
self = this, smp = this.originalMousePosition, a = this.axis;
var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
var trigger = this._change[a];
if (!trigger) return false;
// Calculate the attrs that will be change
var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
if (this._aspectRatio || event.shiftKey)
data = this._updateRatio(data, event);
data = this._respectSize(data, event);
// plugins callbacks need to be called first
this._propagate("resize", event);
el.css({
top: this.position.top + "px", left: this.position.left + "px",
width: this.size.width + "px", height: this.size.height + "px"
});
if (!this._helper && this._proportionallyResizeElements.length)
this._proportionallyResize();
this._updateCache(data);
// calling the user callback at the end
this._trigger('resize', event, this.ui());
return false;
},
_mouseStop: function(event) {
this.resizing = false;
var o = this.options, self = this;
if(this._helper) {
var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
soffsetw = ista ? 0 : self.sizeDiff.width;
var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
if (!o.animate)
this.element.css($.extend(s, { top: top, left: left }));
self.helper.height(self.size.height);
self.helper.width(self.size.width);
if (this._helper && !o.animate) this._proportionallyResize();
}
$('body').css('cursor', 'auto');
this.element.removeClass("ui-resizable-resizing");
this._propagate("stop", event);
if (this._helper) this.helper.remove();
return false;
},
_updateCache: function(data) {
var o = this.options;
this.offset = this.helper.offset();
if (isNumber(data.left)) this.position.left = data.left;
if (isNumber(data.top)) this.position.top = data.top;
if (isNumber(data.height)) this.size.height = data.height;
if (isNumber(data.width)) this.size.width = data.width;
},
_updateRatio: function(data, event) {
var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
if (data.height) data.width = (csize.height * this.aspectRatio);
else if (data.width) data.height = (csize.width / this.aspectRatio);
if (a == 'sw') {
data.left = cpos.left + (csize.width - data.width);
data.top = null;
}
if (a == 'nw') {
data.top = cpos.top + (csize.height - data.height);
data.left = cpos.left + (csize.width - data.width);
}
return data;
},
_respectSize: function(data, event) {
var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
if (isminw) data.width = o.minWidth;
if (isminh) data.height = o.minHeight;
if (ismaxw) data.width = o.maxWidth;
if (ismaxh) data.height = o.maxHeight;
var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
if (isminw && cw) data.left = dw - o.minWidth;
if (ismaxw && cw) data.left = dw - o.maxWidth;
if (isminh && ch) data.top = dh - o.minHeight;
if (ismaxh && ch) data.top = dh - o.maxHeight;
// fixing jump error on top/left - bug #2330
var isNotwh = !data.width && !data.height;
if (isNotwh && !data.left && data.top) data.top = null;
else if (isNotwh && !data.top && data.left) data.left = null;
return data;
},
_proportionallyResize: function() {
var o = this.options;
if (!this._proportionallyResizeElements.length) return;
var element = this.helper || this.element;
for (var i=0; i < this._proportionallyResizeElements.length; i++) {
var prel = this._proportionallyResizeElements[i];
if (!this.borderDif) {
var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
this.borderDif = $.map(b, function(v, i) {
var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
return border + padding;
});
}
if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
continue;
prel.css({
height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
});
};
},
_renderProxy: function() {
var el = this.element, o = this.options;
this.elementOffset = el.offset();
if(this._helper) {
this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
// fix ie6 offset TODO: This seems broken
var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
pxyoffset = ( ie6 ? 2 : -1 );
this.helper.addClass(this._helper).css({
width: this.element.outerWidth() + pxyoffset,
height: this.element.outerHeight() + pxyoffset,
position: 'absolute',
left: this.elementOffset.left - ie6offset +'px',
top: this.elementOffset.top - ie6offset +'px',
zIndex: ++o.zIndex //TODO: Don't modify option
});
this.helper
.appendTo("body")
.disableSelection();
} else {
this.helper = this.element;
}
},
_change: {
e: function(event, dx, dy) {
return { width: this.originalSize.width + dx };
},
w: function(event, dx, dy) {
var o = this.options, cs = this.originalSize, sp = this.originalPosition;
return { left: sp.left + dx, width: cs.width - dx };
},
n: function(event, dx, dy) {
var o = this.options, cs = this.originalSize, sp = this.originalPosition;
return { top: sp.top + dy, height: cs.height - dy };
},
s: function(event, dx, dy) {
return { height: this.originalSize.height + dy };
},
se: function(event, dx, dy) {
return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
},
sw: function(event, dx, dy) {
return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
},
ne: function(event, dx, dy) {
return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
},
nw: function(event, dx, dy) {
return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
}
},
_propagate: function(n, event) {
$.ui.plugin.call(this, n, [event, this.ui()]);
(n != "resize" && this._trigger(n, event, this.ui()));
},
plugins: {},
ui: function() {
return {
originalElement: this.originalElement,
element: this.element,
helper: this.helper,
position: this.position,
size: this.size,
originalSize: this.originalSize,
originalPosition: this.originalPosition
};
}
}));
$.extend($.ui.resizable, {
version: "1.7.1",
eventPrefix: "resize",
defaults: {
alsoResize: false,
animate: false,
animateDuration: "slow",
animateEasing: "swing",
aspectRatio: false,
autoHide: false,
cancel: ":input,option",
containment: false,
delay: 0,
distance: 1,
ghost: false,
grid: false,
handles: "e,s,se",
helper: false,
maxHeight: null,
maxWidth: null,
minHeight: 10,
minWidth: 10,
zIndex: 1000
}
});
/*
* Resizable Extensions
*/
$.ui.plugin.add("resizable", "alsoResize", {
start: function(event, ui) {
var self = $(this).data("resizable"), o = self.options;
_store = function(exp) {
$(exp).each(function() {
$(this).data("resizable-alsoresize", {
width: parseInt($(this).width(), 10), height: parseInt($(this).height(), 10),
left: parseInt($(this).css('left'), 10), top: parseInt($(this).css('top'), 10)
});
});
};
if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
else { $.each(o.alsoResize, function(exp, c) { _store(exp); }); }
}else{
_store(o.alsoResize);
}
},
resize: function(event, ui){
var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
var delta = {
height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
},
_alsoResize = function(exp, c) {
$(exp).each(function() {
var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left'];
$.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
var sum = (start[prop]||0) + (delta[prop]||0);
if (sum && sum >= 0)
style[prop] = sum || null;
});
//Opera fixing relative position
if (/relative/.test(el.css('position')) && $.browser.opera) {
self._revertToRelativePosition = true;
el.css({ position: 'absolute', top: 'auto', left: 'auto' });
}
el.css(style);
});
};
if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
$.each(o.alsoResize, function(exp, c) { _alsoResize(exp, c); });
}else{
_alsoResize(o.alsoResize);
}
},
stop: function(event, ui){
var self = $(this).data("resizable");
//Opera fixing relative position
if (self._revertToRelativePosition && $.browser.opera) {
self._revertToRelativePosition = false;
el.css({ position: 'relative' });
}
$(this).removeData("resizable-alsoresize-start");
}
});
$.ui.plugin.add("resizable", "animate", {
stop: function(event, ui) {
var self = $(this).data("resizable"), o = self.options;
var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
soffsetw = ista ? 0 : self.sizeDiff.width;
var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
self.element.animate(
$.extend(style, top && left ? { top: top, left: left } : {}), {
duration: o.animateDuration,
easing: o.animateEasing,
step: function() {
var data = {
width: parseInt(self.element.css('width'), 10),
height: parseInt(self.element.css('height'), 10),
top: parseInt(self.element.css('top'), 10),
left: parseInt(self.element.css('left'), 10)
};
if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
// propagating resize, and updating values for each animation step
self._updateCache(data);
self._propagate("resize", event);
}
}
);
}
});
$.ui.plugin.add("resizable", "containment", {
start: function(event, ui) {
var self = $(this).data("resizable"), o = self.options, el = self.element;
var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
if (!ce) return;
self.containerElement = $(ce);
if (/document/.test(oc) || oc == document) {
self.containerOffset = { left: 0, top: 0 };
self.containerPosition = { left: 0, top: 0 };
self.parentData = {
element: $(document), left: 0, top: 0,
width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
};
}
// i'm a node, so compute top, left, right, bottom
else {
var element = $(ce), p = [];
$([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
self.containerOffset = element.offset();
self.containerPosition = element.position();
self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width,
width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
self.parentData = {
element: ce, left: co.left, top: co.top, width: width, height: height
};
}
},
resize: function(event, ui) {
var self = $(this).data("resizable"), o = self.options,
ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
if (cp.left < (self._helper ? co.left : 0)) {
self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
if (pRatio) self.size.height = self.size.width / o.aspectRatio;
self.position.left = o.helper ? co.left : 0;
}
if (cp.top < (self._helper ? co.top : 0)) {
self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
if (pRatio) self.size.width = self.size.height * o.aspectRatio;
self.position.top = self._helper ? co.top : 0;
}
self.offset.left = self.parentData.left+self.position.left;
self.offset.top = self.parentData.top+self.position.top;
var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
var isParent = self.containerElement.get(0) == self.element.parent().get(0),
isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
if(isParent && isOffsetRelative) woset -= self.parentData.left;
if (woset + self.size.width >= self.parentData.width) {
self.size.width = self.parentData.width - woset;
if (pRatio) self.size.height = self.size.width / self.aspectRatio;
}
if (hoset + self.size.height >= self.parentData.height) {
self.size.height = self.parentData.height - hoset;
if (pRatio) self.size.width = self.size.height * self.aspectRatio;
}
},
stop: function(event, ui){
var self = $(this).data("resizable"), o = self.options, cp = self.position,
co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
$(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
if (self._helper && !o.animate && (/static/).test(ce.css('position')))
$(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
}
});
$.ui.plugin.add("resizable", "ghost", {
start: function(event, ui) {
var self = $(this).data("resizable"), o = self.options, cs = self.size;
self.ghost = self.originalElement.clone();
self.ghost
.css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
.addClass('ui-resizable-ghost')
.addClass(typeof o.ghost == 'string' ? o.ghost : '');
self.ghost.appendTo(self.helper);
},
resize: function(event, ui){
var self = $(this).data("resizable"), o = self.options;
if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
},
stop: function(event, ui){
var self = $(this).data("resizable"), o = self.options;
if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
}
});
$.ui.plugin.add("resizable", "grid", {
resize: function(event, ui) {
var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
if (/^(se|s|e)$/.test(a)) {
self.size.width = os.width + ox;
self.size.height = os.height + oy;
}
else if (/^(ne)$/.test(a)) {
self.size.width = os.width + ox;
self.size.height = os.height + oy;
self.position.top = op.top - oy;
}
else if (/^(sw)$/.test(a)) {
self.size.width = os.width + ox;
self.size.height = os.height + oy;
self.position.left = op.left - ox;
}
else {
self.size.width = os.width + ox;
self.size.height = os.height + oy;
self.position.top = op.top - oy;
self.position.left = op.left - ox;
}
}
});
var num = function(v) {
return parseInt(v, 10) || 0;
};
var isNumber = function(value) {
return !isNaN(parseInt(value, 10));
};
})(jQuery);
/*
* jQuery UI Selectable 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Selectables
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.selectable", $.extend({}, $.ui.mouse, {
_init: function() {
var self = this;
this.element.addClass("ui-selectable");
this.dragged = false;
// cache selectee children based on filter
var selectees;
this.refresh = function() {
selectees = $(self.options.filter, self.element[0]);
selectees.each(function() {
var $this = $(this);
var pos = $this.offset();
$.data(this, "selectable-item", {
element: this,
$element: $this,
left: pos.left,
top: pos.top,
right: pos.left + $this.outerWidth(),
bottom: pos.top + $this.outerHeight(),
startselected: false,
selected: $this.hasClass('ui-selected'),
selecting: $this.hasClass('ui-selecting'),
unselecting: $this.hasClass('ui-unselecting')
});
});
};
this.refresh();
this.selectees = selectees.addClass("ui-selectee");
this._mouseInit();
this.helper = $(document.createElement('div'))
.css({border:'1px dotted black'})
.addClass("ui-selectable-helper");
},
destroy: function() {
this.element
.removeClass("ui-selectable ui-selectable-disabled")
.removeData("selectable")
.unbind(".selectable");
this._mouseDestroy();
},
_mouseStart: function(event) {
var self = this;
this.opos = [event.pageX, event.pageY];
if (this.options.disabled)
return;
var options = this.options;
this.selectees = $(options.filter, this.element[0]);
this._trigger("start", event);
$(options.appendTo).append(this.helper);
// position helper (lasso)
this.helper.css({
"z-index": 100,
"position": "absolute",
"left": event.clientX,
"top": event.clientY,
"width": 0,
"height": 0
});
if (options.autoRefresh) {
this.refresh();
}
this.selectees.filter('.ui-selected').each(function() {
var selectee = $.data(this, "selectable-item");
selectee.startselected = true;
if (!event.metaKey) {
selectee.$element.removeClass('ui-selected');
selectee.selected = false;
selectee.$element.addClass('ui-unselecting');
selectee.unselecting = true;
// selectable UNSELECTING callback
self._trigger("unselecting", event, {
unselecting: selectee.element
});
}
});
$(event.target).parents().andSelf().each(function() {
var selectee = $.data(this, "selectable-item");
if (selectee) {
selectee.$element.removeClass("ui-unselecting").addClass('ui-selecting');
selectee.unselecting = false;
selectee.selecting = true;
selectee.selected = true;
// selectable SELECTING callback
self._trigger("selecting", event, {
selecting: selectee.element
});
return false;
}
});
},
_mouseDrag: function(event) {
var self = this;
this.dragged = true;
if (this.options.disabled)
return;
var options = this.options;
var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
this.selectees.each(function() {
var selectee = $.data(this, "selectable-item");
//prevent helper from being selected if appendTo: selectable
if (!selectee || selectee.element == self.element[0])
return;
var hit = false;
if (options.tolerance == 'touch') {
hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
} else if (options.tolerance == 'fit') {
hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
}
if (hit) {
// SELECT
if (selectee.selected) {
selectee.$element.removeClass('ui-selected');
selectee.selected = false;
}
if (selectee.unselecting) {
selectee.$element.removeClass('ui-unselecting');
selectee.unselecting = false;
}
if (!selectee.selecting) {
selectee.$element.addClass('ui-selecting');
selectee.selecting = true;
// selectable SELECTING callback
self._trigger("selecting", event, {
selecting: selectee.element
});
}
} else {
// UNSELECT
if (selectee.selecting) {
if (event.metaKey && selectee.startselected) {
selectee.$element.removeClass('ui-selecting');
selectee.selecting = false;
selectee.$element.addClass('ui-selected');
selectee.selected = true;
} else {
selectee.$element.removeClass('ui-selecting');
selectee.selecting = false;
if (selectee.startselected) {
selectee.$element.addClass('ui-unselecting');
selectee.unselecting = true;
}
// selectable UNSELECTING callback
self._trigger("unselecting", event, {
unselecting: selectee.element
});
}
}
if (selectee.selected) {
if (!event.metaKey && !selectee.startselected) {
selectee.$element.removeClass('ui-selected');
selectee.selected = false;
selectee.$element.addClass('ui-unselecting');
selectee.unselecting = true;
// selectable UNSELECTING callback
self._trigger("unselecting", event, {
unselecting: selectee.element
});
}
}
}
});
return false;
},
_mouseStop: function(event) {
var self = this;
this.dragged = false;
var options = this.options;
$('.ui-unselecting', this.element[0]).each(function() {
var selectee = $.data(this, "selectable-item");
selectee.$element.removeClass('ui-unselecting');
selectee.unselecting = false;
selectee.startselected = false;
self._trigger("unselected", event, {
unselected: selectee.element
});
});
$('.ui-selecting', this.element[0]).each(function() {
var selectee = $.data(this, "selectable-item");
selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
selectee.selecting = false;
selectee.selected = true;
selectee.startselected = true;
self._trigger("selected", event, {
selected: selectee.element
});
});
this._trigger("stop", event);
this.helper.remove();
return false;
}
}));
$.extend($.ui.selectable, {
version: "1.7.1",
defaults: {
appendTo: 'body',
autoRefresh: true,
cancel: ":input,option",
delay: 0,
distance: 0,
filter: '*',
tolerance: 'touch'
}
});
})(jQuery);
/*
* jQuery UI Sortable 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Sortables
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.sortable", $.extend({}, $.ui.mouse, {
_init: function() {
var o = this.options;
this.containerCache = {};
this.element.addClass("ui-sortable");
//Get the items
this.refresh();
//Let's determine if the items are floating
this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
//Let's determine the parent's offset
this.offset = this.element.offset();
//Initialize mouse events for interaction
this._mouseInit();
},
destroy: function() {
this.element
.removeClass("ui-sortable ui-sortable-disabled")
.removeData("sortable")
.unbind(".sortable");
this._mouseDestroy();
for ( var i = this.items.length - 1; i >= 0; i-- )
this.items[i].item.removeData("sortable-item");
},
_mouseCapture: function(event, overrideHandle) {
if (this.reverting) {
return false;
}
if(this.options.disabled || this.options.type == 'static') return false;
//We have to refresh the items data once first
this._refreshItems(event);
//Find out if the clicked node (or one of its parents) is a actual item in this.items
var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
if($.data(this, 'sortable-item') == self) {
currentItem = $(this);
return false;
}
});
if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
if(!currentItem) return false;
if(this.options.handle && !overrideHandle) {
var validHandle = false;
$(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
if(!validHandle) return false;
}
this.currentItem = currentItem;
this._removeCurrentsFromItems();
return true;
},
_mouseStart: function(event, overrideHandle, noActivation) {
var o = this.options, self = this;
this.currentContainer = this;
//We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
this.refreshPositions();
//Create and append the visible helper
this.helper = this._createHelper(event);
//Cache the helper size
this._cacheHelperProportions();
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Get the next scrolling parent
this.scrollParent = this.helper.scrollParent();
//The element's absolute position on the page minus margins
this.offset = this.currentItem.offset();
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
// Only after we got the offset, we can change the helper's position to absolute
// TODO: Still need to figure out a way to make relative sorting possible
this.helper.css("position", "absolute");
this.cssPosition = this.helper.css("position");
$.extend(this.offset, {
click: { //Where the click happened, relative to the element
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
},
parent: this._getParentOffset(),
relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
});
//Generate the original position
this.originalPosition = this._generatePosition(event);
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
//Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
if(o.cursorAt)
this._adjustOffsetFromHelper(o.cursorAt);
//Cache the former DOM position
this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
//If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
if(this.helper[0] != this.currentItem[0]) {
this.currentItem.hide();
}
//Create the placeholder
this._createPlaceholder();
//Set a containment if given in the options
if(o.containment)
this._setContainment();
if(o.cursor) { // cursor option
if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
$('body').css("cursor", o.cursor);
}
if(o.opacity) { // opacity option
if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
this.helper.css("opacity", o.opacity);
}
if(o.zIndex) { // zIndex option
if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
this.helper.css("zIndex", o.zIndex);
}
//Prepare scrolling
if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
this.overflowOffset = this.scrollParent.offset();
//Call callbacks
this._trigger("start", event, this._uiHash());
//Recache the helper size
if(!this._preserveHelperProportions)
this._cacheHelperProportions();
//Post 'activate' events to possible containers
if(!noActivation) {
for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
}
//Prepare possible droppables
if($.ui.ddmanager)
$.ui.ddmanager.current = this;
if ($.ui.ddmanager && !o.dropBehaviour)
$.ui.ddmanager.prepareOffsets(this, event);
this.dragging = true;
this.helper.addClass("ui-sortable-helper");
this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
return true;
},
_mouseDrag: function(event) {
//Compute the helpers position
this.position = this._generatePosition(event);
this.positionAbs = this._convertPositionTo("absolute");
if (!this.lastPositionAbs) {
this.lastPositionAbs = this.positionAbs;
}
//Do scrolling
if(this.options.scroll) {
var o = this.options, scrolled = false;
if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
} else {
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
}
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
$.ui.ddmanager.prepareOffsets(this, event);
}
//Regenerate the absolute position used for position checks
this.positionAbs = this._convertPositionTo("absolute");
//Set the helper position
if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
//Rearrange
for (var i = this.items.length - 1; i >= 0; i--) {
//Cache variables and intersection, continue if no intersection
var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
if (!intersection) continue;
if(itemElement != this.currentItem[0] //cannot intersect with itself
&& this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
&& !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
&& (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
) {
this.direction = intersection == 1 ? "down" : "up";
if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
this._rearrange(event, item);
} else {
break;
}
this._trigger("change", event, this._uiHash());
break;
}
}
//Post events to containers
this._contactContainers(event);
//Interconnect with droppables
if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
//Call callbacks
this._trigger('sort', event, this._uiHash());
this.lastPositionAbs = this.positionAbs;
return false;
},
_mouseStop: function(event, noPropagation) {
if(!event) return;
//If we are using droppables, inform the manager about the drop
if ($.ui.ddmanager && !this.options.dropBehaviour)
$.ui.ddmanager.drop(this, event);
if(this.options.revert) {
var self = this;
var cur = self.placeholder.offset();
self.reverting = true;
$(this.helper).animate({
left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
}, parseInt(this.options.revert, 10) || 500, function() {
self._clear(event);
});
} else {
this._clear(event, noPropagation);
}
return false;
},
cancel: function() {
var self = this;
if(this.dragging) {
this._mouseUp();
if(this.options.helper == "original")
this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
else
this.currentItem.show();
//Post deactivating events to containers
for (var i = this.containers.length - 1; i >= 0; i--){
this.containers[i]._trigger("deactivate", null, self._uiHash(this));
if(this.containers[i].containerCache.over) {
this.containers[i]._trigger("out", null, self._uiHash(this));
this.containers[i].containerCache.over = 0;
}
}
}
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
$.extend(this, {
helper: null,
dragging: false,
reverting: false,
_noFinalSort: null
});
if(this.domPosition.prev) {
$(this.domPosition.prev).after(this.currentItem);
} else {
$(this.domPosition.parent).prepend(this.currentItem);
}
return true;
},
serialize: function(o) {
var items = this._getItemsAsjQuery(o && o.connected);
var str = []; o = o || {};
$(items).each(function() {
var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
});
return str.join('&');
},
toArray: function(o) {
var items = this._getItemsAsjQuery(o && o.connected);
var ret = []; o = o || {};
items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
return ret;
},
/* Be careful with the following core functions */
_intersectsWith: function(item) {
var x1 = this.positionAbs.left,
x2 = x1 + this.helperProportions.width,
y1 = this.positionAbs.top,
y2 = y1 + this.helperProportions.height;
var l = item.left,
r = l + item.width,
t = item.top,
b = t + item.height;
var dyClick = this.offset.click.top,
dxClick = this.offset.click.left;
var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
if( this.options.tolerance == "pointer"
|| this.options.forcePointerForContainers
|| (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
) {
return isOverElement;
} else {
return (l < x1 + (this.helperProportions.width / 2) // Right Half
&& x2 - (this.helperProportions.width / 2) < r // Left Half
&& t < y1 + (this.helperProportions.height / 2) // Bottom Half
&& y2 - (this.helperProportions.height / 2) < b ); // Top Half
}
},
_intersectsWithPointer: function(item) {
var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
isOverElement = isOverElementHeight && isOverElementWidth,
verticalDirection = this._getDragVerticalDirection(),
horizontalDirection = this._getDragHorizontalDirection();
if (!isOverElement)
return false;
return this.floating ?
( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
: ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
},
_intersectsWithSides: function(item) {
var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
verticalDirection = this._getDragVerticalDirection(),
horizontalDirection = this._getDragHorizontalDirection();
if (this.floating && horizontalDirection) {
return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
} else {
return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
}
},
_getDragVerticalDirection: function() {
var delta = this.positionAbs.top - this.lastPositionAbs.top;
return delta != 0 && (delta > 0 ? "down" : "up");
},
_getDragHorizontalDirection: function() {
var delta = this.positionAbs.left - this.lastPositionAbs.left;
return delta != 0 && (delta > 0 ? "right" : "left");
},
refresh: function(event) {
this._refreshItems(event);
this.refreshPositions();
},
_connectWith: function() {
var options = this.options;
return options.connectWith.constructor == String
? [options.connectWith]
: options.connectWith;
},
_getItemsAsjQuery: function(connected) {
var self = this;
var items = [];
var queries = [];
var connectWith = this._connectWith();
if(connectWith && connected) {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
var inst = $.data(cur[j], 'sortable');
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper"), inst]);
}
};
};
}
queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper"), this]);
for (var i = queries.length - 1; i >= 0; i--){
queries[i][0].each(function() {
items.push(this);
});
};
return $(items);
},
_removeCurrentsFromItems: function() {
var list = this.currentItem.find(":data(sortable-item)");
for (var i=0; i < this.items.length; i++) {
for (var j=0; j < list.length; j++) {
if(list[j] == this.items[i].item[0])
this.items.splice(i,1);
};
};
},
_refreshItems: function(event) {
this.items = [];
this.containers = [this];
var items = this.items;
var self = this;
var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
var connectWith = this._connectWith();
if(connectWith) {
for (var i = connectWith.length - 1; i >= 0; i--){
var cur = $(connectWith[i]);
for (var j = cur.length - 1; j >= 0; j--){
var inst = $.data(cur[j], 'sortable');
if(inst && inst != this && !inst.options.disabled) {
queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
this.containers.push(inst);
}
};
};
}
for (var i = queries.length - 1; i >= 0; i--) {
var targetData = queries[i][1];
var _queries = queries[i][0];
for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
var item = $(_queries[j]);
item.data('sortable-item', targetData); // Data for target checking (mouse manager)
items.push({
item: item,
instance: targetData,
width: 0, height: 0,
left: 0, top: 0
});
};
};
},
refreshPositions: function(fast) {
//This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
if(this.offsetParent && this.helper) {
this.offset.parent = this._getParentOffset();
}
for (var i = this.items.length - 1; i >= 0; i--){
var item = this.items[i];
//We ignore calculating positions of all connected containers when we're not over them
if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
continue;
var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
if (!fast) {
item.width = t.outerWidth();
item.height = t.outerHeight();
}
var p = t.offset();
item.left = p.left;
item.top = p.top;
};
if(this.options.custom && this.options.custom.refreshContainers) {
this.options.custom.refreshContainers.call(this);
} else {
for (var i = this.containers.length - 1; i >= 0; i--){
var p = this.containers[i].element.offset();
this.containers[i].containerCache.left = p.left;
this.containers[i].containerCache.top = p.top;
this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
};
}
},
_createPlaceholder: function(that) {
var self = that || this, o = self.options;
if(!o.placeholder || o.placeholder.constructor == String) {
var className = o.placeholder;
o.placeholder = {
element: function() {
var el = $(document.createElement(self.currentItem[0].nodeName))
.addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
.removeClass("ui-sortable-helper")[0];
if(!className)
el.style.visibility = "hidden";
return el;
},
update: function(container, p) {
// 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
// 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
if(className && !o.forcePlaceholderSize) return;
//If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
}
};
}
//Create the placeholder
self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
//Append it after the actual current item
self.currentItem.after(self.placeholder);
//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
o.placeholder.update(self, self.placeholder);
},
_contactContainers: function(event) {
for (var i = this.containers.length - 1; i >= 0; i--){
if(this._intersectsWith(this.containers[i].containerCache)) {
if(!this.containers[i].containerCache.over) {
if(this.currentContainer != this.containers[i]) {
//When entering a new container, we will find the item with the least distance and append our item near it
var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[i].floating ? 'left' : 'top'];
for (var j = this.items.length - 1; j >= 0; j--) {
if(!$.ui.contains(this.containers[i].element[0], this.items[j].item[0])) continue;
var cur = this.items[j][this.containers[i].floating ? 'left' : 'top'];
if(Math.abs(cur - base) < dist) {
dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
}
}
if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
continue;
this.currentContainer = this.containers[i];
itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[i].element, true);
this._trigger("change", event, this._uiHash());
this.containers[i]._trigger("change", event, this._uiHash(this));
//Update the placeholder
this.options.placeholder.update(this.currentContainer, this.placeholder);
}
this.containers[i]._trigger("over", event, this._uiHash(this));
this.containers[i].containerCache.over = 1;
}
} else {
if(this.containers[i].containerCache.over) {
this.containers[i]._trigger("out", event, this._uiHash(this));
this.containers[i].containerCache.over = 0;
}
}
};
},
_createHelper: function(event) {
var o = this.options;
var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
$(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
if(helper[0] == this.currentItem[0])
this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
return helper;
},
_adjustOffsetFromHelper: function(obj) {
if(obj.left != undefined) this.offset.click.left = obj.left + this.margins.left;
if(obj.right != undefined) this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
if(obj.top != undefined) this.offset.click.top = obj.top + this.margins.top;
if(obj.bottom != undefined) this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
},
_getParentOffset: function() {
//Get the offsetParent and cache its position
this.offsetParent = this.helper.offsetParent();
var po = this.offsetParent.offset();
// This is a special case where we need to modify a offset calculated on start, since the following happened:
// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
// the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
po.left += this.scrollParent.scrollLeft();
po.top += this.scrollParent.scrollTop();
}
if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
|| (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
po = { top: 0, left: 0 };
return {
top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
};
},
_getRelativeOffset: function() {
if(this.cssPosition == "relative") {
var p = this.currentItem.position();
return {
top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
};
} else {
return { top: 0, left: 0 };
}
},
_cacheMargins: function() {
this.margins = {
left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
};
},
_cacheHelperProportions: function() {
this.helperProportions = {
width: this.helper.outerWidth(),
height: this.helper.outerHeight()
};
},
_setContainment: function() {
var o = this.options;
if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
if(o.containment == 'document' || o.containment == 'window') this.containment = [
0 - this.offset.relative.left - this.offset.parent.left,
0 - this.offset.relative.top - this.offset.parent.top,
$(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
];
if(!(/^(document|window|parent)$/).test(o.containment)) {
var ce = $(o.containment)[0];
var co = $(o.containment).offset();
var over = ($(ce).css("overflow") != 'hidden');
this.containment = [
co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
];
}
},
_convertPositionTo: function(d, pos) {
if(!pos) pos = this.position;
var mod = d == "absolute" ? 1 : -1;
var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
return {
top: (
pos.top // The absolute mouse position
+ this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
- ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
),
left: (
pos.left // The absolute mouse position
+ this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
- ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
)
};
},
_generatePosition: function(event) {
var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
// This is another very weird special case that only happens for relative elements:
// 1. If the css position is relative
// 2. and the scroll parent is the document or similar to the offset parent
// we have to refresh the relative offset during the scroll so there are no jumps
if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
this.offset.relative = this._getRelativeOffset();
}
var pageX = event.pageX;
var pageY = event.pageY;
/*
* - Position constraining -
* Constrain the position to a mix of grid, containment.
*/
if(this.originalPosition) { //If we are not dragging yet, we won't check for options
if(this.containment) {
if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
}
if(o.grid) {
var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
}
}
return {
top: (
pageY // The absolute mouse position
- this.offset.click.top // Click offset (relative to the element)
- this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.top // The offsetParent's offset without borders (offset + border)
+ ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
),
left: (
pageX // The absolute mouse position
- this.offset.click.left // Click offset (relative to the element)
- this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.left // The offsetParent's offset without borders (offset + border)
+ ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
)
};
},
_rearrange: function(event, i, a, hardRefresh) {
a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
//Various things done here to improve the performance:
// 1. we create a setTimeout, that calls refreshPositions
// 2. on the instance, we have a counter variable, that get's higher after every append
// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
// 4. this lets only the last addition to the timeout stack through
this.counter = this.counter ? ++this.counter : 1;
var self = this, counter = this.counter;
window.setTimeout(function() {
if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
},0);
},
_clear: function(event, noPropagation) {
this.reverting = false;
// We delay all events that have to be triggered to after the point where the placeholder has been removed and
// everything else normalized again
var delayedTriggers = [], self = this;
// We first have to update the dom position of the actual currentItem
// Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
if(!this._noFinalSort && this.currentItem[0].parentNode) this.placeholder.before(this.currentItem);
this._noFinalSort = null;
if(this.helper[0] == this.currentItem[0]) {
for(var i in this._storedCSS) {
if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
}
this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
} else {
this.currentItem.show();
}
if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
for (var i = this.containers.length - 1; i >= 0; i--){
if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) {
delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
}
};
};
//Post events to containers
for (var i = this.containers.length - 1; i >= 0; i--){
if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
if(this.containers[i].containerCache.over) {
delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
this.containers[i].containerCache.over = 0;
}
}
//Do what was originally in plugins
if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset cursor
if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
this.dragging = false;
if(this.cancelHelperRemoval) {
if(!noPropagation) {
this._trigger("beforeStop", event, this._uiHash());
for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
this._trigger("stop", event, this._uiHash());
}
return false;
}
if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
if(!noPropagation) {
for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
this._trigger("stop", event, this._uiHash());
}
this.fromOutside = false;
return true;
},
_trigger: function() {
if ($.widget.prototype._trigger.apply(this, arguments) === false) {
this.cancel();
}
},
_uiHash: function(inst) {
var self = inst || this;
return {
helper: self.helper,
placeholder: self.placeholder || $([]),
position: self.position,
absolutePosition: self.positionAbs, //deprecated
offset: self.positionAbs,
item: self.currentItem,
sender: inst ? inst.element : null
};
}
}));
$.extend($.ui.sortable, {
getter: "serialize toArray",
version: "1.7.1",
eventPrefix: "sort",
defaults: {
appendTo: "parent",
axis: false,
cancel: ":input,option",
connectWith: false,
containment: false,
cursor: 'auto',
cursorAt: false,
delay: 0,
distance: 1,
dropOnEmpty: true,
forcePlaceholderSize: false,
forceHelperSize: false,
grid: false,
handle: false,
helper: "original",
items: '> *',
opacity: false,
placeholder: false,
revert: false,
scroll: true,
scrollSensitivity: 20,
scrollSpeed: 20,
scope: "default",
tolerance: "intersect",
zIndex: 1000
}
});
})(jQuery);
/*
* jQuery UI Accordion 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Accordion
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.accordion", {
_init: function() {
var o = this.options, self = this;
this.running = 0;
// if the user set the alwaysOpen option on init
// then we need to set the collapsible option
// if they set both on init, collapsible will take priority
if (o.collapsible == $.ui.accordion.defaults.collapsible &&
o.alwaysOpen != $.ui.accordion.defaults.alwaysOpen) {
o.collapsible = !o.alwaysOpen;
}
if ( o.navigation ) {
var current = this.element.find("a").filter(o.navigationFilter);
if ( current.length ) {
if ( current.filter(o.header).length ) {
this.active = current;
} else {
this.active = current.parent().parent().prev();
current.addClass("ui-accordion-content-active");
}
}
}
this.element.addClass("ui-accordion ui-widget ui-helper-reset");
// in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fix
if (this.element[0].nodeName == "UL") {
this.element.children("li").addClass("ui-accordion-li-fix");
}
this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all")
.bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); })
.bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); })
.bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); })
.bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });
this.headers
.next()
.addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");
this.active.next().addClass('ui-accordion-content-active');
//Append icon elements
$("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);
this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);
// IE7-/Win - Extra vertical space in lists fixed
if ($.browser.msie) {
this.element.find('a').css('zoom', '1');
}
this.resize();
//ARIA
this.element.attr('role','tablist');
this.headers
.attr('role','tab')
.bind('keydown', function(event) { return self._keydown(event); })
.next()
.attr('role','tabpanel');
this.headers
.not(this.active || "")
.attr('aria-expanded','false')
.attr("tabIndex", "-1")
.next()
.hide();
// make sure at least one header is in the tab order
if (!this.active.length) {
this.headers.eq(0).attr('tabIndex','0');
} else {
this.active
.attr('aria-expanded','true')
.attr('tabIndex', '0');
}
// only need links in taborder for Safari
if (!$.browser.safari)
this.headers.find('a').attr('tabIndex','-1');
if (o.event) {
this.headers.bind((o.event) + ".accordion", function(event) { return self._clickHandler.call(self, event, this); });
}
},
destroy: function() {
var o = this.options;
this.element
.removeClass("ui-accordion ui-widget ui-helper-reset")
.removeAttr("role")
.unbind('.accordion')
.removeData('accordion');
this.headers
.unbind(".accordion")
.removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top")
.removeAttr("role").removeAttr("aria-expanded").removeAttr("tabindex");
this.headers.find("a").removeAttr("tabindex");
this.headers.children(".ui-icon").remove();
var contents = this.headers.next().css("display", "").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");
if (o.autoHeight || o.fillHeight) {
contents.css("height", "");
}
},
_setData: function(key, value) {
if(key == 'alwaysOpen') { key = 'collapsible'; value = !value; }
$.widget.prototype._setData.apply(this, arguments);
},
_keydown: function(event) {
var o = this.options, keyCode = $.ui.keyCode;
if (o.disabled || event.altKey || event.ctrlKey)
return;
var length = this.headers.length;
var currentIndex = this.headers.index(event.target);
var toFocus = false;
switch(event.keyCode) {
case keyCode.RIGHT:
case keyCode.DOWN:
toFocus = this.headers[(currentIndex + 1) % length];
break;
case keyCode.LEFT:
case keyCode.UP:
toFocus = this.headers[(currentIndex - 1 + length) % length];
break;
case keyCode.SPACE:
case keyCode.ENTER:
return this._clickHandler({ target: event.target }, event.target);
}
if (toFocus) {
$(event.target).attr('tabIndex','-1');
$(toFocus).attr('tabIndex','0');
toFocus.focus();
return false;
}
return true;
},
resize: function() {
var o = this.options, maxHeight;
if (o.fillSpace) {
if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }
maxHeight = this.element.parent().height();
if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }
this.headers.each(function() {
maxHeight -= $(this).outerHeight();
});
var maxPadding = 0;
this.headers.next().each(function() {
maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
}).height(Math.max(0, maxHeight - maxPadding))
.css('overflow', 'auto');
} else if ( o.autoHeight ) {
maxHeight = 0;
this.headers.next().each(function() {
maxHeight = Math.max(maxHeight, $(this).outerHeight());
}).height(maxHeight);
}
},
activate: function(index) {
// call clickHandler with custom event
var active = this._findActive(index)[0];
this._clickHandler({ target: active }, active);
},
_findActive: function(selector) {
return selector
? typeof selector == "number"
? this.headers.filter(":eq(" + selector + ")")
: this.headers.not(this.headers.not(selector))
: selector === false
? $([])
: this.headers.filter(":eq(0)");
},
_clickHandler: function(event, target) {
var o = this.options;
if (o.disabled) return false;
// called only when using activate(false) to close all parts programmatically
if (!event.target && o.collapsible) {
this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
.find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
this.active.next().addClass('ui-accordion-content-active');
var toHide = this.active.next(),
data = {
options: o,
newHeader: $([]),
oldHeader: o.active,
newContent: $([]),
oldContent: toHide
},
toShow = (this.active = $([]));
this._toggle(toShow, toHide, data);
return false;
}
// get the click target
var clicked = $(event.currentTarget || target);
var clickedIsActive = clicked[0] == this.active[0];
// if animations are still active, or the active header is the target, ignore click
if (this.running || (!o.collapsible && clickedIsActive)) {
return false;
}
// switch classes
this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
.find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
this.active.next().addClass('ui-accordion-content-active');
if (!clickedIsActive) {
clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top")
.find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);
clicked.next().addClass('ui-accordion-content-active');
}
// find elements to show and hide
var toShow = clicked.next(),
toHide = this.active.next(),
data = {
options: o,
newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,
oldHeader: this.active,
newContent: clickedIsActive && o.collapsible ? $([]) : toShow.find('> *'),
oldContent: toHide.find('> *')
},
down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
this.active = clickedIsActive ? $([]) : clicked;
this._toggle(toShow, toHide, data, clickedIsActive, down);
return false;
},
_toggle: function(toShow, toHide, data, clickedIsActive, down) {
var o = this.options, self = this;
this.toShow = toShow;
this.toHide = toHide;
this.data = data;
var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };
// trigger changestart event
this._trigger("changestart", null, this.data);
// count elements to animate
this.running = toHide.size() === 0 ? toShow.size() : toHide.size();
if (o.animated) {
var animOptions = {};
if ( o.collapsible && clickedIsActive ) {
animOptions = {
toShow: $([]),
toHide: toHide,
complete: complete,
down: down,
autoHeight: o.autoHeight || o.fillSpace
};
} else {
animOptions = {
toShow: toShow,
toHide: toHide,
complete: complete,
down: down,
autoHeight: o.autoHeight || o.fillSpace
};
}
if (!o.proxied) {
o.proxied = o.animated;
}
if (!o.proxiedDuration) {
o.proxiedDuration = o.duration;
}
o.animated = $.isFunction(o.proxied) ?
o.proxied(animOptions) : o.proxied;
o.duration = $.isFunction(o.proxiedDuration) ?
o.proxiedDuration(animOptions) : o.proxiedDuration;
var animations = $.ui.accordion.animations,
duration = o.duration,
easing = o.animated;
if (!animations[easing]) {
animations[easing] = function(options) {
this.slide(options, {
easing: easing,
duration: duration || 700
});
};
}
animations[easing](animOptions);
} else {
if (o.collapsible && clickedIsActive) {
toShow.toggle();
} else {
toHide.hide();
toShow.show();
}
complete(true);
}
toHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();
toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();
},
_completed: function(cancel) {
var o = this.options;
this.running = cancel ? 0 : --this.running;
if (this.running) return;
if (o.clearStyle) {
this.toShow.add(this.toHide).css({
height: "",
overflow: ""
});
}
this._trigger('change', null, this.data);
}
});
$.extend($.ui.accordion, {
version: "1.7.1",
defaults: {
active: null,
alwaysOpen: true, //deprecated, use collapsible
animated: 'slide',
autoHeight: true,
clearStyle: false,
collapsible: false,
event: "click",
fillSpace: false,
header: "> li > :first-child,> :not(li):even",
icons: {
header: "ui-icon-triangle-1-e",
headerSelected: "ui-icon-triangle-1-s"
},
navigation: false,
navigationFilter: function() {
return this.href.toLowerCase() == location.href.toLowerCase();
}
},
animations: {
slide: function(options, additions) {
options = $.extend({
easing: "swing",
duration: 300
}, options, additions);
if ( !options.toHide.size() ) {
options.toShow.animate({height: "show"}, options);
return;
}
if ( !options.toShow.size() ) {
options.toHide.animate({height: "hide"}, options);
return;
}
var overflow = options.toShow.css('overflow'),
percentDone,
showProps = {},
hideProps = {},
fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
originalWidth;
// fix width before calculating height of hidden element
var s = options.toShow;
originalWidth = s[0].style.width;
s.width( parseInt(s.parent().width(),10) - parseInt(s.css("paddingLeft"),10) - parseInt(s.css("paddingRight"),10) - (parseInt(s.css("borderLeftWidth"),10) || 0) - (parseInt(s.css("borderRightWidth"),10) || 0) );
$.each(fxAttrs, function(i, prop) {
hideProps[prop] = 'hide';
var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);
showProps[prop] = {
value: parts[1],
unit: parts[2] || 'px'
};
});
options.toShow.css({ height: 0, overflow: 'hidden' }).show();
options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{
step: function(now, settings) {
// only calculate the percent when animating height
// IE gets very inconsistent results when animating elements
// with small values, which is common for padding
if (settings.prop == 'height') {
percentDone = (settings.now - settings.start) / (settings.end - settings.start);
}
options.toShow[0].style[settings.prop] =
(percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;
},
duration: options.duration,
easing: options.easing,
complete: function() {
if ( !options.autoHeight ) {
options.toShow.css("height", "");
}
options.toShow.css("width", originalWidth);
options.toShow.css({overflow: overflow});
options.complete();
}
});
},
bounceslide: function(options) {
this.slide(options, {
easing: options.down ? "easeOutBounce" : "swing",
duration: options.down ? 1000 : 200
});
},
easeslide: function(options) {
this.slide(options, {
easing: "easeinout",
duration: 700
});
}
}
});
})(jQuery);
/*
* jQuery UI Dialog 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Dialog
*
* Depends:
* ui.core.js
* ui.draggable.js
* ui.resizable.js
*/
(function($) {
var setDataSwitch = {
dragStart: "start.draggable",
drag: "drag.draggable",
dragStop: "stop.draggable",
maxHeight: "maxHeight.resizable",
minHeight: "minHeight.resizable",
maxWidth: "maxWidth.resizable",
minWidth: "minWidth.resizable",
resizeStart: "start.resizable",
resize: "drag.resizable",
resizeStop: "stop.resizable"
},
uiDialogClasses =
'ui-dialog ' +
'ui-widget ' +
'ui-widget-content ' +
'ui-corner-all ';
$.widget("ui.dialog", {
_init: function() {
this.originalTitle = this.element.attr('title');
var self = this,
options = this.options,
title = options.title || this.originalTitle || '&nbsp;',
titleId = $.ui.dialog.getTitleId(this.element),
uiDialog = (this.uiDialog = $('<div/>'))
.appendTo(document.body)
.hide()
.addClass(uiDialogClasses + options.dialogClass)
.css({
position: 'absolute',
overflow: 'hidden',
zIndex: options.zIndex
})
// setting tabIndex makes the div focusable
// setting outline to 0 prevents a border on focus in Mozilla
.attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
(options.closeOnEscape && event.keyCode
&& event.keyCode == $.ui.keyCode.ESCAPE && self.close(event));
})
.attr({
role: 'dialog',
'aria-labelledby': titleId
})
.mousedown(function(event) {
self.moveToTop(false, event);
}),
uiDialogContent = this.element
.show()
.removeAttr('title')
.addClass(
'ui-dialog-content ' +
'ui-widget-content')
.appendTo(uiDialog),
uiDialogTitlebar = (this.uiDialogTitlebar = $('<div></div>'))
.addClass(
'ui-dialog-titlebar ' +
'ui-widget-header ' +
'ui-corner-all ' +
'ui-helper-clearfix'
)
.prependTo(uiDialog),
uiDialogTitlebarClose = $('<a href="#"/>')
.addClass(
'ui-dialog-titlebar-close ' +
'ui-corner-all'
)
.attr('role', 'button')
.hover(
function() {
uiDialogTitlebarClose.addClass('ui-state-hover');
},
function() {
uiDialogTitlebarClose.removeClass('ui-state-hover');
}
)
.focus(function() {
uiDialogTitlebarClose.addClass('ui-state-focus');
})
.blur(function() {
uiDialogTitlebarClose.removeClass('ui-state-focus');
})
.mousedown(function(ev) {
ev.stopPropagation();
})
.click(function(event) {
self.close(event);
return false;
})
.appendTo(uiDialogTitlebar),
uiDialogTitlebarCloseText = (this.uiDialogTitlebarCloseText = $('<span/>'))
.addClass(
'ui-icon ' +
'ui-icon-closethick'
)
.text(options.closeText)
.appendTo(uiDialogTitlebarClose),
uiDialogTitle = $('<span/>')
.addClass('ui-dialog-title')
.attr('id', titleId)
.html(title)
.prependTo(uiDialogTitlebar);
uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
(options.draggable && $.fn.draggable && this._makeDraggable());
(options.resizable && $.fn.resizable && this._makeResizable());
this._createButtons(options.buttons);
this._isOpen = false;
(options.bgiframe && $.fn.bgiframe && uiDialog.bgiframe());
(options.autoOpen && this.open());
},
destroy: function() {
(this.overlay && this.overlay.destroy());
this.uiDialog.hide();
this.element
.unbind('.dialog')
.removeData('dialog')
.removeClass('ui-dialog-content ui-widget-content')
.hide().appendTo('body');
this.uiDialog.remove();
(this.originalTitle && this.element.attr('title', this.originalTitle));
},
close: function(event) {
var self = this;
if (false === self._trigger('beforeclose', event)) {
return;
}
(self.overlay && self.overlay.destroy());
self.uiDialog.unbind('keypress.ui-dialog');
(self.options.hide
? self.uiDialog.hide(self.options.hide, function() {
self._trigger('close', event);
})
: self.uiDialog.hide() && self._trigger('close', event));
$.ui.dialog.overlay.resize();
self._isOpen = false;
},
isOpen: function() {
return this._isOpen;
},
// the force parameter allows us to move modal dialogs to their correct
// position on open
moveToTop: function(force, event) {
if ((this.options.modal && !force)
|| (!this.options.stack && !this.options.modal)) {
return this._trigger('focus', event);
}
if (this.options.zIndex > $.ui.dialog.maxZ) {
$.ui.dialog.maxZ = this.options.zIndex;
}
(this.overlay && this.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = ++$.ui.dialog.maxZ));
//Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
// http://ui.jquery.com/bugs/ticket/3193
var saveScroll = { scrollTop: this.element.attr('scrollTop'), scrollLeft: this.element.attr('scrollLeft') };
this.uiDialog.css('z-index', ++$.ui.dialog.maxZ);
this.element.attr(saveScroll);
this._trigger('focus', event);
},
open: function() {
if (this._isOpen) { return; }
var options = this.options,
uiDialog = this.uiDialog;
this.overlay = options.modal ? new $.ui.dialog.overlay(this) : null;
(uiDialog.next().length && uiDialog.appendTo('body'));
this._size();
this._position(options.position);
uiDialog.show(options.show);
this.moveToTop(true);
// prevent tabbing out of modal dialogs
(options.modal && uiDialog.bind('keypress.ui-dialog', function(event) {
if (event.keyCode != $.ui.keyCode.TAB) {
return;
}
var tabbables = $(':tabbable', this),
first = tabbables.filter(':first')[0],
last = tabbables.filter(':last')[0];
if (event.target == last && !event.shiftKey) {
setTimeout(function() {
first.focus();
}, 1);
} else if (event.target == first && event.shiftKey) {
setTimeout(function() {
last.focus();
}, 1);
}
}));
// set focus to the first tabbable element in the content area or the first button
// if there are no tabbable elements, set focus on the dialog itself
$([])
.add(uiDialog.find('.ui-dialog-content :tabbable:first'))
.add(uiDialog.find('.ui-dialog-buttonpane :tabbable:first'))
.add(uiDialog)
.filter(':first')
.focus();
this._trigger('open');
this._isOpen = true;
},
_createButtons: function(buttons) {
var self = this,
hasButtons = false,
uiDialogButtonPane = $('<div></div>')
.addClass(
'ui-dialog-buttonpane ' +
'ui-widget-content ' +
'ui-helper-clearfix'
);
// if we already have a button pane, remove it
this.uiDialog.find('.ui-dialog-buttonpane').remove();
(typeof buttons == 'object' && buttons !== null &&
$.each(buttons, function() { return !(hasButtons = true); }));
if (hasButtons) {
$.each(buttons, function(name, fn) {
$('<button type="button"></button>')
.addClass(
'ui-state-default ' +
'ui-corner-all'
)
.text(name)
.click(function() { fn.apply(self.element[0], arguments); })
.hover(
function() {
$(this).addClass('ui-state-hover');
},
function() {
$(this).removeClass('ui-state-hover');
}
)
.focus(function() {
$(this).addClass('ui-state-focus');
})
.blur(function() {
$(this).removeClass('ui-state-focus');
})
.appendTo(uiDialogButtonPane);
});
uiDialogButtonPane.appendTo(this.uiDialog);
}
},
_makeDraggable: function() {
var self = this,
options = this.options,
heightBeforeDrag;
this.uiDialog.draggable({
cancel: '.ui-dialog-content',
handle: '.ui-dialog-titlebar',
containment: 'document',
start: function() {
heightBeforeDrag = options.height;
$(this).height($(this).height()).addClass("ui-dialog-dragging");
(options.dragStart && options.dragStart.apply(self.element[0], arguments));
},
drag: function() {
(options.drag && options.drag.apply(self.element[0], arguments));
},
stop: function() {
$(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
(options.dragStop && options.dragStop.apply(self.element[0], arguments));
$.ui.dialog.overlay.resize();
}
});
},
_makeResizable: function(handles) {
handles = (handles === undefined ? this.options.resizable : handles);
var self = this,
options = this.options,
resizeHandles = typeof handles == 'string'
? handles
: 'n,e,s,w,se,sw,ne,nw';
this.uiDialog.resizable({
cancel: '.ui-dialog-content',
alsoResize: this.element,
maxWidth: options.maxWidth,
maxHeight: options.maxHeight,
minWidth: options.minWidth,
minHeight: options.minHeight,
start: function() {
$(this).addClass("ui-dialog-resizing");
(options.resizeStart && options.resizeStart.apply(self.element[0], arguments));
},
resize: function() {
(options.resize && options.resize.apply(self.element[0], arguments));
},
handles: resizeHandles,
stop: function() {
$(this).removeClass("ui-dialog-resizing");
options.height = $(this).height();
options.width = $(this).width();
(options.resizeStop && options.resizeStop.apply(self.element[0], arguments));
$.ui.dialog.overlay.resize();
}
})
.find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
},
_position: function(pos) {
var wnd = $(window), doc = $(document),
pTop = doc.scrollTop(), pLeft = doc.scrollLeft(),
minTop = pTop;
if ($.inArray(pos, ['center','top','right','bottom','left']) >= 0) {
pos = [
pos == 'right' || pos == 'left' ? pos : 'center',
pos == 'top' || pos == 'bottom' ? pos : 'middle'
];
}
if (pos.constructor != Array) {
pos = ['center', 'middle'];
}
if (pos[0].constructor == Number) {
pLeft += pos[0];
} else {
switch (pos[0]) {
case 'left':
pLeft += 0;
break;
case 'right':
pLeft += wnd.width() - this.uiDialog.outerWidth();
break;
default:
case 'center':
pLeft += (wnd.width() - this.uiDialog.outerWidth()) / 2;
}
}
if (pos[1].constructor == Number) {
pTop += pos[1];
} else {
switch (pos[1]) {
case 'top':
pTop += 0;
break;
case 'bottom':
pTop += wnd.height() - this.uiDialog.outerHeight();
break;
default:
case 'middle':
pTop += (wnd.height() - this.uiDialog.outerHeight()) / 2;
}
}
// prevent the dialog from being too high (make sure the titlebar
// is accessible)
pTop = Math.max(pTop, minTop);
this.uiDialog.css({top: pTop, left: pLeft});
},
_setData: function(key, value){
(setDataSwitch[key] && this.uiDialog.data(setDataSwitch[key], value));
switch (key) {
case "buttons":
this._createButtons(value);
break;
case "closeText":
this.uiDialogTitlebarCloseText.text(value);
break;
case "dialogClass":
this.uiDialog
.removeClass(this.options.dialogClass)
.addClass(uiDialogClasses + value);
break;
case "draggable":
(value
? this._makeDraggable()
: this.uiDialog.draggable('destroy'));
break;
case "height":
this.uiDialog.height(value);
break;
case "position":
this._position(value);
break;
case "resizable":
var uiDialog = this.uiDialog,
isResizable = this.uiDialog.is(':data(resizable)');
// currently resizable, becoming non-resizable
(isResizable && !value && uiDialog.resizable('destroy'));
// currently resizable, changing handles
(isResizable && typeof value == 'string' &&
uiDialog.resizable('option', 'handles', value));
// currently non-resizable, becoming resizable
(isResizable || this._makeResizable(value));
break;
case "title":
$(".ui-dialog-title", this.uiDialogTitlebar).html(value || '&nbsp;');
break;
case "width":
this.uiDialog.width(value);
break;
}
$.widget.prototype._setData.apply(this, arguments);
},
_size: function() {
/* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
* divs will both have width and height set, so we need to reset them
*/
var options = this.options;
// reset content sizing
this.element.css({
height: 0,
minHeight: 0,
width: 'auto'
});
// reset wrapper sizing
// determine the height of all the non-content elements
var nonContentHeight = this.uiDialog.css({
height: 'auto',
width: options.width
})
.height();
this.element
.css({
minHeight: Math.max(options.minHeight - nonContentHeight, 0),
height: options.height == 'auto'
? 'auto'
: Math.max(options.height - nonContentHeight, 0)
});
}
});
$.extend($.ui.dialog, {
version: "1.7.1",
defaults: {
autoOpen: true,
bgiframe: false,
buttons: {},
closeOnEscape: true,
closeText: 'close',
dialogClass: '',
draggable: true,
hide: null,
height: 'auto',
maxHeight: false,
maxWidth: false,
minHeight: 150,
minWidth: 150,
modal: false,
position: 'center',
resizable: true,
show: null,
stack: true,
title: '',
width: 300,
zIndex: 1000
},
getter: 'isOpen',
uuid: 0,
maxZ: 0,
getTitleId: function($el) {
return 'ui-dialog-title-' + ($el.attr('id') || ++this.uuid);
},
overlay: function(dialog) {
this.$el = $.ui.dialog.overlay.create(dialog);
}
});
$.extend($.ui.dialog.overlay, {
instances: [],
maxZ: 0,
events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
function(event) { return event + '.dialog-overlay'; }).join(' '),
create: function(dialog) {
if (this.instances.length === 0) {
// prevent use of anchors and inputs
// we use a setTimeout in case the overlay is created from an
// event that we're going to be cancelling (see #2804)
setTimeout(function() {
$(document).bind($.ui.dialog.overlay.events, function(event) {
var dialogZ = $(event.target).parents('.ui-dialog').css('zIndex') || 0;
return (dialogZ > $.ui.dialog.overlay.maxZ);
});
}, 1);
// allow closing by pressing the escape key
$(document).bind('keydown.dialog-overlay', function(event) {
(dialog.options.closeOnEscape && event.keyCode
&& event.keyCode == $.ui.keyCode.ESCAPE && dialog.close(event));
});
// handle window resize
$(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
}
var $el = $('<div></div>').appendTo(document.body)
.addClass('ui-widget-overlay').css({
width: this.width(),
height: this.height()
});
(dialog.options.bgiframe && $.fn.bgiframe && $el.bgiframe());
this.instances.push($el);
return $el;
},
destroy: function($el) {
this.instances.splice($.inArray(this.instances, $el), 1);
if (this.instances.length === 0) {
$([document, window]).unbind('.dialog-overlay');
}
$el.remove();
},
height: function() {
// handle IE 6
if ($.browser.msie && $.browser.version < 7) {
var scrollHeight = Math.max(
document.documentElement.scrollHeight,
document.body.scrollHeight
);
var offsetHeight = Math.max(
document.documentElement.offsetHeight,
document.body.offsetHeight
);
if (scrollHeight < offsetHeight) {
return $(window).height() + 'px';
} else {
return scrollHeight + 'px';
}
// handle "good" browsers
} else {
return $(document).height() + 'px';
}
},
width: function() {
// handle IE 6
if ($.browser.msie && $.browser.version < 7) {
var scrollWidth = Math.max(
document.documentElement.scrollWidth,
document.body.scrollWidth
);
var offsetWidth = Math.max(
document.documentElement.offsetWidth,
document.body.offsetWidth
);
if (scrollWidth < offsetWidth) {
return $(window).width() + 'px';
} else {
return scrollWidth + 'px';
}
// handle "good" browsers
} else {
return $(document).width() + 'px';
}
},
resize: function() {
/* If the dialog is draggable and the user drags it past the
* right edge of the window, the document becomes wider so we
* need to stretch the overlay. If the user then drags the
* dialog back to the left, the document will become narrower,
* so we need to shrink the overlay to the appropriate size.
* This is handled by shrinking the overlay before setting it
* to the full document size.
*/
var $overlays = $([]);
$.each($.ui.dialog.overlay.instances, function() {
$overlays = $overlays.add(this);
});
$overlays.css({
width: 0,
height: 0
}).css({
width: $.ui.dialog.overlay.width(),
height: $.ui.dialog.overlay.height()
});
}
});
$.extend($.ui.dialog.overlay.prototype, {
destroy: function() {
$.ui.dialog.overlay.destroy(this.$el);
}
});
})(jQuery);
/*
* jQuery UI Slider 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Slider
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.slider", $.extend({}, $.ui.mouse, {
_init: function() {
var self = this, o = this.options;
this._keySliding = false;
this._handleIndex = null;
this._detectOrientation();
this._mouseInit();
this.element
.addClass("ui-slider"
+ " ui-slider-" + this.orientation
+ " ui-widget"
+ " ui-widget-content"
+ " ui-corner-all");
this.range = $([]);
if (o.range) {
if (o.range === true) {
this.range = $('<div></div>');
if (!o.values) o.values = [this._valueMin(), this._valueMin()];
if (o.values.length && o.values.length != 2) {
o.values = [o.values[0], o.values[0]];
}
} else {
this.range = $('<div></div>');
}
this.range
.appendTo(this.element)
.addClass("ui-slider-range");
if (o.range == "min" || o.range == "max") {
this.range.addClass("ui-slider-range-" + o.range);
}
// note: this isn't the most fittingly semantic framework class for this element,
// but worked best visually with a variety of themes
this.range.addClass("ui-widget-header");
}
if ($(".ui-slider-handle", this.element).length == 0)
$('<a href="#"></a>')
.appendTo(this.element)
.addClass("ui-slider-handle");
if (o.values && o.values.length) {
while ($(".ui-slider-handle", this.element).length < o.values.length)
$('<a href="#"></a>')
.appendTo(this.element)
.addClass("ui-slider-handle");
}
this.handles = $(".ui-slider-handle", this.element)
.addClass("ui-state-default"
+ " ui-corner-all");
this.handle = this.handles.eq(0);
this.handles.add(this.range).filter("a")
.click(function(event) { event.preventDefault(); })
.hover(function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); })
.focus(function() { $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus'); })
.blur(function() { $(this).removeClass('ui-state-focus'); });
this.handles.each(function(i) {
$(this).data("index.ui-slider-handle", i);
});
this.handles.keydown(function(event) {
var ret = true;
var index = $(this).data("index.ui-slider-handle");
if (self.options.disabled)
return;
switch (event.keyCode) {
case $.ui.keyCode.HOME:
case $.ui.keyCode.END:
case $.ui.keyCode.UP:
case $.ui.keyCode.RIGHT:
case $.ui.keyCode.DOWN:
case $.ui.keyCode.LEFT:
ret = false;
if (!self._keySliding) {
self._keySliding = true;
$(this).addClass("ui-state-active");
self._start(event, index);
}
break;
}
var curVal, newVal, step = self._step();
if (self.options.values && self.options.values.length) {
curVal = newVal = self.values(index);
} else {
curVal = newVal = self.value();
}
switch (event.keyCode) {
case $.ui.keyCode.HOME:
newVal = self._valueMin();
break;
case $.ui.keyCode.END:
newVal = self._valueMax();
break;
case $.ui.keyCode.UP:
case $.ui.keyCode.RIGHT:
if(curVal == self._valueMax()) return;
newVal = curVal + step;
break;
case $.ui.keyCode.DOWN:
case $.ui.keyCode.LEFT:
if(curVal == self._valueMin()) return;
newVal = curVal - step;
break;
}
self._slide(event, index, newVal);
return ret;
}).keyup(function(event) {
var index = $(this).data("index.ui-slider-handle");
if (self._keySliding) {
self._stop(event, index);
self._change(event, index);
self._keySliding = false;
$(this).removeClass("ui-state-active");
}
});
this._refreshValue();
},
destroy: function() {
this.handles.remove();
this.range.remove();
this.element
.removeClass("ui-slider"
+ " ui-slider-horizontal"
+ " ui-slider-vertical"
+ " ui-slider-disabled"
+ " ui-widget"
+ " ui-widget-content"
+ " ui-corner-all")
.removeData("slider")
.unbind(".slider");
this._mouseDestroy();
},
_mouseCapture: function(event) {
var o = this.options;
if (o.disabled)
return false;
this.elementSize = {
width: this.element.outerWidth(),
height: this.element.outerHeight()
};
this.elementOffset = this.element.offset();
var position = { x: event.pageX, y: event.pageY };
var normValue = this._normValueFromMouse(position);
var distance = this._valueMax() - this._valueMin() + 1, closestHandle;
var self = this, index;
this.handles.each(function(i) {
var thisDistance = Math.abs(normValue - self.values(i));
if (distance > thisDistance) {
distance = thisDistance;
closestHandle = $(this);
index = i;
}
});
// workaround for bug #3736 (if both handles of a range are at 0,
// the first is always used as the one with least distance,
// and moving it is obviously prevented by preventing negative ranges)
if(o.range == true && this.values(1) == o.min) {
closestHandle = $(this.handles[++index]);
}
this._start(event, index);
self._handleIndex = index;
closestHandle
.addClass("ui-state-active")
.focus();
var offset = closestHandle.offset();
var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
left: event.pageX - offset.left - (closestHandle.width() / 2),
top: event.pageY - offset.top
- (closestHandle.height() / 2)
- (parseInt(closestHandle.css('borderTopWidth'),10) || 0)
- (parseInt(closestHandle.css('borderBottomWidth'),10) || 0)
+ (parseInt(closestHandle.css('marginTop'),10) || 0)
};
normValue = this._normValueFromMouse(position);
this._slide(event, index, normValue);
return true;
},
_mouseStart: function(event) {
return true;
},
_mouseDrag: function(event) {
var position = { x: event.pageX, y: event.pageY };
var normValue = this._normValueFromMouse(position);
this._slide(event, this._handleIndex, normValue);
return false;
},
_mouseStop: function(event) {
this.handles.removeClass("ui-state-active");
this._stop(event, this._handleIndex);
this._change(event, this._handleIndex);
this._handleIndex = null;
this._clickOffset = null;
return false;
},
_detectOrientation: function() {
this.orientation = this.options.orientation == 'vertical' ? 'vertical' : 'horizontal';
},
_normValueFromMouse: function(position) {
var pixelTotal, pixelMouse;
if ('horizontal' == this.orientation) {
pixelTotal = this.elementSize.width;
pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
} else {
pixelTotal = this.elementSize.height;
pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
}
var percentMouse = (pixelMouse / pixelTotal);
if (percentMouse > 1) percentMouse = 1;
if (percentMouse < 0) percentMouse = 0;
if ('vertical' == this.orientation)
percentMouse = 1 - percentMouse;
var valueTotal = this._valueMax() - this._valueMin(),
valueMouse = percentMouse * valueTotal,
valueMouseModStep = valueMouse % this.options.step,
normValue = this._valueMin() + valueMouse - valueMouseModStep;
if (valueMouseModStep > (this.options.step / 2))
normValue += this.options.step;
// Since JavaScript has problems with large floats, round
// the final value to 5 digits after the decimal point (see #4124)
return parseFloat(normValue.toFixed(5));
},
_start: function(event, index) {
var uiHash = {
handle: this.handles[index],
value: this.value()
};
if (this.options.values && this.options.values.length) {
uiHash.value = this.values(index)
uiHash.values = this.values()
}
this._trigger("start", event, uiHash);
},
_slide: function(event, index, newVal) {
var handle = this.handles[index];
if (this.options.values && this.options.values.length) {
var otherVal = this.values(index ? 0 : 1);
if ((index == 0 && newVal >= otherVal) || (index == 1 && newVal <= otherVal))
newVal = otherVal;
if (newVal != this.values(index)) {
var newValues = this.values();
newValues[index] = newVal;
// A slide can be canceled by returning false from the slide callback
var allowed = this._trigger("slide", event, {
handle: this.handles[index],
value: newVal,
values: newValues
});
var otherVal = this.values(index ? 0 : 1);
if (allowed !== false) {
this.values(index, newVal, ( event.type == 'mousedown' && this.options.animate ), true);
}
}
} else {
if (newVal != this.value()) {
// A slide can be canceled by returning false from the slide callback
var allowed = this._trigger("slide", event, {
handle: this.handles[index],
value: newVal
});
if (allowed !== false) {
this._setData('value', newVal, ( event.type == 'mousedown' && this.options.animate ));
}
}
}
},
_stop: function(event, index) {
var uiHash = {
handle: this.handles[index],
value: this.value()
};
if (this.options.values && this.options.values.length) {
uiHash.value = this.values(index)
uiHash.values = this.values()
}
this._trigger("stop", event, uiHash);
},
_change: function(event, index) {
var uiHash = {
handle: this.handles[index],
value: this.value()
};
if (this.options.values && this.options.values.length) {
uiHash.value = this.values(index)
uiHash.values = this.values()
}
this._trigger("change", event, uiHash);
},
value: function(newValue) {
if (arguments.length) {
this._setData("value", newValue);
this._change(null, 0);
}
return this._value();
},
values: function(index, newValue, animated, noPropagation) {
if (arguments.length > 1) {
this.options.values[index] = newValue;
this._refreshValue(animated);
if(!noPropagation) this._change(null, index);
}
if (arguments.length) {
if (this.options.values && this.options.values.length) {
return this._values(index);
} else {
return this.value();
}
} else {
return this._values();
}
},
_setData: function(key, value, animated) {
$.widget.prototype._setData.apply(this, arguments);
switch (key) {
case 'orientation':
this._detectOrientation();
this.element
.removeClass("ui-slider-horizontal ui-slider-vertical")
.addClass("ui-slider-" + this.orientation);
this._refreshValue(animated);
break;
case 'value':
this._refreshValue(animated);
break;
}
},
_step: function() {
var step = this.options.step;
return step;
},
_value: function() {
var val = this.options.value;
if (val < this._valueMin()) val = this._valueMin();
if (val > this._valueMax()) val = this._valueMax();
return val;
},
_values: function(index) {
if (arguments.length) {
var val = this.options.values[index];
if (val < this._valueMin()) val = this._valueMin();
if (val > this._valueMax()) val = this._valueMax();
return val;
} else {
return this.options.values;
}
},
_valueMin: function() {
var valueMin = this.options.min;
return valueMin;
},
_valueMax: function() {
var valueMax = this.options.max;
return valueMax;
},
_refreshValue: function(animate) {
var oRange = this.options.range, o = this.options, self = this;
if (this.options.values && this.options.values.length) {
var vp0, vp1;
this.handles.each(function(i, j) {
var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
$(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
if (self.options.range === true) {
if (self.orientation == 'horizontal') {
(i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
(i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
} else {
(i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
(i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
}
}
lastValPercent = valPercent;
});
} else {
var value = this.value(),
valueMin = this._valueMin(),
valueMax = this._valueMax(),
valPercent = valueMax != valueMin
? (value - valueMin) / (valueMax - valueMin) * 100
: 0;
var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
(oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ width: valPercent + '%' }, o.animate);
(oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
(oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ height: valPercent + '%' }, o.animate);
(oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
}
}
}));
$.extend($.ui.slider, {
getter: "value values",
version: "1.7.1",
eventPrefix: "slide",
defaults: {
animate: false,
delay: 0,
distance: 0,
max: 100,
min: 0,
orientation: 'horizontal',
range: false,
step: 1,
value: 0,
values: null
}
});
})(jQuery);
/*
* jQuery UI Tabs 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Tabs
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.tabs", {
_init: function() {
if (this.options.deselectable !== undefined) {
this.options.collapsible = this.options.deselectable;
}
this._tabify(true);
},
_setData: function(key, value) {
if (key == 'selected') {
if (this.options.collapsible && value == this.options.selected) {
return;
}
this.select(value);
}
else {
this.options[key] = value;
if (key == 'deselectable') {
this.options.collapsible = value;
}
this._tabify();
}
},
_tabId: function(a) {
return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '') ||
this.options.idPrefix + $.data(a);
},
_sanitizeSelector: function(hash) {
return hash.replace(/:/g, '\\:'); // we need this because an id may contain a ":"
},
_cookie: function() {
var cookie = this.cookie || (this.cookie = this.options.cookie.name || 'ui-tabs-' + $.data(this.list[0]));
return $.cookie.apply(null, [cookie].concat($.makeArray(arguments)));
},
_ui: function(tab, panel) {
return {
tab: tab,
panel: panel,
index: this.anchors.index(tab)
};
},
_cleanup: function() {
// restore all former loading tabs labels
this.lis.filter('.ui-state-processing').removeClass('ui-state-processing')
.find('span:data(label.tabs)')
.each(function() {
var el = $(this);
el.html(el.data('label.tabs')).removeData('label.tabs');
});
},
_tabify: function(init) {
this.list = this.element.children('ul:first');
this.lis = $('li:has(a[href])', this.list);
this.anchors = this.lis.map(function() { return $('a', this)[0]; });
this.panels = $([]);
var self = this, o = this.options;
var fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
this.anchors.each(function(i, a) {
var href = $(a).attr('href');
// For dynamically created HTML that contains a hash as href IE < 8 expands
// such href to the full page url with hash and then misinterprets tab as ajax.
// Same consideration applies for an added tab with a fragment identifier
// since a[href=#fragment-identifier] does unexpectedly not match.
// Thus normalize href attribute...
var hrefBase = href.split('#')[0], baseEl;
if (hrefBase && (hrefBase === location.toString().split('#')[0] ||
(baseEl = $('base')[0]) && hrefBase === baseEl.href)) {
href = a.hash;
a.href = href;
}
// inline tab
if (fragmentId.test(href)) {
self.panels = self.panels.add(self._sanitizeSelector(href));
}
// remote tab
else if (href != '#') { // prevent loading the page itself if href is just "#"
$.data(a, 'href.tabs', href); // required for restore on destroy
// TODO until #3808 is fixed strip fragment identifier from url
// (IE fails to load from such url)
$.data(a, 'load.tabs', href.replace(/#.*$/, '')); // mutable data
var id = self._tabId(a);
a.href = '#' + id;
var $panel = $('#' + id);
if (!$panel.length) {
$panel = $(o.panelTemplate).attr('id', id).addClass('ui-tabs-panel ui-widget-content ui-corner-bottom')
.insertAfter(self.panels[i - 1] || self.list);
$panel.data('destroy.tabs', true);
}
self.panels = self.panels.add($panel);
}
// invalid tab href
else {
o.disabled.push(i);
}
});
// initialization from scratch
if (init) {
// attach necessary classes for styling
this.element.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all');
this.list.addClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all');
this.lis.addClass('ui-state-default ui-corner-top');
this.panels.addClass('ui-tabs-panel ui-widget-content ui-corner-bottom');
// Selected tab
// use "selected" option or try to retrieve:
// 1. from fragment identifier in url
// 2. from cookie
// 3. from selected class attribute on <li>
if (o.selected === undefined) {
if (location.hash) {
this.anchors.each(function(i, a) {
if (a.hash == location.hash) {
o.selected = i;
return false; // break
}
});
}
if (typeof o.selected != 'number' && o.cookie) {
o.selected = parseInt(self._cookie(), 10);
}
if (typeof o.selected != 'number' && this.lis.filter('.ui-tabs-selected').length) {
o.selected = this.lis.index(this.lis.filter('.ui-tabs-selected'));
}
o.selected = o.selected || 0;
}
else if (o.selected === null) { // usage of null is deprecated, TODO remove in next release
o.selected = -1;
}
// sanity check - default to first tab...
o.selected = ((o.selected >= 0 && this.anchors[o.selected]) || o.selected < 0) ? o.selected : 0;
// Take disabling tabs via class attribute from HTML
// into account and update option properly.
// A selected tab cannot become disabled.
o.disabled = $.unique(o.disabled.concat(
$.map(this.lis.filter('.ui-state-disabled'),
function(n, i) { return self.lis.index(n); } )
)).sort();
if ($.inArray(o.selected, o.disabled) != -1) {
o.disabled.splice($.inArray(o.selected, o.disabled), 1);
}
// highlight selected tab
this.panels.addClass('ui-tabs-hide');
this.lis.removeClass('ui-tabs-selected ui-state-active');
if (o.selected >= 0 && this.anchors.length) { // check for length avoids error when initializing empty list
this.panels.eq(o.selected).removeClass('ui-tabs-hide');
this.lis.eq(o.selected).addClass('ui-tabs-selected ui-state-active');
// seems to be expected behavior that the show callback is fired
self.element.queue("tabs", function() {
self._trigger('show', null, self._ui(self.anchors[o.selected], self.panels[o.selected]));
});
this.load(o.selected);
}
// clean up to avoid memory leaks in certain versions of IE 6
$(window).bind('unload', function() {
self.lis.add(self.anchors).unbind('.tabs');
self.lis = self.anchors = self.panels = null;
});
}
// update selected after add/remove
else {
o.selected = this.lis.index(this.lis.filter('.ui-tabs-selected'));
}
// update collapsible
this.element[o.collapsible ? 'addClass' : 'removeClass']('ui-tabs-collapsible');
// set or update cookie after init and add/remove respectively
if (o.cookie) {
this._cookie(o.selected, o.cookie);
}
// disable tabs
for (var i = 0, li; (li = this.lis[i]); i++) {
$(li)[$.inArray(i, o.disabled) != -1 &&
!$(li).hasClass('ui-tabs-selected') ? 'addClass' : 'removeClass']('ui-state-disabled');
}
// reset cache if switching from cached to not cached
if (o.cache === false) {
this.anchors.removeData('cache.tabs');
}
// remove all handlers before, tabify may run on existing tabs after add or option change
this.lis.add(this.anchors).unbind('.tabs');
if (o.event != 'mouseover') {
var addState = function(state, el) {
if (el.is(':not(.ui-state-disabled)')) {
el.addClass('ui-state-' + state);
}
};
var removeState = function(state, el) {
el.removeClass('ui-state-' + state);
};
this.lis.bind('mouseover.tabs', function() {
addState('hover', $(this));
});
this.lis.bind('mouseout.tabs', function() {
removeState('hover', $(this));
});
this.anchors.bind('focus.tabs', function() {
addState('focus', $(this).closest('li'));
});
this.anchors.bind('blur.tabs', function() {
removeState('focus', $(this).closest('li'));
});
}
// set up animations
var hideFx, showFx;
if (o.fx) {
if ($.isArray(o.fx)) {
hideFx = o.fx[0];
showFx = o.fx[1];
}
else {
hideFx = showFx = o.fx;
}
}
// Reset certain styles left over from animation
// and prevent IE's ClearType bug...
function resetStyle($el, fx) {
$el.css({ display: '' });
if ($.browser.msie && fx.opacity) {
$el[0].style.removeAttribute('filter');
}
}
// Show a tab...
var showTab = showFx ?
function(clicked, $show) {
$(clicked).closest('li').removeClass('ui-state-default').addClass('ui-tabs-selected ui-state-active');
$show.hide().removeClass('ui-tabs-hide') // avoid flicker that way
.animate(showFx, showFx.duration || 'normal', function() {
resetStyle($show, showFx);
self._trigger('show', null, self._ui(clicked, $show[0]));
});
} :
function(clicked, $show) {
$(clicked).closest('li').removeClass('ui-state-default').addClass('ui-tabs-selected ui-state-active');
$show.removeClass('ui-tabs-hide');
self._trigger('show', null, self._ui(clicked, $show[0]));
};
// Hide a tab, $show is optional...
var hideTab = hideFx ?
function(clicked, $hide) {
$hide.animate(hideFx, hideFx.duration || 'normal', function() {
self.lis.removeClass('ui-tabs-selected ui-state-active').addClass('ui-state-default');
$hide.addClass('ui-tabs-hide');
resetStyle($hide, hideFx);
self.element.dequeue("tabs");
});
} :
function(clicked, $hide, $show) {
self.lis.removeClass('ui-tabs-selected ui-state-active').addClass('ui-state-default');
$hide.addClass('ui-tabs-hide');
self.element.dequeue("tabs");
};
// attach tab event handler, unbind to avoid duplicates from former tabifying...
this.anchors.bind(o.event + '.tabs', function() {
var el = this, $li = $(this).closest('li'), $hide = self.panels.filter(':not(.ui-tabs-hide)'),
$show = $(self._sanitizeSelector(this.hash));
// If tab is already selected and not collapsible or tab disabled or
// or is already loading or click callback returns false stop here.
// Check if click handler returns false last so that it is not executed
// for a disabled or loading tab!
if (($li.hasClass('ui-tabs-selected') && !o.collapsible) ||
$li.hasClass('ui-state-disabled') ||
$li.hasClass('ui-state-processing') ||
self._trigger('select', null, self._ui(this, $show[0])) === false) {
this.blur();
return false;
}
o.selected = self.anchors.index(this);
self.abort();
// if tab may be closed
if (o.collapsible) {
if ($li.hasClass('ui-tabs-selected')) {
o.selected = -1;
if (o.cookie) {
self._cookie(o.selected, o.cookie);
}
self.element.queue("tabs", function() {
hideTab(el, $hide);
}).dequeue("tabs");
this.blur();
return false;
}
else if (!$hide.length) {
if (o.cookie) {
self._cookie(o.selected, o.cookie);
}
self.element.queue("tabs", function() {
showTab(el, $show);
});
self.load(self.anchors.index(this)); // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
this.blur();
return false;
}
}
if (o.cookie) {
self._cookie(o.selected, o.cookie);
}
// show new tab
if ($show.length) {
if ($hide.length) {
self.element.queue("tabs", function() {
hideTab(el, $hide);
});
}
self.element.queue("tabs", function() {
showTab(el, $show);
});
self.load(self.anchors.index(this));
}
else {
throw 'jQuery UI Tabs: Mismatching fragment identifier.';
}
// Prevent IE from keeping other link focussed when using the back button
// and remove dotted border from clicked link. This is controlled via CSS
// in modern browsers; blur() removes focus from address bar in Firefox
// which can become a usability and annoying problem with tabs('rotate').
if ($.browser.msie) {
this.blur();
}
});
// disable click in any case
this.anchors.bind('click.tabs', function(){return false;});
},
destroy: function() {
var o = this.options;
this.abort();
this.element.unbind('.tabs')
.removeClass('ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible')
.removeData('tabs');
this.list.removeClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all');
this.anchors.each(function() {
var href = $.data(this, 'href.tabs');
if (href) {
this.href = href;
}
var $this = $(this).unbind('.tabs');
$.each(['href', 'load', 'cache'], function(i, prefix) {
$this.removeData(prefix + '.tabs');
});
});
this.lis.unbind('.tabs').add(this.panels).each(function() {
if ($.data(this, 'destroy.tabs')) {
$(this).remove();
}
else {
$(this).removeClass([
'ui-state-default',
'ui-corner-top',
'ui-tabs-selected',
'ui-state-active',
'ui-state-hover',
'ui-state-focus',
'ui-state-disabled',
'ui-tabs-panel',
'ui-widget-content',
'ui-corner-bottom',
'ui-tabs-hide'
].join(' '));
}
});
if (o.cookie) {
this._cookie(null, o.cookie);
}
},
add: function(url, label, index) {
if (index === undefined) {
index = this.anchors.length; // append by default
}
var self = this, o = this.options,
$li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label)),
id = !url.indexOf('#') ? url.replace('#', '') : this._tabId($('a', $li)[0]);
$li.addClass('ui-state-default ui-corner-top').data('destroy.tabs', true);
// try to find an existing element before creating a new one
var $panel = $('#' + id);
if (!$panel.length) {
$panel = $(o.panelTemplate).attr('id', id).data('destroy.tabs', true);
}
$panel.addClass('ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide');
if (index >= this.lis.length) {
$li.appendTo(this.list);
$panel.appendTo(this.list[0].parentNode);
}
else {
$li.insertBefore(this.lis[index]);
$panel.insertBefore(this.panels[index]);
}
o.disabled = $.map(o.disabled,
function(n, i) { return n >= index ? ++n : n; });
this._tabify();
if (this.anchors.length == 1) { // after tabify
$li.addClass('ui-tabs-selected ui-state-active');
$panel.removeClass('ui-tabs-hide');
this.element.queue("tabs", function() {
self._trigger('show', null, self._ui(self.anchors[0], self.panels[0]));
});
this.load(0);
}
// callback
this._trigger('add', null, this._ui(this.anchors[index], this.panels[index]));
},
remove: function(index) {
var o = this.options, $li = this.lis.eq(index).remove(),
$panel = this.panels.eq(index).remove();
// If selected tab was removed focus tab to the right or
// in case the last tab was removed the tab to the left.
if ($li.hasClass('ui-tabs-selected') && this.anchors.length > 1) {
this.select(index + (index + 1 < this.anchors.length ? 1 : -1));
}
o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
function(n, i) { return n >= index ? --n : n; });
this._tabify();
// callback
this._trigger('remove', null, this._ui($li.find('a')[0], $panel[0]));
},
enable: function(index) {
var o = this.options;
if ($.inArray(index, o.disabled) == -1) {
return;
}
this.lis.eq(index).removeClass('ui-state-disabled');
o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
// callback
this._trigger('enable', null, this._ui(this.anchors[index], this.panels[index]));
},
disable: function(index) {
var self = this, o = this.options;
if (index != o.selected) { // cannot disable already selected tab
this.lis.eq(index).addClass('ui-state-disabled');
o.disabled.push(index);
o.disabled.sort();
// callback
this._trigger('disable', null, this._ui(this.anchors[index], this.panels[index]));
}
},
select: function(index) {
if (typeof index == 'string') {
index = this.anchors.index(this.anchors.filter('[href$=' + index + ']'));
}
else if (index === null) { // usage of null is deprecated, TODO remove in next release
index = -1;
}
if (index == -1 && this.options.collapsible) {
index = this.options.selected;
}
this.anchors.eq(index).trigger(this.options.event + '.tabs');
},
load: function(index) {
var self = this, o = this.options, a = this.anchors.eq(index)[0], url = $.data(a, 'load.tabs');
this.abort();
// not remote or from cache
if (!url || this.element.queue("tabs").length !== 0 && $.data(a, 'cache.tabs')) {
this.element.dequeue("tabs");
return;
}
// load remote from here on
this.lis.eq(index).addClass('ui-state-processing');
if (o.spinner) {
var span = $('span', a);
span.data('label.tabs', span.html()).html(o.spinner);
}
this.xhr = $.ajax($.extend({}, o.ajaxOptions, {
url: url,
success: function(r, s) {
$(self._sanitizeSelector(a.hash)).html(r);
// take care of tab labels
self._cleanup();
if (o.cache) {
$.data(a, 'cache.tabs', true); // if loaded once do not load them again
}
// callbacks
self._trigger('load', null, self._ui(self.anchors[index], self.panels[index]));
try {
o.ajaxOptions.success(r, s);
}
catch (e) {}
// last, so that load event is fired before show...
self.element.dequeue("tabs");
}
}));
},
abort: function() {
// stop possibly running animations
this.element.queue([]);
this.panels.stop(false, true);
// terminate pending requests from other tabs
if (this.xhr) {
this.xhr.abort();
delete this.xhr;
}
// take care of tab labels
this._cleanup();
},
url: function(index, url) {
this.anchors.eq(index).removeData('cache.tabs').data('load.tabs', url);
},
length: function() {
return this.anchors.length;
}
});
$.extend($.ui.tabs, {
version: '1.7.1',
getter: 'length',
defaults: {
ajaxOptions: null,
cache: false,
cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
collapsible: false,
disabled: [],
event: 'click',
fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
idPrefix: 'ui-tabs-',
panelTemplate: '<div></div>',
spinner: '<em>Loading&#8230;</em>',
tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>'
}
});
/*
* Tabs Extensions
*/
/*
* Rotate
*/
$.extend($.ui.tabs.prototype, {
rotation: null,
rotate: function(ms, continuing) {
var self = this, o = this.options;
var rotate = self._rotate || (self._rotate = function(e) {
clearTimeout(self.rotation);
self.rotation = setTimeout(function() {
var t = o.selected;
self.select( ++t < self.anchors.length ? t : 0 );
}, ms);
if (e) {
e.stopPropagation();
}
});
var stop = self._unrotate || (self._unrotate = !continuing ?
function(e) {
if (e.clientX) { // in case of a true click
self.rotate(null);
}
} :
function(e) {
t = o.selected;
rotate();
});
// start rotation
if (ms) {
this.element.bind('tabsshow', rotate);
this.anchors.bind(o.event + '.tabs', stop);
rotate();
}
// stop rotation
else {
clearTimeout(self.rotation);
this.element.unbind('tabsshow', rotate);
this.anchors.unbind(o.event + '.tabs', stop);
delete this._rotate;
delete this._unrotate;
}
}
});
})(jQuery);
/*
* jQuery UI Datepicker 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Datepicker
*
* Depends:
* ui.core.js
*/
(function($) { // hide the namespace
$.extend($.ui, { datepicker: { version: "1.7.1" } });
var PROP_NAME = 'datepicker';
/* Date picker manager.
Use the singleton instance of this class, $.datepicker, to interact with the date picker.
Settings for (groups of) date pickers are maintained in an instance object,
allowing multiple different settings on the same page. */
function Datepicker() {
this.debug = false; // Change this to true to start debugging
this._curInst = null; // The current instance in use
this._keyEvent = false; // If the last event was a key event
this._disabledInputs = []; // List of date picker inputs that have been disabled
this._datepickerShowing = false; // True if the popup picker is showing , false if not
this._inDialog = false; // True if showing within a "dialog", false if not
this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
this.regional = []; // Available regional settings, indexed by language code
this.regional[''] = { // Default regional settings
closeText: 'Done', // Display text for close link
prevText: 'Prev', // Display text for previous month link
nextText: 'Next', // Display text for next month link
currentText: 'Today', // Display text for current month link
monthNames: ['January','February','March','April','May','June',
'July','August','September','October','November','December'], // Names of months for drop-down and formatting
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
dateFormat: 'mm/dd/yy', // See format options on parseDate
firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
isRTL: false // True if right-to-left language, false if left-to-right
};
this._defaults = { // Global defaults for all the date picker instances
showOn: 'focus', // 'focus' for popup on focus,
// 'button' for trigger button, or 'both' for either
showAnim: 'show', // Name of jQuery animation for popup
showOptions: {}, // Options for enhanced animations
defaultDate: null, // Used when field is blank: actual date,
// +/-number for offset from today, null for today
appendText: '', // Display text following the input box, e.g. showing the format
buttonText: '...', // Text for trigger button
buttonImage: '', // URL for trigger button image
buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
hideIfNoPrevNext: false, // True to hide next/previous month links
// if not applicable, false to just disable them
navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
gotoCurrent: false, // True if today link goes back to current selection instead
changeMonth: false, // True if month can be selected directly, false if only prev/next
changeYear: false, // True if year can be selected directly, false if only prev/next
showMonthAfterYear: false, // True if the year select precedes month, false for month then year
yearRange: '-10:+10', // Range of years to display in drop-down,
// either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
showOtherMonths: false, // True to show dates in other months, false to leave blank
calculateWeek: this.iso8601Week, // How to calculate the week of the year,
// takes a Date and returns the number of the week for it
shortYearCutoff: '+10', // Short year values < this are in the current century,
// > this are in the previous century,
// string value starting with '+' for current year + value
minDate: null, // The earliest selectable date, or null for no limit
maxDate: null, // The latest selectable date, or null for no limit
duration: 'normal', // Duration of display/closure
beforeShowDay: null, // Function that takes a date and returns an array with
// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
// [2] = cell title (optional), e.g. $.datepicker.noWeekends
beforeShow: null, // Function that takes an input field and
// returns a set of custom settings for the date picker
onSelect: null, // Define a callback function when a date is selected
onChangeMonthYear: null, // Define a callback function when the month or year is changed
onClose: null, // Define a callback function when the datepicker is closed
numberOfMonths: 1, // Number of months to show at a time
showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
stepMonths: 1, // Number of months to step back/forward
stepBigMonths: 12, // Number of months to step back/forward for the big links
altField: '', // Selector for an alternate field to store selected dates into
altFormat: '', // The date format to use for the alternate field
constrainInput: true, // The input is constrained by the current date format
showButtonPanel: false // True to show button panel, false to not show it
};
$.extend(this._defaults, this.regional['']);
this.dpDiv = $('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible"></div>');
}
$.extend(Datepicker.prototype, {
/* Class name added to elements to indicate already configured with a date picker. */
markerClassName: 'hasDatepicker',
/* Debug logging (if enabled). */
log: function () {
if (this.debug)
console.log.apply('', arguments);
},
/* Override the default settings for all instances of the date picker.
@param settings object - the new settings to use as defaults (anonymous object)
@return the manager object */
setDefaults: function(settings) {
extendRemove(this._defaults, settings || {});
return this;
},
/* Attach the date picker to a jQuery selection.
@param target element - the target input field or division or span
@param settings object - the new settings to use for this date picker instance (anonymous) */
_attachDatepicker: function(target, settings) {
// check for settings on the control itself - in namespace 'date:'
var inlineSettings = null;
for (var attrName in this._defaults) {
var attrValue = target.getAttribute('date:' + attrName);
if (attrValue) {
inlineSettings = inlineSettings || {};
try {
inlineSettings[attrName] = eval(attrValue);
} catch (err) {
inlineSettings[attrName] = attrValue;
}
}
}
var nodeName = target.nodeName.toLowerCase();
var inline = (nodeName == 'div' || nodeName == 'span');
if (!target.id)
target.id = 'dp' + (++this.uuid);
var inst = this._newInst($(target), inline);
inst.settings = $.extend({}, settings || {}, inlineSettings || {});
if (nodeName == 'input') {
this._connectDatepicker(target, inst);
} else if (inline) {
this._inlineDatepicker(target, inst);
}
},
/* Create a new instance object. */
_newInst: function(target, inline) {
var id = target[0].id.replace(/([:\[\]\.])/g, '\\\\$1'); // escape jQuery meta chars
return {id: id, input: target, // associated target
selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
drawMonth: 0, drawYear: 0, // month being drawn
inline: inline, // is datepicker inline or not
dpDiv: (!inline ? this.dpDiv : // presentation div
$('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))};
},
/* Attach the date picker to an input field. */
_connectDatepicker: function(target, inst) {
var input = $(target);
inst.trigger = $([]);
if (input.hasClass(this.markerClassName))
return;
var appendText = this._get(inst, 'appendText');
var isRTL = this._get(inst, 'isRTL');
if (appendText)
input[isRTL ? 'before' : 'after']('<span class="' + this._appendClass + '">' + appendText + '</span>');
var showOn = this._get(inst, 'showOn');
if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
input.focus(this._showDatepicker);
if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
var buttonText = this._get(inst, 'buttonText');
var buttonImage = this._get(inst, 'buttonImage');
inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
$('<img/>').addClass(this._triggerClass).
attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
$('<button type="button"></button>').addClass(this._triggerClass).
html(buttonImage == '' ? buttonText : $('<img/>').attr(
{ src:buttonImage, alt:buttonText, title:buttonText })));
input[isRTL ? 'before' : 'after'](inst.trigger);
inst.trigger.click(function() {
if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target)
$.datepicker._hideDatepicker();
else
$.datepicker._showDatepicker(target);
return false;
});
}
input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).
bind("setData.datepicker", function(event, key, value) {
inst.settings[key] = value;
}).bind("getData.datepicker", function(event, key) {
return this._get(inst, key);
});
$.data(target, PROP_NAME, inst);
},
/* Attach an inline date picker to a div. */
_inlineDatepicker: function(target, inst) {
var divSpan = $(target);
if (divSpan.hasClass(this.markerClassName))
return;
divSpan.addClass(this.markerClassName).append(inst.dpDiv).
bind("setData.datepicker", function(event, key, value){
inst.settings[key] = value;
}).bind("getData.datepicker", function(event, key){
return this._get(inst, key);
});
$.data(target, PROP_NAME, inst);
this._setDate(inst, this._getDefaultDate(inst));
this._updateDatepicker(inst);
this._updateAlternate(inst);
},
/* Pop-up the date picker in a "dialog" box.
@param input element - ignored
@param dateText string - the initial date to display (in the current format)
@param onSelect function - the function(dateText) to call when a date is selected
@param settings object - update the dialog date picker instance's settings (anonymous object)
@param pos int[2] - coordinates for the dialog's position within the screen or
event - with x/y coordinates or
leave empty for default (screen centre)
@return the manager object */
_dialogDatepicker: function(input, dateText, onSelect, settings, pos) {
var inst = this._dialogInst; // internal instance
if (!inst) {
var id = 'dp' + (++this.uuid);
this._dialogInput = $('<input type="text" id="' + id +
'" size="1" style="position: absolute; top: -100px;"/>');
this._dialogInput.keydown(this._doKeyDown);
$('body').append(this._dialogInput);
inst = this._dialogInst = this._newInst(this._dialogInput, false);
inst.settings = {};
$.data(this._dialogInput[0], PROP_NAME, inst);
}
extendRemove(inst.settings, settings || {});
this._dialogInput.val(dateText);
this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
if (!this._pos) {
var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
this._pos = // should use actual width/height below
[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
}
// move input on screen for focus, but hidden behind dialog
this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px');
inst.settings.onSelect = onSelect;
this._inDialog = true;
this.dpDiv.addClass(this._dialogClass);
this._showDatepicker(this._dialogInput[0]);
if ($.blockUI)
$.blockUI(this.dpDiv);
$.data(this._dialogInput[0], PROP_NAME, inst);
return this;
},
/* Detach a datepicker from its control.
@param target element - the target input field or division or span */
_destroyDatepicker: function(target) {
var $target = $(target);
var inst = $.data(target, PROP_NAME);
if (!$target.hasClass(this.markerClassName)) {
return;
}
var nodeName = target.nodeName.toLowerCase();
$.removeData(target, PROP_NAME);
if (nodeName == 'input') {
inst.trigger.remove();
$target.siblings('.' + this._appendClass).remove().end().
removeClass(this.markerClassName).
unbind('focus', this._showDatepicker).
unbind('keydown', this._doKeyDown).
unbind('keypress', this._doKeyPress);
} else if (nodeName == 'div' || nodeName == 'span')
$target.removeClass(this.markerClassName).empty();
},
/* Enable the date picker to a jQuery selection.
@param target element - the target input field or division or span */
_enableDatepicker: function(target) {
var $target = $(target);
var inst = $.data(target, PROP_NAME);
if (!$target.hasClass(this.markerClassName)) {
return;
}
var nodeName = target.nodeName.toLowerCase();
if (nodeName == 'input') {
target.disabled = false;
inst.trigger.filter("button").
each(function() { this.disabled = false; }).end().
filter("img").
css({opacity: '1.0', cursor: ''});
}
else if (nodeName == 'div' || nodeName == 'span') {
var inline = $target.children('.' + this._inlineClass);
inline.children().removeClass('ui-state-disabled');
}
this._disabledInputs = $.map(this._disabledInputs,
function(value) { return (value == target ? null : value); }); // delete entry
},
/* Disable the date picker to a jQuery selection.
@param target element - the target input field or division or span */
_disableDatepicker: function(target) {
var $target = $(target);
var inst = $.data(target, PROP_NAME);
if (!$target.hasClass(this.markerClassName)) {
return;
}
var nodeName = target.nodeName.toLowerCase();
if (nodeName == 'input') {
target.disabled = true;
inst.trigger.filter("button").
each(function() { this.disabled = true; }).end().
filter("img").
css({opacity: '0.5', cursor: 'default'});
}
else if (nodeName == 'div' || nodeName == 'span') {
var inline = $target.children('.' + this._inlineClass);
inline.children().addClass('ui-state-disabled');
}
this._disabledInputs = $.map(this._disabledInputs,
function(value) { return (value == target ? null : value); }); // delete entry
this._disabledInputs[this._disabledInputs.length] = target;
},
/* Is the first field in a jQuery collection disabled as a datepicker?
@param target element - the target input field or division or span
@return boolean - true if disabled, false if enabled */
_isDisabledDatepicker: function(target) {
if (!target) {
return false;
}
for (var i = 0; i < this._disabledInputs.length; i++) {
if (this._disabledInputs[i] == target)
return true;
}
return false;
},
/* Retrieve the instance data for the target control.
@param target element - the target input field or division or span
@return object - the associated instance data
@throws error if a jQuery problem getting data */
_getInst: function(target) {
try {
return $.data(target, PROP_NAME);
}
catch (err) {
throw 'Missing instance data for this datepicker';
}
},
/* Update the settings for a date picker attached to an input field or division.
@param target element - the target input field or division or span
@param name object - the new settings to update or
string - the name of the setting to change or
@param value any - the new value for the setting (omit if above is an object) */
_optionDatepicker: function(target, name, value) {
var settings = name || {};
if (typeof name == 'string') {
settings = {};
settings[name] = value;
}
var inst = this._getInst(target);
if (inst) {
if (this._curInst == inst) {
this._hideDatepicker(null);
}
extendRemove(inst.settings, settings);
var date = new Date();
extendRemove(inst, {rangeStart: null, // start of range
endDay: null, endMonth: null, endYear: null, // end of range
selectedDay: date.getDate(), selectedMonth: date.getMonth(),
selectedYear: date.getFullYear(), // starting point
currentDay: date.getDate(), currentMonth: date.getMonth(),
currentYear: date.getFullYear(), // current selection
drawMonth: date.getMonth(), drawYear: date.getFullYear()}); // month being drawn
this._updateDatepicker(inst);
}
},
// change method deprecated
_changeDatepicker: function(target, name, value) {
this._optionDatepicker(target, name, value);
},
/* Redraw the date picker attached to an input field or division.
@param target element - the target input field or division or span */
_refreshDatepicker: function(target) {
var inst = this._getInst(target);
if (inst) {
this._updateDatepicker(inst);
}
},
/* Set the dates for a jQuery selection.
@param target element - the target input field or division or span
@param date Date - the new date
@param endDate Date - the new end date for a range (optional) */
_setDateDatepicker: function(target, date, endDate) {
var inst = this._getInst(target);
if (inst) {
this._setDate(inst, date, endDate);
this._updateDatepicker(inst);
this._updateAlternate(inst);
}
},
/* Get the date(s) for the first entry in a jQuery selection.
@param target element - the target input field or division or span
@return Date - the current date or
Date[2] - the current dates for a range */
_getDateDatepicker: function(target) {
var inst = this._getInst(target);
if (inst && !inst.inline)
this._setDateFromField(inst);
return (inst ? this._getDate(inst) : null);
},
/* Handle keystrokes. */
_doKeyDown: function(event) {
var inst = $.datepicker._getInst(event.target);
var handled = true;
var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
inst._keyEvent = true;
if ($.datepicker._datepickerShowing)
switch (event.keyCode) {
case 9: $.datepicker._hideDatepicker(null, '');
break; // hide on tab out
case 13: var sel = $('td.' + $.datepicker._dayOverClass +
', td.' + $.datepicker._currentClass, inst.dpDiv);
if (sel[0])
$.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
else
$.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
return false; // don't submit the form
break; // select the value on enter
case 27: $.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
break; // hide on escape
case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
-$.datepicker._get(inst, 'stepBigMonths') :
-$.datepicker._get(inst, 'stepMonths')), 'M');
break; // previous month/year on page up/+ ctrl
case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+$.datepicker._get(inst, 'stepBigMonths') :
+$.datepicker._get(inst, 'stepMonths')), 'M');
break; // next month/year on page down/+ ctrl
case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
handled = event.ctrlKey || event.metaKey;
break; // clear on ctrl or command +end
case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
handled = event.ctrlKey || event.metaKey;
break; // current on ctrl or command +home
case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
handled = event.ctrlKey || event.metaKey;
// -1 day on ctrl or command +left
if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
-$.datepicker._get(inst, 'stepBigMonths') :
-$.datepicker._get(inst, 'stepMonths')), 'M');
// next month/year on alt +left on Mac
break;
case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
handled = event.ctrlKey || event.metaKey;
break; // -1 week on ctrl or command +up
case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
handled = event.ctrlKey || event.metaKey;
// +1 day on ctrl or command +right
if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+$.datepicker._get(inst, 'stepBigMonths') :
+$.datepicker._get(inst, 'stepMonths')), 'M');
// next month/year on alt +right
break;
case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
handled = event.ctrlKey || event.metaKey;
break; // +1 week on ctrl or command +down
default: handled = false;
}
else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
$.datepicker._showDatepicker(this);
else {
handled = false;
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
},
/* Filter entered characters - based on date format. */
_doKeyPress: function(event) {
var inst = $.datepicker._getInst(event.target);
if ($.datepicker._get(inst, 'constrainInput')) {
var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
return event.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
}
},
/* Pop-up the date picker for a given input field.
@param input element - the input field attached to the date picker or
event - if triggered by focus */
_showDatepicker: function(input) {
input = input.target || input;
if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
input = $('input', input.parentNode)[0];
if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
return;
var inst = $.datepicker._getInst(input);
var beforeShow = $.datepicker._get(inst, 'beforeShow');
extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
$.datepicker._hideDatepicker(null, '');
$.datepicker._lastInput = input;
$.datepicker._setDateFromField(inst);
if ($.datepicker._inDialog) // hide cursor
input.value = '';
if (!$.datepicker._pos) { // position below input
$.datepicker._pos = $.datepicker._findPos(input);
$.datepicker._pos[1] += input.offsetHeight; // add the height
}
var isFixed = false;
$(input).parents().each(function() {
isFixed |= $(this).css('position') == 'fixed';
return !isFixed;
});
if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
$.datepicker._pos[0] -= document.documentElement.scrollLeft;
$.datepicker._pos[1] -= document.documentElement.scrollTop;
}
var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
$.datepicker._pos = null;
inst.rangeStart = null;
// determine sizing offscreen
inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
$.datepicker._updateDatepicker(inst);
// fix width for dynamic number of date pickers
// and adjust position before showing
offset = $.datepicker._checkOffset(inst, offset, isFixed);
inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
left: offset.left + 'px', top: offset.top + 'px'});
if (!inst.inline) {
var showAnim = $.datepicker._get(inst, 'showAnim') || 'show';
var duration = $.datepicker._get(inst, 'duration');
var postProcess = function() {
$.datepicker._datepickerShowing = true;
if ($.browser.msie && parseInt($.browser.version,10) < 7) // fix IE < 7 select problems
$('iframe.ui-datepicker-cover').css({width: inst.dpDiv.width() + 4,
height: inst.dpDiv.height() + 4});
};
if ($.effects && $.effects[showAnim])
inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
else
inst.dpDiv[showAnim](duration, postProcess);
if (duration == '')
postProcess();
if (inst.input[0].type != 'hidden')
inst.input[0].focus();
$.datepicker._curInst = inst;
}
},
/* Generate the date picker content. */
_updateDatepicker: function(inst) {
var dims = {width: inst.dpDiv.width() + 4,
height: inst.dpDiv.height() + 4};
var self = this;
inst.dpDiv.empty().append(this._generateHTML(inst))
.find('iframe.ui-datepicker-cover').
css({width: dims.width, height: dims.height})
.end()
.find('button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a')
.bind('mouseout', function(){
$(this).removeClass('ui-state-hover');
if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
if(this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
})
.bind('mouseover', function(){
if (!self._isDisabledDatepicker( inst.inline ? inst.dpDiv.parent()[0] : inst.input[0])) {
$(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
$(this).addClass('ui-state-hover');
if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
if(this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
}
})
.end()
.find('.' + this._dayOverClass + ' a')
.trigger('mouseover')
.end();
var numMonths = this._getNumberOfMonths(inst);
var cols = numMonths[1];
var width = 17;
if (cols > 1) {
inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
} else {
inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
}
inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
'Class']('ui-datepicker-multi');
inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
'Class']('ui-datepicker-rtl');
if (inst.input && inst.input[0].type != 'hidden' && inst == $.datepicker._curInst)
$(inst.input[0]).focus();
},
/* Check positioning to remain on screen. */
_checkOffset: function(inst, offset, isFixed) {
var dpWidth = inst.dpDiv.outerWidth();
var dpHeight = inst.dpDiv.outerHeight();
var inputWidth = inst.input ? inst.input.outerWidth() : 0;
var inputHeight = inst.input ? inst.input.outerHeight() : 0;
var viewWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) + $(document).scrollLeft();
var viewHeight = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) + $(document).scrollTop();
offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
// now check if datepicker is showing outside window viewport - move to a better place if so.
offset.left -= (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? Math.abs(offset.left + dpWidth - viewWidth) : 0;
offset.top -= (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? Math.abs(offset.top + dpHeight + inputHeight*2 - viewHeight) : 0;
return offset;
},
/* Find an object's position on the screen. */
_findPos: function(obj) {
while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
obj = obj.nextSibling;
}
var position = $(obj).offset();
return [position.left, position.top];
},
/* Hide the date picker from view.
@param input element - the input field attached to the date picker
@param duration string - the duration over which to close the date picker */
_hideDatepicker: function(input, duration) {
var inst = this._curInst;
if (!inst || (input && inst != $.data(input, PROP_NAME)))
return;
if (inst.stayOpen)
this._selectDate('#' + inst.id, this._formatDate(inst,
inst.currentDay, inst.currentMonth, inst.currentYear));
inst.stayOpen = false;
if (this._datepickerShowing) {
duration = (duration != null ? duration : this._get(inst, 'duration'));
var showAnim = this._get(inst, 'showAnim');
var postProcess = function() {
$.datepicker._tidyDialog(inst);
};
if (duration != '' && $.effects && $.effects[showAnim])
inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'),
duration, postProcess);
else
inst.dpDiv[(duration == '' ? 'hide' : (showAnim == 'slideDown' ? 'slideUp' :
(showAnim == 'fadeIn' ? 'fadeOut' : 'hide')))](duration, postProcess);
if (duration == '')
this._tidyDialog(inst);
var onClose = this._get(inst, 'onClose');
if (onClose)
onClose.apply((inst.input ? inst.input[0] : null),
[(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
this._datepickerShowing = false;
this._lastInput = null;
if (this._inDialog) {
this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
if ($.blockUI) {
$.unblockUI();
$('body').append(this.dpDiv);
}
}
this._inDialog = false;
}
this._curInst = null;
},
/* Tidy up after a dialog display. */
_tidyDialog: function(inst) {
inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
},
/* Close date picker if clicked elsewhere. */
_checkExternalClick: function(event) {
if (!$.datepicker._curInst)
return;
var $target = $(event.target);
if (($target.parents('#' + $.datepicker._mainDivId).length == 0) &&
!$target.hasClass($.datepicker.markerClassName) &&
!$target.hasClass($.datepicker._triggerClass) &&
$.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
$.datepicker._hideDatepicker(null, '');
},
/* Adjust one of the date sub-fields. */
_adjustDate: function(id, offset, period) {
var target = $(id);
var inst = this._getInst(target[0]);
if (this._isDisabledDatepicker(target[0])) {
return;
}
this._adjustInstDate(inst, offset +
(period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
period);
this._updateDatepicker(inst);
},
/* Action for current link. */
_gotoToday: function(id) {
var target = $(id);
var inst = this._getInst(target[0]);
if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
inst.selectedDay = inst.currentDay;
inst.drawMonth = inst.selectedMonth = inst.currentMonth;
inst.drawYear = inst.selectedYear = inst.currentYear;
}
else {
var date = new Date();
inst.selectedDay = date.getDate();
inst.drawMonth = inst.selectedMonth = date.getMonth();
inst.drawYear = inst.selectedYear = date.getFullYear();
}
this._notifyChange(inst);
this._adjustDate(target);
},
/* Action for selecting a new month/year. */
_selectMonthYear: function(id, select, period) {
var target = $(id);
var inst = this._getInst(target[0]);
inst._selectingMonthYear = false;
inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
parseInt(select.options[select.selectedIndex].value,10);
this._notifyChange(inst);
this._adjustDate(target);
},
/* Restore input focus after not changing month/year. */
_clickMonthYear: function(id) {
var target = $(id);
var inst = this._getInst(target[0]);
if (inst.input && inst._selectingMonthYear && !$.browser.msie)
inst.input[0].focus();
inst._selectingMonthYear = !inst._selectingMonthYear;
},
/* Action for selecting a day. */
_selectDay: function(id, month, year, td) {
var target = $(id);
if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
return;
}
var inst = this._getInst(target[0]);
inst.selectedDay = inst.currentDay = $('a', td).html();
inst.selectedMonth = inst.currentMonth = month;
inst.selectedYear = inst.currentYear = year;
if (inst.stayOpen) {
inst.endDay = inst.endMonth = inst.endYear = null;
}
this._selectDate(id, this._formatDate(inst,
inst.currentDay, inst.currentMonth, inst.currentYear));
if (inst.stayOpen) {
inst.rangeStart = this._daylightSavingAdjust(
new Date(inst.currentYear, inst.currentMonth, inst.currentDay));
this._updateDatepicker(inst);
}
},
/* Erase the input field and hide the date picker. */
_clearDate: function(id) {
var target = $(id);
var inst = this._getInst(target[0]);
inst.stayOpen = false;
inst.endDay = inst.endMonth = inst.endYear = inst.rangeStart = null;
this._selectDate(target, '');
},
/* Update the input field with the selected date. */
_selectDate: function(id, dateStr) {
var target = $(id);
var inst = this._getInst(target[0]);
dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
if (inst.input)
inst.input.val(dateStr);
this._updateAlternate(inst);
var onSelect = this._get(inst, 'onSelect');
if (onSelect)
onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
else if (inst.input)
inst.input.trigger('change'); // fire the change event
if (inst.inline)
this._updateDatepicker(inst);
else if (!inst.stayOpen) {
this._hideDatepicker(null, this._get(inst, 'duration'));
this._lastInput = inst.input[0];
if (typeof(inst.input[0]) != 'object')
inst.input[0].focus(); // restore focus
this._lastInput = null;
}
},
/* Update any alternate field to synchronise with the main field. */
_updateAlternate: function(inst) {
var altField = this._get(inst, 'altField');
if (altField) { // update alternate field too
var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
var date = this._getDate(inst);
dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
$(altField).each(function() { $(this).val(dateStr); });
}
},
/* Set as beforeShowDay function to prevent selection of weekends.
@param date Date - the date to customise
@return [boolean, string] - is this date selectable?, what is its CSS class? */
noWeekends: function(date) {
var day = date.getDay();
return [(day > 0 && day < 6), ''];
},
/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
@param date Date - the date to get the week for
@return number - the number of the week within the year that contains this date */
iso8601Week: function(date) {
var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan
var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7
firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday
if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary
checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year
return $.datepicker.iso8601Week(checkDate);
} else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year
firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7;
if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary
return 1;
}
}
return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date
},
/* Parse a string value into a date object.
See formatDate below for the possible formats.
@param format string - the expected format of the date
@param value string - the date in the above format
@param settings Object - attributes include:
shortYearCutoff number - the cutoff year for determining the century (optional)
dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
dayNames string[7] - names of the days from Sunday (optional)
monthNamesShort string[12] - abbreviated names of the months (optional)
monthNames string[12] - names of the months (optional)
@return Date - the extracted date value or null if value is blank */
parseDate: function (format, value, settings) {
if (format == null || value == null)
throw 'Invalid arguments';
value = (typeof value == 'object' ? value.toString() : value + '');
if (value == '')
return null;
var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
var year = -1;
var month = -1;
var day = -1;
var doy = -1;
var literal = false;
// Check whether a format character is doubled
var lookAhead = function(match) {
var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
if (matches)
iFormat++;
return matches;
};
// Extract a number from the string value
var getNumber = function(match) {
lookAhead(match);
var origSize = (match == '@' ? 14 : (match == 'y' ? 4 : (match == 'o' ? 3 : 2)));
var size = origSize;
var num = 0;
while (size > 0 && iValue < value.length &&
value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') {
num = num * 10 + parseInt(value.charAt(iValue++),10);
size--;
}
if (size == origSize)
throw 'Missing number at position ' + iValue;
return num;
};
// Extract a name from the string value and convert to an index
var getName = function(match, shortNames, longNames) {
var names = (lookAhead(match) ? longNames : shortNames);
var size = 0;
for (var j = 0; j < names.length; j++)
size = Math.max(size, names[j].length);
var name = '';
var iInit = iValue;
while (size > 0 && iValue < value.length) {
name += value.charAt(iValue++);
for (var i = 0; i < names.length; i++)
if (name == names[i])
return i + 1;
size--;
}
throw 'Unknown name at position ' + iInit;
};
// Confirm that a literal character matches the string value
var checkLiteral = function() {
if (value.charAt(iValue) != format.charAt(iFormat))
throw 'Unexpected literal at position ' + iValue;
iValue++;
};
var iValue = 0;
for (var iFormat = 0; iFormat < format.length; iFormat++) {
if (literal)
if (format.charAt(iFormat) == "'" && !lookAhead("'"))
literal = false;
else
checkLiteral();
else
switch (format.charAt(iFormat)) {
case 'd':
day = getNumber('d');
break;
case 'D':
getName('D', dayNamesShort, dayNames);
break;
case 'o':
doy = getNumber('o');
break;
case 'm':
month = getNumber('m');
break;
case 'M':
month = getName('M', monthNamesShort, monthNames);
break;
case 'y':
year = getNumber('y');
break;
case '@':
var date = new Date(getNumber('@'));
year = date.getFullYear();
month = date.getMonth() + 1;
day = date.getDate();
break;
case "'":
if (lookAhead("'"))
checkLiteral();
else
literal = true;
break;
default:
checkLiteral();
}
}
if (year == -1)
year = new Date().getFullYear();
else if (year < 100)
year += new Date().getFullYear() - new Date().getFullYear() % 100 +
(year <= shortYearCutoff ? 0 : -100);
if (doy > -1) {
month = 1;
day = doy;
do {
var dim = this._getDaysInMonth(year, month - 1);
if (day <= dim)
break;
month++;
day -= dim;
} while (true);
}
var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
throw 'Invalid date'; // E.g. 31/02/*
return date;
},
/* Standard date formats. */
ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
COOKIE: 'D, dd M yy',
ISO_8601: 'yy-mm-dd',
RFC_822: 'D, d M y',
RFC_850: 'DD, dd-M-y',
RFC_1036: 'D, d M y',
RFC_1123: 'D, d M yy',
RFC_2822: 'D, d M yy',
RSS: 'D, d M y', // RFC 822
TIMESTAMP: '@',
W3C: 'yy-mm-dd', // ISO 8601
/* Format a date object into a string value.
The format can be combinations of the following:
d - day of month (no leading zero)
dd - day of month (two digit)
o - day of year (no leading zeros)
oo - day of year (three digit)
D - day name short
DD - day name long
m - month of year (no leading zero)
mm - month of year (two digit)
M - month name short
MM - month name long
y - year (two digit)
yy - year (four digit)
@ - Unix timestamp (ms since 01/01/1970)
'...' - literal text
'' - single quote
@param format string - the desired format of the date
@param date Date - the date value to format
@param settings Object - attributes include:
dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
dayNames string[7] - names of the days from Sunday (optional)
monthNamesShort string[12] - abbreviated names of the months (optional)
monthNames string[12] - names of the months (optional)
@return string - the date in the above format */
formatDate: function (format, date, settings) {
if (!date)
return '';
var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
// Check whether a format character is doubled
var lookAhead = function(match) {
var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
if (matches)
iFormat++;
return matches;
};
// Format a number, with leading zero if necessary
var formatNumber = function(match, value, len) {
var num = '' + value;
if (lookAhead(match))
while (num.length < len)
num = '0' + num;
return num;
};
// Format a name, short or long as requested
var formatName = function(match, value, shortNames, longNames) {
return (lookAhead(match) ? longNames[value] : shortNames[value]);
};
var output = '';
var literal = false;
if (date)
for (var iFormat = 0; iFormat < format.length; iFormat++) {
if (literal)
if (format.charAt(iFormat) == "'" && !lookAhead("'"))
literal = false;
else
output += format.charAt(iFormat);
else
switch (format.charAt(iFormat)) {
case 'd':
output += formatNumber('d', date.getDate(), 2);
break;
case 'D':
output += formatName('D', date.getDay(), dayNamesShort, dayNames);
break;
case 'o':
var doy = date.getDate();
for (var m = date.getMonth() - 1; m >= 0; m--)
doy += this._getDaysInMonth(date.getFullYear(), m);
output += formatNumber('o', doy, 3);
break;
case 'm':
output += formatNumber('m', date.getMonth() + 1, 2);
break;
case 'M':
output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
break;
case 'y':
output += (lookAhead('y') ? date.getFullYear() :
(date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
break;
case '@':
output += date.getTime();
break;
case "'":
if (lookAhead("'"))
output += "'";
else
literal = true;
break;
default:
output += format.charAt(iFormat);
}
}
return output;
},
/* Extract all possible characters from the date format. */
_possibleChars: function (format) {
var chars = '';
var literal = false;
for (var iFormat = 0; iFormat < format.length; iFormat++)
if (literal)
if (format.charAt(iFormat) == "'" && !lookAhead("'"))
literal = false;
else
chars += format.charAt(iFormat);
else
switch (format.charAt(iFormat)) {
case 'd': case 'm': case 'y': case '@':
chars += '0123456789';
break;
case 'D': case 'M':
return null; // Accept anything
case "'":
if (lookAhead("'"))
chars += "'";
else
literal = true;
break;
default:
chars += format.charAt(iFormat);
}
return chars;
},
/* Get a setting value, defaulting if necessary. */
_get: function(inst, name) {
return inst.settings[name] !== undefined ?
inst.settings[name] : this._defaults[name];
},
/* Parse existing date and initialise date picker. */
_setDateFromField: function(inst) {
var dateFormat = this._get(inst, 'dateFormat');
var dates = inst.input ? inst.input.val() : null;
inst.endDay = inst.endMonth = inst.endYear = null;
var date = defaultDate = this._getDefaultDate(inst);
var settings = this._getFormatConfig(inst);
try {
date = this.parseDate(dateFormat, dates, settings) || defaultDate;
} catch (event) {
this.log(event);
date = defaultDate;
}
inst.selectedDay = date.getDate();
inst.drawMonth = inst.selectedMonth = date.getMonth();
inst.drawYear = inst.selectedYear = date.getFullYear();
inst.currentDay = (dates ? date.getDate() : 0);
inst.currentMonth = (dates ? date.getMonth() : 0);
inst.currentYear = (dates ? date.getFullYear() : 0);
this._adjustInstDate(inst);
},
/* Retrieve the default date shown on opening. */
_getDefaultDate: function(inst) {
var date = this._determineDate(this._get(inst, 'defaultDate'), new Date());
var minDate = this._getMinMaxDate(inst, 'min', true);
var maxDate = this._getMinMaxDate(inst, 'max');
date = (minDate && date < minDate ? minDate : date);
date = (maxDate && date > maxDate ? maxDate : date);
return date;
},
/* A date may be specified as an exact value or a relative one. */
_determineDate: function(date, defaultDate) {
var offsetNumeric = function(offset) {
var date = new Date();
date.setDate(date.getDate() + offset);
return date;
};
var offsetString = function(offset, getDaysInMonth) {
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
var matches = pattern.exec(offset);
while (matches) {
switch (matches[2] || 'd') {
case 'd' : case 'D' :
day += parseInt(matches[1],10); break;
case 'w' : case 'W' :
day += parseInt(matches[1],10) * 7; break;
case 'm' : case 'M' :
month += parseInt(matches[1],10);
day = Math.min(day, getDaysInMonth(year, month));
break;
case 'y': case 'Y' :
year += parseInt(matches[1],10);
day = Math.min(day, getDaysInMonth(year, month));
break;
}
matches = pattern.exec(offset);
}
return new Date(year, month, day);
};
date = (date == null ? defaultDate :
(typeof date == 'string' ? offsetString(date, this._getDaysInMonth) :
(typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : date)));
date = (date && date.toString() == 'Invalid Date' ? defaultDate : date);
if (date) {
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
}
return this._daylightSavingAdjust(date);
},
/* Handle switch to/from daylight saving.
Hours may be non-zero on daylight saving cut-over:
> 12 when midnight changeover, but then cannot generate
midnight datetime, so jump to 1AM, otherwise reset.
@param date (Date) the date to check
@return (Date) the corrected date */
_daylightSavingAdjust: function(date) {
if (!date) return null;
date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
return date;
},
/* Set the date(s) directly. */
_setDate: function(inst, date, endDate) {
var clear = !(date);
var origMonth = inst.selectedMonth;
var origYear = inst.selectedYear;
date = this._determineDate(date, new Date());
inst.selectedDay = inst.currentDay = date.getDate();
inst.drawMonth = inst.selectedMonth = inst.currentMonth = date.getMonth();
inst.drawYear = inst.selectedYear = inst.currentYear = date.getFullYear();
if (origMonth != inst.selectedMonth || origYear != inst.selectedYear)
this._notifyChange(inst);
this._adjustInstDate(inst);
if (inst.input) {
inst.input.val(clear ? '' : this._formatDate(inst));
}
},
/* Retrieve the date(s) directly. */
_getDate: function(inst) {
var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
this._daylightSavingAdjust(new Date(
inst.currentYear, inst.currentMonth, inst.currentDay)));
return startDate;
},
/* Generate the HTML for the current state of the date picker. */
_generateHTML: function(inst) {
var today = new Date();
today = this._daylightSavingAdjust(
new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
var isRTL = this._get(inst, 'isRTL');
var showButtonPanel = this._get(inst, 'showButtonPanel');
var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
var numMonths = this._getNumberOfMonths(inst);
var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
var stepMonths = this._get(inst, 'stepMonths');
var stepBigMonths = this._get(inst, 'stepBigMonths');
var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
var minDate = this._getMinMaxDate(inst, 'min', true);
var maxDate = this._getMinMaxDate(inst, 'max');
var drawMonth = inst.drawMonth - showCurrentAtPos;
var drawYear = inst.drawYear;
if (drawMonth < 0) {
drawMonth += 12;
drawYear--;
}
if (maxDate) {
var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate()));
maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
drawMonth--;
if (drawMonth < 0) {
drawMonth = 11;
drawYear--;
}
}
}
inst.drawMonth = drawMonth;
inst.drawYear = drawYear;
var prevText = this._get(inst, 'prevText');
prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
this._getFormatConfig(inst)));
var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
(hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
var nextText = this._get(inst, 'nextText');
nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
this._getFormatConfig(inst)));
var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
(hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
var currentText = this._get(inst, 'currentText');
var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
currentText = (!navigationAsDateFormat ? currentText :
this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
(this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery.datepicker._gotoToday(\'#' + inst.id + '\');"' +
'>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
var firstDay = parseInt(this._get(inst, 'firstDay'),10);
firstDay = (isNaN(firstDay) ? 0 : firstDay);
var dayNames = this._get(inst, 'dayNames');
var dayNamesShort = this._get(inst, 'dayNamesShort');
var dayNamesMin = this._get(inst, 'dayNamesMin');
var monthNames = this._get(inst, 'monthNames');
var monthNamesShort = this._get(inst, 'monthNamesShort');
var beforeShowDay = this._get(inst, 'beforeShowDay');
var showOtherMonths = this._get(inst, 'showOtherMonths');
var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
var endDate = inst.endDay ? this._daylightSavingAdjust(
new Date(inst.endYear, inst.endMonth, inst.endDay)) : currentDate;
var defaultDate = this._getDefaultDate(inst);
var html = '';
for (var row = 0; row < numMonths[0]; row++) {
var group = '';
for (var col = 0; col < numMonths[1]; col++) {
var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
var cornerClass = ' ui-corner-all';
var calender = '';
if (isMultiMonth) {
calender += '<div class="ui-datepicker-group ui-datepicker-group-';
switch (col) {
case 0: calender += 'first'; cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
case numMonths[1]-1: calender += 'last'; cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
default: calender += 'middle'; cornerClass = ''; break;
}
calender += '">';
}
calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
(/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
(/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
selectedDate, row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
'</div><table class="ui-datepicker-calendar"><thead>' +
'<tr>';
var thead = '';
for (var dow = 0; dow < 7; dow++) { // days of the week
var day = (dow + firstDay) % 7;
thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
'<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
}
calender += thead + '</tr></thead><tbody>';
var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
calender += '<tr>';
var tbody = '';
for (var dow = 0; dow < 7; dow++) { // create date picker days
var daySettings = (beforeShowDay ?
beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
var otherMonth = (printDate.getMonth() != drawMonth);
var unselectable = otherMonth || !daySettings[0] ||
(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
tbody += '<td class="' +
((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
(otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
(defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
// or defaultDate is current printedDate and defaultDate is selectedDate
' ' + this._dayOverClass : '') + // highlight selected day
(unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
(otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
(printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range
' ' + this._currentClass : '') + // highlight selected day
(printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
(unselectable ? '' : ' onclick="DP_jQuery.datepicker._selectDay(\'#' +
inst.id + '\',' + drawMonth + ',' + drawYear + ', this);return false;"') + '>' + // actions
(otherMonth ? (showOtherMonths ? printDate.getDate() : '&#xa0;') : // display for other months
(unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
(printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
(printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range
' ui-state-active' : '') + // highlight selected day
'" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display for this month
printDate.setDate(printDate.getDate() + 1);
printDate = this._daylightSavingAdjust(printDate);
}
calender += tbody + '</tr>';
}
drawMonth++;
if (drawMonth > 11) {
drawMonth = 0;
drawYear++;
}
calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
group += calender;
}
html += group;
}
html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
inst._keyEvent = false;
return html;
},
/* Generate the month and year header. */
_generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
selectedDate, secondary, monthNames, monthNamesShort) {
minDate = (inst.rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate);
var changeMonth = this._get(inst, 'changeMonth');
var changeYear = this._get(inst, 'changeYear');
var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
var html = '<div class="ui-datepicker-title">';
var monthHtml = '';
// month selection
if (secondary || !changeMonth)
monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span> ';
else {
var inMinYear = (minDate && minDate.getFullYear() == drawYear);
var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
monthHtml += '<select class="ui-datepicker-month" ' +
'onchange="DP_jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
'onclick="DP_jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
'>';
for (var month = 0; month < 12; month++) {
if ((!inMinYear || month >= minDate.getMonth()) &&
(!inMaxYear || month <= maxDate.getMonth()))
monthHtml += '<option value="' + month + '"' +
(month == drawMonth ? ' selected="selected"' : '') +
'>' + monthNamesShort[month] + '</option>';
}
monthHtml += '</select>';
}
if (!showMonthAfterYear)
html += monthHtml + ((secondary || changeMonth || changeYear) && (!(changeMonth && changeYear)) ? '&#xa0;' : '');
// year selection
if (secondary || !changeYear)
html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
else {
// determine range of years to display
var years = this._get(inst, 'yearRange').split(':');
var year = 0;
var endYear = 0;
if (years.length != 2) {
year = drawYear - 10;
endYear = drawYear + 10;
} else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
year = drawYear + parseInt(years[0], 10);
endYear = drawYear + parseInt(years[1], 10);
} else {
year = parseInt(years[0], 10);
endYear = parseInt(years[1], 10);
}
year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
html += '<select class="ui-datepicker-year" ' +
'onchange="DP_jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
'onclick="DP_jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
'>';
for (; year <= endYear; year++) {
html += '<option value="' + year + '"' +
(year == drawYear ? ' selected="selected"' : '') +
'>' + year + '</option>';
}
html += '</select>';
}
if (showMonthAfterYear)
html += (secondary || changeMonth || changeYear ? '&#xa0;' : '') + monthHtml;
html += '</div>'; // Close datepicker_header
return html;
},
/* Adjust one of the date sub-fields. */
_adjustInstDate: function(inst, offset, period) {
var year = inst.drawYear + (period == 'Y' ? offset : 0);
var month = inst.drawMonth + (period == 'M' ? offset : 0);
var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
(period == 'D' ? offset : 0);
var date = this._daylightSavingAdjust(new Date(year, month, day));
// ensure it is within the bounds set
var minDate = this._getMinMaxDate(inst, 'min', true);
var maxDate = this._getMinMaxDate(inst, 'max');
date = (minDate && date < minDate ? minDate : date);
date = (maxDate && date > maxDate ? maxDate : date);
inst.selectedDay = date.getDate();
inst.drawMonth = inst.selectedMonth = date.getMonth();
inst.drawYear = inst.selectedYear = date.getFullYear();
if (period == 'M' || period == 'Y')
this._notifyChange(inst);
},
/* Notify change of month/year. */
_notifyChange: function(inst) {
var onChange = this._get(inst, 'onChangeMonthYear');
if (onChange)
onChange.apply((inst.input ? inst.input[0] : null),
[inst.selectedYear, inst.selectedMonth + 1, inst]);
},
/* Determine the number of months to show. */
_getNumberOfMonths: function(inst) {
var numMonths = this._get(inst, 'numberOfMonths');
return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
},
/* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */
_getMinMaxDate: function(inst, minMax, checkRange) {
var date = this._determineDate(this._get(inst, minMax + 'Date'), null);
return (!checkRange || !inst.rangeStart ? date :
(!date || inst.rangeStart > date ? inst.rangeStart : date));
},
/* Find the number of days in a given month. */
_getDaysInMonth: function(year, month) {
return 32 - new Date(year, month, 32).getDate();
},
/* Find the day of the week of the first of a month. */
_getFirstDayOfMonth: function(year, month) {
return new Date(year, month, 1).getDay();
},
/* Determines if we should allow a "next/prev" month display change. */
_canAdjustMonth: function(inst, offset, curYear, curMonth) {
var numMonths = this._getNumberOfMonths(inst);
var date = this._daylightSavingAdjust(new Date(
curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1));
if (offset < 0)
date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
return this._isInRange(inst, date);
},
/* Is the given date in the accepted range? */
_isInRange: function(inst, date) {
// during range selection, use minimum of selected date and range start
var newMinDate = (!inst.rangeStart ? null : this._daylightSavingAdjust(
new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay)));
newMinDate = (newMinDate && inst.rangeStart < newMinDate ? inst.rangeStart : newMinDate);
var minDate = newMinDate || this._getMinMaxDate(inst, 'min');
var maxDate = this._getMinMaxDate(inst, 'max');
return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
},
/* Provide the configuration settings for formatting/parsing. */
_getFormatConfig: function(inst) {
var shortYearCutoff = this._get(inst, 'shortYearCutoff');
shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
return {shortYearCutoff: shortYearCutoff,
dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
},
/* Format the given date for display. */
_formatDate: function(inst, day, month, year) {
if (!day) {
inst.currentDay = inst.selectedDay;
inst.currentMonth = inst.selectedMonth;
inst.currentYear = inst.selectedYear;
}
var date = (day ? (typeof day == 'object' ? day :
this._daylightSavingAdjust(new Date(year, month, day))) :
this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
}
});
/* jQuery extend now ignores nulls! */
function extendRemove(target, props) {
$.extend(target, props);
for (var name in props)
if (props[name] == null || props[name] == undefined)
target[name] = props[name];
return target;
};
/* Determine whether an object is an array. */
function isArray(a) {
return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
(a.constructor && a.constructor.toString().match(/\Array\(\)/))));
};
/* Invoke the datepicker functionality.
@param options string - a command, optionally followed by additional parameters or
Object - settings for attaching new datepicker functionality
@return jQuery object */
$.fn.datepicker = function(options){
/* Initialise the date picker. */
if (!$.datepicker.initialized) {
$(document).mousedown($.datepicker._checkExternalClick).
find('body').append($.datepicker.dpDiv);
$.datepicker.initialized = true;
}
var otherArgs = Array.prototype.slice.call(arguments, 1);
if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate'))
return $.datepicker['_' + options + 'Datepicker'].
apply($.datepicker, [this[0]].concat(otherArgs));
return this.each(function() {
typeof options == 'string' ?
$.datepicker['_' + options + 'Datepicker'].
apply($.datepicker, [this].concat(otherArgs)) :
$.datepicker._attachDatepicker(this, options);
});
};
$.datepicker = new Datepicker(); // singleton instance
$.datepicker.initialized = false;
$.datepicker.uuid = new Date().getTime();
$.datepicker.version = "1.7.1";
// Workaround for #4055
// Add another global to avoid noConflict issues with inline event handlers
window.DP_jQuery = $;
})(jQuery);
/*
* jQuery UI Progressbar 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Progressbar
*
* Depends:
* ui.core.js
*/
(function($) {
$.widget("ui.progressbar", {
_init: function() {
this.element
.addClass("ui-progressbar"
+ " ui-widget"
+ " ui-widget-content"
+ " ui-corner-all")
.attr({
role: "progressbar",
"aria-valuemin": this._valueMin(),
"aria-valuemax": this._valueMax(),
"aria-valuenow": this._value()
});
this.valueDiv = $('<div class="ui-progressbar-value ui-widget-header ui-corner-left"></div>').appendTo(this.element);
this._refreshValue();
},
destroy: function() {
this.element
.removeClass("ui-progressbar"
+ " ui-widget"
+ " ui-widget-content"
+ " ui-corner-all")
.removeAttr("role")
.removeAttr("aria-valuemin")
.removeAttr("aria-valuemax")
.removeAttr("aria-valuenow")
.removeData("progressbar")
.unbind(".progressbar");
this.valueDiv.remove();
$.widget.prototype.destroy.apply(this, arguments);
},
value: function(newValue) {
arguments.length && this._setData("value", newValue);
return this._value();
},
_setData: function(key, value) {
switch (key) {
case 'value':
this.options.value = value;
this._refreshValue();
this._trigger('change', null, {});
break;
}
$.widget.prototype._setData.apply(this, arguments);
},
_value: function() {
var val = this.options.value;
if (val < this._valueMin()) val = this._valueMin();
if (val > this._valueMax()) val = this._valueMax();
return val;
},
_valueMin: function() {
var valueMin = 0;
return valueMin;
},
_valueMax: function() {
var valueMax = 100;
return valueMax;
},
_refreshValue: function() {
var value = this.value();
this.valueDiv[value == this._valueMax() ? 'addClass' : 'removeClass']("ui-corner-right");
this.valueDiv.width(value + '%');
this.element.attr("aria-valuenow", value);
}
});
$.extend($.ui.progressbar, {
version: "1.7.1",
defaults: {
value: 0
}
});
})(jQuery);
/*
* jQuery UI Effects 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/
*/
;jQuery.effects || (function($) {
$.effects = {
version: "1.7.1",
// Saves a set of properties in a data storage
save: function(element, set) {
for(var i=0; i < set.length; i++) {
if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]);
}
},
// Restores a set of previously saved properties from a data storage
restore: function(element, set) {
for(var i=0; i < set.length; i++) {
if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i]));
}
},
setMode: function(el, mode) {
if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle
return mode;
},
getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value
// this should be a little more flexible in the future to handle a string & hash
var y, x;
switch (origin[0]) {
case 'top': y = 0; break;
case 'middle': y = 0.5; break;
case 'bottom': y = 1; break;
default: y = origin[0] / original.height;
};
switch (origin[1]) {
case 'left': x = 0; break;
case 'center': x = 0.5; break;
case 'right': x = 1; break;
default: x = origin[1] / original.width;
};
return {x: x, y: y};
},
// Wraps the element around a wrapper that copies position properties
createWrapper: function(element) {
//if the element is already wrapped, return it
if (element.parent().is('.ui-effects-wrapper'))
return element.parent();
//Cache width,height and float properties of the element, and create a wrapper around it
var props = { width: element.outerWidth(true), height: element.outerHeight(true), 'float': element.css('float') };
element.wrap('<div class="ui-effects-wrapper" style="font-size:100%;background:transparent;border:none;margin:0;padding:0"></div>');
var wrapper = element.parent();
//Transfer the positioning of the element to the wrapper
if (element.css('position') == 'static') {
wrapper.css({ position: 'relative' });
element.css({ position: 'relative'} );
} else {
var top = element.css('top'); if(isNaN(parseInt(top,10))) top = 'auto';
var left = element.css('left'); if(isNaN(parseInt(left,10))) left = 'auto';
wrapper.css({ position: element.css('position'), top: top, left: left, zIndex: element.css('z-index') }).show();
element.css({position: 'relative', top: 0, left: 0 });
}
wrapper.css(props);
return wrapper;
},
removeWrapper: function(element) {
if (element.parent().is('.ui-effects-wrapper'))
return element.parent().replaceWith(element);
return element;
},
setTransition: function(element, list, factor, value) {
value = value || {};
$.each(list, function(i, x){
unit = element.cssUnit(x);
if (unit[0] > 0) value[x] = unit[0] * factor + unit[1];
});
return value;
},
//Base function to animate from one class to another in a seamless transition
animateClass: function(value, duration, easing, callback) {
var cb = (typeof easing == "function" ? easing : (callback ? callback : null));
var ea = (typeof easing == "string" ? easing : null);
return this.each(function() {
var offset = {}; var that = $(this); var oldStyleAttr = that.attr("style") || '';
if(typeof oldStyleAttr == 'object') oldStyleAttr = oldStyleAttr["cssText"]; /* Stupidly in IE, style is a object.. */
if(value.toggle) { that.hasClass(value.toggle) ? value.remove = value.toggle : value.add = value.toggle; }
//Let's get a style offset
var oldStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
if(value.add) that.addClass(value.add); if(value.remove) that.removeClass(value.remove);
var newStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
if(value.add) that.removeClass(value.add); if(value.remove) that.addClass(value.remove);
// The main function to form the object for animation
for(var n in newStyle) {
if( typeof newStyle[n] != "function" && newStyle[n] /* No functions and null properties */
&& n.indexOf("Moz") == -1 && n.indexOf("length") == -1 /* No mozilla spezific render properties. */
&& newStyle[n] != oldStyle[n] /* Only values that have changed are used for the animation */
&& (n.match(/color/i) || (!n.match(/color/i) && !isNaN(parseInt(newStyle[n],10)))) /* Only things that can be parsed to integers or colors */
&& (oldStyle.position != "static" || (oldStyle.position == "static" && !n.match(/left|top|bottom|right/))) /* No need for positions when dealing with static positions */
) offset[n] = newStyle[n];
}
that.animate(offset, duration, ea, function() { // Animate the newly constructed offset object
// Change style attribute back to original. For stupid IE, we need to clear the damn object.
if(typeof $(this).attr("style") == 'object') { $(this).attr("style")["cssText"] = ""; $(this).attr("style")["cssText"] = oldStyleAttr; } else $(this).attr("style", oldStyleAttr);
if(value.add) $(this).addClass(value.add); if(value.remove) $(this).removeClass(value.remove);
if(cb) cb.apply(this, arguments);
});
});
}
};
function _normalizeArguments(a, m) {
var o = a[1] && a[1].constructor == Object ? a[1] : {}; if(m) o.mode = m;
var speed = a[1] && a[1].constructor != Object ? a[1] : (o.duration ? o.duration : a[2]); //either comes from options.duration or the secon/third argument
speed = $.fx.off ? 0 : typeof speed === "number" ? speed : $.fx.speeds[speed] || $.fx.speeds._default;
var callback = o.callback || ( $.isFunction(a[1]) && a[1] ) || ( $.isFunction(a[2]) && a[2] ) || ( $.isFunction(a[3]) && a[3] );
return [a[0], o, speed, callback];
}
//Extend the methods of jQuery
$.fn.extend({
//Save old methods
_show: $.fn.show,
_hide: $.fn.hide,
__toggle: $.fn.toggle,
_addClass: $.fn.addClass,
_removeClass: $.fn.removeClass,
_toggleClass: $.fn.toggleClass,
// New effect methods
effect: function(fx, options, speed, callback) {
return $.effects[fx] ? $.effects[fx].call(this, {method: fx, options: options || {}, duration: speed, callback: callback }) : null;
},
show: function() {
if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])))
return this._show.apply(this, arguments);
else {
return this.effect.apply(this, _normalizeArguments(arguments, 'show'));
}
},
hide: function() {
if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])))
return this._hide.apply(this, arguments);
else {
return this.effect.apply(this, _normalizeArguments(arguments, 'hide'));
}
},
toggle: function(){
if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])) || (arguments[0].constructor == Function))
return this.__toggle.apply(this, arguments);
else {
return this.effect.apply(this, _normalizeArguments(arguments, 'toggle'));
}
},
addClass: function(classNames, speed, easing, callback) {
return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
},
removeClass: function(classNames,speed,easing,callback) {
return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
},
toggleClass: function(classNames,speed,easing,callback) {
return ( (typeof speed !== "boolean") && speed ) ? $.effects.animateClass.apply(this, [{ toggle: classNames },speed,easing,callback]) : this._toggleClass(classNames, speed);
},
morph: function(remove,add,speed,easing,callback) {
return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
},
switchClass: function() {
return this.morph.apply(this, arguments);
},
// helper functions
cssUnit: function(key) {
var style = this.css(key), val = [];
$.each( ['em','px','%','pt'], function(i, unit){
if(style.indexOf(unit) > 0)
val = [parseFloat(style), unit];
});
return val;
}
});
/*
* jQuery Color Animations
* Copyright 2007 John Resig
* Released under the MIT and GPL licenses.
*/
// We override the animation for all of these color styles
$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
$.fx.step[attr] = function(fx) {
if ( fx.state == 0 ) {
fx.start = getColor( fx.elem, attr );
fx.end = getRGB( fx.end );
}
fx.elem.style[attr] = "rgb(" + [
Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0],10), 255), 0),
Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1],10), 255), 0),
Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2],10), 255), 0)
].join(",") + ")";
};
});
// Color Conversion functions from highlightFade
// By Blair Mitchelmore
// http://jquery.offput.ca/highlightFade/
// Parse strings looking for color tuples [255,255,255]
function getRGB(color) {
var result;
// Check if we're already dealing with an array of colors
if ( color && color.constructor == Array && color.length == 3 )
return color;
// Look for rgb(num,num,num)
if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
// Look for rgb(num%,num%,num%)
if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
// Look for #a0b1c2
if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
// Look for #fff
if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
// Look for rgba(0, 0, 0, 0) == transparent in Safari 3
if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
return colors['transparent'];
// Otherwise, we're most likely dealing with a named color
return colors[$.trim(color).toLowerCase()];
}
function getColor(elem, attr) {
var color;
do {
color = $.curCSS(elem, attr);
// Keep going until we find an element that has color, or we hit the body
if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") )
break;
attr = "backgroundColor";
} while ( elem = elem.parentNode );
return getRGB(color);
};
// Some named colors to work with
// From Interface by Stefan Petre
// http://interface.eyecon.ro/
var colors = {
aqua:[0,255,255],
azure:[240,255,255],
beige:[245,245,220],
black:[0,0,0],
blue:[0,0,255],
brown:[165,42,42],
cyan:[0,255,255],
darkblue:[0,0,139],
darkcyan:[0,139,139],
darkgrey:[169,169,169],
darkgreen:[0,100,0],
darkkhaki:[189,183,107],
darkmagenta:[139,0,139],
darkolivegreen:[85,107,47],
darkorange:[255,140,0],
darkorchid:[153,50,204],
darkred:[139,0,0],
darksalmon:[233,150,122],
darkviolet:[148,0,211],
fuchsia:[255,0,255],
gold:[255,215,0],
green:[0,128,0],
indigo:[75,0,130],
khaki:[240,230,140],
lightblue:[173,216,230],
lightcyan:[224,255,255],
lightgreen:[144,238,144],
lightgrey:[211,211,211],
lightpink:[255,182,193],
lightyellow:[255,255,224],
lime:[0,255,0],
magenta:[255,0,255],
maroon:[128,0,0],
navy:[0,0,128],
olive:[128,128,0],
orange:[255,165,0],
pink:[255,192,203],
purple:[128,0,128],
violet:[128,0,128],
red:[255,0,0],
silver:[192,192,192],
white:[255,255,255],
yellow:[255,255,0],
transparent: [255,255,255]
};
/*
* jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
*
* Uses the built in easing capabilities added In jQuery 1.1
* to offer multiple easing options
*
* TERMS OF USE - jQuery Easing
*
* Open source under the BSD License.
*
* Copyright 2008 George McGinley Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// t: current time, b: begInnIng value, c: change In value, d: duration
$.easing.jswing = $.easing.swing;
$.extend($.easing,
{
def: 'easeOutQuad',
swing: function (x, t, b, c, d) {
//alert($.easing.default);
return $.easing[$.easing.def](x, t, b, c, d);
},
easeInQuad: function (x, t, b, c, d) {
return c*(t/=d)*t + b;
},
easeOutQuad: function (x, t, b, c, d) {
return -c *(t/=d)*(t-2) + b;
},
easeInOutQuad: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInCubic: function (x, t, b, c, d) {
return c*(t/=d)*t*t + b;
},
easeOutCubic: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOutCubic: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
},
easeInQuart: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t + b;
},
easeOutQuart: function (x, t, b, c, d) {
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOutQuart: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
},
easeInQuint: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t*t + b;
},
easeOutQuint: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOutQuint: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
},
easeInSine: function (x, t, b, c, d) {
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
},
easeOutSine: function (x, t, b, c, d) {
return c * Math.sin(t/d * (Math.PI/2)) + b;
},
easeInOutSine: function (x, t, b, c, d) {
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
},
easeInExpo: function (x, t, b, c, d) {
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOutExpo: function (x, t, b, c, d) {
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOutExpo: function (x, t, b, c, d) {
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
},
easeInCirc: function (x, t, b, c, d) {
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOutCirc: function (x, t, b, c, d) {
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOutCirc: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
},
easeInElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
easeOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
},
easeInOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
},
easeInBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
easeInBounce: function (x, t, b, c, d) {
return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;
},
easeOutBounce: function (x, t, b, c, d) {
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOutBounce: function (x, t, b, c, d) {
if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
}
});
/*
*
* TERMS OF USE - EASING EQUATIONS
*
* Open source under the BSD License.
*
* Copyright 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
})(jQuery);
/*
* jQuery UI Effects Blind 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Blind
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.blind = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
var direction = o.options.direction || 'vertical'; // Default direction
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
var ref = (direction == 'vertical') ? 'height' : 'width';
var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width();
if(mode == 'show') wrapper.css(ref, 0); // Shift
// Animation
var animation = {};
animation[ref] = mode == 'show' ? distance : 0;
// Animate
wrapper.animate(animation, o.duration, o.options.easing, function() {
if(mode == 'hide') el.hide(); // Hide
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(el[0], arguments); // Callback
el.dequeue();
});
});
};
})(jQuery);
/*
* jQuery UI Effects Bounce 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Bounce
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.bounce = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
var direction = o.options.direction || 'up'; // Default direction
var distance = o.options.distance || 20; // Default distance
var times = o.options.times || 5; // Default # of times
var speed = o.duration || 250; // Default speed per bounce
if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
$.effects.createWrapper(el); // Create Wrapper
var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3);
if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
if (mode == 'hide') distance = distance / (times * 2);
if (mode != 'hide') times--;
// Animate
if (mode == 'show') { // Show Bounce
var animation = {opacity: 1};
animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
el.animate(animation, speed / 2, o.options.easing);
distance = distance / 2;
times--;
};
for (var i = 0; i < times; i++) { // Bounces
var animation1 = {}, animation2 = {};
animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing);
distance = (mode == 'hide') ? distance * 2 : distance / 2;
};
if (mode == 'hide') { // Last Bounce
var animation = {opacity: 0};
animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
el.animate(animation, speed / 2, o.options.easing, function(){
el.hide(); // Hide
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(this, arguments); // Callback
});
} else {
var animation1 = {}, animation2 = {};
animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(this, arguments); // Callback
});
};
el.queue('fx', function() { el.dequeue(); });
el.dequeue();
});
};
})(jQuery);
/*
* jQuery UI Effects Clip 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Clip
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.clip = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left','height','width'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
var direction = o.options.direction || 'vertical'; // Default direction
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
var animate = el[0].tagName == 'IMG' ? wrapper : el;
var ref = {
size: (direction == 'vertical') ? 'height' : 'width',
position: (direction == 'vertical') ? 'top' : 'left'
};
var distance = (direction == 'vertical') ? animate.height() : animate.width();
if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift
// Animation
var animation = {};
animation[ref.size] = mode == 'show' ? distance : 0;
animation[ref.position] = mode == 'show' ? 0 : distance / 2;
// Animate
animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
if(mode == 'hide') el.hide(); // Hide
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(el[0], arguments); // Callback
el.dequeue();
}});
});
};
})(jQuery);
/*
* jQuery UI Effects Drop 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Drop
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.drop = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left','opacity'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
var direction = o.options.direction || 'left'; // Default Direction
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
$.effects.createWrapper(el); // Create Wrapper
var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 2 : el.outerWidth({margin:true}) / 2);
if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
// Animation
var animation = {opacity: mode == 'show' ? 1 : 0};
animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
// Animate
el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
if(mode == 'hide') el.hide(); // Hide
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(this, arguments); // Callback
el.dequeue();
}});
});
};
})(jQuery);
/*
* jQuery UI Effects Explode 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Explode
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.explode = function(o) {
return this.queue(function() {
var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode;
var el = $(this).show().css('visibility', 'hidden');
var offset = el.offset();
//Substract the margins - not fixing the problem yet.
offset.top -= parseInt(el.css("marginTop"),10) || 0;
offset.left -= parseInt(el.css("marginLeft"),10) || 0;
var width = el.outerWidth(true);
var height = el.outerHeight(true);
for(var i=0;i<rows;i++) { // =
for(var j=0;j<cells;j++) { // ||
el
.clone()
.appendTo('body')
.wrap('<div></div>')
.css({
position: 'absolute',
visibility: 'visible',
left: -j*(width/cells),
top: -i*(height/rows)
})
.parent()
.addClass('ui-effects-explode')
.css({
position: 'absolute',
overflow: 'hidden',
width: width/cells,
height: height/rows,
left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0),
top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0),
opacity: o.options.mode == 'show' ? 0 : 1
}).animate({
left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)),
top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)),
opacity: o.options.mode == 'show' ? 1 : 0
}, o.duration || 500);
}
}
// Set a timeout, to call the callback approx. when the other animations have finished
setTimeout(function() {
o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide();
if(o.callback) o.callback.apply(el[0]); // Callback
el.dequeue();
$('div.ui-effects-explode').remove();
}, o.duration || 500);
});
};
})(jQuery);
/*
* jQuery UI Effects Fold 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Fold
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.fold = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
var size = o.options.size || 15; // Default fold size
var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value
var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2;
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
var widthFirst = ((mode == 'show') != horizFirst);
var ref = widthFirst ? ['width', 'height'] : ['height', 'width'];
var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()];
var percent = /([0-9]+)%/.exec(size);
if(percent) size = parseInt(percent[1],10) / 100 * distance[mode == 'hide' ? 0 : 1];
if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift
// Animation
var animation1 = {}, animation2 = {};
animation1[ref[0]] = mode == 'show' ? distance[0] : size;
animation2[ref[1]] = mode == 'show' ? distance[1] : 0;
// Animate
wrapper.animate(animation1, duration, o.options.easing)
.animate(animation2, duration, o.options.easing, function() {
if(mode == 'hide') el.hide(); // Hide
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(el[0], arguments); // Callback
el.dequeue();
});
});
};
})(jQuery);
/*
* jQuery UI Effects Highlight 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Highlight
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.highlight = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['backgroundImage','backgroundColor','opacity'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
var color = o.options.color || "#ffff99"; // Default highlight color
var oldColor = el.css("backgroundColor");
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
el.css({backgroundImage: 'none', backgroundColor: color}); // Shift
// Animation
var animation = {backgroundColor: oldColor };
if (mode == "hide") animation['opacity'] = 0;
// Animate
el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
if(mode == "hide") el.hide();
$.effects.restore(el, props);
if (mode == "show" && $.browser.msie) this.style.removeAttribute('filter');
if(o.callback) o.callback.apply(this, arguments);
el.dequeue();
}});
});
};
})(jQuery);
/*
* jQuery UI Effects Pulsate 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Pulsate
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.pulsate = function(o) {
return this.queue(function() {
// Create element
var el = $(this);
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
var times = o.options.times || 5; // Default # of times
var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2;
// Adjust
if (mode == 'hide') times--;
if (el.is(':hidden')) { // Show fadeIn
el.css('opacity', 0);
el.show(); // Show
el.animate({opacity: 1}, duration, o.options.easing);
times = times-2;
}
// Animate
for (var i = 0; i < times; i++) { // Pulsate
el.animate({opacity: 0}, duration, o.options.easing).animate({opacity: 1}, duration, o.options.easing);
};
if (mode == 'hide') { // Last Pulse
el.animate({opacity: 0}, duration, o.options.easing, function(){
el.hide(); // Hide
if(o.callback) o.callback.apply(this, arguments); // Callback
});
} else {
el.animate({opacity: 0}, duration, o.options.easing).animate({opacity: 1}, duration, o.options.easing, function(){
if(o.callback) o.callback.apply(this, arguments); // Callback
});
};
el.queue('fx', function() { el.dequeue(); });
el.dequeue();
});
};
})(jQuery);
/*
* jQuery UI Effects Scale 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Scale
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.puff = function(o) {
return this.queue(function() {
// Create element
var el = $(this);
// Set options
var options = $.extend(true, {}, o.options);
var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
var percent = parseInt(o.options.percent,10) || 150; // Set default puff percent
options.fade = true; // It's not a puff if it doesn't fade! :)
var original = {height: el.height(), width: el.width()}; // Save original
// Adjust
var factor = percent / 100;
el.from = (mode == 'hide') ? original : {height: original.height * factor, width: original.width * factor};
// Animation
options.from = el.from;
options.percent = (mode == 'hide') ? percent : 100;
options.mode = mode;
// Animate
el.effect('scale', options, o.duration, o.callback);
el.dequeue();
});
};
$.effects.scale = function(o) {
return this.queue(function() {
// Create element
var el = $(this);
// Set options
var options = $.extend(true, {}, o.options);
var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
var percent = parseInt(o.options.percent,10) || (parseInt(o.options.percent,10) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent
var direction = o.options.direction || 'both'; // Set default axis
var origin = o.options.origin; // The origin of the scaling
if (mode != 'effect') { // Set default origin and restore for show/hide
options.origin = origin || ['middle','center'];
options.restore = true;
}
var original = {height: el.height(), width: el.width()}; // Save original
el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state
// Adjust
var factor = { // Set scaling factor
y: direction != 'horizontal' ? (percent / 100) : 1,
x: direction != 'vertical' ? (percent / 100) : 1
};
el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state
if (o.options.fade) { // Fade option to support puff
if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;};
if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;};
};
// Animation
options.from = el.from; options.to = el.to; options.mode = mode;
// Animate
el.effect('size', options, o.duration, o.callback);
el.dequeue();
});
};
$.effects.size = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left','width','height','overflow','opacity'];
var props1 = ['position','top','left','overflow','opacity']; // Always restore
var props2 = ['width','height','overflow']; // Copy for children
var cProps = ['fontSize'];
var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom'];
var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
var restore = o.options.restore || false; // Default restore
var scale = o.options.scale || 'both'; // Default scale mode
var origin = o.options.origin; // The origin of the sizing
var original = {height: el.height(), width: el.width()}; // Save original
el.from = o.options.from || original; // Default from state
el.to = o.options.to || original; // Default to state
// Adjust
if (origin) { // Calculate baseline shifts
var baseline = $.effects.getBaseline(origin, original);
el.from.top = (original.height - el.from.height) * baseline.y;
el.from.left = (original.width - el.from.width) * baseline.x;
el.to.top = (original.height - el.to.height) * baseline.y;
el.to.left = (original.width - el.to.width) * baseline.x;
};
var factor = { // Set scaling factor
from: {y: el.from.height / original.height, x: el.from.width / original.width},
to: {y: el.to.height / original.height, x: el.to.width / original.width}
};
if (scale == 'box' || scale == 'both') { // Scale the css box
if (factor.from.y != factor.to.y) { // Vertical props scaling
props = props.concat(vProps);
el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from);
el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to);
};
if (factor.from.x != factor.to.x) { // Horizontal props scaling
props = props.concat(hProps);
el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from);
el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to);
};
};
if (scale == 'content' || scale == 'both') { // Scale the content
if (factor.from.y != factor.to.y) { // Vertical props scaling
props = props.concat(cProps);
el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from);
el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to);
};
};
$.effects.save(el, restore ? props : props1); el.show(); // Save & Show
$.effects.createWrapper(el); // Create Wrapper
el.css('overflow','hidden').css(el.from); // Shift
// Animate
if (scale == 'content' || scale == 'both') { // Scale the children
vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size
hProps = hProps.concat(['marginLeft','marginRight']); // Add margins
props2 = props.concat(vProps).concat(hProps); // Concat
el.find("*[width]").each(function(){
child = $(this);
if (restore) $.effects.save(child, props2);
var c_original = {height: child.height(), width: child.width()}; // Save original
child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x};
child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x};
if (factor.from.y != factor.to.y) { // Vertical props scaling
child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from);
child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to);
};
if (factor.from.x != factor.to.x) { // Horizontal props scaling
child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from);
child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to);
};
child.css(child.from); // Shift children
child.animate(child.to, o.duration, o.options.easing, function(){
if (restore) $.effects.restore(child, props2); // Restore children
}); // Animate children
});
};
// Animate
el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
if(mode == 'hide') el.hide(); // Hide
$.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(this, arguments); // Callback
el.dequeue();
}});
});
};
})(jQuery);
/*
* jQuery UI Effects Shake 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Shake
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.shake = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
var direction = o.options.direction || 'left'; // Default direction
var distance = o.options.distance || 20; // Default distance
var times = o.options.times || 3; // Default # of times
var speed = o.duration || o.options.duration || 140; // Default speed per shake
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
$.effects.createWrapper(el); // Create Wrapper
var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
// Animation
var animation = {}, animation1 = {}, animation2 = {};
animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
animation1[ref] = (motion == 'pos' ? '+=' : '-=') + distance * 2;
animation2[ref] = (motion == 'pos' ? '-=' : '+=') + distance * 2;
// Animate
el.animate(animation, speed, o.options.easing);
for (var i = 1; i < times; i++) { // Shakes
el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing);
};
el.animate(animation1, speed, o.options.easing).
animate(animation, speed / 2, o.options.easing, function(){ // Last shake
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(this, arguments); // Callback
});
el.queue('fx', function() { el.dequeue(); });
el.dequeue();
});
};
})(jQuery);
/*
* jQuery UI Effects Slide 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Slide
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.slide = function(o) {
return this.queue(function() {
// Create element
var el = $(this), props = ['position','top','left'];
// Set options
var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
var direction = o.options.direction || 'left'; // Default Direction
// Adjust
$.effects.save(el, props); el.show(); // Save & Show
$.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true}));
if (mode == 'show') el.css(ref, motion == 'pos' ? -distance : distance); // Shift
// Animation
var animation = {};
animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
// Animate
el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
if(mode == 'hide') el.hide(); // Hide
$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
if(o.callback) o.callback.apply(this, arguments); // Callback
el.dequeue();
}});
});
};
})(jQuery);
/*
* jQuery UI Effects Transfer 1.7.1
*
* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://docs.jquery.com/UI/Effects/Transfer
*
* Depends:
* effects.core.js
*/
(function($) {
$.effects.transfer = function(o) {
return this.queue(function() {
var elem = $(this),
target = $(o.options.to),
endPosition = target.offset(),
animation = {
top: endPosition.top,
left: endPosition.left,
height: target.innerHeight(),
width: target.innerWidth()
},
startPosition = elem.offset(),
transfer = $('<div class="ui-effects-transfer"></div>')
.appendTo(document.body)
.addClass(o.options.className)
.css({
top: startPosition.top,
left: startPosition.left,
height: elem.innerHeight(),
width: elem.innerWidth(),
position: 'absolute'
})
.animate(animation, o.duration, o.options.easing, function() {
transfer.remove();
(o.callback && o.callback.apply(elem[0], arguments));
elem.dequeue();
});
});
};
})(jQuery);

150
thirdparty/jquery-layout/nested.html vendored Executable file
View File

@ -0,0 +1,150 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Nested Layouts</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.layout.js"></script>
<script type="text/javascript" src="jquery.ui.all.js"></script>
<script>
var outerLayout, middleLayout, innerLayout;
$(document).ready(function () {
outerLayout = $('body').layout({
center__paneSelector: ".outer-center"
, west__paneSelector: ".outer-west"
, east__paneSelector: ".outer-east"
, west__size: 125
, east__size: 125
, spacing_open: 8 // ALL panes
, spacing_closed: 12 // ALL panes
, north__spacing_open: 0
, south__spacing_open: 0
, center__onresize: "middleLayout.resizeAll"
});
middleLayout = $('div.outer-center').layout({
center__paneSelector: ".middle-center"
, west__paneSelector: ".middle-west"
, east__paneSelector: ".middle-east"
, west__size: 100
, east__size: 100
, spacing_open: 8 // ALL panes
, spacing_closed: 12 // ALL panes
, center__onresize: "innerLayout.resizeAll"
});
innerLayout = $('div.middle-center').layout({
center__paneSelector: ".inner-center"
, west__paneSelector: ".inner-west"
, east__paneSelector: ".inner-east"
, west__size: 75
, east__size: 75
, spacing_open: 8 // ALL panes
, spacing_closed: 8 // ALL panes
, west__spacing_closed: 12
, east__spacing_closed: 12
});
});
</script>
<style type="text/css">
.ui-layout-pane { /* all 'panes' */
padding: 10px;
background: #FFF;
border-top: 1px solid #BBB;
border-bottom: 1px solid #BBB;
}
.ui-layout-pane-north ,
.ui-layout-pane-south {
border: 1px solid #BBB;
}
.ui-layout-pane-west {
border-left: 1px solid #BBB;
}
.ui-layout-pane-east {
border-right: 1px solid #BBB;
}
.ui-layout-pane-center {
border-left: 0;
border-right: 0;
}
.inner-center {
border: 1px solid #BBB;
}
.outer-west ,
.outer-east {
background-color: #EEE;
}
.middle-west ,
.middle-east {
background-color: #F8F8F8;
}
.ui-layout-resizer { /* all 'resizer-bars' */
background: #DDD;
}
.ui-layout-resizer:hover { /* all 'resizer-bars' */
background: #FED;
}
.ui-layout-resizer-west {
border-left: 1px solid #BBB;
}
.ui-layout-resizer-east {
border-right: 1px solid #BBB;
}
.ui-layout-toggler { /* all 'toggler-buttons' */
background: #AAA;
}
.ui-layout-toggler:hover { /* all 'toggler-buttons' */
background: #FC3;
}
.outer-center ,
.middle-center {
/* center pane that are 'containers' for a nested layout */
padding: 0;
border: 0;
}
</style>
</head>
<body>
<div class="outer-center">
<div class="middle-center">
<div class="inner-center">Inner Center</div>
<div class="inner-west">Inner West</div>
<div class="inner-east">Inner East</div>
<div class="ui-layout-north">Inner North</div>
<div class="ui-layout-south">Inner South</div>
</div>
<div class="middle-west">Middle West</div>
<div class="middle-east">Middle East</div>
</div>
<div class="outer-west">Outer West</div>
<div class="outer-east">Outer East</div>
<div class="ui-layout-north">Outer North</div>
<div class="ui-layout-south">Outer South</div>
</body>
</html>

197
thirdparty/jquery-layout/simple.html vendored Executable file
View File

@ -0,0 +1,197 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Simple Layout Demo</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.ui.all.js"></script>
<script type="text/javascript" src="jquery.layout.js"></script>
<script type="text/javascript">
var myLayout; // a var is required because this page utilizes: myLayout.allowOverflow() method
$(document).ready(function () {
myLayout = $('body').layout({
// enable showOverflow on west-pane so popups will overlap north pane
west__showOverflowOnHover: true
//, west__fxSettings_open: { easing: "easeOutBounce", duration: 750 }
});
});
</script>
<style type="text/css">
/**
* Basic Layout Theme
*
* This theme uses the default layout class-names for all classes
* Add any 'custom class-names', from options: paneClass, resizerClass, togglerClass
*/
.ui-layout-pane { /* all 'panes' */
background: #FFF;
border: 1px solid #BBB;
padding: 10px;
overflow: auto;
}
.ui-layout-resizer { /* all 'resizer-bars' */
background: #DDD;
}
.ui-layout-toggler { /* all 'toggler-buttons' */
background: #AAA;
}
</style>
<style type="text/css">
/**
* ALL CSS below is only for cosmetic and demo purposes
* Nothing here affects the appearance of the layout
*/
body {
font-family: Arial, sans-serif;
font-size: 0.85em;
}
p {
margin: 1em 0;
}
/*
* Rules below are for simulated drop-down/pop-up lists
*/
ul {
/* rules common to BOTH inner and outer UL */
z-index: 100000;
margin: 1ex 0;
padding: 0;
list-style: none;
cursor: pointer;
border: 1px solid Black;
/* rules for outer UL only */
width: 15ex;
position: relative;
}
ul li {
background-color: #EEE;
padding: 0.15em 1em 0.3em 5px;
}
ul ul {
display: none;
position: absolute;
width: 100%;
left: -1px;
/* Pop-Up */
bottom: 0;
margin: 0;
margin-bottom: 1.55em;
}
.ui-layout-north ul ul {
/* Drop-Down */
bottom: auto;
margin: 0;
margin-top: 1.45em;
}
ul ul li { padding: 3px 1em 3px 5px; }
ul ul li:hover { background-color: #FF9; }
ul li:hover ul { display: block; background-color: #EEE; }
</style>
</head>
<body>
<!-- manually attach allowOverflow method to pane -->
<div class="ui-layout-north" onmouseover="myLayout.allowOverflow('north')" onmouseout="myLayout.resetOverflow(this)">
This is the north pane, closable, slidable and resizable
<ul>
<li>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
<li>five</li>
</ul>
Drop-Down <!-- put this below so IE and FF render the same! -->
</li>
</ul>
</div>
<!-- allowOverflow auto-attached by option: west__showOverflowOnHover = true -->
<div class="ui-layout-west">
This is the west pane, closable, slidable and resizable
<ul>
<li>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
<li>five</li>
</ul>
Pop-Up <!-- put this below so IE and FF render the same! -->
</li>
</ul>
<p><button onclick="myLayout.close('west')">Close Me</button></p>
</div>
<div class="ui-layout-south">
This is the south pane, closable, slidable and resizable &nbsp;
<button onclick="myLayout.toggle('north')">Toggle North Pane</button>
</div>
<div class="ui-layout-east">
This is the east pane, closable, slidable and resizable
<!-- attach allowOverflow method to this specific element -->
<ul onmouseover="myLayout.allowOverflow(this)" onmouseout="myLayout.resetOverflow('east')">
<li>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
<li>five</li>
</ul>
Pop-Up <!-- put this below so IE and FF render the same! -->
</li>
</ul>
<p><button onclick="myLayout.close('east')">Close Me</button></p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
</div>
<div class="ui-layout-center">
This center pane auto-sizes to fit the space <i>between</i> the 'border-panes'
<p>This layout was created with only <b>default options</b> - no customization</p>
<p>Only the <b>applyDefaultStyles</b> option was enabled for <i>basic</i> formatting</p>
<p>The Pop-Up and Drop-Down lists demonstrate the <b>allowOverflow()</b> utility</p>
<p>The Close and Toggle buttons are examples of <b>custom buttons</b></p>
<p><a href="http://layout.jquery-dev.net/demos.html">Go to the Demos page</a></p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
<p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p><p>...</p>
</div>
</body>
</html>

8
thirdparty/swfupload/.piston.yml vendored Normal file
View File

@ -0,0 +1,8 @@
---
format: 1
handler:
piston:remote-revision: 854
piston:uuid: 46bd1c57-1c41-0410-9cc9-652e43039c97
lock: false
repository_url: http://swfupload.googlecode.com/svn/swfupload/tags/swfupload_v2.0.1/FlashDevelop
repository_class: Piston::Svn::Repository

193
thirdparty/swfupload/Changelog.txt vendored Normal file
View File

@ -0,0 +1,193 @@
* SWFUpload v2.0 by Jacob Roberts, Nov 2007, www.swfupload.org, linebyline.blogspot.com
* --------- Version 2.0.1 -----------
* = Fixed a bug where zero-byte files would stop file the queuing process.
* = Finished updating Features Demo
* + Added GetFileByIndex(i) that gets a JavaScript File Object. The index never changes. Finished files continue to be available.
* The JavaScript GetFile function will accept a file_id or an index.
* + Added CheckFileSize constants to replace the magic numbers
* + Added some code in an attempt to fix "Script is running slowly" error messages
* = Better cleanup of FileReference objects
* --------- Version 2.0 -----------
* + Re-created SWFUpload in Actionscript v2 for Flash 8 compatibility. Flash 8 loses POST and Server Data features. The two versions are otherwise fully compatible.
* Flash 8 uses URL to pass post_params/file_params.
* = Changed uploadStart event so it's part of the setTimeout/eventQueue workaround. This allows Flash functions to be called from uploadStart.
* = Renamed uploadComplete to uploadSuccess and fileComplete to uploadComplete. All started uploads call uploadComplete (even if cancelled or stopped).
* = Changed startUpload validation failure behavior. Rather than cancelling the upload the file is now requeued. Devs can cancel
* or do whatever the need to in uploadError to handle the problem.
* = Fixed fileQueueLimit/fileUploadLimit logic so it works correctly.
* = Moved the upload request building to a later point so that the post params and file params can be updated in uploadStart.
* - Removed the last of the UI stuff (ui_container, degraded_container).
* + Started development on Plug-ins. Graceful Degradation, v1.0.2, Cookies, Queue Handling
* = Fixed missing file_status field in FileItem.
* + Added modificationDate to FileItem (file object)
* + Added setStats function that lets you change the file upload count, etc. This will give more power over the queue limits. Not well tested.
* = Renamed compeleted_uploads to successful_uploads in getStats object
* + Added in_progress to getStats object
* --------- Revision 7.0 beta 3 -----------
* + Added an "event queue". Events are added to an array and executeEvent is called on a setTimeout. This prevents out of order issues that occur
* in the Safari browser.
* + Added a check for the UPLOAD_COMPLETE_DATA event constant which only became available in Flash Player 9.0.28. This
* fixes the Flash Version detection (Flash Object Detection) which was accepting Flash Player 9 versions before 9.0.28.
* - Removed old code block that was missed when moving from a Flash Timer to the JavaScript timeout (caused certain cancel events to be called twice)
* = Change ShowUI to the swfUploadLoaded event which will hopefully make it more clear that this is an overrideable event
* = Changed flashReady to behave like the other events (uses setTimeout and the Event Queue).
* --------- Revision 7.0 beta 2 -----------
* = Changed ERROR_CODE_FILE_NOT_FOUND to ERROR_CODE_FILE_ID_NOT_FOUND
* + Grouped the error code constants in objects for queue errors and upload errors.
* + Added an UPLOAD_STOPPED error code.
* = Changed Event calling method (using Timer) in Flash. Timer is no longer called
* instead setTimeout is called in JavaScript. This includes a change to the
* JavaSCript design so the Event methods are not directly overridden but stored
* internally and called if defined (with a setTimeout). This is an effort
* be more compatible with the current Flash Player on Linux
* = Changed the parameter order for the fileQueueError and uploadError events so the fileObj is first, like other events.
* + Added an empty JavaScript object (customSettings) where users can store settings associated with the instance.
* + Worked around an escaping bug in the ExternalInterface library by escaping all backslashes in out-going strings.
* = Updated all the demos.
* --------- Revision 7.0 beta 1 -----------
* = Redesigned the Event Chain
* - Removed much of the queue concepts
* - Removed the fileValidation events. This can be done in the new uploadStart event
* - Removed beginUploadOnQueue feature. This can be done in the new dialogComplete event.
* - Removed use_server_data. This is now always on.
* + Added functions for retrieving queue stats (number of files uploaded, queued, errors, etc)
* + Added a file status property to the FileObject. This indicates, uploaded, error, waiting.
* + Added a single file browser (user cannot select multiple files)
* + Fixed bug (hopefully) caused if Flash call to JavaScript and in the callback JavaSCript calls to Flash
* This only place this does not apply is to uploadStart. If you call in to Flash from uploadStart use a setTimeout to do it.
* --------- Revision 6.2 -----------
* + Added API calls for changing all the changeable settings dynamically
* = Fixed a bug in FileComplete event handler (in the SWF) that caused an error in Debug Players
* and prevent the event from being called
* + Added a setting (use_server_data_event) to indicate whether FileComplete or ServerData should be called.
* The SWFUpload architecture requires that only one file upload success event can be called.
* = Updated all the Demos, especially the Features Demo and the Forms Demo
* --------- Revision 6 -----------
* - Removed the call to setUploadSettings in flashReady. This was a left over call that is unnecessary.
* + Finished the parsing of post params during the init stage. This ommision was hidden by the call to setUploadSettings.
* - Removed the flash_target_id setting. The Flash file should only ever be added to the body tag.
* + Fixed (hopefully for good) another SWF race condition. IE executes the SWF very very early. The this.movieElement value should never be referenced.
* The movie Element should always be retrieved using this.getMovieElement().
* --------- Revision 6 -----------
* + Ported to ActionScript 3. Revision 6 requires Flash Player 9.
* = Fixed bug caused when cancelling single files. Would break any function that searched for a file_id.
* - Removed the automatic cookie sending setting. Devs should just pass the value they want to send in the post_params
* - Removed the query params settings (global and file specific). All params should be sent in the post_params
* + Added post_params which adds post values to the file upload post.
* + Added validate_files setting flag which causes the fileValidation event to be called before each file is uploaded.
* + Added fileValidation event. Return false if validation fails and true if validation is successful.
* + Added server_data parameter to the fileComplete event which returns any text that the upload script returns.
* = Updated all demos to work with Revision 6
* + Added in-code file extension validation. Before a file is queued the extension is checked against the valid extensions.
* Files the have invalid extensions cause the error event to be raised.
* + Added 'file_post_name' setting that allows the post variable name containing the file data to be named something other than 'Filedata'
* = Fixed a race condition in loadFlash where a cached flash movie would execute before this.movieElement could be assigned and loading would fail.
* --------- Revision 5.2 -----------
* = A little more code cleaning and variable renaming
* + Changed from an array queue to a FIFO queue. This eliminates the "current_index" and
* should reduce some memory usage.
* + Added out of order file uploading. Call StartUpload(/file_id/).
* + Added custom query_string parameters in addition to the cookies
* + Added the ability to modify the URL, cookies, params and send to flash
* + Added per file query_string params
* + Added files queued limit. Sometimes you may want the user to only queue one file at a time (or something)
* + Fixed limits so a zero(0) value means unlimited.
* --------- Revision 5 -------------
* = More code cleaning. Ported SWF to FlashDevelop. (Since my Flash Studio trial expired)
* The port to FlashDevelop is a big deal. It significantly changes the code structure
* and could introduce bugs. Also there have been reported issues with the FlashDevelop
* version from swfupload.mammon.se: Doesn't start when reloading in IE. Doesn't start
* in Firefox if the SWF file is visible because of a page scroll.
* + I fixed the Firefox issue by removing the wmode attribute from the embed object.
* + I cannot reproduce the IE issue on my local machine (although I can reproduce it at swfupload.mammon.se)
* + Event Handlers are now attached to the SWFUpload javascript object. The SWF file
* now calls the handlers in the context of the SWFUpload object which means the "this"
* object inside the handler refers to the proper SWFUpload instance.
* + Tested and Fixed upload target cookie attachment
* = Cleaned up / renamed everything for clarity and consistancy
* + File queuing is now subject to the upload limit. If the user attempts to queue more files
* than allowed an error is returned and the files are not queued.
* + Fixed misc bugs and text encodings.
* + Added more debug info for the SWF file.
* + SWF file now obeys the debug setting.
* + Added SetUploadTargetURL function that allows you to "dynamically" change the upload target
* + Added error code for zero byte file uploads which always return an IO error. The files are now rejected
* instead of being uploaded.
* --------- Revision 4 -------------
* = Cleaned up code. Added comments. Reorganized. Added more try..catches. Removed old unused methods.
* - Removed the 'create_ui' setting. The UI is now completely up to the developer.
* + Added upload_backend_cookies setting. Can set a string, or array of cookie names. These values will be
* passed as part of the upload_backend url
*
* = Changed QueueComplete event to only fire if at least one file has been successfully uploaded.
* + Added "Stop Upload" feature.
* = Revised the FLA file to clean things up, better handle errors, etc.
* = Fixed a bug where cancelling the first upload would cause the remaining uploads to fire before calling
* "startUpload". This change is in the FLA.
*
* + Fixed a bug in the upload.swf that prevented further file processing after an error is returned.
* + Added uploadLimit variable. Only complete uploads are counted. Once the limit is reached the flash
* movie will not upload any more files. (The ability to select or queue many files is not affected
* by the upload limit)
* + Added cancelQueue and cancelUpload methods.
* + Added ID property to the FileObj in the upload.swf
* + Added Upload and Queue settings
* + Added methods for generating the flash HTML and inserting it into the DOM.
* - Removed SWFObject
* + Updated the upload.swf and added the "flashReady" event. This will only call back
* for Flash 8 and above. With this we don't need a flash version detect script.
* The script initializes the Flash then waits for the Callback to init the UI.
* + Added seperate ui_target, degraded_target, create_ui settings. This allows fine control
* over what parts of the GUI the script displays and hides
*
* + Changed from a Static Class to an Instance (changed code/class structure)
* + Added "flash_version" setting. When set to zero the version check is skipped
* + Added Debug Console. The Instance class can't do document.write.
* = De-obfuscated SWFObject a bit
* - Removed standalone mode.
* + Added "ui_target" setting. When non-blank the link is added.
* + Added "flash_target" setting. When blank the flash is appended to the <body> tag
* = This fixes ASP.Net not allowing the flash to be added to the Form
* + Added error checking to the callSWF method
*
* -------- -------- -------- -------- -------- -------- -------- --------
* SWFUpload 0.7: Flash upload dialog - http://profandesign.se/swfupload/
* SWFUpload is (c) 2006 Lars Huring and Mammon Media and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
* -------- -------- -------- -------- -------- -------- -------- --------
* SWFUpload 0.7: Flash upload dialog - http://profandesign.se/swfupload/
*
* VERSION HISTORY
* 0.5 - First release
*
* 0.6 - 2006-11-24
* - Got rid of flash overlay
* - SWF size reduced to 840b
* - CSS-only styling of button
* - Add upload to links etc.
*
* 0.7 - 2006-11-27
* - Added filesize param and check in SWF
*
* 0.7.1 - 2006-12-01
* - Added link_mode param for standalone links
* if set to "standalone", createElement("a") won't run.
* - Added link_text param if css isn't needed.
* - Renamed cssClass to css_class for consistency
*
*/

View File

@ -0,0 +1,8 @@
class Delegate
{
static function create(obj:Object, func:Function):Function
{
return function() { return func.apply(obj, arguments); };
}
}

View File

@ -0,0 +1,115 @@
import flash.external.ExternalInterface;
class ExternalCall
{
/*public function ExternalCall()
{
}
*/
public static function Simple(callback:String):Void {
ExternalInterface.call(callback);
}
public static function FileQueued(callback:String, file_object:Object):Void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function FileQueueError(callback:String, error_code:Number, file_object:Object, message:String):Void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message));
}
public static function FileDialogComplete(callback:String, num_files_selected:Number):Void {
ExternalInterface.call(callback, EscapeMessage(num_files_selected));
}
public static function UploadStart(callback:String, file_object:Object):Void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function UploadProgress(callback:String, file_object:Object, bytes_loaded:Number, bytes_total:Number):Void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(bytes_loaded), EscapeMessage(bytes_total));
}
public static function UploadSuccess(callback:String, file_object:Object):Void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function UploadError(callback:String, error_code:Number, file_object:Object, message:String):Void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message));
}
public static function UploadComplete(callback:String, file_object:Object):Void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function Debug(callback:String, message:String):Void {
ExternalInterface.call(callback, EscapeMessage(message));
}
/* Escapes all the backslashes which are not translated correctly in the Flash -> JavaScript Interface
*
* These functions had to be developed because the ExternalInterface has a bug that simply places the
* value a string in quotes (except for a " which is escaped) in a JavaScript string literal which
* is executed by the browser. These often results in improperly escaped string literals if your
* input string has any backslash characters. For example the string:
* "c:\Program Files\uploadtools\"
* is placed in a string literal (with quotes escaped) and becomes:
* var __flash__temp = "\"c:\Program Files\uploadtools\\"";
* This statement will cause errors when executed by the JavaScript interpreter:
* 1) The first \" is succesfully transformed to a "
* 2) \P is translated to P and the \ is lost
* 3) \u is interpreted as a unicode character and causes an error in IE
* 4) \\ is translated to \
* 5) leaving an unescaped " which causes an error
*
* I fixed this by escaping \ characters in all outgoing strings. The above escaped string becomes:
* var __flash__temp = "\"c:\\Program Files\\uploadtools\\\"";
* which contains the correct string literal.
*
* Note: The "var __flash__temp = " portion of the example is part of the ExternalInterface not part of
* my escaping routine.
*/
private static function EscapeMessage(message) {
if (typeof message == "string") {
message = EscapeString(message);
}
else if (typeof message == "array") {
message = EscapeArray(message);
}
else if (typeof message == "object") {
message = EscapeObject(message);
}
return message;
}
private static function EscapeString(message:String):String {
//var replacePattern:RegExp = /\\/g; //new RegExp("/\\/", "g");
return message.split("\\").join("\\\\").split("\n").join("\\n").split("\r").join("\\r").split("\f").join("\\f").split("\b").join("\\b");
}
private static function EscapeArray(message_array:Array):Array {
var length:Number = message_array.length;
var i:Number = 0;
for (i=0; i < length; i++) {
message_array[i] = EscapeMessage(message_array[i]);
}
return message_array;
}
private static function EscapeObject(message_obj:Object):Object {
for (var name:String in message_obj) {
message_obj[name] = EscapeMessage(message_obj[name]);
}
return message_obj;
}
}

65
thirdparty/swfupload/Flash8/FileItem.as vendored Normal file
View File

@ -0,0 +1,65 @@
import flash.net.FileReference;
class FileItem
{
private static var file_id_sequence:Number = 0; // tracks the file id sequence
private var postObject:Object;
public var file_reference:FileReference;
public var id:String;
public var index:Number = -1;
public var file_status:Number = 0;
private var js_object:Object;
public static var FILE_STATUS_QUEUED:Number = -1;
public static var FILE_STATUS_IN_PROGRESS:Number = -2;
public static var FILE_STATUS_ERROR:Number = -3;
public static var FILE_STATUS_SUCCESS:Number = -4;
public static var FILE_STATUS_CANCELLED:Number = -5;
public static var FILE_STATUS_NEW:Number = -6; // This file status should never be sent to JavaScript
public function FileItem(file_reference:FileReference, control_id:String)
{
this.postObject = {};
this.file_reference = file_reference;
this.id = control_id + "_" + (FileItem.file_id_sequence++);
this.file_status = FileItem.FILE_STATUS_QUEUED;
this.js_object = {
id: this.id,
index: this.index,
name: this.file_reference.name,
size: this.file_reference.size,
type: this.file_reference.type,
creationdate: this.file_reference.creationDate,
modificationdate: this.file_reference.modificationDate,
filestatus: this.file_status,
post: this.GetPostObject()
};
}
public function AddParam(name:String, value:String):Void {
this.postObject[name] = value;
}
public function RemoveParam(name:String):Void {
delete this.postObject[name];
}
public function GetPostObject():Object {
return this.postObject;
}
// Update the js_object and return it.
public function ToJavaScriptObject():Object {
this.js_object.filestatus = this.file_status;
this.js_object.post = this.GetPostObject();
return this.js_object;
}
public function toString():String {
return "FileItem - ID: " + this.id;
}
}

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<!-- Output SWF options -->
<output>
<movie disabled="False" />
<movie input="" />
<movie path="swfupload_f8.swf" />
<movie fps="1" />
<movie width="1" />
<movie height="1" />
<movie version="8" />
<movie background="#FFFFFF" />
</output>
<!-- Other classes to be compiled into your SWF -->
<classpaths>
<class path="." />
</classpaths>
<!-- Build options -->
<build>
<option verbose="False" />
<option strict="False" />
<option infer="False" />
<option useMain="True" />
<option useMX="False" />
<option warnUnusedImports="False" />
<option traceMode="FlashConnect" />
<option traceFunction="" />
<option libraryPrefix="" />
<option excludeFile="" />
<option groupClasses="False" />
<option frame="1" />
<option keep="True" />
</build>
<!-- Class files to compile (other referenced classes will automatically be included) -->
<compileTargets>
<compile path="SWFUpload.as" />
</compileTargets>
<!-- Assets to embed into the output SWF -->
<library>
<!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
</library>
<!-- Paths to exclude from the Project Explorer tree -->
<hiddenPaths>
<!-- example: <hidden path="..." /> -->
</hiddenPaths>
<!-- Executed before build -->
<preBuildCommand />
<!-- Executed after build -->
<postBuildCommand alwaysRun="False">deploy.bat</postBuildCommand>
<!-- Other project options -->
<options>
<option showHiddenPaths="False" />
<option testMovie="Default" />
</options>
</project>

914
thirdparty/swfupload/Flash8/SWFUpload.as vendored Normal file
View File

@ -0,0 +1,914 @@
/*
* Todo:
* In SWFUpload v3 the file_queue and file_index should be merged. This probably means we'll remove FileItem.id and just
* use indexes everywhere.
* */
import flash.net.FileReferenceList;
import flash.net.FileReference;
import flash.external.ExternalInterface;
import FileItem;
import ExternalCall;
import Delegate;
class SWFUpload {
// Cause SWFUpload to start as soon as the movie starts
public static function main():Void
{
// Code to attempt to fix "flash script running slowly" error messages
var counter:Number = 0;
_root.onEnterFrame = function () {
if (++counter > 100) counter = 0;
};
// Start SWFUpload
var SWFUpload:SWFUpload = new SWFUpload();
}
private var build_number:String = "SWFUPLOAD 2.0.1 FP8 2007-12-05 0001";
// State tracking variables
private var fileBrowserMany:FileReferenceList = new FileReferenceList();
private var fileBrowserOne:FileReference = null; // This isn't set because it can't be reused like the FileReferenceList. It gets setup in the SelectFile method
private var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded.
private var current_file_item:FileItem = null; // the item that is currently being uploaded.
private var file_index:Array = new Array();
private var successful_uploads:Number = 0; // Tracks the uploads that have been completed
private var queue_errors:Number = 0; // Tracks files rejected during queueing
private var upload_errors:Number = 0; // Tracks files that fail upload
private var upload_cancelled:Number = 0; // Tracks number of cancelled files
private var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded.
private var valid_file_extensions:Array = new Array();// Holds the parsed valid extensions.
private var file_reference_listener:Object;
// Callbacks
private var flashReady_Callback:String;
private var fileDialogStart_Callback:String;
private var fileQueued_Callback:String;
private var fileQueueError_Callback:String;
private var fileDialogComplete_Callback:String;
private var uploadStart_Callback:String;
private var uploadProgress_Callback:String;
private var uploadError_Callback:String;
private var uploadSuccess_Callback:String;
private var uploadComplete_Callback:String;
private var debug_Callback:String;
// Values passed in from the HTML
private var movieName:String;
private var uploadURL:String;
private var filePostName:String;
private var uploadPostObject:Object;
private var fileTypes:String;
private var fileTypesDescription:String;
private var fileSizeLimit:Number;
private var fileUploadLimit:Number = 0;
private var fileQueueLimit:Number = 0;
private var debugEnabled:Boolean;
// Error code "constants"
// Size check constants
private var SIZE_TOO_BIG:Number = 1;
private var SIZE_ZERO_BYTE:Number = -1;
private var SIZE_OK:Number = 0;
// Queue errors
private var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -100;
private var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -110;
private var ERROR_CODE_ZERO_BYTE_FILE:Number = -120;
private var ERROR_CODE_INVALID_FILETYPE:Number = -130;
// Upload Errors
private var ERROR_CODE_HTTP_ERROR:Number = -200;
private var ERROR_CODE_MISSING_UPLOAD_URL:Number = -210;
private var ERROR_CODE_IO_ERROR:Number = -220;
private var ERROR_CODE_SECURITY_ERROR:Number = -230;
private var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -240;
private var ERROR_CODE_UPLOAD_FAILED:Number = -250;
private var ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND:Number = -260;
private var ERROR_CODE_FILE_VALIDATION_FAILED:Number = -270;
private var ERROR_CODE_FILE_CANCELLED:Number = -280;
private var ERROR_CODE_UPLOAD_STOPPED:Number = -290;
public function SWFUpload() {
System.security.allowDomain("*"); // Allow uploading to any domain
// Setup file FileReferenceList events
var fbmListener:Object = {
onSelect : Delegate.create(this, this.Select_Many_Handler),
onCancel : Delegate.create(this, this.DialogCancelled_Handler)
}
this.fileBrowserMany.addListener(fbmListener);
// Setup the events listner
this.file_reference_listener = {
onProgress : Delegate.create(this, this.FileProgress_Handler),
onIOError : Delegate.create(this, this.IOError_Handler),
onSecurityError : Delegate.create(this, this.SecurityError_Handler),
onHTTPError : Delegate.create(this, this.HTTPError_Handler),
onComplete : Delegate.create(this, this.FileComplete_Handler)
};
// Get the move name
this.movieName = _root.movieName;
// **Configure the callbacks**
// The JavaScript tracks all the instances of SWFUpload on a page. We can access the instance
// associated with this SWF file using the movieName. Each callback is accessible by making
// a call directly to it on our instance. There is no error handling for undefined callback functions.
// A developer would have to deliberately remove the default functions,set the variable to null, or remove
// it from the init function.
this.flashReady_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].flashReady";
this.fileDialogStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogStart";
this.fileQueued_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueued";
this.fileQueueError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueueError";
this.fileDialogComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogComplete";
this.uploadStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadStart";
this.uploadProgress_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadProgress";
this.uploadError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadError";
this.uploadSuccess_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadSuccess";
this.uploadComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadComplete";
this.debug_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].debug";
// Get the Flash Vars
this.uploadURL = _root.uploadURL;
this.filePostName = _root.filePostName;
this.fileTypes = _root.fileTypes;
this.fileTypesDescription = _root.fileTypesDescription + " (" + this.fileTypes + ")";
this.loadPostParams(_root.params);
if (!this.filePostName) {
this.filePostName = "Filedata";
}
if (!this.fileTypes) {
this.fileTypes = "*.*";
}
if (!this.fileTypesDescription) {
this.fileTypesDescription = "All Files";
}
this.LoadFileExensions(this.fileTypes);
try {
this.debugEnabled = _root.debugEnabled == "true" ? true : false;
} catch (ex:Object) {
this.debugEnabled = false;
}
try {
this.fileSizeLimit = Number(_root.fileSizeLimit);
if (this.fileSizeLimit < 0) this.fileSizeLimit = 0;
} catch (ex:Object) {
this.fileSizeLimit = 0;
}
try {
this.fileUploadLimit = Number(_root.fileUploadLimit);
if (this.fileUploadLimit < 0) this.fileUploadLimit = 0;
} catch (ex:Object) {
this.fileUploadLimit = 0;
}
try {
this.fileQueueLimit = Number(_root.fileQueueLimit);
if (this.fileQueueLimit < 0) this.fileQueueLimit = 0;
} catch (ex:Object) {
this.fileQueueLimit = 0;
}
// Set the queue limit to match the upload limit when the queue limit is bigger than the upload limit
if (this.fileQueueLimit > this.fileUploadLimit && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
// The the queue limit is unlimited and the upload limit is not then set the queue limit to the upload limit
if (this.fileQueueLimit == 0 && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
try {
ExternalInterface.addCallback("SelectFile", this, this.SelectFile);
ExternalInterface.addCallback("SelectFiles", this, this.SelectFiles);
ExternalInterface.addCallback("StartUpload", this, this.StartUpload);
ExternalInterface.addCallback("ReturnUploadStart", this, this.ReturnUploadStart);
ExternalInterface.addCallback("StopUpload", this, this.StopUpload);
ExternalInterface.addCallback("CancelUpload", this, this.CancelUpload);
ExternalInterface.addCallback("GetStats", this, this.GetStats);
ExternalInterface.addCallback("SetStats", this, this.SetStats);
ExternalInterface.addCallback("GetFile", this, this.GetFile);
ExternalInterface.addCallback("GetFileByIndex", this, this.GetFileByIndex);
ExternalInterface.addCallback("AddFileParam", this, this.AddFileParam);
ExternalInterface.addCallback("RemoveFileParam", this, this.RemoveFileParam);
ExternalInterface.addCallback("SetUploadURL", this, this.SetUploadURL);
ExternalInterface.addCallback("SetPostParams", this, this.SetPostParams);
ExternalInterface.addCallback("SetFileTypes", this, this.SetFileTypes);
ExternalInterface.addCallback("SetFileSizeLimit", this, this.SetFileSizeLimit);
ExternalInterface.addCallback("SetFileUploadLimit", this, this.SetFileUploadLimit);
ExternalInterface.addCallback("SetFileQueueLimit", this, this.SetFileQueueLimit);
ExternalInterface.addCallback("SetFilePostName", this, this.SetFilePostName);
ExternalInterface.addCallback("SetDebugEnabled", this, this.SetDebugEnabled);
} catch (ex:Error) {
this.Debug("Callbacks where not set.");
}
this.Debug("SWFUpload Init Complete");
this.PrintDebugInfo();
// Do some feature detection
if (flash.net.FileReferenceList && flash.net.FileReference && flash.external.ExternalInterface && flash.external.ExternalInterface.available) {
ExternalCall.Simple(this.flashReady_Callback);
} else {
this.Debug("Feature Detection Failed");
}
}
/* *****************************************
* FileReference Event Handlers
* *************************************** */
private function DialogCancelled_Handler():Void {
this.Debug("Event: fileDialogComplete: File Dialog window cancelled.");
ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0);
}
private function FileProgress_Handler(file:FileReference, bytesLoaded:Number, bytesTotal:Number):Void {
this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + bytesLoaded + ". Total: " + bytesTotal);
ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), bytesLoaded, bytesTotal);
}
private function FileComplete_Handler():Void {
this.successful_uploads++;
this.current_file_item.file_status = FileItem.FILE_STATUS_SUCCESS;
this.Debug("Event: uploadSuccess: File ID: " + this.current_file_item.id + " Data: n/a");
ExternalCall.UploadSuccess(this.uploadSuccess_Callback, this.current_file_item.ToJavaScriptObject());
this.UploadComplete();
}
private function HTTPError_Handler(file:FileReference, httpError:Number):Void {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
this.Debug("Event: uploadError: HTTP ERROR : File ID: " + this.current_file_item.id + ". HTTP Status: " + httpError + ".");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), httpError.toString());
this.UploadComplete();
}
// Note: Flash Player does not support Uploads that require authentication. Attempting this will trigger an
// IO Error or it will prompt for a username and password and the crash the browser (FireFox/Opera)
private function IOError_Handler(file:FileReference):Void {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
if(!this.uploadURL.length) {
this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty.");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, this.current_file_item.ToJavaScriptObject(), "IO Error");
} else {
this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error.");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), "IO Error");
}
this.UploadComplete();
}
private function SecurityError_Handler(file:FileReference, errorString:String):Void {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
this.Debug("Event: uploadError : Security Error : File Number: " + this.current_file_item.id + ". Error:" + errorString);
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), errorString);
this.UploadComplete();
}
private function Select_Many_Handler(frl:FileReferenceList):Void {
this.Select_Handler(frl.fileList);
}
private function Select_One_Handler(file:FileReference):Void {
var fileArray:Array = new Array(1);
fileArray[0] = file;
this.Select_Handler(fileArray);
}
private function Select_Handler(file_reference_list:Array):Void {
this.Debug("Select Handler: Files Selected from Dialog. Processing file list");
this.Debug("Type: " + typeof(this.fileQueueLimit) + " Value:" + this.fileQueueLimit);
// Determine how many queue slots are remaining (check the unlimited (0) settings, successful uploads and queued uploads)
var queue_slots_remaining:Number = 0;
if (this.fileUploadLimit == 0) {
queue_slots_remaining = (this.fileQueueLimit == 0) ? file_reference_list.length : (this.fileQueueLimit - this.queued_uploads); // If unlimited queue make the allowed size match however many files were selected.
} else {
var remaining_uploads:Number = this.fileUploadLimit - this.successful_uploads - this.queued_uploads;
if (remaining_uploads < 0) remaining_uploads = 0;
if (this.fileQueueLimit == 0 || this.fileQueueLimit >= remaining_uploads) {
queue_slots_remaining = remaining_uploads;
} else if (this.fileQueueLimit < remaining_uploads) {
queue_slots_remaining = this.fileQueueLimit;
}
}
// Check if the number of files selected is greater than the number allowed to queue up.
if (queue_slots_remaining < file_reference_list.length) {
this.Debug("Event: fileQueueError : Selected Files (" + file_reference_list.length + ") exceeds remaining Queue size (" + queue_slots_remaining + ").");
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining.toString());
} else {
// Process each selected file
for (var i:Number = 0; i < file_reference_list.length; i++) {
var file_item:FileItem = new FileItem(file_reference_list[i], this.movieName);
// Add the file to the index
file_item.index = this.file_index.length;
this.file_index[file_item.index] = file_item;
// The the file to see if it is acceptable
var size_result:Number = this.CheckFileSize(file_item);
var is_valid_filetype:Boolean = this.CheckFileType(file_item);
if(size_result == this.SIZE_OK && is_valid_filetype) {
this.Debug("Event: fileQueued : File ID: " + file_item.id);
this.file_queue.push(file_item);
this.queued_uploads++;
ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject());
}
else if (!is_valid_filetype) {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File not of a valid type.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_INVALID_FILETYPE, file_item.ToJavaScriptObject(), "File is not an allowed file type.");
}
else if (size_result == this.SIZE_TOO_BIG) {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File exceeds size limit.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit.");
}
else if (size_result == this.SIZE_ZERO_BYTE) {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File is zero bytes.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded.");
}
else {
file_item.file_reference = null; // Cleanup the object
this.Debug("Entered an unexpected state checking the file in Select_Handler");
}
}
}
this.Debug("Event: fileDialogComplete : Finished adding files");
ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length);
}
/* ****************************************************************
Externally exposed functions
****************************************************************** */
// Opens a file browser dialog that allows one file to be selected.
private function SelectFile():Void {
this.fileBrowserOne = new FileReference();
var fbo_listener:Object = {
onSelect : Delegate.create(this, this.Select_One_Handler),
onCancel : Delegate.create(this, this.DialogCancelled_Handler)
}
this.fileBrowserOne.addListener(fbo_listener);
// Default file type settings
var allowed_file_types:String = "*.*";
var allowed_file_types_description:String = "All Files";
// Get the instance settings
if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
this.Debug("Event: fileDialogStart : Browsing files. Single Select. Allowed file types: " + allowed_file_types);
ExternalCall.Simple(this.fileDialogStart_Callback);
this.fileBrowserOne.browse([{ description : allowed_file_types_description, extension : allowed_file_types }]);
}
// Opens a file browser dialog that allows multiple files to be selected.
private function SelectFiles():Void {
var allowed_file_types:String = "*.*";
var allowed_file_types_description:String = "All Files";
if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
this.Debug("Event: fileDialogStart : Browsing files. Multi Select. Allowed file types: " + allowed_file_types);
ExternalCall.Simple(this.fileDialogStart_Callback);
this.fileBrowserMany.browse([{ description : allowed_file_types_description, extension : allowed_file_types }]);
}
// Starts uploading. Checks to see if a file is currently uploading and, if not, starts the upload.
private function StartUpload(file_id:String):Void {
if (file_id == undefined) file_id = "";
if (this.current_file_item == null) {
this.Debug("StartUpload(): Starting Upload: " + (file_id ? "File ID:" + file_id : "First file in queue"));
this.StartFile(file_id);
} else {
this.Debug("StartUpload(): Upload run already in progress");
}
}
// Cancel the current upload and stops. Doesn't advance the upload pointer. The current file is requeued at the beginning.
private function StopUpload():Void {
if (this.current_file_item != null) {
// Cancel the upload and re-queue the FileItem
this.current_file_item.file_reference.cancel();
this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
// Remove the event handlers
this.current_file_item.file_reference.removeListener(this.file_reference_listener);
this.file_queue.unshift(this.current_file_item);
var js_object:Object = this.current_file_item.ToJavaScriptObject();
this.current_file_item = null;
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped");
ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
this.Debug("StopUpload(): upload stopped.");
} else {
this.Debug("StopUpload(): Upload run not in progress");
}
}
/* Cancels the upload specified by file_id
* If the file is currently uploading it is cancelled and the uploadComplete
* event gets called.
* If the file is not currently uploading then only the uploadCancelled event is fired.
* */
private function CancelUpload(file_id:String):Void {
var file_item:FileItem = null;
if (file_id == undefined) file_id = "";
// Check the current file item
if (this.current_file_item != null && (this.current_file_item.id == file_id || !file_id)) {
this.current_file_item.file_reference.cancel();
this.current_file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
this.upload_cancelled++;
this.Debug("Event: fileCancelled: File ID: " + this.current_file_item.id + ". Cancelling current upload");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled.");
this.UploadComplete(); // <-- this advanced the upload to the next file
} else if (file_id) {
// Find the file in the queue
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
// Remove the file from the queue
file_item = FileItem(this.file_queue[file_index]);
file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
this.file_queue[file_index] = null;
this.queued_uploads--;
this.upload_cancelled++;
// Cancel the file (just for good measure) and make the callback
file_item.file_reference.cancel();
file_item.file_reference.removeListener(this.file_reference_listener);
file_item.file_reference = null;
this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload");
this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
// Get rid of the file object
file_item = null;
}
} else {
// Get the first file and cancel it
while (this.file_queue.length > 0 && file_item == null) {
// Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
if (typeof(file_item) == "undefined") {
file_item = null;
continue;
}
}
if (file_item != null) {
file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
this.queued_uploads--;
this.upload_cancelled++;
// Cancel the file (just for good measure) and make the callback
file_item.file_reference.cancel();
file_item.file_reference.removeListener(this.file_reference_listener);
file_item.file_reference = null;
this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
// Get rid of the file object
file_item = null;
}
}
}
private function GetStats():Object {
return {
in_progress : this.current_file_item == null ? 0 : 1,
files_queued : this.queued_uploads,
successful_uploads : this.successful_uploads,
upload_errors : this.upload_errors,
upload_cancelled : this.upload_cancelled,
queue_errors : this.queue_errors
};
}
private function SetStats(stats:Object):Void {
this.successful_uploads = (typeof(stats["successful_uploads"]) === "Number") ? stats["successful_uploads"] : this.successful_uploads;
this.upload_errors = (typeof(stats["upload_errors"]) === "Number") ? stats["upload_errors"] : this.upload_errors;
this.upload_cancelled = (typeof(stats["upload_cancelled"]) === "Number") ? stats["upload_cancelled"] : this.upload_cancelled;
this.queue_errors = (typeof(stats["queue_errors"]) === "Number") ? stats["queue_errors"] : this.queue_errors;
}
private function GetFile(file_id:String):Object {
var file:FileItem = null;
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
file = this.file_queue[file_index];
} else {
if (this.current_file_item != null) {
file = this.current_file_item;
} else {
for (var i:Number = 0; i < this.file_queue.length; i++) {
file = this.file_queue[i];
if (file != null) break;
}
}
}
if (file == null) {
return null;
} else {
return file.ToJavaScriptObject();
}
}
private function GetFileByIndex(index:Number):Object {
if (index < 0 || index > this.file_index.length - 1) {
return null;
} else {
return this.file_index[index].ToJavaScriptObject();
}
}
private function AddFileParam(file_id:String, name:String, value:String):Boolean {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file_item:FileItem = FileItem(this.file_queue[file_index]);
file_item.AddParam(name, value);
return true;
} else {
return false;
}
}
private function RemoveFileParam(file_id:String, name:String):Boolean {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file_item:FileItem = FileItem(this.file_queue[file_index]);
file_item.RemoveParam(name);
return true;
} else {
return false;
}
}
private function SetUploadURL(url:String):Void {
if (typeof(url) !== "undefined" && url !== "") {
this.uploadURL = url;
}
}
private function SetPostParams(post_object:Object):Void {
if (typeof(post_object) !== "undefined" && post_object !== null) {
this.uploadPostObject = post_object;
}
}
private function SetFileTypes(types:String, description:String):Void {
this.fileTypes = types;
this.fileTypesDescription = description;
this.LoadFileExensions(this.fileTypes);
}
private function SetFileSizeLimit(bytes:Number):Void {
if (bytes < 0) bytes = 0;
this.fileSizeLimit = bytes;
}
private function SetFileUploadLimit(file_upload_limit:Number):Void {
if (file_upload_limit < 0) file_upload_limit = 0;
this.fileUploadLimit = file_upload_limit;
}
private function SetFileQueueLimit(file_queue_limit:Number):Void {
if (file_queue_limit < 0) file_queue_limit = 0;
this.fileQueueLimit = file_queue_limit;
}
private function SetFilePostName(file_post_name:String):Void {
if (file_post_name != "") {
this.filePostName = file_post_name;
}
}
private function SetDebugEnabled(debug_enabled:Boolean):Void {
this.debugEnabled = debug_enabled;
}
/* *************************************************************
File processing and handling functions
*************************************************************** */
//
private function StartFile(file_id:String):Void {
if (file_id == undefined) file_id = "";
// Only upload a file uploads are being processed.
// startFile could be called by a file cancellation even when we aren't currently uploading
if (this.current_file_item != null) {
this.Debug("StartFile(): Upload already in progress. Not starting another upload.");
}
this.Debug("StartFile: " + (file_id ? "File ID: " + file_id : "First file in queue"));
// Check the upload limit
if (this.successful_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) {
this.Debug("Event: uploadError : Upload limit reached. No more files can be uploaded.");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, null, "The upload limit has been reached.");
this.current_file_item = null;
return;
}
// Get the next file to upload
if (!file_id) {
while (this.file_queue.length > 0 && this.current_file_item == null) {
// Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
this.current_file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
if (typeof(this.current_file_item) == "undefined") {
this.current_file_item = null;
continue;
}
}
} else {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
// Set the file as the current upload and remove it from the queue
this.current_file_item = FileItem(this.file_queue[file_index]);
this.file_queue[file_index] = null;
} else {
this.Debug("Event: uploadError : File ID not found in queue: " + file_id);
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND, null, "File ID not queued.");
}
}
// Start the upload if we found an item to upload
if (this.current_file_item != null) {
// Begin the upload
this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id);
ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject());
}
// Otherwise we've would have looped through all the FileItems. This means the queue is empty)
else {
this.Debug("startFile(): No File Reference found. There are no files left to upload.\nstartFile(): Ending upload run.");
}
}
// This starts the upload when the user returns TRUE from the uploadStart event. Rather than just have the value returned from
// the function we do a return function call so we can use the setTimeout work-around for Flash/JS circular calls.
private function ReturnUploadStart(start_upload:Boolean):Void {
if (this.current_file_item == null) {
this.Debug("ReturnUploadStart called but file was no longer queued. This is okay if the file was stopped or cancelled.");
return;
}
if (start_upload) {
try {
// Set the event handlers
this.current_file_item.file_reference.addListener(this.file_reference_listener);
// Upload the file
var url:String = this.BuildRequest();
this.Debug("startFile(): File Reference found. File accepted by startUpload event. Starting upload to " + this.uploadURL + " for File ID: " + this.current_file_item.id);
this.current_file_item.file_reference.upload(url, this.filePostName, false);
} catch (ex:Error) {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
var message:String = ex.name + "\n" + ex.message;
this.Debug("Event: uploadError(): Unhandled exception: " + message);
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message);
this.UploadComplete();
}
this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS;
} else {
this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading file.");
// Remove the event handlers
this.current_file_item.file_reference.removeListener(this.file_reference_listener);
// Re-queue the FileItem
this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
var js_object:Object = this.current_file_item.ToJavaScriptObject();
this.file_queue.unshift(this.current_file_item);
this.current_file_item = null;
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file.");
ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
this.Debug("startFile(): upload rejected by startUpload event. File re-queued.");
}
}
// Completes the file upload by deleting it's reference, advancing the pointer.
// Once this event files a new upload can be started.
private function UploadComplete():Void {
var jsFileObj:Object = this.current_file_item.ToJavaScriptObject();
this.current_file_item.file_reference.removeListener(this.file_reference_listener);
this.current_file_item.file_reference = null;
this.current_file_item = null;
this.queued_uploads--;
this.Debug("Event: uploadComplete : Upload cycle complete.");
ExternalCall.UploadComplete(this.uploadComplete_Callback, jsFileObj);
}
/* *************************************************************
Utility Functions
*************************************************************** */
// Check the size of the file against the allowed file size.
private function CheckFileSize(file_item:FileItem):Number {
if (file_item.file_reference.size == 0) {
return this.SIZE_ZERO_BYTE;
} else if (this.fileSizeLimit != 0 && file_item.file_reference.size > (this.fileSizeLimit * 1000)) {
return this.SIZE_TOO_BIG;
} else {
return this.SIZE_OK;
}
}
private function CheckFileType(file_item:FileItem):Boolean {
// If no extensions are defined then a *.* was passed and the check is unnecessary
if (this.valid_file_extensions.length == 0) {
return true;
}
var fileRef:FileReference = file_item.file_reference;
var last_dot_index:Number = fileRef.name.lastIndexOf(".");
var extension:String = "";
if (last_dot_index >= 0) {
extension = fileRef.name.substr(last_dot_index + 1).toLowerCase();
}
var is_valid_filetype:Boolean = false;
for (var i:Number=0; i < this.valid_file_extensions.length; i++) {
if (String(this.valid_file_extensions[i]) == extension) {
is_valid_filetype = true;
break;
}
}
return is_valid_filetype;
}
private function BuildRequest():String {
// Create the request object
var file_post:Object = this.current_file_item.GetPostObject();
var key:String;
var url:String = this.uploadURL;
var pairs:Array = new Array();
for (key in this.uploadPostObject) {
this.Debug("Global URL Item: " + key + "=" + this.uploadPostObject[key]);
if (this.uploadPostObject.hasOwnProperty(key)) {
pairs.push(key + "=" + this.uploadPostObject[key]);
}
}
for (key in file_post) {
this.Debug("File Post Item: " + key + "=" + this.uploadPostObject[key]);
if (file_post.hasOwnProperty(key)) {
pairs.push(escape(key) + "=" + escape(file_post[key]));
}
}
url = this.uploadURL + (this.uploadURL.indexOf("?") > -1 ? "&" : "?") + pairs.join("&");
return url;
}
private function Debug(msg:String):Void {
if (this.debugEnabled) {
var lines:Array = msg.split("\n");
for (var i:Number=0; i < lines.length; i++) {
lines[i] = "SWF DEBUG: " + lines[i];
}
try {
ExternalCall.Debug(this.debug_Callback, lines.join("\n"));
} catch (ex:Error) {
// pretend nothing happened
}
}
}
private function PrintDebugInfo():Void {
var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n"
+ "Build Number: " + this.build_number + "\n"
+ "movieName: " + this.movieName + "\n"
+ "Upload URL: " + this.uploadURL + "\n"
+ "File Types String: " + this.fileTypes + "\n"
+ "Parsed File Types: " + this.valid_file_extensions.toString() + "\n"
+ "File Types Description: " + this.fileTypesDescription + "\n"
+ "File Size Limit: " + this.fileSizeLimit + "\n"
+ "File Upload Limit: " + this.fileUploadLimit + "\n"
+ "File Queue Limit: " + this.fileQueueLimit + "\n"
+ "Post Params:\n";
for (var key:String in this.uploadPostObject) {
debug_info += " " + key + "=" + this.uploadPostObject[key] + "\n";
}
debug_info += "----- END SWF DEBUG OUTPUT ----\n";
this.Debug(debug_info);
}
private function FindIndexInFileQueue(file_id:String):Number {
for (var i:Number = 0; i<this.file_queue.length; i++) {
var item:FileItem = this.file_queue[i];
if (item != null && item.id == file_id) return i;
}
return -1;
}
// Parse the file extensions in to an array so we can validate them agains
// the files selected later.
private function LoadFileExensions(filetypes:String):Void {
var extensions:Array = filetypes.split(";");
this.valid_file_extensions = new Array();
for (var i:Number=0; i < extensions.length; i++) {
var extension:String = String(extensions[i]);
var dot_index:Number = extension.lastIndexOf(".");
if (dot_index >= 0) {
extension = extension.substr(dot_index + 1).toLowerCase();
} else {
extension = extension.toLowerCase();
}
// If one of the extensions is * then we allow all files
if (extension == "*") {
this.valid_file_extensions = new Array();
break;
}
this.valid_file_extensions.push(extension);
}
}
private function loadPostParams(param_string:String):Void {
var post_object:Object = {};
if (param_string != null) {
var name_value_pairs:Array = param_string.split("&");
for (var i:Number = 0; i < name_value_pairs.length; i++) {
var name_value:String = String(name_value_pairs[i]);
var index_of_equals:Number = name_value.indexOf("=");
if (index_of_equals > 0) {
post_object[unescape(name_value.substring(0, index_of_equals))] = unescape(name_value.substr(index_of_equals + 1));
}
}
}
this.uploadPostObject = post_object;
}
}

View File

@ -0,0 +1,3 @@
@echo off
copy ..\swfupload.js ..\..\demos\swfupload
copy swfupload_f8.swf ..\..\demos\swfupload

Binary file not shown.

View File

@ -0,0 +1,117 @@
package {
import flash.external.ExternalInterface;
internal class ExternalCall
{
/*public function ExternalCall()
{
}
*/
public static function Simple(callback:String):void {
ExternalInterface.call(callback);
}
public static function FileQueued(callback:String, file_object:Object):void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function FileQueueError(callback:String, error_code:Number, file_object:Object, message:String):void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message));
}
public static function FileDialogComplete(callback:String, num_files_selected:Number):void {
ExternalInterface.call(callback, EscapeMessage(num_files_selected));
}
public static function UploadStart(callback:String, file_object:Object):void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function UploadProgress(callback:String, file_object:Object, bytes_loaded:uint, bytes_total:uint):void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(bytes_loaded), EscapeMessage(bytes_total));
}
public static function UploadSuccess(callback:String, file_object:Object, server_data:String):void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(server_data));
}
public static function UploadError(callback:String, error_code:Number, file_object:Object, message:String):void {
ExternalInterface.call(callback, EscapeMessage(file_object), EscapeMessage(error_code), EscapeMessage(message));
}
public static function UploadComplete(callback:String, file_object:Object):void {
ExternalInterface.call(callback, EscapeMessage(file_object));
}
public static function Debug(callback:String, message:String):void {
ExternalInterface.call(callback, EscapeMessage(message));
}
/* Escapes all the backslashes which are not translated correctly in the Flash -> JavaScript Interface
*
* These functions had to be developed because the ExternalInterface has a bug that simply places the
* value a string in quotes (except for a " which is escaped) in a JavaScript string literal which
* is executed by the browser. These often results in improperly escaped string literals if your
* input string has any backslash characters. For example the string:
* "c:\Program Files\uploadtools\"
* is placed in a string literal (with quotes escaped) and becomes:
* var __flash__temp = "\"c:\Program Files\uploadtools\\"";
* This statement will cause errors when executed by the JavaScript interpreter:
* 1) The first \" is succesfully transformed to a "
* 2) \P is translated to P and the \ is lost
* 3) \u is interpreted as a unicode character and causes an error in IE
* 4) \\ is translated to \
* 5) leaving an unescaped " which causes an error
*
* I fixed this by escaping \ characters in all outgoing strings. The above escaped string becomes:
* var __flash__temp = "\"c:\\Program Files\\uploadtools\\\"";
* which contains the correct string literal.
*
* Note: The "var __flash__temp = " portion of the example is part of the ExternalInterface not part of
* my escaping routine.
*/
private static function EscapeMessage(message:*):* {
if (message is String) {
message = EscapeString(message);
}
else if (message is Array) {
message = EscapeArray(message);
}
else if (message is Object) {
message = EscapeObject(message);
}
return message;
}
private static function EscapeString(message:String):String {
var replacePattern:RegExp = /\\/g; //new RegExp("/\\/", "g");
return message.replace(replacePattern, "$&$&");
}
private static function EscapeArray(message_array:Array):Array {
var length:uint = message_array.length;
var i:uint = 0;
for (i; i < length; i++) {
message_array[i] = EscapeMessage(message_array[i]);
}
return message_array;
}
private static function EscapeObject(message_obj:Object):Object {
for (var name:String in message_obj) {
message_obj[name] = EscapeMessage(message_obj[name]);
}
return message_obj;
}
}
}

74
thirdparty/swfupload/Flash9/FileItem.as vendored Normal file
View File

@ -0,0 +1,74 @@
package {
import flash.net.FileReference;
internal class FileItem
{
private static var file_id_sequence:Number = 0; // tracks the file id sequence
private var postObject:Object;
public var file_reference:FileReference;
public var id:String;
public var index:Number = -1;
public var file_status:int = 0;
private var js_object:Object;
public static var FILE_STATUS_QUEUED:int = -1;
public static var FILE_STATUS_IN_PROGRESS:int = -2;
public static var FILE_STATUS_ERROR:int = -3;
public static var FILE_STATUS_SUCCESS:int = -4;
public static var FILE_STATUS_CANCELLED:int = -5;
public static var FILE_STATUS_NEW:int = -6; // This file status should never be sent to JavaScript
public function FileItem(file_reference:FileReference, control_id:String)
{
this.postObject = {};
this.file_reference = file_reference;
this.id = control_id + "_" + (FileItem.file_id_sequence++);
this.file_status = FileItem.FILE_STATUS_NEW;
this.js_object = {
id: this.id,
index: this.index,
post: this.GetPostObject()
};
// Cleanly attempt to retrieve the FileReference info
// this can fail and so is wrapped in try..catch
try {
this.js_object.name = this.file_reference.name;
this.js_object.size = this.file_reference.size;
this.js_object.type = this.file_reference.type;
this.js_object.creationdate = this.file_reference.creationDate;
this.js_object.modificationdate = this.file_reference.modificationDate;
} catch (ex:Error) {
this.file_status = FileItem.FILE_STATUS_ERROR;
}
this.js_object.filestatus = this.file_status;
}
public function AddParam(name:String, value:String):void {
this.postObject[name] = value;
}
public function RemoveParam(name:String):void {
delete this.postObject[name];
}
public function GetPostObject():Object {
return this.postObject;
}
// Create the simply file object that is passed to the browser
public function ToJavaScriptObject():Object {
this.js_object.file_status = this.file_status;
this.js_object.post = this.GetPostObject();
return this.js_object;
}
public function toString():String {
return "FileItem - ID: " + this.id;
}
}
}

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<!-- Output SWF options -->
<output>
<movie disabled="False" />
<movie input="" />
<movie path="swfupload_f9.swf" />
<movie fps="1" />
<movie width="1" />
<movie height="1" />
<movie version="9" />
<movie background="#FFFFFF" />
</output>
<!-- Other classes to be compiled into your SWF -->
<classpaths>
<class path="." />
</classpaths>
<!-- Build options -->
<build>
<option accessible="False" />
<option allowSourcePathOverlap="False" />
<option benchmark="False" />
<option es="False" />
<option loadConfig="" />
<option optimize="True" />
<option showActionScriptWarnings="True" />
<option showBindingWarnings="True" />
<option showDeprecationWarnings="True" />
<option showUnusedTypeSelectorWarnings="True" />
<option strict="True" />
<option useNetwork="True" />
<option useResourceBundleMetadata="True" />
<option warnings="True" />
<option verboseStackTraces="False" />
<option additional="" />
<option customSDK="" />
</build>
<!-- SWC Include Libraries -->
<includeLibraries>
<!-- example: <element path="..." /> -->
</includeLibraries>
<!-- SWC Libraries -->
<libraryPaths>
<!-- example: <element path="..." /> -->
</libraryPaths>
<!-- External Libraries -->
<externalLibraryPaths>
<!-- example: <element path="..." /> -->
</externalLibraryPaths>
<!-- Runtime Shared Libraries -->
<rslPaths>
<!-- example: <element path="..." /> -->
</rslPaths>
<!-- Assets to embed into the output SWF -->
<library>
<!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
</library>
<!-- Class files to compile (other referenced classes will automatically be included) -->
<compileTargets>
<compile path="SWFUpload.as" />
</compileTargets>
<!-- Paths to exclude from the Project Explorer tree -->
<hiddenPaths>
<!-- example: <hidden path="..." /> -->
</hiddenPaths>
<!-- Executed before build -->
<preBuildCommand />
<!-- Executed after build -->
<postBuildCommand alwaysRun="True">deploy.bat</postBuildCommand>
<!-- Other project options -->
<options>
<option showHiddenPaths="False" />
<option testMovie="NewWindow" />
</options>
</project>

920
thirdparty/swfupload/Flash9/SWFUpload.as vendored Normal file
View File

@ -0,0 +1,920 @@
package {
/*
* Todo:
* I should look in to using array.splice to remove cancelled files from the array.
* Add GetFile(file_id) function that returns the FileItem js object for any file (defaults to current or first in queue).
* */
import flash.display.Stage;
import flash.display.Sprite;
import flash.net.FileReferenceList;
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.events.*;
import flash.external.ExternalInterface;
import flash.system.Security;
import FileItem;
import ExternalCall;
public class SWFUpload extends Sprite {
// Cause SWFUpload to start as soon as the movie starts
public static function main():void
{
var SWFUpload:SWFUpload = new SWFUpload();
}
private const build_number:String = "SWFUPLOAD 2.0.1 FP9 2007-12-05 0001";
// State tracking variables
private var fileBrowserMany:FileReferenceList = new FileReferenceList();
private var fileBrowserOne:FileReference = null; // This isn't set because it can't be reused like the FileReferenceList. It gets setup in the SelectFile method
private var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded.
private var current_file_item:FileItem = null; // the item that is currently being uploaded.
private var file_index:Array = new Array();
private var successful_uploads:Number = 0; // Tracks the uploads that have been completed
private var queue_errors:Number = 0; // Tracks files rejected during queueing
private var upload_errors:Number = 0; // Tracks files that fail upload
private var upload_cancelled:Number = 0; // Tracks number of cancelled files
private var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded.
private var valid_file_extensions:Array = new Array();// Holds the parsed valid extensions.
// Callbacks
private var flashReady_Callback:String;
private var fileDialogStart_Callback:String;
private var fileQueued_Callback:String;
private var fileQueueError_Callback:String;
private var fileDialogComplete_Callback:String;
private var uploadStart_Callback:String;
private var uploadProgress_Callback:String;
private var uploadError_Callback:String;
private var uploadSuccess_Callback:String;
private var uploadComplete_Callback:String;
private var debug_Callback:String;
// Values passed in from the HTML
private var movieName:String;
private var uploadURL:String;
private var filePostName:String;
private var uploadPostObject:Object;
private var fileTypes:String;
private var fileTypesDescription:String;
private var fileSizeLimit:Number;
private var fileUploadLimit:Number = 0;
private var fileQueueLimit:Number = 0;
private var debugEnabled:Boolean;
// Error code "constants"
// Size check constants
private var SIZE_TOO_BIG:Number = 1;
private var SIZE_ZERO_BYTE:Number = -1;
private var SIZE_OK:Number = 0;
// Queue errors
private var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -100;
private var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -110;
private var ERROR_CODE_ZERO_BYTE_FILE:Number = -120;
private var ERROR_CODE_INVALID_FILETYPE:Number = -130;
// Upload Errors
private var ERROR_CODE_HTTP_ERROR:Number = -200;
private var ERROR_CODE_MISSING_UPLOAD_URL:Number = -210;
private var ERROR_CODE_IO_ERROR:Number = -220;
private var ERROR_CODE_SECURITY_ERROR:Number = -230;
private var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -240;
private var ERROR_CODE_UPLOAD_FAILED:Number = -250;
private var ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND:Number = -260;
private var ERROR_CODE_FILE_VALIDATION_FAILED:Number = -270;
private var ERROR_CODE_FILE_CANCELLED:Number = -280;
private var ERROR_CODE_UPLOAD_STOPPED:Number = -290;
public function SWFUpload() {
Security.allowDomain("*"); // Allow uploading to any domain
// Keep Flash Player busy so it doesn't show the "flash script is running slowly" error
var counter:Number = 0;
root.addEventListener(Event.ENTER_FRAME, function ():void { if (++counter > 100) counter = 100; });
// Setup file FileReferenceList events
this.fileBrowserMany.addEventListener(Event.SELECT, this.Select_Many_Handler);
this.fileBrowserMany.addEventListener(Event.CANCEL, this.DialogCancelled_Handler);
// Get the move name
this.movieName = root.loaderInfo.parameters.movieName;
// **Configure the callbacks**
// The JavaScript tracks all the instances of SWFUpload on a page. We can access the instance
// associated with this SWF file using the movieName. Each callback is accessible by making
// a call directly to it on our instance. There is no error handling for undefined callback functions.
// A developer would have to deliberately remove the default functions,set the variable to null, or remove
// it from the init function.
this.flashReady_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].flashReady";
this.fileDialogStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogStart";
this.fileQueued_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueued";
this.fileQueueError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueueError";
this.fileDialogComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogComplete";
this.uploadStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadStart";
this.uploadProgress_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadProgress";
this.uploadError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadError";
this.uploadSuccess_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadSuccess";
this.uploadComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadComplete";
this.debug_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].debug";
// Get the Flash Vars
this.uploadURL = root.loaderInfo.parameters.uploadURL;
this.filePostName = root.loaderInfo.parameters.filePostName;
this.fileTypes = root.loaderInfo.parameters.fileTypes;
this.fileTypesDescription = root.loaderInfo.parameters.fileTypesDescription + " (" + this.fileTypes + ")";
this.loadPostParams(root.loaderInfo.parameters.params);
if (!this.filePostName) {
this.filePostName = "Filedata";
}
if (!this.fileTypes) {
this.fileTypes = "*.*";
}
if (!this.fileTypesDescription) {
this.fileTypesDescription = "All Files";
}
this.LoadFileExensions(this.fileTypes);
try {
this.debugEnabled = root.loaderInfo.parameters.debugEnabled == "true" ? true : false;
} catch (ex:Object) {
this.debugEnabled = false;
}
try {
this.fileSizeLimit = Number(root.loaderInfo.parameters.fileSizeLimit);
if (this.fileSizeLimit < 0) this.fileSizeLimit = 0;
} catch (ex:Object) {
this.fileSizeLimit = 0;
}
try {
this.fileUploadLimit = Number(root.loaderInfo.parameters.fileUploadLimit);
if (this.fileUploadLimit < 0) this.fileUploadLimit = 0;
} catch (ex:Object) {
this.fileUploadLimit = 0;
}
try {
this.fileQueueLimit = Number(root.loaderInfo.parameters.fileQueueLimit);
if (this.fileQueueLimit < 0) this.fileQueueLimit = 0;
} catch (ex:Object) {
this.fileQueueLimit = 0;
}
// Set the queue limit to match the upload limit when the queue limit is bigger than the upload limit
if (this.fileQueueLimit > this.fileUploadLimit && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
// The the queue limit is unlimited and the upload limit is not then set the queue limit to the upload limit
if (this.fileQueueLimit == 0 && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
try {
ExternalInterface.addCallback("SelectFile", this.SelectFile);
ExternalInterface.addCallback("SelectFiles", this.SelectFiles);
ExternalInterface.addCallback("StartUpload", this.StartUpload);
ExternalInterface.addCallback("ReturnUploadStart", this.ReturnUploadStart);
ExternalInterface.addCallback("StopUpload", this.StopUpload);
ExternalInterface.addCallback("CancelUpload", this.CancelUpload);
ExternalInterface.addCallback("GetStats", this.GetStats);
ExternalInterface.addCallback("SetStats", this.SetStats);
ExternalInterface.addCallback("GetFile", this.GetFile);
ExternalInterface.addCallback("GetFileByIndex", this.GetFileByIndex);
ExternalInterface.addCallback("AddFileParam", this.AddFileParam);
ExternalInterface.addCallback("RemoveFileParam", this.RemoveFileParam);
ExternalInterface.addCallback("SetUploadURL", this.SetUploadURL);
ExternalInterface.addCallback("SetPostParams", this.SetPostParams);
ExternalInterface.addCallback("SetFileTypes", this.SetFileTypes);
ExternalInterface.addCallback("SetFileSizeLimit", this.SetFileSizeLimit);
ExternalInterface.addCallback("SetFileUploadLimit", this.SetFileUploadLimit);
ExternalInterface.addCallback("SetFileQueueLimit", this.SetFileQueueLimit);
ExternalInterface.addCallback("SetFilePostName", this.SetFilePostName);
ExternalInterface.addCallback("SetDebugEnabled", this.SetDebugEnabled);
} catch (ex:Error) {
this.Debug("Callbacks where not set.");
}
this.Debug("SWFUpload Init Complete");
this.PrintDebugInfo();
// Do some feature detection
if (flash.net.FileReferenceList && flash.net.FileReference && flash.net.URLRequest && flash.external.ExternalInterface && flash.external.ExternalInterface.available && DataEvent.UPLOAD_COMPLETE_DATA) {
ExternalCall.Simple(this.flashReady_Callback);
} else {
this.Debug("Feature Detection Failed");
}
}
/* *****************************************
* FileReference Event Handlers
* *************************************** */
private function DialogCancelled_Handler(event:Event):void {
this.Debug("Event: fileDialogComplete: File Dialog window cancelled.");
ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0);
}
private function FileProgress_Handler(event:ProgressEvent):void {
this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + event.bytesLoaded + ". Total: " + event.bytesTotal);
ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), event.bytesLoaded, event.bytesTotal);
}
private function ServerData_Handler(event:DataEvent):void {
this.successful_uploads++;
this.current_file_item.file_status = FileItem.FILE_STATUS_SUCCESS;
this.Debug("Event: uploadSuccess: File ID: " + this.current_file_item.id + " Data: " + event.data);
ExternalCall.UploadSuccess(this.uploadSuccess_Callback, this.current_file_item.ToJavaScriptObject(), event.data);
this.UploadComplete();
}
private function HTTPError_Handler(event:HTTPStatusEvent):void {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
this.Debug("Event: uploadError: HTTP ERROR : File ID: " + this.current_file_item.id + ". HTTP Status: " + event.status + ".");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), event.status.toString());
this.UploadComplete();
}
// Note: Flash Player does not support Uploads that require authentication. Attempting this will trigger an
// IO Error or it will prompt for a username and password and the crash the browser (FireFox/Opera)
private function IOError_Handler(event:IOErrorEvent):void {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
if(!this.uploadURL.length) {
this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty.");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, this.current_file_item.ToJavaScriptObject(), event.text);
} else {
this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error.");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), event.text);
}
this.UploadComplete();
}
private function SecurityError_Handler(event:SecurityErrorEvent):void {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
this.Debug("Event: uploadError : Security Error : File Number: " + this.current_file_item.id + ". Error:" + event.text);
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), event.text);
this.UploadComplete();
}
private function Select_Many_Handler(event:Event):void {
this.Select_Handler(this.fileBrowserMany.fileList);
}
private function Select_One_Handler(event:Event):void {
var fileArray:Array = new Array(1);
fileArray[0] = this.fileBrowserOne;
this.Select_Handler(fileArray);
}
private function Select_Handler(file_reference_list:Array):void {
this.Debug("Select Handler: Files Selected from Dialog. Processing file list");
// Determine how many queue slots are remaining (check the unlimited (0) settings, successful uploads and queued uploads)
var queue_slots_remaining:Number = 0;
if (this.fileUploadLimit == 0) {
queue_slots_remaining = this.fileQueueLimit == 0 ? file_reference_list.length : (this.fileQueueLimit - this.queued_uploads); // If unlimited queue make the allowed size match however many files were selected.
} else {
var remaining_uploads:Number = this.fileUploadLimit - this.successful_uploads - this.queued_uploads;
if (remaining_uploads < 0) remaining_uploads = 0;
if (this.fileQueueLimit == 0 || this.fileQueueLimit >= remaining_uploads) {
queue_slots_remaining = remaining_uploads;
} else if (this.fileQueueLimit < remaining_uploads) {
queue_slots_remaining = this.fileQueueLimit;
}
}
// Check if the number of files selected is greater than the number allowed to queue up.
if (queue_slots_remaining < file_reference_list.length) {
this.Debug("Event: fileQueueError : Selected Files (" + file_reference_list.length + ") exceeds remaining Queue size (" + queue_slots_remaining + ").");
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining.toString());
} else {
// Process each selected file
for (var i:Number = 0; i < file_reference_list.length; i++) {
var file_item:FileItem = new FileItem(file_reference_list[i], this.movieName);
// Add the file to the index
file_item.index = this.file_index.length;
this.file_index[file_item.index] = file_item;
// Verify that the file is accessible. Zero byte files and possibly other conditions can cause a file to be inaccessible.
var jsFileObj:Object = file_item.ToJavaScriptObject();
var is_valid_file_reference:Boolean = (jsFileObj.filestatus !== FileItem.FILE_STATUS_ERROR);
if (is_valid_file_reference) {
// Check the size, if it's within the limit add it to the upload list.
var size_result:Number = this.CheckFileSize(file_item);
var is_valid_filetype:Boolean = this.CheckFileType(file_item);
if(size_result == this.SIZE_OK && is_valid_filetype) {
this.Debug("Event: fileQueued : File ID: " + file_item.id);
file_item.file_status = FileItem.FILE_STATUS_QUEUED;
this.file_queue.push(file_item);
this.queued_uploads++;
ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject());
}
else if (!is_valid_filetype) {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File not of a valid type.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_INVALID_FILETYPE, file_item.ToJavaScriptObject(), "File is not an allowed file type.");
}
else if (size_result == this.SIZE_TOO_BIG) {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File exceeds size limit.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit.");
} else if (size_result == this.SIZE_ZERO_BYTE) {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File is zero bytes.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded.");
}
} else {
file_item.file_reference = null; // Cleanup the object
this.Debug("Event: fileQueueError : File is zero bytes or FileReference is invalid.");
this.queue_errors++;
ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes or cannot be accessed and cannot be uploaded.");
}
}
}
this.Debug("Event: fileDialogComplete : Finished adding files");
ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length);
}
/* ****************************************************************
Externally exposed functions
****************************************************************** */
// Opens a file browser dialog that allows one file to be selected.
private function SelectFile():void {
this.fileBrowserOne = new FileReference();
this.fileBrowserOne.addEventListener(Event.SELECT, this.Select_One_Handler);
this.fileBrowserOne.addEventListener(Event.CANCEL, this.DialogCancelled_Handler);
// Default file type settings
var allowed_file_types:String = "*.*";
var allowed_file_types_description:String = "All Files";
// Get the instance settings
if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
this.Debug("Event: fileDialogStart : Browsing files. Single Select. Allowed file types: " + allowed_file_types);
ExternalCall.Simple(this.fileDialogStart_Callback);
this.fileBrowserOne.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]);
}
// Opens a file browser dialog that allows multiple files to be selected.
private function SelectFiles():void {
var allowed_file_types:String = "*.*";
var allowed_file_types_description:String = "All Files";
if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
this.Debug("Event: fileDialogStart : Browsing files. Multi Select. Allowed file types: " + allowed_file_types);
ExternalCall.Simple(this.fileDialogStart_Callback);
this.fileBrowserMany.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]);
}
// Starts uploading. Checks to see if a file is currently uploading and, if not, starts the upload.
private function StartUpload(file_id:String = ""):void {
if (this.current_file_item == null) {
this.Debug("StartUpload(): Starting Upload: " + (file_id ? "File ID:" + file_id : "First file in queue"));
this.StartFile(file_id);
} else {
this.Debug("StartUpload(): Upload run already in progress");
}
}
// Cancel the current upload and stops. Doesn't advance the upload pointer. The current file is requeued at the beginning.
private function StopUpload():void {
if (this.current_file_item != null) {
// Cancel the upload and re-queue the FileItem
this.current_file_item.file_reference.cancel();
this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
// Remove the event handlers
this.removeFileReferenceEventListeners(this.current_file_item);
this.file_queue.unshift(this.current_file_item);
var js_object:Object = this.current_file_item.ToJavaScriptObject();
this.current_file_item = null;
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped");
ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
this.Debug("StopUpload(): upload stopped.");
} else {
this.Debug("StopUpload(): Upload run not in progress");
}
}
/* Cancels the upload specified by file_id
* If the file is currently uploading it is cancelled and the uploadComplete
* event gets called.
* If the file is not currently uploading then only the uploadCancelled event is fired.
* */
private function CancelUpload(file_id:String):void {
var file_item:FileItem = null;
// Check the current file item
if (this.current_file_item != null && (this.current_file_item.id == file_id || !file_id)) {
this.current_file_item.file_reference.cancel();
this.current_file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
this.upload_cancelled++;
this.Debug("Event: fileCancelled: File ID: " + this.current_file_item.id + ". Cancelling current upload");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled.");
this.UploadComplete(); // <-- this advanced the upload to the next file
} else if (file_id) {
// Find the file in the queue
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
// Remove the file from the queue
file_item = FileItem(this.file_queue[file_index]);
file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
this.file_queue[file_index] = null;
this.queued_uploads--;
this.upload_cancelled++;
// Cancel the file (just for good measure) and make the callback
file_item.file_reference.cancel();
this.removeFileReferenceEventListeners(file_item);
file_item.file_reference = null;
this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload");
this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
// Get rid of the file object
file_item = null;
}
} else {
// Get the first file and cancel it
while (this.file_queue.length > 0 && file_item == null) {
// Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
if (typeof(file_item) == "undefined") {
file_item = null;
continue;
}
}
if (file_item != null) {
file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
this.queued_uploads--;
this.upload_cancelled++;
// Cancel the file (just for good measure) and make the callback
file_item.file_reference.cancel();
this.removeFileReferenceEventListeners(file_item);
file_item.file_reference = null;
this.Debug("Event: uploadError : " + file_item.id + ". Cancelling queued upload");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
// Get rid of the file object
file_item = null;
}
}
}
private function GetStats():Object {
return {
in_progress : this.current_file_item == null ? 0 : 1,
files_queued : this.queued_uploads,
successful_uploads : this.successful_uploads,
upload_errors : this.upload_errors,
upload_cancelled : this.upload_cancelled,
queue_errors : this.queue_errors
};
}
private function SetStats(stats:Object):void {
this.successful_uploads = typeof(stats["successful_uploads"]) === "Number" ? stats["successful_uploads"] : this.successful_uploads;
this.upload_errors = typeof(stats["upload_errors"]) === "Number" ? stats["upload_errors"] : this.upload_errors;
this.upload_cancelled = typeof(stats["upload_cancelled"]) === "Number" ? stats["upload_cancelled"] : this.upload_cancelled;
this.queue_errors = typeof(stats["queue_errors"]) === "Number" ? stats["queue_errors"] : this.queue_errors;
}
private function GetFile(file_id:String):Object {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file:FileItem = this.file_queue[file_index];
} else {
if (this.current_file_item != null) {
file = this.current_file_item;
} else {
for (var i:Number = 0; i < this.file_queue.length; i++) {
file = this.file_queue[i];
if (file != null) break;
}
}
}
if (file == null) {
return null;
} else {
return file.ToJavaScriptObject();
}
}
private function GetFileByIndex(index:Number):Object {
if (index < 0 || index > this.file_index.length - 1) {
return null;
} else {
return this.file_index[index].ToJavaScriptObject();
}
}
private function AddFileParam(file_id:String, name:String, value:String):Boolean {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file_item:FileItem = FileItem(this.file_queue[file_index]);
file_item.AddParam(name, value);
return true;
} else {
return false;
}
}
private function RemoveFileParam(file_id:String, name:String):Boolean {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file_item:FileItem = FileItem(this.file_queue[file_index]);
file_item.RemoveParam(name);
return true;
} else {
return false;
}
}
private function SetUploadURL(url:String):void {
if (typeof(url) !== "undefined" && url !== "") {
this.uploadURL = url;
}
}
private function SetPostParams(post_object:Object):void {
if (typeof(post_object) !== "undefined" && post_object !== null) {
this.uploadPostObject = post_object;
}
}
private function SetFileTypes(types:String, description:String):void {
this.fileTypes = types;
this.fileTypesDescription = description;
this.LoadFileExensions(this.fileTypes);
}
private function SetFileSizeLimit(bytes:Number):void {
if (bytes < 0) bytes = 0;
this.fileSizeLimit = bytes;
}
private function SetFileUploadLimit(file_upload_limit:Number):void {
if (file_upload_limit < 0) file_upload_limit = 0;
this.fileUploadLimit = file_upload_limit;
}
private function SetFileQueueLimit(file_queue_limit:Number):void {
if (file_queue_limit < 0) file_queue_limit = 0;
this.fileQueueLimit = file_queue_limit;
}
private function SetFilePostName(file_post_name:String):void {
if (file_post_name != "") {
this.filePostName = file_post_name;
}
}
private function SetDebugEnabled(debug_enabled:Boolean):void {
this.debugEnabled = debug_enabled;
}
/* *************************************************************
File processing and handling functions
*************************************************************** */
//
private function StartFile(file_id:String = ""):void {
// Only upload a file uploads are being processed.
// startFile could be called by a file cancellation even when we aren't currently uploading
if (this.current_file_item != null) {
this.Debug("StartFile(): Upload already in progress. Not starting another upload.");
}
this.Debug("StartFile: " + (file_id ? "File ID: " + file_id : "First file in queue"));
// Check the upload limit
if (this.successful_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) {
this.Debug("Event: uploadError : Upload limit reached. No more files can be uploaded.");
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, null, "The upload limit has been reached.");
this.current_file_item = null;
return;
}
// Get the next file to upload
if (!file_id) {
while (this.file_queue.length > 0 && this.current_file_item == null) {
// Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
this.current_file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
if (typeof(this.current_file_item) == "undefined") {
this.current_file_item = null;
continue;
}
}
} else {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
// Set the file as the current upload and remove it from the queue
this.current_file_item = FileItem(this.file_queue[file_index]);
this.file_queue[file_index] = null;
} else {
this.Debug("Event: uploadError : File ID not found in queue: " + file_id);
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND, null, "File ID not queued.");
}
}
// Start the upload if we found an item to upload
if (this.current_file_item != null) {
// Begin the upload
this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id);
ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject());
}
// Otherwise we've would have looped through all the FileItems. This means the queue is empty)
else {
this.Debug("startFile(): No File Reference found. There are no files left to upload.\nstartFile(): Ending upload run.");
}
}
// This starts the upload when the user returns TRUE from the uploadStart event. Rather than just have the value returned from
// the function we do a return function call so we can use the setTimeout work-around for Flash/JS circular calls.
private function ReturnUploadStart(start_upload:Boolean):void {
if (this.current_file_item == null) {
this.Debug("ReturnUploadStart called but file was no longer queued. This is okay if the file was stopped or cancelled.");
return;
}
if (start_upload) {
try {
// Set the event handlers
this.current_file_item.file_reference.addEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler);
this.current_file_item.file_reference.addEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler);
this.current_file_item.file_reference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler);
this.current_file_item.file_reference.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler);
this.current_file_item.file_reference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler);
// Upload the file
var request:URLRequest = this.BuildRequest();
this.Debug("startFile(): File Reference found. File accepted by startUpload event. Starting upload to " + request.url + " for File ID: " + this.current_file_item.id);
this.current_file_item.file_reference.upload(request, this.filePostName, false);
} catch (ex:Error) {
this.upload_errors++;
this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
var message:String = ex.errorID + "\n" + ex.name + "\n" + ex.message + "\n" + ex.getStackTrace();
this.Debug("Event: uploadError(): Unhandled exception: " + message);
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message);
this.UploadComplete();
}
this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS;
} else {
this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading file.");
// Remove the event handlers
this.removeFileReferenceEventListeners(this.current_file_item);
// Re-queue the FileItem
this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
var js_object:Object = this.current_file_item.ToJavaScriptObject();
this.file_queue.unshift(this.current_file_item);
this.current_file_item = null;
ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file.");
ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
this.Debug("startFile(): upload rejected by startUpload event. File re-queued.");
}
}
// Completes the file upload by deleting it's reference, advancing the pointer.
// Once this event files a new upload can be started.
private function UploadComplete():void {
var jsFileObj:Object = this.current_file_item.ToJavaScriptObject();
this.removeFileReferenceEventListeners(this.current_file_item);
this.current_file_item.file_reference = null;
this.current_file_item = null;
this.queued_uploads--;
this.Debug("Event: uploadComplete : Upload cycle complete.");
ExternalCall.UploadComplete(this.uploadComplete_Callback, jsFileObj);
}
/* *************************************************************
Utility Functions
*************************************************************** */
// Check the size of the file against the allowed file size. If it is less the return TRUE. If it is too large return FALSE
private function CheckFileSize(file_item:FileItem):Number {
if (file_item.file_reference.size == 0) {
return this.SIZE_ZERO_BYTE;
} else if (this.fileSizeLimit != 0 && file_item.file_reference.size > (this.fileSizeLimit * 1000)) {
return this.SIZE_TOO_BIG;
} else {
return this.SIZE_OK;
}
}
private function CheckFileType(file_item:FileItem):Boolean {
// If no extensions are defined then a *.* was passed and the check is unnecessary
if (this.valid_file_extensions.length == 0) {
return true;
}
var fileRef:FileReference = file_item.file_reference;
var last_dot_index:Number = fileRef.name.lastIndexOf(".");
var extension:String = "";
if (last_dot_index >= 0) {
extension = fileRef.name.substr(last_dot_index + 1).toLowerCase();
}
var is_valid_filetype:Boolean = false;
for (var i:Number=0; i < this.valid_file_extensions.length; i++) {
if (String(this.valid_file_extensions[i]) == extension) {
is_valid_filetype = true;
break;
}
}
return is_valid_filetype;
}
private function BuildRequest():URLRequest {
// Create the request object
var request:URLRequest = new URLRequest();
request.method = URLRequestMethod.POST;
request.url = this.uploadURL;
var file_post:Object = this.current_file_item.GetPostObject();
var key:String;
var post:URLVariables = new URLVariables();
for (key in this.uploadPostObject) {
this.Debug("Global Post Item: " + key + "=" + this.uploadPostObject[key]);
if (this.uploadPostObject.hasOwnProperty(key)) {
post[key] = this.uploadPostObject[key];
}
}
for (key in file_post) {
this.Debug("File Post Item: " + key + "=" + this.uploadPostObject[key]);
if (file_post.hasOwnProperty(key)) {
post[key] = file_post[key];
}
}
request.data = post;
return request;
}
private function Debug(msg:String):void {
if (this.debugEnabled) {
var lines:Array = msg.split("\n");
for (var i:Number=0; i < lines.length; i++) {
lines[i] = "SWF DEBUG: " + lines[i];
}
try {
ExternalCall.Debug(this.debug_Callback, lines.join("\n"));
} catch (ex:Error) {
// pretend nothing happened
}
}
}
private function PrintDebugInfo():void {
var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n";
debug_info += "Build Number: " + this.build_number + "\n";
debug_info += "movieName: " + this.movieName + "\n";
debug_info += "Upload URL: " + this.uploadURL + "\n";
debug_info += "File Types String: " + this.fileTypes + "\n";
debug_info += "Parsed File Types: " + this.valid_file_extensions.toString() + "\n";
debug_info += "File Types Description: " + this.fileTypesDescription + "\n";
debug_info += "File Size Limit: " + this.fileSizeLimit + "\n";
debug_info += "File Upload Limit: " + this.fileUploadLimit + "\n";
debug_info += "File Queue Limit: " + this.fileQueueLimit + "\n";
debug_info += "Post Params:\n";
for (var key:String in this.uploadPostObject) {
debug_info += " " + key + "=" + this.uploadPostObject[key] + "\n";
}
debug_info += "----- END SWF DEBUG OUTPUT ----\n";
this.Debug(debug_info);
}
private function FindIndexInFileQueue(file_id:String):Number {
for (var i:Number = 0; i<this.file_queue.length; i++) {
var item:FileItem = this.file_queue[i];
if (item != null && item.id == file_id) return i;
}
return -1;
}
// Parse the file extensions in to an array so we can validate them agains
// the files selected later.
private function LoadFileExensions(filetypes:String):void {
var extensions:Array = filetypes.split(";");
this.valid_file_extensions = new Array();
for (var i:Number=0; i < extensions.length; i++) {
var extension:String = String(extensions[i]);
var dot_index:Number = extension.lastIndexOf(".");
if (dot_index >= 0) {
extension = extension.substr(dot_index + 1).toLowerCase();
} else {
extension = extension.toLowerCase();
}
// If one of the extensions is * then we allow all files
if (extension == "*") {
this.valid_file_extensions = new Array();
break;
}
this.valid_file_extensions.push(extension);
}
}
private function loadPostParams(param_string:String):void {
var post_object:Object = {};
if (param_string != null) {
var name_value_pairs:Array = param_string.split("&");
for (var i:Number = 0; i < name_value_pairs.length; i++) {
var name_value:String = String(name_value_pairs[i]);
var index_of_equals:Number = name_value.indexOf("=");
if (index_of_equals > 0) {
post_object[decodeURIComponent(name_value.substring(0, index_of_equals))] = decodeURIComponent(name_value.substr(index_of_equals + 1));
}
}
}
this.uploadPostObject = post_object;
}
private function removeFileReferenceEventListeners(file_item:FileItem):void {
if (file_item != null && file_item.file_reference != null) {
file_item.file_reference.removeEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler);
file_item.file_reference.removeEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler);
file_item.file_reference.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler);
file_item.file_reference.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler);
file_item.file_reference.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler);
}
}
}
}

View File

@ -0,0 +1,3 @@
@echo off
copy ..\swfupload.js ..\..\demos\swfupload
copy swfupload_f9.swf ..\..\demos\swfupload

Binary file not shown.

11
thirdparty/swfupload/license.txt vendored Normal file
View File

@ -0,0 +1,11 @@
/**
* mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/
*
* SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
*
* SWFUpload Revision 7.0 is (c) 2007 Jake Roberts and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
*/

View File

@ -0,0 +1,50 @@
/*
Cookie Plug-in
This plug in automatically gets all the cookies for this site and adds them to the post_params.
Cookies are loaded only on initialization. The refreshCookies function can be called to update the post_params.
The cookies will override any other post params with the same name.
*/
var SWFUpload;
if (typeof(SWFUpload) === "function") {
SWFUpload.prototype.initSettings = function (old_initSettings) {
return function (init_settings) {
if (typeof(old_initSettings) === "function") {
old_initSettings.call(this, init_settings);
}
this.refreshCookies(false); // The false parameter must be sent since SWFUpload has not initialzed at this point
};
}(SWFUpload.prototype.initSettings);
// refreshes the post_params and updates SWFUpload. The send_to_flash parameters is optional and defaults to True
SWFUpload.prototype.refreshCookies = function (send_to_flash) {
if (send_to_flash !== false) send_to_flash = true;
// Get the post_params object
var post_params = this.getSetting("post_params");
// Get the cookies
var i, cookie_array = document.cookie.split(';'), ca_length = cookie_array.length, c, eq_index, name, value;
for(i = 0; i < ca_length; i++) {
c = cookie_array[i];
// Left Trim spaces
while (c.charAt(0) == " ") {
c = c.substring(1, c.length);
}
eq_index = c.indexOf("=");
if (eq_index > 0) {
name = c.substring(0, eq_index);
value = c.substring(eq_index+1);
post_params[name] = value;
}
}
if (send_to_flash) {
this.setPostParams(post_params);
}
};
}

View File

View File

@ -0,0 +1,102 @@
/*
DocumentReady Plug-in
This plugin loads SWFUpload as soon as the document is ready. You should not load SWFUpload inside window.onload using this plugin.
You can also chain other functions by calling SWFUpload.DocumentReady(your function).
Warning: Embedded Ads or other scripts that overwrite window.onload or use their own document ready functions may interfer with this plugin. You
should not set window.onload when using this plugin.
Usage Example:
var swfu = new SWFUpload(your settings object);
SWFUpload.DocumentReady(function () { alert('Document Ready!'; });
*/
var SWFUpload;
if (typeof(SWFUpload) === "function") {
// Override iniSWFUpload so SWFUpload gets inited when the document is ready rather than immediately
SWFUpload.prototype.initSWFUpload = function (old_initSWFUpload) {
return function (init_settings) {
var self = this;
if (typeof(old_initSWFUpload) === "function") {
SWFUpload.DocumentReady(function () {
old_initSWFUpload.call(self, init_settings);
});
}
}
}(SWFUpload.prototype.initSWFUpload);
// The DocumentReady function adds the passed in function to
// the functions that will be executed when the document is ready/loaded
SWFUpload.DocumentReady = function (fn) {
// Add the function to the chain
SWFUpload.DocumentReady.InternalOnloadChain = function (previous_link_fn) {
return function () {
if (typeof(previous_link_fn) === "function") {
previous_link_fn();
}
fn();
};
}(SWFUpload.DocumentReady.InternalOnloadChain);
};
SWFUpload.DocumentReady.InternalOnloadChain = null;
SWFUpload.DocumentReady.Onload = function () {
// Execute the onload function chain
if (typeof(SWFUpload.DocumentReady.InternalOnloadChain) === "function") {
SWFUpload.DocumentReady.InternalOnloadChain();
}
};
SWFUpload.DocumentReady.SetupComplete = false;
/* ********************************************
This portion of the code gets executed as soon it is loaded.
It binds the proper event for executing JavaScript is
early as possible. This is a per browser function and so
some browser sniffing is used.
This solution still has the "exposed" issue (See the Global Delegation section at http://peter.michaux.ca/article/553 )
Base solution from http://dean.edwards.name/weblog/2006/06/again/ and http://dean.edwards.name/weblog/2005/09/busted/
******************************************** */
if (!SWFUpload.DocumentReady.SetupComplete) {
// for Internet Explorer (using conditional comments)
/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
SWFUpload.DocumentReady.Onload(); // call the onload handler
}
};
SWFUpload.DocumentReady.SetupComplete = true;
/*@end @*/
}
if (!SWFUpload.DocumentReady.SetupComplete && /WebKit/i.test(navigator.userAgent)) { // sniff
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
clearInterval(_timer);
SWFUpload.DocumentReady.Onload(); // call the onload handler
}
}, 10);
SWFUpload.DocumentReady.SetupComplete = true;
}
/* for Mozilla */
if (!SWFUpload.DocumentReady.SetupComplete && document.addEventListener) {
document.addEventListener("DOMContentLoaded", SWFUpload.DocumentReady.Onload, false);
SWFUpload.DocumentReady.SetupComplete = true;
}
/* for other browsers */
if (!SWFUpload.DocumentReady.SetupComplete) {
window.onload = SWFUpload.DocumentReady.Onload;
SWFUpload.DocumentReady.SetupComplete = true;
}
}

View File

@ -0,0 +1,63 @@
/*
SWFUpload Graceful Degradation Plug-in
This plugin allows SWFUpload to display only if it is loaded successfully. Otherwise a default form is left displayed.
Usage:
To use this plugin create two HTML containers. Each should have an ID defined. One container should hold the SWFUpload UI. The other should hold the degraded UI.
The SWFUpload container should have its CSS "display" property set to "none".
If SWFUpload loads successfully the SWFUpload container will be displayed ("display" set to "block") and the
degraded container will be hidden ("display" set to "none").
Use the settings "swfupload_element_id" and "degraded_element_id" to indicate your container IDs. The default values are "swfupload_container" and "degraded_container".
*/
var SWFUpload;
if (typeof(SWFUpload) === "function") {
SWFUpload.gracefulDegradation = {};
SWFUpload.prototype.initSettings = function (old_initSettings) {
return function (init_settings) {
if (typeof(old_initSettings) === "function") {
old_initSettings.call(this, init_settings);
}
this.addSetting("swfupload_element_id", init_settings.swfupload_element_id, "swfupload_container");
this.addSetting("degraded_element_id", init_settings.degraded_element_id, "degraded_container");
this.addSetting("user_swfUploadLoaded_handler", init_settings.swfupload_loaded_handler, SWFUpload.swfUploadLoaded);
this.swfUploadLoaded_handler = SWFUpload.gracefulDegradation.swfUploadLoaded;
};
}(SWFUpload.prototype.initSettings);
SWFUpload.gracefulDegradation.swfUploadLoaded = function () {
var swfupload_container_id, swfupload_container, degraded_container_id, degraded_container, user_swfUploadLoaded_handler;
try {
swfupload_element_id = this.getSetting("swfupload_element_id");
degraded_element_id = this.getSetting("degraded_element_id");
// Show the UI container
swfupload_container = document.getElementById(swfupload_element_id);
if (swfupload_container !== null) {
swfupload_container.style.display = "block";
// Now take care of hiding the degraded UI
degraded_container = document.getElementById(degraded_element_id);
if (degraded_container !== null) {
degraded_container.style.display = "none";
}
}
} catch (ex) {
this.debug(ex);
}
user_swfUploadLoaded_handler = this.getSetting("user_swfUploadLoaded_handler");
if (typeof(user_swfUploadLoaded_handler) === "function") {
user_swfUploadLoaded_handler.apply(this);
}
};
}

View File

@ -0,0 +1,58 @@
/*
Queue Plug-in
Features:
cancelQueue method for cancelling the entire queue.
All queued files are uploaded when startUpload() is called.
If false is returned from uploadComplete then the queue upload is stopped. If false is not returned (strict comparison) then the queue upload is continued.
*/
var SWFUpload;
if (typeof(SWFUpload) === "function") {
SWFUpload.queue = {};
SWFUpload.prototype.initSettings = function (old_initSettings) {
return function (init_settings) {
if (typeof(old_initSettings) === "function") {
old_initSettings.call(this, init_settings);
}
this.customSettings.queue_cancelled_flag = false;
this.addSetting("user_upload_complete_handler", init_settings.upload_complete_handler, SWFUpload.uploadComplete);
this.uploadComplete_handler = SWFUpload.queue.uploadComplete;
};
}(SWFUpload.prototype.initSettings);
SWFUpload.prototype.cancelQueue = function () {
var stats = this.getStats();
this.customSettings.queue_cancelled_flag = false;
if (stats.in_progress > 0) {
this.customSettings.queue_cancelled_flag = true;
}
while(stats.files_queued > 0) {
this.cancelUpload();
stats = this.getStats();
}
};
SWFUpload.queue.uploadComplete = function (file) {
var user_upload_complete_handler = this.getSetting("user_upload_complete_handler");
var continue_upload = true;
if (typeof(user_upload_complete_handler) === "function") {
continue_upload = (user_upload_complete_handler.call(this, file) === false) ? false : true;
}
if (continue_upload) {
var stats = this.getStats();
if (stats.files_queued > 0 && this.customSettings.queue_cancelled_flag === false) {
this.startUpload();
} else {
this.customSettings.queue_cancelled_flag = false;
}
}
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

View File

@ -0,0 +1,73 @@
function fileQueued(file, queuelength) {
var listingfiles = document.getElementById("SWFUploadFileListingFiles");
if(!listingfiles.getElementsByTagName("ul")[0]) {
var info = document.createElement("h4");
info.appendChild(document.createTextNode("File queue"));
listingfiles.appendChild(info);
var ul = document.createElement("ul")
listingfiles.appendChild(ul);
}
listingfiles = listingfiles.getElementsByTagName("ul")[0];
var li = document.createElement("li");
li.id = file.id;
li.className = "SWFUploadFileItem";
li.innerHTML = file.name + " <span class='progressBar' id='" + file.id + "progress'></span><a id='" + file.id + "deletebtn' class='cancelbtn' href='javascript:swfu.cancelFile(\"" + file.id + "\");'><!-- IE --></a>";
listingfiles.appendChild(li);
var queueinfo = document.getElementById("queueinfo");
queueinfo.innerHTML = queuelength + " files queued";
document.getElementById(swfu.movieName + "UploadBtn").style.display = "block";
document.getElementById("cancelqueuebtn").style.display = "block";
}
function uploadFileCancelled(file, queuelength) {
var li = document.getElementById(file.id);
li.innerHTML = file.name + " - cancelled";
li.className = "SWFUploadFileItem uploadCancelled";
var queueinfo = document.getElementById("queueinfo");
queueinfo.innerHTML = queuelength + " files queued";
}
function uploadFileStart(file, position, queuelength) {
var div = document.getElementById("queueinfo");
div.innerHTML = "Uploading file " + position + " of " + queuelength;
var li = document.getElementById(file.id);
li.className += " fileUploading";
}
function uploadProgress(file, bytesLoaded) {
var progress = document.getElementById(file.id + "progress");
var percent = Math.ceil((bytesLoaded / file.size) * 200)
progress.style.background = "#f0f0f0 url(swfupload.v102.progressbar.png) no-repeat -" + (200 - percent) + "px 0";
}
function uploadError(errno) {
// SWFUpload.debug(errno);
}
function uploadFileComplete(file) {
var li = document.getElementById(file.id);
li.className = "SWFUploadFileItem uploadCompleted";
}
function cancelQueue() {
swfu.cancelQueue();
document.getElementById(swfu.movieName + "UploadBtn").style.display = "none";
document.getElementById("cancelqueuebtn").style.display = "none";
}
function uploadQueueComplete(file) {
var div = document.getElementById("queueinfo");
//div.innerHTML = "All files uploaded..."
document.getElementById("cancelqueuebtn").style.display = "none";
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

View File

@ -0,0 +1,332 @@
/*
SWFUpload v1.0.2 Plug-in
This plug in creates API compatibility with SWFUpload v1.0.2. Many SWFUpload v1.0.2 behaviors are emulated as well.
*/
var SWFUpload;
if (typeof(SWFUpload) === "function") {
SWFUpload.v102 = {};
SWFUpload.prototype.initSWFUpload = function (init_settings) {
try {
this.customSettings = {}; // A container where developers can place their own settings associated with this instance.
this.settings = {};
this.eventQueue = [];
this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
this.movieElement = null;
// Setup global control tracking
SWFUpload.instances[this.movieName] = this;
// Load the settings. Load the Flash movie.
this.initSettings(init_settings);
this.loadFlash();
this.displayDebugInfo();
} catch (ex2) {
this.debug(ex2);
}
};
SWFUpload.prototype.initSettings = function (init_settings) {
// Store v1.0.2 settings
this.customSettings["target"] = this.retrieveSetting(init_settings["target"], "");
this.customSettings["create_ui"] = this.retrieveSetting(init_settings["create_ui"], false);
this.customSettings["browse_link_class"] = this.retrieveSetting(init_settings["browse_link_class"], "SWFBrowseLink");
this.customSettings["upload_link_class"] = this.retrieveSetting(init_settings["upload_link_class"], "SWFUploadLink");
this.customSettings["browse_link_innerhtml"] = this.retrieveSetting(init_settings["browse_link_innerhtml"], "<span>Browse...</span>");
this.customSettings["upload_link_innerhtml"] = this.retrieveSetting(init_settings["upload_link_innerhtml"], "<span>Upload</span>");
this.customSettings["auto_upload"] = this.retrieveSetting(init_settings["auto_upload"], false);
// Store v1.0.2 events
this.customSettings["upload_file_queued_callback"] = this.retrieveSetting(init_settings["upload_file_queued_callback"], null);
this.customSettings["upload_file_start_callback"] = this.retrieveSetting(init_settings["upload_file_start_callback"], null);
this.customSettings["upload_file_complete_callback"] = this.retrieveSetting(init_settings["upload_file_complete_callback"], null);
this.customSettings["upload_queue_complete_callback"] = this.retrieveSetting(init_settings["upload_queue_complete_callback"], null);
this.customSettings["upload_progress_callback"] = this.retrieveSetting(init_settings["upload_progress_callback"], null);
this.customSettings["upload_dialog_cancel_callback"] = this.retrieveSetting(init_settings["upload_dialog_cancel_callback"], null);
this.customSettings["upload_file_error_callback"] = this.retrieveSetting(init_settings["upload_file_error_callback"], null);
this.customSettings["upload_file_cancel_callback"] = this.retrieveSetting(init_settings["upload_file_cancel_callback"], null);
this.customSettings["upload_queue_cancel_callback"] = this.retrieveSetting(init_settings["upload_queue_cancel_callback"], null);
this.customSettings["queue_cancelled_flag"] = false;
// Upload backend settings
this.addSetting("upload_url", init_settings["upload_script"], "");
this.addSetting("file_post_name", "Filedata");
this.addSetting("post_params", {});
// File Settings
this.addSetting("file_types", init_settings["allowed_filetypes"], "*.*");
this.addSetting("file_types_description", init_settings["allowed_filetypes_description"], "All Files");
this.addSetting("file_size_limit", init_settings["allowed_filesize"], "1024");
this.addSetting("file_upload_limit", "0");
this.addSetting("file_queue_limit", "0");
// Flash Settings
this.addSetting("flash_url", init_settings["flash_path"], "swfupload.swf");
this.addSetting("flash_width", init_settings["flash_width"], "1px");
this.addSetting("flash_height", init_settings["flash_height"], "1px");
this.addSetting("flash_color", init_settings["flash_color"], "#000000");
// Debug Settings
this.addSetting("debug_enabled", init_settings.debug, false);
// Event Handlers
this.flashReady_handler = SWFUpload.flashReady; // This is a non-overrideable event handler
this.swfUploadLoaded_handler = SWFUpload.v102.swfUploadLoaded;
this.fileDialogStart_handler = SWFUpload.fileDialogStart;
this.fileQueued_handler = SWFUpload.v102.fileQueued;
this.fileQueueError_handler = SWFUpload.v102.uploadError;
this.fileDialogComplete_handler = SWFUpload.v102.fileDialogComplete;
this.uploadStart_handler = SWFUpload.v102.uploadStart;
this.uploadProgress_handler = SWFUpload.v102.uploadProgress;
this.uploadError_handler = SWFUpload.v102.uploadError;
this.uploadSuccess_handler = SWFUpload.v102.uploadSuccess;
this.uploadComplete_handler = SWFUpload.v102.uploadComplete;
this.debug_handler = SWFUpload.v102.debug;
// Hook up the v1.0.2 methods
this.browse = SWFUpload.v102.browse;
this.upload = SWFUpload.v102.upload;
this.cancelFile = SWFUpload.v102.cancelFile;
this.cancelQueue = SWFUpload.v102.cancelQueue;
this.debugSettings = SWFUpload.v102.debugSettings;
}
// Emulate the v1.0.2 events
SWFUpload.v102.swfUploadLoaded = function() {
try {
var target_id = this.customSettings["target"];
if(target_id !== "" && target_id !== "fileinputs") {
var self = this;
var target = document.getElementById(target_id);
if (target != null) {
// Create the link for uploading
var browselink = document.createElement("a");
browselink.className = this.customSettings["browse_link_class"];
browselink.id = this.movieName + "BrowseBtn";
browselink.href = "javascript:void(0);";
browselink.onclick = function() { self.browse(); return false; }
browselink.innerHTML = this.customSettings["browse_link_innerhtml"];
target.innerHTML = "";
target.appendChild(browselink);
// Add upload btn if auto upload not used
if(this.customSettings["auto_upload"] === false) {
// Create the link for uploading
var uploadlink = document.createElement("a");
uploadlink.className = this.customSettings["upload_link_class"];
uploadlink.id = this.movieName + "UploadBtn";
uploadlink.href = "#";
uploadlink.onclick = function() { self.upload(); return false; }
uploadlink.innerHTML = this.customSettings["upload_link_innerhtml"];
target.appendChild(uploadlink);
}
}
}
}
catch (ex) {
this.debug("Exception in swfUploadLoaded");
this.debug(ex);
}
}
SWFUpload.v102.fileQueued = function(file) {
var stats = this.getStats();
var total_files = stats.successful_uploads + stats.upload_errors + stats.files_queued;
var v102fileQueued = this.customSettings["upload_file_queued_callback"];
if (typeof(v102fileQueued) === "function") {
v102fileQueued.call(this, file, total_files);
}
}
SWFUpload.v102.fileDialogComplete = function(num_selected) {
if (!!this.customSettings["auto_upload"]) {
this.startUpload();
}
};
SWFUpload.v102.uploadStart = function (file) {
var callback = this.customSettings["upload_file_start_callback"];
var stats = this.getStats();
var current_file_number = stats.successful_uploads + stats.upload_errors + 1;
var total_files = stats.successful_uploads + stats.upload_errors + stats.files_queued;
if (typeof(callback) === "function") {
callback.call(this, file, current_file_number, total_files);
}
return true;
};
SWFUpload.v102.uploadProgress = function (file, bytes_complete, bytes_total) {
var callback = this.customSettings["upload_progress_callback"];
if (typeof(callback) === "function") {
callback.call(this, file, bytes_complete, bytes_total);
}
};
SWFUpload.v102.uploadSuccess = function (file, server_data) {
var callback = this.customSettings["upload_file_complete_callback"];
if (typeof(callback) === "function") {
callback.call(this, file, server_data);
}
};
SWFUpload.v102.uploadComplete = function (file) {
var stats = this.getStats();
if (stats.files_queued > 0 && !this.customSettings["queue_cancelled_flag"]) {
// Automatically start the next upload (if the queue wasn't cancelled)
this.startUpload();
} else if (stats.files_queued === 0 && !this.customSettings["queue_cancelled_flag"]) {
// Call Queue Complete if there are no more files queued and the queue wasn't cancelled
var callback = this.customSettings["upload_queue_complete_callback"];
if (typeof(callback) === "function") {
callback.call(this, file);
}
} else {
// Don't do anything. Remove the queue cancelled flag (if the queue was cancelled it will be set again)
this.customSettings["queue_cancelled_flag"] = false;
}
}
SWFUpload.v102.uploadError = function (file, error_code, msg) {
var translated_error_code = SWFUpload.v102.translateErrorCode(error_code);
switch (error_code) {
case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
var stats = this.getStats();
var total_files = stats.successful_uploads + stats.upload_errors + stats.files_queued;
var callback = this.customSettings["upload_file_cancel_callback"];
if (typeof(callback) === "function") {
callback.call(this, file, total_files);
}
break;
defaut:
var error_callback = this.customSettings["upload_file_error_callback"];
if (error_callback === null || typeof(error_callback) !== "function") {
SWFUpload.v102.defaultHandleErrors.call(this, translated_error_code, file, msg);
} else {
error_callback.call(this, translated_error_code, file, msg);
}
}
};
SWFUpload.v102.translateErrorCode = function (error_code) {
var translated_error_code = 0;
switch (error_code) {
case SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED:
translated_error_code = -40;
break;
case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
translated_error_code = -50;
break;
case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
translated_error_code = -30;
break;
case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
translated_error_code = -30;
break;
case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
translated_error_code = -10;
break;
case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:
translated_error_code = -20;
break;
case SWFUpload.UPLOAD_ERROR.IO_ERROR:
translated_error_code = -30;
break;
case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
translated_error_code = -40;
break;
case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND:
translated_error_code = -30;
break;
case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
translated_error_code = -30;
break;
case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
translated_error_code = -10;
// FIX ME - call the upload_cancelled_callback
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
translated_error_code = -30;
break;
}
return translated_error_code;
}
// Default error handling.
SWFUpload.v102.defaultHandleErrors = function(errcode, file, msg) {
switch(errcode) {
case -10: // HTTP error
alert("Error Code: HTTP Error, File name: " + file.name + ", Message: " + msg);
break;
case -20: // No upload script specified
alert("Error Code: No upload script, File name: " + file.name + ", Message: " + msg);
break;
case -30: // IOError
alert("Error Code: IO Error, File name: " + file.name + ", Message: " + msg);
break;
case -40: // Security error
alert("Error Code: Security Error, File name: " + file.name + ", Message: " + msg);
break;
case -50: // Filesize too big
alert("Error Code: Filesize exceeds limit, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
break;
default:
alert("Error Code: " + errcode + ". File name: " + file.name + ", Message: " + msg);
}
};
SWFUpload.v102.debug = function (message) {
if (this.getSetting("debug_enabled")) {
if (window.console) {
window.console.log(message);
} else {
alert(message);
}
}
};
// Emulate the v1.0.2 function calls
SWFUpload.v102.browse = function() {
this.selectFiles();
};
SWFUpload.v102.upload = function () {
this.startUpload();
};
SWFUpload.v102.cancelFile = function (file_id) {
this.cancelUpload(file_id);
};
SWFUpload.v102.cancelQueue = function () {
var stats = this.getStats();
while (stats["files_queued"] > 0) {
this.customSettings["queue_cancelled_flag"] = true;
this.cancelUpload();
stats = this.getStats();
}
if (status.in_progress === 0) {
this.customSettings["queue_cancelled_flag"] = false;
}
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

View File

@ -0,0 +1,73 @@
.clr {
clear:both;
}
/* SWFUpload CSS */
.swfuploadbtn {
display: block;
width: 100px;
padding: 0 0 0 20px;
line-height: 16px;
}
.browsebtn {
background: url(swfupload.v102.add.png) no-repeat 0 0px;
}
.uploadbtn {
display: none;
background: url(swfupload.v102.accept.png) no-repeat 0 0px;
}
.cancelbtn {
display: block;
width: 16px;
height: 16px;
float: right;
background: url(swfupload.v102.cancel.png) no-repeat;
}
#cancelqueuebtn {
display: none;
background-image: url(swfupload.v102.cancel.png);
background-repeat: no-repeat;
background-position: 0px 0px;
background-attachment: scroll;
margin: 10px 0;
line-height: 16px;
}
#SWFUploadFileListingFiles ul {
margin: 0;
padding: 0;
list-style: none;
}
.SWFUploadFileItem {
display: block;
width: 230px;
height: 70px;
float: left;
background: #eaefea;
margin: 0 10px 10px 0;
padding: 5px;
}
.fileUploading { background: #fee727; }
.uploadCompleted { background: #d2fa7c; }
.uploadCancelled { background: #f77c7c; }
.uploadCompleted .cancelbtn, .uploadCancelled .cancelbtn {
display: none;
}
span.progressBar {
width: 200px;
display: block;
font-size: 10px;
height: 4px;
margin-top: 2px;
margin-bottom: 10px;
background-color: #CCC;
}

1032
thirdparty/swfupload/swfupload.js vendored Normal file
View File

@ -0,0 +1,1032 @@
/**
* SWFUpload v2.0 by Jacob Roberts, Nov 2007, http://www.swfupload.org, http://linebyline.blogspot.com
* -------- -------- -------- -------- -------- -------- -------- --------
* SWFUpload is (c) 2006 Lars Huring and Mammon Media and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
* See Changelog.txt for version history
*
* Development Notes:
* * This version of SWFUpload requires Flash Player 9.0.28 and should autodetect the correct flash version.
* * In Linux Flash Player 9 setting the post file variable name does not work. It is always set to "Filedata".
* * There is a lot of repeated code that could be refactored to single functions. Feel free.
* * It's dangerous to do "circular calls" between Flash and JavaScript. I've taken steps to try to work around issues
* by having the event calls pipe through setTimeout. However you should still avoid calling in to Flash from
* within the event handler methods. Especially the "startUpload" event since it cannot use the setTimeout hack.
*/
/* *********** */
/* Constructor */
/* *********** */
var SWFUpload = function (init_settings) {
this.initSWFUpload(init_settings);
};
SWFUpload.prototype.initSWFUpload = function (init_settings) {
// Remove background flicker in IE (read this: http://misterpixel.blogspot.com/2006/09/forensic-analysis-of-ie6.html)
// This doesn't have anything to do with SWFUpload but can help your UI behave better in IE.
try {
document.execCommand('BackgroundImageCache', false, true);
} catch (ex1) {
}
try {
this.customSettings = {}; // A container where developers can place their own settings associated with this instance.
this.settings = {};
this.eventQueue = [];
this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
this.movieElement = null;
// Setup global control tracking
SWFUpload.instances[this.movieName] = this;
// Load the settings. Load the Flash movie.
this.initSettings(init_settings);
this.loadFlash();
this.displayDebugInfo();
} catch (ex2) {
this.debug(ex2);
}
}
/* *************** */
/* Static thingies */
/* *************** */
SWFUpload.instances = {};
SWFUpload.movieCount = 0;
SWFUpload.QUEUE_ERROR = {
QUEUE_LIMIT_EXCEEDED : -100,
FILE_EXCEEDS_SIZE_LIMIT : -110,
ZERO_BYTE_FILE : -120,
INVALID_FILETYPE : -130
};
SWFUpload.UPLOAD_ERROR = {
HTTP_ERROR : -200,
MISSING_UPLOAD_URL : -210,
IO_ERROR : -220,
SECURITY_ERROR : -230,
UPLOAD_LIMIT_EXCEEDED : -240,
UPLOAD_FAILED : -250,
SPECIFIED_FILE_ID_NOT_FOUND : -260,
FILE_VALIDATION_FAILED : -270,
FILE_CANCELLED : -280,
UPLOAD_STOPPED : -290
};
SWFUpload.FILE_STATUS = {
QUEUED : -1,
IN_PROGRESS : -2,
ERROR : -3,
COMPLETE : -4,
CANCELLED : -5
};
/* ***************** */
/* Instance Thingies */
/* ***************** */
// init is a private method that ensures that all the object settings are set, getting a default value if one was not assigned.
SWFUpload.prototype.initSettings = function (init_settings) {
// Upload backend settings
this.addSetting("upload_url", init_settings.upload_url, "");
this.addSetting("file_post_name", init_settings.file_post_name, "Filedata");
this.addSetting("post_params", init_settings.post_params, {});
// File Settings
this.addSetting("file_types", init_settings.file_types, "*.*");
this.addSetting("file_types_description", init_settings.file_types_description, "All Files");
this.addSetting("file_size_limit", init_settings.file_size_limit, "1024");
this.addSetting("file_upload_limit", init_settings.file_upload_limit, "0");
this.addSetting("file_queue_limit", init_settings.file_queue_limit, "0");
// Flash Settings
this.addSetting("flash_url", init_settings.flash_url, "swfupload.swf");
this.addSetting("flash_width", init_settings.flash_width, "1px");
this.addSetting("flash_height", init_settings.flash_height, "1px");
this.addSetting("flash_color", init_settings.flash_color, "#FFFFFF");
// Debug Settings
this.addSetting("debug_enabled", init_settings.debug, false);
// Event Handlers
this.flashReady_handler = SWFUpload.flashReady; // This is a non-overrideable event handler
this.swfUploadLoaded_handler = this.retrieveSetting(init_settings.swfupload_loaded_handler, SWFUpload.swfUploadLoaded);
this.fileDialogStart_handler = this.retrieveSetting(init_settings.file_dialog_start_handler, SWFUpload.fileDialogStart);
this.fileQueued_handler = this.retrieveSetting(init_settings.file_queued_handler, SWFUpload.fileQueued);
this.fileQueueError_handler = this.retrieveSetting(init_settings.file_queue_error_handler, SWFUpload.fileQueueError);
this.fileDialogComplete_handler = this.retrieveSetting(init_settings.file_dialog_complete_handler, SWFUpload.fileDialogComplete);
this.uploadStart_handler = this.retrieveSetting(init_settings.upload_start_handler, SWFUpload.uploadStart);
this.uploadProgress_handler = this.retrieveSetting(init_settings.upload_progress_handler, SWFUpload.uploadProgress);
this.uploadError_handler = this.retrieveSetting(init_settings.upload_error_handler, SWFUpload.uploadError);
this.uploadSuccess_handler = this.retrieveSetting(init_settings.upload_success_handler, SWFUpload.uploadSuccess);
this.uploadComplete_handler = this.retrieveSetting(init_settings.upload_complete_handler, SWFUpload.uploadComplete);
this.debug_handler = this.retrieveSetting(init_settings.debug_handler, SWFUpload.debug);
// Other settings
this.customSettings = this.retrieveSetting(init_settings.custom_settings, {});
};
// loadFlash is a private method that generates the HTML tag for the Flash
// It then adds the flash to the "target" or to the body and stores a
// reference to the flash element in "movieElement".
SWFUpload.prototype.loadFlash = function () {
var html, target_element, container;
// Make sure an element with the ID we are going to use doesn't already exist
if (document.getElementById(this.movieName) !== null) {
return false;
}
// Get the body tag where we will be adding the flash movie
try {
target_element = document.getElementsByTagName("body")[0];
if (typeof(target_element) === "undefined" || target_element === null) {
this.debug('Could not find the BODY element. SWFUpload failed to load.');
return false;
}
} catch (ex) {
return false;
}
// Append the container and load the flash
container = document.createElement("div");
container.style.width = this.getSetting("flash_width");
container.style.height = this.getSetting("flash_height");
target_element.appendChild(container);
container.innerHTML = this.getFlashHTML(); // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
};
// Generates the embed/object tags needed to embed the flash in to the document
SWFUpload.prototype.getFlashHTML = function () {
var html = "";
// Create Mozilla Embed HTML
if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) {
// Build the basic embed html
html = '<embed type="application/x-shockwave-flash" src="' + this.getSetting("flash_url") + '" width="' + this.getSetting("flash_width") + '" height="' + this.getSetting("flash_height") + '"';
html += ' id="' + this.movieName + '" name="' + this.movieName + '" ';
html += 'bgcolor="' + this.getSetting("flash_color") + '" quality="high" menu="false" flashvars="';
html += this.getFlashVars();
html += '" />';
// Create IE Object HTML
} else {
// Build the basic Object tag
html = '<object id="' + this.movieName + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="' + this.getSetting("flash_width") + '" height="' + this.getSetting("flash_height") + '">';
html += '<param name="movie" value="' + this.getSetting("flash_url") + '">';
html += '<param name="bgcolor" value="' + this.getSetting("flash_color") + '" />';
html += '<param name="quality" value="high" />';
html += '<param name="menu" value="false" />';
html += '<param name="flashvars" value="' + this.getFlashVars() + '" />';
html += '</object>';
}
return html;
};
// This private method builds the parameter string that will be passed
// to flash.
SWFUpload.prototype.getFlashVars = function () {
// Build a string from the post param object
var param_string = this.buildParamString();
// Build the parameter string
var html = "";
html += "movieName=" + encodeURIComponent(this.movieName);
html += "&uploadURL=" + encodeURIComponent(this.getSetting("upload_url"));
html += "&params=" + encodeURIComponent(param_string);
html += "&filePostName=" + encodeURIComponent(this.getSetting("file_post_name"));
html += "&fileTypes=" + encodeURIComponent(this.getSetting("file_types"));
html += "&fileTypesDescription=" + encodeURIComponent(this.getSetting("file_types_description"));
html += "&fileSizeLimit=" + encodeURIComponent(this.getSetting("file_size_limit"));
html += "&fileUploadLimit=" + encodeURIComponent(this.getSetting("file_upload_limit"));
html += "&fileQueueLimit=" + encodeURIComponent(this.getSetting("file_queue_limit"));
html += "&debugEnabled=" + encodeURIComponent(this.getSetting("debug_enabled"));
return html;
};
SWFUpload.prototype.getMovieElement = function () {
if (typeof(this.movieElement) === "undefined" || this.movieElement === null) {
this.movieElement = document.getElementById(this.movieName);
// Fix IEs "Flash can't callback when in a form" issue (http://www.extremefx.com.ar/blog/fixing-flash-external-interface-inside-form-on-internet-explorer)
// Removed because Revision 6 always adds the flash to the body (inside a containing div)
// If you insist on adding the Flash file inside a Form then in IE you have to make you wait until the DOM is ready
// and run this code to make the form's ID available from the window object so Flash and JavaScript can communicate.
//if (typeof(window[this.movieName]) === "undefined" || window[this.moveName] !== this.movieElement) {
// window[this.movieName] = this.movieElement;
//}
}
return this.movieElement;
};
SWFUpload.prototype.buildParamString = function () {
var post_params = this.getSetting("post_params");
var param_string_pairs = [];
var i, value, name;
// Retrieve the user defined parameters
if (typeof(post_params) === "object") {
for (name in post_params) {
if (post_params.hasOwnProperty(name)) {
if (typeof(post_params[name]) === "string") {
param_string_pairs.push(encodeURIComponent(name) + "=" + encodeURIComponent(post_params[name]));
}
}
}
}
return param_string_pairs.join("&");
};
// Saves a setting. If the value given is undefined or null then the default_value is used.
SWFUpload.prototype.addSetting = function (name, value, default_value) {
if (typeof(value) === "undefined" || value === null) {
this.settings[name] = default_value;
} else {
this.settings[name] = value;
}
return this.settings[name];
};
// Gets a setting. Returns empty string if not found.
SWFUpload.prototype.getSetting = function (name) {
if (typeof(this.settings[name]) === "undefined") {
return "";
} else {
return this.settings[name];
}
};
// Gets a setting, if the setting is undefined then return the default value
// This does not affect or use the interal setting object.
SWFUpload.prototype.retrieveSetting = function (value, default_value) {
if (typeof(value) === "undefined" || value === null) {
return default_value;
} else {
return value;
}
};
// It loops through all the settings and displays
// them in the debug Console.
SWFUpload.prototype.displayDebugInfo = function () {
var key, debug_message = "";
debug_message += "----- SWFUPLOAD SETTINGS ----\nID: " + this.moveName + "\n";
debug_message += this.outputObject(this.settings);
debug_message += "----- SWFUPLOAD SETTINGS END ----\n";
debug_message += "\n";
this.debug(debug_message);
};
SWFUpload.prototype.outputObject = function (object, prefix) {
var output = "", key;
if (typeof(prefix) !== "string") {
prefix = "";
}
if (typeof(object) !== "object") {
return "";
}
for (key in object) {
if (object.hasOwnProperty(key)) {
if (typeof(object[key]) === "object") {
output += (prefix + key + ": { \n" + this.outputObject(object[key], "\t" + prefix) + prefix + "}" + "\n");
} else {
output += (prefix + key + ": " + object[key] + "\n");
}
}
}
return output;
};
/* *****************************
-- Flash control methods --
Your UI should use these
to operate SWFUpload
***************************** */
SWFUpload.prototype.selectFile = function () {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SelectFile) === "function") {
try {
movie_element.SelectFile();
}
catch (ex) {
this.debug("Could not call SelectFile: " + ex);
}
} else {
this.debug("Could not find Flash element");
}
};
SWFUpload.prototype.selectFiles = function () {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SelectFiles) === "function") {
try {
movie_element.SelectFiles();
}
catch (ex) {
this.debug("Could not call SelectFiles: " + ex);
}
} else {
this.debug("Could not find Flash element");
}
};
/* Start the upload. If a file_id is specified that file is uploaded. Otherwise the first
* file in the queue is uploaded. If no files are in the queue then nothing happens.
* This call uses setTimeout since Flash will be calling back in to JavaScript
*/
SWFUpload.prototype.startUpload = function (file_id) {
var self = this;
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.StartUpload) === "function") {
setTimeout(
function () {
try {
movie_element.StartUpload(file_id);
}
catch (ex) {
self.debug("Could not call StartUpload: " + ex);
}
}, 0
);
} else {
this.debug("Could not find Flash element");
}
};
/* Cancels a the file upload. You must specify a file_id */
SWFUpload.prototype.cancelUpload = function (file_id) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.CancelUpload) === "function") {
try {
movie_element.CancelUpload(file_id);
}
catch (ex) {
this.debug("Could not call CancelUpload: " + ex);
}
} else {
this.debug("Could not find Flash element");
}
};
// Stops the current upload. The file is re-queued. If nothing is currently uploading then nothing happens.
SWFUpload.prototype.stopUpload = function () {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.StopUpload) === "function") {
try {
movie_element.StopUpload();
}
catch (ex) {
this.debug("Could not call StopUpload: " + ex);
}
} else {
this.debug("Could not find Flash element");
}
};
/* ************************
* Settings methods
* These methods change the settings inside SWFUpload
* They shouldn't need to be called in a setTimeout since they
* should not call back from Flash to JavaScript (except perhaps in a Debug call)
* and some need to return data so setTimeout won't work.
*/
/* Gets the file statistics object. It looks like this (where n = number):
{
files_queued: n,
complete_uploads: n,
upload_errors: n,
uploads_cancelled: n,
queue_errors: n
}
*/
SWFUpload.prototype.getStats = function () {
var self = this;
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.GetStats) === "function") {
try {
return movie_element.GetStats();
}
catch (ex) {
self.debug("Could not call GetStats");
}
} else {
this.debug("Could not find Flash element");
}
};
SWFUpload.prototype.setStats = function (stats_object) {
var self = this;
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetStats) === "function") {
try {
movie_element.SetStats(stats_object);
}
catch (ex) {
self.debug("Could not call SetStats");
}
} else {
this.debug("Could not find Flash element");
}
};
SWFUpload.prototype.getFile = function (file_id) {
var self = this;
var movie_element = this.getMovieElement();
if (typeof(file_id) === "number") {
if (movie_element !== null && typeof(movie_element.GetFileByIndex) === "function") {
try {
return movie_element.GetFileByIndex(file_id);
}
catch (ex) {
self.debug("Could not call GetFileByIndex");
}
} else {
this.debug("Could not find Flash element");
}
} else {
if (movie_element !== null && typeof(movie_element.GetFile) === "function") {
try {
return movie_element.GetFile(file_id);
}
catch (ex) {
self.debug("Could not call GetFile");
}
} else {
this.debug("Could not find Flash element");
}
}
};
SWFUpload.prototype.addFileParam = function (file_id, name, value) {
var self = this;
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.AddFileParam) === "function") {
try {
return movie_element.AddFileParam(file_id, name, value);
}
catch (ex) {
self.debug("Could not call AddFileParam");
}
} else {
this.debug("Could not find Flash element");
}
};
SWFUpload.prototype.removeFileParam = function (file_id, name) {
var self = this;
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.RemoveFileParam) === "function") {
try {
return movie_element.RemoveFileParam(file_id, name);
}
catch (ex) {
self.debug("Could not call AddFileParam");
}
} else {
this.debug("Could not find Flash element");
}
};
SWFUpload.prototype.setUploadURL = function (url) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetUploadURL) === "function") {
try {
this.addSetting("upload_url", url);
movie_element.SetUploadURL(this.getSetting("upload_url"));
}
catch (ex) {
this.debug("Could not call SetUploadURL");
}
} else {
this.debug("Could not find Flash element in setUploadURL");
}
};
SWFUpload.prototype.setPostParams = function (param_object) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetPostParams) === "function") {
try {
this.addSetting("post_params", param_object);
movie_element.SetPostParams(this.getSetting("post_params"));
}
catch (ex) {
this.debug("Could not call SetPostParams");
}
} else {
this.debug("Could not find Flash element in SetPostParams");
}
};
SWFUpload.prototype.setFileTypes = function (types, description) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetFileTypes) === "function") {
try {
this.addSetting("file_types", types);
this.addSetting("file_types_description", description);
movie_element.SetFileTypes(this.getSetting("file_types"), this.getSetting("file_types_description"));
}
catch (ex) {
this.debug("Could not call SetFileTypes");
}
} else {
this.debug("Could not find Flash element in SetFileTypes");
}
};
SWFUpload.prototype.setFileSizeLimit = function (file_size_limit) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetFileSizeLimit) === "function") {
try {
this.addSetting("file_size_limit", file_size_limit);
movie_element.SetFileSizeLimit(this.getSetting("file_size_limit"));
}
catch (ex) {
this.debug("Could not call SetFileSizeLimit");
}
} else {
this.debug("Could not find Flash element in SetFileSizeLimit");
}
};
SWFUpload.prototype.setFileUploadLimit = function (file_upload_limit) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetFileUploadLimit) === "function") {
try {
this.addSetting("file_upload_limit", file_upload_limit);
movie_element.SetFileUploadLimit(this.getSetting("file_upload_limit"));
}
catch (ex) {
this.debug("Could not call SetFileUploadLimit");
}
} else {
this.debug("Could not find Flash element in SetFileUploadLimit");
}
};
SWFUpload.prototype.setFileQueueLimit = function (file_queue_limit) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetFileQueueLimit) === "function") {
try {
this.addSetting("file_queue_limit", file_queue_limit);
movie_element.SetFileQueueLimit(this.getSetting("file_queue_limit"));
}
catch (ex) {
this.debug("Could not call SetFileQueueLimit");
}
} else {
this.debug("Could not find Flash element in SetFileQueueLimit");
}
};
SWFUpload.prototype.setFilePostName = function (file_post_name) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetFilePostName) === "function") {
try {
this.addSetting("file_post_name", file_post_name);
movie_element.SetFilePostName(this.getSetting("file_post_name"));
}
catch (ex) {
this.debug("Could not call SetFilePostName");
}
} else {
this.debug("Could not find Flash element in SetFilePostName");
}
};
SWFUpload.prototype.setDebugEnabled = function (debug_enabled) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.SetDebugEnabled) === "function") {
try {
this.addSetting("debug_enabled", debug_enabled);
movie_element.SetDebugEnabled(this.getSetting("debug_enabled"));
}
catch (ex) {
this.debug("Could not call SetDebugEnabled");
}
} else {
this.debug("Could not find Flash element in SetDebugEnabled");
}
};
/* *******************************
Internal Event Callers
Don't override these! These event callers ensure that your custom event handlers
are called safely and in order.
******************************* */
/* This is the callback method that the Flash movie will call when it has been loaded and is ready to go.
Calling this or showUI() "manually" will bypass the Flash Detection built in to SWFUpload.
Use a ui_function setting if you want to control the UI loading after the flash has loaded.
*/
SWFUpload.prototype.flashReady = function () {
var self = this;
if (typeof(self.fileDialogStart_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.flashReady_handler(); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("fileDialogStart event not defined");
}
};
/*
Event Queue. Rather can call events directly from Flash they events are
are placed in a queue and then executed. This ensures that each event is
executed in the order it was called which is not guarenteed when calling
setTimeout. Out of order events was especially problematic in Safari.
*/
SWFUpload.prototype.executeNextEvent = function () {
var f = this.eventQueue.shift();
if (typeof(f) === "function") {
f();
}
}
/* This is a chance to do something before the browse window opens */
SWFUpload.prototype.fileDialogStart = function () {
var self = this;
if (typeof(self.fileDialogStart_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.fileDialogStart_handler(); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("fileDialogStart event not defined");
}
};
/* Called when a file is successfully added to the queue. */
SWFUpload.prototype.fileQueued = function (file) {
var self = this;
if (typeof(self.fileQueued_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.fileQueued_handler(file); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("fileQueued event not defined");
}
};
/* Handle errors that occur when an attempt to queue a file fails. */
SWFUpload.prototype.fileQueueError = function (file, error_code, message) {
var self = this;
if (typeof(self.fileQueueError_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.fileQueueError_handler(file, error_code, message); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("fileQueueError event not defined");
}
};
/* Called after the file dialog has closed and the selected files have been queued.
You could call startUpload here if you want the queued files to begin uploading immediately. */
SWFUpload.prototype.fileDialogComplete = function (num_files_selected) {
var self = this;
if (typeof(self.fileDialogComplete_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.fileDialogComplete_handler(num_files_selected); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("fileDialogComplete event not defined");
}
};
/* Gets called when a file upload is about to be started. Return true to continue the upload. Return false to stop the upload.
If you return false then uploadError and uploadComplete are called (like normal).
This is a good place to do any file validation you need.
*/
SWFUpload.prototype.uploadStart = function (file) {
var self = this;
if (typeof(self.fileDialogComplete_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.returnUploadStart(self.uploadStart_handler(file)); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("uploadStart event not defined");
}
};
/* Note: Internal use only. This function returns the result of uploadStart to
flash. Since returning values in the normal way can result in Flash/JS circular
call issues we split up the call in a Timeout. This is transparent from the API
point of view.
*/
SWFUpload.prototype.returnUploadStart = function (return_value) {
var movie_element = this.getMovieElement();
if (movie_element !== null && typeof(movie_element.ReturnUploadStart) === "function") {
try {
movie_element.ReturnUploadStart(return_value);
}
catch (ex) {
this.debug("Could not call ReturnUploadStart");
}
} else {
this.debug("Could not find Flash element in returnUploadStart");
}
};
/* Called during upload as the file progresses. Use this event to update your UI. */
SWFUpload.prototype.uploadProgress = function (file, bytes_complete, bytes_total) {
var self = this;
if (typeof(self.uploadProgress_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.uploadProgress_handler(file, bytes_complete, bytes_total); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("uploadProgress event not defined");
}
};
/* Called when an error occurs during an upload. Use error_code and the SWFUpload.UPLOAD_ERROR constants to determine
which error occurred. The uploadComplete event is called after an error code indicating that the next file is
ready for upload. For files cancelled out of order the uploadComplete event will not be called. */
SWFUpload.prototype.uploadError = function (file, error_code, message) {
var self = this;
if (typeof(this.uploadError_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.uploadError_handler(file, error_code, message); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("uploadError event not defined");
}
};
/* This gets called when a file finishes uploading and the server-side upload script has completed and returned a 200
status code. Any text returned by the server is available in server_data.
**NOTE: The upload script MUST return some text or the uploadSuccess and uploadComplete events will not fire and the
upload will become 'stuck'. */
SWFUpload.prototype.uploadSuccess = function (file, server_data) {
var self = this;
if (typeof(self.uploadSuccess_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.uploadSuccess_handler(file, server_data); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("uploadSuccess event not defined");
}
};
/* uploadComplete is called when the file is uploaded or an error occurred and SWFUpload is ready to make the next upload.
If you want the next upload to start to automatically you can call startUpload() from this event. */
SWFUpload.prototype.uploadComplete = function (file) {
var self = this;
if (typeof(self.uploadComplete_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.uploadComplete_handler(file); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.debug("uploadComplete event not defined");
}
};
/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
internal debug console. You can override this event and have messages written where you want. */
SWFUpload.prototype.debug = function (message) {
var self = this;
if (typeof(self.debug_handler) === "function") {
this.eventQueue[this.eventQueue.length] = function() { self.debug_handler(message); };
setTimeout(function () { self.executeNextEvent();}, 0);
} else {
this.eventQueue[this.eventQueue.length] = function() { self.debugMessage(message); };
setTimeout(function () { self.executeNextEvent();}, 0);
}
};
/* **********************************
Default Event Handlers.
These event handlers are used by default if an overriding handler is
not defined in the SWFUpload settings object.
JS Note: even though these are defined on the SWFUpload object (rather than the prototype) they
are attached (read: copied) to a SWFUpload instance and 'this' is given the proper context.
********************************** */
/* This is a special event handler that has no override in the settings. Flash calls this when it has
been loaded by the browser and is ready for interaction. You should not override it. If you need
to do something with SWFUpload has loaded then use the swfupload_loaded_handler setting.
*/
SWFUpload.flashReady = function () {
try {
this.debug("Flash called back and is ready.");
if (typeof(this.swfUploadLoaded_handler) === "function") {
this.swfUploadLoaded_handler();
}
} catch (ex) {
this.debug(ex);
}
};
/* This is a chance to something immediately after SWFUpload has loaded.
Like, hide the default/degraded upload form and display the SWFUpload form. */
SWFUpload.swfUploadLoaded = function () {
};
/* This is a chance to do something before the browse window opens */
SWFUpload.fileDialogStart = function () {
};
/* Called when a file is successfully added to the queue. */
SWFUpload.fileQueued = function (file) {
};
/* Handle errors that occur when an attempt to queue a file fails. */
SWFUpload.fileQueueError = function (file, error_code, message) {
try {
switch (error_code) {
case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
this.debug("Error Code: File too big, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
break;
case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
this.debug("Error Code: Zero Byte File, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
break;
case SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED:
this.debug("Error Code: Upload limit reached, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
break;
case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
this.debug("Error Code: File extension is not allowed, Message: " + message);
break;
default:
this.debug("Error Code: Unhandled error occured. Errorcode: " + error_code);
}
} catch (ex) {
this.debug(ex);
}
};
/* Called after the file dialog has closed and the selected files have been queued.
You could call startUpload here if you want the queued files to begin uploading immediately. */
SWFUpload.fileDialogComplete = function (num_files_selected) {
};
/* Gets called when a file upload is about to be started. Return true to continue the upload. Return false to stop the upload.
If you return false then the uploadError callback is called and then uploadComplete (like normal).
This is a good place to do any file validation you need.
This is the only function that cannot be called on a setTimeout because it must return a value to Flash.
You SHOULD NOT make any calls in to Flash (e.i, changing settings, getting stats, etc). Flash Player bugs prevent
calls in to Flash from working reliably.
*/
SWFUpload.uploadStart = function (file) {
return true;
};
// Called during upload as the file progresses
SWFUpload.uploadProgress = function (file, bytes_complete, bytes_total) {
this.debug("File Progress: " + file.id + ", Bytes: " + bytes_complete + ". Total: " + bytes_total);
};
/* This gets called when a file finishes uploading and the upload script has completed and returned a 200 status code. Any text returned by the
server is available in server_data. The upload script must return some text or uploadSuccess will not fire (neither will uploadComplete). */
SWFUpload.uploadSuccess = function (file, server_data) {
};
/* This is called last. The file is uploaded or an error occurred and SWFUpload is ready to make the next upload.
If you want to automatically start the next file just call startUpload from here.
*/
SWFUpload.uploadComplete = function (file) {
};
// Called by SWFUpload JavaScript and Flash functions when debug is enabled.
// Override this method in your settings to call your own debug message handler
SWFUpload.debug = function (message) {
if (this.getSetting("debug_enabled")) {
this.debugMessage(message);
}
};
/* Called when an upload occurs during upload. For HTTP errors 'message' will contain the HTTP STATUS CODE */
SWFUpload.uploadError = function (file, error_code, message) {
try {
switch (errcode) {
case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND:
this.debug("Error Code: File ID specified for upload was not found, Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
this.debug("Error Code: HTTP Error, File name: " + file.name + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:
this.debug("Error Code: No backend file, File name: " + file.name + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.IO_ERROR:
this.debug("Error Code: IO Error, File name: " + file.name + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
this.debug("Error Code: Security Error, File name: " + file.name + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
this.debug("Error Code: Upload limit reached, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
this.debug("Error Code: Upload Initialization exception, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
this.debug("Error Code: uploadStart callback returned false, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
this.debug("Error Code: The file upload was cancelled, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
this.debug("Error Code: The file upload was stopped, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
break;
default:
this.debug("Error Code: Unhandled error occured. Errorcode: " + errcode);
}
} catch (ex) {
this.debug(ex);
}
};
/* **********************************
Debug Console
The debug console is a self contained, in page location
for debug message to be sent. The Debug Console adds
itself to the body if necessary.
The console is automatically scrolled as messages appear.
You can override this console (to use FireBug's console for instance) by setting the debug event method to your own function
that handles the debug message
********************************** */
SWFUpload.prototype.debugMessage = function (message) {
var exception_message, exception_values;
if (typeof(message) === "object" && typeof(message.name) === "string" && typeof(message.message) === "string") {
exception_message = "";
exception_values = [];
for (var key in message) {
exception_values.push(key + ": " + message[key]);
}
exception_message = exception_values.join("\n");
exception_values = exception_message.split("\n");
exception_message = "EXCEPTION: " + exception_values.join("\nEXCEPTION: ");
SWFUpload.Console.writeLine(exception_message);
} else {
SWFUpload.Console.writeLine(message);
}
};
SWFUpload.Console = {};
SWFUpload.Console.writeLine = function (message) {
var console, documentForm;
try {
console = document.getElementById("SWFUpload_Console");
if (!console) {
documentForm = document.createElement("form");
document.getElementsByTagName("body")[0].appendChild(documentForm);
console = document.createElement("textarea");
console.id = "SWFUpload_Console";
console.style.fontFamily = "monospace";
console.setAttribute("wrap", "off");
console.wrap = "off";
console.style.overflow = "auto";
console.style.width = "700px";
console.style.height = "350px";
console.style.margin = "5px";
documentForm.appendChild(console);
}
console.value += message + "\n";
console.scrollTop = console.scrollHeight - console.clientHeight;
} catch (ex) {
alert("Exception: " + ex.name + " Message: " + ex.message);
}
};